| 1 | /* |
| 2 | * FreeBSD, PCI product support functions |
| 3 | * |
| 4 | * Copyright (c) 1995-2001 Justin T. Gibbs |
| 5 | * 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 | * without modification, immediately at the beginning of the file. |
| 13 | * 2. The name of the author may not be used to endorse or promote products |
| 14 | * derived from this software without specific prior written permission. |
| 15 | * |
| 16 | * Alternatively, this software may be distributed under the terms of the |
| 17 | * GNU Public License ("GPL"). |
| 18 | * |
| 19 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
| 20 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
| 21 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
| 22 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR |
| 23 | * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
| 24 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
| 25 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
| 26 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
| 27 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
| 28 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
| 29 | * SUCH DAMAGE. |
| 30 | * |
| 31 | * $Id: //depot/aic7xxx/freebsd/dev/aic7xxx/ahd_pci.c#17 $ |
| 32 | * |
| 33 | * $FreeBSD: src/sys/dev/aic7xxx/ahd_pci.c,v 1.16 2004/03/17 17:50:25 njl Exp $ |
| 34 | * $DragonFly: src/sys/dev/disk/aic7xxx/ahd_pci.c,v 1.10 2007/07/06 01:11:07 pavalos Exp $ |
| 35 | */ |
| 36 | |
| 37 | #include "aic79xx_osm.h" |
| 38 | |
| 39 | static int ahd_pci_probe(device_t dev); |
| 40 | static int ahd_pci_attach(device_t dev); |
| 41 | |
| 42 | static device_method_t ahd_pci_device_methods[] = { |
| 43 | /* Device interface */ |
| 44 | DEVMETHOD(device_probe, ahd_pci_probe), |
| 45 | DEVMETHOD(device_attach, ahd_pci_attach), |
| 46 | DEVMETHOD(device_detach, ahd_detach), |
| 47 | { 0, 0 } |
| 48 | }; |
| 49 | |
| 50 | static driver_t ahd_pci_driver = { |
| 51 | "ahd", |
| 52 | ahd_pci_device_methods, |
| 53 | sizeof(struct ahd_softc) |
| 54 | }; |
| 55 | |
| 56 | static devclass_t ahd_devclass; |
| 57 | |
| 58 | DRIVER_MODULE(ahd, pci, ahd_pci_driver, ahd_devclass, 0, 0); |
| 59 | DRIVER_MODULE(ahd, cardbus, ahd_pci_driver, ahd_devclass, 0, 0); |
| 60 | MODULE_DEPEND(ahd_pci, ahd, 1, 1, 1); |
| 61 | MODULE_VERSION(ahd_pci, 1); |
| 62 | |
| 63 | static int |
| 64 | ahd_pci_probe(device_t dev) |
| 65 | { |
| 66 | struct ahd_pci_identity *entry; |
| 67 | |
| 68 | entry = ahd_find_pci_device(dev); |
| 69 | if (entry != NULL) { |
| 70 | device_set_desc(dev, entry->name); |
| 71 | return (0); |
| 72 | } |
| 73 | return (ENXIO); |
| 74 | } |
| 75 | |
| 76 | static int |
| 77 | ahd_pci_attach(device_t dev) |
| 78 | { |
| 79 | struct ahd_pci_identity *entry; |
| 80 | struct ahd_softc *ahd; |
| 81 | char *name; |
| 82 | int error; |
| 83 | |
| 84 | entry = ahd_find_pci_device(dev); |
| 85 | if (entry == NULL) |
| 86 | return (ENXIO); |
| 87 | |
| 88 | /* |
| 89 | * Allocate a softc for this card and |
| 90 | * set it up for attachment by our |
| 91 | * common detect routine. |
| 92 | */ |
| 93 | name = kmalloc(strlen(device_get_nameunit(dev)) + 1, M_DEVBUF, M_WAITOK); |
| 94 | strcpy(name, device_get_nameunit(dev)); |
| 95 | ahd = ahd_alloc(dev, name); |
| 96 | if (ahd == NULL) |
| 97 | return (ENOMEM); |
| 98 | |
| 99 | ahd_set_unit(ahd, device_get_unit(dev)); |
| 100 | |
| 101 | /* |
| 102 | * Should we bother disabling 39Bit addressing |
| 103 | * based on installed memory? |
| 104 | */ |
| 105 | if (sizeof(bus_addr_t) > 4) |
| 106 | ahd->flags |= AHD_39BIT_ADDRESSING; |
| 107 | |
| 108 | /* Allocate a dmatag for our SCB DMA maps */ |
| 109 | /* XXX Should be a child of the PCI bus dma tag */ |
| 110 | error = aic_dma_tag_create(ahd, /*parent*/NULL, /*alignment*/1, |
| 111 | /*boundary*/0, |
| 112 | (ahd->flags & AHD_39BIT_ADDRESSING) |
| 113 | ? 0x7FFFFFFFFFULL |
| 114 | : BUS_SPACE_MAXADDR_32BIT, |
| 115 | /*highaddr*/BUS_SPACE_MAXADDR, |
| 116 | /*filter*/NULL, /*filterarg*/NULL, |
| 117 | /*maxsize*/BUS_SPACE_MAXSIZE_32BIT, |
| 118 | /*nsegments*/AHD_NSEG, |
| 119 | /*maxsegsz*/AHD_MAXTRANSFER_SIZE, |
| 120 | /*flags*/0, |
| 121 | &ahd->parent_dmat); |
| 122 | |
| 123 | if (error != 0) { |
| 124 | kprintf("ahd_pci_attach: Could not allocate DMA tag " |
| 125 | "- error %d\n", error); |
| 126 | ahd_free(ahd); |
| 127 | return (ENOMEM); |
| 128 | } |
| 129 | ahd->dev_softc = dev; |
| 130 | error = ahd_pci_config(ahd, entry); |
| 131 | if (error != 0) { |
| 132 | ahd_free(ahd); |
| 133 | return (error); |
| 134 | } |
| 135 | |
| 136 | ahd_attach(ahd); |
| 137 | return (0); |
| 138 | } |
| 139 | |
| 140 | int |
| 141 | ahd_pci_map_registers(struct ahd_softc *ahd) |
| 142 | { |
| 143 | struct resource *regs; |
| 144 | struct resource *regs2; |
| 145 | u_int command; |
| 146 | int regs_type; |
| 147 | int regs_id; |
| 148 | int regs_id2; |
| 149 | int allow_memio; |
| 150 | |
| 151 | command = aic_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1); |
| 152 | regs = NULL; |
| 153 | regs2 = NULL; |
| 154 | regs_type = 0; |
| 155 | regs_id = 0; |
| 156 | |
| 157 | /* Retrieve the per-device 'allow_memio' hint */ |
| 158 | if (resource_int_value(device_get_name(ahd->dev_softc), |
| 159 | device_get_unit(ahd->dev_softc), |
| 160 | "allow_memio", &allow_memio) != 0) { |
| 161 | if (bootverbose) |
| 162 | device_printf(ahd->dev_softc, |
| 163 | "Defaulting to MEMIO on\n"); |
| 164 | } |
| 165 | |
| 166 | if ((command & PCIM_CMD_MEMEN) != 0 |
| 167 | && (ahd->bugs & AHD_PCIX_MMAPIO_BUG) == 0 |
| 168 | && allow_memio != 0) { |
| 169 | |
| 170 | regs_type = SYS_RES_MEMORY; |
| 171 | regs_id = AHD_PCI_MEMADDR; |
| 172 | regs = bus_alloc_resource_any(ahd->dev_softc, regs_type, |
| 173 | ®s_id, RF_ACTIVE); |
| 174 | if (regs != NULL) { |
| 175 | int error; |
| 176 | |
| 177 | ahd->tags[0] = rman_get_bustag(regs); |
| 178 | ahd->bshs[0] = rman_get_bushandle(regs); |
| 179 | ahd->tags[1] = ahd->tags[0]; |
| 180 | error = bus_space_subregion(ahd->tags[0], ahd->bshs[0], |
| 181 | /*offset*/0x100, |
| 182 | /*size*/0x100, |
| 183 | &ahd->bshs[1]); |
| 184 | /* |
| 185 | * Do a quick test to see if memory mapped |
| 186 | * I/O is functioning correctly. |
| 187 | */ |
| 188 | if (error != 0 |
| 189 | || ahd_pci_test_register_access(ahd) != 0) { |
| 190 | device_printf(ahd->dev_softc, |
| 191 | "PCI Device %d:%d:%d failed memory " |
| 192 | "mapped test. Using PIO.\n", |
| 193 | aic_get_pci_bus(ahd->dev_softc), |
| 194 | aic_get_pci_slot(ahd->dev_softc), |
| 195 | aic_get_pci_function(ahd->dev_softc)); |
| 196 | bus_release_resource(ahd->dev_softc, regs_type, |
| 197 | regs_id, regs); |
| 198 | regs = NULL; |
| 199 | } else { |
| 200 | command &= ~PCIM_CMD_PORTEN; |
| 201 | aic_pci_write_config(ahd->dev_softc, |
| 202 | PCIR_COMMAND, |
| 203 | command, /*bytes*/1); |
| 204 | } |
| 205 | } |
| 206 | } |
| 207 | if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { |
| 208 | regs_type = SYS_RES_IOPORT; |
| 209 | regs_id = AHD_PCI_IOADDR0; |
| 210 | regs = bus_alloc_resource_any(ahd->dev_softc, regs_type, |
| 211 | ®s_id, RF_ACTIVE); |
| 212 | if (regs == NULL) { |
| 213 | device_printf(ahd->dev_softc, |
| 214 | "can't allocate register resources\n"); |
| 215 | return (ENOMEM); |
| 216 | } |
| 217 | ahd->tags[0] = rman_get_bustag(regs); |
| 218 | ahd->bshs[0] = rman_get_bushandle(regs); |
| 219 | |
| 220 | /* And now the second BAR */ |
| 221 | regs_id2 = AHD_PCI_IOADDR1; |
| 222 | regs2 = bus_alloc_resource_any(ahd->dev_softc, regs_type, |
| 223 | ®s_id2, RF_ACTIVE); |
| 224 | if (regs2 == NULL) { |
| 225 | device_printf(ahd->dev_softc, |
| 226 | "can't allocate register resources\n"); |
| 227 | return (ENOMEM); |
| 228 | } |
| 229 | ahd->tags[1] = rman_get_bustag(regs2); |
| 230 | ahd->bshs[1] = rman_get_bushandle(regs2); |
| 231 | command &= ~PCIM_CMD_MEMEN; |
| 232 | aic_pci_write_config(ahd->dev_softc, PCIR_COMMAND, |
| 233 | command, /*bytes*/1); |
| 234 | ahd->platform_data->regs_res_type[1] = regs_type; |
| 235 | ahd->platform_data->regs_res_id[1] = regs_id2; |
| 236 | ahd->platform_data->regs[1] = regs2; |
| 237 | } |
| 238 | ahd->platform_data->regs_res_type[0] = regs_type; |
| 239 | ahd->platform_data->regs_res_id[0] = regs_id; |
| 240 | ahd->platform_data->regs[0] = regs; |
| 241 | return (0); |
| 242 | } |
| 243 | |
| 244 | int |
| 245 | ahd_pci_map_int(struct ahd_softc *ahd) |
| 246 | { |
| 247 | int zero; |
| 248 | |
| 249 | zero = 0; |
| 250 | ahd->platform_data->irq = |
| 251 | bus_alloc_resource_any(ahd->dev_softc, SYS_RES_IRQ, &zero, |
| 252 | RF_ACTIVE | RF_SHAREABLE); |
| 253 | if (ahd->platform_data->irq == NULL) |
| 254 | return (ENOMEM); |
| 255 | ahd->platform_data->irq_res_type = SYS_RES_IRQ; |
| 256 | return (ahd_map_int(ahd)); |
| 257 | } |