| Commit | Line | Data |
|---|---|---|
| 1ac8d5ba MD |
1 | /* |
| 2 | * Copyright (c) 2006 David Gwynne <dlg@openbsd.org> | |
| 3 | * | |
| 4 | * Permission to use, copy, modify, and distribute this software for any | |
| 5 | * purpose with or without fee is hereby granted, provided that the above | |
| 6 | * copyright notice and this permission notice appear in all copies. | |
| 7 | * | |
| 8 | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES | |
| 9 | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF | |
| 10 | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR | |
| 11 | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES | |
| 12 | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN | |
| 13 | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF | |
| 14 | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. | |
| 15 | * | |
| 16 | * | |
| 17 | * Copyright (c) 2009 The DragonFly Project. All rights reserved. | |
| 18 | * | |
| 19 | * This code is derived from software contributed to The DragonFly Project | |
| 20 | * by Matthew Dillon <dillon@backplane.com> | |
| 21 | * | |
| 22 | * Redistribution and use in source and binary forms, with or without | |
| 23 | * modification, are permitted provided that the following conditions | |
| 24 | * are met: | |
| 25 | * | |
| 26 | * 1. Redistributions of source code must retain the above copyright | |
| 27 | * notice, this list of conditions and the following disclaimer. | |
| 28 | * 2. Redistributions in binary form must reproduce the above copyright | |
| 29 | * notice, this list of conditions and the following disclaimer in | |
| 30 | * the documentation and/or other materials provided with the | |
| 31 | * distribution. | |
| 32 | * 3. Neither the name of The DragonFly Project nor the names of its | |
| 33 | * contributors may be used to endorse or promote products derived | |
| 34 | * from this software without specific, prior written permission. | |
| 35 | * | |
| 36 | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 37 | * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 38 | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS | |
| 39 | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE | |
| 40 | * COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, | |
| 41 | * INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES (INCLUDING, | |
| 42 | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; | |
| 43 | * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED | |
| 44 | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, | |
| 45 | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT | |
| 46 | * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | |
| 47 | * SUCH DAMAGE. | |
| 48 | * | |
| 49 | * $OpenBSD: sili.c,v 1.147 2009/02/16 21:19:07 miod Exp $ | |
| 50 | */ | |
| 51 | ||
| 52 | #include "sili.h" | |
| 53 | ||
| 54 | static int sili_pci_attach(device_t); | |
| 55 | static int sili_pci_detach(device_t); | |
| 56 | ||
| 57 | static const struct sili_device sili_devices[] = { | |
| 58 | { | |
| 59 | .ad_vendor = PCI_VENDOR_SII, | |
| 60 | .ad_product = PCI_PRODUCT_SII_3132, | |
| 61 | .ad_nports = 2, | |
| 62 | .ad_attach = sili_pci_attach, | |
| 63 | .ad_detach = sili_pci_detach, | |
| 64 | .name = "SiliconImage-3132-SATA" | |
| 65 | }, | |
| a68ed61d MD |
66 | { |
| 67 | .ad_vendor = PCI_VENDOR_SII, | |
| 68 | .ad_product = 0x3124, | |
| 69 | .ad_nports = 4, | |
| 70 | .ad_attach = sili_pci_attach, | |
| 71 | .ad_detach = sili_pci_detach, | |
| 72 | .name = "Rosewill-3124-SATA" | |
| 73 | }, | |
| 1ac8d5ba MD |
74 | { 0, 0, 0, NULL, NULL, NULL } |
| 75 | }; | |
| 76 | ||
| 77 | /* | |
| 78 | * Match during probe and attach. The device does not yet have a softc. | |
| 79 | */ | |
| 80 | const struct sili_device * | |
| 81 | sili_lookup_device(device_t dev) | |
| 82 | { | |
| 83 | const struct sili_device *ad; | |
| 84 | u_int16_t vendor = pci_get_vendor(dev); | |
| 85 | u_int16_t product = pci_get_device(dev); | |
| 86 | #if 0 | |
| 87 | u_int8_t class = pci_get_class(dev); | |
| 88 | u_int8_t subclass = pci_get_subclass(dev); | |
| 89 | u_int8_t progif = pci_read_config(dev, PCIR_PROGIF, 1); | |
| 90 | #endif | |
| 91 | ||
| 92 | for (ad = &sili_devices[0]; ad->ad_vendor; ++ad) { | |
| 93 | if (ad->ad_vendor == vendor && ad->ad_product == product) | |
| 94 | return (ad); | |
| 95 | } | |
| 96 | return (NULL); | |
| 97 | #if 0 | |
| 98 | /* | |
| 99 | * Last ad is the default match if the PCI device matches SATA. | |
| 100 | */ | |
| 101 | if (class == PCIC_STORAGE && subclass == PCIS_STORAGE_SATA && | |
| 102 | progif == PCIP_STORAGE_SATA_SILI_1_0) { | |
| 103 | return (ad); | |
| 104 | } | |
| 105 | return (NULL); | |
| 106 | #endif | |
| 107 | } | |
| 108 | ||
| 109 | static int | |
| 110 | sili_pci_attach(device_t dev) | |
| 111 | { | |
| 112 | struct sili_softc *sc = device_get_softc(dev); | |
| 113 | struct sili_port *ap; | |
| 114 | const char *gen; | |
| 115 | u_int32_t nports, reg; | |
| 116 | bus_addr_t addr; | |
| 117 | int i; | |
| 118 | int error; | |
| 119 | ||
| 120 | /* | |
| 121 | * Map the SILI controller's IRQ, BAR(0) (global regs), | |
| 122 | * and BAR(1) (port regs and lram). | |
| 123 | */ | |
| 124 | sc->sc_dev = dev; | |
| 125 | sc->sc_rid_irq = SILI_IRQ_RID; | |
| 126 | sc->sc_irq = bus_alloc_resource_any(dev, SYS_RES_IRQ, &sc->sc_rid_irq, | |
| 127 | RF_SHAREABLE | RF_ACTIVE); | |
| 128 | if (sc->sc_irq == NULL) { | |
| 129 | device_printf(dev, "unable to map interrupt\n"); | |
| 130 | sili_pci_detach(dev); | |
| 131 | return (ENXIO); | |
| 132 | } | |
| 133 | ||
| 134 | /* | |
| 135 | * When mapping the register window store the tag and handle | |
| 136 | * separately so we can use the tag with per-port bus handle | |
| 137 | * sub-spaces. | |
| 138 | */ | |
| 139 | sc->sc_rid_regs = PCIR_BAR(0); | |
| 140 | sc->sc_regs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | |
| 141 | &sc->sc_rid_regs, RF_ACTIVE); | |
| 142 | if (sc->sc_regs == NULL) { | |
| 143 | device_printf(dev, "unable to map registers\n"); | |
| 144 | sili_pci_detach(dev); | |
| 145 | return (ENXIO); | |
| 146 | } | |
| 147 | sc->sc_iot = rman_get_bustag(sc->sc_regs); | |
| 148 | sc->sc_ioh = rman_get_bushandle(sc->sc_regs); | |
| 149 | ||
| 150 | sc->sc_rid_pregs = PCIR_BAR(2); | |
| 151 | sc->sc_pregs = bus_alloc_resource_any(dev, SYS_RES_MEMORY, | |
| 152 | &sc->sc_rid_pregs, RF_ACTIVE); | |
| 153 | if (sc->sc_pregs == NULL) { | |
| 154 | device_printf(dev, "unable to map port registers\n"); | |
| 155 | sili_pci_detach(dev); | |
| 156 | return (ENXIO); | |
| 157 | } | |
| 158 | sc->sc_piot = rman_get_bustag(sc->sc_pregs); | |
| 159 | sc->sc_pioh = rman_get_bushandle(sc->sc_pregs); | |
| 160 | ||
| 161 | /* | |
| 162 | * Initialize the chipset and then set the interrupt vector up | |
| 163 | */ | |
| 164 | error = sili_init(sc); | |
| 165 | if (error) { | |
| 166 | sili_pci_detach(dev); | |
| 167 | return (ENXIO); | |
| 168 | } | |
| 169 | ||
| 170 | /* | |
| 171 | * We assume at least 4 commands. | |
| 172 | */ | |
| 173 | sc->sc_ncmds = SILI_MAX_CMDS; | |
| 174 | sc->sc_flags |= SILI_F_64BIT; | |
| 175 | sc->sc_flags |= SILI_F_NCQ; | |
| 176 | sc->sc_flags |= SILI_F_SSNTF; | |
| 177 | sc->sc_flags |= SILI_F_SPM; | |
| 178 | ||
| 179 | addr = (sc->sc_flags & SILI_F_64BIT) ? | |
| 180 | BUS_SPACE_MAXADDR : BUS_SPACE_MAXADDR_32BIT; | |
| 181 | ||
| 182 | /* | |
| 183 | * DMA tags for allocation of DMA memory buffers, lists, and so | |
| 184 | * forth. These are typically per-port. | |
| 185 | * | |
| 186 | * The stuff is mostly built into the BAR mappings. We only need | |
| 187 | * tags for our external SGE list and data. | |
| 188 | */ | |
| 189 | error = 0; | |
| 190 | error += bus_dma_tag_create( | |
| 191 | NULL, /* parent tag */ | |
| 192 | 256, /* alignment */ | |
| 193 | 65536, /* boundary */ | |
| 194 | addr, /* loaddr? */ | |
| 195 | BUS_SPACE_MAXADDR, /* hiaddr */ | |
| 196 | NULL, /* filter */ | |
| 197 | NULL, /* filterarg */ | |
| 2102f407 | 198 | sizeof(struct sili_prb) * SILI_MAX_CMDS, |
| 1ac8d5ba MD |
199 | /* [max]size */ |
| 200 | 1, /* maxsegs */ | |
| 2102f407 | 201 | sizeof(struct sili_prb) * SILI_MAX_CMDS, |
| 1ac8d5ba MD |
202 | /* maxsegsz */ |
| 203 | 0, /* flags */ | |
| 2102f407 | 204 | &sc->sc_tag_prbs); /* return tag */ |
| 1ac8d5ba MD |
205 | |
| 206 | /* | |
| 207 | * The data tag is used for later dmamaps and not immediately | |
| 208 | * allocated. | |
| 209 | */ | |
| 210 | error += bus_dma_tag_create( | |
| 211 | NULL, /* parent tag */ | |
| 212 | 4, /* alignment */ | |
| 213 | 0, /* boundary */ | |
| 214 | addr, /* loaddr? */ | |
| 215 | BUS_SPACE_MAXADDR, /* hiaddr */ | |
| 216 | NULL, /* filter */ | |
| 217 | NULL, /* filterarg */ | |
| 218 | 4096 * 1024, /* maxiosize */ | |
| 219 | SILI_MAX_SGET, /* maxsegs */ | |
| 220 | 65536, /* maxsegsz */ | |
| 221 | 0, /* flags */ | |
| 222 | &sc->sc_tag_data); /* return tag */ | |
| 223 | ||
| 224 | if (error) { | |
| 225 | device_printf(dev, "unable to create dma tags\n"); | |
| 226 | sili_pci_detach(dev); | |
| 227 | return (ENXIO); | |
| 228 | } | |
| 229 | ||
| 230 | if (sili_read(sc, SILI_REG_GCTL) & SILI_REG_GCTL_300CAP) { | |
| 231 | gen = "1 (1.5Gbps) and 2 (3Gbps)"; | |
| 232 | sc->sc_flags |= SILI_F_300; | |
| 233 | } else { | |
| 234 | gen = "1 (1.5Gbps)"; | |
| 235 | } | |
| 236 | ||
| 237 | nports = sc->sc_ad->ad_nports; | |
| 238 | KKASSERT(nports <= SILI_MAX_PORTS); | |
| 239 | ||
| a35ddbb4 | 240 | device_printf(dev, "ports=%d tags=31, cap=NCQ,FBSS,SPM\n", nports); |
| 1ac8d5ba MD |
241 | |
| 242 | /* | |
| 243 | * Allocate per-port resources | |
| 244 | * | |
| 245 | * All ports are attached in parallel but the CAM scan-bus | |
| 246 | * is held up until all ports are attached so we get a deterministic | |
| 247 | * order. | |
| 248 | */ | |
| 249 | for (i = 0; error == 0 && i < nports; i++) { | |
| 250 | error = sili_port_alloc(sc, i); | |
| 251 | } | |
| 252 | ||
| 253 | /* | |
| 254 | * Setup the interrupt vector and enable interrupts. Note that | |
| 255 | * since the irq may be shared we do not set it up until we are | |
| 256 | * ready to go. | |
| 257 | */ | |
| 258 | if (error == 0) { | |
| 259 | error = bus_setup_intr(dev, sc->sc_irq, 0, sili_intr, sc, | |
| 260 | &sc->sc_irq_handle, NULL); | |
| 261 | } | |
| 262 | ||
| 263 | if (error) { | |
| 264 | device_printf(dev, "unable to install interrupt\n"); | |
| 265 | sili_pci_detach(dev); | |
| 266 | return (ENXIO); | |
| 267 | } | |
| 268 | ||
| 269 | /* | |
| 270 | * Interrupt subsystem is good to go now, enable all port interrupts | |
| 271 | */ | |
| 272 | crit_enter(); | |
| 273 | reg = sili_read(sc, SILI_REG_GCTL); | |
| 274 | for (i = 0; i < nports; ++i) | |
| 275 | reg |= SILI_REG_GCTL_PORTEN(i); | |
| 276 | sili_write(sc, SILI_REG_GCTL, reg); | |
| 277 | sc->sc_flags |= SILI_F_INT_GOOD; | |
| 278 | crit_exit(); | |
| 279 | sili_intr(sc); | |
| 280 | ||
| 281 | /* | |
| 282 | * All ports are probing in parallel. Wait for them to finish | |
| 283 | * and then issue the cam attachment and bus scan serially so | |
| 284 | * the 'da' assignments are deterministic. | |
| 285 | */ | |
| 286 | for (i = 0; i < nports; i++) { | |
| 287 | if ((ap = sc->sc_ports[i]) != NULL) { | |
| 288 | while (ap->ap_signal & AP_SIGF_INIT) | |
| 289 | tsleep(&ap->ap_signal, 0, "ahprb1", hz); | |
| 290 | sili_os_lock_port(ap); | |
| 291 | if (sili_cam_attach(ap) == 0) { | |
| 292 | sili_cam_changed(ap, NULL, -1); | |
| 293 | sili_os_unlock_port(ap); | |
| 294 | while ((ap->ap_flags & AP_F_SCAN_COMPLETED) == 0) { | |
| 295 | tsleep(&ap->ap_flags, 0, "ahprb2", hz); | |
| 296 | } | |
| 297 | } else { | |
| 298 | sili_os_unlock_port(ap); | |
| 299 | } | |
| 300 | } | |
| 301 | } | |
| 302 | ||
| 303 | return(0); | |
| 304 | } | |
| 305 | ||
| 306 | /* | |
| 307 | * Device unload / detachment | |
| 308 | */ | |
| 309 | static int | |
| 310 | sili_pci_detach(device_t dev) | |
| 311 | { | |
| 312 | struct sili_softc *sc = device_get_softc(dev); | |
| 313 | struct sili_port *ap; | |
| 314 | int i; | |
| 315 | ||
| 316 | /* | |
| 317 | * Disable the controller and de-register the interrupt, if any. | |
| 318 | * | |
| 319 | * XXX interlock last interrupt? | |
| 320 | */ | |
| 321 | sc->sc_flags &= ~SILI_F_INT_GOOD; | |
| 322 | if (sc->sc_regs) | |
| 323 | sili_write(sc, SILI_REG_GCTL, SILI_REG_GCTL_GRESET); | |
| 324 | ||
| 325 | if (sc->sc_irq_handle) { | |
| 326 | bus_teardown_intr(dev, sc->sc_irq, sc->sc_irq_handle); | |
| 327 | sc->sc_irq_handle = NULL; | |
| 328 | } | |
| 329 | ||
| 330 | /* | |
| 331 | * Free port structures and DMA memory | |
| 332 | */ | |
| 333 | for (i = 0; i < SILI_MAX_PORTS; i++) { | |
| 334 | ap = sc->sc_ports[i]; | |
| 335 | if (ap) { | |
| 336 | sili_cam_detach(ap); | |
| 337 | sili_port_free(sc, i); | |
| 338 | } | |
| 339 | } | |
| 340 | ||
| 341 | /* | |
| 342 | * Clean up the bus space | |
| 343 | */ | |
| 344 | if (sc->sc_irq) { | |
| 345 | bus_release_resource(dev, SYS_RES_IRQ, | |
| 346 | sc->sc_rid_irq, sc->sc_irq); | |
| 347 | sc->sc_irq = NULL; | |
| 348 | } | |
| 349 | if (sc->sc_regs) { | |
| 350 | bus_release_resource(dev, SYS_RES_MEMORY, | |
| 351 | sc->sc_rid_regs, sc->sc_regs); | |
| 352 | sc->sc_regs = NULL; | |
| 353 | } | |
| 354 | if (sc->sc_pregs) { | |
| 355 | bus_release_resource(dev, SYS_RES_MEMORY, | |
| 356 | sc->sc_rid_pregs, sc->sc_pregs); | |
| 357 | sc->sc_regs = NULL; | |
| 358 | } | |
| 359 | ||
| 2102f407 MD |
360 | if (sc->sc_tag_prbs) { |
| 361 | bus_dma_tag_destroy(sc->sc_tag_prbs); | |
| 362 | sc->sc_tag_prbs = NULL; | |
| 1ac8d5ba MD |
363 | } |
| 364 | if (sc->sc_tag_data) { | |
| 365 | bus_dma_tag_destroy(sc->sc_tag_data); | |
| 366 | sc->sc_tag_data = NULL; | |
| 367 | } | |
| 368 | ||
| 369 | return (0); | |
| 370 | } |