freebsd-src/sys/dev/isp/isp_pci.c
Kenneth D. Merry 31354813f3 Add an isp(4) tunable to default to ispfw(4) firmware.
ispfw(4) recently gained firmware for Qlogic 27XX and 28XX
FC controllers, and isp(4) now selects the newer of firmware in
flash or in ispfw(4) to load for those controllers.

This differs from the previous behavior (which remains for older
controllers), which was to always load the ispfw(4) firmware if it
is available.

This adds a loader tunable, hint.isp.N.fwload_force to default to
loading the ispfw(4) firmware, whether or not it is newer than the
firmware in flash.  This allows the user to always use the known
firmware version included with the kernel.

Note that there is an existing fwload_disable tunable that tells
the driver to always load the firmware from flash and ignore
ispfw(4).  If fwload_disable is set, fwload_force will be ignored.
So users with existing fwload_disable tunables will have the same
behavior.

If a user specifies both fwload_force and fwload_disable for the
same controller, the isp(4) driver prints a warning message,
and fwload_disable will be honored.

The user can see which firmware is active through the
dev.isp.N.fw_version* sysctl variables.

share/man/man4/isp.4:
	Document the new loader tunable.

sys/dev/isp/isp.c:
	In isp_load_risc_flash(), changet the decision logic to
	also consider ISP_CFG_FWLOAD_ONLY.  Load the flash firmware
	and get the version, so the user knows what it is, but if
	the user set fwload_force, honor that.  If the user didn't
	set fwload_force, the behavior remains to select the newer
	firmware version.

sys/dev/isp/isp_pci.c:
	Add a new fwload_force tunable.  Print out a warning if the
	user sets both fwload_disable and fwload_force.

sys/dev/isp/ispvar.h:
	Add a new ISP_CFG_FWLOAD_FORCE configuration bit.

Reviewed by:	mav
MFC after:	1 week
Sponsored by:	Spectra Logic
Differential Revision:	<https://reviews.freebsd.org/D45688>
2024-06-24 16:47:55 -04:00

1311 lines
36 KiB
C

/*-
* SPDX-License-Identifier: BSD-2-Clause
*
* Copyright (c) 2009-2020 Alexander Motin <mav@FreeBSD.org>
* Copyright (c) 1997-2008 by Matthew Jacob
* 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 immediately at the beginning of the file, without modification,
* this list of conditions, and the following disclaimer.
* 2. The name of the author may not be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* 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.
*/
/*
* PCI specific probe and attach routines for Qlogic ISP SCSI adapters.
* FreeBSD Version.
*/
#include <sys/param.h>
#include <sys/systm.h>
#include <sys/kernel.h>
#include <sys/module.h>
#include <sys/linker.h>
#include <sys/bus.h>
#include <sys/stdint.h>
#include <dev/pci/pcireg.h>
#include <dev/pci/pcivar.h>
#include <machine/bus.h>
#include <machine/resource.h>
#include <sys/rman.h>
#include <sys/malloc.h>
#include <sys/uio.h>
#include <dev/isp/isp_freebsd.h>
static uint32_t isp_pci_rd_reg_2400(ispsoftc_t *, int);
static void isp_pci_wr_reg_2400(ispsoftc_t *, int, uint32_t);
static uint32_t isp_pci_rd_reg_2600(ispsoftc_t *, int);
static void isp_pci_wr_reg_2600(ispsoftc_t *, int, uint32_t);
static void isp_pci_run_isr_2400(ispsoftc_t *);
static int isp_pci_mbxdma(ispsoftc_t *);
static void isp_pci_mbxdmafree(ispsoftc_t *);
static int isp_pci_irqsetup(ispsoftc_t *);
static struct ispmdvec mdvec_2400 = {
isp_pci_run_isr_2400,
isp_pci_rd_reg_2400,
isp_pci_wr_reg_2400,
isp_pci_mbxdma,
isp_send_cmd,
isp_pci_irqsetup,
NULL
};
static struct ispmdvec mdvec_2500 = {
isp_pci_run_isr_2400,
isp_pci_rd_reg_2400,
isp_pci_wr_reg_2400,
isp_pci_mbxdma,
isp_send_cmd,
isp_pci_irqsetup,
NULL
};
static struct ispmdvec mdvec_2600 = {
isp_pci_run_isr_2400,
isp_pci_rd_reg_2600,
isp_pci_wr_reg_2600,
isp_pci_mbxdma,
isp_send_cmd,
isp_pci_irqsetup,
NULL
};
static struct ispmdvec mdvec_2700 = {
isp_pci_run_isr_2400,
isp_pci_rd_reg_2600,
isp_pci_wr_reg_2600,
isp_pci_mbxdma,
isp_send_cmd,
isp_pci_irqsetup,
NULL
};
static struct ispmdvec mdvec_2800 = {
isp_pci_run_isr_2400,
isp_pci_rd_reg_2600,
isp_pci_wr_reg_2600,
isp_pci_mbxdma,
isp_send_cmd,
isp_pci_irqsetup,
NULL
};
#ifndef PCIM_CMD_INVEN
#define PCIM_CMD_INVEN 0x10
#endif
#ifndef PCIM_CMD_BUSMASTEREN
#define PCIM_CMD_BUSMASTEREN 0x0004
#endif
#ifndef PCIM_CMD_PERRESPEN
#define PCIM_CMD_PERRESPEN 0x0040
#endif
#ifndef PCIM_CMD_SEREN
#define PCIM_CMD_SEREN 0x0100
#endif
#ifndef PCIM_CMD_INTX_DISABLE
#define PCIM_CMD_INTX_DISABLE 0x0400
#endif
#ifndef PCIR_COMMAND
#define PCIR_COMMAND 0x04
#endif
#ifndef PCIR_CACHELNSZ
#define PCIR_CACHELNSZ 0x0c
#endif
#ifndef PCIR_LATTIMER
#define PCIR_LATTIMER 0x0d
#endif
#ifndef PCIR_ROMADDR
#define PCIR_ROMADDR 0x30
#endif
#define PCI_VENDOR_QLOGIC 0x1077
#define PCI_PRODUCT_QLOGIC_ISP2422 0x2422
#define PCI_PRODUCT_QLOGIC_ISP2432 0x2432
#define PCI_PRODUCT_QLOGIC_ISP2532 0x2532
#define PCI_PRODUCT_QLOGIC_ISP5432 0x5432
#define PCI_PRODUCT_QLOGIC_ISP2031 0x2031
#define PCI_PRODUCT_QLOGIC_ISP8031 0x8031
#define PCI_PRODUCT_QLOGIC_ISP2684 0x2171
#define PCI_PRODUCT_QLOGIC_ISP2692 0x2b61
#define PCI_PRODUCT_QLOGIC_ISP2714 0x2071
#define PCI_PRODUCT_QLOGIC_ISP2722 0x2261
#define PCI_PRODUCT_QLOGIC_ISP2812 0x2281
#define PCI_PRODUCT_QLOGIC_ISP2814 0x2081
#define PCI_QLOGIC_ISP2422 \
((PCI_PRODUCT_QLOGIC_ISP2422 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2432 \
((PCI_PRODUCT_QLOGIC_ISP2432 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2532 \
((PCI_PRODUCT_QLOGIC_ISP2532 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP5432 \
((PCI_PRODUCT_QLOGIC_ISP5432 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2031 \
((PCI_PRODUCT_QLOGIC_ISP2031 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP8031 \
((PCI_PRODUCT_QLOGIC_ISP8031 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2684 \
((PCI_PRODUCT_QLOGIC_ISP2684 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2692 \
((PCI_PRODUCT_QLOGIC_ISP2692 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2714 \
((PCI_PRODUCT_QLOGIC_ISP2714 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2722 \
((PCI_PRODUCT_QLOGIC_ISP2722 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2812 \
((PCI_PRODUCT_QLOGIC_ISP2812 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_QLOGIC_ISP2814 \
((PCI_PRODUCT_QLOGIC_ISP2814 << 16) | PCI_VENDOR_QLOGIC)
#define PCI_DFLT_LTNCY 0x40
#define PCI_DFLT_LNSZ 0x10
static int isp_pci_probe (device_t);
static int isp_pci_attach (device_t);
static int isp_pci_detach (device_t);
struct isp_pcisoftc {
ispsoftc_t pci_isp;
struct resource * regs;
struct resource * regs1;
struct resource * regs2;
struct {
int iqd;
struct resource * irq;
void * ih;
} irq[ISP_MAX_IRQS];
int rtp;
int rgd;
int rtp1;
int rgd1;
int rtp2;
int rgd2;
bus_dma_tag_t dmat;
int msicount;
};
static device_method_t isp_pci_methods[] = {
/* Device interface */
DEVMETHOD(device_probe, isp_pci_probe),
DEVMETHOD(device_attach, isp_pci_attach),
DEVMETHOD(device_detach, isp_pci_detach),
{ 0, 0 }
};
static driver_t isp_pci_driver = {
"isp", isp_pci_methods, sizeof (struct isp_pcisoftc)
};
DRIVER_MODULE(isp, pci, isp_pci_driver, 0, 0);
MODULE_DEPEND(isp, cam, 1, 1, 1);
MODULE_DEPEND(isp, firmware, 1, 1, 1);
static int isp_nvports = 0;
static int
isp_pci_probe(device_t dev)
{
switch ((pci_get_device(dev) << 16) | (pci_get_vendor(dev))) {
case PCI_QLOGIC_ISP2422:
device_set_desc(dev, "Qlogic ISP 2422 PCI FC-AL Adapter");
break;
case PCI_QLOGIC_ISP2432:
device_set_desc(dev, "Qlogic ISP 2432 PCI FC-AL Adapter");
break;
case PCI_QLOGIC_ISP2532:
device_set_desc(dev, "Qlogic ISP 2532 PCI FC-AL Adapter");
break;
case PCI_QLOGIC_ISP5432:
device_set_desc(dev, "Qlogic ISP 5432 PCI FC-AL Adapter");
break;
case PCI_QLOGIC_ISP2031:
device_set_desc(dev, "Qlogic ISP 2031 PCI FC-AL Adapter");
break;
case PCI_QLOGIC_ISP8031:
device_set_desc(dev, "Qlogic ISP 8031 PCI FCoE Adapter");
break;
case PCI_QLOGIC_ISP2684:
device_set_desc(dev, "Qlogic ISP 2684 PCI FC Adapter");
break;
case PCI_QLOGIC_ISP2692:
device_set_desc(dev, "Qlogic ISP 2692 PCI FC Adapter");
break;
case PCI_QLOGIC_ISP2714:
device_set_desc(dev, "Qlogic ISP 2714 PCI FC Adapter");
break;
case PCI_QLOGIC_ISP2722:
device_set_desc(dev, "Qlogic ISP 2722 PCI FC Adapter");
break;
case PCI_QLOGIC_ISP2812:
device_set_desc(dev, "Qlogic ISP 2812 PCI FC Adapter");
break;
case PCI_QLOGIC_ISP2814:
device_set_desc(dev, "Qlogic ISP 2814 PCI FC Adapter");
break;
default:
return (ENXIO);
}
if (isp_announced == 0 && bootverbose) {
printf("Qlogic ISP Driver, FreeBSD Version %d.%d, "
"Core Version %d.%d\n",
ISP_PLATFORM_VERSION_MAJOR, ISP_PLATFORM_VERSION_MINOR,
ISP_CORE_VERSION_MAJOR, ISP_CORE_VERSION_MINOR);
isp_announced++;
}
/*
* XXXX: Here is where we might load the f/w module
* XXXX: (or increase a reference count to it).
*/
return (BUS_PROBE_DEFAULT);
}
static void
isp_get_generic_options(device_t dev, ispsoftc_t *isp)
{
int tval;
tval = 0;
if (resource_int_value(device_get_name(dev), device_get_unit(dev), "fwload_disable", &tval) == 0 && tval != 0) {
isp->isp_confopts |= ISP_CFG_NORELOAD;
}
tval = 0;
if (resource_int_value(device_get_name(dev), device_get_unit(dev), "fwload_force", &tval) == 0 && tval != 0) {
isp->isp_confopts |= ISP_CFG_FWLOAD_FORCE;
}
if ((isp->isp_confopts & (ISP_CFG_NORELOAD|ISP_CFG_FWLOAD_FORCE)) ==
(ISP_CFG_NORELOAD|ISP_CFG_FWLOAD_FORCE)) {
device_printf(dev, "WARNING: both fwload_disable and "
"fwload_force set, ispfw(4) loading disabled\n");
}
tval = 0;
if (resource_int_value(device_get_name(dev), device_get_unit(dev), "ignore_nvram", &tval) == 0 && tval != 0) {
isp->isp_confopts |= ISP_CFG_NONVRAM;
}
tval = 0;
(void) resource_int_value(device_get_name(dev), device_get_unit(dev), "debug", &tval);
if (tval) {
isp->isp_dblev = tval;
} else {
isp->isp_dblev = ISP_LOGWARN|ISP_LOGERR;
}
if (bootverbose) {
isp->isp_dblev |= ISP_LOGCONFIG|ISP_LOGINFO;
}
tval = -1;
(void) resource_int_value(device_get_name(dev), device_get_unit(dev), "vports", &tval);
if (tval > 0 && tval <= 254) {
isp_nvports = tval;
}
tval = 7;
(void) resource_int_value(device_get_name(dev), device_get_unit(dev), "quickboot_time", &tval);
isp_quickboot_time = tval;
}
static void
isp_get_specific_options(device_t dev, int chan, ispsoftc_t *isp)
{
const char *sptr;
int tval = 0;
char prefix[12], name[16];
if (chan == 0)
prefix[0] = 0;
else
snprintf(prefix, sizeof(prefix), "chan%d.", chan);
snprintf(name, sizeof(name), "%siid", prefix);
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval)) {
ISP_FC_PC(isp, chan)->default_id = 109 - chan;
} else {
ISP_FC_PC(isp, chan)->default_id = tval - chan;
isp->isp_confopts |= ISP_CFG_OWNLOOPID;
}
tval = -1;
snprintf(name, sizeof(name), "%srole", prefix);
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval) == 0) {
switch (tval) {
case ISP_ROLE_NONE:
case ISP_ROLE_INITIATOR:
case ISP_ROLE_TARGET:
case ISP_ROLE_BOTH:
device_printf(dev, "Chan %d setting role to 0x%x\n", chan, tval);
break;
default:
tval = -1;
break;
}
}
if (tval == -1) {
tval = ISP_DEFAULT_ROLES;
}
ISP_FC_PC(isp, chan)->def_role = tval;
tval = 0;
snprintf(name, sizeof(name), "%sfullduplex", prefix);
if (resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval) == 0 && tval != 0) {
isp->isp_confopts |= ISP_CFG_FULL_DUPLEX;
}
sptr = NULL;
snprintf(name, sizeof(name), "%stopology", prefix);
if (resource_string_value(device_get_name(dev), device_get_unit(dev),
name, (const char **) &sptr) == 0 && sptr != NULL) {
if (strcmp(sptr, "lport") == 0) {
isp->isp_confopts |= ISP_CFG_LPORT;
} else if (strcmp(sptr, "nport") == 0) {
isp->isp_confopts |= ISP_CFG_NPORT;
} else if (strcmp(sptr, "lport-only") == 0) {
isp->isp_confopts |= ISP_CFG_LPORT_ONLY;
} else if (strcmp(sptr, "nport-only") == 0) {
isp->isp_confopts |= ISP_CFG_NPORT_ONLY;
}
}
#ifdef ISP_FCTAPE_OFF
isp->isp_confopts |= ISP_CFG_NOFCTAPE;
#else
isp->isp_confopts |= ISP_CFG_FCTAPE;
#endif
tval = 0;
snprintf(name, sizeof(name), "%snofctape", prefix);
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval);
if (tval) {
isp->isp_confopts &= ~ISP_CFG_FCTAPE;
isp->isp_confopts |= ISP_CFG_NOFCTAPE;
}
tval = 0;
snprintf(name, sizeof(name), "%sfctape", prefix);
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval);
if (tval) {
isp->isp_confopts &= ~ISP_CFG_NOFCTAPE;
isp->isp_confopts |= ISP_CFG_FCTAPE;
}
/*
* Because the resource_*_value functions can neither return
* 64 bit integer values, nor can they be directly coerced
* to interpret the right hand side of the assignment as
* you want them to interpret it, we have to force WWN
* hint replacement to specify WWN strings with a leading
* 'w' (e..g w50000000aaaa0001). Sigh.
*/
sptr = NULL;
snprintf(name, sizeof(name), "%sportwwn", prefix);
tval = resource_string_value(device_get_name(dev), device_get_unit(dev),
name, (const char **) &sptr);
if (tval == 0 && sptr != NULL && *sptr++ == 'w') {
char *eptr = NULL;
ISP_FC_PC(isp, chan)->def_wwpn = strtouq(sptr, &eptr, 16);
if (eptr < sptr + 16 || ISP_FC_PC(isp, chan)->def_wwpn == -1) {
device_printf(dev, "mangled portwwn hint '%s'\n", sptr);
ISP_FC_PC(isp, chan)->def_wwpn = 0;
}
}
sptr = NULL;
snprintf(name, sizeof(name), "%snodewwn", prefix);
tval = resource_string_value(device_get_name(dev), device_get_unit(dev),
name, (const char **) &sptr);
if (tval == 0 && sptr != NULL && *sptr++ == 'w') {
char *eptr = NULL;
ISP_FC_PC(isp, chan)->def_wwnn = strtouq(sptr, &eptr, 16);
if (eptr < sptr + 16 || ISP_FC_PC(isp, chan)->def_wwnn == 0) {
device_printf(dev, "mangled nodewwn hint '%s'\n", sptr);
ISP_FC_PC(isp, chan)->def_wwnn = 0;
}
}
tval = -1;
snprintf(name, sizeof(name), "%sloop_down_limit", prefix);
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval);
if (tval >= 0 && tval < 0xffff) {
ISP_FC_PC(isp, chan)->loop_down_limit = tval;
} else {
ISP_FC_PC(isp, chan)->loop_down_limit = isp_loop_down_limit;
}
tval = -1;
snprintf(name, sizeof(name), "%sgone_device_time", prefix);
(void) resource_int_value(device_get_name(dev), device_get_unit(dev),
name, &tval);
if (tval >= 0 && tval < 0xffff) {
ISP_FC_PC(isp, chan)->gone_device_time = tval;
} else {
ISP_FC_PC(isp, chan)->gone_device_time = isp_gone_device_time;
}
}
static int
isp_pci_attach(device_t dev)
{
struct isp_pcisoftc *pcs = device_get_softc(dev);
ispsoftc_t *isp = &pcs->pci_isp;
int i;
uint32_t data, cmd, linesz;
size_t psize, xsize;
isp->isp_dev = dev;
isp->isp_nchan = 1;
mtx_init(&isp->isp_lock, "isp", NULL, MTX_DEF);
/*
* Get Generic Options
*/
isp_nvports = 0;
isp_get_generic_options(dev, isp);
linesz = PCI_DFLT_LNSZ;
pcs->regs = pcs->regs2 = NULL;
pcs->rgd = pcs->rtp = 0;
isp->isp_nchan += isp_nvports;
switch (pci_get_devid(dev)) {
case PCI_QLOGIC_ISP2422:
case PCI_QLOGIC_ISP2432:
isp->isp_did = 0x2400;
isp->isp_mdvec = &mdvec_2400;
isp->isp_type = ISP_HA_FC_2400;
break;
case PCI_QLOGIC_ISP2532:
isp->isp_did = 0x2500;
isp->isp_mdvec = &mdvec_2500;
isp->isp_type = ISP_HA_FC_2500;
break;
case PCI_QLOGIC_ISP5432:
isp->isp_did = 0x2500;
isp->isp_mdvec = &mdvec_2500;
isp->isp_type = ISP_HA_FC_2500;
break;
case PCI_QLOGIC_ISP2031:
case PCI_QLOGIC_ISP8031:
isp->isp_did = 0x2600;
isp->isp_mdvec = &mdvec_2600;
isp->isp_type = ISP_HA_FC_2600;
break;
case PCI_QLOGIC_ISP2684:
case PCI_QLOGIC_ISP2692:
case PCI_QLOGIC_ISP2714:
case PCI_QLOGIC_ISP2722:
isp->isp_did = 0x2700;
isp->isp_mdvec = &mdvec_2700;
isp->isp_type = ISP_HA_FC_2700;
break;
case PCI_QLOGIC_ISP2812:
case PCI_QLOGIC_ISP2814:
isp->isp_did = 0x2800;
isp->isp_mdvec = &mdvec_2800;
isp->isp_type = ISP_HA_FC_2800;
break;
default:
device_printf(dev, "unknown device type\n");
goto bad;
break;
}
isp->isp_revision = pci_get_revid(dev);
if (IS_26XX(isp)) {
pcs->rtp = SYS_RES_MEMORY;
pcs->rgd = PCIR_BAR(0);
pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd,
RF_ACTIVE);
pcs->rtp1 = SYS_RES_MEMORY;
pcs->rgd1 = PCIR_BAR(2);
pcs->regs1 = bus_alloc_resource_any(dev, pcs->rtp1, &pcs->rgd1,
RF_ACTIVE);
pcs->rtp2 = SYS_RES_MEMORY;
pcs->rgd2 = PCIR_BAR(4);
pcs->regs2 = bus_alloc_resource_any(dev, pcs->rtp2, &pcs->rgd2,
RF_ACTIVE);
} else {
pcs->rtp = SYS_RES_MEMORY;
pcs->rgd = PCIR_BAR(1);
pcs->regs = bus_alloc_resource_any(dev, pcs->rtp, &pcs->rgd,
RF_ACTIVE);
if (pcs->regs == NULL) {
pcs->rtp = SYS_RES_IOPORT;
pcs->rgd = PCIR_BAR(0);
pcs->regs = bus_alloc_resource_any(dev, pcs->rtp,
&pcs->rgd, RF_ACTIVE);
}
}
if (pcs->regs == NULL) {
device_printf(dev, "Unable to map any ports\n");
goto bad;
}
if (bootverbose) {
device_printf(dev, "Using %s space register mapping\n",
(pcs->rtp == SYS_RES_IOPORT)? "I/O" : "Memory");
}
isp->isp_regs = pcs->regs;
isp->isp_regs2 = pcs->regs2;
psize = sizeof(fcparam) * isp->isp_nchan;
xsize = sizeof(struct isp_fc) * isp->isp_nchan;
isp->isp_param = malloc(psize, M_DEVBUF, M_NOWAIT | M_ZERO);
if (isp->isp_param == NULL) {
device_printf(dev, "cannot allocate parameter data\n");
goto bad;
}
isp->isp_osinfo.fc = malloc(xsize, M_DEVBUF, M_NOWAIT | M_ZERO);
if (isp->isp_osinfo.fc == NULL) {
device_printf(dev, "cannot allocate parameter data\n");
goto bad;
}
/*
* Now that we know who we are (roughly) get/set specific options
*/
for (i = 0; i < isp->isp_nchan; i++) {
isp_get_specific_options(dev, i, isp);
}
/*
* Make sure that SERR, PERR, WRITE INVALIDATE and BUSMASTER are set.
*/
cmd = pci_read_config(dev, PCIR_COMMAND, 2);
cmd |= PCIM_CMD_SEREN | PCIM_CMD_PERRESPEN | PCIM_CMD_BUSMASTEREN | PCIM_CMD_INVEN;
cmd &= ~PCIM_CMD_INTX_DISABLE;
pci_write_config(dev, PCIR_COMMAND, cmd, 2);
/*
* Make sure the Cache Line Size register is set sensibly.
*/
data = pci_read_config(dev, PCIR_CACHELNSZ, 1);
if (data == 0 || (linesz != PCI_DFLT_LNSZ && data != linesz)) {
isp_prt(isp, ISP_LOGDEBUG0, "set PCI line size to %d from %d", linesz, data);
data = linesz;
pci_write_config(dev, PCIR_CACHELNSZ, data, 1);
}
/*
* Make sure the Latency Timer is sane.
*/
data = pci_read_config(dev, PCIR_LATTIMER, 1);
if (data < PCI_DFLT_LTNCY) {
data = PCI_DFLT_LTNCY;
isp_prt(isp, ISP_LOGDEBUG0, "set PCI latency to %d", data);
pci_write_config(dev, PCIR_LATTIMER, data, 1);
}
/*
* Make sure we've disabled the ROM.
*/
data = pci_read_config(dev, PCIR_ROMADDR, 4);
data &= ~1;
pci_write_config(dev, PCIR_ROMADDR, data, 4);
/*
* Last minute checks...
*/
isp->isp_port = pci_get_function(dev);
/*
* Make sure we're in reset state.
*/
ISP_LOCK(isp);
if (isp_reinit(isp, 1) != 0) {
ISP_UNLOCK(isp);
goto bad;
}
ISP_UNLOCK(isp);
if (isp_attach(isp)) {
ISP_LOCK(isp);
isp_shutdown(isp);
ISP_UNLOCK(isp);
goto bad;
}
return (0);
bad:
for (i = 0; i < isp->isp_nirq; i++) {
(void) bus_teardown_intr(dev, pcs->irq[i].irq, pcs->irq[i].ih);
(void) bus_release_resource(dev, SYS_RES_IRQ, pcs->irq[i].iqd,
pcs->irq[0].irq);
}
if (pcs->msicount) {
pci_release_msi(dev);
}
if (pcs->regs)
(void) bus_release_resource(dev, pcs->rtp, pcs->rgd, pcs->regs);
if (pcs->regs1)
(void) bus_release_resource(dev, pcs->rtp1, pcs->rgd1, pcs->regs1);
if (pcs->regs2)
(void) bus_release_resource(dev, pcs->rtp2, pcs->rgd2, pcs->regs2);
if (pcs->pci_isp.isp_param) {
free(pcs->pci_isp.isp_param, M_DEVBUF);
pcs->pci_isp.isp_param = NULL;
}
if (pcs->pci_isp.isp_osinfo.fc) {
free(pcs->pci_isp.isp_osinfo.fc, M_DEVBUF);
pcs->pci_isp.isp_osinfo.fc = NULL;
}
mtx_destroy(&isp->isp_lock);
return (ENXIO);
}
static int
isp_pci_detach(device_t dev)
{
struct isp_pcisoftc *pcs = device_get_softc(dev);
ispsoftc_t *isp = &pcs->pci_isp;
int i, status;
status = isp_detach(isp);
if (status)
return (status);
ISP_LOCK(isp);
isp_shutdown(isp);
ISP_UNLOCK(isp);
for (i = 0; i < isp->isp_nirq; i++) {
(void) bus_teardown_intr(dev, pcs->irq[i].irq, pcs->irq[i].ih);
(void) bus_release_resource(dev, SYS_RES_IRQ, pcs->irq[i].iqd,
pcs->irq[i].irq);
}
if (pcs->msicount)
pci_release_msi(dev);
(void) bus_release_resource(dev, pcs->rtp, pcs->rgd, pcs->regs);
if (pcs->regs1)
(void) bus_release_resource(dev, pcs->rtp1, pcs->rgd1, pcs->regs1);
if (pcs->regs2)
(void) bus_release_resource(dev, pcs->rtp2, pcs->rgd2, pcs->regs2);
isp_pci_mbxdmafree(isp);
if (pcs->pci_isp.isp_param) {
free(pcs->pci_isp.isp_param, M_DEVBUF);
pcs->pci_isp.isp_param = NULL;
}
if (pcs->pci_isp.isp_osinfo.fc) {
free(pcs->pci_isp.isp_osinfo.fc, M_DEVBUF);
pcs->pci_isp.isp_osinfo.fc = NULL;
}
mtx_destroy(&isp->isp_lock);
return (0);
}
#define BXR2(isp, off) bus_read_2((isp)->isp_regs, (off))
#define BXW2(isp, off, v) bus_write_2((isp)->isp_regs, (off), (v))
#define BXR4(isp, off) bus_read_4((isp)->isp_regs, (off))
#define BXW4(isp, off, v) bus_write_4((isp)->isp_regs, (off), (v))
#define B2R4(isp, off) bus_read_4((isp)->isp_regs2, (off))
#define B2W4(isp, off, v) bus_write_4((isp)->isp_regs2, (off), (v))
static void
isp_pci_run_isr_2400(ispsoftc_t *isp)
{
uint32_t r2hisr;
uint16_t isr, info;
r2hisr = BXR4(isp, BIU2400_R2HSTS);
isp_prt(isp, ISP_LOGDEBUG3, "RISC2HOST ISR 0x%x", r2hisr);
if ((r2hisr & BIU_R2HST_INTR) == 0)
return;
isr = r2hisr & BIU_R2HST_ISTAT_MASK;
info = (r2hisr >> 16);
switch (isr) {
case ISPR2HST_ROM_MBX_OK:
case ISPR2HST_ROM_MBX_FAIL:
case ISPR2HST_MBX_OK:
case ISPR2HST_MBX_FAIL:
isp_intr_mbox(isp, info);
break;
case ISPR2HST_ASYNC_EVENT:
isp_intr_async(isp, info);
break;
case ISPR2HST_RSPQ_UPDATE:
isp_intr_respq(isp);
break;
case ISPR2HST_RSPQ_UPDATE2:
#ifdef ISP_TARGET_MODE
case ISPR2HST_ATIO_RSPQ_UPDATE:
#endif
isp_intr_respq(isp);
/* FALLTHROUGH */
#ifdef ISP_TARGET_MODE
case ISPR2HST_ATIO_UPDATE:
case ISPR2HST_ATIO_UPDATE2:
isp_intr_atioq(isp);
#endif
break;
default:
isp_prt(isp, ISP_LOGERR, "unknown interrupt 0x%x\n", r2hisr);
}
ISP_WRITE(isp, BIU2400_HCCR, HCCR_2400_CMD_CLEAR_RISC_INT);
}
static uint32_t
isp_pci_rd_reg_2400(ispsoftc_t *isp, int regoff)
{
int block = regoff & _BLK_REG_MASK;
switch (block) {
case BIU_BLOCK:
return (BXR4(isp, regoff));
case MBOX_BLOCK:
return (BXR2(isp, regoff));
}
isp_prt(isp, ISP_LOGERR, "unknown block read at 0x%x", regoff);
return (0xffffffff);
}
static void
isp_pci_wr_reg_2400(ispsoftc_t *isp, int regoff, uint32_t val)
{
int block = regoff & _BLK_REG_MASK;
switch (block) {
case BIU_BLOCK:
BXW4(isp, regoff, val);
#ifdef MEMORYBARRIERW
if (regoff == BIU2400_REQINP ||
regoff == BIU2400_RSPOUTP ||
regoff == BIU2400_PRI_REQINP ||
regoff == BIU2400_ATIO_RSPOUTP)
MEMORYBARRIERW(isp, SYNC_REG, regoff, 4, -1)
else
#endif
MEMORYBARRIER(isp, SYNC_REG, regoff, 4, -1);
return;
case MBOX_BLOCK:
BXW2(isp, regoff, val);
MEMORYBARRIER(isp, SYNC_REG, regoff, 2, -1);
return;
}
isp_prt(isp, ISP_LOGERR, "unknown block write at 0x%x", regoff);
}
static uint32_t
isp_pci_rd_reg_2600(ispsoftc_t *isp, int regoff)
{
uint32_t rv;
switch (regoff) {
case BIU2400_PRI_REQINP:
case BIU2400_PRI_REQOUTP:
isp_prt(isp, ISP_LOGERR, "unknown register read at 0x%x",
regoff);
rv = 0xffffffff;
break;
case BIU2400_REQINP:
rv = B2R4(isp, 0x00);
break;
case BIU2400_REQOUTP:
rv = B2R4(isp, 0x04);
break;
case BIU2400_RSPINP:
rv = B2R4(isp, 0x08);
break;
case BIU2400_RSPOUTP:
rv = B2R4(isp, 0x0c);
break;
case BIU2400_ATIO_RSPINP:
rv = B2R4(isp, 0x10);
break;
case BIU2400_ATIO_RSPOUTP:
rv = B2R4(isp, 0x14);
break;
default:
rv = isp_pci_rd_reg_2400(isp, regoff);
break;
}
return (rv);
}
static void
isp_pci_wr_reg_2600(ispsoftc_t *isp, int regoff, uint32_t val)
{
int off;
switch (regoff) {
case BIU2400_PRI_REQINP:
case BIU2400_PRI_REQOUTP:
isp_prt(isp, ISP_LOGERR, "unknown register write at 0x%x",
regoff);
return;
case BIU2400_REQINP:
off = 0x00;
break;
case BIU2400_REQOUTP:
off = 0x04;
break;
case BIU2400_RSPINP:
off = 0x08;
break;
case BIU2400_RSPOUTP:
off = 0x0c;
break;
case BIU2400_ATIO_RSPINP:
off = 0x10;
break;
case BIU2400_ATIO_RSPOUTP:
off = 0x14;
break;
default:
isp_pci_wr_reg_2400(isp, regoff, val);
return;
}
B2W4(isp, off, val);
}
struct imush {
bus_addr_t maddr;
int error;
};
static void
imc(void *arg, bus_dma_segment_t *segs, int nseg, int error)
{
struct imush *imushp = (struct imush *) arg;
if (!(imushp->error = error))
imushp->maddr = segs[0].ds_addr;
}
static int
isp_pci_mbxdma(ispsoftc_t *isp)
{
bus_dma_tag_t ptag;
caddr_t base;
uint32_t len;
int i, error, cmap;
bus_size_t slim; /* segment size */
struct imush im;
#ifdef ISP_TARGET_MODE
isp_ecmd_t *ecmd;
#endif
/* Already been here? If so, leave... */
if (isp->isp_xflist != NULL)
return (0);
if (isp->isp_rquest != NULL && isp->isp_maxcmds == 0)
return (0);
ISP_UNLOCK(isp);
ptag = bus_get_dma_tag(isp->isp_osinfo.dev);
if (sizeof (bus_size_t) > 4)
slim = (bus_size_t) (1ULL << 32);
else
slim = (bus_size_t) (1UL << 31);
if (isp->isp_rquest != NULL)
goto gotmaxcmds;
/*
* Allocate and map the request queue.
*/
len = ISP_QUEUE_SIZE(RQUEST_QUEUE_LEN(isp));
if (bus_dma_tag_create(ptag, QENTRY_LEN, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
len, 1, len, 0, NULL, NULL, &isp->isp_osinfo.reqdmat)) {
isp_prt(isp, ISP_LOGERR, "cannot create request DMA tag");
goto bad;
}
if (bus_dmamem_alloc(isp->isp_osinfo.reqdmat, (void **)&base,
BUS_DMA_COHERENT, &isp->isp_osinfo.reqmap) != 0) {
isp_prt(isp, ISP_LOGERR, "cannot allocate request DMA memory");
bus_dma_tag_destroy(isp->isp_osinfo.reqdmat);
goto bad;
}
isp->isp_rquest = base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.reqdmat, isp->isp_osinfo.reqmap,
base, len, imc, &im, BUS_DMA_NOWAIT) || im.error) {
isp_prt(isp, ISP_LOGERR, "error loading request DMA map %d", im.error);
goto bad;
}
isp_prt(isp, ISP_LOGDEBUG0, "request area @ 0x%jx/0x%jx",
(uintmax_t)im.maddr, (uintmax_t)len);
isp->isp_rquest_dma = im.maddr;
#ifdef ISP_TARGET_MODE
/*
* Allocate region for external DMA addressable command/status structures.
*/
len = N_XCMDS * XCMD_SIZE;
if (bus_dma_tag_create(ptag, XCMD_SIZE, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
len, 1, len, 0, NULL, NULL, &isp->isp_osinfo.ecmd_dmat)) {
isp_prt(isp, ISP_LOGERR, "cannot create ECMD DMA tag");
goto bad;
}
if (bus_dmamem_alloc(isp->isp_osinfo.ecmd_dmat, (void **)&base,
BUS_DMA_COHERENT, &isp->isp_osinfo.ecmd_map) != 0) {
isp_prt(isp, ISP_LOGERR, "cannot allocate ECMD DMA memory");
bus_dma_tag_destroy(isp->isp_osinfo.ecmd_dmat);
goto bad;
}
isp->isp_osinfo.ecmd_base = (isp_ecmd_t *)base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.ecmd_dmat, isp->isp_osinfo.ecmd_map,
base, len, imc, &im, BUS_DMA_NOWAIT) || im.error) {
isp_prt(isp, ISP_LOGERR, "error loading ECMD DMA map %d", im.error);
goto bad;
}
isp_prt(isp, ISP_LOGDEBUG0, "ecmd area @ 0x%jx/0x%jx",
(uintmax_t)im.maddr, (uintmax_t)len);
isp->isp_osinfo.ecmd_dma = im.maddr;
isp->isp_osinfo.ecmd_free = (isp_ecmd_t *)base;
for (ecmd = isp->isp_osinfo.ecmd_free;
ecmd < &isp->isp_osinfo.ecmd_free[N_XCMDS]; ecmd++) {
if (ecmd == &isp->isp_osinfo.ecmd_free[N_XCMDS - 1])
ecmd->next = NULL;
else
ecmd->next = ecmd + 1;
}
#endif
/*
* Allocate and map the result queue.
*/
len = ISP_QUEUE_SIZE(RESULT_QUEUE_LEN(isp));
if (bus_dma_tag_create(ptag, QENTRY_LEN, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
len, 1, len, 0, NULL, NULL, &isp->isp_osinfo.respdmat)) {
isp_prt(isp, ISP_LOGERR, "cannot create response DMA tag");
goto bad;
}
if (bus_dmamem_alloc(isp->isp_osinfo.respdmat, (void **)&base,
BUS_DMA_COHERENT, &isp->isp_osinfo.respmap) != 0) {
isp_prt(isp, ISP_LOGERR, "cannot allocate response DMA memory");
bus_dma_tag_destroy(isp->isp_osinfo.respdmat);
goto bad;
}
isp->isp_result = base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.respdmat, isp->isp_osinfo.respmap,
base, len, imc, &im, BUS_DMA_NOWAIT) || im.error) {
isp_prt(isp, ISP_LOGERR, "error loading response DMA map %d", im.error);
goto bad;
}
isp_prt(isp, ISP_LOGDEBUG0, "response area @ 0x%jx/0x%jx",
(uintmax_t)im.maddr, (uintmax_t)len);
isp->isp_result_dma = im.maddr;
#ifdef ISP_TARGET_MODE
/*
* Allocate and map ATIO queue.
*/
len = ISP_QUEUE_SIZE(ATIO_QUEUE_LEN(isp));
if (bus_dma_tag_create(ptag, QENTRY_LEN, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
len, 1, len, 0, NULL, NULL, &isp->isp_osinfo.atiodmat)) {
isp_prt(isp, ISP_LOGERR, "cannot create ATIO DMA tag");
goto bad;
}
if (bus_dmamem_alloc(isp->isp_osinfo.atiodmat, (void **)&base,
BUS_DMA_COHERENT, &isp->isp_osinfo.atiomap) != 0) {
isp_prt(isp, ISP_LOGERR, "cannot allocate ATIO DMA memory");
bus_dma_tag_destroy(isp->isp_osinfo.atiodmat);
goto bad;
}
isp->isp_atioq = base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.atiodmat, isp->isp_osinfo.atiomap,
base, len, imc, &im, BUS_DMA_NOWAIT) || im.error) {
isp_prt(isp, ISP_LOGERR, "error loading ATIO DMA map %d", im.error);
goto bad;
}
isp_prt(isp, ISP_LOGDEBUG0, "ATIO area @ 0x%jx/0x%jx",
(uintmax_t)im.maddr, (uintmax_t)len);
isp->isp_atioq_dma = im.maddr;
#endif
if (bus_dma_tag_create(ptag, 64, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
2*QENTRY_LEN, 1, 2*QENTRY_LEN, 0, NULL, NULL,
&isp->isp_osinfo.iocbdmat)) {
goto bad;
}
if (bus_dmamem_alloc(isp->isp_osinfo.iocbdmat,
(void **)&base, BUS_DMA_COHERENT, &isp->isp_osinfo.iocbmap) != 0)
goto bad;
isp->isp_iocb = base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.iocbdmat, isp->isp_osinfo.iocbmap,
base, 2*QENTRY_LEN, imc, &im, BUS_DMA_NOWAIT) || im.error)
goto bad;
isp->isp_iocb_dma = im.maddr;
if (bus_dma_tag_create(ptag, 64, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
ISP_FC_SCRLEN, 1, ISP_FC_SCRLEN, 0, NULL, NULL,
&isp->isp_osinfo.scdmat))
goto bad;
for (cmap = 0; cmap < isp->isp_nchan; cmap++) {
struct isp_fc *fc = ISP_FC_PC(isp, cmap);
if (bus_dmamem_alloc(isp->isp_osinfo.scdmat,
(void **)&base, BUS_DMA_COHERENT, &fc->scmap) != 0)
goto bad;
FCPARAM(isp, cmap)->isp_scratch = base;
im.error = 0;
if (bus_dmamap_load(isp->isp_osinfo.scdmat, fc->scmap,
base, ISP_FC_SCRLEN, imc, &im, BUS_DMA_NOWAIT) ||
im.error) {
bus_dmamem_free(isp->isp_osinfo.scdmat,
base, fc->scmap);
FCPARAM(isp, cmap)->isp_scratch = NULL;
goto bad;
}
FCPARAM(isp, cmap)->isp_scdma = im.maddr;
for (i = 0; i < INITIAL_NEXUS_COUNT; i++) {
struct isp_nexus *n = malloc(sizeof (struct isp_nexus), M_DEVBUF, M_NOWAIT | M_ZERO);
if (n == NULL) {
while (fc->nexus_free_list) {
n = fc->nexus_free_list;
fc->nexus_free_list = n->next;
free(n, M_DEVBUF);
}
goto bad;
}
n->next = fc->nexus_free_list;
fc->nexus_free_list = n;
}
}
if (isp->isp_maxcmds == 0) {
ISP_LOCK(isp);
return (0);
}
gotmaxcmds:
if (bus_dma_tag_create(ptag, 1, slim,
BUS_SPACE_MAXADDR, BUS_SPACE_MAXADDR, NULL, NULL,
(ISP_NSEG64_MAX - 1) * PAGE_SIZE, ISP_NSEG64_MAX,
(ISP_NSEG64_MAX - 1) * PAGE_SIZE, 0,
busdma_lock_mutex, &isp->isp_lock, &isp->isp_osinfo.dmat))
goto bad;
len = isp->isp_maxcmds * sizeof (struct isp_pcmd);
isp->isp_osinfo.pcmd_pool = (struct isp_pcmd *)
malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
for (i = 0; i < isp->isp_maxcmds; i++) {
struct isp_pcmd *pcmd = &isp->isp_osinfo.pcmd_pool[i];
error = bus_dmamap_create(isp->isp_osinfo.dmat, 0, &pcmd->dmap);
if (error) {
isp_prt(isp, ISP_LOGERR, "error %d creating per-cmd DMA maps", error);
while (--i >= 0) {
bus_dmamap_destroy(isp->isp_osinfo.dmat,
isp->isp_osinfo.pcmd_pool[i].dmap);
}
goto bad;
}
callout_init_mtx(&pcmd->wdog, &isp->isp_lock, 0);
if (i == isp->isp_maxcmds-1)
pcmd->next = NULL;
else
pcmd->next = &isp->isp_osinfo.pcmd_pool[i+1];
}
isp->isp_osinfo.pcmd_free = &isp->isp_osinfo.pcmd_pool[0];
len = sizeof(isp_hdl_t) * ISP_HANDLE_NUM(isp);
isp->isp_xflist = (isp_hdl_t *) malloc(len, M_DEVBUF, M_WAITOK | M_ZERO);
for (len = 0; len < ISP_HANDLE_NUM(isp) - 1; len++)
isp->isp_xflist[len].cmd = &isp->isp_xflist[len+1];
isp->isp_xffree = isp->isp_xflist;
ISP_LOCK(isp);
return (0);
bad:
isp_pci_mbxdmafree(isp);
ISP_LOCK(isp);
return (1);
}
static void
isp_pci_mbxdmafree(ispsoftc_t *isp)
{
int i;
if (isp->isp_xflist != NULL) {
free(isp->isp_xflist, M_DEVBUF);
isp->isp_xflist = NULL;
}
if (isp->isp_osinfo.pcmd_pool != NULL) {
for (i = 0; i < isp->isp_maxcmds; i++) {
bus_dmamap_destroy(isp->isp_osinfo.dmat,
isp->isp_osinfo.pcmd_pool[i].dmap);
}
free(isp->isp_osinfo.pcmd_pool, M_DEVBUF);
isp->isp_osinfo.pcmd_pool = NULL;
}
if (isp->isp_osinfo.dmat) {
bus_dma_tag_destroy(isp->isp_osinfo.dmat);
isp->isp_osinfo.dmat = NULL;
}
for (i = 0; i < isp->isp_nchan; i++) {
struct isp_fc *fc = ISP_FC_PC(isp, i);
if (FCPARAM(isp, i)->isp_scdma != 0) {
bus_dmamap_unload(isp->isp_osinfo.scdmat,
fc->scmap);
FCPARAM(isp, i)->isp_scdma = 0;
}
if (FCPARAM(isp, i)->isp_scratch != NULL) {
bus_dmamem_free(isp->isp_osinfo.scdmat,
FCPARAM(isp, i)->isp_scratch, fc->scmap);
FCPARAM(isp, i)->isp_scratch = NULL;
}
while (fc->nexus_free_list) {
struct isp_nexus *n = fc->nexus_free_list;
fc->nexus_free_list = n->next;
free(n, M_DEVBUF);
}
}
if (isp->isp_osinfo.scdmat) {
bus_dma_tag_destroy(isp->isp_osinfo.scdmat);
isp->isp_osinfo.scdmat = NULL;
}
if (isp->isp_iocb_dma != 0) {
bus_dmamap_unload(isp->isp_osinfo.iocbdmat,
isp->isp_osinfo.iocbmap);
isp->isp_iocb_dma = 0;
}
if (isp->isp_iocb != NULL) {
bus_dmamem_free(isp->isp_osinfo.iocbdmat,
isp->isp_iocb, isp->isp_osinfo.iocbmap);
bus_dma_tag_destroy(isp->isp_osinfo.iocbdmat);
}
#ifdef ISP_TARGET_MODE
if (isp->isp_atioq_dma != 0) {
bus_dmamap_unload(isp->isp_osinfo.atiodmat,
isp->isp_osinfo.atiomap);
isp->isp_atioq_dma = 0;
}
if (isp->isp_atioq != NULL) {
bus_dmamem_free(isp->isp_osinfo.atiodmat, isp->isp_atioq,
isp->isp_osinfo.atiomap);
bus_dma_tag_destroy(isp->isp_osinfo.atiodmat);
isp->isp_atioq = NULL;
}
#endif
if (isp->isp_result_dma != 0) {
bus_dmamap_unload(isp->isp_osinfo.respdmat,
isp->isp_osinfo.respmap);
isp->isp_result_dma = 0;
}
if (isp->isp_result != NULL) {
bus_dmamem_free(isp->isp_osinfo.respdmat, isp->isp_result,
isp->isp_osinfo.respmap);
bus_dma_tag_destroy(isp->isp_osinfo.respdmat);
isp->isp_result = NULL;
}
#ifdef ISP_TARGET_MODE
if (isp->isp_osinfo.ecmd_dma != 0) {
bus_dmamap_unload(isp->isp_osinfo.ecmd_dmat,
isp->isp_osinfo.ecmd_map);
isp->isp_osinfo.ecmd_dma = 0;
}
if (isp->isp_osinfo.ecmd_base != NULL) {
bus_dmamem_free(isp->isp_osinfo.ecmd_dmat, isp->isp_osinfo.ecmd_base,
isp->isp_osinfo.ecmd_map);
bus_dma_tag_destroy(isp->isp_osinfo.ecmd_dmat);
isp->isp_osinfo.ecmd_base = NULL;
}
#endif
if (isp->isp_rquest_dma != 0) {
bus_dmamap_unload(isp->isp_osinfo.reqdmat,
isp->isp_osinfo.reqmap);
isp->isp_rquest_dma = 0;
}
if (isp->isp_rquest != NULL) {
bus_dmamem_free(isp->isp_osinfo.reqdmat, isp->isp_rquest,
isp->isp_osinfo.reqmap);
bus_dma_tag_destroy(isp->isp_osinfo.reqdmat);
isp->isp_rquest = NULL;
}
}
static int
isp_pci_irqsetup(ispsoftc_t *isp)
{
device_t dev = isp->isp_osinfo.dev;
struct isp_pcisoftc *pcs = device_get_softc(dev);
driver_intr_t *f;
int i, max_irq;
/* Allocate IRQs only once. */
if (isp->isp_nirq > 0)
return (0);
ISP_UNLOCK(isp);
if (ISP_CAP_MSIX(isp)) {
max_irq = IS_26XX(isp) ? 3 : (IS_25XX(isp) ? 2 : 0);
resource_int_value(device_get_name(dev),
device_get_unit(dev), "msix", &max_irq);
max_irq = imin(ISP_MAX_IRQS, max_irq);
pcs->msicount = imin(pci_msix_count(dev), max_irq);
if (pcs->msicount > 0 &&
pci_alloc_msix(dev, &pcs->msicount) != 0)
pcs->msicount = 0;
}
if (pcs->msicount == 0) {
max_irq = 1;
resource_int_value(device_get_name(dev),
device_get_unit(dev), "msi", &max_irq);
max_irq = imin(1, max_irq);
pcs->msicount = imin(pci_msi_count(dev), max_irq);
if (pcs->msicount > 0 &&
pci_alloc_msi(dev, &pcs->msicount) != 0)
pcs->msicount = 0;
}
for (i = 0; i < MAX(1, pcs->msicount); i++) {
pcs->irq[i].iqd = i + (pcs->msicount > 0);
pcs->irq[i].irq = bus_alloc_resource_any(dev, SYS_RES_IRQ,
&pcs->irq[i].iqd, RF_ACTIVE | RF_SHAREABLE);
if (pcs->irq[i].irq == NULL) {
device_printf(dev, "could not allocate interrupt\n");
break;
}
if (i == 0)
f = isp_platform_intr;
else if (i == 1)
f = isp_platform_intr_resp;
else
f = isp_platform_intr_atio;
if (bus_setup_intr(dev, pcs->irq[i].irq, ISP_IFLAGS, NULL,
f, isp, &pcs->irq[i].ih)) {
device_printf(dev, "could not setup interrupt\n");
(void) bus_release_resource(dev, SYS_RES_IRQ,
pcs->irq[i].iqd, pcs->irq[i].irq);
break;
}
if (pcs->msicount > 1) {
bus_describe_intr(dev, pcs->irq[i].irq, pcs->irq[i].ih,
"%d", i);
}
isp->isp_nirq = i + 1;
}
ISP_LOCK(isp);
return (isp->isp_nirq == 0);
}