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