| Commit | Line | Data |
|---|---|---|
| 984263bc MD |
1 | /*- |
| 2 | * Copyright (c) 1999,2000 Michael Smith | |
| 3 | * Copyright (c) 2000 BSDi | |
| 4 | * All rights reserved. | |
| 5 | * | |
| 6 | * Redistribution and use in source and binary forms, with or without | |
| 7 | * modification, are permitted provided that the following conditions | |
| 8 | * are met: | |
| 9 | * 1. Redistributions of source code must retain the above copyright | |
| 10 | * notice, this list of conditions and the following disclaimer. | |
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 12 | * notice, this list of conditions and the following disclaimer in the | |
| 13 | * documentation and/or other materials provided with the distribution. | |
| 14 | * | |
| 15 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 16 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 17 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 18 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 19 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 20 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 21 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 22 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 23 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 24 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 25 | * SUCH DAMAGE. | |
| 1fcd0ba2 SW |
26 | */ |
| 27 | /*- | |
| 984263bc | 28 | * Copyright (c) 2002 Eric Moore |
| 1fcd0ba2 | 29 | * Copyright (c) 2002, 2004 LSI Logic Corporation |
| 984263bc MD |
30 | * All rights reserved. |
| 31 | * | |
| 32 | * Redistribution and use in source and binary forms, with or without | |
| 33 | * modification, are permitted provided that the following conditions | |
| 34 | * are met: | |
| 35 | * 1. Redistributions of source code must retain the above copyright | |
| 36 | * notice, this list of conditions and the following disclaimer. | |
| 37 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 38 | * notice, this list of conditions and the following disclaimer in the | |
| 39 | * documentation and/or other materials provided with the distribution. | |
| 40 | * 3. The party using or redistributing the source code and binary forms | |
| 41 | * agrees to the disclaimer below and the terms and conditions set forth | |
| 42 | * herein. | |
| 43 | * | |
| 44 | * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND | |
| 45 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| 46 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| 47 | * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE | |
| 48 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | |
| 49 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | |
| 50 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | |
| 51 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | |
| 52 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | |
| 53 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 54 | * SUCH DAMAGE. | |
| 55 | * | |
| 1fcd0ba2 | 56 | * $FreeBSD: src/sys/dev/amr/amr_pci.c,v 1.40 2007/12/12 05:55:03 scottl Exp $ |
| 984263bc MD |
57 | */ |
| 58 | ||
| 59 | #include <sys/param.h> | |
| 60 | #include <sys/systm.h> | |
| 61 | #include <sys/kernel.h> | |
| 1fcd0ba2 SW |
62 | #include <sys/module.h> |
| 63 | #include <sys/sysctl.h> | |
| 984263bc | 64 | |
| 1fcd0ba2 | 65 | #include <sys/bio.h> |
| 984263bc MD |
66 | #include <sys/bus.h> |
| 67 | #include <sys/conf.h> | |
| 1fcd0ba2 | 68 | |
| 984263bc MD |
69 | #include <sys/rman.h> |
| 70 | ||
| 1f2de5d4 MD |
71 | #include <bus/pci/pcireg.h> |
| 72 | #include <bus/pci/pcivar.h> | |
| 984263bc | 73 | |
| 1fcd0ba2 SW |
74 | #include <dev/raid/amr/amrio.h> |
| 75 | #include <dev/raid/amr/amrreg.h> | |
| 76 | #include <dev/raid/amr/amrvar.h> | |
| 984263bc MD |
77 | |
| 78 | static int amr_pci_probe(device_t dev); | |
| 79 | static int amr_pci_attach(device_t dev); | |
| 80 | static int amr_pci_detach(device_t dev); | |
| 81 | static int amr_pci_shutdown(device_t dev); | |
| 82 | static int amr_pci_suspend(device_t dev); | |
| 83 | static int amr_pci_resume(device_t dev); | |
| 84 | static void amr_pci_intr(void *arg); | |
| 85 | static void amr_pci_free(struct amr_softc *sc); | |
| 1fcd0ba2 | 86 | static void amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error); |
| 984263bc | 87 | static int amr_sglist_map(struct amr_softc *sc); |
| 984263bc | 88 | static int amr_setup_mbox(struct amr_softc *sc); |
| 1fcd0ba2 SW |
89 | static int amr_ccb_map(struct amr_softc *sc); |
| 90 | ||
| 91 | static u_int amr_force_sg32 = 0; | |
| 92 | TUNABLE_INT("hw.amr.force_sg32", &amr_force_sg32); | |
| 93 | SYSCTL_DECL(_hw_amr); | |
| 94 | SYSCTL_UINT(_hw_amr, OID_AUTO, force_sg32, CTLFLAG_RD, &amr_force_sg32, 0, | |
| 95 | "Force the AMR driver to use 32bit scatter gather"); | |
| 984263bc MD |
96 | |
| 97 | static device_method_t amr_methods[] = { | |
| 98 | /* Device interface */ | |
| 99 | DEVMETHOD(device_probe, amr_pci_probe), | |
| 100 | DEVMETHOD(device_attach, amr_pci_attach), | |
| 101 | DEVMETHOD(device_detach, amr_pci_detach), | |
| 102 | DEVMETHOD(device_shutdown, amr_pci_shutdown), | |
| 103 | DEVMETHOD(device_suspend, amr_pci_suspend), | |
| 104 | DEVMETHOD(device_resume, amr_pci_resume), | |
| 105 | ||
| 106 | DEVMETHOD(bus_print_child, bus_generic_print_child), | |
| 107 | DEVMETHOD(bus_driver_added, bus_generic_driver_added), | |
| 108 | { 0, 0 } | |
| 109 | }; | |
| 110 | ||
| 111 | static driver_t amr_pci_driver = { | |
| 112 | "amr", | |
| 113 | amr_methods, | |
| 114 | sizeof(struct amr_softc) | |
| 115 | }; | |
| 116 | ||
| 117 | static devclass_t amr_devclass; | |
| 118 | DRIVER_MODULE(amr, pci, amr_pci_driver, amr_devclass, 0, 0); | |
| 1fcd0ba2 SW |
119 | MODULE_DEPEND(amr, pci, 1, 1, 1); |
| 120 | MODULE_DEPEND(amr, cam, 1, 1, 1); | |
| 984263bc | 121 | |
| 1fcd0ba2 | 122 | static struct amr_ident |
| 984263bc MD |
123 | { |
| 124 | int vendor; | |
| 125 | int device; | |
| 1fcd0ba2 SW |
126 | int flags; |
| 127 | #define AMR_ID_PROBE_SIG (1<<0) /* generic i960RD, check signature */ | |
| 128 | #define AMR_ID_DO_SG64 (1<<1) | |
| 129 | #define AMR_ID_QUARTZ (1<<2) | |
| 984263bc MD |
130 | } amr_device_ids[] = { |
| 131 | {0x101e, 0x9010, 0}, | |
| 132 | {0x101e, 0x9060, 0}, | |
| 1fcd0ba2 SW |
133 | {0x8086, 0x1960, AMR_ID_QUARTZ | AMR_ID_PROBE_SIG}, |
| 134 | {0x101e, 0x1960, AMR_ID_QUARTZ}, | |
| 135 | {0x1000, 0x1960, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, | |
| 136 | {0x1000, 0x0407, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, | |
| 137 | {0x1000, 0x0408, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, | |
| 138 | {0x1000, 0x0409, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, | |
| 139 | {0x1028, 0x000e, AMR_ID_QUARTZ | AMR_ID_DO_SG64 | AMR_ID_PROBE_SIG}, /* perc4/di i960 */ | |
| 140 | {0x1028, 0x000f, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di Verde*/ | |
| 141 | {0x1028, 0x0013, AMR_ID_QUARTZ | AMR_ID_DO_SG64}, /* perc4/di */ | |
| 984263bc MD |
142 | {0, 0, 0} |
| 143 | }; | |
| 144 | ||
| 1fcd0ba2 SW |
145 | static struct amr_ident * |
| 146 | amr_find_ident(device_t dev) | |
| 984263bc | 147 | { |
| 1fcd0ba2 SW |
148 | struct amr_ident *id; |
| 149 | int sig; | |
| 984263bc | 150 | |
| 1fcd0ba2 SW |
151 | for (id = amr_device_ids; id->vendor != 0; id++) { |
| 152 | if ((pci_get_vendor(dev) == id->vendor) && | |
| 153 | (pci_get_device(dev) == id->device)) { | |
| 984263bc MD |
154 | |
| 155 | /* do we need to test for a signature? */ | |
| 1fcd0ba2 | 156 | if (id->flags & AMR_ID_PROBE_SIG) { |
| 984263bc MD |
157 | sig = pci_read_config(dev, AMR_CFG_SIG, 2); |
| 158 | if ((sig != AMR_SIGNATURE_1) && (sig != AMR_SIGNATURE_2)) | |
| 159 | continue; | |
| 160 | } | |
| 1fcd0ba2 | 161 | return (id); |
| 984263bc MD |
162 | } |
| 163 | } | |
| 1fcd0ba2 SW |
164 | return (NULL); |
| 165 | } | |
| 166 | ||
| 167 | static int | |
| 168 | amr_pci_probe(device_t dev) | |
| 169 | { | |
| 170 | ||
| 171 | debug_called(1); | |
| 172 | ||
| 173 | if (amr_find_ident(dev) != NULL) { | |
| 174 | device_set_desc(dev, LSI_DESC_PCI); | |
| 175 | return(BUS_PROBE_DEFAULT); | |
| 176 | } | |
| 984263bc MD |
177 | return(ENXIO); |
| 178 | } | |
| 179 | ||
| 180 | static int | |
| 181 | amr_pci_attach(device_t dev) | |
| 182 | { | |
| 183 | struct amr_softc *sc; | |
| 1fcd0ba2 | 184 | struct amr_ident *id; |
| 984263bc MD |
185 | int rid, rtype, error; |
| 186 | u_int32_t command; | |
| 187 | ||
| 188 | debug_called(1); | |
| 189 | ||
| 190 | /* | |
| 191 | * Initialise softc. | |
| 192 | */ | |
| 193 | sc = device_get_softc(dev); | |
| 194 | bzero(sc, sizeof(*sc)); | |
| 195 | sc->amr_dev = dev; | |
| 196 | ||
| 197 | /* assume failure is 'not configured' */ | |
| 198 | error = ENXIO; | |
| 199 | ||
| 200 | /* | |
| 201 | * Determine board type. | |
| 202 | */ | |
| 1fcd0ba2 SW |
203 | if ((id = amr_find_ident(dev)) == NULL) |
| 204 | return (ENXIO); | |
| 205 | ||
| 984263bc | 206 | command = pci_read_config(dev, PCIR_COMMAND, 1); |
| 1fcd0ba2 | 207 | if (id->flags & AMR_ID_QUARTZ) { |
| 984263bc MD |
208 | /* |
| 209 | * Make sure we are going to be able to talk to this board. | |
| 210 | */ | |
| 211 | if ((command & PCIM_CMD_MEMEN) == 0) { | |
| 212 | device_printf(dev, "memory window not available\n"); | |
| 1fcd0ba2 | 213 | return (ENXIO); |
| 984263bc MD |
214 | } |
| 215 | sc->amr_type |= AMR_TYPE_QUARTZ; | |
| 984263bc MD |
216 | } else { |
| 217 | /* | |
| 218 | * Make sure we are going to be able to talk to this board. | |
| 219 | */ | |
| 220 | if ((command & PCIM_CMD_PORTEN) == 0) { | |
| 221 | device_printf(dev, "I/O window not available\n"); | |
| 1fcd0ba2 | 222 | return (ENXIO); |
| 984263bc MD |
223 | } |
| 224 | } | |
| 225 | ||
| 1fcd0ba2 SW |
226 | if ((amr_force_sg32 == 0) && (id->flags & AMR_ID_DO_SG64) && |
| 227 | (sizeof(vm_paddr_t) > 4)) { | |
| 228 | device_printf(dev, "Using 64-bit DMA\n"); | |
| 229 | sc->amr_type |= AMR_TYPE_SG64; | |
| 230 | } | |
| 231 | ||
| 984263bc MD |
232 | /* force the busmaster enable bit on */ |
| 233 | if (!(command & PCIM_CMD_BUSMASTEREN)) { | |
| 234 | device_printf(dev, "busmaster bit not set, enabling\n"); | |
| 235 | command |= PCIM_CMD_BUSMASTEREN; | |
| 236 | pci_write_config(dev, PCIR_COMMAND, command, 2); | |
| 237 | } | |
| 238 | ||
| 239 | /* | |
| 240 | * Allocate the PCI register window. | |
| 241 | */ | |
| 1fcd0ba2 | 242 | rid = PCIR_BAR(0); |
| 984263bc | 243 | rtype = AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT; |
| 1fcd0ba2 | 244 | sc->amr_reg = bus_alloc_resource_any(dev, rtype, &rid, RF_ACTIVE); |
| 984263bc MD |
245 | if (sc->amr_reg == NULL) { |
| 246 | device_printf(sc->amr_dev, "can't allocate register window\n"); | |
| 247 | goto out; | |
| 248 | } | |
| 249 | sc->amr_btag = rman_get_bustag(sc->amr_reg); | |
| 250 | sc->amr_bhandle = rman_get_bushandle(sc->amr_reg); | |
| 251 | ||
| 252 | /* | |
| 253 | * Allocate and connect our interrupt. | |
| 254 | */ | |
| 255 | rid = 0; | |
| 1fcd0ba2 SW |
256 | sc->amr_irq = bus_alloc_resource_any(sc->amr_dev, SYS_RES_IRQ, &rid, |
| 257 | RF_SHAREABLE | RF_ACTIVE); | |
| 984263bc MD |
258 | if (sc->amr_irq == NULL) { |
| 259 | device_printf(sc->amr_dev, "can't allocate interrupt\n"); | |
| 260 | goto out; | |
| 261 | } | |
| 1fcd0ba2 SW |
262 | if (bus_setup_intr(sc->amr_dev, sc->amr_irq, |
| 263 | 0, amr_pci_intr, | |
| 264 | sc, &sc->amr_intr, NULL)) { | |
| 984263bc MD |
265 | device_printf(sc->amr_dev, "can't set up interrupt\n"); |
| 266 | goto out; | |
| 267 | } | |
| 268 | ||
| 269 | debug(2, "interrupt attached"); | |
| 270 | ||
| 271 | /* assume failure is 'out of memory' */ | |
| 272 | error = ENOMEM; | |
| 273 | ||
| 274 | /* | |
| 275 | * Allocate the parent bus DMA tag appropriate for PCI. | |
| 276 | */ | |
| 277 | if (bus_dma_tag_create(NULL, /* parent */ | |
| 1fcd0ba2 SW |
278 | 1, 0, /* alignment,boundary */ |
| 279 | AMR_IS_SG64(sc) ? | |
| 280 | BUS_SPACE_MAXADDR : | |
| 984263bc MD |
281 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ |
| 282 | BUS_SPACE_MAXADDR, /* highaddr */ | |
| 283 | NULL, NULL, /* filter, filterarg */ | |
| 284 | MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ | |
| 285 | BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ | |
| 1fcd0ba2 | 286 | 0, /* flags */ |
| 984263bc MD |
287 | &sc->amr_parent_dmat)) { |
| 288 | device_printf(dev, "can't allocate parent DMA tag\n"); | |
| 289 | goto out; | |
| 290 | } | |
| 291 | ||
| 292 | /* | |
| 293 | * Create DMA tag for mapping buffers into controller-addressable space. | |
| 294 | */ | |
| 295 | if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ | |
| 1fcd0ba2 SW |
296 | 1, 0, /* alignment,boundary */ |
| 297 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | |
| 984263bc MD |
298 | BUS_SPACE_MAXADDR, /* highaddr */ |
| 299 | NULL, NULL, /* filter, filterarg */ | |
| 300 | MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ | |
| 1fcd0ba2 SW |
301 | MAXBSIZE, /* maxsegsize */ |
| 302 | 0, /* flags */ | |
| 984263bc MD |
303 | &sc->amr_buffer_dmat)) { |
| 304 | device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); | |
| 305 | goto out; | |
| 306 | } | |
| 307 | ||
| 1fcd0ba2 SW |
308 | if (bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ |
| 309 | 1, 0, /* alignment,boundary */ | |
| 310 | BUS_SPACE_MAXADDR, /* lowaddr */ | |
| 311 | BUS_SPACE_MAXADDR, /* highaddr */ | |
| 312 | NULL, NULL, /* filter, filterarg */ | |
| 313 | MAXBSIZE, AMR_NSEG, /* maxsize, nsegments */ | |
| 314 | MAXBSIZE, /* maxsegsize */ | |
| 315 | 0, /* flags */ | |
| 316 | &sc->amr_buffer64_dmat)) { | |
| 317 | device_printf(sc->amr_dev, "can't allocate buffer DMA tag\n"); | |
| 318 | goto out; | |
| 319 | } | |
| 320 | ||
| 984263bc MD |
321 | debug(2, "dma tag done"); |
| 322 | ||
| 323 | /* | |
| 324 | * Allocate and set up mailbox in a bus-visible fashion. | |
| 325 | */ | |
| 1fcd0ba2 SW |
326 | lockinit(&sc->amr_list_lock, "AMR List Lock", 0, LK_CANRECURSE); |
| 327 | lockinit(&sc->amr_hw_lock, "AMR HW Lock", 0, LK_CANRECURSE); | |
| 984263bc MD |
328 | if ((error = amr_setup_mbox(sc)) != 0) |
| 329 | goto out; | |
| 330 | ||
| 331 | debug(2, "mailbox setup"); | |
| 332 | ||
| 333 | /* | |
| 334 | * Build the scatter/gather buffers. | |
| 335 | */ | |
| 336 | if (amr_sglist_map(sc)) | |
| 337 | goto out; | |
| 984263bc MD |
338 | debug(2, "s/g list mapped"); |
| 339 | ||
| 1fcd0ba2 SW |
340 | if (amr_ccb_map(sc)) |
| 341 | goto out; | |
| 342 | debug(2, "ccb mapped"); | |
| 343 | ||
| 344 | ||
| 984263bc MD |
345 | /* |
| 346 | * Do bus-independant initialisation, bring controller online. | |
| 347 | */ | |
| 348 | error = amr_attach(sc); | |
| 349 | ||
| 350 | out: | |
| 351 | if (error) | |
| 352 | amr_pci_free(sc); | |
| 353 | return(error); | |
| 354 | } | |
| 355 | ||
| 356 | /******************************************************************************** | |
| 357 | * Disconnect from the controller completely, in preparation for unload. | |
| 358 | */ | |
| 359 | static int | |
| 360 | amr_pci_detach(device_t dev) | |
| 361 | { | |
| 362 | struct amr_softc *sc = device_get_softc(dev); | |
| 363 | int error; | |
| 364 | ||
| 365 | debug_called(1); | |
| 366 | ||
| 367 | if (sc->amr_state & AMR_STATE_OPEN) | |
| 368 | return(EBUSY); | |
| 369 | ||
| 370 | if ((error = amr_pci_shutdown(dev))) | |
| 371 | return(error); | |
| 372 | ||
| 373 | amr_pci_free(sc); | |
| 374 | ||
| 375 | return(0); | |
| 376 | } | |
| 377 | ||
| 378 | /******************************************************************************** | |
| 379 | * Bring the controller down to a dormant state and detach all child devices. | |
| 380 | * | |
| 381 | * This function is called before detach, system shutdown, or before performing | |
| 382 | * an operation which may add or delete system disks. (Call amr_startup to | |
| 383 | * resume normal operation.) | |
| 384 | * | |
| 385 | * Note that we can assume that the bioq on the controller is empty, as we won't | |
| 386 | * allow shutdown if any device is open. | |
| 387 | */ | |
| 388 | static int | |
| 389 | amr_pci_shutdown(device_t dev) | |
| 390 | { | |
| 391 | struct amr_softc *sc = device_get_softc(dev); | |
| 7f2216bc | 392 | int i,error; |
| 984263bc MD |
393 | |
| 394 | debug_called(1); | |
| 395 | ||
| 396 | /* mark ourselves as in-shutdown */ | |
| 397 | sc->amr_state |= AMR_STATE_SHUTDOWN; | |
| 398 | ||
| 399 | ||
| 400 | /* flush controller */ | |
| 401 | device_printf(sc->amr_dev, "flushing cache..."); | |
| e3869ec7 | 402 | kprintf("%s\n", amr_flush(sc) ? "failed" : "done"); |
| 984263bc | 403 | |
| 984263bc MD |
404 | error = 0; |
| 405 | ||
| 406 | /* delete all our child devices */ | |
| 407 | for(i = 0 ; i < AMR_MAXLD; i++) { | |
| 408 | if( sc->amr_drive[i].al_disk != 0) { | |
| 409 | if((error = device_delete_child(sc->amr_dev,sc->amr_drive[i].al_disk)) != 0) | |
| 410 | goto shutdown_out; | |
| 411 | sc->amr_drive[i].al_disk = 0; | |
| 412 | } | |
| 413 | } | |
| 414 | ||
| 415 | /* XXX disable interrupts? */ | |
| 416 | ||
| 417 | shutdown_out: | |
| 984263bc MD |
418 | return(error); |
| 419 | } | |
| 420 | ||
| 421 | /******************************************************************************** | |
| 422 | * Bring the controller to a quiescent state, ready for system suspend. | |
| 423 | */ | |
| 424 | static int | |
| 425 | amr_pci_suspend(device_t dev) | |
| 426 | { | |
| 427 | struct amr_softc *sc = device_get_softc(dev); | |
| 428 | ||
| 429 | debug_called(1); | |
| 430 | ||
| 431 | sc->amr_state |= AMR_STATE_SUSPEND; | |
| 432 | ||
| 433 | /* flush controller */ | |
| 434 | device_printf(sc->amr_dev, "flushing cache..."); | |
| e3869ec7 | 435 | kprintf("%s\n", amr_flush(sc) ? "failed" : "done"); |
| 984263bc MD |
436 | |
| 437 | /* XXX disable interrupts? */ | |
| 438 | ||
| 439 | return(0); | |
| 440 | } | |
| 441 | ||
| 442 | /******************************************************************************** | |
| 443 | * Bring the controller back to a state ready for operation. | |
| 444 | */ | |
| 445 | static int | |
| 446 | amr_pci_resume(device_t dev) | |
| 447 | { | |
| 448 | struct amr_softc *sc = device_get_softc(dev); | |
| 449 | ||
| 450 | debug_called(1); | |
| 451 | ||
| 452 | sc->amr_state &= ~AMR_STATE_SUSPEND; | |
| 453 | ||
| 454 | /* XXX enable interrupts? */ | |
| 455 | ||
| 456 | return(0); | |
| 457 | } | |
| 458 | ||
| 459 | /******************************************************************************* | |
| 460 | * Take an interrupt, or be poked by other code to look for interrupt-worthy | |
| 461 | * status. | |
| 462 | */ | |
| 463 | static void | |
| 464 | amr_pci_intr(void *arg) | |
| 465 | { | |
| 466 | struct amr_softc *sc = (struct amr_softc *)arg; | |
| 467 | ||
| 1fcd0ba2 | 468 | debug_called(3); |
| 984263bc MD |
469 | |
| 470 | /* collect finished commands, queue anything waiting */ | |
| 471 | amr_done(sc); | |
| 472 | } | |
| 473 | ||
| 474 | /******************************************************************************** | |
| 475 | * Free all of the resources associated with (sc) | |
| 476 | * | |
| 477 | * Should not be called if the controller is active. | |
| 478 | */ | |
| 479 | static void | |
| 480 | amr_pci_free(struct amr_softc *sc) | |
| 481 | { | |
| 1fcd0ba2 SW |
482 | void *p; |
| 483 | ||
| 984263bc MD |
484 | debug_called(1); |
| 485 | ||
| 486 | amr_free(sc); | |
| 487 | ||
| 488 | /* destroy data-transfer DMA tag */ | |
| 489 | if (sc->amr_buffer_dmat) | |
| 490 | bus_dma_tag_destroy(sc->amr_buffer_dmat); | |
| 1fcd0ba2 SW |
491 | if (sc->amr_buffer64_dmat) |
| 492 | bus_dma_tag_destroy(sc->amr_buffer64_dmat); | |
| 493 | ||
| 494 | /* free and destroy DMA memory and tag for passthrough pool */ | |
| 495 | if (sc->amr_ccb) | |
| 496 | bus_dmamem_free(sc->amr_ccb_dmat, sc->amr_ccb, sc->amr_ccb_dmamap); | |
| 497 | if (sc->amr_ccb_dmat) | |
| 498 | bus_dma_tag_destroy(sc->amr_ccb_dmat); | |
| 984263bc MD |
499 | |
| 500 | /* free and destroy DMA memory and tag for s/g lists */ | |
| 501 | if (sc->amr_sgtable) | |
| 502 | bus_dmamem_free(sc->amr_sg_dmat, sc->amr_sgtable, sc->amr_sg_dmamap); | |
| 503 | if (sc->amr_sg_dmat) | |
| 504 | bus_dma_tag_destroy(sc->amr_sg_dmat); | |
| 505 | ||
| 506 | /* free and destroy DMA memory and tag for mailbox */ | |
| 1fcd0ba2 | 507 | p = (void *)(uintptr_t)(volatile void *)sc->amr_mailbox64; |
| 984263bc | 508 | if (sc->amr_mailbox) { |
| 1fcd0ba2 | 509 | bus_dmamem_free(sc->amr_mailbox_dmat, p, sc->amr_mailbox_dmamap); |
| 984263bc MD |
510 | } |
| 511 | if (sc->amr_mailbox_dmat) | |
| 512 | bus_dma_tag_destroy(sc->amr_mailbox_dmat); | |
| 513 | ||
| 514 | /* disconnect the interrupt handler */ | |
| 515 | if (sc->amr_intr) | |
| 516 | bus_teardown_intr(sc->amr_dev, sc->amr_irq, sc->amr_intr); | |
| 517 | if (sc->amr_irq != NULL) | |
| 518 | bus_release_resource(sc->amr_dev, SYS_RES_IRQ, 0, sc->amr_irq); | |
| 519 | ||
| 520 | /* destroy the parent DMA tag */ | |
| 521 | if (sc->amr_parent_dmat) | |
| 522 | bus_dma_tag_destroy(sc->amr_parent_dmat); | |
| 523 | ||
| 524 | /* release the register window mapping */ | |
| 525 | if (sc->amr_reg != NULL) | |
| 526 | bus_release_resource(sc->amr_dev, | |
| 527 | AMR_IS_QUARTZ(sc) ? SYS_RES_MEMORY : SYS_RES_IOPORT, | |
| 1fcd0ba2 | 528 | PCIR_BAR(0), sc->amr_reg); |
| 984263bc MD |
529 | } |
| 530 | ||
| 531 | /******************************************************************************** | |
| 532 | * Allocate and map the scatter/gather table in bus space. | |
| 533 | */ | |
| 534 | static void | |
| 1fcd0ba2 | 535 | amr_sglist_helper(void *arg, bus_dma_segment_t *segs, int nseg, int error) |
| 984263bc | 536 | { |
| 1fcd0ba2 | 537 | uint32_t *addr; |
| 984263bc MD |
538 | |
| 539 | debug_called(1); | |
| 540 | ||
| 1fcd0ba2 SW |
541 | addr = arg; |
| 542 | *addr = segs[0].ds_addr; | |
| 984263bc MD |
543 | } |
| 544 | ||
| 545 | static int | |
| 546 | amr_sglist_map(struct amr_softc *sc) | |
| 547 | { | |
| 548 | size_t segsize; | |
| 1fcd0ba2 | 549 | void *p; |
| 984263bc MD |
550 | int error; |
| 551 | ||
| 552 | debug_called(1); | |
| 553 | ||
| 554 | /* | |
| 555 | * Create a single tag describing a region large enough to hold all of | |
| 556 | * the s/g lists we will need. | |
| 557 | * | |
| 1fcd0ba2 SW |
558 | * Note that we could probably use AMR_LIMITCMD here, but that may become |
| 559 | * tunable. | |
| 984263bc | 560 | */ |
| 1fcd0ba2 SW |
561 | if (AMR_IS_SG64(sc)) |
| 562 | segsize = sizeof(struct amr_sg64entry) * AMR_NSEG * AMR_MAXCMD; | |
| 563 | else | |
| 564 | segsize = sizeof(struct amr_sgentry) * AMR_NSEG * AMR_MAXCMD; | |
| 565 | ||
| 984263bc | 566 | error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ |
| 1fcd0ba2 SW |
567 | 512, 0, /* alignment,boundary */ |
| 568 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | |
| 984263bc MD |
569 | BUS_SPACE_MAXADDR, /* highaddr */ |
| 570 | NULL, NULL, /* filter, filterarg */ | |
| 571 | segsize, 1, /* maxsize, nsegments */ | |
| 572 | BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ | |
| 573 | 0, /* flags */ | |
| 574 | &sc->amr_sg_dmat); | |
| 575 | if (error != 0) { | |
| 576 | device_printf(sc->amr_dev, "can't allocate scatter/gather DMA tag\n"); | |
| 577 | return(ENOMEM); | |
| 578 | } | |
| 579 | ||
| 580 | /* | |
| 581 | * Allocate enough s/g maps for all commands and permanently map them into | |
| 582 | * controller-visible space. | |
| 583 | * | |
| 584 | * XXX this assumes we can get enough space for all the s/g maps in one | |
| 1fcd0ba2 SW |
585 | * contiguous slab. We may need to switch to a more complex arrangement |
| 586 | * where we allocate in smaller chunks and keep a lookup table from slot | |
| 587 | * to bus address. | |
| 984263bc | 588 | * |
| 1fcd0ba2 SW |
589 | * XXX HACK ALERT: at least some controllers don't like the s/g memory |
| 590 | * being allocated below 0x2000. We leak some memory if | |
| 591 | * we get some below this mark and allocate again. We | |
| 592 | * should be able to avoid this with the tag setup, but | |
| 593 | * that does't seem to work. | |
| 984263bc MD |
594 | */ |
| 595 | retry: | |
| 1fcd0ba2 | 596 | error = bus_dmamem_alloc(sc->amr_sg_dmat, (void **)&p, BUS_DMA_NOWAIT, &sc->amr_sg_dmamap); |
| 984263bc MD |
597 | if (error) { |
| 598 | device_printf(sc->amr_dev, "can't allocate s/g table\n"); | |
| 599 | return(ENOMEM); | |
| 600 | } | |
| 1fcd0ba2 | 601 | bus_dmamap_load(sc->amr_sg_dmat, sc->amr_sg_dmamap, p, segsize, amr_sglist_helper, &sc->amr_sgbusaddr, 0); |
| 984263bc MD |
602 | if (sc->amr_sgbusaddr < 0x2000) { |
| 603 | debug(1, "s/g table too low (0x%x), reallocating\n", sc->amr_sgbusaddr); | |
| 604 | goto retry; | |
| 605 | } | |
| 1fcd0ba2 SW |
606 | |
| 607 | if (AMR_IS_SG64(sc)) | |
| 608 | sc->amr_sg64table = (struct amr_sg64entry *)p; | |
| 609 | sc->amr_sgtable = (struct amr_sgentry *)p; | |
| 610 | ||
| 984263bc MD |
611 | return(0); |
| 612 | } | |
| 613 | ||
| 614 | /******************************************************************************** | |
| 615 | * Allocate and set up mailbox areas for the controller (sc) | |
| 616 | * | |
| 1fcd0ba2 | 617 | * The basic mailbox structure should be 16-byte aligned. |
| 984263bc | 618 | */ |
| 984263bc MD |
619 | static int |
| 620 | amr_setup_mbox(struct amr_softc *sc) | |
| 621 | { | |
| 622 | int error; | |
| 1fcd0ba2 SW |
623 | void *p; |
| 624 | uint32_t baddr; | |
| 984263bc MD |
625 | |
| 626 | debug_called(1); | |
| 627 | ||
| 628 | /* | |
| 629 | * Create a single tag describing a region large enough to hold the entire | |
| 630 | * mailbox. | |
| 631 | */ | |
| 632 | error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ | |
| 1fcd0ba2 SW |
633 | 16, 0, /* alignment,boundary */ |
| 634 | BUS_SPACE_MAXADDR_32BIT, /* lowaddr */ | |
| 984263bc MD |
635 | BUS_SPACE_MAXADDR, /* highaddr */ |
| 636 | NULL, NULL, /* filter, filterarg */ | |
| 1fcd0ba2 SW |
637 | sizeof(struct amr_mailbox64), /* maxsize */ |
| 638 | 1, /* nsegments */ | |
| 984263bc MD |
639 | BUS_SPACE_MAXSIZE_32BIT, /* maxsegsize */ |
| 640 | 0, /* flags */ | |
| 641 | &sc->amr_mailbox_dmat); | |
| 642 | if (error != 0) { | |
| 643 | device_printf(sc->amr_dev, "can't allocate mailbox tag\n"); | |
| 644 | return(ENOMEM); | |
| 645 | } | |
| 646 | ||
| 647 | /* | |
| 648 | * Allocate the mailbox structure and permanently map it into | |
| 649 | * controller-visible space. | |
| 650 | */ | |
| 1fcd0ba2 | 651 | error = bus_dmamem_alloc(sc->amr_mailbox_dmat, (void **)&p, BUS_DMA_NOWAIT, |
| 984263bc MD |
652 | &sc->amr_mailbox_dmamap); |
| 653 | if (error) { | |
| 654 | device_printf(sc->amr_dev, "can't allocate mailbox memory\n"); | |
| 655 | return(ENOMEM); | |
| 656 | } | |
| 1fcd0ba2 SW |
657 | bus_dmamap_load(sc->amr_mailbox_dmat, sc->amr_mailbox_dmamap, p, |
| 658 | sizeof(struct amr_mailbox64), amr_sglist_helper, &baddr, 0); | |
| 984263bc MD |
659 | /* |
| 660 | * Conventional mailbox is inside the mailbox64 region. | |
| 661 | */ | |
| 1fcd0ba2 SW |
662 | /* save physical base of the basic mailbox structure */ |
| 663 | sc->amr_mailboxphys = baddr + offsetof(struct amr_mailbox64, mb); | |
| 984263bc | 664 | bzero(p, sizeof(struct amr_mailbox64)); |
| 1fcd0ba2 SW |
665 | sc->amr_mailbox64 = (struct amr_mailbox64 *)p; |
| 666 | sc->amr_mailbox = &sc->amr_mailbox64->mb; | |
| 984263bc MD |
667 | |
| 668 | return(0); | |
| 669 | } | |
| 1fcd0ba2 SW |
670 | |
| 671 | static int | |
| 672 | amr_ccb_map(struct amr_softc *sc) | |
| 673 | { | |
| 674 | int ccbsize, error; | |
| 675 | ||
| 676 | /* | |
| 677 | * Passthrough and Extended passthrough structures will share the same | |
| 678 | * memory. | |
| 679 | */ | |
| 680 | ccbsize = sizeof(union amr_ccb) * AMR_MAXCMD; | |
| 681 | error = bus_dma_tag_create(sc->amr_parent_dmat, /* parent */ | |
| 682 | 128, 0, /* alignment,boundary */ | |
| 683 | BUS_SPACE_MAXADDR_32BIT,/* lowaddr */ | |
| 684 | BUS_SPACE_MAXADDR, /* highaddr */ | |
| 685 | NULL, NULL, /* filter, filterarg */ | |
| 686 | ccbsize, /* maxsize */ | |
| 687 | 1, /* nsegments */ | |
| 688 | ccbsize, /* maxsegsize */ | |
| 689 | 0, /* flags */ | |
| 690 | &sc->amr_ccb_dmat); | |
| 691 | if (error != 0) { | |
| 692 | device_printf(sc->amr_dev, "can't allocate ccb tag\n"); | |
| 693 | return (ENOMEM); | |
| 694 | } | |
| 695 | ||
| 696 | error = bus_dmamem_alloc(sc->amr_ccb_dmat, (void **)&sc->amr_ccb, | |
| 697 | BUS_DMA_NOWAIT, &sc->amr_ccb_dmamap); | |
| 698 | if (error) { | |
| 699 | device_printf(sc->amr_dev, "can't allocate ccb memory\n"); | |
| 700 | return (ENOMEM); | |
| 701 | } | |
| 702 | bus_dmamap_load(sc->amr_ccb_dmat, sc->amr_ccb_dmamap, sc->amr_ccb, | |
| 703 | ccbsize, amr_sglist_helper, &sc->amr_ccb_busaddr, 0); | |
| 704 | bzero(sc->amr_ccb, ccbsize); | |
| 705 | ||
| 706 | return (0); | |
| 707 | } |