summaryrefslogtreecommitdiff
path: root/sys
diff options
context:
space:
mode:
Diffstat (limited to 'sys')
-rw-r--r--sys/conf/files6
-rw-r--r--sys/dev/ic/qla.c1900
-rw-r--r--sys/dev/ic/qlareg.h626
-rw-r--r--sys/dev/ic/qlavar.h177
-rw-r--r--sys/dev/pci/files.pci6
-rw-r--r--sys/dev/pci/qla_pci.c224
6 files changed, 2937 insertions, 2 deletions
diff --git a/sys/conf/files b/sys/conf/files
index 60f3f081d9f..1e75b16ebb9 100644
--- a/sys/conf/files
+++ b/sys/conf/files
@@ -1,4 +1,4 @@
-# $OpenBSD: files,v 1.564 2014/01/18 09:23:26 jsing Exp $
+# $OpenBSD: files,v 1.565 2014/01/19 06:04:03 jmatthew Exp $
# $NetBSD: files,v 1.87 1996/05/19 17:17:50 jonathan Exp $
# @(#)files.newconf 7.5 (Berkeley) 5/10/93
@@ -195,6 +195,10 @@ file dev/ic/isp.c isp
file dev/ic/isp_openbsd.c isp
file dev/ic/isp_library.c isp
+# QLogic ISP23xx FC Controllers
+device qla: scsi
+file dev/ic/qla.c qla
+
# Advanced Host Controller Interface for Serial ATA
device ahci: scsi, atascsi
file dev/ic/ahci.c ahci | ahci_pci | ahci_jmb needs-flag
diff --git a/sys/dev/ic/qla.c b/sys/dev/ic/qla.c
new file mode 100644
index 00000000000..a94d0e6a9d8
--- /dev/null
+++ b/sys/dev/ic/qla.c
@@ -0,0 +1,1900 @@
+/* $OpenBSD: qla.c,v 1.1 2014/01/19 06:04:03 jmatthew Exp $ */
+
+/*
+ * Copyright (c) 2011 David Gwynne <dlg@openbsd.org>
+ * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/buf.h>
+#include <sys/device.h>
+#include <sys/ioctl.h>
+#include <sys/proc.h>
+#include <sys/malloc.h>
+#include <sys/kernel.h>
+#include <sys/mutex.h>
+#include <sys/rwlock.h>
+#include <sys/sensors.h>
+#include <sys/queue.h>
+
+#include <machine/bus.h>
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/ic/qlareg.h>
+#include <dev/ic/qlavar.h>
+
+/* firmware */
+#include <dev/microcode/isp/asm_2300.h>
+
+struct cfdriver qla_cd = {
+ NULL,
+ "qla",
+ DV_DULL
+};
+
+void qla_scsi_cmd(struct scsi_xfer *);
+struct qla_ccb *qla_scsi_cmd_poll(struct qla_softc *);
+int qla_scsi_probe(struct scsi_link *);
+
+int qla_handle_intr(struct qla_softc *, u_int16_t, u_int16_t);
+
+u_int16_t qla_read(struct qla_softc *, int);
+void qla_write(struct qla_softc *, int, u_int16_t);
+void qla_host_cmd(struct qla_softc *sc, u_int16_t);
+
+int qla_mbox(struct qla_softc *, int, int);
+int qla_sns_req(struct qla_softc *, struct qla_dmamem *, int);
+void qla_mbox_putaddr(u_int16_t *, struct qla_dmamem *);
+u_int16_t qla_read_mbox(struct qla_softc *, int);
+void qla_write_mbox(struct qla_softc *, int, u_int16_t);
+void qla_set_ints(struct qla_softc *, int);
+int qla_read_isr(struct qla_softc *, u_int16_t *, u_int16_t *);
+void qla_clear_isr(struct qla_softc *);
+int qla_queue_reg(struct qla_softc *, enum qla_qptr);
+u_int16_t qla_read_queue_ptr(struct qla_softc *, enum qla_qptr);
+void qla_write_queue_ptr(struct qla_softc *, enum qla_qptr,
+ u_int16_t);
+void qla_update(struct qla_softc *, int);
+int qla_async(struct qla_softc *, u_int16_t);
+void qla_put_marker(struct qla_softc *, void *);
+void qla_put_cmd(struct qla_softc *, void *, struct scsi_xfer *,
+ struct qla_ccb *);
+#if 0
+void qla_put_cmd_cont(struct qla_softc *, void *,
+ struct scsi_xfer *, bus_dmamap_t, int);
+#endif
+struct qla_ccb *qla_handle_resp(struct qla_softc *, u_int16_t);
+void qla_put_data_seg(struct qla_iocb_seg *, bus_dmamap_t, int);
+
+int qla_load_fwchunk_2300(struct qla_softc *,
+ struct qla_dmamem *, const u_int16_t *, u_int32_t);
+int qla_load_firmware_2300(struct qla_softc *);
+int qla_read_nvram(struct qla_softc *);
+
+struct qla_dmamem *qla_dmamem_alloc(struct qla_softc *, size_t);
+void qla_dmamem_free(struct qla_softc *, struct qla_dmamem *);
+
+int qla_add_port(struct qla_softc *, u_int16_t, u_int32_t,
+ u_int32_t);
+int qla_classify_port(struct qla_softc *, u_int32_t, u_int64_t,
+ u_int64_t);
+int qla_get_loop_id(struct qla_softc *sc);
+
+int qla_alloc_ccbs(struct qla_softc *);
+void qla_free_ccbs(struct qla_softc *);
+void *qla_get_ccb(void *);
+void qla_put_ccb(void *, void *);
+
+void qla_dump_iocb(struct qla_softc *, void *);
+void qla_dump_iocb_segs(struct qla_softc *, void *, int);
+
+struct scsi_adapter qla_switch = {
+ qla_scsi_cmd,
+ scsi_minphys,
+ qla_scsi_probe,
+ NULL, /* scsi_free */
+ NULL /* ioctl */
+};
+
+int
+qla_classify_port(struct qla_softc *sc, u_int32_t location,
+ u_int64_t port_name, u_int64_t node_name)
+{
+ struct qla_fc_port *port, *locmatch, *wwnmatch;
+ locmatch = NULL;
+ wwnmatch = NULL;
+
+ /* make sure we don't try to add a port or location twice */
+ TAILQ_FOREACH(port, &sc->sc_ports_new, update) {
+ if ((port->port_name == port_name &&
+ port->node_name == node_name) ||
+ port->location == location)
+ return (QLA_PORT_DISP_DUP);
+ }
+
+ /* if we're attaching, everything is new */
+ if (sc->sc_scan_taskq == NULL)
+ return (QLA_PORT_DISP_NEW);
+
+ TAILQ_FOREACH(port, &sc->sc_ports_gone, update) {
+ if (port->location == location)
+ locmatch = port;
+
+ if (port->port_name == port_name &&
+ port->node_name == node_name)
+ wwnmatch = port;
+ }
+
+ if (locmatch == NULL && wwnmatch == NULL) {
+ return (QLA_PORT_DISP_NEW);
+ } else if (locmatch == wwnmatch) {
+ return (QLA_PORT_DISP_SAME);
+ } else if (wwnmatch != NULL) {
+ return (QLA_PORT_DISP_MOVED);
+ } else {
+ return (QLA_PORT_DISP_CHANGED);
+ }
+}
+
+int
+qla_get_loop_id(struct qla_softc *sc)
+{
+ int i, last;
+
+ if (sc->sc_2k_logins) {
+ i = QLA_2KL_MIN_HANDLE;
+ last = QLA_2KL_MAX_HANDLE;
+ } else {
+ /* if we're an F port, we can have two ranges, but meh */
+ i = QLA_MIN_HANDLE;
+ last = QLA_MAX_HANDLE;
+ }
+ for (; i <= last; i++) {
+ if (sc->sc_targets[i] == NULL)
+ return (i);
+ }
+
+ return (-1);
+}
+
+int
+qla_add_port(struct qla_softc *sc, u_int16_t loopid, u_int32_t portid,
+ u_int32_t location)
+{
+ struct qla_get_port_db *pdb;
+ struct qla_fc_port *port;
+
+ sc->sc_mbox[0] = QLA_MBOX_GET_PORT_DB;
+ if (sc->sc_2k_logins) {
+ sc->sc_mbox[1] = loopid;
+ } else {
+ sc->sc_mbox[1] = loopid << 8;
+ }
+ qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(struct qla_get_port_db), BUS_DMASYNC_PREREAD);
+ if (qla_mbox(sc, 0x00cf, 0x0001)) {
+ if (portid != 0)
+ printf("%s: get port db for %x failed: %x\n",
+ DEVNAME(sc), loopid, sc->sc_mbox[0]);
+ return (1);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat,
+ QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(struct qla_get_port_db),
+ BUS_DMASYNC_POSTREAD);
+ pdb = QLA_DMA_KVA(sc->sc_scratch);
+
+ /* could also check that the port/node names match what we thought we
+ * logged in to?
+ */
+
+ port = malloc(sizeof(*port), M_DEVBUF, M_ZERO | M_NOWAIT);
+ if (port == NULL) {
+ printf("%s: failed to allocate a port structure\n",
+ DEVNAME(sc));
+ return (1);
+ }
+
+ if (letoh16(pdb->prli_svc_word3) & QLA_SVC3_TARGET_ROLE)
+ port->flags |= QLA_PORT_FLAG_IS_TARGET;
+
+ port->port_name = betoh64(pdb->port_name);
+ port->node_name = betoh64(pdb->node_name);
+ port->location = location;
+ port->loopid = loopid;
+ port->portid = portid;
+
+ mtx_enter(&sc->sc_port_mtx);
+ TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update);
+ sc->sc_targets[loopid] = port;
+ mtx_leave(&sc->sc_port_mtx);
+
+ printf("%s: %s %d; port id %06x, name %llx\n",
+ DEVNAME(sc), ISSET(port->flags, QLA_PORT_FLAG_IS_TARGET) ?
+ "target" : "non-target", loopid,
+ (letoh16(pdb->port_id[0]) << 16) | letoh16(pdb->port_id[1]),
+ betoh64(pdb->port_name));
+ return (0);
+}
+
+int
+qla_attach(struct qla_softc *sc)
+{
+ struct scsibus_attach_args saa;
+ struct qla_init_cb *icb;
+ int scan_limit, scan;
+ int i, rv;
+
+ TAILQ_INIT(&sc->sc_ports);
+ TAILQ_INIT(&sc->sc_ports_new);
+ TAILQ_INIT(&sc->sc_ports_gone);
+
+ switch (sc->sc_isp_gen) {
+ case QLA_GEN_ISP2100:
+ case QLA_GEN_ISP2200:
+ printf("not supported yet\n");
+ return (ENXIO);
+
+ case QLA_GEN_ISP23XX:
+ sc->sc_mbox_base = QLA_MBOX_BASE_23XX;
+ break;
+
+ default:
+ printf("unknown isp type\n");
+ return (ENXIO);
+ }
+
+ /* after reset, mbox registers 1-3 should contain the string "ISP " */
+ if (qla_read_mbox(sc, 1) != 0x4953 ||
+ qla_read_mbox(sc, 2) != 0x5020 ||
+ qla_read_mbox(sc, 3) != 0x2020) {
+ /* try releasing the risc processor */
+ qla_host_cmd(sc, QLA_HOST_CMD_RELEASE);
+ }
+
+ sc->sc_mbox[0] = QLA_MBOX_REGISTER_TEST;
+ sc->sc_mbox[1] = 0x1234;
+ sc->sc_mbox[2] = 0x4321;
+ sc->sc_mbox[3] = 0xaaa5;
+ sc->sc_mbox[4] = 0xbbbb;
+ if (qla_mbox(sc, 0x001f, 0x001f)) {
+ printf("register test command failed\n");
+ return (ENXIO);
+ } else if (sc->sc_mbox[1] != 0x1234 || sc->sc_mbox[2] != 0x4321 ||
+ sc->sc_mbox[3] != 0xaaa5 || sc->sc_mbox[4] != 0xbbbb) {
+ printf("register test command failed\n");
+ return (ENXIO);
+ }
+
+ qla_host_cmd(sc, QLA_HOST_CMD_PAUSE);
+ qla_set_ints(sc, 0);
+
+ /* reset */
+ qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_RESET);
+ delay(100);
+ /* clear data and control dma engines? */
+
+ /* wait for soft reset to clear */
+ for (i = 0; i < 1000; i++) {
+ if ((qla_read(sc, QLA_CTRL_STATUS) & QLA_CTRL_RESET) == 0)
+ break;
+
+ delay(100);
+ }
+
+ if (i == 1000) {
+ printf("reset didn't clear\n");
+ qla_set_ints(sc, 0);
+ return (ENXIO);
+ }
+
+ /* reset FPM */
+ qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_FPM0_REGS);
+ qla_write(sc, QLA_FPM_DIAG, QLA_FPM_RESET);
+ qla_write(sc, QLA_FPM_DIAG, 0); /* isp(4) doesn't do this? */
+ qla_write(sc, QLA_CTRL_STATUS, QLA_CTRL_RISC_REGS);
+
+ /* reset risc processor */
+ qla_host_cmd(sc, QLA_HOST_CMD_RESET);
+ delay(100);
+ qla_write(sc, QLA_SEMA, 0);
+ qla_host_cmd(sc, QLA_HOST_CMD_MASK_PARITY); /* from isp(4) */
+ qla_host_cmd(sc, QLA_HOST_CMD_RELEASE);
+
+ /* reset queue pointers */
+ qla_write_queue_ptr(sc, QLA_REQ_QUEUE_IN, 0);
+ qla_write_queue_ptr(sc, QLA_REQ_QUEUE_OUT, 0);
+ qla_write_queue_ptr(sc, QLA_RESP_QUEUE_IN, 0);
+ qla_write_queue_ptr(sc, QLA_RESP_QUEUE_OUT, 0);
+
+ qla_set_ints(sc, 1);
+ /* isp(4) sends QLA_HOST_CMD_BIOS here.. not documented? */
+
+ /* do a basic mailbox operation to check we're alive */
+ sc->sc_mbox[0] = QLA_MBOX_NOP;
+ if (qla_mbox(sc, 0x0001, 0x0001)) {
+ printf("ISP not responding after reset\n");
+ return (ENXIO);
+ }
+
+ if (qla_read_nvram(sc) == 0)
+ sc->sc_nvram_valid = 1;
+ if (qla_load_firmware_2300(sc)) {
+ printf("firmware load failed\n");
+ return (ENXIO);
+ }
+
+ /* execute firmware */
+ sc->sc_mbox[0] = QLA_MBOX_EXEC_FIRMWARE;
+ sc->sc_mbox[1] = QLA_2300_CODE_ORG;
+ if (qla_mbox(sc, 0x0003, 0x0001)) {
+ printf("ISP couldn't exec firmware: %x\n", sc->sc_mbox[0]);
+ return (ENXIO);
+ }
+
+ delay(250000); /* from isp(4) */
+
+ sc->sc_mbox[0] = QLA_MBOX_ABOUT_FIRMWARE;
+ if (qla_mbox(sc, QLA_MBOX_ABOUT_FIRMWARE_IN,
+ QLA_MBOX_ABOUT_FIRMWARE_OUT)) {
+ printf("ISP not talking after firmware exec: %x\n",
+ sc->sc_mbox[0]);
+ return (ENXIO);
+ }
+ printf("firmware v%d.%d.%d, attrs %x\n", sc->sc_mbox[1], sc->sc_mbox[2],
+ sc->sc_mbox[3], sc->sc_mbox[6]);
+
+ if (sc->sc_mbox[6] & QLA_FW_ATTR_EXPANDED_LUN)
+ sc->sc_expanded_lun = 1;
+ if (sc->sc_mbox[6] & QLA_FW_ATTR_FABRIC)
+ sc->sc_fabric = 1;
+ if (sc->sc_mbox[6] & QLA_FW_ATTR_2K_LOGINS)
+ sc->sc_2k_logins = 1;
+
+ /* work out how many ccbs to allocate */
+ sc->sc_mbox[0] = QLA_MBOX_GET_FIRMWARE_STATUS;
+ if (qla_mbox(sc, 0x0001, 0x0007)) {
+ printf("couldn't get firmware status: %x\n", sc->sc_mbox[0]);
+ return (ENXIO);
+ }
+ sc->sc_maxcmds = sc->sc_mbox[2];
+
+ if (qla_alloc_ccbs(sc)) {
+ /* error already printed */
+ return (ENOMEM);
+ }
+ sc->sc_scratch = qla_dmamem_alloc(sc, QLA_SCRATCH_SIZE);
+ if (sc->sc_scratch == NULL) {
+ printf("%s: unable to allocate scratch\n", DEVNAME(sc));
+ goto free_ccbs;
+ }
+
+ /* build init buffer thing */
+ icb = (struct qla_init_cb *)QLA_DMA_KVA(sc->sc_scratch);
+ memset(icb, 0, sizeof(*icb));
+ icb->icb_version = QLA_ICB_VERSION;
+ if (sc->sc_nvram_valid) {
+ icb->icb_fw_options = sc->sc_nvram.fw_options;
+ icb->icb_max_frame_len = sc->sc_nvram.frame_payload_size;
+ icb->icb_max_alloc = sc->sc_nvram.max_iocb_allocation;
+ icb->icb_exec_throttle = sc->sc_nvram.execution_throttle;
+ icb->icb_retry_count = sc->sc_nvram.retry_count;
+ icb->icb_retry_delay = sc->sc_nvram.retry_delay;
+ icb->icb_portname = sc->sc_nvram.port_name;
+ icb->icb_hardaddr = sc->sc_nvram.hard_address;
+ icb->icb_inquiry_data = sc->sc_nvram.inquiry_data;
+ icb->icb_login_timeout = sc->sc_nvram.login_timeout;
+ icb->icb_nodename = sc->sc_nvram.node_name;
+ icb->icb_xfwoptions = sc->sc_nvram.add_fw_options;
+ icb->icb_zfwoptions = sc->sc_nvram.special_options;
+ } else {
+ /* defaults copied from isp(4) */
+ icb->icb_retry_count = 3;
+ icb->icb_retry_delay = 5;
+ icb->icb_exec_throttle = htole16(16);
+ icb->icb_max_alloc = htole16(256);
+ icb->icb_max_frame_len = htole16(1024);
+ /* port name is big-endian in the icb */
+ icb->icb_portname = htobe64(QLA_DEFAULT_PORT_NAME);
+ icb->icb_nodename = 0;
+
+ icb->icb_fw_options = htole16(QLA_ICB_FW_FAIRNESS |
+ QLA_ICB_FW_ENABLE_PDB_CHANGED | QLA_ICB_FW_HARD_ADDR |
+ QLA_ICB_FW_FULL_DUPLEX);
+ }
+ /* target mode stuff that we don't care about */
+ icb->icb_lun_enables = 0;
+ icb->icb_cmd_count = 0;
+ icb->icb_notify_count = 0;
+ icb->icb_lun_timeout = 0;
+
+ /* "zero interrupt operation" */
+ icb->icb_int_delaytimer = 0;
+
+ icb->icb_req_out = 0;
+ icb->icb_resp_in = 0;
+ icb->icb_req_queue_len = htole16(sc->sc_maxcmds);
+ icb->icb_resp_queue_len = htole16(sc->sc_maxcmds);
+ icb->icb_req_queue_addr = htole64(QLA_DMA_DVA(sc->sc_requests));
+ icb->icb_resp_queue_addr = htole64(QLA_DMA_DVA(sc->sc_responses));
+
+ /* adjust firmware options a bit */
+ icb->icb_fw_options |= htole16(QLA_ICB_FW_EXTENDED_INIT_CB);
+ icb->icb_xfwoptions &= htole16(~QLA_ICB_FW_FAST_POST);
+
+ sc->sc_mbox[0] = QLA_MBOX_INIT_FIRMWARE;
+ sc->sc_mbox[4] = 0;
+ sc->sc_mbox[5] = 0;
+ qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(*icb), BUS_DMASYNC_PREWRITE);
+ rv = qla_mbox(sc, QLA_MBOX_INIT_FIRMWARE_IN, 0x0001);
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(*icb), BUS_DMASYNC_POSTWRITE);
+
+ if (rv != 0) {
+ printf("%s: ISP firmware init failed: %x\n", DEVNAME(sc),
+ sc->sc_mbox[0]);
+ goto free_scratch;
+ }
+
+ /* enable some more notifications */
+ sc->sc_mbox[0] = QLA_MBOX_SET_FIRMWARE_OPTIONS;
+ sc->sc_mbox[1] = QLA_FW_OPTION1_ASYNC_LIP_F8 |
+ QLA_FW_OPTION1_ASYNC_LIP_RESET |
+ QLA_FW_OPTION1_ASYNC_LIP_ERROR |
+ QLA_FW_OPTION1_ASYNC_LOGIN_RJT;
+ sc->sc_mbox[2] = 0;
+ sc->sc_mbox[3] = 0;
+ if (qla_mbox(sc, QLA_MBOX_SET_FIRMWARE_OPTIONS_IN, 0x0001)) {
+ printf("%s: setting firmware options failed: %x\n",
+ DEVNAME(sc), sc->sc_mbox[0]);
+ goto free_scratch;
+ }
+
+ /* wait a bit for link to come up so we can scan and attach devices */
+ for (i = 0; i < QLA_WAIT_FOR_LOOP * 10000; i++) {
+ u_int16_t isr, info;
+
+ delay(100);
+
+ if (qla_read_isr(sc, &isr, &info) == 0)
+ continue;
+
+ qla_handle_intr(sc, isr, info);
+
+ if (sc->sc_loop_up)
+ break;
+ }
+
+ if (sc->sc_loop_up == 0) {
+ printf("%s: loop still down, giving up\n", DEVNAME(sc));
+ return (0);
+ }
+
+ /* connection topology tells us what to scan */
+ sc->sc_mbox[0] = QLA_MBOX_GET_LOOP_ID;
+ if (qla_mbox(sc, 0x0001, QLA_MBOX_GET_LOOP_ID_OUT)) {
+ printf("%s: unable to get loop id\n", DEVNAME(sc));
+ sc->sc_topology = QLA_TOPO_N_PORT_NO_TARGET;
+ } else {
+ sc->sc_topology = sc->sc_mbox[6];
+ sc->sc_loop_id = sc->sc_mbox[1];
+
+ switch (sc->sc_topology) {
+ case QLA_TOPO_NL_PORT:
+ case QLA_TOPO_N_PORT:
+ printf("%s: loop id %d\n", DEVNAME(sc),
+ sc->sc_loop_id);
+ break;
+
+ case QLA_TOPO_FL_PORT:
+ case QLA_TOPO_F_PORT:
+ sc->sc_port_id = sc->sc_mbox[2] |
+ (sc->sc_mbox[3] << 16);
+ printf("%s: fabric port id %06x\n", DEVNAME(sc),
+ sc->sc_port_id);
+ break;
+
+ case QLA_TOPO_N_PORT_NO_TARGET:
+ default:
+ printf("%s: not useful\n", DEVNAME(sc));
+ break;
+ }
+ }
+
+ /* scan loop */
+ switch (sc->sc_topology) {
+ case QLA_TOPO_NL_PORT:
+ case QLA_TOPO_FL_PORT:
+ scan_limit = 126;
+ break;
+
+ case QLA_TOPO_N_PORT:
+ scan_limit = 2;
+ break;
+
+ default:
+ scan_limit = 0;
+ break;
+ }
+
+ for (scan = 0; scan < scan_limit; scan++) {
+ if (scan == sc->sc_loop_id)
+ continue;
+
+ qla_add_port(sc, scan, 0, QLA_LOCATION_LOOP_ID(scan));
+ }
+
+ /* scan fabric, if there is one */
+ if (sc->sc_fabric && (sc->sc_topology == QLA_TOPO_F_PORT ||
+ sc->sc_topology == QLA_TOPO_FL_PORT)) {
+ struct qla_sns_rft_id *rft;
+ struct qla_sns_ga_nxt *ga;
+ struct qla_sns_ga_nxt_resp *gar;
+ struct qla_fc_port *fport;
+ u_int32_t lastport, firstport;
+ TAILQ_HEAD(, qla_fc_port) found;
+
+ /* get the name server's port db entry */
+ sc->sc_mbox[0] = QLA_MBOX_GET_PORT_DB;
+ if (sc->sc_2k_logins) {
+ sc->sc_mbox[1] = QLA_F_PORT_HANDLE;
+ } else {
+ sc->sc_mbox[1] = QLA_F_PORT_HANDLE << 8;
+ }
+ qla_mbox_putaddr(sc->sc_mbox, sc->sc_scratch);
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(struct qla_get_port_db), BUS_DMASYNC_PREREAD);
+ if (qla_mbox(sc, 0x00cf, 0x0001)) {
+ printf("%s: get port db for SNS failed: %x\n",
+ DEVNAME(sc), sc->sc_mbox[0]);
+ /* what now? */
+ } else {
+ struct qla_get_port_db *pdb;
+ bus_dmamap_sync(sc->sc_dmat,
+ QLA_DMA_MAP(sc->sc_scratch), 0,
+ sizeof(struct qla_get_port_db),
+ BUS_DMASYNC_POSTREAD);
+ pdb = QLA_DMA_KVA(sc->sc_scratch);
+ printf("%s: SNS; port id %06x, name %llx\n",
+ DEVNAME(sc), ((letoh16(pdb->port_id[0]) << 16) |
+ letoh16(pdb->port_id[1])) & 0xffffff,
+ betoh64(pdb->port_name));
+ }
+
+ /*
+ * register fc4 types with the fabric
+ * some switches do this automatically, but apparently
+ * some don't.
+ */
+ rft = QLA_DMA_KVA(sc->sc_scratch);
+ memset(rft, 0, sizeof(*rft) + sizeof(struct qla_sns_req_hdr));
+ rft->subcmd = htole16(QLA_SNS_RFT_ID);
+ rft->max_word = htole16(sizeof(struct qla_sns_req_hdr) / 4);
+ rft->port_id = htole32(sc->sc_port_id);
+ rft->fc4_types[0] = (1 << QLA_FC4_SCSI);
+ if (qla_sns_req(sc, sc->sc_scratch, sizeof(*rft))) {
+ printf("%s: RFT_ID failed\n", DEVNAME(sc));
+ /* we might be able to continue after this fails */
+ }
+
+ TAILQ_INIT(&found);
+ lastport = 0;
+ firstport = -1;
+ i = 0;
+ do {
+ int result;
+
+ /* get the next port from the fabric nameserver */
+ ga = QLA_DMA_KVA(sc->sc_scratch);
+ memset(ga, 0, sizeof(*ga) + sizeof(*gar));
+ ga->subcmd = htole16(QLA_SNS_GA_NXT);
+ ga->max_word = htole16(sizeof(*gar) / 4);
+ ga->port_id = htole32(lastport);
+ result = qla_sns_req(sc, sc->sc_scratch, sizeof(*ga));
+ if (result) {
+ printf("%s: GA_NXT %x failed: %x\n",
+ DEVNAME(sc), lastport, result);
+ break;
+ }
+
+ gar = (struct qla_sns_ga_nxt_resp *)(ga + 1);
+ /* if the response is all zeroes, try again */
+ if (gar->port_type_id == 0 && gar->port_name == 0 &&
+ gar->node_name == 0) {
+ printf("%s: GA_NXT returned junk\n",
+ DEVNAME(sc));
+ continue;
+ }
+
+ /* are we back at the start? */
+ lastport = betoh32(gar->port_type_id) & 0xffffff;
+ if (lastport == firstport)
+ break;
+ if (firstport == -1)
+ firstport = lastport;
+
+ /*
+ printf("%s: GA_NXT: port type/id: %x, wwpn %llx, wwnn %llx, fct: %x\n", DEVNAME(sc), lastport, betoh64(gar->port_name), betoh64(gar->node_name), gar->fc4_types[0]);
+ */
+
+ /* don't try to log in to ourselves */
+ if (lastport == sc->sc_port_id) {
+ continue;
+ }
+
+ fport = malloc(sizeof(*fport), M_DEVBUF,
+ M_ZERO | M_NOWAIT);
+ if (fport == NULL) {
+ printf("%s: failed to allocate a port struct\n",
+ DEVNAME(sc));
+ break;
+ }
+ fport->port_name = betoh64(gar->port_name);
+ fport->node_name = betoh64(gar->node_name);
+ fport->location = QLA_LOCATION_PORT_ID(lastport);
+ fport->portid = lastport;
+ TAILQ_INSERT_TAIL(&found, fport, update);
+ } while (1);
+
+ while (!TAILQ_EMPTY(&found)) {
+ int loopid;
+
+ mtx_enter(&sc->sc_port_mtx);
+ loopid = qla_get_loop_id(sc);
+ mtx_leave(&sc->sc_port_mtx);
+
+ if (loopid == -1) {
+ printf("%s: ran out of loop ids\n",
+ DEVNAME(sc));
+ break;
+ }
+ fport = TAILQ_FIRST(&found);
+ TAILQ_REMOVE(&found, fport, update);
+
+ sc->sc_mbox[0] = QLA_MBOX_FABRIC_PLOGI;
+ sc->sc_mbox[2] = (fport->portid >> 16) & 0xff;
+ sc->sc_mbox[3] = fport->portid & 0xffff;
+ if (sc->sc_2k_logins) {
+ sc->sc_mbox[1] = loopid;
+ sc->sc_mbox[10] = 0;
+ } else {
+ sc->sc_mbox[1] = loopid << 8;
+ }
+ if (qla_mbox(sc, 0x000f, 0x00c7)) {
+ printf("%s: port %x login failed: %x %x %x %x\n",
+ DEVNAME(sc), fport->portid, sc->sc_mbox[0],
+ sc->sc_mbox[1], sc->sc_mbox[2],
+ sc->sc_mbox[6]);
+ } else {
+ qla_add_port(sc, loopid, fport->portid,
+ fport->location);
+ }
+ free(fport, M_DEVBUF);
+ }
+
+ /* firmware implicitly registers for change notification for
+ * events detected by the controller. we might want full
+ * registration instead.
+ */
+ }
+
+ /* we should be good to go now, attach scsibus */
+ sc->sc_link.adapter = &qla_switch;
+ sc->sc_link.adapter_softc = sc;
+ sc->sc_link.adapter_target = QLA_MAX_TARGETS;
+ sc->sc_link.adapter_buswidth = QLA_MAX_TARGETS;
+ sc->sc_link.openings = sc->sc_maxcmds; /* / sc->sc_buswidth? */
+ sc->sc_link.pool = &sc->sc_iopool;
+ if (sc->sc_nvram_valid) {
+ sc->sc_link.port_wwn = betoh64(sc->sc_nvram.port_name);
+ sc->sc_link.node_wwn = betoh64(sc->sc_nvram.node_name);
+ } else {
+ sc->sc_link.port_wwn = QLA_DEFAULT_PORT_NAME;
+ sc->sc_link.node_wwn = 0;
+ }
+ if (sc->sc_link.node_wwn == 0) {
+ /*
+ * mask out the port number from the port name to get
+ * the node name.
+ */
+ sc->sc_link.node_wwn = sc->sc_link.port_wwn;
+ sc->sc_link.node_wwn &= ~(0xfULL << 56);
+ }
+
+ memset(&saa, 0, sizeof(saa));
+ saa.saa_sc_link = &sc->sc_link;
+
+ /* config_found() returns the scsibus attached to us */
+ sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev,
+ &saa, scsiprint);
+
+ return(0);
+
+free_scratch:
+ qla_dmamem_free(sc, sc->sc_scratch);
+free_ccbs:
+ qla_free_ccbs(sc);
+ return (ENXIO);
+}
+
+int
+qla_detach(struct qla_softc *sc, int flags)
+{
+ return (0);
+}
+
+struct qla_ccb *
+qla_handle_resp(struct qla_softc *sc, u_int16_t id)
+{
+ struct qla_ccb *ccb;
+ struct qla_iocb_status *status;
+ struct scsi_xfer *xs;
+ u_int32_t handle;
+ u_int8_t *entry;
+
+ ccb = NULL;
+ entry = QLA_DMA_KVA(sc->sc_responses) + (id * QLA_QUEUE_ENTRY_SIZE);
+
+ bus_dmamap_sync(sc->sc_dmat,
+ QLA_DMA_MAP(sc->sc_responses), id * QLA_QUEUE_ENTRY_SIZE,
+ QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTREAD);
+
+ /*qla_dump_iocb(sc, entry);*/
+ switch(entry[0]) {
+ case QLA_IOCB_STATUS:
+ status = (struct qla_iocb_status *)entry;
+ handle = status->handle;
+ if (handle > sc->sc_maxcmds) {
+ panic("bad completed command handle: %d (> %d)",
+ handle, sc->sc_maxcmds);
+ }
+
+ ccb = &sc->sc_ccbs[handle];
+ xs = ccb->ccb_xs;
+ if (xs == NULL) {
+ printf("%s: got status for inactive ccb %d\n",
+ DEVNAME(sc), handle);
+ ccb = NULL;
+ break;
+ }
+ if (xs->io != ccb) {
+ panic("completed command handle doesn't match xs "
+ "(handle %d, ccb %p, xs->io %p)", handle, ccb,
+ xs->io);
+ }
+
+ if (xs->datalen > 0) {
+ if (ccb->ccb_dmamap->dm_nsegs >
+ QLA_IOCB_SEGS_PER_CMD) {
+ bus_dmamap_sync(sc->sc_dmat,
+ QLA_DMA_MAP(sc->sc_segments),
+ ccb->ccb_seg_offset,
+ sizeof(*ccb->ccb_t4segs) *
+ ccb->ccb_dmamap->dm_nsegs,
+ BUS_DMASYNC_POSTWRITE);
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0,
+ ccb->ccb_dmamap->dm_mapsize,
+ (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD :
+ BUS_DMASYNC_POSTWRITE);
+ bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap);
+ }
+
+ xs->status = letoh16(status->scsi_status);
+ switch (letoh16(status->completion)) {
+ case QLA_IOCB_STATUS_COMPLETE:
+ if (letoh16(status->scsi_status) &
+ QLA_SCSI_STATUS_SENSE_VALID) {
+ memcpy(&xs->sense, status->sense_data,
+ sizeof(xs->sense));
+ xs->error = XS_SENSE;
+ } else {
+ xs->error = XS_NOERROR;
+ }
+ xs->resid = 0;
+ break;
+
+ case QLA_IOCB_STATUS_DMA_ERROR:
+ printf("%s: dma error\n", DEVNAME(sc));
+ /* set resid apparently? */
+ break;
+
+ case QLA_IOCB_STATUS_RESET:
+ printf("%s: reset destroyed command\n", DEVNAME(sc));
+ sc->sc_marker_required = 1;
+ xs->error = XS_RESET;
+ break;
+
+ case QLA_IOCB_STATUS_ABORTED:
+ printf("%s: aborted\n", DEVNAME(sc));
+ sc->sc_marker_required = 1;
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+
+ case QLA_IOCB_STATUS_TIMEOUT:
+ printf("%s: command timed out\n", DEVNAME(sc));
+ xs->error = XS_TIMEOUT;
+ break;
+
+ case QLA_IOCB_STATUS_DATA_OVERRUN:
+ case QLA_IOCB_STATUS_DATA_UNDERRUN:
+ xs->resid = letoh32(status->resid);
+ xs->error = XS_NOERROR;
+ break;
+
+ case QLA_IOCB_STATUS_QUEUE_FULL:
+ printf("%s: queue full\n", DEVNAME(sc));
+ xs->error = XS_BUSY;
+ break;
+
+ case QLA_IOCB_STATUS_PORT_UNAVAIL:
+ case QLA_IOCB_STATUS_PORT_LOGGED_OUT:
+ case QLA_IOCB_STATUS_PORT_CHANGED:
+ printf("%s: dev gone\n", DEVNAME(sc));
+ xs->error = XS_SELTIMEOUT;
+ break;
+
+ default:
+ printf("%s: unexpected completion status %x\n",
+ DEVNAME(sc), status->completion);
+ xs->error = XS_DRIVER_STUFFUP;
+ break;
+ }
+ break;
+
+ case QLA_IOCB_STATUS_CONT:
+ printf("%s: ignoring status continuation iocb\n",
+ DEVNAME(sc));
+ break;
+
+ case QLA_IOCB_MAILBOX:
+ printf("%s: eat mailbox?\n", DEVNAME(sc));
+ /* ok */
+ break;
+
+ /* check for requests that bounce back? */
+ default:
+ printf("%s: unexpected response entry type %x\n",
+ DEVNAME(sc), entry[0]);
+ break;
+ }
+
+ return (ccb);
+}
+
+int
+qla_handle_intr(struct qla_softc *sc, u_int16_t isr, u_int16_t info)
+{
+ int rv, i;
+ u_int16_t rspin;
+ struct qla_ccb *ccb;
+
+ switch (isr) {
+ case QLA_INT_ASYNC:
+ rv = qla_async(sc, info);
+ break;
+
+ case QLA_INT_RSPQ:
+ /* apparently can't read the out ptr with <2300 chips,
+ * and apparently also need to debounce the in ptr reads
+ */
+ rspin = qla_read_queue_ptr(sc, QLA_RESP_QUEUE_IN);
+ if (rspin == sc->sc_last_resp_id) {
+ /* isp(4) has some weird magic for this case */
+ printf("%s: nonsense interrupt (%x)\n", DEVNAME(sc),
+ rspin);
+ } else {
+ while (sc->sc_last_resp_id != rspin) {
+ ccb = qla_handle_resp(sc, sc->sc_last_resp_id);
+ if (ccb)
+ scsi_done(ccb->ccb_xs);
+
+ sc->sc_last_resp_id++;
+ if (sc->sc_last_resp_id == sc->sc_maxcmds)
+ sc->sc_last_resp_id = 0;
+ }
+
+ qla_write_queue_ptr(sc, QLA_RESP_QUEUE_OUT,
+ sc->sc_last_resp_id);
+ }
+ rv = 1;
+ break;
+
+ case QLA_INT_MBOX:
+ case QLA_INT_ROM_MBOX:
+ case QLA_INT_MBOX_FAIL:
+ case QLA_INT_ROM_MBOX_FAIL:
+ if (sc->sc_mbox_pending) {
+ if (info == QLA_MBOX_COMPLETE) {
+ for (i = 1; i < nitems(sc->sc_mbox); i++) {
+ sc->sc_mbox[i] = qla_read_mbox(sc, i);
+ }
+ } else {
+ sc->sc_mbox[0] = info;
+ }
+ sc->sc_mbox_pending = 0;
+ wakeup(sc->sc_mbox);
+ } else {
+ printf("%s: unexpected mbox interrupt: %x\n",
+ DEVNAME(sc), info);
+ }
+ rv = 1;
+ break;
+
+ case 0:
+ rv = 0;
+ break;
+
+ default:
+ /* maybe log something? */
+ rv = 1;
+ }
+
+ qla_clear_isr(sc);
+ return (rv);
+}
+
+int
+qla_intr(void *xsc)
+{
+ struct qla_softc *sc = xsc;
+ u_int16_t isr;
+ u_int16_t info;
+
+ if (qla_read_isr(sc, &isr, &info) == 0)
+ return (0);
+
+ return (qla_handle_intr(sc, isr, info));
+}
+
+int
+qla_scsi_probe(struct scsi_link *link)
+{
+ struct qla_softc *sc = link->adapter_softc;
+ int rv = 0;
+
+ mtx_enter(&sc->sc_port_mtx);
+ if (sc->sc_targets[link->target] == NULL)
+ rv = ENXIO;
+ else if (!ISSET(sc->sc_targets[link->target]->flags,
+ QLA_PORT_FLAG_IS_TARGET))
+ rv = ENXIO;
+ mtx_leave(&sc->sc_port_mtx);
+
+ return (rv);
+}
+
+void
+qla_scsi_cmd(struct scsi_xfer *xs)
+{
+ struct scsi_link *link = xs->sc_link;
+ struct qla_softc *sc = link->adapter_softc;
+ struct qla_ccb *ccb;
+ struct qla_iocb_req34 *iocb;
+ struct qla_ccb_list list;
+ u_int16_t req;
+ int offset, error;
+ bus_dmamap_t dmap;
+
+ if (xs->cmdlen > sizeof(iocb->req_cdb)) {
+ printf("%s: too fat (%d)\n", DEVNAME(sc), xs->cmdlen);
+ memset(&xs->sense, 0, sizeof(xs->sense));
+ xs->sense.error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT;
+ xs->sense.flags = SKEY_ILLEGAL_REQUEST;
+ xs->sense.add_sense_code = 0x20;
+ xs->error = XS_SENSE;
+ scsi_done(xs);
+ return;
+ }
+
+ ccb = xs->io;
+ dmap = ccb->ccb_dmamap;
+ if (xs->datalen > 0) {
+ error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data,
+ xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ?
+ BUS_DMA_NOWAIT : BUS_DMA_WAITOK);
+ if (error) {
+ xs->error = XS_DRIVER_STUFFUP;
+ scsi_done(xs);
+ return;
+ }
+
+ bus_dmamap_sync(sc->sc_dmat, dmap, 0,
+ dmap->dm_mapsize,
+ (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD :
+ BUS_DMASYNC_PREWRITE);
+ }
+
+ mtx_enter(&sc->sc_queue_mtx);
+
+ /* put in a sync marker if required */
+ if (sc->sc_marker_required) {
+ req = sc->sc_next_req_id++;
+ if (sc->sc_next_req_id == sc->sc_maxcmds)
+ sc->sc_next_req_id = 0;
+
+ printf("%s: writing marker at request %d\n", DEVNAME(sc), req);
+ offset = (req * QLA_QUEUE_ENTRY_SIZE);
+ iocb = QLA_DMA_KVA(sc->sc_requests) + offset;
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_requests),
+ offset, QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
+ qla_put_marker(sc, iocb);
+ qla_write_queue_ptr(sc, QLA_REQ_QUEUE_IN, sc->sc_next_req_id);
+ sc->sc_marker_required = 0;
+ }
+
+ req = sc->sc_next_req_id++;
+ if (sc->sc_next_req_id == sc->sc_maxcmds)
+ sc->sc_next_req_id = 0;
+
+ offset = (req * QLA_QUEUE_ENTRY_SIZE);
+ iocb = QLA_DMA_KVA(sc->sc_requests) + offset;
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(sc->sc_requests), offset,
+ QLA_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE);
+
+ ccb->ccb_xs = xs;
+
+ qla_put_cmd(sc, iocb, xs, ccb);
+
+ qla_write_queue_ptr(sc, QLA_REQ_QUEUE_IN, sc->sc_next_req_id);
+
+ if (!ISSET(xs->flags, SCSI_POLL)) {
+ mtx_leave(&sc->sc_queue_mtx);
+ return;
+ }
+
+ SIMPLEQ_INIT(&list);
+ do {
+ ccb = qla_scsi_cmd_poll(sc);
+ SIMPLEQ_INSERT_TAIL(&list, ccb, ccb_link);
+ } while (xs->io != ccb);
+
+ mtx_leave(&sc->sc_queue_mtx);
+
+ while ((ccb = SIMPLEQ_FIRST(&list)) != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&list, ccb_link);
+ scsi_done(ccb->ccb_xs);
+ }
+}
+
+struct qla_ccb *
+qla_scsi_cmd_poll(struct qla_softc *sc)
+{
+ u_int16_t rspin;
+ struct qla_ccb *ccb = NULL;
+
+ while (ccb == NULL) {
+ u_int16_t isr, info;
+
+ if (qla_read_isr(sc, &isr, &info) == 0) {
+ delay(1000);
+ continue;
+ }
+
+ if (isr != QLA_INT_RSPQ) {
+ qla_handle_intr(sc, isr, info);
+ continue;
+ }
+
+ rspin = qla_read_queue_ptr(sc, QLA_RESP_QUEUE_IN);
+ if (rspin != sc->sc_last_resp_id) {
+ ccb = qla_handle_resp(sc, sc->sc_last_resp_id);
+
+ sc->sc_last_resp_id++;
+ if (sc->sc_last_resp_id == sc->sc_maxcmds)
+ sc->sc_last_resp_id = 0;
+
+ qla_write_queue_ptr(sc, QLA_RESP_QUEUE_OUT,
+ sc->sc_last_resp_id);
+ } else {
+ delay(1000);
+ }
+
+ qla_clear_isr(sc);
+ }
+
+ return (ccb);
+}
+
+u_int16_t
+qla_read(struct qla_softc *sc, int offset)
+{
+ u_int16_t v;
+ v = bus_space_read_2(sc->sc_iot, sc->sc_ioh, offset);
+ bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 2,
+ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+ return (v);
+}
+
+void
+qla_write(struct qla_softc *sc, int offset, u_int16_t value)
+{
+ bus_space_write_2(sc->sc_iot, sc->sc_ioh, offset, value);
+ bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 2,
+ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+}
+
+u_int16_t
+qla_read_mbox(struct qla_softc *sc, int mbox)
+{
+ /* could range-check mboxes according to chip type? */
+ return (qla_read(sc, sc->sc_mbox_base + (mbox * 2)));
+}
+
+void
+qla_write_mbox(struct qla_softc *sc, int mbox, u_int16_t value)
+{
+ qla_write(sc, sc->sc_mbox_base + (mbox * 2), value);
+}
+
+void
+qla_host_cmd(struct qla_softc *sc, u_int16_t cmd)
+{
+ qla_write(sc, QLA_HOST_CMD_CTRL, cmd << QLA_HOST_CMD_SHIFT);
+}
+
+#define MBOX_COMMAND_TIMEOUT 4000
+
+int
+qla_mbox(struct qla_softc *sc, int maskin, int maskout)
+{
+ int i;
+ int result = 0;
+ int rv;
+
+ for (i = 0; i < nitems(sc->sc_mbox); i++) {
+ if (maskin & (1 << i)) {
+ qla_write_mbox(sc, i, sc->sc_mbox[i]);
+ }
+ }
+ qla_host_cmd(sc, QLA_HOST_CMD_SET_HOST_INT);
+
+ if (sc->sc_scsibus == NULL) {
+ for (i = 0; i < MBOX_COMMAND_TIMEOUT && result == 0; i++) {
+ u_int16_t isr, info;
+
+ delay(100);
+
+ if (qla_read_isr(sc, &isr, &info) == 0)
+ continue;
+
+ switch (isr) {
+ case QLA_INT_MBOX:
+ case QLA_INT_ROM_MBOX:
+ case QLA_INT_MBOX_FAIL:
+ case QLA_INT_ROM_MBOX_FAIL:
+ result = info;
+ break;
+
+ default:
+ qla_handle_intr(sc, isr, info);
+ break;
+ }
+ }
+ } else {
+ sc->sc_mbox_pending = 1;
+ tsleep(sc->sc_mbox, PRIBIO, "qla_mbox", 0);
+ result = sc->sc_mbox[0];
+ }
+
+ switch (result) {
+ case QLA_MBOX_COMPLETE:
+ for (i = 1; i < nitems(sc->sc_mbox); i++) {
+ sc->sc_mbox[i] = (maskout & (1 << i)) ?
+ qla_read_mbox(sc, i) : 0;
+ }
+ rv = 0;
+ break;
+
+ case 0:
+ /* timed out; do something? */
+ printf("mbox timed out\n");
+ rv = 1;
+ break;
+
+ default:
+ /* log a thing? */
+ sc->sc_mbox[0] = result;
+ rv = result;
+ break;
+ }
+
+ qla_clear_isr(sc);
+ return (rv);
+}
+
+void
+qla_mbox_putaddr(u_int16_t *mbox, struct qla_dmamem *mem)
+{
+ mbox[2] = (QLA_DMA_DVA(mem) >> 16) & 0xffff;
+ mbox[3] = (QLA_DMA_DVA(mem) >> 0) & 0xffff;
+ mbox[6] = (QLA_DMA_DVA(mem) >> 48) & 0xffff;
+ mbox[7] = (QLA_DMA_DVA(mem) >> 32) & 0xffff;
+}
+
+int
+qla_sns_req(struct qla_softc *sc, struct qla_dmamem *mem, int reqsize)
+{
+ struct qla_sns_req_hdr *header;
+ int rv;
+
+ memset(&sc->sc_mbox, 0, sizeof(sc->sc_mbox));
+ sc->sc_mbox[0] = QLA_MBOX_SEND_SNS;
+ sc->sc_mbox[1] = reqsize / 2;
+ qla_mbox_putaddr(sc->sc_mbox, mem);
+
+ header = QLA_DMA_KVA(mem);
+ header->resp_len = htole16((QLA_DMA_LEN(mem) - reqsize) / 2);
+ header->resp_addr = htole64(QLA_DMA_DVA(mem) + reqsize);
+ header->subcmd_len = htole16((reqsize - sizeof(*header)) / 2);
+
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, QLA_DMA_LEN(mem),
+ BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE);
+ rv = qla_mbox(sc, 0x00cf, 0x0003);
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, QLA_DMA_LEN(mem),
+ BUS_DMASYNC_POSTREAD | BUS_DMASYNC_POSTWRITE);
+
+ return (rv);
+}
+
+void
+qla_set_ints(struct qla_softc *sc, int enabled)
+{
+ u_int16_t v = enabled ? (QLA_INT_REQ | QLA_RISC_INT_REQ) : 0;
+ qla_write(sc, QLA_INT_CTRL, v);
+}
+
+int
+qla_read_isr(struct qla_softc *sc, u_int16_t *isr, u_int16_t *info)
+{
+ u_int32_t v;
+
+ if ((qla_read(sc, QLA_INT_STATUS) & QLA_INT_REQ) == 0) {
+ return (0);
+ }
+
+ v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, QLA_RISC_STATUS_LOW);
+ bus_space_barrier(sc->sc_iot, sc->sc_ioh, QLA_RISC_STATUS_LOW, 4,
+ BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE);
+
+ *isr = (v & QLA_INT_STATUS_MASK);
+ if (*isr == 0)
+ return (0);
+
+ *info = (v >> QLA_INT_INFO_SHIFT);
+ return (1);
+}
+
+void
+qla_clear_isr(struct qla_softc *sc)
+{
+ qla_host_cmd(sc, QLA_HOST_CMD_CLR_RISC_INT);
+ qla_write(sc, QLA_SEMA, 0); /* necessary? */
+}
+
+int
+qla_queue_reg(struct qla_softc *sc, enum qla_qptr queue)
+{
+ switch (queue) {
+ case QLA_REQ_QUEUE_IN:
+ return (QLA_REQ_IN);
+ case QLA_REQ_QUEUE_OUT:
+ return (QLA_REQ_OUT);
+ case QLA_RESP_QUEUE_IN:
+ return (QLA_RESP_IN);
+ case QLA_RESP_QUEUE_OUT:
+ return (QLA_RESP_OUT);
+ default:
+ panic("unknown queue");
+ }
+}
+
+u_int16_t
+qla_read_queue_ptr(struct qla_softc *sc, enum qla_qptr queue)
+{
+ return (qla_read(sc, qla_queue_reg(sc, queue)));
+}
+
+void
+qla_write_queue_ptr(struct qla_softc *sc, enum qla_qptr queue,
+ u_int16_t value)
+{
+ qla_write(sc, qla_queue_reg(sc, queue), value);
+}
+
+void
+qla_update(struct qla_softc *sc, int task)
+{
+ /* do things */
+}
+
+int
+qla_async(struct qla_softc *sc, u_int16_t info)
+{
+ u_int16_t id, exp;
+
+ switch (info) {
+ case QLA_ASYNC_SYSTEM_ERROR:
+ qla_update(sc, QLA_UPDATE_SOFTRESET);
+ break;
+
+ case QLA_ASYNC_REQ_XFER_ERROR:
+ qla_update(sc, QLA_UPDATE_SOFTRESET);
+ break;
+
+ case QLA_ASYNC_RSP_XFER_ERROR:
+ qla_update(sc, QLA_UPDATE_SOFTRESET);
+ break;
+
+ case QLA_ASYNC_LIP_OCCURRED:
+ printf("%s: lip occurred\n", DEVNAME(sc));
+ break;
+
+ case QLA_ASYNC_LOOP_UP:
+ printf("%s: loop up\n", DEVNAME(sc));
+ sc->sc_loop_up = 1;
+ sc->sc_marker_required = 1;
+ qla_update(sc, QLA_UPDATE_FULL_SCAN);
+ break;
+
+ case QLA_ASYNC_LOOP_DOWN:
+ printf("%s: loop down\n", DEVNAME(sc));
+ sc->sc_loop_up = 0;
+ qla_update(sc, QLA_UPDATE_DISCARD);
+ break;
+
+ case QLA_ASYNC_LIP_RESET:
+ printf("%s: lip reset\n", DEVNAME(sc));
+ sc->sc_marker_required = 1;
+ qla_update(sc, QLA_UPDATE_FABRIC_RELOGIN);
+ break;
+
+ case QLA_ASYNC_PORT_DB_CHANGE:
+ printf("%s: port db changed %x\n", DEVNAME(sc),
+ qla_read_mbox(sc, 1));
+ qla_update(sc, QLA_UPDATE_LOOP_SCAN);
+ break;
+
+ case QLA_ASYNC_CHANGE_NOTIFY:
+ printf("%s: name server change (%02x:%02x)\n", DEVNAME(sc),
+ qla_read_mbox(sc, 1), qla_read_mbox(sc, 2));
+ qla_update(sc, QLA_UPDATE_FABRIC_SCAN);
+ break;
+
+ case QLA_ASYNC_LIP_F8:
+ printf("%s: lip f8\n", DEVNAME(sc));
+ break;
+
+ case QLA_ASYNC_LOOP_INIT_ERROR:
+ printf("%s: loop initialization error: %x", DEVNAME(sc),
+ qla_read_mbox(sc, 1));
+ break;
+
+ case QLA_ASYNC_LOGIN_REJECT:
+ id = qla_read_mbox(sc, 1);
+ exp = qla_read_mbox(sc, 2);
+ printf("%s: login reject from %x (reason %d, explanation %x)",
+ id >> 8, id & 0xff, exp);
+ break;
+
+ case QLA_ASYNC_SCSI_CMD_COMPLETE:
+ /* shouldn't happen, we disable fast posting */
+ break;
+
+ case QLA_ASYNC_CTIO_COMPLETE:
+ /* definitely shouldn't happen, we don't do target mode */
+ break;
+
+ case QLA_ASYNC_POINT_TO_POINT:
+ printf("%s: connected in point-to-point mode\n", DEVNAME(sc));
+ /* we get stuck handling these if we have the wrong loop
+ * topology; should somehow reinit with different things
+ * somehow.
+ */
+ break;
+
+ case QLA_ASYNC_ZIO_RESP_UPDATE:
+ /* shouldn't happen, we don't do zio */
+ break;
+
+ case QLA_ASYNC_RND_ERROR:
+ /* do nothing? */
+ break;
+
+ case QLA_ASYNC_QUEUE_FULL:
+ break;
+
+ default:
+ printf("%s: unknown async %x\n", DEVNAME(sc), info);
+ break;
+ }
+ return (1);
+}
+
+void
+qla_dump_iocb(struct qla_softc *sc, void *buf)
+{
+ u_int8_t *iocb = buf;
+ int l;
+ int b;
+
+ printf("%s: iocb:\n", DEVNAME(sc));
+ for (l = 0; l < 4; l++) {
+ for (b = 0; b < 16; b++) {
+ printf(" %2.2x", iocb[(l*16)+b]);
+ }
+ printf("\n");
+ }
+}
+
+void
+qla_dump_iocb_segs(struct qla_softc *sc, void *segs, int n)
+{
+ u_int8_t *buf = segs;
+ int s, b;
+ printf("%s: iocb segs:\n", DEVNAME(sc));
+ for (s = 0; s < n; s++) {
+ for (b = 0; b < sizeof(struct qla_iocb_seg); b++) {
+ printf(" %2.2x", buf[(s*(sizeof(struct qla_iocb_seg)))
+ + b]);
+ }
+ printf("\n");
+ }
+}
+
+void
+qla_put_marker(struct qla_softc *sc, void *buf)
+{
+ struct qla_iocb_marker *marker = buf;
+
+ marker->entry_type = QLA_IOCB_MARKER;
+ marker->entry_count = 1;
+ marker->seqno = 0;
+ marker->flags = 0;
+
+ /* could be more specific here; isp(4) isn't */
+ marker->target = 0;
+ marker->modifier = QLA_IOCB_MARKER_SYNC_ALL;
+}
+
+void
+qla_put_data_seg(struct qla_iocb_seg *seg, bus_dmamap_t dmap, int num)
+{
+ seg->seg_addr = htole64(dmap->dm_segs[num].ds_addr);
+ seg->seg_len = htole32(dmap->dm_segs[num].ds_len);
+}
+
+void
+qla_put_cmd(struct qla_softc *sc, void *buf, struct scsi_xfer *xs,
+ struct qla_ccb *ccb)
+{
+ struct qla_iocb_req34 *req = buf;
+ u_int16_t dir;
+ int seg;
+ int target = xs->sc_link->target;
+
+ req->seqno = 0;
+ req->flags = 0;
+ req->entry_count = 1;
+
+ if (xs->datalen == 0) {
+ dir = QLA_IOCB_CMD_NO_DATA;
+ req->req_seg_count = 0;
+ req->entry_type = QLA_IOCB_CMD_TYPE_3;
+ } else {
+ dir = xs->flags & SCSI_DATA_IN ? QLA_IOCB_CMD_READ_DATA :
+ QLA_IOCB_CMD_WRITE_DATA;
+ req->req_seg_count = htole16(ccb->ccb_dmamap->dm_nsegs);
+ if (ccb->ccb_dmamap->dm_nsegs > QLA_IOCB_SEGS_PER_CMD) {
+ req->entry_type = QLA_IOCB_CMD_TYPE_4;
+ for (seg = 0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) {
+ qla_put_data_seg(&ccb->ccb_t4segs[seg],
+ ccb->ccb_dmamap, seg);
+ }
+ req->req_type.req4.req4_seg_type = htole16(1);
+ req->req_type.req4.req4_seg_base = 0;
+ req->req_type.req4.req4_seg_addr =
+ htole64(QLA_DMA_DVA(sc->sc_segments) +
+ ccb->ccb_seg_offset);
+ memset(req->req_type.req4.req4_reserved, 0,
+ sizeof(req->req_type.req4.req4_reserved));
+ bus_dmamap_sync(sc->sc_dmat,
+ QLA_DMA_MAP(sc->sc_segments), ccb->ccb_seg_offset,
+ sizeof(*ccb->ccb_t4segs) * ccb->ccb_dmamap->dm_nsegs,
+ BUS_DMASYNC_PREWRITE);
+ } else {
+ req->entry_type = QLA_IOCB_CMD_TYPE_3;
+ for (seg = 0; seg < ccb->ccb_dmamap->dm_nsegs; seg++) {
+ qla_put_data_seg(&req->req_type.req3_segs[seg],
+ ccb->ccb_dmamap, seg);
+ }
+ }
+ }
+
+ /* isp(4) uses head of queue for 'request sense' commands */
+ req->req_flags = htole16(QLA_IOCB_CMD_SIMPLE_QUEUE | dir);
+
+ /*
+ * timeout is in seconds. make sure it's at least 1 if a timeout
+ * was specified in xs
+ */
+ if (xs->timeout != 0)
+ req->req_time = htole16(MAX(1, xs->timeout/1000));
+
+ /* lun and target layout vary with firmware attributes */
+ if (sc->sc_expanded_lun) {
+ if (sc->sc_2k_logins) {
+ req->req_target = htole16(target);
+ } else {
+ req->req_target = htole16(target << 8);
+ }
+ req->req_scclun = htole16(xs->sc_link->lun);
+ } else {
+ req->req_target = htole16(target << 8 | xs->sc_link->lun);
+ }
+ memcpy(req->req_cdb, xs->cmd, xs->cmdlen);
+ req->req_totalcnt = htole32(xs->datalen);
+
+ req->req_handle = ccb->ccb_id;
+}
+
+#if 0
+void
+qla_put_cmd_cont(struct qla_softc *sc, void *buf, struct scsi_xfer *xs,
+ bus_dmamap_t dmap, int offset)
+{
+ int seg;
+ struct qla_iocb_cont1 *cont1 = buf;
+ cont1->entry_type = QLA_IOCB_CONT_TYPE_1;
+ cont1->entry_count = 1;
+ for (seg = 0; seg < QLA_IOCB_SEGS_PER_CMD_CONT; seg++) {
+ if (seg + offset == dmap->dm_nsegs)
+ break;
+ qla_put_data_seg(&cont1->segs[seg], dmap, seg + offset);
+ }
+}
+#endif
+
+int
+qla_load_fwchunk_2300(struct qla_softc *sc, struct qla_dmamem *mem, const u_int16_t *src, u_int32_t dest)
+{
+ u_int16_t origin, done, total;
+ int i;
+
+ origin = dest;
+ done = 0;
+ total = src[3];
+
+ while (done < total) {
+ u_int16_t *copy;
+ u_int32_t words;
+
+ /* limit transfer size otherwise it just doesn't work */
+ words = MIN(total - done, 1 << 10);
+ copy = QLA_DMA_KVA(mem);
+ for (i = 0; i < words; i++) {
+ copy[i] = htole16(src[done++]);
+ }
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, words * 2,
+ BUS_DMASYNC_PREWRITE);
+
+ sc->sc_mbox[0] = QLA_MBOX_LOAD_RAM_EXT;
+ sc->sc_mbox[1] = dest;
+ sc->sc_mbox[4] = words;
+ sc->sc_mbox[8] = dest >> 16;
+ qla_mbox_putaddr(sc->sc_mbox, mem);
+ if (qla_mbox(sc, 0x01ff, 0x0001)) {
+ printf("firmware load failed\n");
+ return (1);
+ }
+ bus_dmamap_sync(sc->sc_dmat, QLA_DMA_MAP(mem), 0, words * 2,
+ BUS_DMASYNC_POSTWRITE);
+
+ dest += words;
+ }
+
+ sc->sc_mbox[0] = QLA_MBOX_VERIFY_CSUM;
+ sc->sc_mbox[1] = origin;
+ if (qla_mbox(sc, 0x0003, 0x0003)) {
+ printf("verification of chunk at %x failed: %x\n", origin,
+ sc->sc_mbox[1]);
+ return (1);
+ }
+
+ return (0);
+}
+
+int
+qla_load_firmware_2300(struct qla_softc *sc)
+{
+ struct qla_dmamem *mem;
+ const u_int16_t *fw = isp_2300_risc_code;
+
+ mem = qla_dmamem_alloc(sc, 65536);
+ qla_load_fwchunk_2300(sc, mem, fw, QLA_2300_CODE_ORG);
+
+ /* additional firmware chunks for 2322 */
+ if (sc->sc_isp_type == QLA_ISP2322) {
+ u_int32_t addr;
+ int i;
+
+ for (i = 0; i < 2; i++) {
+ fw += fw[3];
+ addr = fw[5] | ((fw[4] & 0x3f) << 16);
+ qla_load_fwchunk_2300(sc, mem, fw, addr);
+ }
+ }
+
+ qla_dmamem_free(sc, mem);
+ return (0);
+}
+
+int
+qla_read_nvram(struct qla_softc *sc)
+{
+ u_int16_t data[sizeof(sc->sc_nvram) >> 1];
+ u_int16_t req, cmd, val;
+ u_int8_t csum;
+ int i, base, bit;
+
+ base = sc->sc_port * 0x80;
+
+ qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL);
+ delay(10);
+ qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL | QLA_NVRAM_CLOCK);
+ delay(10);
+
+ for (i = 0; i < nitems(data); i++) {
+ req = (i + base) | (QLA_NVRAM_CMD_READ << 8);
+
+ /* write each bit out through the nvram register */
+ for (bit = 10; bit >= 0; bit--) {
+ cmd = QLA_NVRAM_CHIP_SEL;
+ if ((req >> bit) & 1) {
+ cmd |= QLA_NVRAM_DATA_OUT;
+ }
+ qla_write(sc, QLA_NVRAM, cmd);
+ delay(10);
+ qla_read(sc, QLA_NVRAM);
+
+ qla_write(sc, QLA_NVRAM, cmd | QLA_NVRAM_CLOCK);
+ delay(10);
+ qla_read(sc, QLA_NVRAM);
+
+ qla_write(sc, QLA_NVRAM, cmd);
+ delay(10);
+ qla_read(sc, QLA_NVRAM);
+ }
+
+ /* read the result back */
+ val = 0;
+ for (bit = 0; bit < 16; bit++) {
+ val <<= 1;
+ qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL |
+ QLA_NVRAM_CLOCK);
+ delay(10);
+ if (qla_read(sc, QLA_NVRAM) & QLA_NVRAM_DATA_IN)
+ val |= 1;
+ delay(10);
+
+ qla_write(sc, QLA_NVRAM, QLA_NVRAM_CHIP_SEL);
+ delay(10);
+ qla_read(sc, QLA_NVRAM);
+ }
+
+ qla_write(sc, QLA_NVRAM, 0);
+ delay(10);
+ qla_read(sc, QLA_NVRAM);
+
+ data[i] = letoh16(val);
+ }
+
+ csum = 0;
+ for (i = 0; i < nitems(data); i++) {
+ csum += data[i] & 0xff;
+ csum += data[i] >> 8;
+ }
+
+ bcopy(data, &sc->sc_nvram, sizeof(sc->sc_nvram));
+ /* id field should be 'ISP ', version should be at least 1 */
+ if (sc->sc_nvram.id[0] != 'I' || sc->sc_nvram.id[1] != 'S' ||
+ sc->sc_nvram.id[2] != 'P' || sc->sc_nvram.id[3] != ' ' ||
+ sc->sc_nvram.nvram_version < 1 || (csum != 0)) {
+ printf("%s: nvram corrupt\n", DEVNAME(sc));
+ return (1);
+ }
+ return (0);
+}
+
+struct qla_dmamem *
+qla_dmamem_alloc(struct qla_softc *sc, size_t size)
+{
+ struct qla_dmamem *m;
+ int nsegs;
+
+ m = malloc(sizeof(*m), M_DEVBUF, M_NOWAIT | M_ZERO);
+ if (m == NULL)
+ return (NULL);
+
+ m->qdm_size = size;
+
+ if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &m->qdm_map) != 0)
+ goto qdmfree;
+
+ if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &m->qdm_seg, 1,
+ &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0)
+ goto destroy;
+
+ if (bus_dmamem_map(sc->sc_dmat, &m->qdm_seg, nsegs, size, &m->qdm_kva,
+ BUS_DMA_NOWAIT) != 0)
+ goto free;
+
+ if (bus_dmamap_load(sc->sc_dmat, m->qdm_map, m->qdm_kva, size, NULL,
+ BUS_DMA_NOWAIT) != 0)
+ goto unmap;
+
+ return (m);
+
+unmap:
+ bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
+free:
+ bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
+destroy:
+ bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
+qdmfree:
+ free(m, M_DEVBUF);
+
+ return (NULL);
+}
+
+void
+qla_dmamem_free(struct qla_softc *sc, struct qla_dmamem *m)
+{
+ bus_dmamap_unload(sc->sc_dmat, m->qdm_map);
+ bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size);
+ bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1);
+ bus_dmamap_destroy(sc->sc_dmat, m->qdm_map);
+ free(m, M_DEVBUF);
+}
+
+int
+qla_alloc_ccbs(struct qla_softc *sc)
+{
+ struct qla_ccb *ccb;
+ u_int8_t *cmd;
+ int i;
+
+ SIMPLEQ_INIT(&sc->sc_ccb_free);
+ mtx_init(&sc->sc_ccb_mtx, IPL_BIO);
+ mtx_init(&sc->sc_queue_mtx, IPL_BIO);
+ mtx_init(&sc->sc_port_mtx, IPL_BIO);
+
+ sc->sc_ccbs = malloc(sizeof(struct qla_ccb) * sc->sc_maxcmds,
+ M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO);
+ if (sc->sc_ccbs == NULL) {
+ printf("%s: unable to allocate ccbs\n", DEVNAME(sc));
+ return (1);
+ }
+
+ sc->sc_requests = qla_dmamem_alloc(sc, sc->sc_maxcmds *
+ QLA_QUEUE_ENTRY_SIZE);
+ if (sc->sc_requests == NULL) {
+ printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc));
+ goto free_ccbs;
+ }
+ sc->sc_responses = qla_dmamem_alloc(sc, sc->sc_maxcmds *
+ QLA_QUEUE_ENTRY_SIZE);
+ if (sc->sc_responses == NULL) {
+ printf("%s: unable to allocate rcb dmamem\n", DEVNAME(sc));
+ goto free_req;
+ }
+ sc->sc_segments = qla_dmamem_alloc(sc, sc->sc_maxcmds * QLA_MAX_SEGS *
+ sizeof(struct qla_iocb_seg));
+ if (sc->sc_segments == NULL) {
+ printf("%s: unable to allocate iocb segments\n", DEVNAME(sc));
+ goto free_res;
+ }
+
+ cmd = QLA_DMA_KVA(sc->sc_requests);
+ memset(cmd, 0, QLA_QUEUE_ENTRY_SIZE * sc->sc_maxcmds);
+ for (i = 0; i < sc->sc_maxcmds; i++) {
+ ccb = &sc->sc_ccbs[i];
+
+ if (bus_dmamap_create(sc->sc_dmat, MAXPHYS,
+ QLA_MAX_SEGS, MAXPHYS, 0,
+ BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW,
+ &ccb->ccb_dmamap) != 0) {
+ printf("%s: unable to create dma map\n", DEVNAME(sc));
+ goto free_maps;
+ }
+
+ ccb->ccb_sc = sc;
+ ccb->ccb_id = i;
+
+ ccb->ccb_seg_offset = i * QLA_MAX_SEGS *
+ sizeof(struct qla_iocb_seg);
+ ccb->ccb_t4segs = QLA_DMA_KVA(sc->sc_segments) +
+ ccb->ccb_seg_offset;
+
+ qla_put_ccb(sc, ccb);
+ }
+
+ scsi_iopool_init(&sc->sc_iopool, sc, qla_get_ccb, qla_put_ccb);
+ return (0);
+
+free_maps:
+ while ((ccb = qla_get_ccb(sc)) != NULL)
+ bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
+
+ qla_dmamem_free(sc, sc->sc_segments);
+free_res:
+ qla_dmamem_free(sc, sc->sc_responses);
+free_req:
+ qla_dmamem_free(sc, sc->sc_requests);
+free_ccbs:
+ free(sc->sc_ccbs, M_DEVBUF);
+
+ return (1);
+}
+
+void
+qla_free_ccbs(struct qla_softc *sc)
+{
+ struct qla_ccb *ccb;
+
+ scsi_iopool_destroy(&sc->sc_iopool);
+ while ((ccb = qla_get_ccb(sc)) != NULL)
+ bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap);
+ qla_dmamem_free(sc, sc->sc_segments);
+ qla_dmamem_free(sc, sc->sc_responses);
+ qla_dmamem_free(sc, sc->sc_requests);
+ free(sc->sc_ccbs, M_DEVBUF);
+}
+
+void *
+qla_get_ccb(void *xsc)
+{
+ struct qla_softc *sc = xsc;
+ struct qla_ccb *ccb;
+
+ mtx_enter(&sc->sc_ccb_mtx);
+ ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free);
+ if (ccb != NULL) {
+ SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link);
+ }
+ mtx_leave(&sc->sc_ccb_mtx);
+ return (ccb);
+}
+
+void
+qla_put_ccb(void *xsc, void *io)
+{
+ struct qla_softc *sc = xsc;
+ struct qla_ccb *ccb = io;
+
+ ccb->ccb_xs = NULL;
+ mtx_enter(&sc->sc_ccb_mtx);
+ SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link);
+ mtx_leave(&sc->sc_ccb_mtx);
+}
diff --git a/sys/dev/ic/qlareg.h b/sys/dev/ic/qlareg.h
new file mode 100644
index 00000000000..7a4b2b1c8f4
--- /dev/null
+++ b/sys/dev/ic/qlareg.h
@@ -0,0 +1,626 @@
+/* $OpenBSD: qlareg.h,v 1.1 2014/01/19 06:04:03 jmatthew Exp $ */
+
+/*
+ * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+/* firmware loading */
+#define QLA_2300_CODE_ORG 0x0800
+
+/* firmware attributes */
+#define QLA_FW_ATTR_EXPANDED_LUN 0x0002
+#define QLA_FW_ATTR_FABRIC 0x0004
+#define QLA_FW_ATTR_2K_LOGINS 0x0100
+
+/* interrupt types */
+#define QLA_INT_ROM_MBOX 0x01
+#define QLA_INT_ROM_MBOX_FAIL 0x02
+#define QLA_INT_MBOX 0x10
+#define QLA_INT_MBOX_FAIL 0x11
+#define QLA_INT_ASYNC 0x12
+#define QLA_INT_RSPQ 0x13
+#define QLA_INT_FP16 0x15
+#define QLA_INT_FP_SCSI 0x16
+#define QLA_INT_FP_CTIO 0x17
+
+/* ISP registers */
+#define QLA_FLASH_BIOS_ADDR 0x00
+#define QLA_FLASH_BIOS_DATA 0x02
+#define QLA_CTRL_STATUS 0x06
+#define QLA_INT_CTRL 0x08
+#define QLA_INT_STATUS 0x0A
+#define QLA_SEMA 0x0C
+#define QLA_NVRAM 0x0E
+#define QLA_REQ_IN 0x10
+#define QLA_REQ_OUT 0x12
+#define QLA_RESP_IN 0x14
+#define QLA_RESP_OUT 0x16
+#define QLA_RISC_STATUS_LOW 0x18
+#define QLA_RISC_STATUS_HIGH 0x1A
+#define QLA_HOST_CMD_CTRL 0xC0
+#define QLA_GPIO_DATA 0xCC
+#define QLA_GPIO_ENABLE 0xCE
+
+#define QLA_FPM_DIAG 0x96
+
+
+/* mailbox base moves around between generations */
+#define QLA_MBOX_BASE_23XX 0x40
+#define QLA_MBOX_BASE_2100 0x10
+#define QLA_MBOX_BASE_2200 0x10
+
+/* QLA_CTRL_STATUS */
+#define QLA_CTRL_RESET 0x0001
+#define QLA_CTRL_RISC_REGS 0x0000
+#define QLA_CTRL_FB_REGS 0x0010
+#define QLA_CTRL_FPM0_REGS 0x0020
+#define QLA_CTRL_FPM1_REGS 0x0030
+
+/* QLA_INT_STATUS */
+#define QLA_INT_REQ 0x8000
+#define QLA_RISC_INT_REQ 0x0008
+
+/* QLA_SEMA */
+#define QLA_SEMA_STATUS 0x0002
+#define QLA_SEMA_LOCK 0x0001
+
+/* QLA_NVRAM */
+#define QLA_NVRAM_DATA_IN 0x0008
+#define QLA_NVRAM_DATA_OUT 0x0004
+#define QLA_NVRAM_CHIP_SEL 0x0002
+#define QLA_NVRAM_CLOCK 0x0001
+#define QLA_NVRAM_CMD_READ 6
+
+
+/* QLA_RISC_STATUS LOW/HIGH */
+#define QLA_INT_INFO_SHIFT 16
+#define QLA_RISC_HOST_INT_REQ 0x8000
+#define QLA_RISC_PAUSED 0x0100
+#define QLA_INT_STATUS_MASK 0x00FF
+
+/* QLA_HOST_CMD_CTRL write */
+#define QLA_HOST_CMD_SHIFT 12
+#define QLA_HOST_CMD_NOP 0x0
+#define QLA_HOST_CMD_RESET 0x1
+#define QLA_HOST_CMD_PAUSE 0x2
+#define QLA_HOST_CMD_RELEASE 0x3
+#define QLA_HOST_CMD_MASK_PARITY 0x4
+#define QLA_HOST_CMD_SET_HOST_INT 0x5
+#define QLA_HOST_CMD_CLR_HOST_INT 0x6
+#define QLA_HOST_CMD_CLR_RISC_INT 0x7
+#define QLA_HOST_CMD_ENABLE_PARITY 0xA
+#define QLA_HOST_CMD_PARITY_ERROR 0xE
+
+/* QLA_HOST_CMD_CTRL read */
+#define QLA_HOST_STATUS_HOST_INT 0x0080
+#define QLA_HOST_STATUS_RISC_RESET 0x0040
+#define QLA_HOST_STATUS_RISC_PAUSE 0x0020
+#define QLA_HOST_STATUS_RISC_EXT 0x0010
+
+/* QLA_FPM_DIAG */
+#define QLA_FPM_RESET 0x0100
+
+/* QLA_MBOX_BASE (reg 0) read */
+#define QLA_MBOX_COMPLETE 0x4000
+#define QLA_MBOX_INVALID 0x4001
+#define QLA_MBOX_INTF_ERROR 0x4002
+#define QLA_MBOX_TEST_FAILED 0x4003
+#define QLA_MBOX_CMD_ERROR 0x4005
+#define QLA_MBOX_CMD_PARAM 0x4006
+#define QLA_MBOX_PORT_USED 0x4007
+#define QLA_MBOX_LOOP_USED 0x4008
+#define QLA_MBOX_ALL_IDS_USED 0x4009
+#define QLA_MBOX_NOT_LOGGED_IN 0x400A
+#define QLA_MBOX_LINK_DOWN 0x400B
+#define QLA_ASYNC_SYSTEM_ERROR 0x8002
+#define QLA_ASYNC_REQ_XFER_ERROR 0x8003
+#define QLA_ASYNC_RSP_XFER_ERROR 0x8004
+#define QLA_ASYNC_LIP_OCCURRED 0x8010
+#define QLA_ASYNC_LOOP_UP 0x8011
+#define QLA_ASYNC_LOOP_DOWN 0x8012
+#define QLA_ASYNC_LIP_RESET 0x8013
+#define QLA_ASYNC_PORT_DB_CHANGE 0x8014
+#define QLA_ASYNC_CHANGE_NOTIFY 0x8015
+#define QLA_ASYNC_LIP_F8 0x8016
+#define QLA_ASYNC_LOOP_INIT_ERROR 0x8017
+#define QLA_ASYNC_LOGIN_REJECT 0x8018
+#define QLA_ASYNC_SCSI_CMD_COMPLETE 0x8020
+#define QLA_ASYNC_CTIO_COMPLETE 0x8021
+#define QLA_ASYNC_POINT_TO_POINT 0x8030
+#define QLA_ASYNC_ZIO_RESP_UPDATE 0x8040
+#define QLA_ASYNC_RND_ERROR 0x8048
+#define QLA_ASYNC_QUEUE_FULL 0x8049
+
+
+/* QLA_MBOX_BASE (reg 0) write */
+#define QLA_MBOX_NOP 0x0000
+#define QLA_MBOX_LOAD_RAM 0x0001
+#define QLA_MBOX_EXEC_FIRMWARE 0x0002
+#define QLA_MBOX_REGISTER_TEST 0x0006
+#define QLA_MBOX_VERIFY_CSUM 0x0007
+#define QLA_MBOX_ABOUT_FIRMWARE 0x0008
+#define QLA_MBOX_LOAD_RAM_EXT 0x000B
+#define QLA_MBOX_CSUM_FIRMWARE 0x000E
+#define QLA_MBOX_INIT_REQ_QUEUE 0x0010
+#define QLA_MBOX_INIT_RSP_QUEUE 0x0011
+#define QLA_MBOX_STOP_FIRMWARE 0x0014
+#define QLA_MBOX_ABORT_IOCB 0x0015
+#define QLA_MBOX_ABORT_DEVICE 0x0016
+#define QLA_MBOX_ABORT_TARGET 0x0017
+#define QLA_MBOX_RESET 0x0018
+#define QLA_MBOX_ABORT_QUEUE 0x001C
+#define QLA_MBOX_GET_QUEUE_STATUS 0x001D
+#define QLA_MBOX_GET_FIRMWARE_STATUS 0x001F
+#define QLA_MBOX_GET_LOOP_ID 0x0020
+#define QLA_MBOX_SET_FIRMWARE_OPTIONS 0x0038
+#define QLA_MBOX_ENH_GET_PORT_DB 0x0047
+#define QLA_MBOX_PLOGO 0x0056
+#define QLA_MBOX_INIT_FIRMWARE 0x0060
+#define QLA_MBOX_GET_INIT_CB 0x0061
+#define QLA_MBOX_LIP 0x0062
+#define QLA_MBOX_GET_FC_AL_POS 0x0063
+#define QLA_MBOX_GET_PORT_DB 0x0064
+#define QLA_MBOX_TARGET_RESET 0x0066
+#define QLA_MBOX_GET_FIRMWARE_STATE 0x0069
+#define QLA_MBOX_GET_PORT_NAME 0x006A
+#define QLA_MBOX_GET_LINK_STATUS 0x006B
+#define QLA_MBOX_LIP_RESET 0x006C
+#define QLA_MBOX_SEND_SNS 0x006E
+#define QLA_MBOX_FABRIC_PLOGI 0x006F
+#define QLA_MBOX_SEND_CHANGE_REQ 0x0070
+#define QLA_MBOX_FABRIC_PLOGO 0x0071
+#define QLA_MBOX_LOOP_PLOGI 0x0074
+#define QLA_MBOX_GET_PORT_NAME_LIST 0x0075
+#define QLA_MBOX_LUN_RESET 0x007E
+
+/* mailbox operation register bitfields */
+#define QLA_MBOX_ABOUT_FIRMWARE_IN 0x0001
+#define QLA_MBOX_ABOUT_FIRMWARE_OUT 0x004f
+#define QLA_MBOX_INIT_FIRMWARE_IN 0x00fd
+#define QLA_MBOX_SET_FIRMWARE_OPTIONS_IN 0x000f
+#define QLA_MBOX_GET_LOOP_ID_OUT 0x00cf
+
+#define QLA_MBOX_COUNT 32
+
+/* nvram layout */
+struct qla_nvram {
+ u_int8_t id[4];
+ u_int8_t nvram_version;
+ u_int8_t reserved_0;
+
+ u_int8_t parameter_block_version;
+ u_int8_t reserved_1;
+
+ u_int16_t fw_options;
+
+ u_int16_t frame_payload_size;
+ u_int16_t max_iocb_allocation;
+ u_int16_t execution_throttle;
+ u_int8_t retry_count;
+ u_int8_t retry_delay;
+ u_int64_t port_name;
+ u_int16_t hard_address;
+ u_int8_t inquiry_data;
+ u_int8_t login_timeout;
+ u_int64_t node_name;
+
+ u_int16_t add_fw_options;
+
+ u_int8_t response_accumulation_timer;
+ u_int8_t interrupt_delay_timer;
+
+ u_int16_t special_options;
+
+ u_int8_t reserved_2[22];
+
+ u_int8_t seriallink_options[4];
+
+ u_int8_t host_p[2];
+
+ u_int64_t boot_node_name;
+ u_int8_t boot_lun_number;
+ u_int8_t reset_delay;
+ u_int8_t port_down_retry_count;
+ u_int8_t boot_id_number;
+ u_int16_t max_luns_per_target;
+ u_int64_t fcode_boot_port_name;
+ u_int64_t alternate_port_name;
+ u_int64_t alternate_node_name;
+
+ u_int8_t efi_parameters;
+
+ u_int8_t link_down_timeout;
+
+ u_int8_t adapter_id[16];
+
+ u_int64_t alt1_boot_node_name;
+ u_int16_t alt1_boot_lun_number;
+ u_int64_t alt2_boot_node_name;
+ u_int16_t alt2_boot_lun_number;
+ u_int64_t alt3_boot_node_name;
+ u_int16_t alt3_boot_lun_number;
+ u_int64_t alt4_boot_node_name;
+ u_int16_t alt4_boot_lun_number;
+ u_int64_t alt5_boot_node_name;
+ u_int16_t alt5_boot_lun_number;
+ u_int64_t alt6_boot_node_name;
+ u_int16_t alt6_boot_lun_number;
+ u_int64_t alt7_boot_node_name;
+ u_int16_t alt7_boot_lun_number;
+
+ u_int8_t reserved_3[2];
+
+ u_int8_t model_number[16];
+
+ u_int8_t oem_specific[16];
+
+ u_int8_t adapter_features[2];
+
+ u_int8_t reserved_4[16];
+
+ u_int16_t subsystem_vendor_id_2200;
+ u_int16_t subsystem_device_id_2200;
+
+ u_int8_t reserved_5;
+ u_int8_t checksum;
+} __packed;
+
+/* init firmware control block */
+#define QLA_ICB_VERSION 1
+
+#define QLA_ICB_FW_HARD_ADDR 0x0001
+#define QLA_ICB_FW_FAIRNESS 0x0002
+#define QLA_ICB_FW_FULL_DUPLEX 0x0004
+#define QLA_ICB_FW_FAST_POST 0x0008
+#define QLA_ICB_FW_TARGET_MODE 0x0010
+#define QLA_ICB_FW_DISABLE_INITIATOR 0x0020
+#define QLA_ICB_FW_ENABLE_ADISC 0x0040
+#define QLA_ICB_FW_ENABLE_TGT_DEV 0x0080
+#define QLA_ICB_FW_ENABLE_PDB_CHANGED 0x0100
+#define QLA_ICB_FW_DISABLE_INIT_LIP 0x0200
+#define QLA_ICB_FW_DESC_LOOP_ID 0x0400
+#define QLA_ICB_FW_PREV_LOOP_ID 0x0800
+#define QLA_ICB_FW_RESERVED 0x1000
+#define QLA_ICB_FW_LOGIN_AFTER_LIP 0x2000
+#define QLA_ICB_FW_NAME_OPTION 0x4000
+#define QLA_ICB_FW_EXTENDED_INIT_CB 0x8000
+
+#define QLA_ICB_XFW_ZIO_DISABLED 0x0000
+#define QLA_ICB_XFW_ZIO_MODE_5 0x0005
+#define QLA_ICB_XFW_ZIO_MODE_6 0x0006
+
+#define QLA_ICB_XFW_LOOP_PTP 0x0020
+#define QLA_ICB_XFW_PTP_ONLY 0x0010
+#define QLA_ICB_XFW_LOOP_ONLY 0x0000
+
+#define QLA_ICB_XFW_HARD_ADDR_ONLY 0x0080
+#define QLA_ICB_XFW_ENABLE_CLASS_2 0x0100
+#define QLA_ICB_XFW_ENABLE_ACK0 0x0200
+#define QLA_ICB_XFW_ENABLE_FC_TAPE 0x1000
+#define QLA_ICB_XFW_ENABLE_FC_CONFIRM 0x2000
+#define QLA_ICB_XFW_ENABLE_TGT_QUEUE 0x4000
+#define QLA_ICB_XFW_NO_IMPLICIT_LOGOUT 0x8000
+
+#define QLA_ICB_ZFW_ENABLE_XFR_RDY 0x0001
+#define QLA_ICB_ZFW_SOFT_ID_ONLY 0x0002
+#define QLA_ICB_ZFW_FCP_RSP_12_0 0x0010
+#define QLA_ICB_ZFW_FCP_RSP_24_0 0x0020
+#define QLA_ICB_ZFW_FCP_RSP_32_BYTES 0x0030
+#define QLA_ICB_ZFW_ENABLE_OOO 0x0040
+#define QLA_ICB_ZFW_NO_AUTO_PLOGI 0x0080
+#define QLA_ICB_ZFW_50_OHMS 0x2000
+#define QLA_ICB_ZFW_1GBPS 0x0000
+#define QLA_ICB_ZFW_2GBPS 0x4000
+#define QLA_ICB_ZFW_AUTONEG 0x8000
+
+
+struct qla_init_cb {
+ u_int8_t icb_version;
+ u_int8_t icb_reserved;
+ u_int16_t icb_fw_options;
+ u_int16_t icb_max_frame_len;
+ u_int16_t icb_max_alloc;
+ u_int16_t icb_exec_throttle;
+ u_int8_t icb_retry_count;
+ u_int8_t icb_retry_delay;
+ u_int64_t icb_portname;
+ u_int16_t icb_hardaddr;
+ u_int8_t icb_inquiry_data;
+ u_int8_t icb_login_timeout;
+ u_int64_t icb_nodename;
+ u_int16_t icb_req_out;
+ u_int16_t icb_resp_in;
+ u_int16_t icb_req_queue_len;
+ u_int16_t icb_resp_queue_len;
+ u_int64_t icb_req_queue_addr;
+ u_int64_t icb_resp_queue_addr;
+ u_int16_t icb_lun_enables;
+ u_int8_t icb_cmd_count;
+ u_int8_t icb_notify_count;
+ u_int16_t icb_lun_timeout;
+ u_int16_t icb_reserved2;
+ u_int16_t icb_xfwoptions;
+ u_int8_t icb_reserved3;
+ u_int8_t icb_int_delaytimer;
+ u_int16_t icb_zfwoptions;
+ u_int16_t icb_reserved4[13];
+} __packed;
+
+#define QLA_FW_OPTION1_ASYNC_LIP_F8 0x0001
+#define QLA_FW_OPTION1_ASYNC_LIP_RESET 0x0002
+#define QLA_FW_OPTION1_SYNC_LOSS_LIP 0x0010
+#define QLA_FW_OPTION1_ASYNC_LIP_ERROR 0x0080
+#define QLA_FW_OPTION1_ASYNC_LOGIN_RJT 0x0800
+
+#define QLA_FW_OPTION3_EMERG_IOCB 0x0001
+#define QLA_FW_OPTION3_ASYNC_RND_ERROR 0x0002
+
+/* topology types returned from QLA_MBOX_GET_LOOP_ID */
+#define QLA_TOPO_NL_PORT 0
+#define QLA_TOPO_FL_PORT 1
+#define QLA_TOPO_N_PORT 2
+#define QLA_TOPO_F_PORT 3
+#define QLA_TOPO_N_PORT_NO_TARGET 4
+
+
+struct qla_get_port_db {
+ u_int8_t options;
+ u_int8_t control;
+ u_int8_t master_state;
+ u_int8_t slave_state;
+ u_int32_t adisc_hard_addr;
+ u_int16_t port_id[2];
+ u_int64_t node_name;
+ u_int64_t port_name;
+ u_int16_t exec_throttle;
+ u_int16_t exec_count;
+ u_int8_t retry_count;
+ u_int8_t reserved;
+ u_int16_t resource_alloc;
+ u_int16_t current_alloc;
+ u_int16_t queue_head;
+ u_int16_t queue_tail;
+ u_int16_t xmit_exec_list_next;
+ u_int16_t xmit_exec_list_prev;
+ u_int16_t common_features;
+ u_int16_t total_concurrent_seq;
+ u_int16_t rel_offset;
+ u_int16_t recip_control_flags;
+ u_int16_t recv_data_size;
+ u_int16_t concurrent_seq;
+ u_int16_t open_seq;
+ u_int8_t reserved2[8];
+ u_int16_t retry_timer;
+ u_int16_t next_seq_id;
+ u_int16_t frame_count;
+ u_int16_t prli_payload_len;
+ u_int16_t prli_svc_word0;
+ u_int16_t prli_svc_word3;
+ u_int16_t loop_id;
+ u_int16_t ext_lun_list_ptr;
+ u_int16_t ext_lun_stop_ptr;
+} __packed;
+
+#define QLA_SVC3_TARGET_ROLE 0x0010
+
+/* fabric name server commands */
+#define QLA_SNS_GA_NXT 0x0100
+#define QLA_SNS_GID_FT 0x0171
+#define QLA_SNS_RFT_ID 0x0217
+
+#define QLA_FC4_SCSI 8
+
+#define QLA_LS_REJECT 0x8001
+#define QLA_LS_ACCEPT 0x8002
+
+struct qla_sns_req_hdr {
+ u_int16_t resp_len;
+ u_int16_t reserved;
+ u_int64_t resp_addr;
+ u_int16_t subcmd_len;
+ u_int16_t reserved2;
+} __packed;
+
+struct qla_sns_ga_nxt {
+ struct qla_sns_req_hdr header;
+ u_int16_t subcmd;
+ u_int16_t max_word;
+ u_int32_t reserved3;
+ u_int32_t port_id;
+} __packed;
+
+struct qla_sns_ga_nxt_resp {
+ struct qla_sns_req_hdr header;
+ u_int32_t port_type_id;
+ u_int64_t port_name;
+ u_int8_t sym_port_name_len;
+ u_int8_t sym_port_name[255];
+ u_int64_t node_name;
+ u_int8_t sym_node_name_len;
+ u_int8_t sym_node_name[255];
+ u_int64_t initial_assoc;
+ u_int8_t ip_addr[16];
+ u_int32_t cos;
+ u_int32_t fc4_types[8];
+} __packed;
+
+struct qla_sns_rft_id {
+ struct qla_sns_req_hdr header;
+ u_int16_t subcmd;
+ u_int16_t max_word;
+ u_int32_t reserved3;
+ u_int32_t port_id;
+ u_int32_t fc4_types[8];
+} __packed;
+
+struct qla_sns_gid_ft {
+ struct qla_sns_req_hdr header;
+ u_int16_t subcmd;
+ u_int16_t max_word;
+ u_int32_t reserved3;
+ u_int32_t fc4_proto;
+} __packed;
+
+/* available handle ranges */
+#define QLA_2KL_MIN_HANDLE 0x81
+#define QLA_2KL_MAX_HANDLE 0x7EF
+
+#define QLA_MIN_HANDLE 0x81
+#define QLA_MAX_HANDLE 0xFE
+
+#define QLA_F_PORT_HANDLE 0x7E
+#define QLA_FABRIC_CTRL_HANDLE 0x7F
+#define QLA_SNS_HANDLE 0x80
+/* where does this go with 2klogin firmware? */
+#define QLA_IP_BCAST_HANDLE 0xFF
+
+
+/* IOCB types */
+/*#define QLA_IOCB_CONT_TYPE_1 0x02 */
+#define QLA_IOCB_STATUS 0x03
+#define QLA_IOCB_MARKER 0x04
+#define QLA_IOCB_STATUS_CONT 0x10
+#define QLA_IOCB_CMD_TYPE_4 0x15
+#define QLA_IOCB_CMD_TYPE_3 0x19
+#define QLA_IOCB_MAILBOX 0x39
+
+#define QLA_REQ_FLAG_CONT 0x01
+#define QLA_REQ_FLAG_FULL 0x02
+#define QLA_REQ_FLAG_BAD_HDR 0x04
+#define QLA_REQ_FLAG_BAD_PKT 0x08
+
+#define QLA_RESP_FLAG_INVALID_COUNT 0x10
+#define QLA_RESP_FLAG_INVALID_ORDER 0x20
+#define QLA_RESP_FLAG_DMA_ERR 0x40
+#define QLA_RESP_FLAG_RESERVED 0x80
+
+#define QLA_IOCB_CMD_HEAD_OF_QUEUE 0x0002
+#define QLA_IOCB_CMD_ORDERED_QUEUE 0x0004
+#define QLA_IOCB_CMD_SIMPLE_QUEUE 0x0008
+#define QLA_IOCB_CMD_NO_DATA 0x0000
+#define QLA_IOCB_CMD_READ_DATA 0x0020
+#define QLA_IOCB_CMD_WRITE_DATA 0x0040
+#define QLA_IOCB_CMD_NO_FAST_POST 0x0080
+
+#define QLA_IOCB_SEGS_PER_CMD 2
+#define QLA_IOCB_SEGS_PER_CMD_CONT 5
+
+#define QLA_IOCB_MARKER_SYNC_ALL 2
+
+struct qla_iocb_seg {
+ u_int64_t seg_addr;
+ u_int32_t seg_len;
+} __packed;
+
+#if 0
+struct qla_iocb_cont1 {
+ u_int8_t entry_type; /* QLA_IOCB_CONT_TYPE_1 */
+ u_int8_t entry_count;
+ u_int8_t seqno;
+ u_int8_t flags;
+
+ struct qla_iocb_seg segs[5];
+} __packed;
+#endif
+
+struct qla_iocb_status {
+ u_int8_t entry_type; /* QLA_IOCB_STATUS */
+ u_int8_t entry_count;
+ u_int8_t seqno;
+ u_int8_t flags;
+
+ u_int32_t handle;
+ u_int16_t scsi_status;
+ u_int16_t completion;
+ u_int16_t state_flags;
+ u_int16_t status_flags;
+ u_int16_t rsp_len;
+ u_int16_t sense_len;
+ u_int32_t resid;
+ u_int8_t fcp_rsp[8];
+ u_int8_t sense_data[32];
+} __packed;
+
+/* completion */
+#define QLA_IOCB_STATUS_COMPLETE 0x0000
+#define QLA_IOCB_STATUS_DMA_ERROR 0x0002
+#define QLA_IOCB_STATUS_RESET 0x0004
+#define QLA_IOCB_STATUS_ABORTED 0x0005
+#define QLA_IOCB_STATUS_TIMEOUT 0x0006
+#define QLA_IOCB_STATUS_DATA_OVERRUN 0x0007
+#define QLA_IOCB_STATUS_DATA_UNDERRUN 0x0015
+#define QLA_IOCB_STATUS_QUEUE_FULL 0x001C
+#define QLA_IOCB_STATUS_PORT_UNAVAIL 0x0028
+#define QLA_IOCB_STATUS_PORT_LOGGED_OUT 0x0029
+#define QLA_IOCB_STATUS_PORT_CHANGED 0x002A
+#define QLA_IOCB_STATUS_PORT_BUSY 0x002B
+
+#define QLA_SCSI_STATUS_FCP_LEN_VALID 0x0100
+#define QLA_SCSI_STATUS_SENSE_VALID 0x0200
+#define QLA_SCSI_STATUS_RESID_OVER 0x0400
+#define QLA_SCSI_STATUS_RESID_UNDER 0x0800
+
+
+struct qla_iocb_marker {
+ u_int8_t entry_type; /* QLA_IOCB_MARKER */
+ u_int8_t entry_count;
+ u_int8_t seqno;
+ u_int8_t flags;
+
+ u_int32_t handle;
+ u_int8_t reserved;
+ u_int8_t target;
+ u_int8_t modifier;
+ u_int8_t vp_index;
+ u_int16_t marker_flags;
+ u_int16_t lun;
+ u_int8_t reserved2[48];
+} __packed;
+
+struct qla_iocb_status_cont {
+ u_int8_t entry_type; /* QLA_IOCB_STATUS_CONT */
+ u_int8_t entry_count;
+ u_int8_t seqno;
+ u_int8_t flags;
+
+ u_int8_t sense[44];
+} __packed;
+
+struct qla_iocb_req34 {
+ u_int8_t entry_type; /* QLA_IOCB_CMD_TYPE_3 or 4 */
+ u_int8_t entry_count;
+ u_int8_t seqno;
+ u_int8_t flags;
+
+ u_int32_t req_handle;
+ u_int16_t req_target;
+ u_int16_t req_scclun;
+ u_int16_t req_flags;
+ u_int16_t req_reserved;
+ u_int16_t req_time;
+ u_int16_t req_seg_count;
+ u_int8_t req_cdb[16];
+ u_int32_t req_totalcnt;
+ union {
+ struct qla_iocb_seg req3_segs[2];
+ struct {
+ u_int16_t req4_seg_type;
+ u_int32_t req4_seg_base;
+ u_int64_t req4_seg_addr;
+ u_int8_t req4_reserved[10];
+ } __packed req4;
+ } req_type;
+} __packed;
+
diff --git a/sys/dev/ic/qlavar.h b/sys/dev/ic/qlavar.h
new file mode 100644
index 00000000000..43598f4d1d7
--- /dev/null
+++ b/sys/dev/ic/qlavar.h
@@ -0,0 +1,177 @@
+/* $OpenBSD: qlavar.h,v 1.1 2014/01/19 06:04:03 jmatthew Exp $ */
+
+/*
+ * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+
+#define QLA_DEFAULT_PORT_NAME 0x400000007F000003ULL /* from isp(4) */
+
+#define QLA_WAIT_FOR_LOOP 10
+
+/* rounded up range of assignable handles for 2k login firmware */
+#define QLA_MAX_TARGETS 2048
+
+/* maximum number of segments allowed for in a single io */
+#define QLA_MAX_SEGS 16
+
+enum qla_isp_gen {
+ QLA_GEN_ISP2100 = 1,
+ QLA_GEN_ISP2200,
+ QLA_GEN_ISP23XX,
+};
+
+enum qla_isp_type {
+ QLA_ISP2100 = 1,
+ QLA_ISP2200,
+ QLA_ISP2300,
+ QLA_ISP2312,
+ QLA_ISP2322
+};
+
+/* needed as <2300 use mailbox registers for queue pointers */
+enum qla_qptr {
+ QLA_REQ_QUEUE_IN,
+ QLA_REQ_QUEUE_OUT,
+ QLA_RESP_QUEUE_IN,
+ QLA_RESP_QUEUE_OUT
+};
+
+/* port database things */
+#define QLA_SCRATCH_SIZE 0x1000
+
+enum qla_port_disp {
+ QLA_PORT_DISP_NEW,
+ QLA_PORT_DISP_GONE,
+ QLA_PORT_DISP_SAME,
+ QLA_PORT_DISP_CHANGED,
+ QLA_PORT_DISP_MOVED,
+ QLA_PORT_DISP_DUP
+};
+
+#define QLA_UPDATE_DISCARD 0
+#define QLA_UPDATE_SOFTRESET 1
+#define QLA_UPDATE_FULL_SCAN 2
+#define QLA_UPDATE_LOOP_SCAN 3
+#define QLA_UPDATE_FABRIC_SCAN 4
+#define QLA_UPDATE_FABRIC_RELOGIN 5
+
+#define QLA_LOCATION_LOOP_ID(l) (l | (1 << 24))
+#define QLA_LOCATION_PORT_ID(p) (p | (2 << 24))
+
+struct qla_fc_port {
+ TAILQ_ENTRY(qla_fc_port) ports;
+ TAILQ_ENTRY(qla_fc_port) update;
+
+ u_int64_t node_name;
+ u_int64_t port_name;
+ u_int32_t location; /* port id or loop id */
+
+ int flags;
+#define QLA_PORT_FLAG_IS_TARGET 1
+#define QLA_PORT_FLAG_NEEDS_LOGIN 2
+
+ u_int32_t portid;
+ u_int16_t loopid;
+};
+
+
+/* request/response queue stuff */
+#define QLA_QUEUE_ENTRY_SIZE 64
+
+struct qla_ccb {
+ struct qla_softc *ccb_sc;
+ int ccb_id;
+ struct scsi_xfer *ccb_xs;
+
+ bus_dmamap_t ccb_dmamap;
+
+ struct qla_iocb_seg *ccb_t4segs;
+ u_int64_t ccb_seg_offset;
+
+ SIMPLEQ_ENTRY(qla_ccb) ccb_link;
+};
+
+SIMPLEQ_HEAD(qla_ccb_list, qla_ccb);
+
+struct qla_dmamem {
+ bus_dmamap_t qdm_map;
+ bus_dma_segment_t qdm_seg;
+ size_t qdm_size;
+ caddr_t qdm_kva;
+};
+#define QLA_DMA_MAP(_qdm) ((_qdm)->qdm_map)
+#define QLA_DMA_LEN(_qdm) ((_qdm)->qdm_size)
+#define QLA_DMA_DVA(_qdm) ((u_int64_t)(_qdm)->qdm_map->dm_segs[0].ds_addr)
+#define QLA_DMA_KVA(_qdm) ((void *)(_qdm)->qdm_kva)
+
+struct qla_softc {
+ struct device sc_dev;
+
+ bus_space_tag_t sc_iot;
+ bus_space_handle_t sc_ioh;
+ bus_size_t sc_ios;
+ bus_dma_tag_t sc_dmat;
+
+ struct scsi_link sc_link;
+
+ struct scsibus_softc *sc_scsibus;
+
+ enum qla_isp_type sc_isp_type;
+ enum qla_isp_gen sc_isp_gen;
+ int sc_port;
+ int sc_expanded_lun;
+ int sc_fabric;
+ int sc_2k_logins;
+
+ int sc_mbox_base;
+ u_int16_t sc_mbox[12];
+ int sc_mbox_pending;
+
+ int sc_loop_up;
+ int sc_topology;
+ int sc_loop_id;
+ int sc_port_id;
+
+ struct mutex sc_port_mtx;
+ TAILQ_HEAD(, qla_fc_port) sc_ports;
+ TAILQ_HEAD(, qla_fc_port) sc_ports_new;
+ TAILQ_HEAD(, qla_fc_port) sc_ports_gone;
+ struct qla_fc_port *sc_targets[QLA_MAX_TARGETS];
+ struct taskq *sc_scan_taskq;
+
+ int sc_maxcmds;
+ struct qla_dmamem *sc_requests;
+ struct qla_dmamem *sc_responses;
+ struct qla_dmamem *sc_segments;
+ struct qla_dmamem *sc_scratch;
+ struct qla_ccb *sc_ccbs;
+ struct qla_ccb_list sc_ccb_free;
+ struct mutex sc_ccb_mtx;
+ struct mutex sc_queue_mtx;
+ struct scsi_iopool sc_iopool;
+ u_int16_t sc_next_req_id;
+ u_int16_t sc_last_resp_id;
+ int sc_marker_required;
+
+ struct qla_nvram sc_nvram;
+ int sc_nvram_valid;
+};
+#define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname)
+
+int qla_attach(struct qla_softc *);
+int qla_detach(struct qla_softc *, int);
+
+int qla_intr(void *);
diff --git a/sys/dev/pci/files.pci b/sys/dev/pci/files.pci
index c62ba3b898f..4965448958f 100644
--- a/sys/dev/pci/files.pci
+++ b/sys/dev/pci/files.pci
@@ -1,4 +1,4 @@
-# $OpenBSD: files.pci,v 1.300 2013/12/20 21:50:49 matthew Exp $
+# $OpenBSD: files.pci,v 1.301 2014/01/19 06:04:03 jmatthew Exp $
# $NetBSD: files.pci,v 1.20 1996/09/24 17:47:15 christos Exp $
#
# Config file and device description for machine-independent PCI code.
@@ -213,6 +213,10 @@ file dev/pci/ciss_pci.c ciss_pci
attach isp at pci with isp_pci
file dev/pci/isp_pci.c isp_pci
+# QLogic ISP23xx FC Controllers
+attach qla at pci with qla_pci
+file dev/pci/qla_pci.c qla_pci
+
# LSI Logic Fusion-MPT Message Passing Interface
attach mpi at pci with mpi_pci
file dev/pci/mpi_pci.c mpi_pci
diff --git a/sys/dev/pci/qla_pci.c b/sys/dev/pci/qla_pci.c
new file mode 100644
index 00000000000..9d88f5ef9f2
--- /dev/null
+++ b/sys/dev/pci/qla_pci.c
@@ -0,0 +1,224 @@
+/* $OpenBSD: qla_pci.c,v 1.1 2014/01/19 06:04:03 jmatthew Exp $ */
+
+/*
+ * Copyright (c) 2011 David Gwynne <dlg@openbsd.org>
+ * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include "bio.h"
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/kernel.h>
+#include <sys/malloc.h>
+#include <sys/device.h>
+#include <sys/sensors.h>
+#include <sys/rwlock.h>
+
+#include <machine/bus.h>
+
+#include <dev/pci/pcireg.h>
+#include <dev/pci/pcivar.h>
+#include <dev/pci/pcidevs.h>
+
+#ifdef __sparc64__
+#include <dev/ofw/openfirm.h>
+#endif
+
+#include <scsi/scsi_all.h>
+#include <scsi/scsiconf.h>
+
+#include <dev/ic/qlareg.h>
+#include <dev/ic/qlavar.h>
+
+#define QLA_PCI_MEM_BAR 0x14
+#define QLA_PCI_IO_BAR 0x10
+
+int qla_pci_match(struct device *, void *, void *);
+void qla_pci_attach(struct device *, struct device *, void *);
+int qla_pci_detach(struct device *, int);
+
+struct qla_pci_softc {
+ struct qla_softc psc_qla;
+
+ pci_chipset_tag_t psc_pc;
+ pcitag_t psc_tag;
+
+ void *psc_ih;
+};
+
+struct cfattach qla_pci_ca = {
+ sizeof(struct qla_pci_softc),
+ qla_pci_match,
+ qla_pci_attach
+};
+
+#define PREAD(s, r) pci_conf_read((s)->psc_pc, (s)->psc_tag, (r))
+#define PWRITE(s, r, v) pci_conf_write((s)->psc_pc, (s)->psc_tag, (r), (v))
+
+static const struct pci_matchid qla_devices[] = {
+ /* { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2100 }, */
+ /* { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2200 }, */
+ { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2300 },
+ { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2312 },
+ { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2322 },
+ { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP6312 },
+};
+
+int
+qla_pci_match(struct device *parent, void *match, void *aux)
+{
+ return (pci_matchbyid(aux, qla_devices, nitems(qla_devices)) * 2);
+}
+
+void
+qla_pci_attach(struct device *parent, struct device *self, void *aux)
+{
+ struct qla_pci_softc *psc = (void *)self;
+ struct qla_softc *sc = &psc->psc_qla;
+ struct pci_attach_args *pa = aux;
+ pci_intr_handle_t ih;
+ u_int32_t pcictl;
+
+ pcireg_t bars[] = { QLA_PCI_MEM_BAR, QLA_PCI_IO_BAR };
+ pcireg_t memtype;
+ int r;
+
+ psc->psc_pc = pa->pa_pc;
+ psc->psc_tag = pa->pa_tag;
+ psc->psc_ih = NULL;
+ sc->sc_dmat = pa->pa_dmat;
+ sc->sc_ios = 0;
+
+ for (r = 0; r < nitems(bars); r++) {
+ memtype = pci_mapreg_type(psc->psc_pc, psc->psc_tag, bars[r]);
+ if (pci_mapreg_map(pa, bars[r], memtype, 0,
+ &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) == 0)
+ break;
+
+ sc->sc_ios = 0;
+ }
+ if (sc->sc_ios == 0) {
+ printf(": unable to map registers\n");
+ return;
+ }
+
+ if (pci_intr_map(pa, &ih)) {
+ printf(": unable to map interrupt\n");
+ goto unmap;
+ }
+ printf(": %s\n", pci_intr_string(psc->psc_pc, ih));
+
+ psc->psc_ih = pci_intr_establish(psc->psc_pc, ih, IPL_BIO,
+ qla_intr, sc, sc->sc_dev.dv_xname);
+ if (psc->psc_ih == NULL) {
+ printf("%s: unable to establish interrupt\n");
+ goto deintr;
+ }
+
+ pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG);
+ pcictl |= PCI_COMMAND_INVALIDATE_ENABLE |
+ PCI_COMMAND_PARITY_ENABLE | PCI_COMMAND_SERR_ENABLE;
+ /* fw manual says to enable bus master here, then disable it while
+ * resetting.. hm.
+ */
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, pcictl);
+
+ pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG);
+ pcictl &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT);
+ pcictl &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT);
+ pcictl |= (0x80 << PCI_LATTIMER_SHIFT);
+ pcictl |= (0x10 << PCI_CACHELINE_SHIFT);
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, pcictl);
+
+ pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG);
+ pcictl &= ~1;
+ pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, pcictl);
+
+ switch (PCI_PRODUCT(pa->pa_id)) {
+ case PCI_PRODUCT_QLOGIC_ISP2100:
+ /* not supported yet */
+ sc->sc_isp_gen = QLA_GEN_ISP2100;
+ sc->sc_isp_type = QLA_ISP2100;
+ break;
+
+ case PCI_PRODUCT_QLOGIC_ISP2200:
+ /* not supported yet */
+ sc->sc_isp_gen = QLA_GEN_ISP2200;
+ sc->sc_isp_type = QLA_ISP2200;
+ break;
+
+ case PCI_PRODUCT_QLOGIC_ISP2300:
+ sc->sc_isp_type = QLA_ISP2300;
+ sc->sc_isp_gen = QLA_GEN_ISP23XX;
+ break;
+
+ case PCI_PRODUCT_QLOGIC_ISP2312:
+ case PCI_PRODUCT_QLOGIC_ISP6312:
+ sc->sc_isp_type = QLA_ISP2312;
+ sc->sc_isp_gen = QLA_GEN_ISP23XX;
+ break;
+
+ case PCI_PRODUCT_QLOGIC_ISP2322:
+ sc->sc_isp_type = QLA_ISP2322;
+ sc->sc_isp_gen = QLA_GEN_ISP23XX;
+ break;
+
+ default:
+ printf("unknown pci id %x", pa->pa_id);
+ return;
+ }
+
+ sc->sc_port = pa->pa_function;
+
+ if (qla_attach(sc) != 0) {
+ /* error printed by qla_attach */
+ goto deintr;
+ }
+
+ return;
+
+deintr:
+ pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
+ psc->psc_ih = NULL;
+unmap:
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ sc->sc_ios = 0;
+}
+
+int
+qla_pci_detach(struct device *self, int flags)
+{
+ struct qla_pci_softc *psc = (struct qla_pci_softc *)self;
+ struct qla_softc *sc = &psc->psc_qla;
+ int rv;
+
+ if (psc->psc_ih == NULL) {
+ /* we didnt attach properly, so nothing to detach */
+ return (0);
+ }
+
+ rv = qla_detach(sc, flags);
+ if (rv != 0)
+ return (rv);
+
+ pci_intr_disestablish(psc->psc_pc, psc->psc_ih);
+ psc->psc_ih = NULL;
+
+ bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios);
+ sc->sc_ios = 0;
+
+ return (0);
+}