1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Aic94xx SAS/SATA driver register access. |
4 | * |
5 | * Copyright (C) 2005 Adaptec, Inc. All rights reserved. |
6 | * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> |
7 | */ |
8 | |
9 | #include <linux/pci.h> |
10 | #include "aic94xx_reg.h" |
11 | #include "aic94xx.h" |
12 | |
13 | /* Writing to device address space. |
14 | * Offset comes before value to remind that the operation of |
15 | * this function is *offs = val. |
16 | */ |
17 | static void asd_write_byte(struct asd_ha_struct *asd_ha, |
18 | unsigned long offs, u8 val) |
19 | { |
20 | if (unlikely(asd_ha->iospace)) |
21 | outb(value: val, |
22 | port: (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); |
23 | else |
24 | writeb(val, addr: asd_ha->io_handle[0].addr + offs); |
25 | wmb(); |
26 | } |
27 | |
28 | static void asd_write_word(struct asd_ha_struct *asd_ha, |
29 | unsigned long offs, u16 val) |
30 | { |
31 | if (unlikely(asd_ha->iospace)) |
32 | outw(value: val, |
33 | port: (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); |
34 | else |
35 | writew(val, addr: asd_ha->io_handle[0].addr + offs); |
36 | wmb(); |
37 | } |
38 | |
39 | static void asd_write_dword(struct asd_ha_struct *asd_ha, |
40 | unsigned long offs, u32 val) |
41 | { |
42 | if (unlikely(asd_ha->iospace)) |
43 | outl(value: val, |
44 | port: (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF)); |
45 | else |
46 | writel(val, addr: asd_ha->io_handle[0].addr + offs); |
47 | wmb(); |
48 | } |
49 | |
50 | /* Reading from device address space. |
51 | */ |
52 | static u8 asd_read_byte(struct asd_ha_struct *asd_ha, unsigned long offs) |
53 | { |
54 | u8 val; |
55 | if (unlikely(asd_ha->iospace)) |
56 | val = inb(port: (unsigned long) asd_ha->io_handle[0].addr |
57 | + (offs & 0xFF)); |
58 | else |
59 | val = readb(addr: asd_ha->io_handle[0].addr + offs); |
60 | rmb(); |
61 | return val; |
62 | } |
63 | |
64 | static u16 asd_read_word(struct asd_ha_struct *asd_ha, |
65 | unsigned long offs) |
66 | { |
67 | u16 val; |
68 | if (unlikely(asd_ha->iospace)) |
69 | val = inw(port: (unsigned long)asd_ha->io_handle[0].addr |
70 | + (offs & 0xFF)); |
71 | else |
72 | val = readw(addr: asd_ha->io_handle[0].addr + offs); |
73 | rmb(); |
74 | return val; |
75 | } |
76 | |
77 | static u32 asd_read_dword(struct asd_ha_struct *asd_ha, |
78 | unsigned long offs) |
79 | { |
80 | u32 val; |
81 | if (unlikely(asd_ha->iospace)) |
82 | val = inl(port: (unsigned long) asd_ha->io_handle[0].addr |
83 | + (offs & 0xFF)); |
84 | else |
85 | val = readl(addr: asd_ha->io_handle[0].addr + offs); |
86 | rmb(); |
87 | return val; |
88 | } |
89 | |
90 | static inline u32 asd_mem_offs_swa(void) |
91 | { |
92 | return 0; |
93 | } |
94 | |
95 | static inline u32 asd_mem_offs_swc(void) |
96 | { |
97 | return asd_mem_offs_swa() + MBAR0_SWA_SIZE; |
98 | } |
99 | |
100 | static inline u32 asd_mem_offs_swb(void) |
101 | { |
102 | return asd_mem_offs_swc() + MBAR0_SWC_SIZE + 0x20; |
103 | } |
104 | |
105 | /* We know that the register wanted is in the range |
106 | * of the sliding window. |
107 | */ |
108 | #define ASD_READ_SW(ww, type, ord) \ |
109 | static type asd_read_##ww##_##ord(struct asd_ha_struct *asd_ha, \ |
110 | u32 reg) \ |
111 | { \ |
112 | struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \ |
113 | u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\ |
114 | return asd_read_##ord(asd_ha, (unsigned long)map_offs); \ |
115 | } |
116 | |
117 | #define ASD_WRITE_SW(ww, type, ord) \ |
118 | static void asd_write_##ww##_##ord(struct asd_ha_struct *asd_ha, \ |
119 | u32 reg, type val) \ |
120 | { \ |
121 | struct asd_ha_addrspace *io_handle = &asd_ha->io_handle[0]; \ |
122 | u32 map_offs = (reg - io_handle->ww##_base) + asd_mem_offs_##ww();\ |
123 | asd_write_##ord(asd_ha, (unsigned long)map_offs, val); \ |
124 | } |
125 | |
126 | ASD_READ_SW(swa, u8, byte); |
127 | ASD_READ_SW(swa, u16, word); |
128 | ASD_READ_SW(swa, u32, dword); |
129 | |
130 | ASD_READ_SW(swb, u8, byte); |
131 | ASD_READ_SW(swb, u16, word); |
132 | ASD_READ_SW(swb, u32, dword); |
133 | |
134 | ASD_READ_SW(swc, u8, byte); |
135 | ASD_READ_SW(swc, u16, word); |
136 | ASD_READ_SW(swc, u32, dword); |
137 | |
138 | ASD_WRITE_SW(swa, u8, byte); |
139 | ASD_WRITE_SW(swa, u16, word); |
140 | ASD_WRITE_SW(swa, u32, dword); |
141 | |
142 | ASD_WRITE_SW(swb, u8, byte); |
143 | ASD_WRITE_SW(swb, u16, word); |
144 | ASD_WRITE_SW(swb, u32, dword); |
145 | |
146 | ASD_WRITE_SW(swc, u8, byte); |
147 | ASD_WRITE_SW(swc, u16, word); |
148 | ASD_WRITE_SW(swc, u32, dword); |
149 | |
150 | /* |
151 | * A word about sliding windows: |
152 | * MBAR0 is divided into sliding windows A, C and B, in that order. |
153 | * SWA starts at offset 0 of MBAR0, up to 0x57, with size 0x58 bytes. |
154 | * SWC starts at offset 0x58 of MBAR0, up to 0x60, with size 0x8 bytes. |
155 | * From 0x60 to 0x7F, we have a copy of PCI config space 0x60-0x7F. |
156 | * SWB starts at offset 0x80 of MBAR0 and extends to the end of MBAR0. |
157 | * See asd_init_sw() in aic94xx_hwi.c |
158 | * |
159 | * We map the most common registers we'd access of the internal 4GB |
160 | * host adapter memory space. If a register/internal memory location |
161 | * is wanted which is not mapped, we slide SWB, by paging it, |
162 | * see asd_move_swb() in aic94xx_reg.c. |
163 | */ |
164 | |
165 | /** |
166 | * asd_move_swb -- move sliding window B |
167 | * @asd_ha: pointer to host adapter structure |
168 | * @reg: register desired to be within range of the new window |
169 | */ |
170 | static void asd_move_swb(struct asd_ha_struct *asd_ha, u32 reg) |
171 | { |
172 | u32 base = reg & ~(MBAR0_SWB_SIZE-1); |
173 | pci_write_config_dword(dev: asd_ha->pcidev, PCI_CONF_MBAR0_SWB, val: base); |
174 | asd_ha->io_handle[0].swb_base = base; |
175 | } |
176 | |
177 | static void __asd_write_reg_byte(struct asd_ha_struct *asd_ha, u32 reg, u8 val) |
178 | { |
179 | struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; |
180 | BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); |
181 | if (io_handle->swa_base <= reg |
182 | && reg < io_handle->swa_base + MBAR0_SWA_SIZE) |
183 | asd_write_swa_byte (asd_ha, reg,val); |
184 | else if (io_handle->swb_base <= reg |
185 | && reg < io_handle->swb_base + MBAR0_SWB_SIZE) |
186 | asd_write_swb_byte (asd_ha, reg, val); |
187 | else if (io_handle->swc_base <= reg |
188 | && reg < io_handle->swc_base + MBAR0_SWC_SIZE) |
189 | asd_write_swc_byte (asd_ha, reg, val); |
190 | else { |
191 | /* Ok, we have to move SWB */ |
192 | asd_move_swb(asd_ha, reg); |
193 | asd_write_swb_byte (asd_ha, reg, val); |
194 | } |
195 | } |
196 | |
197 | #define ASD_WRITE_REG(type, ord) \ |
198 | void asd_write_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg, type val)\ |
199 | { \ |
200 | struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \ |
201 | unsigned long flags; \ |
202 | BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \ |
203 | spin_lock_irqsave(&asd_ha->iolock, flags); \ |
204 | if (io_handle->swa_base <= reg \ |
205 | && reg < io_handle->swa_base + MBAR0_SWA_SIZE) \ |
206 | asd_write_swa_##ord (asd_ha, reg,val); \ |
207 | else if (io_handle->swb_base <= reg \ |
208 | && reg < io_handle->swb_base + MBAR0_SWB_SIZE) \ |
209 | asd_write_swb_##ord (asd_ha, reg, val); \ |
210 | else if (io_handle->swc_base <= reg \ |
211 | && reg < io_handle->swc_base + MBAR0_SWC_SIZE) \ |
212 | asd_write_swc_##ord (asd_ha, reg, val); \ |
213 | else { \ |
214 | /* Ok, we have to move SWB */ \ |
215 | asd_move_swb(asd_ha, reg); \ |
216 | asd_write_swb_##ord (asd_ha, reg, val); \ |
217 | } \ |
218 | spin_unlock_irqrestore(&asd_ha->iolock, flags); \ |
219 | } |
220 | |
221 | ASD_WRITE_REG(u8, byte); |
222 | ASD_WRITE_REG(u16,word); |
223 | ASD_WRITE_REG(u32,dword); |
224 | |
225 | static u8 __asd_read_reg_byte(struct asd_ha_struct *asd_ha, u32 reg) |
226 | { |
227 | struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; |
228 | u8 val; |
229 | BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); |
230 | if (io_handle->swa_base <= reg |
231 | && reg < io_handle->swa_base + MBAR0_SWA_SIZE) |
232 | val = asd_read_swa_byte (asd_ha, reg); |
233 | else if (io_handle->swb_base <= reg |
234 | && reg < io_handle->swb_base + MBAR0_SWB_SIZE) |
235 | val = asd_read_swb_byte (asd_ha, reg); |
236 | else if (io_handle->swc_base <= reg |
237 | && reg < io_handle->swc_base + MBAR0_SWC_SIZE) |
238 | val = asd_read_swc_byte (asd_ha, reg); |
239 | else { |
240 | /* Ok, we have to move SWB */ |
241 | asd_move_swb(asd_ha, reg); |
242 | val = asd_read_swb_byte (asd_ha, reg); |
243 | } |
244 | return val; |
245 | } |
246 | |
247 | #define ASD_READ_REG(type, ord) \ |
248 | type asd_read_reg_##ord (struct asd_ha_struct *asd_ha, u32 reg) \ |
249 | { \ |
250 | struct asd_ha_addrspace *io_handle=&asd_ha->io_handle[0]; \ |
251 | type val; \ |
252 | unsigned long flags; \ |
253 | BUG_ON(reg >= 0xC0000000 || reg < ALL_BASE_ADDR); \ |
254 | spin_lock_irqsave(&asd_ha->iolock, flags); \ |
255 | if (io_handle->swa_base <= reg \ |
256 | && reg < io_handle->swa_base + MBAR0_SWA_SIZE) \ |
257 | val = asd_read_swa_##ord (asd_ha, reg); \ |
258 | else if (io_handle->swb_base <= reg \ |
259 | && reg < io_handle->swb_base + MBAR0_SWB_SIZE) \ |
260 | val = asd_read_swb_##ord (asd_ha, reg); \ |
261 | else if (io_handle->swc_base <= reg \ |
262 | && reg < io_handle->swc_base + MBAR0_SWC_SIZE) \ |
263 | val = asd_read_swc_##ord (asd_ha, reg); \ |
264 | else { \ |
265 | /* Ok, we have to move SWB */ \ |
266 | asd_move_swb(asd_ha, reg); \ |
267 | val = asd_read_swb_##ord (asd_ha, reg); \ |
268 | } \ |
269 | spin_unlock_irqrestore(&asd_ha->iolock, flags); \ |
270 | return val; \ |
271 | } |
272 | |
273 | ASD_READ_REG(u8, byte); |
274 | ASD_READ_REG(u16,word); |
275 | ASD_READ_REG(u32,dword); |
276 | |
277 | /** |
278 | * asd_read_reg_string -- read a string of bytes from io space memory |
279 | * @asd_ha: pointer to host adapter structure |
280 | * @dst: pointer to a destination buffer where data will be written to |
281 | * @offs: start offset (register) to read from |
282 | * @count: number of bytes to read |
283 | */ |
284 | void asd_read_reg_string(struct asd_ha_struct *asd_ha, void *dst, |
285 | u32 offs, int count) |
286 | { |
287 | u8 *p = dst; |
288 | unsigned long flags; |
289 | |
290 | spin_lock_irqsave(&asd_ha->iolock, flags); |
291 | for ( ; count > 0; count--, offs++, p++) |
292 | *p = __asd_read_reg_byte(asd_ha, reg: offs); |
293 | spin_unlock_irqrestore(lock: &asd_ha->iolock, flags); |
294 | } |
295 | |
296 | /** |
297 | * asd_write_reg_string -- write a string of bytes to io space memory |
298 | * @asd_ha: pointer to host adapter structure |
299 | * @src: pointer to source buffer where data will be read from |
300 | * @offs: start offset (register) to write to |
301 | * @count: number of bytes to write |
302 | */ |
303 | void asd_write_reg_string(struct asd_ha_struct *asd_ha, void *src, |
304 | u32 offs, int count) |
305 | { |
306 | u8 *p = src; |
307 | unsigned long flags; |
308 | |
309 | spin_lock_irqsave(&asd_ha->iolock, flags); |
310 | for ( ; count > 0; count--, offs++, p++) |
311 | __asd_write_reg_byte(asd_ha, reg: offs, val: *p); |
312 | spin_unlock_irqrestore(lock: &asd_ha->iolock, flags); |
313 | } |
314 | |