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