2010-09-10 15:03:56 +00:00
|
|
|
/*-
|
|
|
|
* Copyright (c) 2009 Yahoo! Inc.
|
|
|
|
* All rights reserved.
|
|
|
|
*
|
|
|
|
* Redistribution and use in source and binary forms, with or without
|
|
|
|
* modification, are permitted provided that the following conditions
|
|
|
|
* are met:
|
|
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer.
|
|
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
|
|
* documentation and/or other materials provided with the distribution.
|
|
|
|
*
|
|
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
|
|
* SUCH DAMAGE.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <sys/cdefs.h>
|
|
|
|
__FBSDID("$FreeBSD$");
|
|
|
|
|
|
|
|
/* PCI/PCI-X/PCIe bus interface for the LSI MPT2 controllers */
|
|
|
|
|
2012-01-26 18:17:21 +00:00
|
|
|
/* TODO Move headers to mpsvar */
|
2010-09-10 15:03:56 +00:00
|
|
|
#include <sys/types.h>
|
|
|
|
#include <sys/param.h>
|
|
|
|
#include <sys/systm.h>
|
|
|
|
#include <sys/kernel.h>
|
|
|
|
#include <sys/module.h>
|
|
|
|
#include <sys/bus.h>
|
|
|
|
#include <sys/conf.h>
|
|
|
|
#include <sys/malloc.h>
|
|
|
|
#include <sys/sysctl.h>
|
Add Serial Management Protocol (SMP) passthrough support to CAM.
This includes support in the kernel, camcontrol(8), libcam and the mps(4)
driver for SMP passthrough.
The CAM SCSI probe code has been modified to fetch Inquiry VPD page 0x00
to determine supported pages, and will now fetch page 0x83 in addition to
page 0x80 if supported.
Add two new CAM CCBs, XPT_SMP_IO, and XPT_GDEV_ADVINFO. The SMP CCB is
intended for SMP requests and responses. The ADVINFO is currently used to
fetch cached VPD page 0x83 data from the transport layer, but is intended
to be extensible to fetch other types of device-specific data.
SMP-only devices are not currently represented in the CAM topology, and so
the current semantics are that the SIM will route SMP CCBs to either the
addressed device, if it contains an SMP target, or its parent, if it
contains an SMP target. (This is noted in cam_ccb.h, since it will change
later once we have the ability to have SMP-only devices in CAM's topology.)
smp_all.c,
smp_all.h: New helper routines for SMP. This includes
SMP request building routines, response parsing
routines, error decoding routines, and structure
definitions for a number of SMP commands.
libcam/Makefile: Add smp_all.c to libcam, so that SMP functionality
is available to userland applications.
camcontrol.8,
camcontrol.c: Add smp passthrough support to camcontrol. Several
new subcommands are now available:
'smpcmd' functions much like 'cmd', except that it
allows the user to send generic SMP commands.
'smprg' sends the SMP report general command, and
displays the decoded output. It will automatically
fetch extended output if it is available.
'smppc' sends the SMP phy control command, with any
number of potential options. Among other things,
this allows the user to reset a phy on a SAS
expander, or disable a phy on an expander.
'smpmaninfo' sends the SMP report manufacturer
information and displays the decoded output.
'smpphylist' displays a list of phys on an
expander, and the CAM devices attached to those
phys, if any.
cam.h,
cam.c: Add a status value for SMP errors
(CAM_SMP_STATUS_ERROR).
Add a missing description for CAM_SCSI_IT_NEXUS_LOST.
Add support for SMP commands to cam_error_string().
cam_ccb.h: Rename the CAM_DIR_RESV flag to CAM_DIR_BOTH. SMP
commands are by nature bi-directional, and we may
need to support bi-directional SCSI commands later.
Add the XPT_SMP_IO CCB. Since SMP commands are
bi-directional, there are pointers for both the
request and response.
Add a fill routine for SMP CCBs.
Add the XPT_GDEV_ADVINFO CCB. This is currently
used to fetch cached page 0x83 data from the
transport later, but is extensible to fetch many
other types of data.
cam_periph.c: Add support in cam_periph_mapmem() for XPT_SMP_IO
and XPT_GDEV_ADVINFO CCBs.
cam_xpt.c: Add support for executing XPT_SMP_IO CCBs.
cam_xpt_internal.h: Add fields for VPD pages 0x00 and 0x83 in struct
cam_ed.
scsi_all.c: Add scsi_get_sas_addr(), a function that parses
VPD page 0x83 data and pulls out a SAS address.
scsi_all.h: Add VPD page 0x00 and 0x83 structures, and a
prototype for scsi_get_sas_addr().
scsi_pass.c: Add support for mapping buffers in XPT_SMP_IO and
XPT_GDEV_ADVINFO CCBs.
scsi_xpt.c: In the SCSI probe code, first ask the device for
VPD page 0x00. If any VPD pages are supported,
that page is required to be implemented. Based on
the response, we may probe for the serial number
(page 0x80) or device id (page 0x83).
Add support for the XPT_GDEV_ADVINFO CCB.
sys/conf/files: Add smp_all.c.
mps.c: Add support for passing in a uio in mps_map_command(),
so we can map a S/G list at once.
Add support for SMP passthrough commands in
mps_data_cb(). SMP is a special case, because the
first buffer in the S/G list is outbound and the
second buffer is inbound.
Add support for warning the user if the busdma code
comes back with more buffers than will work for the
command. This will, for example, help the user
determine why an SMP command failed if busdma comes
back with three buffers.
mps_pci.c: Add sys/uio.h.
mps_sas.c: Add the SAS address and the parent handle to the
list of fields we pull from device page 0 and cache
in struct mpssas_target. These are needed for SMP
passthrough.
Add support for the XPT_SMP_IO CCB. For now, this
CCB is routed to the addressed device if it supports
SMP, or to its parent if it does not and the parent
does. This is necessary because CAM does not
currently support SMP-only nodes in the topology.
Make SMP passthrough support conditional on
__FreeBSD_version >= 900026. This will make it
easier to MFC this change to the driver without
MFCing the CAM changes as well.
mps_user.c: Un-staticize mpi_init_sge() so we can use it for
the SMP passthrough code.
mpsvar.h: Add a uio and iovecs into struct mps_command for
SMP passthrough commands.
Add a cm_max_segs field to struct mps_command so
that we can warn the user if busdma comes back with
too many segments.
Clear the cm_reply when a command gets freed. If
it is not cleared, reply frames will eventually get
freed into the pool multiple times and corrupt the
pool. (This fix is from scottl.)
Add a prototype for mpi_init_sge().
sys/param.h: Bump __FreeBSD_version to 900026 for the for the
inclusion of the XPT_GDEV_ADVINFO and XPT_SMP_IO
CAM CCBs.
2010-11-30 22:39:46 +00:00
|
|
|
#include <sys/uio.h>
|
2010-09-10 15:03:56 +00:00
|
|
|
|
|
|
|
#include <machine/bus.h>
|
|
|
|
#include <machine/resource.h>
|
|
|
|
#include <sys/rman.h>
|
|
|
|
|
|
|
|
#include <dev/pci/pcireg.h>
|
|
|
|
#include <dev/pci/pcivar.h>
|
2012-01-26 18:17:21 +00:00
|
|
|
#include <dev/pci/pci_private.h>
|
2010-09-10 15:03:56 +00:00
|
|
|
|
|
|
|
#include <dev/mps/mpi/mpi2_type.h>
|
|
|
|
#include <dev/mps/mpi/mpi2.h>
|
|
|
|
#include <dev/mps/mpi/mpi2_ioc.h>
|
|
|
|
#include <dev/mps/mpi/mpi2_cnfg.h>
|
2012-01-26 18:17:21 +00:00
|
|
|
#include <dev/mps/mpi/mpi2_tool.h>
|
2010-09-10 15:03:56 +00:00
|
|
|
|
2012-01-26 18:17:21 +00:00
|
|
|
#include <sys/queue.h>
|
|
|
|
#include <sys/kthread.h>
|
|
|
|
#include <dev/mps/mps_ioctl.h>
|
2010-09-10 15:03:56 +00:00
|
|
|
#include <dev/mps/mpsvar.h>
|
|
|
|
|
|
|
|
static int mps_pci_probe(device_t);
|
|
|
|
static int mps_pci_attach(device_t);
|
|
|
|
static int mps_pci_detach(device_t);
|
|
|
|
static int mps_pci_suspend(device_t);
|
|
|
|
static int mps_pci_resume(device_t);
|
|
|
|
static void mps_pci_free(struct mps_softc *);
|
|
|
|
static int mps_alloc_msix(struct mps_softc *sc, int msgs);
|
|
|
|
static int mps_alloc_msi(struct mps_softc *sc, int msgs);
|
|
|
|
|
|
|
|
static device_method_t mps_methods[] = {
|
|
|
|
DEVMETHOD(device_probe, mps_pci_probe),
|
|
|
|
DEVMETHOD(device_attach, mps_pci_attach),
|
|
|
|
DEVMETHOD(device_detach, mps_pci_detach),
|
|
|
|
DEVMETHOD(device_suspend, mps_pci_suspend),
|
|
|
|
DEVMETHOD(device_resume, mps_pci_resume),
|
2011-11-22 21:28:20 +00:00
|
|
|
|
|
|
|
DEVMETHOD_END
|
2010-09-10 15:03:56 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
static driver_t mps_pci_driver = {
|
|
|
|
"mps",
|
|
|
|
mps_methods,
|
|
|
|
sizeof(struct mps_softc)
|
|
|
|
};
|
|
|
|
|
|
|
|
static devclass_t mps_devclass;
|
|
|
|
DRIVER_MODULE(mps, pci, mps_pci_driver, mps_devclass, 0, 0);
|
|
|
|
|
|
|
|
struct mps_ident {
|
|
|
|
uint16_t vendor;
|
|
|
|
uint16_t device;
|
|
|
|
uint16_t subvendor;
|
|
|
|
uint16_t subdevice;
|
|
|
|
u_int flags;
|
|
|
|
const char *desc;
|
|
|
|
} mps_identifiers[] = {
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2004,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2004" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2008,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2008" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_1,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2108" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_2,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2108" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2108_3,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2108" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_1,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2116" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2116_2,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2116" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_1,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_2,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_3,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_4,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_5,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2208_6,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2208" },
|
2012-01-26 18:17:21 +00:00
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_1,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2308" },
|
|
|
|
// Add Customer specific vender/subdevice id before generic
|
|
|
|
// (0xffff) vender/subdevice id.
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
|
|
|
|
0x8086, 0x3516, 0, "Intel(R) Integrated RAID Module RMS25JB080" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
|
|
|
|
0x8086, 0x3517, 0, "Intel(R) Integrated RAID Module RMS25JB040" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
|
|
|
|
0x8086, 0x3518, 0, "Intel(R) Integrated RAID Module RMS25KB080" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
|
|
|
|
0x8086, 0x3519, 0, "Intel(R) Integrated RAID Module RMS25KB040" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_2,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2308" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SAS2308_3,
|
|
|
|
0xffff, 0xffff, 0, "LSI SAS2308" },
|
|
|
|
{ MPI2_MFGPAGE_VENDORID_LSI, MPI2_MFGPAGE_DEVID_SSS6200,
|
|
|
|
0xffff, 0xffff, MPS_FLAGS_WD_AVAILABLE, "LSI SSS6200" },
|
2010-09-10 15:03:56 +00:00
|
|
|
{ 0, 0, 0, 0, 0, NULL }
|
|
|
|
};
|
|
|
|
|
|
|
|
static struct mps_ident *
|
|
|
|
mps_find_ident(device_t dev)
|
|
|
|
{
|
|
|
|
struct mps_ident *m;
|
|
|
|
|
|
|
|
for (m = mps_identifiers; m->vendor != 0; m++) {
|
|
|
|
if (m->vendor != pci_get_vendor(dev))
|
|
|
|
continue;
|
|
|
|
if (m->device != pci_get_device(dev))
|
|
|
|
continue;
|
|
|
|
if ((m->subvendor != 0xffff) &&
|
|
|
|
(m->subvendor != pci_get_subvendor(dev)))
|
|
|
|
continue;
|
|
|
|
if ((m->subdevice != 0xffff) &&
|
|
|
|
(m->subdevice != pci_get_subdevice(dev)))
|
|
|
|
continue;
|
|
|
|
return (m);
|
|
|
|
}
|
|
|
|
|
|
|
|
return (NULL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_pci_probe(device_t dev)
|
|
|
|
{
|
|
|
|
struct mps_ident *id;
|
|
|
|
|
|
|
|
if ((id = mps_find_ident(dev)) != NULL) {
|
|
|
|
device_set_desc(dev, id->desc);
|
2012-02-11 00:28:30 +00:00
|
|
|
return (BUS_PROBE_DEFAULT);
|
2010-09-10 15:03:56 +00:00
|
|
|
}
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_pci_attach(device_t dev)
|
|
|
|
{
|
|
|
|
struct mps_softc *sc;
|
|
|
|
struct mps_ident *m;
|
|
|
|
uint16_t command;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
bzero(sc, sizeof(*sc));
|
|
|
|
sc->mps_dev = dev;
|
|
|
|
m = mps_find_ident(dev);
|
|
|
|
sc->mps_flags = m->flags;
|
|
|
|
|
|
|
|
/* Twiddle basic PCI config bits for a sanity check */
|
|
|
|
command = pci_read_config(dev, PCIR_COMMAND, 2);
|
|
|
|
command |= PCIM_CMD_BUSMASTEREN;
|
|
|
|
pci_write_config(dev, PCIR_COMMAND, command, 2);
|
|
|
|
command = pci_read_config(dev, PCIR_COMMAND, 2);
|
|
|
|
if ((command & PCIM_CMD_BUSMASTEREN) == 0) {
|
|
|
|
device_printf(dev, "Cannot enable PCI busmaster\n");
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
if ((command & PCIM_CMD_MEMEN) == 0) {
|
|
|
|
device_printf(dev, "PCI memory window not available\n");
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
|
|
|
|
/* Allocate the System Interface Register Set */
|
|
|
|
sc->mps_regs_rid = PCIR_BAR(1);
|
|
|
|
if ((sc->mps_regs_resource = bus_alloc_resource_any(dev,
|
|
|
|
SYS_RES_MEMORY, &sc->mps_regs_rid, RF_ACTIVE)) == NULL) {
|
|
|
|
device_printf(dev, "Cannot allocate PCI registers\n");
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
sc->mps_btag = rman_get_bustag(sc->mps_regs_resource);
|
|
|
|
sc->mps_bhandle = rman_get_bushandle(sc->mps_regs_resource);
|
|
|
|
|
|
|
|
/* Allocate the parent DMA tag */
|
2012-01-26 18:17:21 +00:00
|
|
|
if (bus_dma_tag_create( bus_get_dma_tag(dev), /* parent */
|
2010-09-10 15:03:56 +00:00
|
|
|
1, 0, /* algnmnt, boundary */
|
|
|
|
BUS_SPACE_MAXADDR, /* lowaddr */
|
|
|
|
BUS_SPACE_MAXADDR, /* highaddr */
|
|
|
|
NULL, NULL, /* filter, filterarg */
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,/* maxsize */
|
|
|
|
BUS_SPACE_UNRESTRICTED, /* nsegments */
|
|
|
|
BUS_SPACE_MAXSIZE_32BIT,/* maxsegsize */
|
|
|
|
0, /* flags */
|
|
|
|
NULL, NULL, /* lockfunc, lockarg */
|
|
|
|
&sc->mps_parent_dmat)) {
|
|
|
|
device_printf(dev, "Cannot allocate parent DMA tag\n");
|
|
|
|
mps_pci_free(sc);
|
|
|
|
return (ENOMEM);
|
|
|
|
}
|
|
|
|
|
|
|
|
if ((error = mps_attach(sc)) != 0)
|
|
|
|
mps_pci_free(sc);
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
int
|
|
|
|
mps_pci_setup_interrupts(struct mps_softc *sc)
|
|
|
|
{
|
|
|
|
device_t dev;
|
|
|
|
int i, error, msgs;
|
|
|
|
|
|
|
|
dev = sc->mps_dev;
|
|
|
|
error = ENXIO;
|
2012-01-26 18:17:21 +00:00
|
|
|
if ((sc->disable_msix == 0) &&
|
2010-09-10 15:03:56 +00:00
|
|
|
((msgs = pci_msix_count(dev)) >= MPS_MSI_COUNT))
|
|
|
|
error = mps_alloc_msix(sc, MPS_MSI_COUNT);
|
2012-01-26 18:17:21 +00:00
|
|
|
if ((error != 0) && (sc->disable_msi == 0) &&
|
2010-09-10 15:03:56 +00:00
|
|
|
((msgs = pci_msi_count(dev)) >= MPS_MSI_COUNT))
|
|
|
|
error = mps_alloc_msi(sc, MPS_MSI_COUNT);
|
|
|
|
|
|
|
|
if (error != 0) {
|
|
|
|
sc->mps_flags |= MPS_FLAGS_INTX;
|
|
|
|
sc->mps_irq_rid[0] = 0;
|
|
|
|
sc->mps_irq[0] = bus_alloc_resource_any(dev, SYS_RES_IRQ,
|
|
|
|
&sc->mps_irq_rid[0], RF_SHAREABLE | RF_ACTIVE);
|
|
|
|
if (sc->mps_irq[0] == NULL) {
|
|
|
|
device_printf(dev, "Cannot allocate INTx interrupt\n");
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->mps_irq[0],
|
|
|
|
INTR_TYPE_BIO | INTR_MPSAFE, NULL, mps_intr, sc,
|
|
|
|
&sc->mps_intrhand[0]);
|
|
|
|
if (error)
|
|
|
|
device_printf(dev, "Cannot setup INTx interrupt\n");
|
|
|
|
} else {
|
|
|
|
sc->mps_flags |= MPS_FLAGS_MSI;
|
|
|
|
for (i = 0; i < MPS_MSI_COUNT; i++) {
|
|
|
|
sc->mps_irq_rid[i] = i + 1;
|
|
|
|
sc->mps_irq[i] = bus_alloc_resource_any(dev,
|
|
|
|
SYS_RES_IRQ, &sc->mps_irq_rid[i], RF_ACTIVE);
|
|
|
|
if (sc->mps_irq[i] == NULL) {
|
|
|
|
device_printf(dev,
|
|
|
|
"Cannot allocate MSI interrupt\n");
|
|
|
|
return (ENXIO);
|
|
|
|
}
|
|
|
|
error = bus_setup_intr(dev, sc->mps_irq[i],
|
|
|
|
INTR_TYPE_BIO | INTR_MPSAFE, NULL, mps_intr_msi,
|
|
|
|
sc, &sc->mps_intrhand[i]);
|
|
|
|
if (error) {
|
|
|
|
device_printf(dev,
|
|
|
|
"Cannot setup MSI interrupt %d\n", i);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_pci_detach(device_t dev)
|
|
|
|
{
|
|
|
|
struct mps_softc *sc;
|
|
|
|
int error;
|
|
|
|
|
|
|
|
sc = device_get_softc(dev);
|
|
|
|
|
|
|
|
if ((error = mps_free(sc)) != 0)
|
|
|
|
return (error);
|
|
|
|
|
|
|
|
mps_pci_free(sc);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|
|
|
|
static void
|
|
|
|
mps_pci_free(struct mps_softc *sc)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
|
|
|
|
if (sc->mps_parent_dmat != NULL) {
|
|
|
|
bus_dma_tag_destroy(sc->mps_parent_dmat);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->mps_flags & MPS_FLAGS_MSI) {
|
|
|
|
for (i = 0; i < MPS_MSI_COUNT; i++) {
|
|
|
|
if (sc->mps_irq[i] != NULL) {
|
|
|
|
bus_teardown_intr(sc->mps_dev, sc->mps_irq[i],
|
|
|
|
sc->mps_intrhand[i]);
|
|
|
|
bus_release_resource(sc->mps_dev, SYS_RES_IRQ,
|
|
|
|
sc->mps_irq_rid[i], sc->mps_irq[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
pci_release_msi(sc->mps_dev);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->mps_flags & MPS_FLAGS_INTX) {
|
|
|
|
bus_teardown_intr(sc->mps_dev, sc->mps_irq[0],
|
|
|
|
sc->mps_intrhand[0]);
|
|
|
|
bus_release_resource(sc->mps_dev, SYS_RES_IRQ,
|
|
|
|
sc->mps_irq_rid[0], sc->mps_irq[0]);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (sc->mps_regs_resource != NULL) {
|
|
|
|
bus_release_resource(sc->mps_dev, SYS_RES_MEMORY,
|
|
|
|
sc->mps_regs_rid, sc->mps_regs_resource);
|
|
|
|
}
|
|
|
|
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_pci_suspend(device_t dev)
|
|
|
|
{
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_pci_resume(device_t dev)
|
|
|
|
{
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_alloc_msix(struct mps_softc *sc, int msgs)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = pci_alloc_msix(sc->mps_dev, &msgs);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
mps_alloc_msi(struct mps_softc *sc, int msgs)
|
|
|
|
{
|
|
|
|
int error;
|
|
|
|
|
|
|
|
error = pci_alloc_msi(sc->mps_dev, &msgs);
|
|
|
|
return (error);
|
|
|
|
}
|
|
|
|
|
2012-01-26 18:17:21 +00:00
|
|
|
int
|
|
|
|
mps_pci_restore(struct mps_softc *sc)
|
|
|
|
{
|
|
|
|
struct pci_devinfo *dinfo;
|
|
|
|
|
|
|
|
mps_dprint(sc, MPS_TRACE, "%s\n", __func__);
|
|
|
|
|
|
|
|
dinfo = device_get_ivars(sc->mps_dev);
|
|
|
|
if (dinfo == NULL) {
|
|
|
|
mps_dprint(sc, MPS_FAULT, "%s: NULL dinfo\n", __func__);
|
|
|
|
return (EINVAL);
|
|
|
|
}
|
|
|
|
|
|
|
|
pci_cfg_restore(sc->mps_dev, dinfo);
|
|
|
|
return (0);
|
|
|
|
}
|
|
|
|
|