1 /* $OpenBSD: pci_map.c,v 1.20 2007/01/26 01:18:48 tsi Exp $ */ 2 /* $NetBSD: pci_map.c,v 1.7 2000/05/10 16:58:42 thorpej Exp $ */ 3 4 /*- 5 * Copyright (c) 1998, 2000 The NetBSD Foundation, Inc. 6 * All rights reserved. 7 * 8 * This code is derived from software contributed to The NetBSD Foundation 9 * by Charles M. Hannum; by William R. Studenmund; by Jason R. Thorpe. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. All advertising materials mentioning features or use of this software 20 * must display the following acknowledgement: 21 * This product includes software developed by the NetBSD 22 * Foundation, Inc. and its contributors. 23 * 4. Neither the name of The NetBSD Foundation nor the names of its 24 * contributors may be used to endorse or promote products derived 25 * from this software without specific prior written permission. 26 * 27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37 * POSSIBILITY OF SUCH DAMAGE. 38 */ 39 40 /* 41 * PCI device mapping. 42 */ 43 44 #include <sys/param.h> 45 #include <sys/systm.h> 46 #include <sys/device.h> 47 48 #include <dev/pci/pcireg.h> 49 #include <dev/pci/pcivar.h> 50 51 52 int obsd_pci_io_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t, 53 bus_addr_t *, bus_size_t *, int *); 54 int obsd_pci_mem_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t, 55 bus_addr_t *, bus_size_t *, int *); 56 57 int 58 obsd_pci_io_find(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type, 59 bus_addr_t *basep, bus_size_t *sizep, int *flagsp) 60 { 61 pcireg_t address, mask, csr; 62 int s; 63 64 if (reg < PCI_MAPREG_START || 65 #if 0 66 /* 67 * Can't do this check; some devices have mapping registers 68 * way out in left field. 69 */ 70 reg >= PCI_MAPREG_END || 71 #endif 72 (reg & 3)) 73 panic("pci_io_find: bad request"); 74 75 /* 76 * Section 6.2.5.1, `Address Maps', tells us that: 77 * 78 * 1) The builtin software should have already mapped the device in a 79 * reasonable way. 80 * 81 * 2) A device which wants 2^n bytes of memory will hardwire the bottom 82 * n bits of the address to 0. As recommended, we write all 1s while 83 * the device is disabled and see what we get back. 84 */ 85 s = splhigh(); 86 csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 87 if (csr & PCI_COMMAND_IO_ENABLE) 88 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, 89 csr & ~PCI_COMMAND_IO_ENABLE); 90 address = pci_conf_read(pc, tag, reg); 91 pci_conf_write(pc, tag, reg, 0xffffffff); 92 mask = pci_conf_read(pc, tag, reg); 93 pci_conf_write(pc, tag, reg, address); 94 if (csr & PCI_COMMAND_IO_ENABLE) 95 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); 96 splx(s); 97 98 if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) { 99 #ifdef DEBUG 100 printf("pci_io_find: expected type i/o, found mem\n"); 101 #endif 102 return (EINVAL); 103 } 104 105 if (PCI_MAPREG_IO_SIZE(mask) == 0) { 106 #ifdef DEBUG 107 printf("pci_io_find: void region\n"); 108 #endif 109 return (ENOENT); 110 } 111 112 if (basep != 0) 113 *basep = PCI_MAPREG_IO_ADDR(address); 114 if (sizep != 0) 115 *sizep = PCI_MAPREG_IO_SIZE(mask); 116 if (flagsp != 0) 117 *flagsp = 0; 118 119 return (0); 120 } 121 122 int 123 obsd_pci_mem_find(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type, 124 bus_addr_t *basep, bus_size_t *sizep, int *flagsp) 125 { 126 pcireg_t address, mask, address1 = 0, mask1 = 0xffffffff, csr; 127 u_int64_t waddress, wmask; 128 int s, is64bit; 129 130 is64bit = (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT); 131 132 if (reg < PCI_MAPREG_START || 133 #if 0 134 /* 135 * Can't do this check; some devices have mapping registers 136 * way out in left field. 137 */ 138 reg >= PCI_MAPREG_END || 139 #endif 140 (reg & 3)) 141 panic("pci_mem_find: bad request"); 142 143 if (is64bit && (reg + 4) >= PCI_MAPREG_END) 144 panic("pci_mem_find: bad 64-bit request"); 145 146 /* 147 * Section 6.2.5.1, `Address Maps', tells us that: 148 * 149 * 1) The builtin software should have already mapped the device in a 150 * reasonable way. 151 * 152 * 2) A device which wants 2^n bytes of memory will hardwire the bottom 153 * n bits of the address to 0. As recommended, we write all 1s while 154 * the device is disabled and see what we get back. 155 */ 156 s = splhigh(); 157 csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 158 if (csr & PCI_COMMAND_MEM_ENABLE) 159 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, 160 csr & ~PCI_COMMAND_MEM_ENABLE); 161 address = pci_conf_read(pc, tag, reg); 162 pci_conf_write(pc, tag, reg, 0xffffffff); 163 mask = pci_conf_read(pc, tag, reg); 164 pci_conf_write(pc, tag, reg, address); 165 if (is64bit) { 166 address1 = pci_conf_read(pc, tag, reg + 4); 167 pci_conf_write(pc, tag, reg + 4, 0xffffffff); 168 mask1 = pci_conf_read(pc, tag, reg + 4); 169 pci_conf_write(pc, tag, reg + 4, address1); 170 } 171 if (csr & PCI_COMMAND_MEM_ENABLE) 172 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); 173 splx(s); 174 175 if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) { 176 #ifdef DEBUG 177 printf("pci_mem_find: expected type mem, found i/o\n"); 178 #endif 179 return (EINVAL); 180 } 181 if (type != -1 && 182 PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) { 183 #ifdef DEBUG 184 printf("pci_mem_find: expected mem type %08x, found %08x\n", 185 PCI_MAPREG_MEM_TYPE(type), 186 PCI_MAPREG_MEM_TYPE(address)); 187 #endif 188 return (EINVAL); 189 } 190 191 waddress = (u_int64_t)address1 << 32UL | address; 192 wmask = (u_int64_t)mask1 << 32UL | mask; 193 194 if ((is64bit && PCI_MAPREG_MEM64_SIZE(wmask) == 0) || 195 (!is64bit && PCI_MAPREG_MEM_SIZE(mask) == 0)) { 196 #ifdef DEBUG 197 printf("pci_mem_find: void region\n"); 198 #endif 199 return (ENOENT); 200 } 201 202 switch (PCI_MAPREG_MEM_TYPE(address)) { 203 case PCI_MAPREG_MEM_TYPE_32BIT: 204 case PCI_MAPREG_MEM_TYPE_32BIT_1M: 205 break; 206 case PCI_MAPREG_MEM_TYPE_64BIT: 207 /* 208 * Handle the case of a 64-bit memory register on a 209 * platform with 32-bit addressing. Make sure that 210 * the address assigned and the device's memory size 211 * fit in 32 bits. We implicitly assume that if 212 * bus_addr_t is 64-bit, then so is bus_size_t. 213 */ 214 if (sizeof(u_int64_t) > sizeof(bus_addr_t) && 215 (address1 != 0 || mask1 != 0xffffffff)) { 216 #ifdef DEBUG 217 printf("pci_mem_find: 64-bit memory map which is " 218 "inaccessible on a 32-bit platform\n"); 219 #endif 220 return (EINVAL); 221 } 222 break; 223 default: 224 #ifdef DEBUG 225 printf("pci_mem_find: reserved mapping register type\n"); 226 #endif 227 return (EINVAL); 228 } 229 230 if (sizeof(u_int64_t) > sizeof(bus_addr_t)) { 231 if (basep != 0) 232 *basep = PCI_MAPREG_MEM_ADDR(address); 233 if (sizep != 0) 234 *sizep = PCI_MAPREG_MEM_SIZE(mask); 235 } else { 236 if (basep != 0) 237 *basep = PCI_MAPREG_MEM64_ADDR(waddress); 238 if (sizep != 0) 239 *sizep = PCI_MAPREG_MEM64_SIZE(wmask); 240 } 241 if (flagsp != 0) 242 *flagsp = 243 #ifdef BUS_SPACE_MAP_PREFETCHABLE 244 PCI_MAPREG_MEM_PREFETCHABLE(address) ? 245 BUS_SPACE_MAP_PREFETCHABLE : 246 #endif 247 0; 248 249 return (0); 250 } 251 252 int 253 pci_io_find(pci_chipset_tag_t pc, pcitag_t pcitag, int reg, 254 bus_addr_t *iobasep, bus_size_t *iosizep) 255 { 256 return (obsd_pci_io_find(pc, pcitag, reg, 0, iobasep, iosizep, 0)); 257 } 258 259 int 260 pci_mem_find(pci_chipset_tag_t pc, pcitag_t pcitag, int reg, 261 bus_addr_t *membasep, bus_size_t *memsizep, int *cacheablep) 262 { 263 return (obsd_pci_mem_find(pc, pcitag, reg, -1, membasep, memsizep, 264 cacheablep)); 265 } 266 267 pcireg_t 268 pci_mapreg_type(pci_chipset_tag_t pc, pcitag_t tag, int reg) 269 { 270 return (_PCI_MAPREG_TYPEBITS(pci_conf_read(pc, tag, reg))); 271 } 272 273 int 274 pci_mapreg_probe(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *typep) 275 { 276 pcireg_t address, mask, csr; 277 int s; 278 279 s = splhigh(); 280 csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 281 if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 282 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr & 283 ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)); 284 address = pci_conf_read(pc, tag, reg); 285 pci_conf_write(pc, tag, reg, 0xffffffff); 286 mask = pci_conf_read(pc, tag, reg); 287 pci_conf_write(pc, tag, reg, address); 288 if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 289 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); 290 splx(s); 291 292 if (mask == 0) /* unimplemented mapping register */ 293 return (0); 294 295 if (typep) 296 *typep = _PCI_MAPREG_TYPEBITS(address); 297 return (1); 298 } 299 300 int 301 pci_mapreg_info(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type, 302 bus_addr_t *basep, bus_size_t *sizep, int *flagsp) 303 { 304 305 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) 306 return (obsd_pci_io_find(pc, tag, reg, type, basep, sizep, 307 flagsp)); 308 else 309 return (obsd_pci_mem_find(pc, tag, reg, type, basep, sizep, 310 flagsp)); 311 } 312 313 int 314 pci_mapreg_map(struct pci_attach_args *pa, int reg, pcireg_t type, int busflags, 315 bus_space_tag_t *tagp, bus_space_handle_t *handlep, bus_addr_t *basep, 316 bus_size_t *sizep, bus_size_t maxsize) 317 { 318 bus_space_tag_t tag; 319 bus_space_handle_t handle; 320 bus_addr_t base; 321 bus_size_t size; 322 pcireg_t csr; 323 int flags; 324 int rv; 325 326 if ((rv = pci_mapreg_info(pa->pa_pc, pa->pa_tag, reg, type, 327 &base, &size, &flags)) != 0) 328 return (rv); 329 if (base == 0) 330 return (EINVAL); /* disabled because of invalid BAR */ 331 332 csr = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); 333 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) 334 csr |= PCI_COMMAND_IO_ENABLE; 335 else 336 csr |= PCI_COMMAND_MEM_ENABLE; 337 /* XXX Should this only be done for devices that do DMA? */ 338 csr |= PCI_COMMAND_MASTER_ENABLE; 339 pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, csr); 340 341 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { 342 if ((pa->pa_flags & PCI_FLAGS_IO_ENABLED) == 0) 343 return (EINVAL); 344 tag = pa->pa_iot; 345 } else { 346 if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) == 0) 347 return (EINVAL); 348 tag = pa->pa_memt; 349 } 350 351 /* The caller can request limitation of the mapping's size. */ 352 if (maxsize != 0 && size > maxsize) { 353 #ifdef DEBUG 354 printf("pci_mapreg_map: limited PCI mapping from %lx to %lx\n", 355 (u_long)size, (u_long)maxsize); 356 #endif 357 size = maxsize; 358 } 359 360 if (bus_space_map(tag, base, size, busflags | flags, &handle)) 361 return (1); 362 363 if (tagp != NULL) 364 *tagp = tag; 365 if (handlep != NULL) 366 *handlep = handle; 367 if (basep != NULL) 368 *basep = base; 369 if (sizep != NULL) 370 *sizep = size; 371 372 return (0); 373 } 374