1 /* $OpenBSD: pci_map.c,v 1.10 2003/08/27 20:59:15 mickey 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 static int nbsd_pci_io_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t, 53 bus_addr_t *, bus_size_t *, int *); 54 static int nbsd_pci_mem_find(pci_chipset_tag_t, pcitag_t, int, pcireg_t, 55 bus_addr_t *, bus_size_t *, int *); 56 57 static int 58 nbsd_pci_io_find(pc, tag, reg, type, basep, sizep, flagsp) 59 pci_chipset_tag_t pc; 60 pcitag_t tag; 61 int reg; 62 pcireg_t type; 63 bus_addr_t *basep; 64 bus_size_t *sizep; 65 int *flagsp; 66 { 67 pcireg_t address, mask; 68 int s; 69 70 if (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) 71 panic("pci_io_find: bad request"); 72 73 /* 74 * Section 6.2.5.1, `Address Maps', tells us that: 75 * 76 * 1) The builtin software should have already mapped the device in a 77 * reasonable way. 78 * 79 * 2) A device which wants 2^n bytes of memory will hardwire the bottom 80 * n bits of the address to 0. As recommended, we write all 1s and see 81 * what we get back. 82 */ 83 s = splhigh(); 84 address = pci_conf_read(pc, tag, reg); 85 pci_conf_write(pc, tag, reg, 0xffffffff); 86 mask = pci_conf_read(pc, tag, reg); 87 pci_conf_write(pc, tag, reg, address); 88 splx(s); 89 90 if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) { 91 printf("pci_io_find: expected type i/o, found mem\n"); 92 return (1); 93 } 94 95 if (PCI_MAPREG_IO_SIZE(mask) == 0) { 96 printf("pci_io_find: void region\n"); 97 return (1); 98 } 99 100 if (basep != 0) 101 *basep = PCI_MAPREG_IO_ADDR(address); 102 if (sizep != 0) 103 *sizep = PCI_MAPREG_IO_SIZE(mask); 104 if (flagsp != 0) 105 *flagsp = 0; 106 107 return (0); 108 } 109 110 static int 111 nbsd_pci_mem_find(pc, tag, reg, type, basep, sizep, flagsp) 112 pci_chipset_tag_t pc; 113 pcitag_t tag; 114 int reg; 115 pcireg_t type; 116 bus_addr_t *basep; 117 bus_size_t *sizep; 118 int *flagsp; 119 { 120 pcireg_t address, mask, address1 = 0, mask1 = 0xffffffff; 121 u_int64_t waddress, wmask; 122 int s, is64bit; 123 124 is64bit = (PCI_MAPREG_MEM_TYPE(type) == PCI_MAPREG_MEM_TYPE_64BIT); 125 126 if (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) 127 panic("pci_mem_find: bad request"); 128 129 if (is64bit && (reg + 4) >= PCI_MAPREG_END) 130 panic("pci_mem_find: bad 64-bit request"); 131 132 /* 133 * Section 6.2.5.1, `Address Maps', tells us that: 134 * 135 * 1) The builtin software should have already mapped the device in a 136 * reasonable way. 137 * 138 * 2) A device which wants 2^n bytes of memory will hardwire the bottom 139 * n bits of the address to 0. As recommended, we write all 1s and see 140 * what we get back. 141 */ 142 s = splhigh(); 143 address = pci_conf_read(pc, tag, reg); 144 pci_conf_write(pc, tag, reg, 0xffffffff); 145 mask = pci_conf_read(pc, tag, reg); 146 pci_conf_write(pc, tag, reg, address); 147 if (is64bit) { 148 address1 = pci_conf_read(pc, tag, reg + 4); 149 pci_conf_write(pc, tag, reg + 4, 0xffffffff); 150 mask1 = pci_conf_read(pc, tag, reg + 4); 151 pci_conf_write(pc, tag, reg + 4, address1); 152 } 153 splx(s); 154 155 if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) { 156 printf("pci_mem_find: expected type mem, found i/o\n"); 157 return (1); 158 } 159 if (type != -1 && 160 PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) { 161 printf("pci_mem_find: expected mem type %08x, found %08x\n", 162 PCI_MAPREG_MEM_TYPE(type), 163 PCI_MAPREG_MEM_TYPE(address)); 164 return (1); 165 } 166 167 waddress = (u_int64_t)address1 << 32UL | address; 168 wmask = (u_int64_t)mask1 << 32UL | mask; 169 170 if (PCI_MAPREG_MEM64_SIZE(wmask) == 0) { 171 printf("pci_mem_find: void region\n"); 172 return (1); 173 } 174 175 switch (PCI_MAPREG_MEM_TYPE(address)) { 176 case PCI_MAPREG_MEM_TYPE_32BIT: 177 case PCI_MAPREG_MEM_TYPE_32BIT_1M: 178 break; 179 case PCI_MAPREG_MEM_TYPE_64BIT: 180 /* 181 * Handle the case of a 64-bit memory register on a 182 * platform with 32-bit addressing. Make sure that 183 * the address assigned and the device's memory size 184 * fit in 32 bits. We implicitly assume that if 185 * bus_addr_t is 64-bit, then so is bus_size_t. 186 */ 187 if (sizeof(u_int64_t) > sizeof(bus_addr_t) && 188 (address1 != 0 || mask1 != 0xffffffff)) { 189 printf("pci_mem_find: 64-bit memory map which is " 190 "inaccessible on a 32-bit platform\n"); 191 return (1); 192 } 193 break; 194 default: 195 printf("pci_mem_find: reserved mapping register type\n"); 196 return (1); 197 } 198 199 if (sizeof(u_int64_t) > sizeof(bus_addr_t)) { 200 if (basep != 0) 201 *basep = PCI_MAPREG_MEM_ADDR(address); 202 if (sizep != 0) 203 *sizep = PCI_MAPREG_MEM_SIZE(mask); 204 } else { 205 if (basep != 0) 206 *basep = PCI_MAPREG_MEM64_ADDR(waddress); 207 if (sizep != 0) 208 *sizep = PCI_MAPREG_MEM64_SIZE(wmask); 209 } 210 if (flagsp != 0) 211 *flagsp = 212 #ifdef BUS_SPACE_MAP_PREFETCHABLE 213 PCI_MAPREG_MEM_PREFETCHABLE(address) ? 214 BUS_SPACE_MAP_PREFETCHABLE : 215 #endif 216 0; 217 218 return (0); 219 } 220 221 int 222 pci_io_find(pc, pcitag, reg, iobasep, iosizep) 223 pci_chipset_tag_t pc; 224 pcitag_t pcitag; 225 int reg; 226 bus_addr_t *iobasep; 227 bus_size_t *iosizep; 228 { 229 return (nbsd_pci_io_find(pc, pcitag, reg, 0, iobasep, iosizep, 0)); 230 } 231 232 int 233 pci_mem_find(pc, pcitag, reg, membasep, memsizep, cacheablep) 234 pci_chipset_tag_t pc; 235 pcitag_t pcitag; 236 int reg; 237 bus_addr_t *membasep; 238 bus_size_t *memsizep; 239 int *cacheablep; 240 { 241 return (nbsd_pci_mem_find(pc, pcitag, reg, -1, membasep, memsizep, 242 cacheablep)); 243 } 244 245 pcireg_t 246 pci_mapreg_type(pc, tag, reg) 247 pci_chipset_tag_t pc; 248 pcitag_t tag; 249 int reg; 250 { 251 pcireg_t rv; 252 253 rv = pci_conf_read(pc, tag, reg); 254 if (PCI_MAPREG_TYPE(rv) == PCI_MAPREG_TYPE_IO) 255 rv &= PCI_MAPREG_TYPE_MASK; 256 else 257 rv &= PCI_MAPREG_TYPE_MASK|PCI_MAPREG_MEM_TYPE_MASK; 258 return (rv); 259 } 260 261 int 262 pci_mapreg_info(pc, tag, reg, type, basep, sizep, flagsp) 263 pci_chipset_tag_t pc; 264 pcitag_t tag; 265 int reg; 266 pcireg_t type; 267 bus_addr_t *basep; 268 bus_size_t *sizep; 269 int *flagsp; 270 { 271 272 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) 273 return (nbsd_pci_io_find(pc, tag, reg, type, basep, sizep, 274 flagsp)); 275 else 276 return (nbsd_pci_mem_find(pc, tag, reg, type, basep, sizep, 277 flagsp)); 278 } 279 280 int 281 pci_mapreg_map(pa, reg, type, busflags, tagp, handlep, basep, sizep, maxsize) 282 struct pci_attach_args *pa; 283 int reg, busflags; 284 pcireg_t type; 285 bus_space_tag_t *tagp; 286 bus_space_handle_t *handlep; 287 bus_addr_t *basep; 288 bus_size_t *sizep; 289 bus_size_t maxsize; 290 { 291 bus_space_tag_t tag; 292 bus_space_handle_t handle; 293 bus_addr_t base; 294 bus_size_t size; 295 int flags; 296 297 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { 298 if ((pa->pa_flags & PCI_FLAGS_IO_ENABLED) == 0) 299 return (1); 300 if (nbsd_pci_io_find(pa->pa_pc, pa->pa_tag, reg, type, &base, 301 &size, &flags)) 302 return (1); 303 tag = pa->pa_iot; 304 } else { 305 if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) == 0) 306 return (1); 307 if (nbsd_pci_mem_find(pa->pa_pc, pa->pa_tag, reg, type, &base, 308 &size, &flags)) 309 return (1); 310 tag = pa->pa_memt; 311 } 312 313 /* The caller can request limitation of the mapping's size. */ 314 if (maxsize != 0 && size > maxsize) { 315 #ifdef DEBUG 316 printf("pci_mapreg_map: limited PCI mapping from %lx to %lx\n", 317 (u_long)size, (u_long)maxsize); 318 #endif 319 size = maxsize; 320 } 321 322 if (bus_space_map(tag, base, size, busflags | flags, &handle)) 323 return (1); 324 325 if (tagp != NULL) 326 *tagp = tag; 327 if (handlep != NULL) 328 *handlep = handle; 329 if (basep != NULL) 330 *basep = base; 331 if (sizep != NULL) 332 *sizep = size; 333 334 return (0); 335 } 336