1*9593dc34Smglocker /* $OpenBSD: qle.c,v 1.66 2024/09/04 07:54:52 mglocker Exp $ */ 23759911cSjmatthew 33759911cSjmatthew /* 43759911cSjmatthew * Copyright (c) 2013, 2014 Jonathan Matthew <jmatthew@openbsd.org> 53759911cSjmatthew * 63759911cSjmatthew * Permission to use, copy, modify, and distribute this software for any 73759911cSjmatthew * purpose with or without fee is hereby granted, provided that the above 83759911cSjmatthew * copyright notice and this permission notice appear in all copies. 93759911cSjmatthew * 103759911cSjmatthew * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 113759911cSjmatthew * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 123759911cSjmatthew * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 133759911cSjmatthew * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 143759911cSjmatthew * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 153759911cSjmatthew * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 163759911cSjmatthew * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 173759911cSjmatthew */ 183759911cSjmatthew 193759911cSjmatthew #include <sys/param.h> 203759911cSjmatthew #include <sys/systm.h> 219a1bdb58Svisa #include <sys/atomic.h> 223759911cSjmatthew #include <sys/malloc.h> 233759911cSjmatthew #include <sys/device.h> 243759911cSjmatthew #include <sys/task.h> 253fc6fae1Sjmatthew #include <sys/timeout.h> 263759911cSjmatthew 273759911cSjmatthew #include <machine/bus.h> 283759911cSjmatthew 293759911cSjmatthew #include <dev/pci/pcireg.h> 303759911cSjmatthew #include <dev/pci/pcivar.h> 313759911cSjmatthew #include <dev/pci/pcidevs.h> 323759911cSjmatthew 333759911cSjmatthew #include <scsi/scsi_all.h> 343759911cSjmatthew #include <scsi/scsiconf.h> 353759911cSjmatthew 363759911cSjmatthew #include <dev/pci/qlereg.h> 373759911cSjmatthew 38cc014dc0Sjmatthew #ifdef QLE_DEBUG 39cc014dc0Sjmatthew #define DPRINTF(m, f...) do { if ((qledebug & (m)) == (m)) printf(f); } \ 40cc014dc0Sjmatthew while (0) 41cc014dc0Sjmatthew #define QLE_D_MBOX 0x01 42cc014dc0Sjmatthew #define QLE_D_INTR 0x02 43cc014dc0Sjmatthew #define QLE_D_PORT 0x04 44cc014dc0Sjmatthew #define QLE_D_IO 0x08 45cc014dc0Sjmatthew #define QLE_D_IOCB 0x10 46cc014dc0Sjmatthew int qledebug = QLE_D_PORT; 47cc014dc0Sjmatthew #else 48cc014dc0Sjmatthew #define DPRINTF(m, f...) 49cc014dc0Sjmatthew #endif 50cc014dc0Sjmatthew 51c8005c8bSjmatthew #ifndef QLE_NOFIRMWARE 523759911cSjmatthew #include <dev/microcode/isp/asm_2400.h> 53b803df0bSjmatthew #include <dev/microcode/isp/asm_2500.h> 54989131ebSjmatthew #endif 553759911cSjmatthew 563759911cSjmatthew #define QLE_PCI_MEM_BAR 0x14 573759911cSjmatthew #define QLE_PCI_IO_BAR 0x10 583759911cSjmatthew 593759911cSjmatthew 603759911cSjmatthew #define QLE_DEFAULT_PORT_NAME 0x400000007F000003ULL /* from isp(4) */ 613759911cSjmatthew 6211e354acSjmatthew #define QLE_WAIT_FOR_LOOP 10 /* seconds */ 6311e354acSjmatthew #define QLE_LOOP_SETTLE 200 /* ms */ 643759911cSjmatthew 653759911cSjmatthew /* rounded up range of assignable handles */ 663759911cSjmatthew #define QLE_MAX_TARGETS 2048 673759911cSjmatthew 683759911cSjmatthew /* maximum number of segments allowed for in a single io */ 6920c4379eSjmatthew #define QLE_MAX_SEGS 32 703759911cSjmatthew 713759911cSjmatthew enum qle_isp_gen { 723759911cSjmatthew QLE_GEN_ISP24XX = 1, 73b803df0bSjmatthew QLE_GEN_ISP25XX 743759911cSjmatthew }; 753759911cSjmatthew 763759911cSjmatthew enum qle_isp_type { 773759911cSjmatthew QLE_ISP2422 = 1, 783759911cSjmatthew QLE_ISP2432, 793759911cSjmatthew QLE_ISP2512, 803759911cSjmatthew QLE_ISP2522, 813759911cSjmatthew QLE_ISP2532 823759911cSjmatthew }; 833759911cSjmatthew 843759911cSjmatthew /* port database things */ 853759911cSjmatthew #define QLE_SCRATCH_SIZE 0x1000 863759911cSjmatthew 873759911cSjmatthew enum qle_port_disp { 883759911cSjmatthew QLE_PORT_DISP_NEW, 893759911cSjmatthew QLE_PORT_DISP_GONE, 903759911cSjmatthew QLE_PORT_DISP_SAME, 913759911cSjmatthew QLE_PORT_DISP_CHANGED, 923759911cSjmatthew QLE_PORT_DISP_MOVED, 933759911cSjmatthew QLE_PORT_DISP_DUP 943759911cSjmatthew }; 953759911cSjmatthew 963759911cSjmatthew #define QLE_LOCATION_LOOP (1 << 24) 973759911cSjmatthew #define QLE_LOCATION_FABRIC (2 << 24) 983759911cSjmatthew #define QLE_LOCATION_LOOP_ID(l) (l | QLE_LOCATION_LOOP) 993759911cSjmatthew #define QLE_LOCATION_PORT_ID(p) (p | QLE_LOCATION_FABRIC) 1003759911cSjmatthew 1013759911cSjmatthew struct qle_fc_port { 1023759911cSjmatthew TAILQ_ENTRY(qle_fc_port) ports; 1033759911cSjmatthew TAILQ_ENTRY(qle_fc_port) update; 1043759911cSjmatthew 1053759911cSjmatthew u_int64_t node_name; 1063759911cSjmatthew u_int64_t port_name; 1073759911cSjmatthew u_int32_t location; /* port id or loop id */ 1083759911cSjmatthew 1093759911cSjmatthew int flags; 1103759911cSjmatthew #define QLE_PORT_FLAG_IS_TARGET 1 1113759911cSjmatthew #define QLE_PORT_FLAG_NEEDS_LOGIN 2 1123759911cSjmatthew 1133759911cSjmatthew u_int32_t portid; 1143759911cSjmatthew u_int16_t loopid; 1153759911cSjmatthew }; 1163759911cSjmatthew 1173759911cSjmatthew 1183759911cSjmatthew /* request/response queue stuff */ 1193759911cSjmatthew #define QLE_QUEUE_ENTRY_SIZE 64 1203759911cSjmatthew 1213759911cSjmatthew struct qle_ccb { 1223759911cSjmatthew struct qle_softc *ccb_sc; 1233759911cSjmatthew int ccb_id; 1243759911cSjmatthew struct scsi_xfer *ccb_xs; 1253759911cSjmatthew 1263759911cSjmatthew bus_dmamap_t ccb_dmamap; 1273759911cSjmatthew 1283759911cSjmatthew struct qle_iocb_seg *ccb_segs; 1293759911cSjmatthew u_int64_t ccb_seg_offset; 1303759911cSjmatthew 1313759911cSjmatthew SIMPLEQ_ENTRY(qle_ccb) ccb_link; 1323759911cSjmatthew }; 1333759911cSjmatthew 1343759911cSjmatthew SIMPLEQ_HEAD(qle_ccb_list, qle_ccb); 1353759911cSjmatthew 1363759911cSjmatthew struct qle_dmamem { 1373759911cSjmatthew bus_dmamap_t qdm_map; 1383759911cSjmatthew bus_dma_segment_t qdm_seg; 1393759911cSjmatthew size_t qdm_size; 1403759911cSjmatthew caddr_t qdm_kva; 1413759911cSjmatthew }; 1423759911cSjmatthew #define QLE_DMA_MAP(_qdm) ((_qdm)->qdm_map) 1433759911cSjmatthew #define QLE_DMA_LEN(_qdm) ((_qdm)->qdm_size) 1443759911cSjmatthew #define QLE_DMA_DVA(_qdm) ((u_int64_t)(_qdm)->qdm_map->dm_segs[0].ds_addr) 1453759911cSjmatthew #define QLE_DMA_KVA(_qdm) ((void *)(_qdm)->qdm_kva) 1463759911cSjmatthew 1473759911cSjmatthew struct qle_softc { 1483759911cSjmatthew struct device sc_dev; 1493759911cSjmatthew 1503759911cSjmatthew pci_chipset_tag_t sc_pc; 1513759911cSjmatthew pcitag_t sc_tag; 1523759911cSjmatthew 1533759911cSjmatthew void *sc_ih; 1543759911cSjmatthew bus_space_tag_t sc_iot; 1553759911cSjmatthew bus_space_handle_t sc_ioh; 1563759911cSjmatthew bus_size_t sc_ios; 1573759911cSjmatthew bus_dma_tag_t sc_dmat; 1583759911cSjmatthew 1593759911cSjmatthew struct scsibus_softc *sc_scsibus; 1603759911cSjmatthew 1613759911cSjmatthew enum qle_isp_type sc_isp_type; 1623759911cSjmatthew enum qle_isp_gen sc_isp_gen; 1633759911cSjmatthew int sc_port; 1643759911cSjmatthew 16569e488dfSdlg bus_space_handle_t sc_mbox_ioh; 1663759911cSjmatthew u_int16_t sc_mbox[QLE_MBOX_COUNT]; 1673759911cSjmatthew int sc_mbox_pending; 1683759911cSjmatthew struct mutex sc_mbox_mtx; 1693759911cSjmatthew 1703759911cSjmatthew int sc_loop_up; 1713759911cSjmatthew int sc_topology; 1723759911cSjmatthew int sc_loop_id; 1733759911cSjmatthew int sc_port_id; 1743759911cSjmatthew int sc_loop_max_id; 1753759911cSjmatthew u_int64_t sc_sns_port_name; 1763759911cSjmatthew 1773759911cSjmatthew struct mutex sc_port_mtx; 1783759911cSjmatthew TAILQ_HEAD(, qle_fc_port) sc_ports; 1793759911cSjmatthew TAILQ_HEAD(, qle_fc_port) sc_ports_new; 1803759911cSjmatthew TAILQ_HEAD(, qle_fc_port) sc_ports_gone; 1813759911cSjmatthew TAILQ_HEAD(, qle_fc_port) sc_ports_found; 1823759911cSjmatthew struct qle_fc_port *sc_targets[QLE_MAX_TARGETS]; 1833759911cSjmatthew 1843759911cSjmatthew struct taskq *sc_update_taskq; 1853759911cSjmatthew struct task sc_update_task; 1863fc6fae1Sjmatthew struct timeout sc_update_timeout; 1873759911cSjmatthew int sc_update; 1883759911cSjmatthew int sc_update_tasks; 1893759911cSjmatthew #define QLE_UPDATE_TASK_CLEAR_ALL 0x00000001 1903759911cSjmatthew #define QLE_UPDATE_TASK_SOFTRESET 0x00000002 191580dbe8fSjmatthew #define QLE_UPDATE_TASK_UPDATE_TOPO 0x00000004 192580dbe8fSjmatthew #define QLE_UPDATE_TASK_GET_PORT_LIST 0x00000008 193580dbe8fSjmatthew #define QLE_UPDATE_TASK_PORT_LIST 0x00000010 194580dbe8fSjmatthew #define QLE_UPDATE_TASK_SCAN_FABRIC 0x00000020 195580dbe8fSjmatthew #define QLE_UPDATE_TASK_SCANNING_FABRIC 0x00000040 196580dbe8fSjmatthew #define QLE_UPDATE_TASK_FABRIC_LOGIN 0x00000080 197580dbe8fSjmatthew #define QLE_UPDATE_TASK_FABRIC_RELOGIN 0x00000100 198580dbe8fSjmatthew #define QLE_UPDATE_TASK_DETACH_TARGET 0x00000200 199580dbe8fSjmatthew #define QLE_UPDATE_TASK_ATTACH_TARGET 0x00000400 2003759911cSjmatthew 2013759911cSjmatthew int sc_maxcmds; 2023759911cSjmatthew struct qle_dmamem *sc_requests; 2033759911cSjmatthew struct qle_dmamem *sc_responses; 2043759911cSjmatthew struct qle_dmamem *sc_segments; 2053759911cSjmatthew struct qle_dmamem *sc_pri_requests; 2063759911cSjmatthew struct qle_dmamem *sc_scratch; 2073759911cSjmatthew struct qle_dmamem *sc_fcp_cmnds; 2083759911cSjmatthew struct qle_ccb *sc_ccbs; 2093759911cSjmatthew struct qle_ccb_list sc_ccb_free; 2103759911cSjmatthew struct mutex sc_ccb_mtx; 2113759911cSjmatthew struct mutex sc_queue_mtx; 2123759911cSjmatthew struct scsi_iopool sc_iopool; 2133759911cSjmatthew u_int32_t sc_next_req_id; 2143759911cSjmatthew u_int32_t sc_last_resp_id; 2153759911cSjmatthew int sc_marker_required; 2163759911cSjmatthew int sc_fabric_pending; 217e5d3c726Sjmatthew u_int8_t sc_fabric_response[QLE_QUEUE_ENTRY_SIZE]; 2183759911cSjmatthew 2193759911cSjmatthew struct qle_nvram sc_nvram; 2203759911cSjmatthew int sc_nvram_valid; 2213759911cSjmatthew }; 2223759911cSjmatthew #define DEVNAME(_sc) ((_sc)->sc_dev.dv_xname) 2233759911cSjmatthew 2243759911cSjmatthew int qle_intr(void *); 2253759911cSjmatthew 2263759911cSjmatthew int qle_match(struct device *, void *, void *); 2273759911cSjmatthew void qle_attach(struct device *, struct device *, void *); 2283759911cSjmatthew int qle_detach(struct device *, int); 2293759911cSjmatthew 2308d2c75e4Smpi const struct cfattach qle_ca = { 2313759911cSjmatthew sizeof(struct qle_softc), 2323759911cSjmatthew qle_match, 2333759911cSjmatthew qle_attach, 2343759911cSjmatthew qle_detach 2353759911cSjmatthew }; 2363759911cSjmatthew 2373759911cSjmatthew struct cfdriver qle_cd = { 2383759911cSjmatthew NULL, 2393759911cSjmatthew "qle", 2403759911cSjmatthew DV_DULL 2413759911cSjmatthew }; 2423759911cSjmatthew 2433759911cSjmatthew void qle_scsi_cmd(struct scsi_xfer *); 2443759911cSjmatthew int qle_scsi_probe(struct scsi_link *); 2453759911cSjmatthew 2463759911cSjmatthew 247a454aff3Snaddy const struct scsi_adapter qle_switch = { 24821ceeee0Skrw qle_scsi_cmd, NULL, qle_scsi_probe, NULL, NULL 2493759911cSjmatthew }; 2503759911cSjmatthew 2513759911cSjmatthew u_int32_t qle_read(struct qle_softc *, int); 2523759911cSjmatthew void qle_write(struct qle_softc *, int, u_int32_t); 2533759911cSjmatthew void qle_host_cmd(struct qle_softc *sc, u_int32_t); 2543759911cSjmatthew 25565fd2507Sjmatthew int qle_mbox(struct qle_softc *, int); 2563759911cSjmatthew int qle_ct_pass_through(struct qle_softc *sc, 2573759911cSjmatthew u_int32_t port_handle, struct qle_dmamem *mem, 2583759911cSjmatthew size_t req_size, size_t resp_size); 2593759911cSjmatthew void qle_mbox_putaddr(u_int16_t *, struct qle_dmamem *); 2603759911cSjmatthew u_int16_t qle_read_mbox(struct qle_softc *, int); 2613759911cSjmatthew void qle_write_mbox(struct qle_softc *, int, u_int16_t); 2623759911cSjmatthew 2633759911cSjmatthew void qle_handle_intr(struct qle_softc *, u_int16_t, u_int16_t); 2643759911cSjmatthew void qle_set_ints(struct qle_softc *, int); 2653759911cSjmatthew int qle_read_isr(struct qle_softc *, u_int16_t *, u_int16_t *); 2663759911cSjmatthew void qle_clear_isr(struct qle_softc *, u_int16_t); 2673759911cSjmatthew 2683759911cSjmatthew void qle_put_marker(struct qle_softc *, void *); 2693759911cSjmatthew void qle_put_cmd(struct qle_softc *, void *, struct scsi_xfer *, 2703759911cSjmatthew struct qle_ccb *, u_int32_t); 271ce28b8eaSdlg struct qle_ccb *qle_handle_resp(struct qle_softc *, u_int32_t); 272a0abcb68Sdlg void qle_sge(struct qle_iocb_seg *, u_int64_t, u_int32_t); 2733759911cSjmatthew 2743759911cSjmatthew struct qle_fc_port *qle_next_fabric_port(struct qle_softc *, u_int32_t *, 2753759911cSjmatthew u_int32_t *); 2763759911cSjmatthew int qle_get_port_db(struct qle_softc *, u_int16_t, 2773759911cSjmatthew struct qle_dmamem *); 278580dbe8fSjmatthew int qle_get_port_name_list(struct qle_softc *sc, u_int32_t); 279580dbe8fSjmatthew int qle_add_loop_port(struct qle_softc *, struct qle_fc_port *); 2803759911cSjmatthew int qle_add_fabric_port(struct qle_softc *, struct qle_fc_port *); 2810d6eccfaSjmatthew int qle_add_logged_in_port(struct qle_softc *, u_int16_t, 282b9154820Sjmatthew u_int32_t); 2833759911cSjmatthew int qle_classify_port(struct qle_softc *, u_int32_t, u_int64_t, 2843759911cSjmatthew u_int64_t, struct qle_fc_port **); 2850d6eccfaSjmatthew int qle_get_loop_id(struct qle_softc *sc, int); 2863759911cSjmatthew void qle_clear_port_lists(struct qle_softc *); 2873759911cSjmatthew int qle_softreset(struct qle_softc *); 2883759911cSjmatthew void qle_update_topology(struct qle_softc *); 2893759911cSjmatthew int qle_update_fabric(struct qle_softc *); 290e5d3c726Sjmatthew int qle_fabric_plogx(struct qle_softc *, struct qle_fc_port *, int, 291e5d3c726Sjmatthew u_int32_t *); 2923759911cSjmatthew int qle_fabric_plogi(struct qle_softc *, struct qle_fc_port *); 2933759911cSjmatthew void qle_fabric_plogo(struct qle_softc *, struct qle_fc_port *); 2943759911cSjmatthew 2953759911cSjmatthew void qle_update_start(struct qle_softc *, int); 2963fc6fae1Sjmatthew void qle_update_defer(struct qle_softc *, int); 2973fc6fae1Sjmatthew void qle_update_cancel(struct qle_softc *); 2983759911cSjmatthew void qle_update_done(struct qle_softc *, int); 299e4195480Sdlg void qle_do_update(void *); 3003fc6fae1Sjmatthew void qle_deferred_update(void *); 3013759911cSjmatthew int qle_async(struct qle_softc *, u_int16_t); 3023759911cSjmatthew 303b803df0bSjmatthew int qle_load_fwchunk(struct qle_softc *, 3043759911cSjmatthew struct qle_dmamem *, const u_int32_t *); 305c8005c8bSjmatthew u_int32_t qle_read_ram_word(struct qle_softc *, u_int32_t); 306c8005c8bSjmatthew int qle_verify_firmware(struct qle_softc *, u_int32_t); 307b803df0bSjmatthew int qle_load_firmware_chunks(struct qle_softc *, const u_int32_t *); 3083759911cSjmatthew int qle_read_nvram(struct qle_softc *); 3093759911cSjmatthew 3103759911cSjmatthew struct qle_dmamem *qle_dmamem_alloc(struct qle_softc *, size_t); 3113759911cSjmatthew void qle_dmamem_free(struct qle_softc *, struct qle_dmamem *); 3123759911cSjmatthew 3133759911cSjmatthew int qle_alloc_ccbs(struct qle_softc *); 3143759911cSjmatthew void qle_free_ccbs(struct qle_softc *); 3153759911cSjmatthew void *qle_get_ccb(void *); 3163759911cSjmatthew void qle_put_ccb(void *, void *); 3173759911cSjmatthew 3183759911cSjmatthew void qle_dump_stuff(struct qle_softc *, void *, int); 3193759911cSjmatthew void qle_dump_iocb(struct qle_softc *, void *); 3203759911cSjmatthew void qle_dump_iocb_segs(struct qle_softc *, void *, int); 3213759911cSjmatthew 3223759911cSjmatthew static const struct pci_matchid qle_devices[] = { 3233759911cSjmatthew { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2422 }, 3243759911cSjmatthew { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2432 }, 325b803df0bSjmatthew { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2512 }, 3263759911cSjmatthew { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2522 }, 327b803df0bSjmatthew { PCI_VENDOR_QLOGIC, PCI_PRODUCT_QLOGIC_ISP2532 }, 3283759911cSjmatthew }; 3293759911cSjmatthew 3303759911cSjmatthew int 3313759911cSjmatthew qle_match(struct device *parent, void *match, void *aux) 3323759911cSjmatthew { 333b803df0bSjmatthew return (pci_matchbyid(aux, qle_devices, nitems(qle_devices))); 3343759911cSjmatthew } 3353759911cSjmatthew 3363759911cSjmatthew void 3373759911cSjmatthew qle_attach(struct device *parent, struct device *self, void *aux) 3383759911cSjmatthew { 3393759911cSjmatthew struct qle_softc *sc = (void *)self; 3403759911cSjmatthew struct pci_attach_args *pa = aux; 3413759911cSjmatthew pci_intr_handle_t ih; 342ba6d46bdSkettenis const char *intrstr; 3433759911cSjmatthew u_int32_t pcictl; 3443759911cSjmatthew struct scsibus_attach_args saa; 3453759911cSjmatthew struct qle_init_cb *icb; 34669e488dfSdlg bus_size_t mbox_base; 347c8005c8bSjmatthew u_int32_t firmware_addr; 348c8005c8bSjmatthew #ifndef QLE_NOFIRMWARE 349c8005c8bSjmatthew const u_int32_t *firmware = NULL; 350c8005c8bSjmatthew #endif 3513759911cSjmatthew 3523759911cSjmatthew pcireg_t bars[] = { QLE_PCI_MEM_BAR, QLE_PCI_IO_BAR }; 3533759911cSjmatthew pcireg_t memtype; 35432c7aa28Sjsg int r, i, rv, loop_up = 0; 3553759911cSjmatthew 3563759911cSjmatthew sc->sc_pc = pa->pa_pc; 3573759911cSjmatthew sc->sc_tag = pa->pa_tag; 3583759911cSjmatthew sc->sc_ih = NULL; 3593759911cSjmatthew sc->sc_dmat = pa->pa_dmat; 3603759911cSjmatthew sc->sc_ios = 0; 3613759911cSjmatthew 3623759911cSjmatthew for (r = 0; r < nitems(bars); r++) { 3633759911cSjmatthew memtype = pci_mapreg_type(sc->sc_pc, sc->sc_tag, bars[r]); 3643759911cSjmatthew if (pci_mapreg_map(pa, bars[r], memtype, 0, 3653759911cSjmatthew &sc->sc_iot, &sc->sc_ioh, NULL, &sc->sc_ios, 0) == 0) 3663759911cSjmatthew break; 3673759911cSjmatthew 3683759911cSjmatthew sc->sc_ios = 0; 3693759911cSjmatthew } 3703759911cSjmatthew if (sc->sc_ios == 0) { 3713759911cSjmatthew printf(": unable to map registers\n"); 3723759911cSjmatthew return; 3733759911cSjmatthew } 3743759911cSjmatthew 37531ccda6aSdlg if (pci_intr_map_msi(pa, &ih) != 0 && pci_intr_map(pa, &ih) != 0) { 3763759911cSjmatthew printf(": unable to map interrupt\n"); 3773759911cSjmatthew goto unmap; 3783759911cSjmatthew } 379ba6d46bdSkettenis intrstr = pci_intr_string(sc->sc_pc, ih); 3803759911cSjmatthew sc->sc_ih = pci_intr_establish(sc->sc_pc, ih, IPL_BIO, 38143d6dcd9Sdlg qle_intr, sc, DEVNAME(sc)); 3823759911cSjmatthew if (sc->sc_ih == NULL) { 383ba6d46bdSkettenis printf(": unable to establish interrupt"); 384ba6d46bdSkettenis if (intrstr != NULL) 385ba6d46bdSkettenis printf(" at %s", intrstr); 386ba6d46bdSkettenis printf("\n"); 3873759911cSjmatthew goto deintr; 3883759911cSjmatthew } 3893759911cSjmatthew 390ba6d46bdSkettenis printf(": %s\n", intrstr); 391ba6d46bdSkettenis 3923759911cSjmatthew pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG); 3933759911cSjmatthew pcictl |= PCI_COMMAND_INVALIDATE_ENABLE | 3943759911cSjmatthew PCI_COMMAND_PARITY_ENABLE | PCI_COMMAND_SERR_ENABLE; 3953759911cSjmatthew pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_COMMAND_STATUS_REG, pcictl); 3963759911cSjmatthew 3973759911cSjmatthew pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG); 3983759911cSjmatthew pcictl &= ~(PCI_LATTIMER_MASK << PCI_LATTIMER_SHIFT); 3993759911cSjmatthew pcictl &= ~(PCI_CACHELINE_MASK << PCI_CACHELINE_SHIFT); 4003759911cSjmatthew pcictl |= (0x80 << PCI_LATTIMER_SHIFT); 4013759911cSjmatthew pcictl |= (0x10 << PCI_CACHELINE_SHIFT); 4023759911cSjmatthew pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_BHLC_REG, pcictl); 4033759911cSjmatthew 4043759911cSjmatthew pcictl = pci_conf_read(pa->pa_pc, pa->pa_tag, PCI_ROM_REG); 4053759911cSjmatthew pcictl &= ~1; 4063759911cSjmatthew pci_conf_write(pa->pa_pc, pa->pa_tag, PCI_ROM_REG, pcictl); 4073759911cSjmatthew 4083759911cSjmatthew switch (PCI_PRODUCT(pa->pa_id)) { 4093759911cSjmatthew case PCI_PRODUCT_QLOGIC_ISP2422: 4103759911cSjmatthew sc->sc_isp_type = QLE_ISP2422; 4113759911cSjmatthew sc->sc_isp_gen = QLE_GEN_ISP24XX; 4123759911cSjmatthew break; 4133759911cSjmatthew case PCI_PRODUCT_QLOGIC_ISP2432: 4143759911cSjmatthew sc->sc_isp_type = QLE_ISP2432; 4153759911cSjmatthew sc->sc_isp_gen = QLE_GEN_ISP24XX; 4163759911cSjmatthew break; 4173759911cSjmatthew case PCI_PRODUCT_QLOGIC_ISP2512: 4183759911cSjmatthew sc->sc_isp_type = QLE_ISP2512; 4193759911cSjmatthew sc->sc_isp_gen = QLE_GEN_ISP25XX; 4203759911cSjmatthew break; 4213759911cSjmatthew case PCI_PRODUCT_QLOGIC_ISP2522: 4223759911cSjmatthew sc->sc_isp_type = QLE_ISP2522; 4233759911cSjmatthew sc->sc_isp_gen = QLE_GEN_ISP25XX; 4243759911cSjmatthew break; 4253759911cSjmatthew case PCI_PRODUCT_QLOGIC_ISP2532: 4263759911cSjmatthew sc->sc_isp_type = QLE_ISP2532; 4273759911cSjmatthew sc->sc_isp_gen = QLE_GEN_ISP25XX; 4283759911cSjmatthew break; 4293759911cSjmatthew 4303759911cSjmatthew default: 4313759911cSjmatthew printf("unknown pci id %x", pa->pa_id); 4323759911cSjmatthew goto deintr; 4333759911cSjmatthew } 4343759911cSjmatthew 435c8005c8bSjmatthew /* these are the same for 24xx and 25xx but may vary later */ 436c8005c8bSjmatthew mbox_base = QLE_MBOX_BASE_24XX; 437c8005c8bSjmatthew firmware_addr = QLE_2400_CODE_ORG; 438c8005c8bSjmatthew 43969e488dfSdlg if (bus_space_subregion(sc->sc_iot, sc->sc_ioh, mbox_base, 44069e488dfSdlg sizeof(sc->sc_mbox), &sc->sc_mbox_ioh) != 0) { 44169e488dfSdlg printf("%s: unable to map mbox registers\n", DEVNAME(sc)); 44269e488dfSdlg goto deintr; 44369e488dfSdlg } 44469e488dfSdlg 4453759911cSjmatthew sc->sc_port = pa->pa_function; 4463759911cSjmatthew 4473759911cSjmatthew TAILQ_INIT(&sc->sc_ports); 4483759911cSjmatthew TAILQ_INIT(&sc->sc_ports_new); 4493759911cSjmatthew TAILQ_INIT(&sc->sc_ports_gone); 4503759911cSjmatthew TAILQ_INIT(&sc->sc_ports_found); 4513759911cSjmatthew 4523759911cSjmatthew /* after reset, mbox regs 1 and 2 contain the string "ISP " */ 4533759911cSjmatthew if (qle_read_mbox(sc, 1) != 0x4953 || 4543759911cSjmatthew qle_read_mbox(sc, 2) != 0x5020) { 4553759911cSjmatthew /* try releasing the risc processor */ 4563759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_RELEASE); 4573759911cSjmatthew } 4583759911cSjmatthew 4593759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_PAUSE); 4603759911cSjmatthew if (qle_softreset(sc) != 0) { 4613759911cSjmatthew printf("softreset failed\n"); 4623759911cSjmatthew goto deintr; 4633759911cSjmatthew } 4643759911cSjmatthew 4653759911cSjmatthew if (qle_read_nvram(sc) == 0) 4663759911cSjmatthew sc->sc_nvram_valid = 1; 4673759911cSjmatthew 468c8005c8bSjmatthew #ifdef QLE_NOFIRMWARE 469c8005c8bSjmatthew if (qle_verify_firmware(sc, firmware_addr)) { 470c8005c8bSjmatthew printf("%s: no firmware loaded\n", DEVNAME(sc)); 471c8005c8bSjmatthew goto deintr; 472c8005c8bSjmatthew } 473c8005c8bSjmatthew #else 474b803df0bSjmatthew switch (sc->sc_isp_gen) { 475b803df0bSjmatthew case QLE_GEN_ISP24XX: 476c8005c8bSjmatthew firmware = isp_2400_risc_code; 477b803df0bSjmatthew break; 478b803df0bSjmatthew case QLE_GEN_ISP25XX: 479c8005c8bSjmatthew firmware = isp_2500_risc_code; 480c8005c8bSjmatthew break; 481c8005c8bSjmatthew default: 482c8005c8bSjmatthew printf("%s: no firmware to load?\n", DEVNAME(sc)); 483b803df0bSjmatthew goto deintr; 484b803df0bSjmatthew } 485c8005c8bSjmatthew if (qle_load_firmware_chunks(sc, firmware)) { 486c8005c8bSjmatthew printf("%s: firmware load failed\n", DEVNAME(sc)); 487c8005c8bSjmatthew goto deintr; 488b803df0bSjmatthew } 489989131ebSjmatthew #endif 4903759911cSjmatthew 4913759911cSjmatthew /* execute firmware */ 4923759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_EXEC_FIRMWARE; 493c8005c8bSjmatthew sc->sc_mbox[1] = firmware_addr >> 16; 494c8005c8bSjmatthew sc->sc_mbox[2] = firmware_addr & 0xffff; 495c8005c8bSjmatthew #ifdef QLE_NOFIRMWARE 496989131ebSjmatthew sc->sc_mbox[3] = 1; 497989131ebSjmatthew #else 4983759911cSjmatthew sc->sc_mbox[3] = 0; 499989131ebSjmatthew #endif 500b803df0bSjmatthew sc->sc_mbox[4] = 0; 50165fd2507Sjmatthew if (qle_mbox(sc, 0x001f)) { 5023759911cSjmatthew printf("ISP couldn't exec firmware: %x\n", sc->sc_mbox[0]); 5033759911cSjmatthew goto deintr; 5043759911cSjmatthew } 5053759911cSjmatthew 5063759911cSjmatthew delay(250000); /* from isp(4) */ 5073759911cSjmatthew 5083759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_ABOUT_FIRMWARE; 50965fd2507Sjmatthew if (qle_mbox(sc, 0x0001)) { 5103759911cSjmatthew printf("ISP not talking after firmware exec: %x\n", 5113759911cSjmatthew sc->sc_mbox[0]); 5123759911cSjmatthew goto deintr; 5133759911cSjmatthew } 5140fb098c3Skettenis printf("%s: firmware rev %d.%d.%d, attrs 0x%x\n", DEVNAME(sc), 5150fb098c3Skettenis sc->sc_mbox[1], sc->sc_mbox[2], sc->sc_mbox[3], sc->sc_mbox[6]); 5163759911cSjmatthew 5173759911cSjmatthew sc->sc_maxcmds = 4096; 5183759911cSjmatthew 5193759911cSjmatthew /* reserve queue slots for markers and fabric ops */ 5203759911cSjmatthew sc->sc_maxcmds -= 2; 5213759911cSjmatthew 5223759911cSjmatthew if (qle_alloc_ccbs(sc)) { 5233759911cSjmatthew /* error already printed */ 5243759911cSjmatthew goto deintr; 5253759911cSjmatthew } 5263759911cSjmatthew sc->sc_scratch = qle_dmamem_alloc(sc, QLE_SCRATCH_SIZE); 5273759911cSjmatthew if (sc->sc_scratch == NULL) { 5283759911cSjmatthew printf("%s: unable to allocate scratch\n", DEVNAME(sc)); 5293759911cSjmatthew goto free_ccbs; 5303759911cSjmatthew } 5313759911cSjmatthew 5323759911cSjmatthew /* build init buffer thing */ 5333759911cSjmatthew icb = (struct qle_init_cb *)QLE_DMA_KVA(sc->sc_scratch); 5343759911cSjmatthew memset(icb, 0, sizeof(*icb)); 5353759911cSjmatthew icb->icb_version = QLE_ICB_VERSION; 5363759911cSjmatthew if (sc->sc_nvram_valid) { 5373759911cSjmatthew icb->icb_max_frame_len = sc->sc_nvram.frame_payload_size; 5383759911cSjmatthew icb->icb_exec_throttle = sc->sc_nvram.execution_throttle; 5393759911cSjmatthew icb->icb_hardaddr = sc->sc_nvram.hard_address; 5403759911cSjmatthew icb->icb_portname = sc->sc_nvram.port_name; 5413759911cSjmatthew icb->icb_nodename = sc->sc_nvram.node_name; 5423759911cSjmatthew icb->icb_login_retry = sc->sc_nvram.login_retry; 5433759911cSjmatthew icb->icb_login_timeout = sc->sc_nvram.login_timeout; 5443759911cSjmatthew icb->icb_fwoptions1 = sc->sc_nvram.fwoptions1; 5453759911cSjmatthew icb->icb_fwoptions2 = sc->sc_nvram.fwoptions2; 5463759911cSjmatthew icb->icb_fwoptions3 = sc->sc_nvram.fwoptions3; 5473759911cSjmatthew } else { 5483759911cSjmatthew /* defaults copied from isp(4) */ 54961ff084cSdlg htolem16(&icb->icb_max_frame_len, 1024); 55061ff084cSdlg htolem16(&icb->icb_exec_throttle, 16); 5513759911cSjmatthew icb->icb_portname = htobe64(QLE_DEFAULT_PORT_NAME); 5523759911cSjmatthew icb->icb_nodename = 0; 5533759911cSjmatthew icb->icb_login_retry = 3; 5543759911cSjmatthew 55561ff084cSdlg htolem32(&icb->icb_fwoptions1, QLE_ICB_FW1_FAIRNESS | 55661ff084cSdlg QLE_ICB_FW1_HARD_ADDR | QLE_ICB_FW1_FULL_DUPLEX); 55761ff084cSdlg htolem32(&icb->icb_fwoptions2, QLE_ICB_FW2_LOOP_PTP); 55861ff084cSdlg htolem32(&icb->icb_fwoptions3, QLE_ICB_FW3_FCP_RSP_24_0 | 5593759911cSjmatthew QLE_ICB_FW3_AUTONEG); 5603759911cSjmatthew } 5613759911cSjmatthew 5623759911cSjmatthew icb->icb_exchange_count = 0; 5633759911cSjmatthew 5643759911cSjmatthew icb->icb_req_out = 0; 5653759911cSjmatthew icb->icb_resp_in = 0; 5663759911cSjmatthew icb->icb_pri_req_out = 0; 56761ff084cSdlg htolem16(&icb->icb_req_queue_len, sc->sc_maxcmds); 56861ff084cSdlg htolem16(&icb->icb_resp_queue_len, sc->sc_maxcmds); 56961ff084cSdlg htolem16(&icb->icb_pri_req_queue_len, 8); /* apparently the minimum */ 5709802e30eSdlg htolem32(&icb->icb_req_queue_addr_lo, 5719802e30eSdlg QLE_DMA_DVA(sc->sc_requests)); 5729802e30eSdlg htolem32(&icb->icb_req_queue_addr_hi, 5739802e30eSdlg QLE_DMA_DVA(sc->sc_requests) >> 32); 5749802e30eSdlg htolem32(&icb->icb_resp_queue_addr_lo, 5759802e30eSdlg QLE_DMA_DVA(sc->sc_responses)); 5769802e30eSdlg htolem32(&icb->icb_resp_queue_addr_hi, 5779802e30eSdlg QLE_DMA_DVA(sc->sc_responses) >> 32); 5789802e30eSdlg htolem32(&icb->icb_pri_req_queue_addr_lo, 5799802e30eSdlg QLE_DMA_DVA(sc->sc_pri_requests)); 5809802e30eSdlg htolem32(&icb->icb_pri_req_queue_addr_hi, 5819802e30eSdlg QLE_DMA_DVA(sc->sc_pri_requests) >> 32); 5823759911cSjmatthew 5839802e30eSdlg htolem16(&icb->icb_link_down_nos, 200); 5843759911cSjmatthew icb->icb_int_delay = 0; 5853759911cSjmatthew icb->icb_login_timeout = 0; 5863759911cSjmatthew 5873759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_INIT_FIRMWARE; 5883759911cSjmatthew sc->sc_mbox[4] = 0; 5893759911cSjmatthew sc->sc_mbox[5] = 0; 5903759911cSjmatthew qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch); 5913759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 5923759911cSjmatthew sizeof(*icb), BUS_DMASYNC_PREWRITE); 59365fd2507Sjmatthew rv = qle_mbox(sc, 0x00fd); 5943759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 5953759911cSjmatthew sizeof(*icb), BUS_DMASYNC_POSTWRITE); 5963759911cSjmatthew 5973759911cSjmatthew if (rv != 0) { 5983759911cSjmatthew printf("%s: ISP firmware init failed: %x\n", DEVNAME(sc), 5993759911cSjmatthew sc->sc_mbox[0]); 6003759911cSjmatthew goto free_scratch; 6013759911cSjmatthew } 6023759911cSjmatthew 6033759911cSjmatthew /* enable some more notifications */ 6043759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_SET_FIRMWARE_OPTIONS; 6053759911cSjmatthew sc->sc_mbox[1] = QLE_FW_OPTION1_ASYNC_LIP_F8 | 6063759911cSjmatthew QLE_FW_OPTION1_ASYNC_LIP_RESET | 6073759911cSjmatthew QLE_FW_OPTION1_ASYNC_LIP_ERROR | 6083759911cSjmatthew QLE_FW_OPTION1_ASYNC_LOGIN_RJT; 6093759911cSjmatthew sc->sc_mbox[2] = 0; 6103759911cSjmatthew sc->sc_mbox[3] = 0; 61165fd2507Sjmatthew if (qle_mbox(sc, 0x000f)) { 6123759911cSjmatthew printf("%s: setting firmware options failed: %x\n", 6133759911cSjmatthew DEVNAME(sc), sc->sc_mbox[0]); 6143759911cSjmatthew goto free_scratch; 6153759911cSjmatthew } 6163759911cSjmatthew 61779ea9c08Sdlg sc->sc_update_taskq = taskq_create(DEVNAME(sc), 1, IPL_BIO, 0); 618e4195480Sdlg task_set(&sc->sc_update_task, qle_do_update, sc); 6193fc6fae1Sjmatthew timeout_set(&sc->sc_update_timeout, qle_deferred_update, sc); 6203759911cSjmatthew 6213759911cSjmatthew /* wait a bit for link to come up so we can scan and attach devices */ 62211e354acSjmatthew for (i = 0; i < QLE_WAIT_FOR_LOOP * 1000; i++) { 6233759911cSjmatthew u_int16_t isr, info; 6243759911cSjmatthew 6259382a847Sjmatthew if (sc->sc_loop_up) { 6269382a847Sjmatthew if (++loop_up == QLE_LOOP_SETTLE) 6273759911cSjmatthew break; 6289382a847Sjmatthew } else 6299382a847Sjmatthew loop_up = 0; 63011e354acSjmatthew 63111e354acSjmatthew delay(1000); 63211e354acSjmatthew 63311e354acSjmatthew if (qle_read_isr(sc, &isr, &info) == 0) 63411e354acSjmatthew continue; 63511e354acSjmatthew 63611e354acSjmatthew qle_handle_intr(sc, isr, info); 63711e354acSjmatthew 6383759911cSjmatthew } 6393759911cSjmatthew 6403759911cSjmatthew if (sc->sc_loop_up) { 641e4195480Sdlg qle_do_update(sc); 6423759911cSjmatthew } else { 643cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: loop still down, giving up\n", 644cc014dc0Sjmatthew DEVNAME(sc)); 6453759911cSjmatthew } 6463759911cSjmatthew 647ead808c4Skrw saa.saa_adapter = &qle_switch; 648ead808c4Skrw saa.saa_adapter_softc = sc; 649ead808c4Skrw saa.saa_adapter_target = SDEV_NO_ADAPTER_TARGET; 650ead808c4Skrw saa.saa_adapter_buswidth = QLE_MAX_TARGETS; 651ead808c4Skrw saa.saa_luns = 8; 652e5eae15dSkrw saa.saa_openings = sc->sc_maxcmds; 653e5eae15dSkrw saa.saa_pool = &sc->sc_iopool; 654e5eae15dSkrw if (sc->sc_nvram_valid) { 655e5eae15dSkrw saa.saa_wwpn = betoh64(sc->sc_nvram.port_name); 656e5eae15dSkrw saa.saa_wwnn = betoh64(sc->sc_nvram.node_name); 657e5eae15dSkrw } else { 658e5eae15dSkrw saa.saa_wwpn = QLE_DEFAULT_PORT_NAME; 659e5eae15dSkrw saa.saa_wwnn = 0; 660e5eae15dSkrw } 661e5eae15dSkrw if (saa.saa_wwnn == 0) { 662e5eae15dSkrw /* 663e5eae15dSkrw * mask out the port number from the port name to get 664e5eae15dSkrw * the node name. 665e5eae15dSkrw */ 666e5eae15dSkrw saa.saa_wwnn = saa.saa_wwpn; 667e5eae15dSkrw saa.saa_wwnn &= ~(0xfULL << 56); 668e5eae15dSkrw } 669e5eae15dSkrw saa.saa_quirks = saa.saa_flags = 0; 6703759911cSjmatthew 6713759911cSjmatthew sc->sc_scsibus = (struct scsibus_softc *)config_found(&sc->sc_dev, 6723759911cSjmatthew &saa, scsiprint); 6733759911cSjmatthew 6743759911cSjmatthew return; 6753759911cSjmatthew 6763759911cSjmatthew free_scratch: 6773759911cSjmatthew qle_dmamem_free(sc, sc->sc_scratch); 6783759911cSjmatthew free_ccbs: 6793759911cSjmatthew qle_free_ccbs(sc); 6803759911cSjmatthew deintr: 6813759911cSjmatthew pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 6823759911cSjmatthew sc->sc_ih = NULL; 6833759911cSjmatthew unmap: 6843759911cSjmatthew bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 6853759911cSjmatthew sc->sc_ios = 0; 6863759911cSjmatthew } 6873759911cSjmatthew 6883759911cSjmatthew int 6893759911cSjmatthew qle_detach(struct device *self, int flags) 6903759911cSjmatthew { 6913759911cSjmatthew struct qle_softc *sc = (struct qle_softc *)self; 6923759911cSjmatthew 6933759911cSjmatthew if (sc->sc_ih == NULL) { 694*9593dc34Smglocker /* we didn't attach properly, so nothing to detach */ 6953759911cSjmatthew return (0); 6963759911cSjmatthew } 6973759911cSjmatthew 6983759911cSjmatthew pci_intr_disestablish(sc->sc_pc, sc->sc_ih); 6993759911cSjmatthew sc->sc_ih = NULL; 7003759911cSjmatthew 7013759911cSjmatthew bus_space_unmap(sc->sc_iot, sc->sc_ioh, sc->sc_ios); 7023759911cSjmatthew sc->sc_ios = 0; 7033759911cSjmatthew 7043759911cSjmatthew return (0); 7053759911cSjmatthew } 7063759911cSjmatthew 7073759911cSjmatthew int 7083759911cSjmatthew qle_classify_port(struct qle_softc *sc, u_int32_t location, 7093759911cSjmatthew u_int64_t port_name, u_int64_t node_name, struct qle_fc_port **prev) 7103759911cSjmatthew { 7113759911cSjmatthew struct qle_fc_port *port, *locmatch, *wwnmatch; 7123759911cSjmatthew locmatch = NULL; 7133759911cSjmatthew wwnmatch = NULL; 7143759911cSjmatthew 7153759911cSjmatthew /* make sure we don't try to add a port or location twice */ 7163759911cSjmatthew TAILQ_FOREACH(port, &sc->sc_ports_new, update) { 7173759911cSjmatthew if ((port->port_name == port_name && 7183759911cSjmatthew port->node_name == node_name) || 7193759911cSjmatthew port->location == location) { 7203759911cSjmatthew *prev = port; 7213759911cSjmatthew return (QLE_PORT_DISP_DUP); 7223759911cSjmatthew } 7233759911cSjmatthew } 7243759911cSjmatthew 7253759911cSjmatthew /* if we're attaching, everything is new */ 7263759911cSjmatthew if (sc->sc_scsibus == NULL) { 7273759911cSjmatthew *prev = NULL; 7283759911cSjmatthew return (QLE_PORT_DISP_NEW); 7293759911cSjmatthew } 7303759911cSjmatthew 731580dbe8fSjmatthew TAILQ_FOREACH(port, &sc->sc_ports, ports) { 7323759911cSjmatthew if (port->location == location) 7333759911cSjmatthew locmatch = port; 7343759911cSjmatthew 7353759911cSjmatthew if (port->port_name == port_name && 7363759911cSjmatthew port->node_name == node_name) 7373759911cSjmatthew wwnmatch = port; 7383759911cSjmatthew } 7393759911cSjmatthew 7403759911cSjmatthew if (locmatch == NULL && wwnmatch == NULL) { 7413759911cSjmatthew *prev = NULL; 7423759911cSjmatthew return (QLE_PORT_DISP_NEW); 7433759911cSjmatthew } else if (locmatch == wwnmatch) { 7443759911cSjmatthew *prev = locmatch; 7453759911cSjmatthew return (QLE_PORT_DISP_SAME); 7463759911cSjmatthew } else if (wwnmatch != NULL) { 7473759911cSjmatthew *prev = wwnmatch; 7483759911cSjmatthew return (QLE_PORT_DISP_MOVED); 7493759911cSjmatthew } else { 7503759911cSjmatthew *prev = locmatch; 7513759911cSjmatthew return (QLE_PORT_DISP_CHANGED); 7523759911cSjmatthew } 7533759911cSjmatthew } 7543759911cSjmatthew 7553759911cSjmatthew int 7560d6eccfaSjmatthew qle_get_loop_id(struct qle_softc *sc, int start) 7573759911cSjmatthew { 7583759911cSjmatthew int i, last; 7593759911cSjmatthew 7603759911cSjmatthew i = QLE_MIN_HANDLE; 7613759911cSjmatthew last = QLE_MAX_HANDLE; 7620d6eccfaSjmatthew if (i < start) 7630d6eccfaSjmatthew i = start; 7640d6eccfaSjmatthew 7653759911cSjmatthew for (; i <= last; i++) { 7663759911cSjmatthew if (sc->sc_targets[i] == NULL) 7673759911cSjmatthew return (i); 7683759911cSjmatthew } 7693759911cSjmatthew 7703759911cSjmatthew return (-1); 7713759911cSjmatthew } 7723759911cSjmatthew 7733759911cSjmatthew int 7743759911cSjmatthew qle_get_port_db(struct qle_softc *sc, u_int16_t loopid, struct qle_dmamem *mem) 7753759911cSjmatthew { 7763759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_GET_PORT_DB; 7773759911cSjmatthew sc->sc_mbox[1] = loopid; 7783759911cSjmatthew qle_mbox_putaddr(sc->sc_mbox, mem); 7793759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, 7803759911cSjmatthew sizeof(struct qle_get_port_db), BUS_DMASYNC_PREREAD); 78165fd2507Sjmatthew if (qle_mbox(sc, 0x00cf)) { 782871bea04Sjmatthew DPRINTF(QLE_D_PORT, "%s: get port db for %d failed: %x\n", 7833759911cSjmatthew DEVNAME(sc), loopid, sc->sc_mbox[0]); 7843759911cSjmatthew return (1); 7853759911cSjmatthew } 7863759911cSjmatthew 7873759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, 7883759911cSjmatthew sizeof(struct qle_get_port_db), BUS_DMASYNC_POSTREAD); 7893759911cSjmatthew return (0); 7903759911cSjmatthew } 7913759911cSjmatthew 7923759911cSjmatthew int 793580dbe8fSjmatthew qle_get_port_name_list(struct qle_softc *sc, u_int32_t match) 794580dbe8fSjmatthew { 795580dbe8fSjmatthew struct qle_port_name_list *l; 796580dbe8fSjmatthew struct qle_fc_port *port; 797580dbe8fSjmatthew int i; 798580dbe8fSjmatthew 799580dbe8fSjmatthew sc->sc_mbox[0] = QLE_MBOX_GET_PORT_NAME_LIST; 800580dbe8fSjmatthew sc->sc_mbox[1] = 0; 801580dbe8fSjmatthew sc->sc_mbox[8] = QLE_DMA_LEN(sc->sc_scratch); 802580dbe8fSjmatthew sc->sc_mbox[9] = 0; 803580dbe8fSjmatthew qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch); 804580dbe8fSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 805580dbe8fSjmatthew QLE_DMA_LEN(sc->sc_scratch), BUS_DMASYNC_PREREAD); 806580dbe8fSjmatthew if (qle_mbox(sc, 0x03cf)) { 807580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: get port name list failed: %x\n", 808580dbe8fSjmatthew DEVNAME(sc), sc->sc_mbox[0]); 809580dbe8fSjmatthew return (1); 810580dbe8fSjmatthew } 811580dbe8fSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 812580dbe8fSjmatthew sc->sc_mbox[1], BUS_DMASYNC_POSTREAD); 813580dbe8fSjmatthew 814580dbe8fSjmatthew i = 0; 815580dbe8fSjmatthew l = QLE_DMA_KVA(sc->sc_scratch); 816580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 817580dbe8fSjmatthew while (i * sizeof(*l) < sc->sc_mbox[1]) { 818580dbe8fSjmatthew u_int16_t loopid; 819580dbe8fSjmatthew u_int32_t loc; 820580dbe8fSjmatthew 821580dbe8fSjmatthew loopid = lemtoh16(&l[i].loopid) & 0xfff; 822580dbe8fSjmatthew /* skip special ports */ 823580dbe8fSjmatthew switch (loopid) { 824580dbe8fSjmatthew case QLE_F_PORT_HANDLE: 825580dbe8fSjmatthew case QLE_SNS_HANDLE: 826580dbe8fSjmatthew case QLE_FABRIC_CTRL_HANDLE: 827580dbe8fSjmatthew case QLE_IP_BCAST_HANDLE: 828580dbe8fSjmatthew loc = 0; 829580dbe8fSjmatthew break; 830580dbe8fSjmatthew default: 831580dbe8fSjmatthew if (loopid <= sc->sc_loop_max_id) { 832580dbe8fSjmatthew loc = QLE_LOCATION_LOOP_ID(loopid); 833580dbe8fSjmatthew } else { 834580dbe8fSjmatthew /* 835580dbe8fSjmatthew * we don't have the port id here, so just 836580dbe8fSjmatthew * indicate it's a fabric port. 837580dbe8fSjmatthew */ 838580dbe8fSjmatthew loc = QLE_LOCATION_FABRIC; 839580dbe8fSjmatthew } 840580dbe8fSjmatthew break; 841580dbe8fSjmatthew } 842580dbe8fSjmatthew 843580dbe8fSjmatthew if (match & loc) { 844580dbe8fSjmatthew port = malloc(sizeof(*port), M_DEVBUF, M_ZERO | 845580dbe8fSjmatthew M_NOWAIT); 846580dbe8fSjmatthew if (port == NULL) { 847580dbe8fSjmatthew printf("%s: failed to allocate port struct\n", 848580dbe8fSjmatthew DEVNAME(sc)); 849580dbe8fSjmatthew break; 850580dbe8fSjmatthew } 851580dbe8fSjmatthew port->location = loc; 852580dbe8fSjmatthew port->loopid = loopid; 853580dbe8fSjmatthew port->port_name = letoh64(l[i].port_name); 854580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: loop id %d, port name %llx\n", 855580dbe8fSjmatthew DEVNAME(sc), port->loopid, port->port_name); 856580dbe8fSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports_found, port, update); 857580dbe8fSjmatthew } 858580dbe8fSjmatthew i++; 859580dbe8fSjmatthew } 860580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 861580dbe8fSjmatthew 862580dbe8fSjmatthew return (0); 863580dbe8fSjmatthew } 864580dbe8fSjmatthew 865580dbe8fSjmatthew int 866580dbe8fSjmatthew qle_add_loop_port(struct qle_softc *sc, struct qle_fc_port *port) 8673759911cSjmatthew { 8683759911cSjmatthew struct qle_get_port_db *pdb; 869580dbe8fSjmatthew struct qle_fc_port *pport; 8703759911cSjmatthew int disp; 8713759911cSjmatthew 872580dbe8fSjmatthew if (qle_get_port_db(sc, port->loopid, sc->sc_scratch) != 0) { 8733759911cSjmatthew return (1); 8743759911cSjmatthew } 8753759911cSjmatthew pdb = QLE_DMA_KVA(sc->sc_scratch); 8763759911cSjmatthew 87761ff084cSdlg if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE) 8783759911cSjmatthew port->flags |= QLE_PORT_FLAG_IS_TARGET; 8793759911cSjmatthew 8803759911cSjmatthew port->port_name = betoh64(pdb->port_name); 8813759911cSjmatthew port->node_name = betoh64(pdb->node_name); 8823759911cSjmatthew port->portid = (pdb->port_id[0] << 16) | (pdb->port_id[1] << 8) | 8833759911cSjmatthew pdb->port_id[2]; 8843759911cSjmatthew 8853759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 8863759911cSjmatthew disp = qle_classify_port(sc, port->location, port->port_name, 8873759911cSjmatthew port->node_name, &pport); 8883759911cSjmatthew switch (disp) { 8893759911cSjmatthew case QLE_PORT_DISP_CHANGED: 8903759911cSjmatthew case QLE_PORT_DISP_MOVED: 8913759911cSjmatthew case QLE_PORT_DISP_NEW: 8923759911cSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update); 893580dbe8fSjmatthew sc->sc_targets[port->loopid] = port; 8943759911cSjmatthew break; 8953759911cSjmatthew case QLE_PORT_DISP_DUP: 896c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 8973759911cSjmatthew break; 8983759911cSjmatthew case QLE_PORT_DISP_SAME: 8993759911cSjmatthew TAILQ_REMOVE(&sc->sc_ports_gone, pport, update); 900c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 9013759911cSjmatthew break; 9023759911cSjmatthew } 9033759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 9043759911cSjmatthew 9053759911cSjmatthew switch (disp) { 9063759911cSjmatthew case QLE_PORT_DISP_CHANGED: 9073759911cSjmatthew case QLE_PORT_DISP_MOVED: 9083759911cSjmatthew case QLE_PORT_DISP_NEW: 909cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: %s %d; name %llx\n", 9103759911cSjmatthew DEVNAME(sc), ISSET(port->flags, QLE_PORT_FLAG_IS_TARGET) ? 911580dbe8fSjmatthew "target" : "non-target", port->loopid, 912580dbe8fSjmatthew betoh64(pdb->port_name)); 9133759911cSjmatthew break; 9143759911cSjmatthew default: 9153759911cSjmatthew break; 9163759911cSjmatthew } 9173759911cSjmatthew return (0); 9183759911cSjmatthew } 9193759911cSjmatthew 9203759911cSjmatthew int 9213759911cSjmatthew qle_add_fabric_port(struct qle_softc *sc, struct qle_fc_port *port) 9223759911cSjmatthew { 9233759911cSjmatthew struct qle_get_port_db *pdb; 9243759911cSjmatthew 9253759911cSjmatthew if (qle_get_port_db(sc, port->loopid, sc->sc_scratch) != 0) { 926c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 9273759911cSjmatthew return (1); 9283759911cSjmatthew } 9293759911cSjmatthew pdb = QLE_DMA_KVA(sc->sc_scratch); 9303759911cSjmatthew 93161ff084cSdlg if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE) 9323759911cSjmatthew port->flags |= QLE_PORT_FLAG_IS_TARGET; 9333759911cSjmatthew 934580dbe8fSjmatthew /* 935580dbe8fSjmatthew * if we only know about this port because qle_get_port_name_list 936580dbe8fSjmatthew * returned it, we don't have its port id or node name, so fill 937580dbe8fSjmatthew * those in and update its location. 938580dbe8fSjmatthew */ 939580dbe8fSjmatthew if (port->location == QLE_LOCATION_FABRIC) { 940580dbe8fSjmatthew port->node_name = betoh64(pdb->node_name); 9410d6eccfaSjmatthew port->port_name = betoh64(pdb->port_name); 942580dbe8fSjmatthew port->portid = (pdb->port_id[0] << 16) | 943580dbe8fSjmatthew (pdb->port_id[1] << 8) | pdb->port_id[2]; 944580dbe8fSjmatthew port->location = QLE_LOCATION_PORT_ID(port->portid); 945580dbe8fSjmatthew } 9463759911cSjmatthew 9473759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 9483759911cSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports_new, port, update); 9493759911cSjmatthew sc->sc_targets[port->loopid] = port; 9503759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 9513759911cSjmatthew 952cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: %s %d; name %llx\n", 9533759911cSjmatthew DEVNAME(sc), ISSET(port->flags, QLE_PORT_FLAG_IS_TARGET) ? 9543759911cSjmatthew "target" : "non-target", port->loopid, port->port_name); 9553759911cSjmatthew return (0); 9563759911cSjmatthew } 9573759911cSjmatthew 958b9154820Sjmatthew int 9590d6eccfaSjmatthew qle_add_logged_in_port(struct qle_softc *sc, u_int16_t loopid, 960b9154820Sjmatthew u_int32_t portid) 961b9154820Sjmatthew { 962b9154820Sjmatthew struct qle_fc_port *port; 9630d6eccfaSjmatthew struct qle_get_port_db *pdb; 9640d6eccfaSjmatthew u_int64_t node_name, port_name; 9650d6eccfaSjmatthew int flags, ret; 9660d6eccfaSjmatthew 9670d6eccfaSjmatthew ret = qle_get_port_db(sc, loopid, sc->sc_scratch); 9680d6eccfaSjmatthew mtx_enter(&sc->sc_port_mtx); 9690d6eccfaSjmatthew if (ret != 0) { 9700d6eccfaSjmatthew /* put in a fake port to prevent use of this loop id */ 9710d6eccfaSjmatthew printf("%s: loop id %d used, but can't see what's using it\n", 9720d6eccfaSjmatthew DEVNAME(sc), loopid); 9730d6eccfaSjmatthew node_name = 0; 9740d6eccfaSjmatthew port_name = 0; 9750d6eccfaSjmatthew flags = 0; 9760d6eccfaSjmatthew } else { 9770d6eccfaSjmatthew pdb = QLE_DMA_KVA(sc->sc_scratch); 9780d6eccfaSjmatthew node_name = betoh64(pdb->node_name); 9790d6eccfaSjmatthew port_name = betoh64(pdb->port_name); 9800d6eccfaSjmatthew flags = 0; 9810d6eccfaSjmatthew if (lemtoh16(&pdb->prli_svc_word3) & QLE_SVC3_TARGET_ROLE) 9820d6eccfaSjmatthew flags |= QLE_PORT_FLAG_IS_TARGET; 9830d6eccfaSjmatthew 9840d6eccfaSjmatthew /* see if we've already found this port */ 9850d6eccfaSjmatthew TAILQ_FOREACH(port, &sc->sc_ports_found, update) { 9860d6eccfaSjmatthew if ((port->node_name == node_name) && 9870d6eccfaSjmatthew (port->port_name == port_name) && 9880d6eccfaSjmatthew (port->portid == portid)) { 9890d6eccfaSjmatthew mtx_leave(&sc->sc_port_mtx); 9900d6eccfaSjmatthew DPRINTF(QLE_D_PORT, "%s: already found port " 9910d6eccfaSjmatthew "%06x\n", DEVNAME(sc), portid); 9920d6eccfaSjmatthew return (0); 9930d6eccfaSjmatthew } 9940d6eccfaSjmatthew } 9950d6eccfaSjmatthew } 996b9154820Sjmatthew 997b9154820Sjmatthew port = malloc(sizeof(*port), M_DEVBUF, M_ZERO | M_NOWAIT); 998b9154820Sjmatthew if (port == NULL) { 9990d6eccfaSjmatthew mtx_leave(&sc->sc_port_mtx); 1000b9154820Sjmatthew printf("%s: failed to allocate a port structure\n", 1001b9154820Sjmatthew DEVNAME(sc)); 1002b9154820Sjmatthew return (1); 1003b9154820Sjmatthew } 1004b9154820Sjmatthew port->location = QLE_LOCATION_PORT_ID(portid); 10050d6eccfaSjmatthew port->port_name = port_name; 10060d6eccfaSjmatthew port->node_name = node_name; 1007b9154820Sjmatthew port->loopid = loopid; 1008b9154820Sjmatthew port->portid = portid; 10090d6eccfaSjmatthew port->flags = flags; 1010b9154820Sjmatthew 1011b9154820Sjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports); 1012b9154820Sjmatthew sc->sc_targets[port->loopid] = port; 1013b9154820Sjmatthew mtx_leave(&sc->sc_port_mtx); 1014b9154820Sjmatthew 10150d6eccfaSjmatthew DPRINTF(QLE_D_PORT, "%s: added logged in port %06x at %d\n", 1016b9154820Sjmatthew DEVNAME(sc), portid, loopid); 1017b9154820Sjmatthew return (0); 1018b9154820Sjmatthew } 1019b9154820Sjmatthew 10203759911cSjmatthew struct qle_ccb * 1021ce28b8eaSdlg qle_handle_resp(struct qle_softc *sc, u_int32_t id) 10223759911cSjmatthew { 10233759911cSjmatthew struct qle_ccb *ccb; 10243759911cSjmatthew struct qle_iocb_status *status; 10253759911cSjmatthew struct qle_iocb_req6 *req; 10263759911cSjmatthew struct scsi_xfer *xs; 10273759911cSjmatthew u_int32_t handle; 10283759911cSjmatthew u_int16_t completion; 10293759911cSjmatthew u_int8_t *entry; 10303759911cSjmatthew u_int8_t *data; 10313759911cSjmatthew 10323759911cSjmatthew ccb = NULL; 10333759911cSjmatthew entry = QLE_DMA_KVA(sc->sc_responses) + (id * QLE_QUEUE_ENTRY_SIZE); 10343759911cSjmatthew 10353759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, 10363759911cSjmatthew QLE_DMA_MAP(sc->sc_responses), id * QLE_QUEUE_ENTRY_SIZE, 10373759911cSjmatthew QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTREAD); 10383759911cSjmatthew 1039cc014dc0Sjmatthew qle_dump_iocb(sc, entry); 10403759911cSjmatthew switch(entry[0]) { 10413759911cSjmatthew case QLE_IOCB_STATUS: 10423759911cSjmatthew status = (struct qle_iocb_status *)entry; 10433759911cSjmatthew handle = status->handle; 10443759911cSjmatthew if (handle > sc->sc_maxcmds) { 10453759911cSjmatthew panic("bad completed command handle: %d (> %d)", 10463759911cSjmatthew handle, sc->sc_maxcmds); 10473759911cSjmatthew } 10483759911cSjmatthew 10493759911cSjmatthew ccb = &sc->sc_ccbs[handle]; 10503759911cSjmatthew xs = ccb->ccb_xs; 10513759911cSjmatthew if (xs == NULL) { 1052cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: got status for inactive ccb %d\n", 10533759911cSjmatthew DEVNAME(sc), handle); 10543759911cSjmatthew ccb = NULL; 10553759911cSjmatthew break; 10563759911cSjmatthew } 10573759911cSjmatthew if (xs->io != ccb) { 10583759911cSjmatthew panic("completed command handle doesn't match xs " 10593759911cSjmatthew "(handle %d, ccb %p, xs->io %p)", handle, ccb, 10603759911cSjmatthew xs->io); 10613759911cSjmatthew } 10623759911cSjmatthew 10633759911cSjmatthew if (xs->datalen > 0) { 10643759911cSjmatthew if (ccb->ccb_dmamap->dm_nsegs > 10653759911cSjmatthew QLE_IOCB_SEGS_PER_CMD) { 10663759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, 10673759911cSjmatthew QLE_DMA_MAP(sc->sc_segments), 10683759911cSjmatthew ccb->ccb_seg_offset, 10693759911cSjmatthew sizeof(*ccb->ccb_segs) * 107020c4379eSjmatthew ccb->ccb_dmamap->dm_nsegs + 1, 10713759911cSjmatthew BUS_DMASYNC_POSTWRITE); 10723759911cSjmatthew } 10733759911cSjmatthew 10743759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, ccb->ccb_dmamap, 0, 10753759911cSjmatthew ccb->ccb_dmamap->dm_mapsize, 10763759911cSjmatthew (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_POSTREAD : 10773759911cSjmatthew BUS_DMASYNC_POSTWRITE); 10783759911cSjmatthew bus_dmamap_unload(sc->sc_dmat, ccb->ccb_dmamap); 10793759911cSjmatthew } 10803759911cSjmatthew 108120c4379eSjmatthew xs->status = lemtoh16(&status->scsi_status) & 0xff; 108220c4379eSjmatthew xs->resid = 0; 108361ff084cSdlg completion = lemtoh16(&status->completion); 10843759911cSjmatthew switch (completion) { 10853759911cSjmatthew case QLE_IOCB_STATUS_DATA_UNDERRUN: 108661ff084cSdlg xs->resid = lemtoh32(&status->resid); 108772c1becbSjmatthew /* FALLTHROUGH */ 108820c4379eSjmatthew case QLE_IOCB_STATUS_DATA_OVERRUN: 108920c4379eSjmatthew case QLE_IOCB_STATUS_COMPLETE: 109061ff084cSdlg if (lemtoh16(&status->scsi_status) & 10913759911cSjmatthew QLE_SCSI_STATUS_SENSE_VALID) { 10923759911cSjmatthew u_int32_t *pp; 10933759911cSjmatthew int sr; 10943759911cSjmatthew data = status->data + 109561ff084cSdlg lemtoh32(&status->fcp_rsp_len); 1096ecca1b20Sjmatthew sr = MIN(lemtoh32(&status->fcp_sense_len), 1097ecca1b20Sjmatthew sizeof(xs->sense)); 1098ecca1b20Sjmatthew memcpy(&xs->sense, data, sr); 10993759911cSjmatthew xs->error = XS_SENSE; 11003759911cSjmatthew pp = (u_int32_t *)&xs->sense; 11013759911cSjmatthew for (sr = 0; sr < sizeof(xs->sense)/4; sr++) { 11023759911cSjmatthew pp[sr] = swap32(pp[sr]); 11033759911cSjmatthew } 11043759911cSjmatthew } else { 11053759911cSjmatthew xs->error = XS_NOERROR; 11063759911cSjmatthew } 11073759911cSjmatthew break; 11083759911cSjmatthew 11093759911cSjmatthew case QLE_IOCB_STATUS_DMA_ERROR: 1110cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: dma error\n", DEVNAME(sc)); 11113759911cSjmatthew /* set resid apparently? */ 11123759911cSjmatthew break; 11133759911cSjmatthew 11143759911cSjmatthew case QLE_IOCB_STATUS_RESET: 1115cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: reset destroyed command\n", 1116cc014dc0Sjmatthew DEVNAME(sc)); 11173759911cSjmatthew sc->sc_marker_required = 1; 11183759911cSjmatthew xs->error = XS_RESET; 11193759911cSjmatthew break; 11203759911cSjmatthew 11213759911cSjmatthew case QLE_IOCB_STATUS_ABORTED: 1122cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: aborted\n", DEVNAME(sc)); 11233759911cSjmatthew sc->sc_marker_required = 1; 11243759911cSjmatthew xs->error = XS_DRIVER_STUFFUP; 11253759911cSjmatthew break; 11263759911cSjmatthew 11273759911cSjmatthew case QLE_IOCB_STATUS_TIMEOUT: 1128cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: command timed out\n", 1129cc014dc0Sjmatthew DEVNAME(sc)); 11303759911cSjmatthew xs->error = XS_TIMEOUT; 11313759911cSjmatthew break; 11323759911cSjmatthew 11333759911cSjmatthew case QLE_IOCB_STATUS_QUEUE_FULL: 1134cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: queue full\n", DEVNAME(sc)); 11353759911cSjmatthew xs->error = XS_BUSY; 11363759911cSjmatthew break; 11373759911cSjmatthew 11383759911cSjmatthew case QLE_IOCB_STATUS_PORT_UNAVAIL: 11393759911cSjmatthew case QLE_IOCB_STATUS_PORT_LOGGED_OUT: 11403759911cSjmatthew case QLE_IOCB_STATUS_PORT_CHANGED: 1141cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: dev gone\n", DEVNAME(sc)); 11423759911cSjmatthew xs->error = XS_SELTIMEOUT; 1143580dbe8fSjmatthew /* mark port as needing relogin? */ 11443759911cSjmatthew break; 11453759911cSjmatthew 11463759911cSjmatthew default: 1147cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: unexpected completion status " 1148cc014dc0Sjmatthew "%x\n", DEVNAME(sc), status->completion); 11493759911cSjmatthew xs->error = XS_DRIVER_STUFFUP; 11503759911cSjmatthew break; 11513759911cSjmatthew } 11523759911cSjmatthew break; 11533759911cSjmatthew 11543759911cSjmatthew case QLE_IOCB_STATUS_CONT: 1155cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: ignoring status continuation iocb\n", 11563759911cSjmatthew DEVNAME(sc)); 11573759911cSjmatthew break; 11583759911cSjmatthew 11593759911cSjmatthew case QLE_IOCB_PLOGX: 11603759911cSjmatthew case QLE_IOCB_CT_PASSTHROUGH: 11613759911cSjmatthew if (sc->sc_fabric_pending) { 1162cc014dc0Sjmatthew qle_dump_iocb(sc, entry); 1163e5d3c726Sjmatthew memcpy(sc->sc_fabric_response, entry, 1164e5d3c726Sjmatthew QLE_QUEUE_ENTRY_SIZE); 11653759911cSjmatthew sc->sc_fabric_pending = 2; 11663759911cSjmatthew wakeup(sc->sc_scratch); 11673759911cSjmatthew } else { 1168cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: unexpected fabric response %x\n", 11693759911cSjmatthew DEVNAME(sc), entry[0]); 11703759911cSjmatthew } 11713759911cSjmatthew break; 11723759911cSjmatthew 11733759911cSjmatthew case QLE_IOCB_MARKER: 11743759911cSjmatthew break; 11753759911cSjmatthew 11763759911cSjmatthew case QLE_IOCB_CMD_TYPE_6: 11773759911cSjmatthew case QLE_IOCB_CMD_TYPE_7: 1178cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: request bounced back\n", DEVNAME(sc)); 11793759911cSjmatthew req = (struct qle_iocb_req6 *)entry; 11803759911cSjmatthew handle = req->req_handle; 11813759911cSjmatthew if (handle > sc->sc_maxcmds) { 11823759911cSjmatthew panic("bad bounced command handle: %d (> %d)", 11833759911cSjmatthew handle, sc->sc_maxcmds); 11843759911cSjmatthew } 11853759911cSjmatthew 11863759911cSjmatthew ccb = &sc->sc_ccbs[handle]; 11873759911cSjmatthew xs = ccb->ccb_xs; 11883759911cSjmatthew xs->error = XS_DRIVER_STUFFUP; 11893759911cSjmatthew break; 11903759911cSjmatthew default: 1191cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: unexpected response entry type %x\n", 11923759911cSjmatthew DEVNAME(sc), entry[0]); 11933759911cSjmatthew break; 11943759911cSjmatthew } 11953759911cSjmatthew 11963759911cSjmatthew return (ccb); 11973759911cSjmatthew } 11983759911cSjmatthew 11993759911cSjmatthew void 12003759911cSjmatthew qle_handle_intr(struct qle_softc *sc, u_int16_t isr, u_int16_t info) 12013759911cSjmatthew { 12023759911cSjmatthew int i; 1203ce28b8eaSdlg u_int32_t rspin; 12043759911cSjmatthew struct qle_ccb *ccb; 12053759911cSjmatthew 12063759911cSjmatthew switch (isr) { 12073759911cSjmatthew case QLE_INT_TYPE_ASYNC: 12083759911cSjmatthew qle_async(sc, info); 12093759911cSjmatthew break; 12103759911cSjmatthew 12113759911cSjmatthew case QLE_INT_TYPE_IO: 12123759911cSjmatthew rspin = qle_read(sc, QLE_RESP_IN); 1213fb48a412Sdlg if (rspin == sc->sc_last_resp_id) 1214fb48a412Sdlg break; 1215fb48a412Sdlg 1216fb48a412Sdlg do { 12173759911cSjmatthew ccb = qle_handle_resp(sc, sc->sc_last_resp_id); 12183759911cSjmatthew if (ccb) 12193759911cSjmatthew scsi_done(ccb->ccb_xs); 12203759911cSjmatthew 12213759911cSjmatthew sc->sc_last_resp_id++; 1222fb48a412Sdlg sc->sc_last_resp_id %= sc->sc_maxcmds; 1223fb48a412Sdlg } while (sc->sc_last_resp_id != rspin); 12243759911cSjmatthew 12253759911cSjmatthew qle_write(sc, QLE_RESP_OUT, sc->sc_last_resp_id); 12263759911cSjmatthew break; 12273759911cSjmatthew 12283759911cSjmatthew case QLE_INT_TYPE_MBOX: 12293759911cSjmatthew mtx_enter(&sc->sc_mbox_mtx); 12303759911cSjmatthew if (sc->sc_mbox_pending) { 123165fd2507Sjmatthew for (i = 0; i < nitems(sc->sc_mbox); i++) { 12323759911cSjmatthew sc->sc_mbox[i] = qle_read_mbox(sc, i); 12333759911cSjmatthew } 12343759911cSjmatthew sc->sc_mbox_pending = 2; 12353759911cSjmatthew wakeup(sc->sc_mbox); 12363759911cSjmatthew mtx_leave(&sc->sc_mbox_mtx); 12373759911cSjmatthew } else { 12383759911cSjmatthew mtx_leave(&sc->sc_mbox_mtx); 1239cc014dc0Sjmatthew DPRINTF(QLE_D_INTR, "%s: unexpected mbox interrupt: " 1240cc014dc0Sjmatthew "%x\n", DEVNAME(sc), info); 12413759911cSjmatthew } 12423759911cSjmatthew break; 12433759911cSjmatthew 12443759911cSjmatthew default: 12453759911cSjmatthew break; 12463759911cSjmatthew } 12473759911cSjmatthew 12483759911cSjmatthew qle_clear_isr(sc, isr); 12493759911cSjmatthew } 12503759911cSjmatthew 12513759911cSjmatthew int 12523759911cSjmatthew qle_intr(void *xsc) 12533759911cSjmatthew { 12543759911cSjmatthew struct qle_softc *sc = xsc; 12553759911cSjmatthew u_int16_t isr; 12563759911cSjmatthew u_int16_t info; 12573759911cSjmatthew 12583759911cSjmatthew if (qle_read_isr(sc, &isr, &info) == 0) 12593759911cSjmatthew return (0); 12603759911cSjmatthew 12613759911cSjmatthew qle_handle_intr(sc, isr, info); 12623759911cSjmatthew return (1); 12633759911cSjmatthew } 12643759911cSjmatthew 12653759911cSjmatthew int 12663759911cSjmatthew qle_scsi_probe(struct scsi_link *link) 12673759911cSjmatthew { 12680b29cb40Skrw struct qle_softc *sc = link->bus->sb_adapter_softc; 12693759911cSjmatthew int rv = 0; 12703759911cSjmatthew 12713759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 12723759911cSjmatthew if (sc->sc_targets[link->target] == NULL) 12733759911cSjmatthew rv = ENXIO; 12743759911cSjmatthew else if (!ISSET(sc->sc_targets[link->target]->flags, 12753759911cSjmatthew QLE_PORT_FLAG_IS_TARGET)) 12763759911cSjmatthew rv = ENXIO; 12773759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 12783759911cSjmatthew 12793759911cSjmatthew return (rv); 12803759911cSjmatthew } 12813759911cSjmatthew 12823759911cSjmatthew void 12833759911cSjmatthew qle_scsi_cmd(struct scsi_xfer *xs) 12843759911cSjmatthew { 12853759911cSjmatthew struct scsi_link *link = xs->sc_link; 12860b29cb40Skrw struct qle_softc *sc = link->bus->sb_adapter_softc; 12873759911cSjmatthew struct qle_ccb *ccb; 12883759911cSjmatthew void *iocb; 12893759911cSjmatthew struct qle_ccb_list list; 12903759911cSjmatthew u_int16_t req; 12913759911cSjmatthew u_int32_t portid; 1292a1184b53Sjmatthew int offset, error, done; 12933759911cSjmatthew bus_dmamap_t dmap; 12943759911cSjmatthew 12953759911cSjmatthew if (xs->cmdlen > 16) { 1296cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: cmd too big (%d)\n", DEVNAME(sc), 1297cc014dc0Sjmatthew xs->cmdlen); 12983759911cSjmatthew memset(&xs->sense, 0, sizeof(xs->sense)); 12993759911cSjmatthew xs->sense.error_code = SSD_ERRCODE_VALID | SSD_ERRCODE_CURRENT; 13003759911cSjmatthew xs->sense.flags = SKEY_ILLEGAL_REQUEST; 13013759911cSjmatthew xs->sense.add_sense_code = 0x20; 13023759911cSjmatthew xs->error = XS_SENSE; 13033759911cSjmatthew scsi_done(xs); 13043759911cSjmatthew return; 13053759911cSjmatthew } 13063759911cSjmatthew 13073759911cSjmatthew portid = 0xffffffff; 13083759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 13093759911cSjmatthew if (sc->sc_targets[xs->sc_link->target] != NULL) { 13103759911cSjmatthew portid = sc->sc_targets[xs->sc_link->target]->portid; 13113759911cSjmatthew } 13123759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 13133759911cSjmatthew if (portid == 0xffffffff) { 13143759911cSjmatthew xs->error = XS_DRIVER_STUFFUP; 13153759911cSjmatthew scsi_done(xs); 13163759911cSjmatthew return; 13173759911cSjmatthew } 13183759911cSjmatthew 13193759911cSjmatthew ccb = xs->io; 13203759911cSjmatthew dmap = ccb->ccb_dmamap; 13213759911cSjmatthew if (xs->datalen > 0) { 13223759911cSjmatthew error = bus_dmamap_load(sc->sc_dmat, dmap, xs->data, 13233759911cSjmatthew xs->datalen, NULL, (xs->flags & SCSI_NOSLEEP) ? 13243759911cSjmatthew BUS_DMA_NOWAIT : BUS_DMA_WAITOK); 13253759911cSjmatthew if (error) { 13263759911cSjmatthew xs->error = XS_DRIVER_STUFFUP; 13273759911cSjmatthew scsi_done(xs); 13283759911cSjmatthew return; 13293759911cSjmatthew } 13303759911cSjmatthew 13313759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, dmap, 0, 13323759911cSjmatthew dmap->dm_mapsize, 13333759911cSjmatthew (xs->flags & SCSI_DATA_IN) ? BUS_DMASYNC_PREREAD : 13343759911cSjmatthew BUS_DMASYNC_PREWRITE); 13353759911cSjmatthew } 13363759911cSjmatthew 13373759911cSjmatthew mtx_enter(&sc->sc_queue_mtx); 13383759911cSjmatthew 13393759911cSjmatthew /* put in a sync marker if required */ 13403759911cSjmatthew if (sc->sc_marker_required) { 13413759911cSjmatthew req = sc->sc_next_req_id++; 13423759911cSjmatthew if (sc->sc_next_req_id == sc->sc_maxcmds) 13433759911cSjmatthew sc->sc_next_req_id = 0; 13443759911cSjmatthew 1345cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: writing marker at request %d\n", 1346cc014dc0Sjmatthew DEVNAME(sc), req); 13473759911cSjmatthew offset = (req * QLE_QUEUE_ENTRY_SIZE); 13483759911cSjmatthew iocb = QLE_DMA_KVA(sc->sc_requests) + offset; 13493759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), 13503759911cSjmatthew offset, QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE); 13513759911cSjmatthew qle_put_marker(sc, iocb); 13523759911cSjmatthew qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id); 13533759911cSjmatthew sc->sc_marker_required = 0; 13543759911cSjmatthew } 13553759911cSjmatthew 13563759911cSjmatthew req = sc->sc_next_req_id++; 13573759911cSjmatthew if (sc->sc_next_req_id == sc->sc_maxcmds) 13583759911cSjmatthew sc->sc_next_req_id = 0; 13593759911cSjmatthew 13603759911cSjmatthew offset = (req * QLE_QUEUE_ENTRY_SIZE); 13613759911cSjmatthew iocb = QLE_DMA_KVA(sc->sc_requests) + offset; 13623759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset, 13633759911cSjmatthew QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE); 13643759911cSjmatthew 13653759911cSjmatthew ccb->ccb_xs = xs; 13663759911cSjmatthew 13673759911cSjmatthew qle_put_cmd(sc, iocb, xs, ccb, portid); 13683759911cSjmatthew 13693759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset, 13703759911cSjmatthew QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_PREREAD); 13713759911cSjmatthew qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id); 13723759911cSjmatthew 13733759911cSjmatthew if (!ISSET(xs->flags, SCSI_POLL)) { 13743759911cSjmatthew mtx_leave(&sc->sc_queue_mtx); 13753759911cSjmatthew return; 13763759911cSjmatthew } 13773759911cSjmatthew 1378a1184b53Sjmatthew done = 0; 13793759911cSjmatthew SIMPLEQ_INIT(&list); 13803759911cSjmatthew do { 13813759911cSjmatthew u_int16_t isr, info; 1382a1184b53Sjmatthew u_int32_t rspin; 13833759911cSjmatthew delay(100); 13843759911cSjmatthew 13853759911cSjmatthew if (qle_read_isr(sc, &isr, &info) == 0) { 13863759911cSjmatthew continue; 13873759911cSjmatthew } 13883759911cSjmatthew 13893759911cSjmatthew if (isr != QLE_INT_TYPE_IO) { 13903759911cSjmatthew qle_handle_intr(sc, isr, info); 13913759911cSjmatthew continue; 13923759911cSjmatthew } 13933759911cSjmatthew 13943759911cSjmatthew rspin = qle_read(sc, QLE_RESP_IN); 1395a1184b53Sjmatthew while (rspin != sc->sc_last_resp_id) { 13963759911cSjmatthew ccb = qle_handle_resp(sc, sc->sc_last_resp_id); 13973759911cSjmatthew 13983759911cSjmatthew sc->sc_last_resp_id++; 13993759911cSjmatthew if (sc->sc_last_resp_id == sc->sc_maxcmds) 14003759911cSjmatthew sc->sc_last_resp_id = 0; 14013759911cSjmatthew 1402a1184b53Sjmatthew if (ccb != NULL) 1403a1184b53Sjmatthew SIMPLEQ_INSERT_TAIL(&list, ccb, ccb_link); 1404a1184b53Sjmatthew if (ccb == xs->io) 1405a1184b53Sjmatthew done = 1; 1406a1184b53Sjmatthew } 14073759911cSjmatthew qle_write(sc, QLE_RESP_OUT, sc->sc_last_resp_id); 14083759911cSjmatthew qle_clear_isr(sc, isr); 1409a1184b53Sjmatthew } while (done == 0); 14103759911cSjmatthew 1411a1184b53Sjmatthew mtx_leave(&sc->sc_queue_mtx); 1412a1184b53Sjmatthew 1413a1184b53Sjmatthew while ((ccb = SIMPLEQ_FIRST(&list)) != NULL) { 1414a1184b53Sjmatthew SIMPLEQ_REMOVE_HEAD(&list, ccb_link); 1415a1184b53Sjmatthew scsi_done(ccb->ccb_xs); 1416a1184b53Sjmatthew } 14173759911cSjmatthew } 14183759911cSjmatthew 14193759911cSjmatthew u_int32_t 14203759911cSjmatthew qle_read(struct qle_softc *sc, int offset) 14213759911cSjmatthew { 14223759911cSjmatthew u_int32_t v; 14233759911cSjmatthew v = bus_space_read_4(sc->sc_iot, sc->sc_ioh, offset); 14243759911cSjmatthew bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 4, 14253759911cSjmatthew BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 14263759911cSjmatthew return (v); 14273759911cSjmatthew } 14283759911cSjmatthew 14293759911cSjmatthew void 14303759911cSjmatthew qle_write(struct qle_softc *sc, int offset, u_int32_t value) 14313759911cSjmatthew { 14323759911cSjmatthew bus_space_write_4(sc->sc_iot, sc->sc_ioh, offset, value); 14333759911cSjmatthew bus_space_barrier(sc->sc_iot, sc->sc_ioh, offset, 4, 14343759911cSjmatthew BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 14353759911cSjmatthew } 14363759911cSjmatthew 14373759911cSjmatthew u_int16_t 14383759911cSjmatthew qle_read_mbox(struct qle_softc *sc, int mbox) 14393759911cSjmatthew { 14403759911cSjmatthew u_int16_t v; 144169e488dfSdlg bus_size_t offset = mbox * 2; 144269e488dfSdlg v = bus_space_read_2(sc->sc_iot, sc->sc_mbox_ioh, offset); 144369e488dfSdlg bus_space_barrier(sc->sc_iot, sc->sc_mbox_ioh, offset, 2, 14443759911cSjmatthew BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 14453759911cSjmatthew return (v); 14463759911cSjmatthew } 14473759911cSjmatthew 14483759911cSjmatthew void 14493759911cSjmatthew qle_write_mbox(struct qle_softc *sc, int mbox, u_int16_t value) 14503759911cSjmatthew { 145169e488dfSdlg bus_size_t offset = (mbox * 2); 145269e488dfSdlg bus_space_write_2(sc->sc_iot, sc->sc_mbox_ioh, offset, value); 145369e488dfSdlg bus_space_barrier(sc->sc_iot, sc->sc_mbox_ioh, offset, 2, 14543759911cSjmatthew BUS_SPACE_BARRIER_READ | BUS_SPACE_BARRIER_WRITE); 14553759911cSjmatthew } 14563759911cSjmatthew 14573759911cSjmatthew void 14583759911cSjmatthew qle_host_cmd(struct qle_softc *sc, u_int32_t cmd) 14593759911cSjmatthew { 14603759911cSjmatthew qle_write(sc, QLE_HOST_CMD_CTRL, cmd << QLE_HOST_CMD_SHIFT); 14613759911cSjmatthew } 14623759911cSjmatthew 14633759911cSjmatthew #define MBOX_COMMAND_TIMEOUT 400000 14643759911cSjmatthew 14653759911cSjmatthew int 146665fd2507Sjmatthew qle_mbox(struct qle_softc *sc, int maskin) 14673759911cSjmatthew { 14683759911cSjmatthew int i; 14693759911cSjmatthew int result = 0; 14703759911cSjmatthew int rv; 14713759911cSjmatthew 14723759911cSjmatthew for (i = 0; i < nitems(sc->sc_mbox); i++) { 14733759911cSjmatthew if (maskin & (1 << i)) { 14743759911cSjmatthew qle_write_mbox(sc, i, sc->sc_mbox[i]); 14753759911cSjmatthew } 14763759911cSjmatthew } 14773759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_SET_HOST_INT); 14783759911cSjmatthew 147965fd2507Sjmatthew if (sc->sc_scsibus != NULL) { 148065fd2507Sjmatthew mtx_enter(&sc->sc_mbox_mtx); 148165fd2507Sjmatthew sc->sc_mbox_pending = 1; 148265fd2507Sjmatthew while (sc->sc_mbox_pending == 1) { 1483541245b6Sjsg msleep_nsec(sc->sc_mbox, &sc->sc_mbox_mtx, PRIBIO, 1484541245b6Sjsg "qlembox", INFSLP); 148565fd2507Sjmatthew } 148665fd2507Sjmatthew result = sc->sc_mbox[0]; 148765fd2507Sjmatthew sc->sc_mbox_pending = 0; 148865fd2507Sjmatthew mtx_leave(&sc->sc_mbox_mtx); 148965fd2507Sjmatthew return (result == QLE_MBOX_COMPLETE ? 0 : result); 149065fd2507Sjmatthew } 149165fd2507Sjmatthew 14923759911cSjmatthew for (i = 0; i < MBOX_COMMAND_TIMEOUT && result == 0; i++) { 14933759911cSjmatthew u_int16_t isr, info; 14943759911cSjmatthew 14953759911cSjmatthew delay(100); 14963759911cSjmatthew 14973759911cSjmatthew if (qle_read_isr(sc, &isr, &info) == 0) 14983759911cSjmatthew continue; 14993759911cSjmatthew 15003759911cSjmatthew switch (isr) { 15013759911cSjmatthew case QLE_INT_TYPE_MBOX: 15023759911cSjmatthew result = info; 15033759911cSjmatthew break; 15043759911cSjmatthew 15053759911cSjmatthew default: 15063759911cSjmatthew qle_handle_intr(sc, isr, info); 15073759911cSjmatthew break; 15083759911cSjmatthew } 15093759911cSjmatthew } 15103759911cSjmatthew 151165fd2507Sjmatthew if (result == 0) { 15123759911cSjmatthew /* timed out; do something? */ 151365fd2507Sjmatthew DPRINTF(QLE_D_MBOX, "%s: mbox timed out\n", DEVNAME(sc)); 15143759911cSjmatthew rv = 1; 151565fd2507Sjmatthew } else { 151665fd2507Sjmatthew for (i = 0; i < nitems(sc->sc_mbox); i++) { 151765fd2507Sjmatthew sc->sc_mbox[i] = qle_read_mbox(sc, i); 151865fd2507Sjmatthew } 151965fd2507Sjmatthew rv = (result == QLE_MBOX_COMPLETE ? 0 : result); 15203759911cSjmatthew } 15213759911cSjmatthew 15223759911cSjmatthew qle_clear_isr(sc, QLE_INT_TYPE_MBOX); 15233759911cSjmatthew return (rv); 15243759911cSjmatthew } 15253759911cSjmatthew 15263759911cSjmatthew void 15273759911cSjmatthew qle_mbox_putaddr(u_int16_t *mbox, struct qle_dmamem *mem) 15283759911cSjmatthew { 15293759911cSjmatthew mbox[2] = (QLE_DMA_DVA(mem) >> 16) & 0xffff; 15303759911cSjmatthew mbox[3] = (QLE_DMA_DVA(mem) >> 0) & 0xffff; 15313759911cSjmatthew mbox[6] = (QLE_DMA_DVA(mem) >> 48) & 0xffff; 15323759911cSjmatthew mbox[7] = (QLE_DMA_DVA(mem) >> 32) & 0xffff; 15333759911cSjmatthew } 15343759911cSjmatthew 15353759911cSjmatthew void 15363759911cSjmatthew qle_set_ints(struct qle_softc *sc, int enabled) 15373759911cSjmatthew { 15383759911cSjmatthew u_int32_t v = enabled ? QLE_INT_CTRL_ENABLE : 0; 15393759911cSjmatthew qle_write(sc, QLE_INT_CTRL, v); 15403759911cSjmatthew } 15413759911cSjmatthew 15423759911cSjmatthew int 15433759911cSjmatthew qle_read_isr(struct qle_softc *sc, u_int16_t *isr, u_int16_t *info) 15443759911cSjmatthew { 15453759911cSjmatthew u_int32_t v; 15463759911cSjmatthew 15473759911cSjmatthew switch (sc->sc_isp_gen) { 15483759911cSjmatthew case QLE_GEN_ISP24XX: 1549b803df0bSjmatthew case QLE_GEN_ISP25XX: 15503759911cSjmatthew if ((qle_read(sc, QLE_INT_STATUS) & QLE_RISC_INT_REQ) == 0) 15513759911cSjmatthew return (0); 15523759911cSjmatthew 15533759911cSjmatthew v = qle_read(sc, QLE_RISC_STATUS); 15543759911cSjmatthew 15553759911cSjmatthew switch (v & QLE_INT_STATUS_MASK) { 15563759911cSjmatthew case QLE_24XX_INT_ROM_MBOX: 15573759911cSjmatthew case QLE_24XX_INT_ROM_MBOX_FAIL: 15583759911cSjmatthew case QLE_24XX_INT_MBOX: 15593759911cSjmatthew case QLE_24XX_INT_MBOX_FAIL: 15603759911cSjmatthew *isr = QLE_INT_TYPE_MBOX; 15613759911cSjmatthew break; 15623759911cSjmatthew 15633759911cSjmatthew case QLE_24XX_INT_ASYNC: 15643759911cSjmatthew *isr = QLE_INT_TYPE_ASYNC; 15653759911cSjmatthew break; 15663759911cSjmatthew 15673759911cSjmatthew case QLE_24XX_INT_RSPQ: 15683759911cSjmatthew *isr = QLE_INT_TYPE_IO; 15693759911cSjmatthew break; 15703759911cSjmatthew 15713759911cSjmatthew default: 15723759911cSjmatthew *isr = QLE_INT_TYPE_OTHER; 15733759911cSjmatthew break; 15743759911cSjmatthew } 15753759911cSjmatthew 15763759911cSjmatthew *info = (v >> QLE_INT_INFO_SHIFT); 15773759911cSjmatthew return (1); 15783759911cSjmatthew 15793759911cSjmatthew default: 15803759911cSjmatthew return (0); 15813759911cSjmatthew } 15823759911cSjmatthew } 15833759911cSjmatthew 15843759911cSjmatthew void 15853759911cSjmatthew qle_clear_isr(struct qle_softc *sc, u_int16_t isr) 15863759911cSjmatthew { 15873759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_CLR_RISC_INT); 15883759911cSjmatthew } 15893759911cSjmatthew 15903759911cSjmatthew void 15913759911cSjmatthew qle_update_done(struct qle_softc *sc, int task) 15923759911cSjmatthew { 15933759911cSjmatthew atomic_clearbits_int(&sc->sc_update_tasks, task); 15943759911cSjmatthew } 15953759911cSjmatthew 15963759911cSjmatthew void 15973fc6fae1Sjmatthew qle_update_cancel(struct qle_softc *sc) 15983fc6fae1Sjmatthew { 15993fc6fae1Sjmatthew atomic_swap_uint(&sc->sc_update_tasks, 0); 16003fc6fae1Sjmatthew timeout_del(&sc->sc_update_timeout); 16013fc6fae1Sjmatthew task_del(sc->sc_update_taskq, &sc->sc_update_task); 16023fc6fae1Sjmatthew } 16033fc6fae1Sjmatthew 16043fc6fae1Sjmatthew void 16053759911cSjmatthew qle_update_start(struct qle_softc *sc, int task) 16063759911cSjmatthew { 16073759911cSjmatthew atomic_setbits_int(&sc->sc_update_tasks, task); 16083fc6fae1Sjmatthew if (!timeout_pending(&sc->sc_update_timeout)) 16093759911cSjmatthew task_add(sc->sc_update_taskq, &sc->sc_update_task); 16103759911cSjmatthew } 16113759911cSjmatthew 16123759911cSjmatthew void 16133fc6fae1Sjmatthew qle_update_defer(struct qle_softc *sc, int task) 16143fc6fae1Sjmatthew { 16153fc6fae1Sjmatthew atomic_setbits_int(&sc->sc_update_tasks, task); 16163fc6fae1Sjmatthew timeout_del(&sc->sc_update_timeout); 16173fc6fae1Sjmatthew task_del(sc->sc_update_taskq, &sc->sc_update_task); 16183fc6fae1Sjmatthew timeout_add_msec(&sc->sc_update_timeout, QLE_LOOP_SETTLE); 16193fc6fae1Sjmatthew } 16203fc6fae1Sjmatthew 16213fc6fae1Sjmatthew void 16223759911cSjmatthew qle_clear_port_lists(struct qle_softc *sc) 16233759911cSjmatthew { 1624c9d76b5bSderaadt struct qle_fc_port *port; 16253759911cSjmatthew while (!TAILQ_EMPTY(&sc->sc_ports_found)) { 1626c9d76b5bSderaadt port = TAILQ_FIRST(&sc->sc_ports_found); 1627c9d76b5bSderaadt TAILQ_REMOVE(&sc->sc_ports_found, port, update); 1628c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 16293759911cSjmatthew } 16303759911cSjmatthew 16313759911cSjmatthew while (!TAILQ_EMPTY(&sc->sc_ports_new)) { 1632c9d76b5bSderaadt port = TAILQ_FIRST(&sc->sc_ports_new); 1633c9d76b5bSderaadt TAILQ_REMOVE(&sc->sc_ports_new, port, update); 1634c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 16353759911cSjmatthew } 16363759911cSjmatthew 16373759911cSjmatthew while (!TAILQ_EMPTY(&sc->sc_ports_gone)) { 1638c9d76b5bSderaadt port = TAILQ_FIRST(&sc->sc_ports_gone); 1639c9d76b5bSderaadt TAILQ_REMOVE(&sc->sc_ports_gone, port, update); 16403759911cSjmatthew } 16413759911cSjmatthew } 16423759911cSjmatthew 16433759911cSjmatthew int 16443759911cSjmatthew qle_softreset(struct qle_softc *sc) 16453759911cSjmatthew { 16463759911cSjmatthew int i; 16473759911cSjmatthew qle_set_ints(sc, 0); 16483759911cSjmatthew 16493759911cSjmatthew /* set led control bits, stop dma */ 16503759911cSjmatthew qle_write(sc, QLE_GPIO_DATA, 0); 16513759911cSjmatthew qle_write(sc, QLE_CTRL_STATUS, QLE_CTRL_DMA_SHUTDOWN); 16523759911cSjmatthew while (qle_read(sc, QLE_CTRL_STATUS) & QLE_CTRL_DMA_ACTIVE) { 1653cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: dma still active\n", DEVNAME(sc)); 16543759911cSjmatthew delay(100); 16553759911cSjmatthew } 16563759911cSjmatthew 16573759911cSjmatthew /* reset */ 16583759911cSjmatthew qle_write(sc, QLE_CTRL_STATUS, QLE_CTRL_RESET | QLE_CTRL_DMA_SHUTDOWN); 16593759911cSjmatthew delay(100); 16603759911cSjmatthew /* clear data and control dma engines? */ 16613759911cSjmatthew 16623759911cSjmatthew /* wait for soft reset to clear */ 16633759911cSjmatthew for (i = 0; i < 1000; i++) { 16643759911cSjmatthew if (qle_read_mbox(sc, 0) == 0x0000) 16653759911cSjmatthew break; 16663759911cSjmatthew 16673759911cSjmatthew delay(100); 16683759911cSjmatthew } 16693759911cSjmatthew 16703759911cSjmatthew if (i == 1000) { 16713759911cSjmatthew printf("%s: reset mbox didn't clear\n", DEVNAME(sc)); 16723759911cSjmatthew qle_set_ints(sc, 0); 16733759911cSjmatthew return (ENXIO); 16743759911cSjmatthew } 16753759911cSjmatthew 16763759911cSjmatthew for (i = 0; i < 500000; i++) { 16773759911cSjmatthew if ((qle_read(sc, QLE_CTRL_STATUS) & QLE_CTRL_RESET) == 0) 16783759911cSjmatthew break; 16793759911cSjmatthew delay(5); 16803759911cSjmatthew } 16813759911cSjmatthew if (i == 500000) { 16823759911cSjmatthew printf("%s: reset status didn't clear\n", DEVNAME(sc)); 16833759911cSjmatthew return (ENXIO); 16843759911cSjmatthew } 16853759911cSjmatthew 16863759911cSjmatthew /* reset risc processor */ 16873759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_RESET); 16883759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_RELEASE); 16893759911cSjmatthew qle_host_cmd(sc, QLE_HOST_CMD_CLEAR_RESET); 16903759911cSjmatthew 16913759911cSjmatthew /* wait for reset to clear */ 16923759911cSjmatthew for (i = 0; i < 1000; i++) { 16933759911cSjmatthew if (qle_read_mbox(sc, 0) == 0x0000) 16943759911cSjmatthew break; 16953759911cSjmatthew delay(100); 16963759911cSjmatthew } 16973759911cSjmatthew if (i == 1000) { 16983759911cSjmatthew printf("%s: risc not ready after reset\n", DEVNAME(sc)); 16993759911cSjmatthew return (ENXIO); 17003759911cSjmatthew } 17013759911cSjmatthew 17023759911cSjmatthew /* reset queue pointers */ 17033759911cSjmatthew qle_write(sc, QLE_REQ_IN, 0); 17043759911cSjmatthew qle_write(sc, QLE_REQ_OUT, 0); 17053759911cSjmatthew qle_write(sc, QLE_RESP_IN, 0); 17063759911cSjmatthew qle_write(sc, QLE_RESP_OUT, 0); 17073759911cSjmatthew 17083759911cSjmatthew qle_set_ints(sc, 1); 17093759911cSjmatthew 17103759911cSjmatthew /* do a basic mailbox operation to check we're alive */ 17113759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_NOP; 171265fd2507Sjmatthew if (qle_mbox(sc, 0x0001)) { 17133759911cSjmatthew printf("ISP not responding after reset\n"); 17143759911cSjmatthew return (ENXIO); 17153759911cSjmatthew } 17163759911cSjmatthew 17173759911cSjmatthew return (0); 17183759911cSjmatthew } 17193759911cSjmatthew 17203759911cSjmatthew void 17213759911cSjmatthew qle_update_topology(struct qle_softc *sc) 17223759911cSjmatthew { 17233759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_GET_ID; 172465fd2507Sjmatthew if (qle_mbox(sc, 0x0001)) { 1725cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: unable to get loop id\n", DEVNAME(sc)); 17263759911cSjmatthew sc->sc_topology = QLE_TOPO_N_PORT_NO_TARGET; 17273759911cSjmatthew } else { 17283759911cSjmatthew sc->sc_topology = sc->sc_mbox[6]; 17293759911cSjmatthew sc->sc_loop_id = sc->sc_mbox[1]; 17303759911cSjmatthew 17313759911cSjmatthew switch (sc->sc_topology) { 17323759911cSjmatthew case QLE_TOPO_NL_PORT: 17333759911cSjmatthew case QLE_TOPO_N_PORT: 1734cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: loop id %d\n", DEVNAME(sc), 17353759911cSjmatthew sc->sc_loop_id); 17363759911cSjmatthew break; 17373759911cSjmatthew 17383759911cSjmatthew case QLE_TOPO_FL_PORT: 17393759911cSjmatthew case QLE_TOPO_F_PORT: 17403759911cSjmatthew sc->sc_port_id = sc->sc_mbox[2] | 17413759911cSjmatthew (sc->sc_mbox[3] << 16); 1742cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: fabric port id %06x\n", 1743cc014dc0Sjmatthew DEVNAME(sc), sc->sc_port_id); 17443759911cSjmatthew break; 17453759911cSjmatthew 17463759911cSjmatthew case QLE_TOPO_N_PORT_NO_TARGET: 17473759911cSjmatthew default: 1748cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: not useful\n", DEVNAME(sc)); 17493759911cSjmatthew break; 17503759911cSjmatthew } 17513759911cSjmatthew 17523759911cSjmatthew switch (sc->sc_topology) { 17533759911cSjmatthew case QLE_TOPO_NL_PORT: 17543759911cSjmatthew case QLE_TOPO_FL_PORT: 17553759911cSjmatthew sc->sc_loop_max_id = 126; 17563759911cSjmatthew break; 17573759911cSjmatthew 17583759911cSjmatthew case QLE_TOPO_N_PORT: 17593759911cSjmatthew sc->sc_loop_max_id = 2; 17603759911cSjmatthew break; 17613759911cSjmatthew 17623759911cSjmatthew default: 17633759911cSjmatthew sc->sc_loop_max_id = 0; 17643759911cSjmatthew break; 17653759911cSjmatthew } 17663759911cSjmatthew } 17673759911cSjmatthew } 17683759911cSjmatthew 17693759911cSjmatthew int 17703759911cSjmatthew qle_update_fabric(struct qle_softc *sc) 17713759911cSjmatthew { 17723759911cSjmatthew /*struct qle_sns_rft_id *rft;*/ 17733759911cSjmatthew 17743759911cSjmatthew switch (sc->sc_topology) { 17753759911cSjmatthew case QLE_TOPO_F_PORT: 17763759911cSjmatthew case QLE_TOPO_FL_PORT: 17773759911cSjmatthew break; 17783759911cSjmatthew 17793759911cSjmatthew default: 17803759911cSjmatthew return (0); 17813759911cSjmatthew } 17823759911cSjmatthew 17833759911cSjmatthew /* get the name server's port db entry */ 17843759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_GET_PORT_DB; 17853759911cSjmatthew sc->sc_mbox[1] = QLE_F_PORT_HANDLE; 17863759911cSjmatthew qle_mbox_putaddr(sc->sc_mbox, sc->sc_scratch); 17873759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 17883759911cSjmatthew sizeof(struct qle_get_port_db), BUS_DMASYNC_PREREAD); 178965fd2507Sjmatthew if (qle_mbox(sc, 0x00cf)) { 1790cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: get port db for SNS failed: %x\n", 17913759911cSjmatthew DEVNAME(sc), sc->sc_mbox[0]); 17923759911cSjmatthew sc->sc_sns_port_name = 0; 17933759911cSjmatthew } else { 17943759911cSjmatthew struct qle_get_port_db *pdb; 17953759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_scratch), 0, 17963759911cSjmatthew sizeof(struct qle_get_port_db), BUS_DMASYNC_POSTREAD); 17973759911cSjmatthew pdb = QLE_DMA_KVA(sc->sc_scratch); 1798cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: SNS port name %llx\n", DEVNAME(sc), 17993759911cSjmatthew betoh64(pdb->port_name)); 18003759911cSjmatthew sc->sc_sns_port_name = betoh64(pdb->port_name); 18013759911cSjmatthew } 18023759911cSjmatthew 18033759911cSjmatthew /* 18043759911cSjmatthew * register fc4 types with the fabric 18053759911cSjmatthew * some switches do this automatically, but apparently 18063759911cSjmatthew * some don't. 18073759911cSjmatthew */ 18083759911cSjmatthew /* 18093759911cSjmatthew rft = QLE_DMA_KVA(sc->sc_scratch); 18103759911cSjmatthew memset(rft, 0, sizeof(*rft) + sizeof(struct qle_sns_req_hdr)); 181161ff084cSdlg htolem16(&rft->subcmd, QLE_SNS_RFT_ID); 181261ff084cSdlg htolem16(&rft->max_word, sizeof(struct qle_sns_req_hdr) / 4); 181361ff084cSdlg htolem32(&rft->port_id, sc->sc_port_id); 18143759911cSjmatthew rft->fc4_types[0] = (1 << QLE_FC4_SCSI); 18153759911cSjmatthew if (qle_sns_req(sc, sc->sc_scratch, sizeof(*rft))) { 18163759911cSjmatthew printf("%s: RFT_ID failed\n", DEVNAME(sc)); 18173759911cSjmatthew / * we might be able to continue after this fails * / 18183759911cSjmatthew } 18193759911cSjmatthew */ 18203759911cSjmatthew 18213759911cSjmatthew return (1); 18223759911cSjmatthew } 18233759911cSjmatthew 18243759911cSjmatthew int 18253759911cSjmatthew qle_ct_pass_through(struct qle_softc *sc, u_int32_t port_handle, 18263759911cSjmatthew struct qle_dmamem *mem, size_t req_size, size_t resp_size) 18273759911cSjmatthew { 18283759911cSjmatthew struct qle_iocb_ct_passthrough *iocb; 18293759911cSjmatthew u_int16_t req; 18303759911cSjmatthew u_int64_t offset; 18313759911cSjmatthew int rv; 18323759911cSjmatthew 18333759911cSjmatthew mtx_enter(&sc->sc_queue_mtx); 18343759911cSjmatthew 18353759911cSjmatthew req = sc->sc_next_req_id++; 18363759911cSjmatthew if (sc->sc_next_req_id == sc->sc_maxcmds) 18373759911cSjmatthew sc->sc_next_req_id = 0; 18383759911cSjmatthew 18393759911cSjmatthew offset = (req * QLE_QUEUE_ENTRY_SIZE); 18403759911cSjmatthew iocb = QLE_DMA_KVA(sc->sc_requests) + offset; 18413759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset, 18423759911cSjmatthew QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE); 18433759911cSjmatthew 18443759911cSjmatthew memset(iocb, 0, QLE_QUEUE_ENTRY_SIZE); 18453759911cSjmatthew iocb->entry_type = QLE_IOCB_CT_PASSTHROUGH; 18463759911cSjmatthew iocb->entry_count = 1; 18473759911cSjmatthew 18483759911cSjmatthew iocb->req_handle = 9; 184961ff084cSdlg htolem16(&iocb->req_nport_handle, port_handle); 185061ff084cSdlg htolem16(&iocb->req_dsd_count, 1); 185161ff084cSdlg htolem16(&iocb->req_resp_dsd_count, 1); 185261ff084cSdlg htolem32(&iocb->req_cmd_byte_count, req_size); 185361ff084cSdlg htolem32(&iocb->req_resp_byte_count, resp_size); 1854a0abcb68Sdlg qle_sge(&iocb->req_cmd_seg, QLE_DMA_DVA(mem), req_size); 1855a0abcb68Sdlg qle_sge(&iocb->req_resp_seg, QLE_DMA_DVA(mem) + req_size, resp_size); 18563759911cSjmatthew 18573759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, QLE_DMA_LEN(mem), 18583759911cSjmatthew BUS_DMASYNC_PREREAD | BUS_DMASYNC_PREWRITE); 18593759911cSjmatthew qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id); 18603759911cSjmatthew sc->sc_fabric_pending = 1; 18613759911cSjmatthew mtx_leave(&sc->sc_queue_mtx); 18623759911cSjmatthew 18633759911cSjmatthew /* maybe put a proper timeout on this */ 18643759911cSjmatthew rv = 0; 18653759911cSjmatthew while (sc->sc_fabric_pending == 1) { 18663759911cSjmatthew if (sc->sc_scsibus == NULL) { 18673759911cSjmatthew u_int16_t isr, info; 18683759911cSjmatthew 18693759911cSjmatthew delay(100); 18703759911cSjmatthew if (qle_read_isr(sc, &isr, &info) != 0) 18713759911cSjmatthew qle_handle_intr(sc, isr, info); 18723759911cSjmatthew } else { 1873487476ffScheloha tsleep_nsec(sc->sc_scratch, PRIBIO, "qle_fabric", 1874487476ffScheloha SEC_TO_NSEC(1)); 18753759911cSjmatthew } 18763759911cSjmatthew } 18773759911cSjmatthew if (rv == 0) 18783759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, 18793759911cSjmatthew QLE_DMA_LEN(mem), BUS_DMASYNC_POSTREAD | 18803759911cSjmatthew BUS_DMASYNC_POSTWRITE); 18813759911cSjmatthew 18823759911cSjmatthew sc->sc_fabric_pending = 0; 18833759911cSjmatthew 18843759911cSjmatthew return (rv); 18853759911cSjmatthew } 18863759911cSjmatthew 18873759911cSjmatthew struct qle_fc_port * 18883759911cSjmatthew qle_next_fabric_port(struct qle_softc *sc, u_int32_t *firstport, 18893759911cSjmatthew u_int32_t *lastport) 18903759911cSjmatthew { 18913759911cSjmatthew struct qle_ct_ga_nxt_req *ga; 18923759911cSjmatthew struct qle_ct_ga_nxt_resp *gar; 18933759911cSjmatthew struct qle_fc_port *fport; 18943759911cSjmatthew int result; 18953759911cSjmatthew 18963759911cSjmatthew /* get the next port from the fabric nameserver */ 18973759911cSjmatthew ga = QLE_DMA_KVA(sc->sc_scratch); 18983759911cSjmatthew memset(ga, 0, sizeof(*ga) + sizeof(*gar)); 18993759911cSjmatthew ga->header.ct_revision = 0x01; 19003759911cSjmatthew ga->header.ct_gs_type = 0xfc; 19013759911cSjmatthew ga->header.ct_gs_subtype = 0x02; 19023759911cSjmatthew ga->subcmd = htobe16(QLE_SNS_GA_NXT); 19033759911cSjmatthew ga->max_word = htobe16((sizeof(*gar) - 16) / 4); 19043759911cSjmatthew ga->port_id = htobe32(*lastport); 19053759911cSjmatthew result = qle_ct_pass_through(sc, QLE_SNS_HANDLE, sc->sc_scratch, 19063759911cSjmatthew sizeof(*ga), sizeof(*gar)); 19073759911cSjmatthew if (result) { 1908871bea04Sjmatthew DPRINTF(QLE_D_PORT, "%s: GA_NXT %06x failed: %x\n", DEVNAME(sc), 1909871bea04Sjmatthew *lastport, result); 19103759911cSjmatthew *lastport = 0xffffffff; 19113759911cSjmatthew return (NULL); 19123759911cSjmatthew } 19133759911cSjmatthew 19143759911cSjmatthew gar = (struct qle_ct_ga_nxt_resp *)(ga + 1); 19153759911cSjmatthew /* if the response is all zeroes, try again */ 19163759911cSjmatthew if (gar->port_type_id == 0 && gar->port_name == 0 && 19173759911cSjmatthew gar->node_name == 0) { 1918cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: GA_NXT returned junk\n", DEVNAME(sc)); 19193759911cSjmatthew return (NULL); 19203759911cSjmatthew } 19213759911cSjmatthew 19223759911cSjmatthew /* are we back at the start? */ 19233759911cSjmatthew *lastport = betoh32(gar->port_type_id) & 0xffffff; 19243759911cSjmatthew if (*lastport == *firstport) { 19253759911cSjmatthew *lastport = 0xffffffff; 19263759911cSjmatthew return (NULL); 19273759911cSjmatthew } 19283759911cSjmatthew if (*firstport == 0xffffffff) 19293759911cSjmatthew *firstport = *lastport; 19303759911cSjmatthew 1931871bea04Sjmatthew DPRINTF(QLE_D_PORT, "%s: GA_NXT: port id: %06x, wwpn %llx, wwnn %llx\n", 1932871bea04Sjmatthew DEVNAME(sc), *lastport, betoh64(gar->port_name), 19333759911cSjmatthew betoh64(gar->node_name)); 19343759911cSjmatthew 19353759911cSjmatthew /* don't try to log in to ourselves */ 19363759911cSjmatthew if (*lastport == sc->sc_port_id) { 19373759911cSjmatthew return (NULL); 19383759911cSjmatthew } 19393759911cSjmatthew 19403759911cSjmatthew fport = malloc(sizeof(*fport), M_DEVBUF, M_ZERO | M_NOWAIT); 19413759911cSjmatthew if (fport == NULL) { 19423759911cSjmatthew printf("%s: failed to allocate a port struct\n", 19433759911cSjmatthew DEVNAME(sc)); 19443759911cSjmatthew *lastport = 0xffffffff; 19453759911cSjmatthew return (NULL); 19463759911cSjmatthew } 19473759911cSjmatthew fport->port_name = betoh64(gar->port_name); 19483759911cSjmatthew fport->node_name = betoh64(gar->node_name); 19493759911cSjmatthew fport->location = QLE_LOCATION_PORT_ID(*lastport); 19503759911cSjmatthew fport->portid = *lastport; 19513759911cSjmatthew return (fport); 19523759911cSjmatthew } 19533759911cSjmatthew 19543759911cSjmatthew int 1955e5d3c726Sjmatthew qle_fabric_plogx(struct qle_softc *sc, struct qle_fc_port *port, int flags, 1956e5d3c726Sjmatthew u_int32_t *info) 19573759911cSjmatthew { 19583759911cSjmatthew struct qle_iocb_plogx *iocb; 19593759911cSjmatthew u_int16_t req; 19603759911cSjmatthew u_int64_t offset; 19613759911cSjmatthew int rv; 19623759911cSjmatthew 19633759911cSjmatthew mtx_enter(&sc->sc_queue_mtx); 19643759911cSjmatthew 19653759911cSjmatthew req = sc->sc_next_req_id++; 19663759911cSjmatthew if (sc->sc_next_req_id == sc->sc_maxcmds) 19673759911cSjmatthew sc->sc_next_req_id = 0; 19683759911cSjmatthew 19693759911cSjmatthew offset = (req * QLE_QUEUE_ENTRY_SIZE); 19703759911cSjmatthew iocb = QLE_DMA_KVA(sc->sc_requests) + offset; 19713759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(sc->sc_requests), offset, 19723759911cSjmatthew QLE_QUEUE_ENTRY_SIZE, BUS_DMASYNC_POSTWRITE); 19733759911cSjmatthew 19743759911cSjmatthew memset(iocb, 0, QLE_QUEUE_ENTRY_SIZE); 19753759911cSjmatthew iocb->entry_type = QLE_IOCB_PLOGX; 19763759911cSjmatthew iocb->entry_count = 1; 19773759911cSjmatthew 19783759911cSjmatthew iocb->req_handle = 7; 1979e5d3c726Sjmatthew htolem16(&iocb->req_nport_handle, port->loopid); 198061ff084cSdlg htolem16(&iocb->req_port_id_lo, port->portid); 198161ff084cSdlg iocb->req_port_id_hi = port->portid >> 16; 1982e5d3c726Sjmatthew htolem16(&iocb->req_flags, flags); 19833759911cSjmatthew 1984e5d3c726Sjmatthew DPRINTF(QLE_D_PORT, "%s: plogx loop id %d port %06x, flags %x\n", 1985e5d3c726Sjmatthew DEVNAME(sc), port->loopid, port->portid, flags); 1986e5d3c726Sjmatthew qle_dump_iocb(sc, iocb); 19873759911cSjmatthew 19883759911cSjmatthew qle_write(sc, QLE_REQ_IN, sc->sc_next_req_id); 19893759911cSjmatthew sc->sc_fabric_pending = 1; 19903759911cSjmatthew mtx_leave(&sc->sc_queue_mtx); 19913759911cSjmatthew 19923759911cSjmatthew /* maybe put a proper timeout on this */ 19933759911cSjmatthew rv = 0; 19943759911cSjmatthew while (sc->sc_fabric_pending == 1) { 19953759911cSjmatthew if (sc->sc_scsibus == NULL) { 19963759911cSjmatthew u_int16_t isr, info; 19973759911cSjmatthew 19983759911cSjmatthew delay(100); 19993759911cSjmatthew if (qle_read_isr(sc, &isr, &info) != 0) 20003759911cSjmatthew qle_handle_intr(sc, isr, info); 20013759911cSjmatthew } else { 2002487476ffScheloha tsleep_nsec(sc->sc_scratch, PRIBIO, "qle_fabric", 2003487476ffScheloha SEC_TO_NSEC(1)); 20043759911cSjmatthew } 20053759911cSjmatthew } 20063759911cSjmatthew sc->sc_fabric_pending = 0; 20073759911cSjmatthew 2008e5d3c726Sjmatthew iocb = (struct qle_iocb_plogx *)&sc->sc_fabric_response; 2009e5d3c726Sjmatthew rv = lemtoh16(&iocb->req_status); 2010e5d3c726Sjmatthew if (rv == QLE_PLOGX_ERROR) { 2011e5d3c726Sjmatthew rv = lemtoh32(&iocb->req_ioparms[0]); 2012e5d3c726Sjmatthew *info = lemtoh32(&iocb->req_ioparms[1]); 2013e5d3c726Sjmatthew } 2014e5d3c726Sjmatthew 20153759911cSjmatthew return (rv); 20163759911cSjmatthew } 20173759911cSjmatthew 2018e5d3c726Sjmatthew int 2019e5d3c726Sjmatthew qle_fabric_plogi(struct qle_softc *sc, struct qle_fc_port *port) 2020e5d3c726Sjmatthew { 2021e5d3c726Sjmatthew u_int32_t info; 20220d6eccfaSjmatthew int err, loopid; 2023e5d3c726Sjmatthew 20240d6eccfaSjmatthew loopid = 0; 2025b9154820Sjmatthew retry: 2026e5d3c726Sjmatthew if (port->loopid == 0) { 2027e5d3c726Sjmatthew 2028e5d3c726Sjmatthew mtx_enter(&sc->sc_port_mtx); 20290d6eccfaSjmatthew loopid = qle_get_loop_id(sc, loopid); 2030e5d3c726Sjmatthew mtx_leave(&sc->sc_port_mtx); 2031e5d3c726Sjmatthew if (loopid == -1) { 2032e5d3c726Sjmatthew printf("%s: ran out of loop ids\n", DEVNAME(sc)); 2033e5d3c726Sjmatthew return (1); 2034e5d3c726Sjmatthew } 2035e5d3c726Sjmatthew 2036e5d3c726Sjmatthew port->loopid = loopid; 2037e5d3c726Sjmatthew } 2038e5d3c726Sjmatthew 2039e5d3c726Sjmatthew err = qle_fabric_plogx(sc, port, QLE_PLOGX_LOGIN, &info); 2040580dbe8fSjmatthew switch (err) { 2041580dbe8fSjmatthew case 0: 2042580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: logged in to %06x as %d\n", 2043e5d3c726Sjmatthew DEVNAME(sc), port->portid, port->loopid); 2044e5d3c726Sjmatthew port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN; 2045e5d3c726Sjmatthew return (0); 2046580dbe8fSjmatthew 2047580dbe8fSjmatthew case QLE_PLOGX_ERROR_PORT_ID_USED: 2048580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: already logged in to %06x as %d\n", 2049580dbe8fSjmatthew DEVNAME(sc), port->portid, info); 2050580dbe8fSjmatthew port->loopid = info; 2051b9154820Sjmatthew port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN; 2052580dbe8fSjmatthew return (0); 2053580dbe8fSjmatthew 2054580dbe8fSjmatthew case QLE_PLOGX_ERROR_HANDLE_USED: 20550d6eccfaSjmatthew if (qle_add_logged_in_port(sc, loopid, info)) { 2056b9154820Sjmatthew return (1); 2057b9154820Sjmatthew } 2058b9154820Sjmatthew port->loopid = 0; 20590d6eccfaSjmatthew loopid++; 2060b9154820Sjmatthew goto retry; 2061580dbe8fSjmatthew 2062580dbe8fSjmatthew default: 2063580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: error %x logging in to port %06x\n", 2064b9154820Sjmatthew DEVNAME(sc), err, port->portid); 2065e5d3c726Sjmatthew port->loopid = 0; 2066e5d3c726Sjmatthew return (1); 2067e5d3c726Sjmatthew } 2068e5d3c726Sjmatthew } 2069e5d3c726Sjmatthew 20703759911cSjmatthew void 20713759911cSjmatthew qle_fabric_plogo(struct qle_softc *sc, struct qle_fc_port *port) 20723759911cSjmatthew { 2073e5d3c726Sjmatthew int err; 2074e5d3c726Sjmatthew u_int32_t info; 20753759911cSjmatthew 2076e5d3c726Sjmatthew /* 2077e5d3c726Sjmatthew * we only log out if we can't see the port any more, so we always 2078e5d3c726Sjmatthew * want to do an explicit logout and free the n-port handle. 2079e5d3c726Sjmatthew */ 2080e5d3c726Sjmatthew err = qle_fabric_plogx(sc, port, QLE_PLOGX_LOGOUT | 2081e5d3c726Sjmatthew QLE_PLOGX_LOGOUT_EXPLICIT | QLE_PLOGX_LOGOUT_FREE_HANDLE, &info); 2082e5d3c726Sjmatthew if (err == 0) { 2083e5d3c726Sjmatthew DPRINTF(QLE_D_PORT, "%s: logged out of port %06x\n", 2084e5d3c726Sjmatthew DEVNAME(sc), port->portid); 2085e5d3c726Sjmatthew } else { 2086e5d3c726Sjmatthew DPRINTF(QLE_D_PORT, "%s: failed to log out of port %06x: " 2087e5d3c726Sjmatthew "%x %x\n", DEVNAME(sc), port->portid, err, info); 2088e5d3c726Sjmatthew } 20893759911cSjmatthew } 20903759911cSjmatthew 20913759911cSjmatthew void 20923fc6fae1Sjmatthew qle_deferred_update(void *xsc) 20933fc6fae1Sjmatthew { 20943fc6fae1Sjmatthew struct qle_softc *sc = xsc; 20953fc6fae1Sjmatthew task_add(sc->sc_update_taskq, &sc->sc_update_task); 20963fc6fae1Sjmatthew } 20973fc6fae1Sjmatthew 20983fc6fae1Sjmatthew void 2099e4195480Sdlg qle_do_update(void *xsc) 21003759911cSjmatthew { 21013759911cSjmatthew struct qle_softc *sc = xsc; 2102580dbe8fSjmatthew int firstport, lastport; 2103580dbe8fSjmatthew struct qle_fc_port *port, *fport; 21043759911cSjmatthew 2105cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: updating\n", DEVNAME(sc)); 21063759911cSjmatthew while (sc->sc_update_tasks != 0) { 21073759911cSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_CLEAR_ALL) { 21083759911cSjmatthew TAILQ_HEAD(, qle_fc_port) detach; 2109cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: detaching everything\n", 2110cc014dc0Sjmatthew DEVNAME(sc)); 21113759911cSjmatthew 21123759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 21133759911cSjmatthew qle_clear_port_lists(sc); 21143759911cSjmatthew TAILQ_INIT(&detach); 2115bab068e9Sbket TAILQ_CONCAT(&detach, &sc->sc_ports, ports); 21163759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 21173759911cSjmatthew 21183759911cSjmatthew while (!TAILQ_EMPTY(&detach)) { 21193759911cSjmatthew port = TAILQ_FIRST(&detach); 21203759911cSjmatthew TAILQ_REMOVE(&detach, port, ports); 21213759911cSjmatthew if (port->flags & QLE_PORT_FLAG_IS_TARGET) { 21223759911cSjmatthew scsi_detach_target(sc->sc_scsibus, 2123cc5e21deSkrw port->loopid, DETACH_FORCE | 2124cc5e21deSkrw DETACH_QUIET); 21253759911cSjmatthew sc->sc_targets[port->loopid] = NULL; 21263759911cSjmatthew } 21273759911cSjmatthew if (port->location & QLE_LOCATION_FABRIC) 21283759911cSjmatthew qle_fabric_plogo(sc, port); 21293759911cSjmatthew 2130c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 21313759911cSjmatthew } 21323759911cSjmatthew 21333759911cSjmatthew qle_update_done(sc, QLE_UPDATE_TASK_CLEAR_ALL); 21343759911cSjmatthew continue; 21353759911cSjmatthew } 21363759911cSjmatthew 21373759911cSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_SOFTRESET) { 2138cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: attempting softreset\n", 2139cc014dc0Sjmatthew DEVNAME(sc)); 21403759911cSjmatthew if (qle_softreset(sc) != 0) { 2141cc014dc0Sjmatthew DPRINTF(QLE_D_IO, "%s: couldn't softreset\n", 2142cc014dc0Sjmatthew DEVNAME(sc)); 21433759911cSjmatthew } 21443759911cSjmatthew qle_update_done(sc, QLE_UPDATE_TASK_SOFTRESET); 21453759911cSjmatthew continue; 21463759911cSjmatthew } 21473759911cSjmatthew 2148580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_UPDATE_TOPO) { 2149580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: updating topology\n", 2150580dbe8fSjmatthew DEVNAME(sc)); 2151580dbe8fSjmatthew qle_update_topology(sc); 2152580dbe8fSjmatthew qle_update_done(sc, QLE_UPDATE_TASK_UPDATE_TOPO); 2153580dbe8fSjmatthew continue; 2154580dbe8fSjmatthew } 2155580dbe8fSjmatthew 2156580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_GET_PORT_LIST) { 2157580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: getting port name list\n", 2158580dbe8fSjmatthew DEVNAME(sc)); 2159580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 2160580dbe8fSjmatthew qle_clear_port_lists(sc); 2161580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 2162580dbe8fSjmatthew 2163580dbe8fSjmatthew qle_get_port_name_list(sc, QLE_LOCATION_LOOP | 2164580dbe8fSjmatthew QLE_LOCATION_FABRIC); 2165580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 2166580dbe8fSjmatthew TAILQ_FOREACH(port, &sc->sc_ports, ports) { 2167580dbe8fSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports_gone, port, 2168580dbe8fSjmatthew update); 2169580dbe8fSjmatthew if (port->location & QLE_LOCATION_FABRIC) { 2170580dbe8fSjmatthew port->flags |= 2171580dbe8fSjmatthew QLE_PORT_FLAG_NEEDS_LOGIN; 2172580dbe8fSjmatthew } 2173580dbe8fSjmatthew } 2174580dbe8fSjmatthew 2175580dbe8fSjmatthew /* take care of ports that haven't changed first */ 2176580dbe8fSjmatthew TAILQ_FOREACH(fport, &sc->sc_ports_found, update) { 2177580dbe8fSjmatthew port = sc->sc_targets[fport->loopid]; 2178580dbe8fSjmatthew if (port == NULL || fport->port_name != 2179580dbe8fSjmatthew port->port_name) { 2180580dbe8fSjmatthew /* new or changed port, handled later */ 2181580dbe8fSjmatthew continue; 2182580dbe8fSjmatthew } 2183580dbe8fSjmatthew 2184580dbe8fSjmatthew /* 2185580dbe8fSjmatthew * the port hasn't been logged out, which 2186580dbe8fSjmatthew * means we don't need to log in again, and, 2187580dbe8fSjmatthew * for loop ports, that the port still exists 2188580dbe8fSjmatthew */ 2189580dbe8fSjmatthew port->flags &= ~QLE_PORT_FLAG_NEEDS_LOGIN; 2190580dbe8fSjmatthew if (port->location & QLE_LOCATION_LOOP) 2191580dbe8fSjmatthew TAILQ_REMOVE(&sc->sc_ports_gone, 2192580dbe8fSjmatthew port, update); 2193580dbe8fSjmatthew 2194580dbe8fSjmatthew fport->location = 0; 2195580dbe8fSjmatthew } 2196580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 2197580dbe8fSjmatthew qle_update_start(sc, QLE_UPDATE_TASK_PORT_LIST); 2198580dbe8fSjmatthew qle_update_done(sc, QLE_UPDATE_TASK_GET_PORT_LIST); 2199580dbe8fSjmatthew continue; 2200580dbe8fSjmatthew } 2201580dbe8fSjmatthew 2202580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_PORT_LIST) { 2203580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 2204580dbe8fSjmatthew fport = TAILQ_FIRST(&sc->sc_ports_found); 2205580dbe8fSjmatthew if (fport != NULL) { 2206580dbe8fSjmatthew TAILQ_REMOVE(&sc->sc_ports_found, fport, 2207580dbe8fSjmatthew update); 2208580dbe8fSjmatthew } 2209580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 2210580dbe8fSjmatthew 2211580dbe8fSjmatthew if (fport == NULL) { 2212580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: done with ports\n", 2213580dbe8fSjmatthew DEVNAME(sc)); 2214580dbe8fSjmatthew qle_update_done(sc, 2215580dbe8fSjmatthew QLE_UPDATE_TASK_PORT_LIST); 2216580dbe8fSjmatthew qle_update_start(sc, 2217be68bd62Sjmatthew QLE_UPDATE_TASK_SCAN_FABRIC); 2218580dbe8fSjmatthew } else if (fport->location & QLE_LOCATION_LOOP) { 2219580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: loop port %04x\n", 2220580dbe8fSjmatthew DEVNAME(sc), fport->loopid); 2221580dbe8fSjmatthew if (qle_add_loop_port(sc, fport) != 0) 2222c9d76b5bSderaadt free(fport, M_DEVBUF, sizeof *port); 2223580dbe8fSjmatthew } else if (fport->location & QLE_LOCATION_FABRIC) { 2224580dbe8fSjmatthew qle_add_fabric_port(sc, fport); 2225580dbe8fSjmatthew } else { 2226580dbe8fSjmatthew /* already processed */ 2227c9d76b5bSderaadt free(fport, M_DEVBUF, sizeof *port); 2228580dbe8fSjmatthew } 2229580dbe8fSjmatthew continue; 2230580dbe8fSjmatthew } 2231580dbe8fSjmatthew 2232580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_SCAN_FABRIC) { 2233580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: starting fabric scan\n", 2234580dbe8fSjmatthew DEVNAME(sc)); 2235580dbe8fSjmatthew lastport = sc->sc_port_id; 2236580dbe8fSjmatthew firstport = 0xffffffff; 2237580dbe8fSjmatthew if (qle_update_fabric(sc)) 2238580dbe8fSjmatthew qle_update_start(sc, 2239580dbe8fSjmatthew QLE_UPDATE_TASK_SCANNING_FABRIC); 2240107785f0Sjmatthew else 2241107785f0Sjmatthew qle_update_start(sc, 2242107785f0Sjmatthew QLE_UPDATE_TASK_ATTACH_TARGET | 2243107785f0Sjmatthew QLE_UPDATE_TASK_DETACH_TARGET); 2244580dbe8fSjmatthew 2245580dbe8fSjmatthew qle_update_done(sc, QLE_UPDATE_TASK_SCAN_FABRIC); 2246580dbe8fSjmatthew continue; 2247580dbe8fSjmatthew } 2248580dbe8fSjmatthew 2249580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_SCANNING_FABRIC) { 2250580dbe8fSjmatthew fport = qle_next_fabric_port(sc, &firstport, &lastport); 2251580dbe8fSjmatthew if (fport != NULL) { 2252580dbe8fSjmatthew int disp; 2253580dbe8fSjmatthew 2254580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 2255580dbe8fSjmatthew disp = qle_classify_port(sc, fport->location, 2256580dbe8fSjmatthew fport->port_name, fport->node_name, &port); 2257580dbe8fSjmatthew switch (disp) { 2258580dbe8fSjmatthew case QLE_PORT_DISP_CHANGED: 2259580dbe8fSjmatthew case QLE_PORT_DISP_MOVED: 2260580dbe8fSjmatthew /* we'll log out the old port later */ 2261580dbe8fSjmatthew case QLE_PORT_DISP_NEW: 2262580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: new port " 2263580dbe8fSjmatthew "%06x\n", DEVNAME(sc), 2264580dbe8fSjmatthew fport->portid); 2265580dbe8fSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports_found, 2266580dbe8fSjmatthew fport, update); 2267580dbe8fSjmatthew break; 2268580dbe8fSjmatthew case QLE_PORT_DISP_DUP: 2269c9d76b5bSderaadt free(fport, M_DEVBUF, sizeof *port); 2270580dbe8fSjmatthew break; 2271580dbe8fSjmatthew case QLE_PORT_DISP_SAME: 2272580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: existing port " 2273580dbe8fSjmatthew " %06x\n", DEVNAME(sc), 2274580dbe8fSjmatthew fport->portid); 2275580dbe8fSjmatthew TAILQ_REMOVE(&sc->sc_ports_gone, port, 2276580dbe8fSjmatthew update); 2277c9d76b5bSderaadt free(fport, M_DEVBUF, sizeof *port); 2278580dbe8fSjmatthew break; 2279580dbe8fSjmatthew } 2280580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 2281580dbe8fSjmatthew } 2282580dbe8fSjmatthew if (lastport == 0xffffffff) { 2283580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: finished\n", 2284580dbe8fSjmatthew DEVNAME(sc)); 2285580dbe8fSjmatthew qle_update_done(sc, 2286580dbe8fSjmatthew QLE_UPDATE_TASK_SCANNING_FABRIC); 2287580dbe8fSjmatthew qle_update_start(sc, 2288580dbe8fSjmatthew QLE_UPDATE_TASK_FABRIC_LOGIN); 2289580dbe8fSjmatthew } 2290580dbe8fSjmatthew continue; 2291580dbe8fSjmatthew } 2292580dbe8fSjmatthew 2293580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_FABRIC_LOGIN) { 2294580dbe8fSjmatthew mtx_enter(&sc->sc_port_mtx); 2295580dbe8fSjmatthew port = TAILQ_FIRST(&sc->sc_ports_found); 2296580dbe8fSjmatthew if (port != NULL) { 2297580dbe8fSjmatthew TAILQ_REMOVE(&sc->sc_ports_found, port, update); 2298580dbe8fSjmatthew } 2299580dbe8fSjmatthew mtx_leave(&sc->sc_port_mtx); 2300580dbe8fSjmatthew 2301580dbe8fSjmatthew if (port != NULL) { 2302580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: found port %06x\n", 2303580dbe8fSjmatthew DEVNAME(sc), port->portid); 2304580dbe8fSjmatthew if (qle_fabric_plogi(sc, port) == 0) { 2305580dbe8fSjmatthew qle_add_fabric_port(sc, port); 2306580dbe8fSjmatthew } else { 2307580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: plogi %06x " 2308580dbe8fSjmatthew "failed\n", DEVNAME(sc), 2309580dbe8fSjmatthew port->portid); 2310c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 2311580dbe8fSjmatthew } 2312580dbe8fSjmatthew } else { 2313580dbe8fSjmatthew DPRINTF(QLE_D_PORT, "%s: done with logins\n", 2314580dbe8fSjmatthew DEVNAME(sc)); 2315580dbe8fSjmatthew qle_update_done(sc, 2316580dbe8fSjmatthew QLE_UPDATE_TASK_FABRIC_LOGIN); 2317580dbe8fSjmatthew qle_update_start(sc, 2318580dbe8fSjmatthew QLE_UPDATE_TASK_ATTACH_TARGET | 2319580dbe8fSjmatthew QLE_UPDATE_TASK_DETACH_TARGET); 2320580dbe8fSjmatthew } 2321580dbe8fSjmatthew continue; 2322580dbe8fSjmatthew } 2323580dbe8fSjmatthew 2324580dbe8fSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_FABRIC_RELOGIN) { 2325580dbe8fSjmatthew TAILQ_FOREACH(port, &sc->sc_ports, ports) { 2326580dbe8fSjmatthew if (port->flags & QLE_PORT_FLAG_NEEDS_LOGIN) { 2327580dbe8fSjmatthew qle_fabric_plogi(sc, port); 2328580dbe8fSjmatthew break; 2329580dbe8fSjmatthew } 2330580dbe8fSjmatthew } 2331580dbe8fSjmatthew 2332abcbcc4dSdoug if (port == NULL) 2333580dbe8fSjmatthew qle_update_done(sc, 2334580dbe8fSjmatthew QLE_UPDATE_TASK_FABRIC_RELOGIN); 2335580dbe8fSjmatthew continue; 2336580dbe8fSjmatthew } 2337580dbe8fSjmatthew 23383759911cSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_DETACH_TARGET) { 23393759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 23403759911cSjmatthew port = TAILQ_FIRST(&sc->sc_ports_gone); 23413759911cSjmatthew if (port != NULL) { 23423759911cSjmatthew sc->sc_targets[port->loopid] = NULL; 23433759911cSjmatthew TAILQ_REMOVE(&sc->sc_ports_gone, port, update); 23443759911cSjmatthew TAILQ_REMOVE(&sc->sc_ports, port, ports); 23453759911cSjmatthew } 23463759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 23473759911cSjmatthew 23483759911cSjmatthew if (port != NULL) { 2349cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: detaching port %06x\n", 2350cc014dc0Sjmatthew DEVNAME(sc), port->portid); 23513759911cSjmatthew if (sc->sc_scsibus != NULL) 23523759911cSjmatthew scsi_detach_target(sc->sc_scsibus, 2353cc5e21deSkrw port->loopid, DETACH_FORCE | 2354cc5e21deSkrw DETACH_QUIET); 23553759911cSjmatthew 23563759911cSjmatthew if (port->location & QLE_LOCATION_FABRIC) 23573759911cSjmatthew qle_fabric_plogo(sc, port); 23583759911cSjmatthew 2359c9d76b5bSderaadt free(port, M_DEVBUF, sizeof *port); 23603759911cSjmatthew } else { 2361cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: nothing to detach\n", 2362cc014dc0Sjmatthew DEVNAME(sc)); 23633759911cSjmatthew qle_update_done(sc, 23643759911cSjmatthew QLE_UPDATE_TASK_DETACH_TARGET); 23653759911cSjmatthew } 23663759911cSjmatthew continue; 23673759911cSjmatthew } 23683759911cSjmatthew 23693759911cSjmatthew if (sc->sc_update_tasks & QLE_UPDATE_TASK_ATTACH_TARGET) { 23703759911cSjmatthew mtx_enter(&sc->sc_port_mtx); 23713759911cSjmatthew port = TAILQ_FIRST(&sc->sc_ports_new); 23723759911cSjmatthew if (port != NULL) { 23733759911cSjmatthew TAILQ_REMOVE(&sc->sc_ports_new, port, update); 23743759911cSjmatthew TAILQ_INSERT_TAIL(&sc->sc_ports, port, ports); 23753759911cSjmatthew } 23763759911cSjmatthew mtx_leave(&sc->sc_port_mtx); 23773759911cSjmatthew 23783759911cSjmatthew if (port != NULL) { 23793759911cSjmatthew if (sc->sc_scsibus != NULL) 23803759911cSjmatthew scsi_probe_target(sc->sc_scsibus, 23813759911cSjmatthew port->loopid); 23823759911cSjmatthew } else { 23833759911cSjmatthew qle_update_done(sc, 23843759911cSjmatthew QLE_UPDATE_TASK_ATTACH_TARGET); 23853759911cSjmatthew } 23863759911cSjmatthew continue; 23873759911cSjmatthew } 23883759911cSjmatthew 23893759911cSjmatthew } 23903759911cSjmatthew 2391cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: done updating\n", DEVNAME(sc)); 23923759911cSjmatthew } 23933759911cSjmatthew 23943759911cSjmatthew int 23953759911cSjmatthew qle_async(struct qle_softc *sc, u_int16_t info) 23963759911cSjmatthew { 23973759911cSjmatthew switch (info) { 23983759911cSjmatthew case QLE_ASYNC_SYSTEM_ERROR: 23993759911cSjmatthew qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET); 24003759911cSjmatthew break; 24013759911cSjmatthew 24023759911cSjmatthew case QLE_ASYNC_REQ_XFER_ERROR: 24033759911cSjmatthew qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET); 24043759911cSjmatthew break; 24053759911cSjmatthew 24063759911cSjmatthew case QLE_ASYNC_RSP_XFER_ERROR: 24073759911cSjmatthew qle_update_start(sc, QLE_UPDATE_TASK_SOFTRESET); 24083759911cSjmatthew break; 24093759911cSjmatthew 24103759911cSjmatthew case QLE_ASYNC_LIP_OCCURRED: 2411cc014dc0Sjmatthew DPRINTF(QLE_D_INTR, "%s: lip occurred\n", DEVNAME(sc)); 24123759911cSjmatthew break; 24133759911cSjmatthew 24143759911cSjmatthew case QLE_ASYNC_LOOP_UP: 2415cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: loop up\n", DEVNAME(sc)); 24163759911cSjmatthew sc->sc_loop_up = 1; 24173759911cSjmatthew sc->sc_marker_required = 1; 24183fc6fae1Sjmatthew qle_update_defer(sc, QLE_UPDATE_TASK_UPDATE_TOPO | 2419be68bd62Sjmatthew QLE_UPDATE_TASK_GET_PORT_LIST); 24203759911cSjmatthew break; 24213759911cSjmatthew 24223759911cSjmatthew case QLE_ASYNC_LOOP_DOWN: 2423cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: loop down\n", DEVNAME(sc)); 24243759911cSjmatthew sc->sc_loop_up = 0; 24253fc6fae1Sjmatthew qle_update_cancel(sc); 24263759911cSjmatthew qle_update_start(sc, QLE_UPDATE_TASK_CLEAR_ALL); 24273759911cSjmatthew break; 24283759911cSjmatthew 24293759911cSjmatthew case QLE_ASYNC_LIP_RESET: 2430cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: lip reset\n", DEVNAME(sc)); 24313759911cSjmatthew sc->sc_marker_required = 1; 24323fc6fae1Sjmatthew qle_update_defer(sc, QLE_UPDATE_TASK_FABRIC_RELOGIN); 24333759911cSjmatthew break; 24343759911cSjmatthew 24353759911cSjmatthew case QLE_ASYNC_PORT_DB_CHANGE: 2436cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: port db changed %x\n", DEVNAME(sc), 24373759911cSjmatthew qle_read_mbox(sc, 1)); 2438be68bd62Sjmatthew qle_update_start(sc, QLE_UPDATE_TASK_GET_PORT_LIST); 24393759911cSjmatthew break; 24403759911cSjmatthew 24413759911cSjmatthew case QLE_ASYNC_CHANGE_NOTIFY: 2442cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: name server change (%02x:%02x)\n", 2443cc014dc0Sjmatthew DEVNAME(sc), qle_read_mbox(sc, 1), qle_read_mbox(sc, 2)); 2444be68bd62Sjmatthew qle_update_start(sc, QLE_UPDATE_TASK_GET_PORT_LIST); 24453759911cSjmatthew break; 24463759911cSjmatthew 24473759911cSjmatthew case QLE_ASYNC_LIP_F8: 2448cc014dc0Sjmatthew DPRINTF(QLE_D_INTR, "%s: lip f8\n", DEVNAME(sc)); 24493759911cSjmatthew break; 24503759911cSjmatthew 24513759911cSjmatthew case QLE_ASYNC_LOOP_INIT_ERROR: 2452cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: loop initialization error: %x\n", 2453cc014dc0Sjmatthew DEVNAME(sc), qle_read_mbox(sc, 1)); 24543759911cSjmatthew break; 24553759911cSjmatthew 24563759911cSjmatthew case QLE_ASYNC_POINT_TO_POINT: 2457cc014dc0Sjmatthew DPRINTF(QLE_D_PORT, "%s: connected in point-to-point mode\n", 2458cc014dc0Sjmatthew DEVNAME(sc)); 24593759911cSjmatthew break; 24603759911cSjmatthew 24613759911cSjmatthew case QLE_ASYNC_ZIO_RESP_UPDATE: 24623759911cSjmatthew /* shouldn't happen, we don't do zio */ 24633759911cSjmatthew break; 24643759911cSjmatthew 24653759911cSjmatthew default: 2466cc014dc0Sjmatthew DPRINTF(QLE_D_INTR, "%s: unknown async %x\n", DEVNAME(sc), info); 24673759911cSjmatthew break; 24683759911cSjmatthew } 24693759911cSjmatthew return (1); 24703759911cSjmatthew } 24713759911cSjmatthew 24723759911cSjmatthew void 24733759911cSjmatthew qle_dump_stuff(struct qle_softc *sc, void *buf, int n) 24743759911cSjmatthew { 2475cc014dc0Sjmatthew #ifdef QLE_DEBUG 24763759911cSjmatthew u_int8_t *d = buf; 24773759911cSjmatthew int l; 24783759911cSjmatthew 2479cc014dc0Sjmatthew if ((qledebug & QLE_D_IOCB) == 0) 2480cc014dc0Sjmatthew return; 2481cc014dc0Sjmatthew 24823759911cSjmatthew printf("%s: stuff\n", DEVNAME(sc)); 24833759911cSjmatthew for (l = 0; l < n; l++) { 24843759911cSjmatthew printf(" %2.2x", d[l]); 24853759911cSjmatthew if (l % 16 == 15) 24863759911cSjmatthew printf("\n"); 24873759911cSjmatthew } 24883759911cSjmatthew if (n % 16 != 0) 24893759911cSjmatthew printf("\n"); 2490cc014dc0Sjmatthew #endif 24913759911cSjmatthew } 24923759911cSjmatthew 24933759911cSjmatthew void 24943759911cSjmatthew qle_dump_iocb(struct qle_softc *sc, void *buf) 24953759911cSjmatthew { 2496cc014dc0Sjmatthew #ifdef QLE_DEBUG 24973759911cSjmatthew u_int8_t *iocb = buf; 24983759911cSjmatthew int l; 24993759911cSjmatthew int b; 25003759911cSjmatthew 2501cc014dc0Sjmatthew if ((qledebug & QLE_D_IOCB) == 0) 2502cc014dc0Sjmatthew return; 2503cc014dc0Sjmatthew 25043759911cSjmatthew printf("%s: iocb:\n", DEVNAME(sc)); 25053759911cSjmatthew for (l = 0; l < 4; l++) { 25063759911cSjmatthew for (b = 0; b < 16; b++) { 25073759911cSjmatthew printf(" %2.2x", iocb[(l*16)+b]); 25083759911cSjmatthew } 25093759911cSjmatthew printf("\n"); 25103759911cSjmatthew } 2511cc014dc0Sjmatthew #endif 25123759911cSjmatthew } 25133759911cSjmatthew 25143759911cSjmatthew void 25153759911cSjmatthew qle_dump_iocb_segs(struct qle_softc *sc, void *segs, int n) 25163759911cSjmatthew { 2517cc014dc0Sjmatthew #ifdef QLE_DEBUG 25183759911cSjmatthew u_int8_t *buf = segs; 25193759911cSjmatthew int s, b; 2520cc014dc0Sjmatthew 2521cc014dc0Sjmatthew if ((qledebug & QLE_D_IOCB) == 0) 2522cc014dc0Sjmatthew return; 2523cc014dc0Sjmatthew 25243759911cSjmatthew printf("%s: iocb segs:\n", DEVNAME(sc)); 25253759911cSjmatthew for (s = 0; s < n; s++) { 25263759911cSjmatthew for (b = 0; b < sizeof(struct qle_iocb_seg); b++) { 25273759911cSjmatthew printf(" %2.2x", buf[(s*(sizeof(struct qle_iocb_seg))) 25283759911cSjmatthew + b]); 25293759911cSjmatthew } 25303759911cSjmatthew printf("\n"); 25313759911cSjmatthew } 2532cc014dc0Sjmatthew #endif 25333759911cSjmatthew } 25343759911cSjmatthew 25353759911cSjmatthew void 25363759911cSjmatthew qle_put_marker(struct qle_softc *sc, void *buf) 25373759911cSjmatthew { 25383759911cSjmatthew struct qle_iocb_marker *marker = buf; 25393759911cSjmatthew 25403759911cSjmatthew marker->entry_type = QLE_IOCB_MARKER; 25413759911cSjmatthew marker->entry_count = 1; 25423759911cSjmatthew marker->seqno = 0; 25433759911cSjmatthew marker->flags = 0; 25443759911cSjmatthew 25453759911cSjmatthew /* could be more specific here; isp(4) isn't */ 25463759911cSjmatthew marker->target = 0; 25473759911cSjmatthew marker->modifier = QLE_IOCB_MARKER_SYNC_ALL; 25483759911cSjmatthew } 25493759911cSjmatthew 25503759911cSjmatthew void 2551a0abcb68Sdlg qle_sge(struct qle_iocb_seg *seg, u_int64_t addr, u_int32_t len) 25523759911cSjmatthew { 2553a0abcb68Sdlg htolem32(&seg->seg_addr_lo, addr); 2554a0abcb68Sdlg htolem32(&seg->seg_addr_hi, addr >> 32); 2555a0abcb68Sdlg htolem32(&seg->seg_len, len); 25563759911cSjmatthew } 25573759911cSjmatthew 25583759911cSjmatthew void 25593759911cSjmatthew qle_put_cmd(struct qle_softc *sc, void *buf, struct scsi_xfer *xs, 25603759911cSjmatthew struct qle_ccb *ccb, u_int32_t target_port) 25613759911cSjmatthew { 2562a0abcb68Sdlg bus_dmamap_t dmap = ccb->ccb_dmamap; 25633759911cSjmatthew struct qle_iocb_req6 *req = buf; 25643759911cSjmatthew struct qle_fcp_cmnd *cmnd; 25653759911cSjmatthew u_int64_t fcp_cmnd_offset; 25663759911cSjmatthew u_int32_t fcp_dl; 25673759911cSjmatthew int seg; 25683759911cSjmatthew int target = xs->sc_link->target; 25693759911cSjmatthew int lun = xs->sc_link->lun; 2570f1b43acaSdlg u_int16_t flags; 25713759911cSjmatthew 25723759911cSjmatthew memset(req, 0, sizeof(*req)); 25733759911cSjmatthew req->entry_type = QLE_IOCB_CMD_TYPE_6; 25743759911cSjmatthew req->entry_count = 1; 25753759911cSjmatthew 25763759911cSjmatthew req->req_handle = ccb->ccb_id; 2577f1b43acaSdlg htolem16(&req->req_nport_handle, target); 25783759911cSjmatthew 25793759911cSjmatthew /* 25803759911cSjmatthew * timeout is in seconds. make sure it's at least 1 if a timeout 25813759911cSjmatthew * was specified in xs 25823759911cSjmatthew */ 25833759911cSjmatthew if (xs->timeout != 0) 2584f1b43acaSdlg htolem16(&req->req_timeout, MAX(1, xs->timeout/1000)); 25853759911cSjmatthew 25863759911cSjmatthew if (xs->datalen > 0) { 2587f1b43acaSdlg flags = (xs->flags & SCSI_DATA_IN) ? 2588f1b43acaSdlg QLE_IOCB_CTRL_FLAG_READ : QLE_IOCB_CTRL_FLAG_WRITE; 2589a0abcb68Sdlg if (dmap->dm_nsegs == 1) { 2590a0abcb68Sdlg qle_sge(&req->req_data_seg, dmap->dm_segs[0].ds_addr, 2591a0abcb68Sdlg dmap->dm_segs[0].ds_len); 25923759911cSjmatthew } else { 2593f1b43acaSdlg flags |= QLE_IOCB_CTRL_FLAG_EXT_SEG; 2594a0abcb68Sdlg for (seg = 0; seg < dmap->dm_nsegs; seg++) { 2595a0abcb68Sdlg qle_sge(&ccb->ccb_segs[seg], 2596a0abcb68Sdlg dmap->dm_segs[seg].ds_addr, 2597a0abcb68Sdlg dmap->dm_segs[seg].ds_len); 25983759911cSjmatthew } 259920c4379eSjmatthew qle_sge(&ccb->ccb_segs[seg++], 0, 0); 2600a0abcb68Sdlg 26013759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, 26023759911cSjmatthew QLE_DMA_MAP(sc->sc_segments), ccb->ccb_seg_offset, 260320c4379eSjmatthew seg * sizeof(*ccb->ccb_segs), 26043759911cSjmatthew BUS_DMASYNC_PREWRITE); 260520c4379eSjmatthew 260620c4379eSjmatthew qle_sge(&req->req_data_seg, 260720c4379eSjmatthew QLE_DMA_DVA(sc->sc_segments) + ccb->ccb_seg_offset, 260820c4379eSjmatthew seg * sizeof(struct qle_iocb_seg)); 26093759911cSjmatthew } 26103759911cSjmatthew 2611f1b43acaSdlg htolem16(&req->req_data_seg_count, dmap->dm_nsegs); 2612f1b43acaSdlg htolem32(&req->req_data_len, xs->datalen); 2613f1b43acaSdlg htolem16(&req->req_ctrl_flags, flags); 2614f1b43acaSdlg } 2615f1b43acaSdlg 2616f1b43acaSdlg htobem16(&req->req_fcp_lun[0], lun); 2617f1b43acaSdlg htobem16(&req->req_fcp_lun[1], lun >> 16); 2618f1b43acaSdlg htolem32(&req->req_target_id, target_port & 0xffffff); 26193759911cSjmatthew 26203759911cSjmatthew fcp_cmnd_offset = ccb->ccb_id * sizeof(*cmnd); 26213759911cSjmatthew /* set up FCP_CMND */ 26223759911cSjmatthew cmnd = (struct qle_fcp_cmnd *)QLE_DMA_KVA(sc->sc_fcp_cmnds) + 26233759911cSjmatthew ccb->ccb_id; 26243759911cSjmatthew 26253759911cSjmatthew memset(cmnd, 0, sizeof(*cmnd)); 2626f1b43acaSdlg htobem16(&cmnd->fcp_lun[0], lun); 2627f1b43acaSdlg htobem16(&cmnd->fcp_lun[1], lun >> 16); 26283759911cSjmatthew /* cmnd->fcp_task_attr = TSK_SIMPLE; */ 26293759911cSjmatthew /* cmnd->fcp_task_mgmt = 0; */ 2630664c6166Skrw memcpy(cmnd->fcp_cdb, &xs->cmd, xs->cmdlen); 26313759911cSjmatthew 26323759911cSjmatthew /* FCP_DL goes after the cdb */ 26333759911cSjmatthew fcp_dl = htobe32(xs->datalen); 26343759911cSjmatthew if (xs->cmdlen > 16) { 2635f1b43acaSdlg htolem16(&req->req_fcp_cmnd_len, 12 + xs->cmdlen + 4); 26363759911cSjmatthew cmnd->fcp_add_cdb_len = xs->cmdlen - 16; 2637bd6c3caaSdlg memcpy(cmnd->fcp_cdb + xs->cmdlen, &fcp_dl, sizeof(fcp_dl)); 26383759911cSjmatthew } else { 2639f1b43acaSdlg htolem16(&req->req_fcp_cmnd_len, 12 + 16 + 4); 26403759911cSjmatthew cmnd->fcp_add_cdb_len = 0; 26413759911cSjmatthew memcpy(cmnd->fcp_cdb + 16, &fcp_dl, sizeof(fcp_dl)); 26423759911cSjmatthew } 26433759911cSjmatthew if (xs->datalen > 0) 26443759911cSjmatthew cmnd->fcp_add_cdb_len |= (xs->flags & SCSI_DATA_IN) ? 2 : 1; 26453759911cSjmatthew 26463759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, 26473759911cSjmatthew QLE_DMA_MAP(sc->sc_fcp_cmnds), fcp_cmnd_offset, 26483759911cSjmatthew sizeof(*cmnd), BUS_DMASYNC_PREWRITE); 2649f1b43acaSdlg 2650f1b43acaSdlg /* link req to cmnd */ 2651f1b43acaSdlg fcp_cmnd_offset += QLE_DMA_DVA(sc->sc_fcp_cmnds); 2652f1b43acaSdlg htolem32(&req->req_fcp_cmnd_addr_lo, fcp_cmnd_offset); 2653f1b43acaSdlg htolem32(&req->req_fcp_cmnd_addr_hi, fcp_cmnd_offset >> 32); 26543759911cSjmatthew } 26553759911cSjmatthew 26563759911cSjmatthew int 2657b803df0bSjmatthew qle_load_fwchunk(struct qle_softc *sc, struct qle_dmamem *mem, 26583759911cSjmatthew const u_int32_t *src) 26593759911cSjmatthew { 26603759911cSjmatthew u_int32_t dest, done, total; 26613759911cSjmatthew int i; 26623759911cSjmatthew 26633759911cSjmatthew dest = src[2]; 26643759911cSjmatthew done = 0; 26653759911cSjmatthew total = src[3]; 26663759911cSjmatthew 26673759911cSjmatthew while (done < total) { 26683759911cSjmatthew u_int32_t *copy; 26693759911cSjmatthew u_int32_t words; 26703759911cSjmatthew 26713759911cSjmatthew /* limit transfer size otherwise it just doesn't work */ 26723759911cSjmatthew words = MIN(total - done, 1 << 10); 26733759911cSjmatthew copy = QLE_DMA_KVA(mem); 26743759911cSjmatthew for (i = 0; i < words; i++) { 267561ff084cSdlg htolem32(©[i], src[done++]); 26763759911cSjmatthew } 26773759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, words * 4, 26783759911cSjmatthew BUS_DMASYNC_PREWRITE); 26793759911cSjmatthew 26803759911cSjmatthew sc->sc_mbox[0] = QLE_MBOX_LOAD_RISC_RAM; 26813759911cSjmatthew sc->sc_mbox[1] = dest; 26823759911cSjmatthew sc->sc_mbox[4] = words >> 16; 26833759911cSjmatthew sc->sc_mbox[5] = words & 0xffff; 26843759911cSjmatthew sc->sc_mbox[8] = dest >> 16; 26853759911cSjmatthew qle_mbox_putaddr(sc->sc_mbox, mem); 268665fd2507Sjmatthew if (qle_mbox(sc, 0x01ff)) { 26873759911cSjmatthew printf("firmware load failed\n"); 26883759911cSjmatthew return (1); 26893759911cSjmatthew } 26903759911cSjmatthew bus_dmamap_sync(sc->sc_dmat, QLE_DMA_MAP(mem), 0, words * 4, 26913759911cSjmatthew BUS_DMASYNC_POSTWRITE); 26923759911cSjmatthew 26933759911cSjmatthew dest += words; 26943759911cSjmatthew } 26953759911cSjmatthew 2696c8005c8bSjmatthew return (qle_verify_firmware(sc, src[2])); 26973759911cSjmatthew } 26983759911cSjmatthew 26993759911cSjmatthew int 2700b803df0bSjmatthew qle_load_firmware_chunks(struct qle_softc *sc, const u_int32_t *fw) 27013759911cSjmatthew { 27023759911cSjmatthew struct qle_dmamem *mem; 2703c8005c8bSjmatthew int res = 0; 27043759911cSjmatthew 27053759911cSjmatthew mem = qle_dmamem_alloc(sc, 65536); 27063759911cSjmatthew for (;;) { 2707c8005c8bSjmatthew if (qle_load_fwchunk(sc, mem, fw)) { 2708c8005c8bSjmatthew res = 1; 2709c8005c8bSjmatthew break; 2710c8005c8bSjmatthew } 27113759911cSjmatthew if (fw[1] == 0) 27123759911cSjmatthew break; 27133759911cSjmatthew fw += fw[3]; 27143759911cSjmatthew } 27153759911cSjmatthew 27163759911cSjmatthew qle_dmamem_free(sc, mem); 2717c8005c8bSjmatthew return (res); 2718c8005c8bSjmatthew } 2719c8005c8bSjmatthew 2720c8005c8bSjmatthew u_int32_t 2721c8005c8bSjmatthew qle_read_ram_word(struct qle_softc *sc, u_int32_t addr) 2722c8005c8bSjmatthew { 2723c8005c8bSjmatthew sc->sc_mbox[0] = QLE_MBOX_READ_RISC_RAM; 2724c8005c8bSjmatthew sc->sc_mbox[1] = addr & 0xffff; 2725c8005c8bSjmatthew sc->sc_mbox[8] = addr >> 16; 272665fd2507Sjmatthew if (qle_mbox(sc, 0x0103)) { 2727c8005c8bSjmatthew return (0); 2728c8005c8bSjmatthew } 2729c8005c8bSjmatthew return ((sc->sc_mbox[3] << 16) | sc->sc_mbox[2]); 2730c8005c8bSjmatthew } 2731c8005c8bSjmatthew 2732c8005c8bSjmatthew int 2733c8005c8bSjmatthew qle_verify_firmware(struct qle_softc *sc, u_int32_t addr) 2734c8005c8bSjmatthew { 2735c8005c8bSjmatthew /* 2736c8005c8bSjmatthew * QLE_MBOX_VERIFY_CSUM requires at least the firmware header 2737c8005c8bSjmatthew * to be correct, otherwise it wanders all over ISP memory and 2738c8005c8bSjmatthew * gets lost. Check that chunk address (addr+2) is right and 2739c8005c8bSjmatthew * size (addr+3) is plausible first. 2740c8005c8bSjmatthew */ 2741c8005c8bSjmatthew if ((qle_read_ram_word(sc, addr+2) != addr) || 2742c8005c8bSjmatthew (qle_read_ram_word(sc, addr+3) > 0xffff)) { 2743c8005c8bSjmatthew return (1); 2744c8005c8bSjmatthew } 2745c8005c8bSjmatthew 2746c8005c8bSjmatthew sc->sc_mbox[0] = QLE_MBOX_VERIFY_CSUM; 2747c8005c8bSjmatthew sc->sc_mbox[1] = addr >> 16; 2748c8005c8bSjmatthew sc->sc_mbox[2] = addr; 274965fd2507Sjmatthew if (qle_mbox(sc, 0x0007)) { 2750c8005c8bSjmatthew return (1); 2751c8005c8bSjmatthew } 27523759911cSjmatthew return (0); 27533759911cSjmatthew } 27543759911cSjmatthew 27553759911cSjmatthew int 27563759911cSjmatthew qle_read_nvram(struct qle_softc *sc) 27573759911cSjmatthew { 27583759911cSjmatthew u_int32_t data[sizeof(sc->sc_nvram) / 4]; 27593759911cSjmatthew u_int32_t csum, tmp, v; 27603759911cSjmatthew int i, base, l; 27613759911cSjmatthew 2762bea9c85cSjmatthew switch (sc->sc_isp_gen) { 2763bea9c85cSjmatthew case QLE_GEN_ISP24XX: 2764bea9c85cSjmatthew base = 0x7ffe0080; 2765bea9c85cSjmatthew break; 2766bea9c85cSjmatthew case QLE_GEN_ISP25XX: 2767bea9c85cSjmatthew base = 0x7ff48080; 2768bea9c85cSjmatthew break; 2769bea9c85cSjmatthew } 2770bea9c85cSjmatthew base += sc->sc_port * 0x100; 27713759911cSjmatthew 27723759911cSjmatthew csum = 0; 27733759911cSjmatthew for (i = 0; i < nitems(data); i++) { 27743759911cSjmatthew data[i] = 0xffffffff; 2775bea9c85cSjmatthew qle_write(sc, QLE_FLASH_NVRAM_ADDR, base + i); 27763759911cSjmatthew for (l = 0; l < 5000; l++) { 27773759911cSjmatthew delay(10); 27783759911cSjmatthew tmp = qle_read(sc, QLE_FLASH_NVRAM_ADDR); 27793759911cSjmatthew if (tmp & (1U << 31)) { 27803759911cSjmatthew v = qle_read(sc, QLE_FLASH_NVRAM_DATA); 27813759911cSjmatthew csum += v; 27823759911cSjmatthew data[i] = letoh32(v); 27833759911cSjmatthew break; 27843759911cSjmatthew } 27853759911cSjmatthew } 27863759911cSjmatthew } 27873759911cSjmatthew 27883759911cSjmatthew bcopy(data, &sc->sc_nvram, sizeof(sc->sc_nvram)); 27893759911cSjmatthew /* id field should be 'ISP' */ 27903759911cSjmatthew if (sc->sc_nvram.id[0] != 'I' || sc->sc_nvram.id[1] != 'S' || 27913759911cSjmatthew sc->sc_nvram.id[2] != 'P' || csum != 0) { 27923759911cSjmatthew printf("%s: nvram corrupt\n", DEVNAME(sc)); 27933759911cSjmatthew return (1); 27943759911cSjmatthew } 27953759911cSjmatthew return (0); 27963759911cSjmatthew } 27973759911cSjmatthew 27983759911cSjmatthew struct qle_dmamem * 27993759911cSjmatthew qle_dmamem_alloc(struct qle_softc *sc, size_t size) 28003759911cSjmatthew { 28013759911cSjmatthew struct qle_dmamem *m; 28023759911cSjmatthew int nsegs; 28033759911cSjmatthew 28043759911cSjmatthew m = malloc(sizeof(*m), M_DEVBUF, M_NOWAIT | M_ZERO); 28053759911cSjmatthew if (m == NULL) 28063759911cSjmatthew return (NULL); 28073759911cSjmatthew 28083759911cSjmatthew m->qdm_size = size; 28093759911cSjmatthew 28103759911cSjmatthew if (bus_dmamap_create(sc->sc_dmat, size, 1, size, 0, 28113759911cSjmatthew BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, &m->qdm_map) != 0) 28123759911cSjmatthew goto qdmfree; 28133759911cSjmatthew 28143759911cSjmatthew if (bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, &m->qdm_seg, 1, 28153759911cSjmatthew &nsegs, BUS_DMA_NOWAIT | BUS_DMA_ZERO) != 0) 28163759911cSjmatthew goto destroy; 28173759911cSjmatthew 28183759911cSjmatthew if (bus_dmamem_map(sc->sc_dmat, &m->qdm_seg, nsegs, size, &m->qdm_kva, 28193759911cSjmatthew BUS_DMA_NOWAIT) != 0) 28203759911cSjmatthew goto free; 28213759911cSjmatthew 28223759911cSjmatthew if (bus_dmamap_load(sc->sc_dmat, m->qdm_map, m->qdm_kva, size, NULL, 28233759911cSjmatthew BUS_DMA_NOWAIT) != 0) 28243759911cSjmatthew goto unmap; 28253759911cSjmatthew 28263759911cSjmatthew return (m); 28273759911cSjmatthew 28283759911cSjmatthew unmap: 28293759911cSjmatthew bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size); 28303759911cSjmatthew free: 28313759911cSjmatthew bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1); 28323759911cSjmatthew destroy: 28333759911cSjmatthew bus_dmamap_destroy(sc->sc_dmat, m->qdm_map); 28343759911cSjmatthew qdmfree: 2835c9d76b5bSderaadt free(m, M_DEVBUF, sizeof *m); 28363759911cSjmatthew 28373759911cSjmatthew return (NULL); 28383759911cSjmatthew } 28393759911cSjmatthew 28403759911cSjmatthew void 28413759911cSjmatthew qle_dmamem_free(struct qle_softc *sc, struct qle_dmamem *m) 28423759911cSjmatthew { 28433759911cSjmatthew bus_dmamap_unload(sc->sc_dmat, m->qdm_map); 28443759911cSjmatthew bus_dmamem_unmap(sc->sc_dmat, m->qdm_kva, m->qdm_size); 28453759911cSjmatthew bus_dmamem_free(sc->sc_dmat, &m->qdm_seg, 1); 28463759911cSjmatthew bus_dmamap_destroy(sc->sc_dmat, m->qdm_map); 2847c9d76b5bSderaadt free(m, M_DEVBUF, sizeof *m); 28483759911cSjmatthew } 28493759911cSjmatthew 28503759911cSjmatthew int 28513759911cSjmatthew qle_alloc_ccbs(struct qle_softc *sc) 28523759911cSjmatthew { 28533759911cSjmatthew struct qle_ccb *ccb; 28543759911cSjmatthew u_int8_t *cmd; 28553759911cSjmatthew int i; 28563759911cSjmatthew 28573759911cSjmatthew SIMPLEQ_INIT(&sc->sc_ccb_free); 28583759911cSjmatthew mtx_init(&sc->sc_ccb_mtx, IPL_BIO); 28593759911cSjmatthew mtx_init(&sc->sc_queue_mtx, IPL_BIO); 28603759911cSjmatthew mtx_init(&sc->sc_port_mtx, IPL_BIO); 28613759911cSjmatthew mtx_init(&sc->sc_mbox_mtx, IPL_BIO); 28623759911cSjmatthew 28639f6fb5c7Sderaadt sc->sc_ccbs = mallocarray(sc->sc_maxcmds, sizeof(struct qle_ccb), 28643759911cSjmatthew M_DEVBUF, M_WAITOK | M_CANFAIL | M_ZERO); 28653759911cSjmatthew if (sc->sc_ccbs == NULL) { 28663759911cSjmatthew printf("%s: unable to allocate ccbs\n", DEVNAME(sc)); 28673759911cSjmatthew return (1); 28683759911cSjmatthew } 28693759911cSjmatthew 28703759911cSjmatthew sc->sc_requests = qle_dmamem_alloc(sc, sc->sc_maxcmds * 28713759911cSjmatthew QLE_QUEUE_ENTRY_SIZE); 28723759911cSjmatthew if (sc->sc_requests == NULL) { 28733759911cSjmatthew printf("%s: unable to allocate ccb dmamem\n", DEVNAME(sc)); 28743759911cSjmatthew goto free_ccbs; 28753759911cSjmatthew } 28763759911cSjmatthew sc->sc_responses = qle_dmamem_alloc(sc, sc->sc_maxcmds * 28773759911cSjmatthew QLE_QUEUE_ENTRY_SIZE); 28783759911cSjmatthew if (sc->sc_responses == NULL) { 28793759911cSjmatthew printf("%s: unable to allocate rcb dmamem\n", DEVNAME(sc)); 28803759911cSjmatthew goto free_req; 28813759911cSjmatthew } 28823759911cSjmatthew sc->sc_pri_requests = qle_dmamem_alloc(sc, 8 * QLE_QUEUE_ENTRY_SIZE); 28833759911cSjmatthew if (sc->sc_pri_requests == NULL) { 28843759911cSjmatthew printf("%s: unable to allocate pri ccb dmamem\n", DEVNAME(sc)); 288593cb4b70Sdaniel goto free_res; 28863759911cSjmatthew } 28873759911cSjmatthew sc->sc_segments = qle_dmamem_alloc(sc, sc->sc_maxcmds * QLE_MAX_SEGS * 28883759911cSjmatthew sizeof(struct qle_iocb_seg)); 28893759911cSjmatthew if (sc->sc_segments == NULL) { 28903759911cSjmatthew printf("%s: unable to allocate iocb segments\n", DEVNAME(sc)); 289193cb4b70Sdaniel goto free_pri; 28923759911cSjmatthew } 28933759911cSjmatthew 28943759911cSjmatthew sc->sc_fcp_cmnds = qle_dmamem_alloc(sc, sc->sc_maxcmds * 28953759911cSjmatthew sizeof(struct qle_fcp_cmnd)); 28963759911cSjmatthew if (sc->sc_fcp_cmnds == NULL) { 28973759911cSjmatthew printf("%s: unable to allocate FCP_CMNDs\n", DEVNAME(sc)); 28983759911cSjmatthew goto free_seg; 28993759911cSjmatthew } 29003759911cSjmatthew 29013759911cSjmatthew cmd = QLE_DMA_KVA(sc->sc_requests); 29023759911cSjmatthew memset(cmd, 0, QLE_QUEUE_ENTRY_SIZE * sc->sc_maxcmds); 29033759911cSjmatthew for (i = 0; i < sc->sc_maxcmds; i++) { 29043759911cSjmatthew ccb = &sc->sc_ccbs[i]; 29053759911cSjmatthew 29063759911cSjmatthew if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, 290720c4379eSjmatthew QLE_MAX_SEGS-1, MAXPHYS, 0, 29083759911cSjmatthew BUS_DMA_NOWAIT | BUS_DMA_ALLOCNOW, 29093759911cSjmatthew &ccb->ccb_dmamap) != 0) { 29103759911cSjmatthew printf("%s: unable to create dma map\n", DEVNAME(sc)); 29113759911cSjmatthew goto free_maps; 29123759911cSjmatthew } 29133759911cSjmatthew 29143759911cSjmatthew ccb->ccb_sc = sc; 29153759911cSjmatthew ccb->ccb_id = i; 29163759911cSjmatthew 29173759911cSjmatthew ccb->ccb_seg_offset = i * QLE_MAX_SEGS * 29183759911cSjmatthew sizeof(struct qle_iocb_seg); 29193759911cSjmatthew ccb->ccb_segs = QLE_DMA_KVA(sc->sc_segments) + 29203759911cSjmatthew ccb->ccb_seg_offset; 29213759911cSjmatthew 29223759911cSjmatthew qle_put_ccb(sc, ccb); 29233759911cSjmatthew } 29243759911cSjmatthew 29253759911cSjmatthew scsi_iopool_init(&sc->sc_iopool, sc, qle_get_ccb, qle_put_ccb); 29263759911cSjmatthew return (0); 29273759911cSjmatthew 29283759911cSjmatthew free_maps: 29293759911cSjmatthew while ((ccb = qle_get_ccb(sc)) != NULL) 29303759911cSjmatthew bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); 29313759911cSjmatthew 29323759911cSjmatthew qle_dmamem_free(sc, sc->sc_fcp_cmnds); 29333759911cSjmatthew free_seg: 29343759911cSjmatthew qle_dmamem_free(sc, sc->sc_segments); 29353759911cSjmatthew free_pri: 29363759911cSjmatthew qle_dmamem_free(sc, sc->sc_pri_requests); 29373759911cSjmatthew free_res: 29383759911cSjmatthew qle_dmamem_free(sc, sc->sc_responses); 29393759911cSjmatthew free_req: 29403759911cSjmatthew qle_dmamem_free(sc, sc->sc_requests); 29413759911cSjmatthew free_ccbs: 2942df2ac69fStedu free(sc->sc_ccbs, M_DEVBUF, 0); 29433759911cSjmatthew 29443759911cSjmatthew return (1); 29453759911cSjmatthew } 29463759911cSjmatthew 29473759911cSjmatthew void 29483759911cSjmatthew qle_free_ccbs(struct qle_softc *sc) 29493759911cSjmatthew { 29503759911cSjmatthew struct qle_ccb *ccb; 29513759911cSjmatthew 29523759911cSjmatthew scsi_iopool_destroy(&sc->sc_iopool); 29533759911cSjmatthew while ((ccb = qle_get_ccb(sc)) != NULL) 29543759911cSjmatthew bus_dmamap_destroy(sc->sc_dmat, ccb->ccb_dmamap); 29553759911cSjmatthew qle_dmamem_free(sc, sc->sc_segments); 29563759911cSjmatthew qle_dmamem_free(sc, sc->sc_responses); 29573759911cSjmatthew qle_dmamem_free(sc, sc->sc_requests); 2958df2ac69fStedu free(sc->sc_ccbs, M_DEVBUF, 0); 29593759911cSjmatthew } 29603759911cSjmatthew 29613759911cSjmatthew void * 29623759911cSjmatthew qle_get_ccb(void *xsc) 29633759911cSjmatthew { 29643759911cSjmatthew struct qle_softc *sc = xsc; 29653759911cSjmatthew struct qle_ccb *ccb; 29663759911cSjmatthew 29673759911cSjmatthew mtx_enter(&sc->sc_ccb_mtx); 29683759911cSjmatthew ccb = SIMPLEQ_FIRST(&sc->sc_ccb_free); 29693759911cSjmatthew if (ccb != NULL) { 29703759911cSjmatthew SIMPLEQ_REMOVE_HEAD(&sc->sc_ccb_free, ccb_link); 29713759911cSjmatthew } 29723759911cSjmatthew mtx_leave(&sc->sc_ccb_mtx); 29733759911cSjmatthew return (ccb); 29743759911cSjmatthew } 29753759911cSjmatthew 29763759911cSjmatthew void 29773759911cSjmatthew qle_put_ccb(void *xsc, void *io) 29783759911cSjmatthew { 29793759911cSjmatthew struct qle_softc *sc = xsc; 29803759911cSjmatthew struct qle_ccb *ccb = io; 29813759911cSjmatthew 29823759911cSjmatthew ccb->ccb_xs = NULL; 29833759911cSjmatthew mtx_enter(&sc->sc_ccb_mtx); 29843759911cSjmatthew SIMPLEQ_INSERT_HEAD(&sc->sc_ccb_free, ccb, ccb_link); 29853759911cSjmatthew mtx_leave(&sc->sc_ccb_mtx); 29863759911cSjmatthew } 2987