1135446Strhodes/*- 2193149Sdougb * Copyright (c) 2009 Yahoo! Inc. 3135446Strhodes * All rights reserved. 4135446Strhodes * 5174187Sdougb * Redistribution and use in source and binary forms, with or without 6135446Strhodes * modification, are permitted provided that the following conditions 7135446Strhodes * are met: 8135446Strhodes * 1. Redistributions of source code must retain the above copyright 9135446Strhodes * notice, this list of conditions and the following disclaimer. 10135446Strhodes * 2. Redistributions in binary form must reproduce the above copyright 11135446Strhodes * notice, this list of conditions and the following disclaimer in the 12135446Strhodes * documentation and/or other materials provided with the distribution. 13135446Strhodes * 14135446Strhodes * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15135446Strhodes * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16135446Strhodes * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17135446Strhodes * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18193149Sdougb * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19135446Strhodes * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20186462Sdougb * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21135446Strhodes * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22170222Sdougb * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23135446Strhodes * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24135446Strhodes * SUCH DAMAGE. 25135446Strhodes */ 26135446Strhodes 27135446Strhodes#include <sys/cdefs.h> 28135446Strhodes__FBSDID("$FreeBSD$"); 29135446Strhodes 30135446Strhodes/* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */ 31135446Strhodes 32135446Strhodes/* TODO Move headers to mpsvar */ 33135446Strhodes#include <sys/types.h> 34135446Strhodes#include <sys/param.h> 35135446Strhodes#include <sys/systm.h> 36193149Sdougb#include <sys/kernel.h> 37193149Sdougb#include <sys/module.h> 38135446Strhodes#include <sys/bus.h> 39135446Strhodes#include <sys/conf.h> 40135446Strhodes#include <sys/malloc.h> 41135446Strhodes#include <sys/sysctl.h> 42135446Strhodes#include <sys/uio.h> 43135446Strhodes 44135446Strhodes#include <machine/bus.h> 45135446Strhodes#include <machine/resource.h> 46135446Strhodes#include <sys/rman.h> 47135446Strhodes 48170222Sdougb#include <dev/pci/pcireg.h> 49135446Strhodes#include <dev/pci/pcivar.h> 50135446Strhodes#include <dev/pci/pci_private.h> 51193149Sdougb 52135446Strhodes#include <dev/mps/mpi/mpi2_type.h> 53193149Sdougb#include <dev/mps/mpi/mpi2.h> 54193149Sdougb#include <dev/mps/mpi/mpi2_ioc.h> 55193149Sdougb#include <dev/mps/mpi/mpi2_cnfg.h> 56193149Sdougb#include <dev/mps/mpi/mpi2_tool.h> 57193149Sdougb 58135446Strhodes#include <sys/queue.h> 59193149Sdougb#include <sys/kthread.h> 60170222Sdougb#include <dev/mps/mps_ioctl.h> 61193149Sdougb#include <dev/mps/mpsvar.h> 62193149Sdougb 63193149Sdougbstatic int mps_pci_probe(device_t); 64193149Sdougbstatic int mps_pci_attach(device_t); 65193149Sdougbstatic int mps_pci_detach(device_t); 66193149Sdougbstatic int mps_pci_suspend(device_t); 67135446Strhodesstatic int mps_pci_resume(device_t); 68186462Sdougbstatic void mps_pci_free(struct mps_softc *); 69135446Strhodesstatic int mps_alloc_msix(struct mps_softc *sc, int msgs); 70135446Strhodesstatic int mps_alloc_msi(struct mps_softc *sc, int msgs); 71135446Strhodes 72135446Strhodesstatic device_method_t mps_methods[] = { 73193149Sdougb DEVMETHOD(device_probe, mps_pci_probe), 74135446Strhodes DEVMETHOD(device_attach, mps_pci_attach), 75170222Sdougb DEVMETHOD(device_detach, mps_pci_detach), 76135446Strhodes DEVMETHOD(device_suspend, mps_pci_suspend), 77135446Strhodes DEVMETHOD(device_resume, mps_pci_resume), 78135446Strhodes 79135446Strhodes DEVMETHOD_END 80135446Strhodes}; 81193149Sdougb 82193149Sdougbstatic driver_t mps_pci_driver = { 83193149Sdougb "mps", 84135446Strhodes mps_methods, 85170222Sdougb sizeof(struct mps_softc) 86193149Sdougb}; 87193149Sdougb 88193149Sdougbstatic devclass_t mps_devclass; 89135446StrhodesDRIVER_MODULE(mps, pci, mps_pci_driver, mps_devclass, 0, 0); 90193149SdougbMODULE_DEPEND(mps, cam, 1, 1, 1); 91193149Sdougb 92193149Sdougbstruct mps_ident { 93135446Strhodes uint16_t vendor; 94193149Sdougb uint16_t device; 95193149Sdougb uint16_t subvendor; 96135446Strhodes uint16_t subdevice; 97193149Sdougb u_int flags; 98135446Strhodes const char *desc; 99193149Sdougb} mps_identifiers[] = { 100135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004, 101135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2004" }, 102135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008, 103135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2008" }, 104170222Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1, 105135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2108" }, 106135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2, 107135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2108" }, 108135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3, 109170222Sdougb 0xffff, 0xffff, 0, "LSI SAS2108" }, 110135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1, 111193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2116" }, 112135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2, 113193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2116" }, 114193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1, 115186462Sdougb 0xffff, 0xffff, 0, "LSI SAS2208" }, 116193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2, 117193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2208" }, 118135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3, 119193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2208" }, 120193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4, 121193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2208" }, 122135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5, 123193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2208" }, 124193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6, 125135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2208" }, 126193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1, 127193149Sdougb 0xffff, 0xffff, 0, "LSI SAS2308" }, 128135446Strhodes // Add Customer specific vender/subdevice id before generic 129193149Sdougb // (0xffff) vender/subdevice id. 130193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, 131193149Sdougb 0x8086, 0x3516, 0, "Intel(R) Integrated RAID Module RMS25JB080" }, 132193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, 133193149Sdougb 0x8086, 0x3517, 0, "Intel(R) Integrated RAID Module RMS25JB040" }, 134193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, 135193149Sdougb 0x8086, 0x3518, 0, "Intel(R) Integrated RAID Module RMS25KB080" }, 136193149Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, 137193149Sdougb 0x8086, 0x3519, 0, "Intel(R) Integrated RAID Module RMS25KB040" }, 138170222Sdougb { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2, 139135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2308" }, 140135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3, 141135446Strhodes 0xffff, 0xffff, 0, "LSI SAS2308" }, 142135446Strhodes { MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200, 143193149Sdougb 0xffff, 0xffff, MPS_FLAGS_WD_AVAILABLE, "LSI SSS6200" }, 144193149Sdougb { 0, 0, 0, 0, 0, NULL } 145170222Sdougb}; 146193149Sdougb 147170222Sdougbstatic struct mps_ident * 148193149Sdougbmps_find_ident(device_t dev) 149170222Sdougb{ 150193149Sdougb struct mps_ident *m; 151135446Strhodes 152170222Sdougb for (m = mps_identifiers; m->vendor != 0; m++) { 153135446Strhodes if (m->vendor != pci_get_vendor(dev)) 154135446Strhodes continue; 155135446Strhodes if (m->device != pci_get_device(dev)) 156135446Strhodes continue; 157193149Sdougb if ((m->subvendor != 0xffff) && 158193149Sdougb (m->subvendor != pci_get_subvendor(dev))) 159193149Sdougb continue; 160193149Sdougb if ((m->subdevice != 0xffff) && 161193149Sdougb (m->subdevice != pci_get_subdevice(dev))) 162135446Strhodes continue; 163193149Sdougb return (m); 164193149Sdougb } 165193149Sdougb 166193149Sdougb return (NULL); 167135446Strhodes} 168135446Strhodes 169135446Strhodesstatic int 170135446Strhodesmps_pci_probe(device_t dev) 171135446Strhodes{ 172135446Strhodes struct mps_ident *id; 173170222Sdougb 174135446Strhodes if ((id = mps_find_ident(dev)) != NULL) { 175193149Sdougb device_set_desc(dev, id->desc); 176193149Sdougb return (BUS_PROBE_DEFAULT); 177193149Sdougb } 178193149Sdougb return (ENXIO); 179193149Sdougb} 180193149Sdougb 181193149Sdougbstatic int 182193149Sdougbmps_pci_attach(device_t dev) 183193149Sdougb{ 184193149Sdougb struct mps_softc *sc; 185193149Sdougb struct mps_ident *m; 186193149Sdougb int error; 187193149Sdougb 188193149Sdougb sc = device_get_softc(dev); 189193149Sdougb bzero(sc, sizeof(*sc)); 190193149Sdougb sc->mps_dev = dev; 191193149Sdougb m = mps_find_ident(dev); 192193149Sdougb sc->mps_flags = m->flags; 193193149Sdougb 194193149Sdougb /* Twiddle basic PCI config bits for a sanity check */ 195193149Sdougb pci_enable_busmaster(dev); 196193149Sdougb 197135446Strhodes /* Allocate the System Interface Register Set */ 198135446Strhodes sc->mps_regs_rid = PCIR_BAR(1); 199170222Sdougb if ((sc->mps_regs_resource = bus_alloc_resource_any(dev, 200135446Strhodes SYS_RES_MEMORY, &sc->mps_regs_rid, RF_ACTIVE)) == NULL) { 201193149Sdougb mps_printf(sc, "Cannot allocate PCI registers\n"); 202193149Sdougb return (ENXIO); 203193149Sdougb } 204135446Strhodes sc->mps_btag = rman_get_bustag(sc->mps_regs_resource); 205135446Strhodes sc->mps_bhandle = rman_get_bushandle(sc->mps_regs_resource); 206170222Sdougb 207135446Strhodes /* Allocate the parent DMA tag */ 208135446Strhodes if (bus_dma_tag_create( bus_get_dma_tag(dev), /* parent */ 209135446Strhodes 1, 0, /* algnmnt, boundary */ 210135446Strhodes BUS_SPACE_MAXADDR, /* lowaddr */ 211135446Strhodes BUS_SPACE_MAXADDR, /* highaddr */ 212193149Sdougb NULL, NULL, /* filter, filterarg */ 213193149Sdougb BUS_SPACE_MAXSIZE_32BIT,/* maxsize */ 214193149Sdougb BUS_SPACE_UNRESTRICTED, /* nsegments */ 215135446Strhodes BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */ 216135446Strhodes 0, /* flags */ 217170222Sdougb NULL, NULL, /* lockfunc, lockarg */ 218170222Sdougb &sc->mps_parent_dmat)) { 219135446Strhodes mps_printf(sc, "Cannot allocate parent DMA tag\n"); 220135446Strhodes mps_pci_free(sc); 221135446Strhodes return (ENOMEM); 222170222Sdougb } 223193149Sdougb 224135446Strhodes if ((error = mps_attach(sc)) != 0) 225193149Sdougb mps_pci_free(sc); 226193149Sdougb 227193149Sdougb return (error); 228135446Strhodes} 229193149Sdougb 230135446Strhodesint 231135446Strhodesmps_pci_setup_interrupts(struct mps_softc *sc) 232170222Sdougb{ 233135446Strhodes device_t dev; 234135446Strhodes int i, error, msgs; 235135446Strhodes 236135446Strhodes dev = sc->mps_dev; 237135446Strhodes error = ENXIO; 238193149Sdougb if ((sc->disable_msix == 0) && 239135446Strhodes ((msgs = pci_msix_count(dev)) >= MPS_MSI_COUNT)) 240193149Sdougb error = mps_alloc_msix(sc, MPS_MSI_COUNT); 241193149Sdougb if ((error != 0) && (sc->disable_msi == 0) && 242135446Strhodes ((msgs = pci_msi_count(dev)) >= MPS_MSI_COUNT)) 243193149Sdougb error = mps_alloc_msi(sc, MPS_MSI_COUNT); 244193149Sdougb 245193149Sdougb if (error != 0) { 246135446Strhodes sc->mps_flags |= MPS_FLAGS_INTX; 247193149Sdougb sc->mps_irq_rid[0] = 0; 248170222Sdougb sc->mps_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ, 249135446Strhodes &sc->mps_irq_rid[0], RF_SHAREABLE | RF_ACTIVE); 250135446Strhodes if (sc->mps_irq[0] == NULL) { 251135446Strhodes mps_printf(sc, "Cannot allocate INTx interrupt\n"); 252135446Strhodes return (ENXIO); 253135446Strhodes } 254135446Strhodes error = bus_setup_intr(dev, sc->mps_irq[0], 255135446Strhodes INTR_TYPE_BIO | INTR_MPSAFE, NULL, mps_intr, sc, 256193149Sdougb &sc->mps_intrhand[0]); 257193149Sdougb if (error) 258135446Strhodes mps_printf(sc, "Cannot setup INTx interrupt\n"); 259135446Strhodes } else { 260135446Strhodes sc->mps_flags |= MPS_FLAGS_MSI; 261135446Strhodes for (i = 0; i < MPS_MSI_COUNT; i++) { 262135446Strhodes sc->mps_irq_rid[i] = i + 1; 263135446Strhodes sc->mps_irq[i] = bus_alloc_resource_any(dev, 264135446Strhodes SYS_RES_IRQ, &sc->mps_irq_rid[i], RF_ACTIVE); 265135446Strhodes if (sc->mps_irq[i] == NULL) { 266135446Strhodes mps_printf(sc, 267135446Strhodes "Cannot allocate MSI interrupt\n"); 268170222Sdougb return (ENXIO); 269170222Sdougb } 270170222Sdougb error = bus_setup_intr(dev, sc->mps_irq[i], 271135446Strhodes INTR_TYPE_BIO | INTR_MPSAFE, NULL, mps_intr_msi, 272135446Strhodes sc, &sc->mps_intrhand[i]); 273135446Strhodes if (error) { 274135446Strhodes mps_printf(sc, 275135446Strhodes "Cannot setup MSI interrupt %d\n", i); 276135446Strhodes break; 277135446Strhodes } 278135446Strhodes } 279135446Strhodes } 280135446Strhodes 281135446Strhodes return (error); 282193149Sdougb} 283193149Sdougb 284143731Sdougbstatic int 285135446Strhodesmps_pci_detach(device_t dev) 286135446Strhodes{ 287135446Strhodes struct mps_softc *sc; 288135446Strhodes int error; 289135446Strhodes 290135446Strhodes sc = device_get_softc(dev); 291135446Strhodes 292135446Strhodes if ((error = mps_free(sc)) != 0) 293135446Strhodes return (error); 294135446Strhodes 295135446Strhodes mps_pci_free(sc); 296135446Strhodes return (0); 297135446Strhodes} 298135446Strhodes 299135446Strhodesstatic void 300135446Strhodesmps_pci_free(struct mps_softc *sc) 301135446Strhodes{ 302135446Strhodes int i; 303135446Strhodes 304193149Sdougb if (sc->mps_parent_dmat != NULL) { 305193149Sdougb bus_dma_tag_destroy(sc->mps_parent_dmat); 306193149Sdougb } 307135446Strhodes 308135446Strhodes if (sc->mps_flags & MPS_FLAGS_MSI) { 309135446Strhodes for (i = 0; i < MPS_MSI_COUNT; i++) { 310135446Strhodes if (sc->mps_irq[i] != NULL) { 311135446Strhodes bus_teardown_intr(sc->mps_dev, sc->mps_irq[i], 312135446Strhodes sc->mps_intrhand[i]); 313135446Strhodes bus_release_resource(sc->mps_dev, SYS_RES_IRQ, 314135446Strhodes sc->mps_irq_rid[i], sc->mps_irq[i]); 315135446Strhodes } 316135446Strhodes } 317135446Strhodes pci_release_msi(sc->mps_dev); 318135446Strhodes } 319135446Strhodes 320135446Strhodes if (sc->mps_flags & MPS_FLAGS_INTX) { 321143731Sdougb bus_teardown_intr(sc->mps_dev, sc->mps_irq[0], 322143731Sdougb sc->mps_intrhand[0]); 323135446Strhodes bus_release_resource(sc->mps_dev, SYS_RES_IRQ, 324135446Strhodes sc->mps_irq_rid[0], sc->mps_irq[0]); 325135446Strhodes } 326135446Strhodes 327193149Sdougb if (sc->mps_regs_resource != NULL) { 328193149Sdougb bus_release_resource(sc->mps_dev, SYS_RES_MEMORY, 329193149Sdougb sc->mps_regs_rid, sc->mps_regs_resource); 330193149Sdougb } 331135446Strhodes 332193149Sdougb return; 333193149Sdougb} 334193149Sdougb 335193149Sdougbstatic int 336193149Sdougbmps_pci_suspend(device_t dev) 337193149Sdougb{ 338193149Sdougb return (EINVAL); 339193149Sdougb} 340193149Sdougb 341135446Strhodesstatic int 342135446Strhodesmps_pci_resume(device_t dev) 343193149Sdougb{ 344193149Sdougb return (EINVAL); 345193149Sdougb} 346193149Sdougb 347193149Sdougbstatic int 348193149Sdougbmps_alloc_msix(struct mps_softc *sc, int msgs) 349135446Strhodes{ 350135446Strhodes int error; 351135446Strhodes 352193149Sdougb error = pci_alloc_msix(sc->mps_dev, &msgs); 353193149Sdougb return (error); 354193149Sdougb} 355135446Strhodes 356135446Strhodesstatic int 357135446Strhodesmps_alloc_msi(struct mps_softc *sc, int msgs) 358135446Strhodes{ 359135446Strhodes int error; 360135446Strhodes 361135446Strhodes error = pci_alloc_msi(sc->mps_dev, &msgs); 362135446Strhodes return (error); 363193149Sdougb} 364193149Sdougb 365193149Sdougbint 366193149Sdougbmps_pci_restore(struct mps_softc *sc) 367193149Sdougb{ 368135446Strhodes struct pci_devinfo *dinfo; 369135446Strhodes 370135446Strhodes mps_dprint(sc, MPS_TRACE, "%s\n", __func__); 371135446Strhodes 372193149Sdougb dinfo = device_get_ivars(sc->mps_dev); 373193149Sdougb if (dinfo == NULL) { 374193149Sdougb mps_dprint(sc, MPS_FAULT, "%s: NULL dinfo\n", __func__); 375135446Strhodes return (EINVAL); 376193149Sdougb } 377135446Strhodes 378193149Sdougb pci_cfg_restore(sc->mps_dev, dinfo); 379193149Sdougb return (0); 380193149Sdougb} 381193149Sdougb 382135446Strhodes