diff options
Diffstat (limited to 'sys')
-rw-r--r-- | sys/conf/files | 6 | ||||
-rw-r--r-- | sys/dev/ic/qla.c | 1900 | ||||
-rw-r--r-- | sys/dev/ic/qlareg.h | 626 | ||||
-rw-r--r-- | sys/dev/ic/qlavar.h | 177 | ||||
-rw-r--r-- | sys/dev/pci/files.pci | 6 | ||||
-rw-r--r-- | sys/dev/pci/qla_pci.c | 224 |
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); +} |