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