ahd_pci.c revision 97883
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$ 32 * 33 * $FreeBSD: head/sys/dev/aic7xxx/ahd_pci.c 97883 2002-06-05 19:52:45Z gibbs $ 34 */ 35 36#include <dev/aic7xxx/aic79xx_osm.h> 37 38#define AHD_PCI_IOADDR0 PCIR_MAPS /* Primary I/O BAR */ 39#define AHD_PCI_MEMADDR (PCIR_MAPS + 4) /* Mem I/O Address */ 40#define AHD_PCI_IOADDR1 (PCIR_MAPS + 12)/* Secondary I/O BAR */ 41 42static int ahd_pci_probe(device_t dev); 43static int ahd_pci_attach(device_t dev); 44 45static device_method_t ahd_pci_device_methods[] = { 46 /* Device interface */ 47 DEVMETHOD(device_probe, ahd_pci_probe), 48 DEVMETHOD(device_attach, ahd_pci_attach), 49 DEVMETHOD(device_detach, ahd_detach), 50 { 0, 0 } 51}; 52 53#if __FreeBSD_version >= 500027 54 55DEVINTERFACE(ahd_pci_device, ahd_pci_device_methods, device); 56 57static device_interface_t *ahd_pci_interfaces[] = { 58 &ahd_pci_device_interface, 59 60 NULL 61}; 62 63static driver_t ahd_pci_driver = { 64 "ahd", 65 ahd_pci_interfaces, 66 sizeof(struct ahd_softc) 67}; 68 69#else /* FreeBSD 4.X */ 70 71static driver_t ahd_pci_driver = { 72 "ahd", 73 ahd_pci_device_methods, 74 sizeof(struct ahd_softc) 75}; 76 77#endif /* __FreeBSD_version */ 78 79static devclass_t ahd_devclass; 80 81DRIVER_MODULE(ahd, pci, ahd_pci_driver, ahd_devclass, 0, 0); 82DRIVER_MODULE(ahd, cardbus, ahd_pci_driver, ahd_devclass, 0, 0); 83MODULE_DEPEND(ahd_pci, ahd, 1, 1, 1); 84MODULE_VERSION(ahd_pci, 1); 85 86static int 87ahd_pci_probe(device_t dev) 88{ 89 struct ahd_pci_identity *entry; 90 91 entry = ahd_find_pci_device(dev); 92 if (entry != NULL) { 93 device_set_desc(dev, entry->name); 94 return (0); 95 } 96 return (ENXIO); 97} 98 99static int 100ahd_pci_attach(device_t dev) 101{ 102 struct ahd_pci_identity *entry; 103 struct ahd_softc *ahd; 104 char *name; 105 int error; 106 107 entry = ahd_find_pci_device(dev); 108 if (entry == NULL) 109 return (ENXIO); 110 111 /* 112 * Allocate a softc for this card and 113 * set it up for attachment by our 114 * common detect routine. 115 */ 116 name = malloc(strlen(device_get_nameunit(dev)) + 1, M_DEVBUF, M_NOWAIT); 117 if (name == NULL) 118 return (ENOMEM); 119 strcpy(name, device_get_nameunit(dev)); 120 ahd = ahd_alloc(dev, name); 121 if (ahd == NULL) 122 return (ENOMEM); 123 124 ahd_set_unit(ahd, device_get_unit(dev)); 125 126 /* 127 * Should we bother disabling 39Bit addressing 128 * based on installed memory? 129 */ 130 if (sizeof(bus_addr_t) > 4) 131 ahd->flags |= AHD_39BIT_ADDRESSING; 132 133 /* Allocate a dmatag for our SCB DMA maps */ 134 /* XXX Should be a child of the PCI bus dma tag */ 135 error = bus_dma_tag_create(/*parent*/NULL, /*alignment*/1, 136 /*boundary*/0, 137 (ahd->flags & AHD_39BIT_ADDRESSING) 138 ? 0x7FFFFFFFFF 139 : BUS_SPACE_MAXADDR_32BIT, 140 /*highaddr*/BUS_SPACE_MAXADDR, 141 /*filter*/NULL, /*filterarg*/NULL, 142 /*maxsize*/MAXBSIZE, /*nsegments*/AHD_NSEG, 143 /*maxsegsz*/AHD_MAXTRANSFER_SIZE, 144 /*flags*/BUS_DMA_ALLOCNOW, 145 &ahd->parent_dmat); 146 147 if (error != 0) { 148 printf("ahd_pci_attach: Could not allocate DMA tag " 149 "- error %d\n", error); 150 ahd_free(ahd); 151 return (ENOMEM); 152 } 153 ahd->dev_softc = dev; 154 error = ahd_pci_config(ahd, entry); 155 if (error != 0) { 156 ahd_free(ahd); 157 return (error); 158 } 159 160 ahd_attach(ahd); 161 return (0); 162} 163 164int 165ahd_pci_map_registers(struct ahd_softc *ahd) 166{ 167 struct resource *regs; 168 struct resource *regs2; 169 u_int command; 170 int regs_type; 171 int regs_id; 172 int regs_id2; 173 174 command = ahd_pci_read_config(ahd->dev_softc, PCIR_COMMAND, /*bytes*/1); 175 regs = NULL; 176 regs2 = NULL; 177 regs_type = 0; 178 regs_id = 0; 179 if ((command & PCIM_CMD_MEMEN) != 0) { 180 181 regs_type = SYS_RES_MEMORY; 182 regs_id = AHD_PCI_MEMADDR; 183 regs = bus_alloc_resource(ahd->dev_softc, regs_type, 184 ®s_id, 0, ~0, 1, RF_ACTIVE); 185 if (regs != NULL) { 186 int error; 187 188 ahd->tags[0] = rman_get_bustag(regs); 189 ahd->bshs[0] = rman_get_bushandle(regs); 190 ahd->tags[1] = ahd->tags[0]; 191 error = bus_space_subregion(ahd->tags[0], ahd->bshs[0], 192 /*offset*/0x100, 193 /*size*/0x100, 194 &ahd->bshs[1]); 195 /* 196 * Do a quick test to see if memory mapped 197 * I/O is functioning correctly. 198 */ 199 if (error != 0 || ahd_inb(ahd, HCNTRL) == 0xFF) { 200 device_printf(ahd->dev_softc, 201 "PCI Device %d:%d:%d failed memory " 202 "mapped test. Using PIO.\n", 203 ahd_get_pci_bus(ahd->dev_softc), 204 ahd_get_pci_slot(ahd->dev_softc), 205 ahd_get_pci_function(ahd->dev_softc)); 206 bus_release_resource(ahd->dev_softc, regs_type, 207 regs_id, regs); 208 regs = NULL; 209 } else { 210 command &= ~PCIM_CMD_PORTEN; 211 ahd_pci_write_config(ahd->dev_softc, 212 PCIR_COMMAND, 213 command, /*bytes*/1); 214 } 215 } 216 } 217 if (regs == NULL && (command & PCIM_CMD_PORTEN) != 0) { 218 regs_type = SYS_RES_IOPORT; 219 regs_id = AHD_PCI_IOADDR0; 220 regs = bus_alloc_resource(ahd->dev_softc, regs_type, 221 ®s_id, 0, ~0, 1, RF_ACTIVE); 222 if (regs == NULL) { 223 device_printf(ahd->dev_softc, 224 "can't allocate register resources\n"); 225 return (ENOMEM); 226 } 227 ahd->tags[0] = rman_get_bustag(regs); 228 ahd->bshs[0] = rman_get_bushandle(regs); 229 230 /* And now the second BAR */ 231 regs_id2 = AHD_PCI_IOADDR1; 232 regs2 = bus_alloc_resource(ahd->dev_softc, regs_type, 233 ®s_id2, 0, ~0, 1, RF_ACTIVE); 234 if (regs2 == NULL) { 235 device_printf(ahd->dev_softc, 236 "can't allocate register resources\n"); 237 return (ENOMEM); 238 } 239 ahd->tags[1] = rman_get_bustag(regs2); 240 ahd->bshs[1] = rman_get_bushandle(regs2); 241 command &= ~PCIM_CMD_MEMEN; 242 ahd_pci_write_config(ahd->dev_softc, PCIR_COMMAND, 243 command, /*bytes*/1); 244 ahd->platform_data->regs_res_type[1] = regs_type; 245 ahd->platform_data->regs_res_id[1] = regs_id2; 246 ahd->platform_data->regs[1] = regs2; 247 } 248 ahd->platform_data->regs_res_type[0] = regs_type; 249 ahd->platform_data->regs_res_id[0] = regs_id; 250 ahd->platform_data->regs[0] = regs; 251 return (0); 252} 253 254int 255ahd_pci_map_int(struct ahd_softc *ahd) 256{ 257 int zero; 258 259 zero = 0; 260 ahd->platform_data->irq = 261 bus_alloc_resource(ahd->dev_softc, SYS_RES_IRQ, &zero, 262 0, ~0, 1, RF_ACTIVE | RF_SHAREABLE); 263 if (ahd->platform_data->irq == NULL) 264 return (ENOMEM); 265 ahd->platform_data->irq_res_type = SYS_RES_IRQ; 266 return (ahd_map_int(ahd)); 267} 268 269void 270ahd_power_state_change(struct ahd_softc *ahd, ahd_power_state new_state) 271{ 272 uint32_t cap; 273 u_int cap_offset; 274 275 /* 276 * Traverse the capability list looking for 277 * the power management capability. 278 */ 279 cap = 0; 280 cap_offset = ahd_pci_read_config(ahd->dev_softc, 281 PCIR_CAP_PTR, /*bytes*/1); 282 while (cap_offset != 0) { 283 284 cap = ahd_pci_read_config(ahd->dev_softc, 285 cap_offset, /*bytes*/4); 286 if ((cap & 0xFF) == 1 287 && ((cap >> 16) & 0x3) > 0) { 288 uint32_t pm_control; 289 290 pm_control = ahd_pci_read_config(ahd->dev_softc, 291 cap_offset + 4, 292 /*bytes*/2); 293 pm_control &= ~0x3; 294 pm_control |= new_state; 295 ahd_pci_write_config(ahd->dev_softc, 296 cap_offset + 4, 297 pm_control, /*bytes*/2); 298 break; 299 } 300 cap_offset = (cap >> 8) & 0xFF; 301 } 302} 303