1 /* $OpenBSD: pci_map.c,v 1.30 2013/02/09 20:43:33 miod 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 PCI_MAPREG_MEM_PREFETCHABLE(address) ? 253 BUS_SPACE_MAP_PREFETCHABLE : 0; 254 255 return (0); 256 } 257 258 int 259 pci_io_find(pci_chipset_tag_t pc, pcitag_t pcitag, int reg, 260 bus_addr_t *iobasep, bus_size_t *iosizep) 261 { 262 return (obsd_pci_io_find(pc, pcitag, reg, 0, iobasep, iosizep, 0)); 263 } 264 265 int 266 pci_mem_find(pci_chipset_tag_t pc, pcitag_t pcitag, int reg, 267 bus_addr_t *membasep, bus_size_t *memsizep, int *cacheablep) 268 { 269 return (obsd_pci_mem_find(pc, pcitag, reg, -1, membasep, memsizep, 270 cacheablep)); 271 } 272 273 pcireg_t 274 pci_mapreg_type(pci_chipset_tag_t pc, pcitag_t tag, int reg) 275 { 276 return (_PCI_MAPREG_TYPEBITS(pci_conf_read(pc, tag, reg))); 277 } 278 279 int 280 pci_mapreg_probe(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t *typep) 281 { 282 pcireg_t address, mask, csr; 283 int s; 284 285 s = splhigh(); 286 csr = pci_conf_read(pc, tag, PCI_COMMAND_STATUS_REG); 287 if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 288 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr & 289 ~(PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)); 290 address = pci_conf_read(pc, tag, reg); 291 pci_conf_write(pc, tag, reg, 0xffffffff); 292 mask = pci_conf_read(pc, tag, reg); 293 pci_conf_write(pc, tag, reg, address); 294 if (csr & (PCI_COMMAND_IO_ENABLE | PCI_COMMAND_MEM_ENABLE)) 295 pci_conf_write(pc, tag, PCI_COMMAND_STATUS_REG, csr); 296 splx(s); 297 298 if (mask == 0) /* unimplemented mapping register */ 299 return (0); 300 301 if (typep) 302 *typep = _PCI_MAPREG_TYPEBITS(address); 303 return (1); 304 } 305 306 int 307 pci_mapreg_info(pci_chipset_tag_t pc, pcitag_t tag, int reg, pcireg_t type, 308 bus_addr_t *basep, bus_size_t *sizep, int *flagsp) 309 { 310 311 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) 312 return (obsd_pci_io_find(pc, tag, reg, type, basep, sizep, 313 flagsp)); 314 else 315 return (obsd_pci_mem_find(pc, tag, reg, type, basep, sizep, 316 flagsp)); 317 } 318 319 int 320 pci_mapreg_map(struct pci_attach_args *pa, int reg, pcireg_t type, int flags, 321 bus_space_tag_t *tagp, bus_space_handle_t *handlep, bus_addr_t *basep, 322 bus_size_t *sizep, bus_size_t maxsize) 323 { 324 bus_space_tag_t tag; 325 bus_space_handle_t handle; 326 bus_addr_t base; 327 bus_size_t size; 328 pcireg_t csr; 329 int rv; 330 331 if ((rv = pci_mapreg_info(pa->pa_pc, pa->pa_tag, reg, type, 332 &base, &size, NULL)) != 0) 333 return (rv); 334 #if !defined(__sparc64__) 335 if (base == 0) { 336 struct extent *ex; 337 bus_addr_t start, end; 338 339 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { 340 ex = pa->pa_ioex; 341 if (ex != NULL) { 342 start = max(PCI_IO_START, ex->ex_start); 343 end = min(PCI_IO_END, ex->ex_end); 344 } 345 } else { 346 ex = pa->pa_memex; 347 if (ex != NULL) { 348 start = max(PCI_MEM_START, ex->ex_start); 349 end = min(PCI_MEM_END, ex->ex_end); 350 } 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, 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