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