1 /* $OpenBSD: pci_map.c,v 1.9 2003/02/28 15:26:23 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 = PCI_MAPREG_MEM_CACHEABLE(address) 212 #ifndef __OpenBSD__ 213 ? BUS_SPACE_MAP_CACHEABLE : 0 214 #endif 215 ; 216 217 return (0); 218 } 219 220 int 221 pci_io_find(pc, pcitag, reg, iobasep, iosizep) 222 pci_chipset_tag_t pc; 223 pcitag_t pcitag; 224 int reg; 225 bus_addr_t *iobasep; 226 bus_size_t *iosizep; 227 { 228 return (nbsd_pci_io_find(pc, pcitag, reg, 0, iobasep, iosizep, 0)); 229 } 230 231 int 232 pci_mem_find(pc, pcitag, reg, membasep, memsizep, cacheablep) 233 pci_chipset_tag_t pc; 234 pcitag_t pcitag; 235 int reg; 236 bus_addr_t *membasep; 237 bus_size_t *memsizep; 238 int *cacheablep; 239 { 240 return (nbsd_pci_mem_find(pc, pcitag, reg, -1, membasep, memsizep, 241 cacheablep)); 242 } 243 244 pcireg_t 245 pci_mapreg_type(pc, tag, reg) 246 pci_chipset_tag_t pc; 247 pcitag_t tag; 248 int reg; 249 { 250 pcireg_t rv; 251 252 rv = pci_conf_read(pc, tag, reg); 253 if (PCI_MAPREG_TYPE(rv) == PCI_MAPREG_TYPE_IO) 254 rv &= PCI_MAPREG_TYPE_MASK; 255 else 256 rv &= PCI_MAPREG_TYPE_MASK|PCI_MAPREG_MEM_TYPE_MASK; 257 return (rv); 258 } 259 260 int 261 pci_mapreg_info(pc, tag, reg, type, basep, sizep, flagsp) 262 pci_chipset_tag_t pc; 263 pcitag_t tag; 264 int reg; 265 pcireg_t type; 266 bus_addr_t *basep; 267 bus_size_t *sizep; 268 int *flagsp; 269 { 270 271 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) 272 return (nbsd_pci_io_find(pc, tag, reg, type, basep, sizep, 273 flagsp)); 274 else 275 return (nbsd_pci_mem_find(pc, tag, reg, type, basep, sizep, 276 flagsp)); 277 } 278 279 int 280 pci_mapreg_map(pa, reg, type, busflags, tagp, handlep, basep, sizep, maxsize) 281 struct pci_attach_args *pa; 282 int reg, busflags; 283 pcireg_t type; 284 bus_space_tag_t *tagp; 285 bus_space_handle_t *handlep; 286 bus_addr_t *basep; 287 bus_size_t *sizep; 288 bus_size_t maxsize; 289 { 290 bus_space_tag_t tag; 291 bus_space_handle_t handle; 292 bus_addr_t base; 293 bus_size_t size; 294 int flags; 295 296 if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { 297 if ((pa->pa_flags & PCI_FLAGS_IO_ENABLED) == 0) 298 return (1); 299 if (nbsd_pci_io_find(pa->pa_pc, pa->pa_tag, reg, type, &base, 300 &size, &flags)) 301 return (1); 302 tag = pa->pa_iot; 303 } else { 304 if ((pa->pa_flags & PCI_FLAGS_MEM_ENABLED) == 0) 305 return (1); 306 if (nbsd_pci_mem_find(pa->pa_pc, pa->pa_tag, reg, type, &base, 307 &size, &flags)) 308 return (1); 309 tag = pa->pa_memt; 310 } 311 312 /* The caller can request limitation of the mapping's size. */ 313 if (maxsize != 0 && size > maxsize) { 314 #ifdef DEBUG 315 printf("pci_mapreg_map: limited PCI mapping from %lx to %lx\n", 316 (u_long)size, (u_long)maxsize); 317 #endif 318 size = maxsize; 319 } 320 321 if (bus_space_map(tag, base, size, busflags | flags, &handle)) 322 return (1); 323 324 if (tagp != NULL) 325 *tagp = tag; 326 if (handlep != NULL) 327 *handlep = handle; 328 if (basep != NULL) 329 *basep = base; 330 if (sizep != NULL) 331 *sizep = size; 332 333 return (0); 334 } 335