| 1 | /* $NetBSD: cardbus_map.c,v 1.36 2010/03/15 19:48:31 dyoung Exp $ */ |
| 2 | |
| 3 | /* |
| 4 | * Copyright (c) 1999 and 2000 |
| 5 | * HAYAKAWA Koichi. All rights reserved. |
| 6 | * |
| 7 | * Redistribution and use in source and binary forms, with or without |
| 8 | * modification, are permitted provided that the following conditions |
| 9 | * are met: |
| 10 | * 1. Redistributions of source code must retain the above copyright |
| 11 | * notice, this list of conditions and the following disclaimer. |
| 12 | * 2. Redistributions in binary form must reproduce the above copyright |
| 13 | * notice, this list of conditions and the following disclaimer in the |
| 14 | * documentation and/or other materials provided with the distribution. |
| 15 | * |
| 16 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR |
| 17 | * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| 18 | * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| 19 | * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, |
| 20 | * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| 21 | * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR |
| 22 | * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 23 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, |
| 24 | * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN |
| 25 | * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE |
| 26 | * POSSIBILITY OF SUCH DAMAGE. |
| 27 | */ |
| 28 | |
| 29 | #include <sys/cdefs.h> |
| 30 | __KERNEL_RCSID(0, "$NetBSD: cardbus_map.c,v 1.36 2010/03/15 19:48:31 dyoung Exp $" ); |
| 31 | |
| 32 | #include <sys/param.h> |
| 33 | #include <sys/systm.h> |
| 34 | #include <sys/device.h> |
| 35 | |
| 36 | #include <sys/bus.h> |
| 37 | |
| 38 | #include <dev/cardbus/cardbusvar.h> |
| 39 | |
| 40 | #include <dev/pci/pcireg.h> /* XXX */ |
| 41 | |
| 42 | #if defined DEBUG && !defined CARDBUS_MAP_DEBUG |
| 43 | #define CARDBUS_MAP_DEBUG |
| 44 | #endif |
| 45 | |
| 46 | #if defined CARDBUS_MAP_DEBUG |
| 47 | #define STATIC |
| 48 | #define DPRINTF(a) printf a |
| 49 | #else |
| 50 | #define STATIC static |
| 51 | #define DPRINTF(a) |
| 52 | #endif |
| 53 | |
| 54 | |
| 55 | static int cardbus_io_find(cardbus_chipset_tag_t, cardbus_function_tag_t, |
| 56 | pcitag_t, int, pcireg_t, |
| 57 | bus_addr_t *, bus_size_t *, int *); |
| 58 | static int cardbus_mem_find(cardbus_chipset_tag_t, cardbus_function_tag_t, |
| 59 | pcitag_t, int, pcireg_t, |
| 60 | bus_addr_t *, bus_size_t *, int *); |
| 61 | |
| 62 | /* |
| 63 | * static int cardbus_io_find(cardbus_chipset_tag_t cc, |
| 64 | * cardbus_function_tag_t cf, pcitag_t tag, |
| 65 | * int reg, pcireg_t type, bus_addr_t *basep, |
| 66 | * bus_size_t *sizep, int *flagsp) |
| 67 | * This code is stolen from sys/dev/pci_map.c. |
| 68 | */ |
| 69 | static int |
| 70 | cardbus_io_find( |
| 71 | cardbus_chipset_tag_t cc, |
| 72 | cardbus_function_tag_t cf, |
| 73 | pcitag_t tag, |
| 74 | int reg, |
| 75 | pcireg_t type, |
| 76 | bus_addr_t *basep, |
| 77 | bus_size_t *sizep, |
| 78 | int *flagsp) |
| 79 | { |
| 80 | pcireg_t address, mask; |
| 81 | int s; |
| 82 | |
| 83 | /* EXT ROM is able to map on memory space ONLY. */ |
| 84 | if (reg == CARDBUS_ROM_REG) { |
| 85 | return 1; |
| 86 | } |
| 87 | |
| 88 | if(reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3)) { |
| 89 | panic("cardbus_io_find: bad request" ); |
| 90 | } |
| 91 | |
| 92 | /* |
| 93 | * Section 6.2.5.1, `Address Maps', tells us that: |
| 94 | * |
| 95 | * 1) The builtin software should have already mapped the device in a |
| 96 | * reasonable way. |
| 97 | * |
| 98 | * 2) A device which wants 2^n bytes of memory will hardwire the bottom |
| 99 | * n bits of the address to 0. As recommended, we write all 1s and see |
| 100 | * what we get back. |
| 101 | */ |
| 102 | s = splhigh(); |
| 103 | address = cardbus_conf_read(cc, cf, tag, reg); |
| 104 | cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); |
| 105 | mask = cardbus_conf_read(cc, cf, tag, reg); |
| 106 | cardbus_conf_write(cc, cf, tag, reg, address); |
| 107 | splx(s); |
| 108 | |
| 109 | if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_IO) { |
| 110 | printf("cardbus_io_find: expected type i/o, found mem\n" ); |
| 111 | return 1; |
| 112 | } |
| 113 | |
| 114 | if (PCI_MAPREG_IO_SIZE(mask) == 0) { |
| 115 | printf("cardbus_io_find: void region\n" ); |
| 116 | return 1; |
| 117 | } |
| 118 | |
| 119 | if (basep != 0) { |
| 120 | *basep = PCI_MAPREG_IO_ADDR(address); |
| 121 | } |
| 122 | if (sizep != 0) { |
| 123 | *sizep = PCI_MAPREG_IO_SIZE(mask); |
| 124 | } |
| 125 | if (flagsp != 0) { |
| 126 | *flagsp = 0; |
| 127 | } |
| 128 | |
| 129 | return 0; |
| 130 | } |
| 131 | |
| 132 | |
| 133 | |
| 134 | /* |
| 135 | * static int cardbus_mem_find(cardbus_chipset_tag_t cc, |
| 136 | * cardbus_function_tag_t cf, pcitag_t tag, |
| 137 | * int reg, pcireg_t type, bus_addr_t *basep, |
| 138 | * bus_size_t *sizep, int *flagsp) |
| 139 | * This code is stolen from sys/dev/pci_map.c. |
| 140 | */ |
| 141 | static int |
| 142 | cardbus_mem_find(cardbus_chipset_tag_t cc, cardbus_function_tag_t cf, pcitag_t tag, int reg, pcireg_t type, bus_addr_t *basep, bus_size_t *sizep, int *flagsp) |
| 143 | { |
| 144 | pcireg_t address, mask; |
| 145 | int s; |
| 146 | |
| 147 | if (reg != CARDBUS_ROM_REG && |
| 148 | (reg < PCI_MAPREG_START || reg >= PCI_MAPREG_END || (reg & 3))) { |
| 149 | panic("cardbus_mem_find: bad request" ); |
| 150 | } |
| 151 | |
| 152 | /* |
| 153 | * Section 6.2.5.1, `Address Maps', tells us that: |
| 154 | * |
| 155 | * 1) The builtin software should have already mapped the device in a |
| 156 | * reasonable way. |
| 157 | * |
| 158 | * 2) A device which wants 2^n bytes of memory will hardwire the bottom |
| 159 | * n bits of the address to 0. As recommended, we write all 1s and see |
| 160 | * what we get back. |
| 161 | */ |
| 162 | s = splhigh(); |
| 163 | address = cardbus_conf_read(cc, cf, tag, reg); |
| 164 | cardbus_conf_write(cc, cf, tag, reg, 0xffffffff); |
| 165 | mask = cardbus_conf_read(cc, cf, tag, reg); |
| 166 | cardbus_conf_write(cc, cf, tag, reg, address); |
| 167 | splx(s); |
| 168 | |
| 169 | if (reg != CARDBUS_ROM_REG) { |
| 170 | /* memory space BAR */ |
| 171 | |
| 172 | if (PCI_MAPREG_TYPE(address) != PCI_MAPREG_TYPE_MEM) { |
| 173 | printf("cardbus_mem_find: expected type mem, found i/o\n" ); |
| 174 | return 1; |
| 175 | } |
| 176 | if (PCI_MAPREG_MEM_TYPE(address) != PCI_MAPREG_MEM_TYPE(type)) { |
| 177 | printf("cardbus_mem_find: expected mem type %08x, found %08x\n" , |
| 178 | PCI_MAPREG_MEM_TYPE(type), |
| 179 | PCI_MAPREG_MEM_TYPE(address)); |
| 180 | return 1; |
| 181 | } |
| 182 | } |
| 183 | |
| 184 | if (PCI_MAPREG_MEM_SIZE(mask) == 0) { |
| 185 | printf("cardbus_mem_find: void region\n" ); |
| 186 | return 1; |
| 187 | } |
| 188 | |
| 189 | switch (PCI_MAPREG_MEM_TYPE(address)) { |
| 190 | case PCI_MAPREG_MEM_TYPE_32BIT: |
| 191 | case PCI_MAPREG_MEM_TYPE_32BIT_1M: |
| 192 | break; |
| 193 | case PCI_MAPREG_MEM_TYPE_64BIT: |
| 194 | printf("cardbus_mem_find: 64-bit memory mapping register\n" ); |
| 195 | return 1; |
| 196 | default: |
| 197 | printf("cardbus_mem_find: reserved mapping register type\n" ); |
| 198 | return 1; |
| 199 | } |
| 200 | |
| 201 | if (basep != 0) { |
| 202 | *basep = PCI_MAPREG_MEM_ADDR(address); |
| 203 | } |
| 204 | if (sizep != 0) { |
| 205 | *sizep = PCI_MAPREG_MEM_SIZE(mask); |
| 206 | } |
| 207 | if (flagsp != 0) { |
| 208 | *flagsp = PCI_MAPREG_MEM_PREFETCHABLE(address) ? |
| 209 | BUS_SPACE_MAP_PREFETCHABLE : 0; |
| 210 | } |
| 211 | |
| 212 | return 0; |
| 213 | } |
| 214 | |
| 215 | |
| 216 | |
| 217 | |
| 218 | /* |
| 219 | * int cardbus_mapreg_map(struct cardbus_softc *, int, int, pcireg_t, |
| 220 | * int bus_space_tag_t *, bus_space_handle_t *, |
| 221 | * bus_addr_t *, bus_size_t *) |
| 222 | * This function maps bus-space on the value of Base Address |
| 223 | * Register (BAR) indexed by the argument `reg' (the second argument). |
| 224 | * When the value of the BAR is not valid, such as 0x00000000, a new |
| 225 | * address should be allocated for the BAR and new address values is |
| 226 | * written on the BAR. |
| 227 | */ |
| 228 | int |
| 229 | cardbus_mapreg_map(struct cardbus_softc *sc, int func, int reg, pcireg_t type, int busflags, bus_space_tag_t *tagp, bus_space_handle_t *handlep, bus_addr_t *basep, bus_size_t *sizep) |
| 230 | { |
| 231 | cardbus_chipset_tag_t cc = sc->sc_cc; |
| 232 | cardbus_function_tag_t cf = sc->sc_cf; |
| 233 | bus_space_tag_t bustag; |
| 234 | rbus_tag_t rbustag; |
| 235 | bus_space_handle_t handle; |
| 236 | bus_addr_t base; |
| 237 | bus_size_t size; |
| 238 | int flags; |
| 239 | int status = 0; |
| 240 | pcitag_t tag; |
| 241 | |
| 242 | size = 0; /* XXX gcc */ |
| 243 | flags = 0; /* XXX gcc */ |
| 244 | |
| 245 | tag = cardbus_make_tag(cc, cf, sc->sc_bus, func); |
| 246 | |
| 247 | DPRINTF(("cardbus_mapreg_map called: %s %x\n" , device_xname(sc->sc_dev), |
| 248 | type)); |
| 249 | |
| 250 | if (PCI_MAPREG_TYPE(type) == PCI_MAPREG_TYPE_IO) { |
| 251 | if (cardbus_io_find(cc, cf, tag, reg, type, &base, &size, &flags)) { |
| 252 | status = 1; |
| 253 | } |
| 254 | bustag = sc->sc_iot; |
| 255 | rbustag = sc->sc_rbus_iot; |
| 256 | } else { |
| 257 | if (cardbus_mem_find(cc, cf, tag, reg, type, &base, &size, &flags)){ |
| 258 | status = 1; |
| 259 | } |
| 260 | bustag = sc->sc_memt; |
| 261 | rbustag = sc->sc_rbus_memt; |
| 262 | } |
| 263 | if (status == 0) { |
| 264 | bus_addr_t mask = size - 1; |
| 265 | if (base != 0) { |
| 266 | mask = 0xffffffff; |
| 267 | } |
| 268 | if ((*cf->cardbus_space_alloc)(cc, rbustag, base, size, mask, |
| 269 | size, busflags | flags, &base, &handle)) { |
| 270 | panic("io alloc" ); |
| 271 | } |
| 272 | } |
| 273 | cardbus_conf_write(cc, cf, tag, reg, base); |
| 274 | |
| 275 | DPRINTF(("cardbus_mapreg_map: physaddr %lx\n" , (unsigned long)base)); |
| 276 | |
| 277 | if (tagp != 0) { |
| 278 | *tagp = bustag; |
| 279 | } |
| 280 | if (handlep != 0) { |
| 281 | *handlep = handle; |
| 282 | } |
| 283 | if (basep != 0) { |
| 284 | *basep = base; |
| 285 | } |
| 286 | if (sizep != 0) { |
| 287 | *sizep = size; |
| 288 | } |
| 289 | |
| 290 | return 0; |
| 291 | } |
| 292 | |
| 293 | |
| 294 | |
| 295 | |
| 296 | |
| 297 | /* |
| 298 | * int cardbus_mapreg_unmap(struct cardbus_softc *sc, int func, int reg, |
| 299 | * bus_space_tag_t tag, bus_space_handle_t handle, |
| 300 | * bus_size_t size) |
| 301 | * |
| 302 | * This function releases bus-space region and close memory or io |
| 303 | * window on the bridge. |
| 304 | * |
| 305 | * Arguments: |
| 306 | * struct cardbus_softc *sc; the pointer to the device structure of cardbus. |
| 307 | * int func; the number of function on the device. |
| 308 | * int reg; the offset of BAR register. |
| 309 | */ |
| 310 | int |
| 311 | cardbus_mapreg_unmap(struct cardbus_softc *sc, int func, int reg, bus_space_tag_t tag, bus_space_handle_t handle, bus_size_t size) |
| 312 | { |
| 313 | cardbus_chipset_tag_t cc = sc->sc_cc; |
| 314 | cardbus_function_tag_t cf = sc->sc_cf; |
| 315 | int st = 1; |
| 316 | pcitag_t cardbustag; |
| 317 | rbus_tag_t rbustag; |
| 318 | |
| 319 | if (sc->sc_iot == tag) { |
| 320 | /* bus space is io space */ |
| 321 | DPRINTF(("%s: unmap i/o space\n" , device_xname(sc->sc_dev))); |
| 322 | rbustag = sc->sc_rbus_iot; |
| 323 | } else if (sc->sc_memt == tag) { |
| 324 | /* bus space is memory space */ |
| 325 | DPRINTF(("%s: unmap mem space\n" , device_xname(sc->sc_dev))); |
| 326 | rbustag = sc->sc_rbus_memt; |
| 327 | } else { |
| 328 | return 1; |
| 329 | } |
| 330 | |
| 331 | cardbustag = cardbus_make_tag(cc, cf, sc->sc_bus, func); |
| 332 | |
| 333 | cardbus_conf_write(cc, cf, cardbustag, reg, 0); |
| 334 | |
| 335 | (*cf->cardbus_space_free)(cc, rbustag, handle, size); |
| 336 | |
| 337 | return st; |
| 338 | } |
| 339 | |