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(val,
22 		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
23 	else
24 		writeb(val, 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(val,
33 		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
34 	else
35 		writew(val, 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(val,
44 		     (unsigned long)asd_ha->io_handle[0].addr + (offs & 0xFF));
45 	else
46 		writel(val, 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((unsigned long) asd_ha->io_handle[0].addr
57 			  + (offs & 0xFF));
58 	else
59 		val = readb(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((unsigned long)asd_ha->io_handle[0].addr
70 			  + (offs & 0xFF));
71 	else
72 		val = readw(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((unsigned long) asd_ha->io_handle[0].addr
83 			  + (offs & 0xFF));
84 	else
85 		val = readl(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(asd_ha->pcidev, PCI_CONF_MBAR0_SWB, 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, offs);
293 	spin_unlock_irqrestore(&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, offs, *p);
312 	spin_unlock_irqrestore(&asd_ha->iolock, flags);
313 }
314