/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END */ /* * Copyright 2009-2015 QLogic Corporation. All rights reserved. * Use is subject to license terms. */ /* * Copyright (c) 2009, 2015, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "qlt.h" #include "qlt_dma.h" #include "qlt_ioctl.h" #include "qlt_open.h" #include static int qlt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd); static int qlt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd); static uint8_t *qlt_vpd_findtag(qlt_state_t *qlt, uint8_t *vpdbuf, int8_t *opcode); static int qlt_vpd_lookup(qlt_state_t *qlt, uint8_t *opcode, uint8_t *bp, int32_t bplen); static void qlt_enable_intr(qlt_state_t *); static void qlt_disable_intr(qlt_state_t *); static fct_status_t qlt_reset_chip(qlt_state_t *qlt); static fct_status_t qlt_download_fw(qlt_state_t *qlt); static fct_status_t qlt_load_risc_ram(qlt_state_t *qlt, uint32_t *host_addr, uint32_t word_count, uint32_t risc_addr); static fct_status_t qlt_raw_mailbox_command(qlt_state_t *qlt); static mbox_cmd_t *qlt_alloc_mailbox_command(qlt_state_t *qlt, uint32_t dma_size); void qlt_free_mailbox_command(qlt_state_t *qlt, mbox_cmd_t *mcp); static fct_status_t qlt_mailbox_command(qlt_state_t *qlt, mbox_cmd_t *mcp); static uint_t qlt_isr(caddr_t arg, caddr_t arg2); static uint_t qlt_msix_resp_handler(caddr_t arg, caddr_t arg2); static uint_t qlt_msix_default_handler(caddr_t arg, caddr_t arg2); static fct_status_t qlt_firmware_dump(fct_local_port_t *port, stmf_state_change_info_t *ssci); static void qlt_handle_inot(qlt_state_t *qlt, uint8_t *inot); static void qlt_handle_purex(qlt_state_t *qlt, uint8_t *resp); static void qlt_handle_atio(qlt_state_t *qlt, uint8_t *atio); static void qlt_handle_ctio_completion(qlt_state_t *qlt, uint8_t *rsp, uint16_t qi); static void qlt_handle_sol_abort_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_handle_dereg_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_handle_unsol_els_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_handle_unsol_els_abort_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_handle_sol_els_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_handle_rcvd_abts(qlt_state_t *qlt, uint8_t *resp, uint16_t qi); static void qlt_handle_abts_completion(qlt_state_t *qlt, uint8_t *resp, uint16_t qi); static fct_status_t qlt_read_nvram(qlt_state_t *qlt); static fct_status_t qlt_read_vpd(qlt_state_t *qlt); static fct_status_t qlt_read_rom_image(qlt_state_t *qlt); static void qlt_verify_fw(qlt_state_t *qlt); static void qlt_handle_verify_fw_completion(qlt_state_t *qlt, uint8_t *rsp); fct_status_t qlt_port_start(caddr_t arg); fct_status_t qlt_port_stop(caddr_t arg); fct_status_t qlt_port_online(qlt_state_t *qlt); fct_status_t qlt_port_offline(qlt_state_t *qlt); static fct_status_t qlt_get_link_info(fct_local_port_t *port, fct_link_info_t *li); static void qlt_ctl(struct fct_local_port *port, int cmd, void *arg); static fct_status_t qlt_force_lip(qlt_state_t *); static fct_status_t qlt_do_flogi(struct fct_local_port *port, fct_flogi_xchg_t *fx); void qlt_handle_atio_queue_update(qlt_state_t *qlt); void qlt_handle_resp_queue_update(qlt_state_t *qlt, uint16_t qi); fct_status_t qlt_register_remote_port(fct_local_port_t *port, fct_remote_port_t *rp, fct_cmd_t *login); fct_status_t qlt_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp); fct_status_t qlt_send_cmd_response(fct_cmd_t *cmd, uint32_t ioflags); fct_status_t qlt_send_els_response(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_send_abts_response(qlt_state_t *qlt, fct_cmd_t *cmd, int terminate); static void qlt_handle_inot(qlt_state_t *qlt, uint8_t *inot); int qlt_set_uniq_flag(uint16_t *ptr, uint16_t setf, uint16_t abortf); fct_status_t qlt_abort_cmd(struct fct_local_port *port, fct_cmd_t *cmd, uint32_t flags); fct_status_t qlt_abort_sol_cmd(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_abort_purex(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_abort_unsol_scsi_cmd(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_send_cmd(fct_cmd_t *cmd); fct_status_t qlt_send_els(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_send_status(qlt_state_t *qlt, fct_cmd_t *cmd); fct_status_t qlt_xfer_scsi_data(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags); fct_status_t qlt_send_ct(qlt_state_t *qlt, fct_cmd_t *cmd); static void qlt_handle_ct_completion(qlt_state_t *qlt, uint8_t *rsp); static void qlt_release_intr(qlt_state_t *qlt); static int qlt_setup_interrupts(qlt_state_t *qlt); static void qlt_destroy_mutex(qlt_state_t *qlt); static fct_status_t qlt_read_risc_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words); static fct_status_t qlt_mbx_mpi_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words, uint16_t direction); static int qlt_dump_queue(qlt_state_t *qlt, caddr_t qadr, int entries, caddr_t buf, uint_t size_left); static int qlt_dump_risc_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words, caddr_t buf, uint_t size_left); static int qlt_fwdump_dump_regs(qlt_state_t *qlt, caddr_t buf, int startaddr, int count, uint_t size_left); static int qlt_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval); static int qlt_open(dev_t *devp, int flag, int otype, cred_t *credp); static int qlt_close(dev_t dev, int flag, int otype, cred_t *credp); static int qlt_setup_msi(qlt_state_t *qlt); static int qlt_setup_msix(qlt_state_t *qlt); static int qlt_el_trace_desc_ctor(qlt_state_t *qlt); static int qlt_el_trace_desc_dtor(qlt_state_t *qlt); static int qlt_read_int_prop(qlt_state_t *qlt, char *prop, int defval); static int qlt_read_string_prop(qlt_state_t *qlt, char *prop, char **prop_val); static int qlt_read_string_instance_prop(qlt_state_t *qlt, char *prop, char **prop_val); static int qlt_read_int_instance_prop(qlt_state_t *, char *, int); static int qlt_convert_string_to_ull(char *prop, int radix, u_longlong_t *result); static boolean_t qlt_wwn_overload_prop(qlt_state_t *qlt); static int qlt_quiesce(dev_info_t *dip); static void qlt_disable_intr(qlt_state_t *qlt); static fct_status_t qlt_raw_wrt_risc_ram_word(qlt_state_t *qlt, uint32_t, uint32_t); static fct_status_t qlt_raw_rd_risc_ram_word(qlt_state_t *qlt, uint32_t, uint32_t *); static void qlt_mps_reset(qlt_state_t *qlt); static void qlt_properties(qlt_state_t *qlt); static fct_status_t qlt_mq_create(qlt_state_t *qlt, int idx); static fct_status_t qlt_mq_destroy(qlt_state_t *qlt); static fct_status_t qlt_27xx_get_dmp_template(qlt_state_t *); static uint32_t qlt_27xx_dmp_parse_template(qlt_state_t *, qlt_dt_hdr_t *, uint8_t *, uint32_t); static int qlt_27xx_dump_ram(qlt_state_t *, uint16_t, uint32_t, uint32_t, uint8_t *); #define SETELSBIT(bmp, els) (bmp)[((els) >> 3) & 0x1F] = \ (uint8_t)((bmp)[((els) >> 3) & 0x1F] | ((uint8_t)1) << ((els) & 7)) int qlt_enable_msix = 1; int qlt_enable_msi = 1; string_table_t prop_status_tbl[] = DDI_PROP_STATUS(); /* Array to quickly calculate next free buf index to use */ #if 0 static int qlt_nfb[] = { 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0, 0xff }; #endif static struct cb_ops qlt_cb_ops = { qlt_open, qlt_close, nodev, nodev, nodev, nodev, nodev, qlt_ioctl, nodev, nodev, nodev, nochpoll, ddi_prop_op, 0, D_MP | D_NEW }; static struct dev_ops qlt_ops = { DEVO_REV, 0, nodev, nulldev, nulldev, qlt_attach, qlt_detach, nodev, &qlt_cb_ops, NULL, ddi_power, qlt_quiesce }; #ifndef PORT_SPEED_16G #define PORT_SPEED_16G 32 #endif #ifndef PORT_SPEED_32G #define PORT_SPEED_32G 64 #endif #ifndef QL_NAME #define QL_NAME "qlt" #endif static struct modldrv modldrv = { &mod_driverops, QLT_NAME" "QLT_VERSION, &qlt_ops, }; static struct modlinkage modlinkage = { MODREV_1, &modldrv, NULL }; void *qlt_state = NULL; kmutex_t qlt_global_lock; static uint32_t qlt_loaded_counter = 0; uint8_t qlt_reprocess_attempt_cnt = 5; uint32_t qlt_reprocess_delay = 75; /* default 75 microseconds */ static char *pci_speeds[] = { " 33", "-X Mode 1 66", "-X Mode 1 100", "-X Mode 1 133", "--Invalid--", "-X Mode 2 66", "-X Mode 2 100", "-X Mode 2 133", " 66" }; /* Always use 64 bit DMA. */ static ddi_dma_attr_t qlt_queue_dma_attr = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xffffffffffffffff, /* high DMA address range */ 0xffffffff, /* DMA counter register */ 64, /* DMA address alignment */ 0xff, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* segment boundary */ 1, /* s/g list length */ 1, /* granularity of device */ 0 /* DMA transfer flags */ }; /* Always use 64 bit DMA. */ static ddi_dma_attr_t qlt_queue_dma_attr_mq_req1 = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xffffffffffffffff, /* high DMA address range */ 0xffffffff, /* DMA counter register */ 64, /* DMA address alignment */ 0xff, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* segment boundary */ 1, /* s/g list length */ 1, /* granularity of device */ 0 /* DMA transfer flags */ }; /* Always use 64 bit DMA. */ static ddi_dma_attr_t qlt_queue_dma_attr_mq_rsp1 = { DMA_ATTR_V0, /* dma_attr_version */ 0, /* low DMA address range */ 0xffffffffffffffff, /* high DMA address range */ 0xffffffff, /* DMA counter register */ 64, /* DMA address alignment */ 0xff, /* DMA burstsizes */ 1, /* min effective DMA size */ 0xffffffff, /* max DMA xfer size */ 0xffffffff, /* segment boundary */ 1, /* s/g list length */ 1, /* granularity of device */ 0 /* DMA transfer flags */ }; /* qlogic logging */ int enable_extended_logging = 0; static char qlt_provider_name[] = "qlt"; static struct stmf_port_provider *qlt_pp; int _init(void) { int ret; ret = ddi_soft_state_init(&qlt_state, sizeof (qlt_state_t), 0); if (ret == 0) { mutex_init(&qlt_global_lock, 0, MUTEX_DRIVER, 0); qlt_pp = (stmf_port_provider_t *)stmf_alloc( STMF_STRUCT_PORT_PROVIDER, 0, 0); qlt_pp->pp_portif_rev = PORTIF_REV_1; qlt_pp->pp_name = qlt_provider_name; if (stmf_register_port_provider(qlt_pp) != STMF_SUCCESS) { stmf_free(qlt_pp); mutex_destroy(&qlt_global_lock); ddi_soft_state_fini(&qlt_state); return (EIO); } ret = mod_install(&modlinkage); if (ret != 0) { (void) stmf_deregister_port_provider(qlt_pp); stmf_free(qlt_pp); mutex_destroy(&qlt_global_lock); ddi_soft_state_fini(&qlt_state); } } return (ret); } int _fini(void) { int ret; if (qlt_loaded_counter) return (EBUSY); ret = mod_remove(&modlinkage); if (ret == 0) { (void) stmf_deregister_port_provider(qlt_pp); stmf_free(qlt_pp); mutex_destroy(&qlt_global_lock); ddi_soft_state_fini(&qlt_state); } return (ret); } int _info(struct modinfo *modinfop) { return (mod_info(&modlinkage, modinfop)); } static int qlt_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) { int instance; qlt_state_t *qlt; ddi_device_acc_attr_t dev_acc_attr; uint16_t did; uint16_t val; uint16_t mr; size_t discard; uint_t ncookies; int max_read_size; int max_payload_size; fct_status_t ret; /* No support for suspend resume yet */ if (cmd != DDI_ATTACH) return (DDI_FAILURE); instance = ddi_get_instance(dip); cmn_err(CE_CONT, "!Qlogic %s(%d) FCA Driver v%s\n", QLT_NAME, instance, QLT_VERSION); if (ddi_soft_state_zalloc(qlt_state, instance) != DDI_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): soft state alloc failed", instance); return (DDI_FAILURE); } if ((qlt = (qlt_state_t *)ddi_get_soft_state(qlt_state, instance)) == NULL) { cmn_err(CE_WARN, "qlt(%d): can't get soft state", instance); goto attach_fail_1; } qlt->instance = instance; qlt->nvram = (qlt_nvram_t *)kmem_zalloc(sizeof (qlt_nvram_t), KM_SLEEP); qlt->vpd = (uint32_t *)kmem_zalloc(QL_24XX_VPD_SIZE, KM_SLEEP); qlt->dip = dip; if (qlt_el_trace_desc_ctor(qlt) != DDI_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): can't setup el tracing", instance); goto attach_fail_2; } EL(qlt, "instance=%d, ptr=%p\n", instance, (void *)qlt); if (pci_config_setup(dip, &qlt->pcicfg_acc_handle) != DDI_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): pci_config_setup failed", instance); goto attach_fail_3; } did = PCICFG_RD16(qlt, PCI_CONF_DEVID); if ((did != 0x2422) && (did != 0x2432) && (did != 0x8432) && (did != 0x2532) && (did != 0x8001) && (did != 0x2031) && (did != 0x2071) && (did != 0x2261)) { cmn_err(CE_WARN, "qlt(%d): unknown devid(%x), failing attach", instance, did); goto attach_fail_4; } if ((did & 0xFFFF) == 0x2071) { qlt->qlt_27xx_chip = 1; qlt->qlt_fcoe_enabled = 0; } else if ((did & 0xFFFF) == 0x2261) { qlt->qlt_27xx_chip = 1; qlt->qlt_fcoe_enabled = 0; } else if ((did & 0xFFFF) == 0x2031) { qlt->qlt_83xx_chip = 1; qlt->qlt_fcoe_enabled = 0; } else if ((did & 0xFFF0) == 0x8000) { qlt->qlt_81xx_chip = 1; qlt->qlt_fcoe_enabled = 1; } else if ((did & 0xFF00) == 0x2500) qlt->qlt_25xx_chip = 1; dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { int stat_1; off_t regsize_1; stat_1 = ddi_dev_regsize(dip, 1, ®size_1); if (stat_1 != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "instance=%d, reg 1 regsize failed," " stat %x", instance, stat_1); goto attach_fail_4; } if (ddi_regs_map_setup(dip, 1, &qlt->regs, 0, regsize_1, &dev_acc_attr, &qlt->regs_acc_handle) != DDI_SUCCESS) { cmn_err(CE_NOTE, "qlt(%d) ddi_regs_map_setup failed\n", instance); goto attach_fail_4; } } else { /* * 24xx and 25xx: rnumber 0 is config space * rnumber 1 is for IO space * rnumber 2 is for MBAR0: ISP, MSIX, PBA */ if (ddi_regs_map_setup(dip, 2, &qlt->regs, 0, 0x100, &dev_acc_attr, &qlt->regs_acc_handle) != DDI_SUCCESS) { goto attach_fail_4; } } if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { uint32_t w32h; uint32_t w32l; int stat; off_t regsize; w32l = PCICFG_RD32(qlt, PCI_CONF_BASE2); w32h = PCICFG_RD32(qlt, PCI_CONF_BASE3); if ((w32h > 0) || w32l > 0) { stat = ddi_dev_regsize(dip, 2, ®size); if (stat != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "instance=%d, MSI-X regsize failed," " stat %x", instance, stat); } stmf_trace(qlt->qlt_port_alias, "instance=%d, MSI-X MEM Bar size %x", instance, regsize); stat = ddi_regs_map_setup(dip, 2, &qlt->msix_base, 0, /* ((MQ_MAX_QUEUES * 2) +1) << 2, */ regsize, &dev_acc_attr, &qlt->msix_acc_handle); if (stat != DDI_SUCCESS || qlt->msix_base == NULL || qlt->msix_acc_handle == NULL) { cmn_err(CE_WARN, "qlt(%d): can't map MBar for MSI-X", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, MSI-X MEM Bar map fail", instance); if (qlt->msix_acc_handle != NULL) { ddi_regs_map_free( &qlt->msix_acc_handle); } goto attach_fail_5; } } else { cmn_err(CE_WARN, "qlt(%d): can't setup MBar for MSI-X", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, No MSI-X MEM Bar", instance); goto attach_fail_5; } w32l = PCICFG_RD32(qlt, PCI_CONF_BASE4); w32h = PCICFG_RD32(qlt, PCI_CONF_BASE5); if ((w32h > 0) || w32l > 0) { stat = ddi_dev_regsize(dip, 3, ®size); if (stat != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "instance=%d, MQ regsize failed, stat %x", instance, stat); } stmf_trace(qlt->qlt_port_alias, "instance=%d, MQ MEM Bar size %x", instance, regsize); /* for 83xx the QP pointers are in the 3rd MBar */ stat = ddi_regs_map_setup(dip, 3, &qlt->mq_reg_base, 0, (MQ_MAX_QUEUES * MQBAR_REG_OFFSET), &dev_acc_attr, &qlt->mq_reg_acc_handle); if (stat != DDI_SUCCESS || qlt->mq_reg_base == NULL || qlt->mq_reg_acc_handle == NULL) { cmn_err(CE_WARN, "qlt(%d): can't map QP MBar", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, QP MEM Bar map fail st:%x", instance, stat); if (qlt->msix_acc_handle != NULL) { ddi_regs_map_free( &qlt->msix_acc_handle); } if (qlt->mq_reg_acc_handle != NULL) { ddi_regs_map_free( &qlt->mq_reg_acc_handle); } goto attach_fail_5; } else { qlt->qlt_mq_enabled = 1; } } else { cmn_err(CE_WARN, "qlt(%d): can't setup MBar for QPs", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, No QPs MEM Bar", instance); if (qlt->msix_acc_handle != NULL) { ddi_regs_map_free( &qlt->msix_acc_handle); } goto attach_fail_5; } } else if (qlt->qlt_81xx_chip) { uint32_t w32; int stat; w32 = PCICFG_RD32(qlt, PCI_CONF_BASE3); if (w32 == 0) { cmn_err(CE_WARN, "qlt(%d): can't setup MBar2", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, No MEM Bar2", instance); goto attach_fail_5; } stat = ddi_regs_map_setup(dip, 3, &qlt->mq_reg_base, 0, (MQ_MAX_QUEUES * MQBAR_REG_OFFSET), &dev_acc_attr, &qlt->mq_reg_acc_handle); if (stat != DDI_SUCCESS || qlt->mq_reg_base == NULL || qlt->mq_reg_acc_handle == NULL) { cmn_err(CE_WARN, "qlt(%d): can't map MBar2", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, MEM Bar2 map fail", instance); if (qlt->mq_reg_acc_handle != NULL) { ddi_regs_map_free(&qlt->mq_reg_acc_handle); } goto attach_fail_5; } else { qlt->qlt_mq_enabled = 1; } } else if (qlt->qlt_25xx_chip) { uint32_t w32h; uint32_t w32l; int stat; off_t regsize; /* MBAR2 rnumber 3 */ w32l = PCICFG_RD32(qlt, PCI_CONF_BASE3); w32h = PCICFG_RD32(qlt, PCI_CONF_BASE4); if ((w32h > 0) || (w32l > 0)) { stat = ddi_dev_regsize(dip, 3, ®size); if (stat != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "ISP25xx inst=%d, MQ regsize failed, stat %x", instance, stat); EL(qlt, "ISP25xx MQ regsize failed, stat %x\n", stat); } stmf_trace(qlt->qlt_port_alias, "ISP25xx instance=%d, MQ MEM Bar size %lx", instance, regsize); EL(qlt, "ISP25xx MQ MEM Bar (MBAR2) size: %x\n", regsize); stat = ddi_regs_map_setup(dip, 3, &qlt->mq_reg_base, 0, (MQ_MAX_QUEUES * MQBAR_REG_OFFSET), &dev_acc_attr, &qlt->mq_reg_acc_handle); if (stat != DDI_SUCCESS || qlt->mq_reg_base == NULL || qlt->mq_reg_acc_handle == NULL) { cmn_err(CE_WARN, "qlt(%d): ISP25xx can't map QP MBar", instance); stmf_trace(qlt->qlt_port_alias, "instance=%d, QP MEM Bar map fail st:%x", instance, stat); if (qlt->mq_reg_acc_handle != NULL) { ddi_regs_map_free( &qlt->mq_reg_acc_handle); } } else { qlt->qlt_mq_enabled = 1; } } else { stmf_trace(qlt->qlt_port_alias, "instance=%d, No QPs MEM Bar", instance); EL(qlt, "ISP25xx can't setup MBar QPs, use baseq\n"); } } if (qlt->qlt_mq_enabled) { qlt->mq_req = kmem_zalloc( ((sizeof (qlt_mq_req_ptr_blk_t)) * MQ_MAX_QUEUES), KM_SLEEP); qlt->mq_resp = kmem_zalloc( ((sizeof (qlt_mq_rsp_ptr_blk_t)) * MQ_MAX_QUEUES), KM_SLEEP); } else { qlt->mq_req = kmem_zalloc( (sizeof (qlt_mq_req_ptr_blk_t)), KM_SLEEP); qlt->mq_resp = kmem_zalloc( (sizeof (qlt_mq_rsp_ptr_blk_t)), KM_SLEEP); } if (did == 0x2422) { uint32_t pci_bits = REG_RD32(qlt, REG_CTRL_STATUS); uint32_t slot = pci_bits & PCI_64_BIT_SLOT; pci_bits >>= 8; pci_bits &= 0xf; if ((pci_bits == 3) || (pci_bits == 7)) { cmn_err(CE_NOTE, "!qlt(%d): HBA running at PCI%sMHz (%d)", instance, pci_speeds[pci_bits], pci_bits); } else { cmn_err(CE_WARN, "qlt(%d): HBA running at PCI%sMHz %s(%d)", instance, (pci_bits <= 8) ? pci_speeds[pci_bits] : "(Invalid)", ((pci_bits == 0) || (pci_bits == 8)) ? (slot ? "64 bit slot " : "32 bit slot ") : "", pci_bits); } } if ((ret = qlt_read_nvram(qlt)) != QLT_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): read nvram failure %llx", instance, (unsigned long long)ret); goto attach_fail_5; } if ((ret = qlt_read_vpd(qlt)) != QLT_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): read vpd failure %llx", instance, (unsigned long long)ret); goto attach_fail_5; } if ((ret = qlt_read_rom_image(qlt)) != QLT_SUCCESS) { cmn_err(CE_WARN, "qlt(%d): read rom image failure %llx", instance, (unsigned long long)ret); goto attach_fail_5; } qlt_properties(qlt); if (ddi_dma_alloc_handle(dip, &qlt_queue_dma_attr, DDI_DMA_SLEEP, 0, &qlt->queue_mem_dma_handle) != DDI_SUCCESS) { goto attach_fail_5; } if (ddi_dma_mem_alloc(qlt->queue_mem_dma_handle, TOTAL_DMA_MEM_SIZE, &dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->queue_mem_ptr, &discard, &qlt->queue_mem_acc_handle) != DDI_SUCCESS) { goto attach_fail_6; } if (ddi_dma_addr_bind_handle(qlt->queue_mem_dma_handle, NULL, qlt->queue_mem_ptr, TOTAL_DMA_MEM_SIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->queue_mem_cookie, &ncookies) != DDI_SUCCESS) { goto attach_fail_7; } if (ncookies != 1) goto attach_fail_8; /* * Base queue (0), alwasy available */ qlt->mq_req[0].queue_mem_mq_base_addr = qlt->mq_req[0].mq_ptr = qlt->queue_mem_ptr + REQUEST_QUEUE_OFFSET; qlt->mq_resp[0].queue_mem_mq_base_addr = qlt->mq_resp[0].mq_ptr = qlt->queue_mem_ptr + RESPONSE_QUEUE_OFFSET; qlt->preq_ptr = qlt->queue_mem_ptr + PRIORITY_QUEUE_OFFSET; qlt->atio_ptr = qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET; /* mutex are inited in this function */ if (qlt_setup_interrupts(qlt) != DDI_SUCCESS) goto attach_fail_8; qlt->qlt_queue_cnt = 1; if ((qlt->qlt_mq_enabled) && (qlt->intr_cnt > 1)) { int i; for (i = 1; i < qlt->intr_cnt; i++) { if (qlt_mq_create(qlt, i) != QLT_SUCCESS) { cmn_err(CE_WARN, "qlt(%d) mq create (%d) " "failed\n", qlt->instance, i); break; } qlt->qlt_queue_cnt++; if (qlt->qlt_queue_cnt >= MQ_MAX_QUEUES) break; } } EL(qlt, "Queue count = %d\n", qlt->qlt_queue_cnt); (void) snprintf(qlt->qlt_minor_name, sizeof (qlt->qlt_minor_name), "qlt%d", instance); (void) snprintf(qlt->qlt_port_alias, sizeof (qlt->qlt_port_alias), "%s,0", qlt->qlt_minor_name); if (ddi_create_minor_node(dip, qlt->qlt_minor_name, S_IFCHR, instance, DDI_NT_STMF_PP, 0) != DDI_SUCCESS) { goto attach_fail_9; } cv_init(&qlt->rp_dereg_cv, NULL, CV_DRIVER, NULL); cv_init(&qlt->mbox_cv, NULL, CV_DRIVER, NULL); mutex_init(&qlt->qlt_ioctl_lock, NULL, MUTEX_DRIVER, NULL); /* Setup PCI cfg space registers */ max_read_size = qlt_read_int_prop(qlt, "pci-max-read-request", 11); if (max_read_size == 11) goto over_max_read_xfer_setting; if (did == 0x2422) { if (max_read_size == 512) val = 0; else if (max_read_size == 1024) val = 1; else if (max_read_size == 2048) val = 2; else if (max_read_size == 4096) val = 3; else { cmn_err(CE_WARN, "qlt(%d) malformed " "pci-max-read-request in qlt.conf. Valid values " "for this HBA are 512/1024/2048/4096", instance); goto over_max_read_xfer_setting; } mr = (uint16_t)PCICFG_RD16(qlt, 0x4E); mr = (uint16_t)(mr & 0xfff3); mr = (uint16_t)(mr | (val << 2)); PCICFG_WR16(qlt, 0x4E, mr); } else if ((did == 0x2432) || (did == 0x8432) || (did == 0x2532) || (did == 0x8001) || (did == 0x2031) || (did == 0x2071) || (did == 0x2261)) { if (max_read_size == 128) val = 0; else if (max_read_size == 256) val = 1; else if (max_read_size == 512) val = 2; else if (max_read_size == 1024) val = 3; else if (max_read_size == 2048) val = 4; else if (max_read_size == 4096) val = 5; else { cmn_err(CE_WARN, "qlt(%d) malformed " "pci-max-read-request in qlt.conf. Valid values " "for this HBA are 128/256/512/1024/2048/4096", instance); goto over_max_read_xfer_setting; } mr = (uint16_t)PCICFG_RD16(qlt, 0x54); mr = (uint16_t)(mr & 0x8fff); mr = (uint16_t)(mr | (val << 12)); PCICFG_WR16(qlt, 0x54, mr); } else { cmn_err(CE_WARN, "qlt(%d): dont know how to set " "pci-max-read-request for this device (%x)", instance, did); } over_max_read_xfer_setting:; max_payload_size = qlt_read_int_prop(qlt, "pcie-max-payload-size", 11); if (max_payload_size == 11) goto over_max_payload_setting; if ((did == 0x2432) || (did == 0x8432) || (did == 0x2532) || (did == 0x8001) || (did == 0x2031) || (did == 0x2071) || (did == 0x2261)) { if (max_payload_size == 128) val = 0; else if (max_payload_size == 256) val = 1; else if (max_payload_size == 512) val = 2; else if (max_payload_size == 1024) val = 3; else { cmn_err(CE_WARN, "qlt(%d) malformed " "pcie-max-payload-size in qlt.conf. Valid values " "for this HBA are 128/256/512/1024", instance); goto over_max_payload_setting; } mr = (uint16_t)PCICFG_RD16(qlt, 0x54); mr = (uint16_t)(mr & 0xff1f); mr = (uint16_t)(mr | (val << 5)); PCICFG_WR16(qlt, 0x54, mr); } else { cmn_err(CE_WARN, "qlt(%d): dont know how to set " "pcie-max-payload-size for this device (%x)", instance, did); } over_max_payload_setting:; qlt_enable_intr(qlt); if (qlt_port_start((caddr_t)qlt) != QLT_SUCCESS) { EL(qlt, "qlt_port_start failed, tear down\n"); qlt_disable_intr(qlt); goto attach_fail_10; } ddi_report_dev(dip); return (DDI_SUCCESS); attach_fail_10:; mutex_destroy(&qlt->qlt_ioctl_lock); cv_destroy(&qlt->mbox_cv); cv_destroy(&qlt->rp_dereg_cv); ddi_remove_minor_node(dip, qlt->qlt_minor_name); attach_fail_9:; qlt_destroy_mutex(qlt); qlt_release_intr(qlt); (void) qlt_mq_destroy(qlt); attach_fail_8:; (void) ddi_dma_unbind_handle(qlt->queue_mem_dma_handle); attach_fail_7:; ddi_dma_mem_free(&qlt->queue_mem_acc_handle); attach_fail_6:; ddi_dma_free_handle(&qlt->queue_mem_dma_handle); attach_fail_5:; if (qlt->mq_resp) { kmem_free(qlt->mq_resp, (qlt->qlt_mq_enabled ? (sizeof (qlt_mq_rsp_ptr_blk_t) * MQ_MAX_QUEUES) : (sizeof (qlt_mq_rsp_ptr_blk_t)))); } qlt->mq_resp = NULL; if (qlt->mq_req) { kmem_free(qlt->mq_req, (qlt->qlt_mq_enabled ? (sizeof (qlt_mq_req_ptr_blk_t) * MQ_MAX_QUEUES) : (sizeof (qlt_mq_req_ptr_blk_t)))); } qlt->mq_req = NULL; ddi_regs_map_free(&qlt->regs_acc_handle); attach_fail_4:; pci_config_teardown(&qlt->pcicfg_acc_handle); attach_fail_3:; (void) qlt_el_trace_desc_dtor(qlt); attach_fail_2:; kmem_free(qlt->vpd, QL_24XX_VPD_SIZE); kmem_free(qlt->nvram, sizeof (qlt_nvram_t)); attach_fail_1:; ddi_soft_state_free(qlt_state, instance); return (DDI_FAILURE); } #define FCT_I_EVENT_BRING_PORT_OFFLINE 0x83 /* ARGSUSED */ static int qlt_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) { qlt_state_t *qlt; int instance; instance = ddi_get_instance(dip); if ((qlt = (qlt_state_t *)ddi_get_soft_state(qlt_state, instance)) == NULL) { return (DDI_FAILURE); } if (qlt->fw_code01) { return (DDI_FAILURE); } if ((qlt->qlt_state != FCT_STATE_OFFLINE) || qlt->qlt_state_not_acked) { return (DDI_FAILURE); } if (qlt_port_stop((caddr_t)qlt) != FCT_SUCCESS) { return (DDI_FAILURE); } qlt_disable_intr(qlt); if (qlt->dmp_template_addr != NULL) { (void) ddi_dma_unbind_handle(qlt->dmp_template_dma_handle); ddi_dma_mem_free(&qlt->dmp_template_acc_handle); ddi_dma_free_handle(&qlt->dmp_template_dma_handle); } if (qlt->fw_bin_dump_buf != NULL) { kmem_free(qlt->fw_bin_dump_buf, qlt->fw_bin_dump_size); qlt->fw_bin_dump_buf = NULL; qlt->fw_bin_dump_size = 0; qlt->fw_ascii_dump_size = 0; } if (qlt->qlt_fwdump_buf) { kmem_free(qlt->qlt_fwdump_buf, qlt->fw_dump_size); qlt->qlt_fwdump_buf = NULL; } ddi_remove_minor_node(dip, qlt->qlt_minor_name); qlt_destroy_mutex(qlt); qlt_release_intr(qlt); if (qlt->qlt_mq_enabled == 1) { (void) qlt_mq_destroy(qlt); } (void) ddi_dma_unbind_handle(qlt->queue_mem_dma_handle); ddi_dma_mem_free(&qlt->queue_mem_acc_handle); ddi_dma_free_handle(&qlt->queue_mem_dma_handle); ddi_regs_map_free(&qlt->regs_acc_handle); if (qlt->mq_resp) { kmem_free(qlt->mq_resp, (qlt->qlt_mq_enabled ? (sizeof (qlt_mq_rsp_ptr_blk_t) * MQ_MAX_QUEUES) : (sizeof (qlt_mq_rsp_ptr_blk_t)))); } qlt->mq_resp = NULL; if (qlt->mq_req) { kmem_free(qlt->mq_req, (qlt->qlt_mq_enabled ? (sizeof (qlt_mq_req_ptr_blk_t) * MQ_MAX_QUEUES) : (sizeof (qlt_mq_req_ptr_blk_t)))); } qlt->mq_req = NULL; if (qlt->qlt_mq_enabled == 1) { if ((qlt->msix_acc_handle != NULL) && ((qlt->qlt_83xx_chip == 1) || (qlt->qlt_27xx_chip == 1))) { ddi_regs_map_free(&qlt->msix_acc_handle); } ddi_regs_map_free(&qlt->mq_reg_acc_handle); } pci_config_teardown(&qlt->pcicfg_acc_handle); kmem_free(qlt->nvram, sizeof (qlt_nvram_t)); cv_destroy(&qlt->mbox_cv); cv_destroy(&qlt->rp_dereg_cv); (void) qlt_el_trace_desc_dtor(qlt); ddi_soft_state_free(qlt_state, instance); return (DDI_SUCCESS); } /* * qlt_quiesce quiesce a device attached to the system. */ static int qlt_quiesce(dev_info_t *dip) { qlt_state_t *qlt; uint32_t timer; uint32_t stat; qlt = ddi_get_soft_state(qlt_state, ddi_get_instance(dip)); if (qlt == NULL) { /* Oh well.... */ return (DDI_SUCCESS); } REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_HOST_TO_RISC_INTR)); REG_WR16(qlt, REG_MBOX0, MBC_STOP_FIRMWARE); REG_WR16(qlt, REG_MBOX(1), 0); REG_WR16(qlt, REG_MBOX(2), 0); REG_WR16(qlt, REG_MBOX(3), 0); REG_WR16(qlt, REG_MBOX(4), 0); REG_WR16(qlt, REG_MBOX(5), 0); REG_WR16(qlt, REG_MBOX(6), 0); REG_WR16(qlt, REG_MBOX(7), 0); REG_WR16(qlt, REG_MBOX(8), 0); REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_HOST_TO_RISC_INTR)); for (timer = 0; timer < 30000; timer++) { stat = REG_RD32(qlt, REG_RISC_STATUS); if (stat & RISC_HOST_INTR_REQUEST) { if ((stat & FW_INTR_STATUS_MASK) < 0x12) { REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); break; } REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } drv_usecwait(100); } /* need to ensure no one accesses the hw during the reset 100us */ if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { REG_WR32(qlt, REG_INTR_CTRL, 0); mutex_enter(&qlt->mbox_lock); if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_enter(&qlt->mq_req[i].mq_lock); } } mutex_enter(&qlt->mq_req[0].mq_lock); drv_usecwait(40); } /* Reset the chip. */ REG_WR32(qlt, REG_CTRL_STATUS, CHIP_SOFT_RESET | DMA_SHUTDOWN_CTRL | PCI_X_XFER_CTRL); drv_usecwait(100); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_exit(&qlt->mq_req[0].mq_lock); if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_exit(&qlt->mq_req[i].mq_lock); } } mutex_exit(&qlt->mbox_lock); } qlt_disable_intr(qlt); return (DDI_SUCCESS); } static void qlt_enable_intr(qlt_state_t *qlt) { if (qlt->intr_cap & DDI_INTR_FLAG_BLOCK) { int stat; stat = ddi_intr_block_enable(qlt->htable, qlt->intr_cnt); if (stat != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "qlt_enable_intr: ddi_intr_block_enable failed:%x", stat); cmn_err(CE_WARN, "!qlt(%d): qlt_enable_intr: " "ddi_intr_block_enable failed:%x", qlt->instance, stat); } #ifndef __sparc else { /* Please see CR6840537, MSI isn't re-enabled x86 */ off_t offset; uint8_t val8; ddi_intr_handle_impl_t *hdlp; if (qlt->qlt_81xx_chip || qlt->qlt_25xx_chip) { offset = (off_t)0x8a; } else { offset = (off_t)0x66; } hdlp = (ddi_intr_handle_impl_t *)qlt->htable[0]; if ((hdlp->ih_state == DDI_IHDL_STATE_ENABLE) && (hdlp->ih_type == DDI_INTR_TYPE_MSI)) { /* get MSI control */ val8 = pci_config_get8(qlt->pcicfg_acc_handle, offset); if ((val8 & 1) == 0) { stmf_trace(qlt->qlt_port_alias, "qlt(%d): qlt_enable_intr: " "MSI enable failed (%x)", qlt->instance, val8); /* write enable to MSI control */ val8 = (uint8_t)(val8 | 1); pci_config_put8(qlt->pcicfg_acc_handle, offset, val8); /* read back to veriy */ val8 = pci_config_get8 (qlt->pcicfg_acc_handle, offset); if (val8 & 1) { stmf_trace(qlt->qlt_port_alias, "qlt(%d): qlt_enable_intr: " "MSI enabled kludge!(%x)", qlt->instance, val8); } } } } #endif /* x86 specific hack */ } else { int i; int stat = DDI_SUCCESS; for (i = 0; ((i < qlt->intr_cnt) && (stat == DDI_SUCCESS)); i++) { stat = ddi_intr_enable(qlt->htable[i]); } if (stat != DDI_SUCCESS) { stmf_trace(qlt->qlt_port_alias, "qlt_enable_intr: ddi_intr_enable failed:%x", stat); cmn_err(CE_WARN, "!qlt(%d): qlt_enable_intr: " "ddi_intr_enable failed:%x", qlt->instance, stat); } } } static void qlt_disable_intr(qlt_state_t *qlt) { if (qlt->intr_cap & DDI_INTR_FLAG_BLOCK) { (void) ddi_intr_block_disable(qlt->htable, qlt->intr_cnt); } else { int i; for (i = 0; i < qlt->intr_cnt; i++) (void) ddi_intr_disable(qlt->htable[i]); } qlt->qlt_intr_enabled = 0; } static void qlt_release_intr(qlt_state_t *qlt) { if (qlt->htable) { int i; for (i = 0; i < qlt->intr_cnt; i++) { (void) ddi_intr_remove_handler(qlt->htable[i]); (void) ddi_intr_free(qlt->htable[i]); } kmem_free(qlt->htable, (uint_t)qlt->intr_size); } qlt->htable = NULL; qlt->intr_pri = 0; qlt->intr_cnt = 0; qlt->intr_size = 0; qlt->intr_cap = 0; } static void qlt_init_mutex(qlt_state_t *qlt) { if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < MQ_MAX_QUEUES; i++) { mutex_init(&qlt->mq_req[i].mq_lock, 0, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); mutex_init(&qlt->mq_resp[i].mq_lock, 0, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); } } mutex_init(&qlt->mq_req[0].mq_lock, 0, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); mutex_init(&qlt->preq_lock, 0, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); mutex_init(&qlt->mbox_lock, NULL, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); mutex_init(&qlt->intr_lock, NULL, MUTEX_DRIVER, INT2PTR(qlt->intr_pri, void *)); } static void qlt_destroy_mutex(qlt_state_t *qlt) { if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < MQ_MAX_QUEUES; i++) { mutex_destroy(&qlt->mq_req[i].mq_lock); mutex_destroy(&qlt->mq_resp[i].mq_lock); } } mutex_destroy(&qlt->mq_req[0].mq_lock); mutex_destroy(&qlt->preq_lock); mutex_destroy(&qlt->mbox_lock); mutex_destroy(&qlt->intr_lock); } static int qlt_setup_msix(qlt_state_t *qlt) { int count, avail, actual; int ret; int itype = DDI_INTR_TYPE_MSIX; int i; /* check 24xx revision */ if ((!qlt->qlt_25xx_chip) && (!qlt->qlt_81xx_chip) && (!qlt->qlt_83xx_chip) && (!qlt->qlt_27xx_chip)) { uint8_t rev_id; rev_id = (uint8_t) pci_config_get8(qlt->pcicfg_acc_handle, PCI_CONF_REVID); if (rev_id < 3) { return (DDI_FAILURE); } } ret = ddi_intr_get_nintrs(qlt->dip, itype, &count); if (ret != DDI_SUCCESS || count == 0) { EL(qlt, "ddi_intr_get_nintrs status=%xh, count=%d\n", ret, count); return (DDI_FAILURE); } ret = ddi_intr_get_navail(qlt->dip, itype, &avail); if (ret != DDI_SUCCESS || avail == 0) { EL(qlt, "ddi_intr_get_navail status=%xh, avail=%d\n", ret, avail); return (DDI_FAILURE); } if (avail < count) { stmf_trace(qlt->qlt_port_alias, "qlt_setup_msix: nintrs=%d,avail=%d", count, avail); } if ((qlt->qlt_25xx_chip) && (qlt->qlt_mq_enabled == 0)) { count = 2; } qlt->intr_size = (int)(count * (int)sizeof (ddi_intr_handle_t)); qlt->htable = kmem_zalloc((uint_t)qlt->intr_size, KM_SLEEP); ret = ddi_intr_alloc(qlt->dip, qlt->htable, itype, DDI_INTR_ALLOC_NORMAL, count, &actual, 0); EL(qlt, "qlt_setup_msix: count=%d,avail=%d,actual=%d\n", count, avail, actual); /* we need at least 2 interrupt vectors */ if (((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) && (ret != DDI_SUCCESS || actual < 2)) { EL(qlt, "ddi_intr_alloc status=%xh, actual=%d\n", ret, actual); ret = DDI_FAILURE; goto release_intr; } else if ((qlt->qlt_81xx_chip) && (ret != DDI_SUCCESS || actual < 3)) { EL(qlt, "ddi_intr_alloc status=%xh, actual=%d\n", ret, actual); ret = DDI_FAILURE; goto release_intr; } else if (ret != DDI_SUCCESS || actual < 2) { EL(qlt, "ddi_intr_alloc status=%xh, actual=%d\n", ret, actual); ret = DDI_FAILURE; goto release_intr; } if (actual < count) { EL(qlt, "requested: %d, received: %d\n", count, actual); } qlt->intr_cnt = actual; ret = ddi_intr_get_pri(qlt->htable[0], &qlt->intr_pri); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_get_pri status=%xh\n", ret); ret = DDI_FAILURE; goto release_intr; } qlt_init_mutex(qlt); for (i = 0; i < qlt->intr_cnt; i++) { ret = ddi_intr_add_handler(qlt->htable[i], (i != 0) ? qlt_msix_resp_handler : qlt_msix_default_handler, qlt, INT2PTR((uint_t)i, void *)); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_add_handler status=%xh\n", ret); goto release_mutex; } } (void) ddi_intr_get_cap(qlt->htable[0], &qlt->intr_cap); qlt->intr_flags |= QLT_INTR_MSIX; return (DDI_SUCCESS); release_mutex: qlt_destroy_mutex(qlt); release_intr: for (i = 0; i < actual; i++) (void) ddi_intr_free(qlt->htable[i]); kmem_free(qlt->htable, (uint_t)qlt->intr_size); qlt->htable = NULL; qlt_release_intr(qlt); return (ret); } static int qlt_setup_msi(qlt_state_t *qlt) { int count, avail, actual; int itype = DDI_INTR_TYPE_MSI; int ret; int i; /* 83xx and 27xx doesn't do MSI - don't even bother? */ if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { return (DDI_FAILURE); } /* get the # of interrupts */ ret = ddi_intr_get_nintrs(qlt->dip, itype, &count); if (ret != DDI_SUCCESS || count == 0) { EL(qlt, "ddi_intr_get_nintrs status=%xh, count=%d\n", ret, count); return (DDI_FAILURE); } ret = ddi_intr_get_navail(qlt->dip, itype, &avail); if (ret != DDI_SUCCESS || avail == 0) { EL(qlt, "ddi_intr_get_navail status=%xh, avail=%d\n", ret, avail); return (DDI_FAILURE); } if (avail < count) { EL(qlt, "nintrs=%d, avail=%d\n", count, avail); } /* MSI requires only 1 interrupt. */ count = 1; /* allocate interrupt */ qlt->intr_size = (int)(count * (int)sizeof (ddi_intr_handle_t)); qlt->htable = kmem_zalloc((uint_t)qlt->intr_size, KM_SLEEP); ret = ddi_intr_alloc(qlt->dip, qlt->htable, itype, 0, count, &actual, DDI_INTR_ALLOC_NORMAL); if (ret != DDI_SUCCESS || actual == 0) { EL(qlt, "ddi_intr_alloc status=%xh, actual=%d\n", ret, actual); ret = DDI_FAILURE; goto free_mem; } if (actual < count) { EL(qlt, "requested: %d, received: %d\n", count, actual); } qlt->intr_cnt = actual; /* * Get priority for first msi, assume remaining are all the same. */ ret = ddi_intr_get_pri(qlt->htable[0], &qlt->intr_pri); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_get_pri status=%xh\n", ret); ret = DDI_FAILURE; goto release_intr; } qlt_init_mutex(qlt); /* add handler */ for (i = 0; i < actual; i++) { ret = ddi_intr_add_handler(qlt->htable[i], qlt_isr, qlt, INT2PTR((uint_t)i, void *)); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_add_handler status=%xh\n", ret); goto release_mutex; } } (void) ddi_intr_get_cap(qlt->htable[0], &qlt->intr_cap); qlt->intr_flags |= QLT_INTR_MSI; return (DDI_SUCCESS); release_mutex: qlt_destroy_mutex(qlt); release_intr: for (i = 0; i < actual; i++) (void) ddi_intr_free(qlt->htable[i]); free_mem: kmem_free(qlt->htable, (uint_t)qlt->intr_size); qlt->htable = NULL; qlt_release_intr(qlt); return (ret); } static int qlt_setup_fixed(qlt_state_t *qlt) { int count; int actual; int ret; int itype = DDI_INTR_TYPE_FIXED; ret = ddi_intr_get_nintrs(qlt->dip, itype, &count); /* Fixed interrupts can only have one interrupt. */ if (ret != DDI_SUCCESS || count != 1) { EL(qlt, "ddi_intr_get_nintrs status=%xh, count=%d\n", ret, count); return (DDI_FAILURE); } qlt->intr_size = sizeof (ddi_intr_handle_t); qlt->htable = kmem_zalloc((uint_t)qlt->intr_size, KM_SLEEP); ret = ddi_intr_alloc(qlt->dip, qlt->htable, itype, DDI_INTR_ALLOC_NORMAL, count, &actual, 0); if (ret != DDI_SUCCESS || actual != 1) { EL(qlt, "ddi_intr_alloc status=%xh, actual=%d\n", ret, actual); ret = DDI_FAILURE; goto free_mem; } qlt->intr_cnt = actual; ret = ddi_intr_get_pri(qlt->htable[0], &qlt->intr_pri); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_get_pri status=%xh\n", ret); ret = DDI_FAILURE; goto release_intr; } qlt_init_mutex(qlt); ret = ddi_intr_add_handler(qlt->htable[0], qlt_isr, qlt, 0); if (ret != DDI_SUCCESS) { EL(qlt, "ddi_intr_add_handler status=%xh\n", ret); goto release_mutex; } qlt->intr_flags |= QLT_INTR_FIXED; return (DDI_SUCCESS); release_mutex: qlt_destroy_mutex(qlt); release_intr: (void) ddi_intr_free(qlt->htable[0]); free_mem: kmem_free(qlt->htable, (uint_t)qlt->intr_size); qlt->htable = NULL; qlt_release_intr(qlt); return (ret); } static int qlt_setup_interrupts(qlt_state_t *qlt) { int itypes = 0; /* * x86 has a bug in the ddi_intr_block_enable/disable area (6562198). */ #ifndef __sparc if ((qlt_enable_msi != 0) || (qlt_enable_msix != 0)) { #endif if (ddi_intr_get_supported_types(qlt->dip, &itypes) != DDI_SUCCESS) { itypes = DDI_INTR_TYPE_FIXED; } if (qlt_enable_msix && (itypes & DDI_INTR_TYPE_MSIX)) { if (qlt_setup_msix(qlt) == DDI_SUCCESS) return (DDI_SUCCESS); } if (qlt_enable_msi && (itypes & DDI_INTR_TYPE_MSI)) { if (qlt_setup_msi(qlt) == DDI_SUCCESS) return (DDI_SUCCESS); } #ifndef __sparc } #endif return (qlt_setup_fixed(qlt)); } static uint8_t * qlt_vpd_findtag(qlt_state_t *qlt, uint8_t *vpdbuf, int8_t *opcode) { uint8_t *vpd = vpdbuf; uint8_t *end = vpdbuf + QL_24XX_VPD_SIZE; uint32_t found = 0; if (vpdbuf == NULL || opcode == NULL) { EL(qlt, "null parameter passed!\n"); return (NULL); } while (vpd < end) { if (vpd[0] == VPD_TAG_END) { if (opcode[0] == VPD_TAG_END) { found = 1; } else { found = 0; } break; } if (bcmp(opcode, vpd, strlen(opcode)) == 0) { found = 1; break; } if (!(strncmp((char *)vpd, (char *)VPD_TAG_PRODID, 1))) { vpd += (vpd[2] << 8) + vpd[1] + 3; } else if (*vpd == VPD_TAG_LRT || *vpd == VPD_TAG_LRTC) { vpd += 3; } else { vpd += vpd[2] +3; } } return (found == 1 ? vpd : NULL); } /* * qlt_vpd_lookup * Return the VPD data for the request VPD tag * * Input: * qlt = adapter state pointer. * opcode = VPD opcode to find (must be NULL terminated). * bp = Pointer to returned data buffer. * bplen = Length of returned data buffer. * * Returns: * Length of data copied into returned data buffer. * >0 = VPD data field (NULL terminated) * 0 = no data. * -1 = Could not find opcode in vpd buffer / error. * * Context: * Kernel context. * * NB: The opcode buffer and the bp buffer *could* be the same buffer! * */ static int qlt_vpd_lookup(qlt_state_t *qlt, uint8_t *opcode, uint8_t *bp, int32_t bplen) { uint8_t *vpd = NULL; uint8_t *vpdbuf = NULL; int32_t len = -1; if (opcode == NULL || bp == NULL || bplen < 1) { EL(qlt, "invalid parameter passed: opcode=%ph, " "bp=%ph, bplen=%xh\n", opcode, bp, bplen); return (len); } vpdbuf = (uint8_t *)qlt->vpd; if ((vpd = qlt_vpd_findtag(qlt, vpdbuf, (int8_t *)opcode)) != NULL) { /* * Found the tag */ if (*opcode == VPD_TAG_END || *opcode == VPD_TAG_LRT || *opcode == VPD_TAG_LRTC) { /* * We found it, but the tag doesn't have a data * field. */ len = 0; } else if (!(strncmp((char *)vpd, (char *) VPD_TAG_PRODID, 1))) { len = vpd[2] << 8; len += vpd[1]; } else { len = vpd[2]; } /* * Make sure that the vpd len does not exceed the * vpd end */ if (vpd+len > vpdbuf + QL_24XX_VPD_SIZE) { EL(qlt, "vpd tag len (%xh) exceeds vpd buffer " "length\n", len); len = -1; } } else { EL(qlt, "Cna't find vpd tag \n"); return (-1); } if (len >= 0) { /* * make sure we don't exceed callers buffer space len */ if (len > bplen) { len = bplen - 1; } /* copy the data back */ (void) strncpy((int8_t *)bp, (int8_t *)(vpd+3), (int64_t)len); bp[len] = '\0'; } else { /* error -- couldn't find tag */ bp[0] = '\0'; if (opcode[1] != '\0') { EL(qlt, "unable to find tag '%s'\n", opcode); } else { EL(qlt, "unable to find tag '%xh'\n", opcode[0]); } } return (len); } void qlt_get_rom_version(qlt_state_t *qlt, caddr_t orv) { int i; char bios0_str[32]; char fcode_str[32]; char efi_str[32]; char hppa_str[32]; char tmp[80]; uint32_t bios_cnt = 0; uint32_t fcode_cnt = 0; boolean_t last_image = FALSE; /* collect right rom_version from image[] */ i = 0; do { if (qlt->rimage[0].header.signature[0] != PCI_HEADER0) { break; } if (qlt->rimage[i].data.codetype == PCI_CODE_X86PC) { /* BIOS */ if (bios_cnt == 0) { (void) snprintf(bios0_str, 32, "%d.%02d", qlt->rimage[i].data. revisionlevel[1], qlt->rimage[i].data. revisionlevel[0]); (void) snprintf(tmp, 80, " BIOS: %s;", bios0_str); (void) strcat(orv, tmp); } bios_cnt++; } else if (qlt->rimage[i].data.codetype == PCI_CODE_FCODE) { /* FCode */ if (fcode_cnt == 0) { (void) snprintf(fcode_str, 32, "%d.%02d", qlt->rimage[i].data.revisionlevel[1], qlt->rimage[i].data.revisionlevel[0]); (void) snprintf(tmp, 80, " FCode: %s;", fcode_str); (void) strcat(orv, tmp); } fcode_cnt++; } else if (qlt->rimage[i].data.codetype == PCI_CODE_EFI) { /* EFI */ (void) snprintf(efi_str, 32, "%d.%02d", qlt->rimage[i].data.revisionlevel[1], qlt->rimage[i].data.revisionlevel[0]); (void) snprintf(tmp, 80, " EFI: %s;", efi_str); (void) strcat(orv, tmp); } else if (qlt->rimage[i].data.codetype == PCI_CODE_HPPA) { /* HPPA */ (void) snprintf(hppa_str, 32, "%d.%02d", qlt->rimage[i].data.revisionlevel[1], qlt->rimage[i].data.revisionlevel[0]); (void) snprintf(orv, 80, " HPPA: %s;", hppa_str); (void) strcat(orv, tmp); } else if (qlt->rimage[i].data.codetype == PCI_CODE_FW) { EL(qlt, "fw infor skip\n"); } else { /* Unknown */ EL(qlt, "unknown image\n"); break; } if (qlt->rimage[i].data.indicator == PCI_IND_LAST_IMAGE) { last_image = TRUE; break; } i ++; } while ((last_image != TRUE) && (i < 6)); if (last_image != TRUE) { /* No boot image detected */ (void) snprintf(orv, FCHBA_OPTION_ROM_VERSION_LEN, "%s", "No boot image detected"); } } /* * Filling the hba attributes */ void qlt_populate_hba_fru_details(struct fct_local_port *port, struct fct_port_attrs *port_attrs) { int len; qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; (void) snprintf(port_attrs->manufacturer, FCHBA_MANUFACTURER_LEN, "QLogic Corp."); (void) snprintf(port_attrs->driver_name, FCHBA_DRIVER_NAME_LEN, "%s", QLT_NAME); (void) snprintf(port_attrs->driver_version, FCHBA_DRIVER_VERSION_LEN, "%s", QLT_VERSION); /* get serial_number from vpd data */ if (qlt_vpd_lookup(qlt, (uint8_t *)VPD_TAG_SN, (uint8_t *) port_attrs->serial_number, FCHBA_SERIAL_NUMBER_LEN) == -1) { port_attrs->serial_number[0] = '\0'; } port_attrs->hardware_version[0] = '\0'; (void) snprintf(port_attrs->firmware_version, FCHBA_FIRMWARE_VERSION_LEN, "%d.%d.%d", qlt->fw_major, qlt->fw_minor, qlt->fw_subminor); /* Get FCode version */ qlt_get_rom_version(qlt, (caddr_t)&port_attrs->option_rom_version[0]); port_attrs->vendor_specific_id = qlt->nvram->subsystem_vendor_id[0] | qlt->nvram->subsystem_vendor_id[1] << 8; port_attrs->max_frame_size = qlt->nvram->max_frame_length[1] << 8 | qlt->nvram->max_frame_length[0]; port_attrs->supported_cos = 0x10000000; if (qlt->qlt_fcoe_enabled) { port_attrs->supported_speed = PORT_SPEED_10G; } else if (qlt->qlt_27xx_chip) { if ((qlt->qlt_27xx_speed & MAX_SPEED_MASK) == MAX_SPEED_32G) { port_attrs->supported_speed = PORT_SPEED_8G | PORT_SPEED_16G | PORT_SPEED_32G; } else { port_attrs->supported_speed = PORT_SPEED_4G | PORT_SPEED_8G | PORT_SPEED_16G; } } else if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { port_attrs->supported_speed = PORT_SPEED_4G | PORT_SPEED_8G | PORT_SPEED_16G; } else if (qlt->qlt_25xx_chip) { port_attrs->supported_speed = PORT_SPEED_2G | PORT_SPEED_4G | PORT_SPEED_8G; } else { port_attrs->supported_speed = PORT_SPEED_1G | PORT_SPEED_2G | PORT_SPEED_4G; } /* limit string length to nvr model_name length */ len = ((qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) ? 16 : 8; (void) snprintf(port_attrs->model, (uint_t)(len < FCHBA_MODEL_LEN ? len : FCHBA_MODEL_LEN), "%s", qlt->nvram->model_name); (void) snprintf(port_attrs->model_description, (uint_t)(len < FCHBA_MODEL_DESCRIPTION_LEN ? len : FCHBA_MODEL_DESCRIPTION_LEN), "%s", qlt->nvram->model_name); } /* ARGSUSED */ fct_status_t qlt_info(uint32_t cmd, fct_local_port_t *port, void *arg, uint8_t *buf, uint32_t *bufsizep) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; mbox_cmd_t *mcp; fct_status_t ret = FCT_SUCCESS; uint8_t *p; fct_port_link_status_t *link_status; switch (cmd) { case FC_TGT_PORT_RLS: if (qlt->qlt_state != FCT_STATE_ONLINE) { break; } if ((*bufsizep) < sizeof (fct_port_link_status_t)) { EL(qlt, "FC_TGT_PORT_RLS bufsizep=%xh < " "fct_port_link_status_t=%xh\n", *bufsizep, sizeof (fct_port_link_status_t)); ret = FCT_FAILURE; break; } /* send mailbox command to get link status */ mcp = qlt_alloc_mailbox_command(qlt, 156); if (mcp == NULL) { EL(qlt, "qlt_alloc_mailbox_command mcp=null\n"); ret = FCT_ALLOC_FAILURE; break; } /* GET LINK STATUS count */ mcp->to_fw[0] = MBC_GET_STATUS_COUNTS; mcp->to_fw[8] = 156/4; mcp->to_fw_mask |= BIT_1 | BIT_8; mcp->from_fw_mask |= BIT_1 | BIT_2; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=6dh status=%llxh\n", ret); qlt_free_mailbox_command(qlt, mcp); break; } qlt_dmem_dma_sync(mcp->dbuf, DDI_DMA_SYNC_FORCPU); p = mcp->dbuf->db_sglist[0].seg_addr; link_status = (fct_port_link_status_t *)buf; link_status->LinkFailureCount = LE_32(*((uint32_t *)p)); link_status->LossOfSyncCount = LE_32(*((uint32_t *)(p + 4))); link_status->LossOfSignalsCount = LE_32(*((uint32_t *)(p + 8))); link_status->PrimitiveSeqProtocolErrorCount = LE_32(*((uint32_t *)(p + 12))); link_status->InvalidTransmissionWordCount = LE_32(*((uint32_t *)(p + 16))); link_status->InvalidCRCCount = LE_32(*((uint32_t *)(p + 20))); qlt_free_mailbox_command(qlt, mcp); break; default: EL(qlt, "Unknown cmd=%xh\n", cmd); ret = FCT_FAILURE; break; } return (ret); } fct_status_t qlt_port_start(caddr_t arg) { qlt_state_t *qlt = (qlt_state_t *)arg; fct_local_port_t *port; fct_dbuf_store_t *fds; fct_status_t ret; if (qlt_dmem_init(qlt) != QLT_SUCCESS) { return (FCT_FAILURE); } /* Initialize the ddi_dma_handle free pool */ qlt_dma_handle_pool_init(qlt); port = (fct_local_port_t *)fct_alloc(FCT_STRUCT_LOCAL_PORT, 0, 0); if (port == NULL) { goto qlt_pstart_fail_1; } fds = (fct_dbuf_store_t *)fct_alloc(FCT_STRUCT_DBUF_STORE, 0, 0); if (fds == NULL) { goto qlt_pstart_fail_2; } qlt->qlt_port = port; fds->fds_alloc_data_buf = qlt_dmem_alloc; fds->fds_free_data_buf = qlt_dmem_free; fds->fds_setup_dbuf = qlt_dma_setup_dbuf; fds->fds_teardown_dbuf = qlt_dma_teardown_dbuf; fds->fds_max_sgl_xfer_len = QLT_DMA_SG_LIST_LENGTH * MMU_PAGESIZE; fds->fds_copy_threshold = (uint32_t)MMU_PAGESIZE; fds->fds_fca_private = (void *)qlt; /* * Since we keep everything in the state struct and dont allocate any * port private area, just use that pointer to point to the * state struct. */ port->port_fca_private = qlt; port->port_fca_abort_timeout = 5 * 1000; /* 5 seconds */ bcopy(qlt->nvram->node_name, port->port_nwwn, 8); bcopy(qlt->nvram->port_name, port->port_pwwn, 8); fct_wwn_to_str(port->port_nwwn_str, port->port_nwwn); fct_wwn_to_str(port->port_pwwn_str, port->port_pwwn); port->port_default_alias = qlt->qlt_port_alias; port->port_pp = qlt_pp; port->port_fds = fds; port->port_max_logins = QLT_MAX_LOGINS; port->port_max_xchges = QLT_MAX_XCHGES; port->port_fca_fcp_cmd_size = sizeof (qlt_cmd_t); port->port_fca_rp_private_size = sizeof (qlt_remote_port_t); port->port_fca_sol_els_private_size = sizeof (qlt_cmd_t); port->port_fca_sol_ct_private_size = sizeof (qlt_cmd_t); port->port_get_link_info = qlt_get_link_info; port->port_register_remote_port = qlt_register_remote_port; port->port_deregister_remote_port = qlt_deregister_remote_port; port->port_send_cmd = qlt_send_cmd; port->port_xfer_scsi_data = qlt_xfer_scsi_data; port->port_send_cmd_response = qlt_send_cmd_response; port->port_abort_cmd = qlt_abort_cmd; port->port_ctl = qlt_ctl; port->port_flogi_xchg = qlt_do_flogi; port->port_populate_hba_details = qlt_populate_hba_fru_details; port->port_info = qlt_info; port->port_fca_version = FCT_FCA_MODREV_1; if ((ret = fct_register_local_port(port)) != FCT_SUCCESS) { EL(qlt, "fct_register_local_port status=%llxh\n", ret); goto qlt_pstart_fail_2_5; } EL(qlt, "Qlogic qlt(%d) " "WWPN=%02x%02x%02x%02x%02x%02x%02x%02x:" "WWNN=%02x%02x%02x%02x%02x%02x%02x%02x\n", qlt->instance, qlt->nvram->port_name[0], qlt->nvram->port_name[1], qlt->nvram->port_name[2], qlt->nvram->port_name[3], qlt->nvram->port_name[4], qlt->nvram->port_name[5], qlt->nvram->port_name[6], qlt->nvram->port_name[7], qlt->nvram->node_name[0], qlt->nvram->node_name[1], qlt->nvram->node_name[2], qlt->nvram->node_name[3], qlt->nvram->node_name[4], qlt->nvram->node_name[5], qlt->nvram->node_name[6], qlt->nvram->node_name[7]); return (QLT_SUCCESS); #if 0 qlt_pstart_fail_3: (void) fct_deregister_local_port(port); #endif qlt_pstart_fail_2_5: fct_free(fds); qlt_pstart_fail_2: fct_free(port); qlt->qlt_port = NULL; qlt_pstart_fail_1: qlt_dma_handle_pool_fini(qlt); qlt_dmem_fini(qlt); return (QLT_FAILURE); } fct_status_t qlt_port_stop(caddr_t arg) { qlt_state_t *qlt = (qlt_state_t *)arg; fct_status_t ret; if ((ret = fct_deregister_local_port(qlt->qlt_port)) != FCT_SUCCESS) { EL(qlt, "fct_register_local_port status=%llxh\n", ret); return (QLT_FAILURE); } fct_free(qlt->qlt_port->port_fds); fct_free(qlt->qlt_port); qlt_dma_handle_pool_fini(qlt); qlt->qlt_port = NULL; qlt_dmem_fini(qlt); return (QLT_SUCCESS); } /* * Called by framework to init the HBA. * Can be called in the middle of I/O. (Why ??) * Should make sure sane state both before and after the initialization */ fct_status_t qlt_port_online(qlt_state_t *qlt) { uint64_t da; int instance, i, j; fct_status_t ret; uint16_t rcount; caddr_t icb; mbox_cmd_t *mcp; uint8_t *elsbmp; instance = ddi_get_instance(qlt->dip); /* XXX Make sure a sane state */ if ((ret = qlt_download_fw(qlt)) != QLT_SUCCESS) { cmn_err(CE_NOTE, "qlt(%d): reset chip failed %llx", qlt->instance, (long long)ret); return (ret); } bzero(qlt->queue_mem_ptr, TOTAL_DMA_MEM_SIZE); /* Get resource count */ REG_WR16(qlt, REG_MBOX(0), MBC_GET_RESOURCE_COUNTS); ret = qlt_raw_mailbox_command(qlt); rcount = REG_RD16(qlt, REG_MBOX(3)); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=42h status=%llxh\n", ret); return (ret); } /* Enable PUREX */ REG_WR16(qlt, REG_MBOX(0), MBC_SET_ADDITIONAL_FIRMWARE_OPT); REG_WR16(qlt, REG_MBOX(1), OPT_PUREX_ENABLE); REG_WR16(qlt, REG_MBOX(2), 0x0); REG_WR16(qlt, REG_MBOX(3), 0x0); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=38h status=%llxh\n", ret); cmn_err(CE_NOTE, "Enable PUREX failed"); return (ret); } /* Pass ELS bitmap to fw */ REG_WR16(qlt, REG_MBOX(0), MBC_SET_PARAMETERS); REG_WR16(qlt, REG_MBOX(1), PARAM_TYPE(PUREX_ELS_CMDS)); elsbmp = (uint8_t *)qlt->queue_mem_ptr + MBOX_DMA_MEM_OFFSET; bzero(elsbmp, 32); da = qlt->queue_mem_cookie.dmac_laddress; da += MBOX_DMA_MEM_OFFSET; REG_WR16(qlt, REG_MBOX(3), LSW(LSD(da))); REG_WR16(qlt, REG_MBOX(2), MSW(LSD(da))); REG_WR16(qlt, REG_MBOX(7), LSW(MSD(da))); REG_WR16(qlt, REG_MBOX(6), MSW(MSD(da))); SETELSBIT(elsbmp, ELS_OP_PLOGI); SETELSBIT(elsbmp, ELS_OP_LOGO); SETELSBIT(elsbmp, ELS_OP_ABTX); /* SETELSBIT(elsbmp, ELS_OP_ECHO); till fct handles it */ SETELSBIT(elsbmp, ELS_OP_PRLI); SETELSBIT(elsbmp, ELS_OP_PRLO); SETELSBIT(elsbmp, ELS_OP_SCN); SETELSBIT(elsbmp, ELS_OP_TPRLO); SETELSBIT(elsbmp, ELS_OP_PDISC); SETELSBIT(elsbmp, ELS_OP_ADISC); SETELSBIT(elsbmp, ELS_OP_RSCN); SETELSBIT(elsbmp, ELS_OP_RNID); (void) ddi_dma_sync(qlt->queue_mem_dma_handle, MBOX_DMA_MEM_OFFSET, 32, DDI_DMA_SYNC_FORDEV); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=59h status=llxh\n", ret); cmn_err(CE_NOTE, "Set ELS Bitmap failed ret=%llx, " "elsbmp0=%x elabmp1=%x", (long long)ret, elsbmp[0], elsbmp[1]); return (ret); } /* Init queue pointers */ if (qlt->qlt_mq_enabled == 1) { uint16_t qi; for (qi = 0; qi < MQ_MAX_QUEUES; qi++) { MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_IN, 0); MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_OUT, 0); MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN, 0); MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_OUT, 0); } } else { REG_WR32(qlt, REG_REQ_IN_PTR, 0); REG_WR32(qlt, REG_REQ_OUT_PTR, 0); REG_WR32(qlt, REG_RESP_IN_PTR, 0); REG_WR32(qlt, REG_RESP_OUT_PTR, 0); } if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { REG_WR32(qlt, REG_PREQ_IN_PTR, 0); REG_WR32(qlt, REG_PREQ_OUT_PTR, 0); REG_WR32(qlt, REG_ATIO_IN_PTR, 0); REG_WR32(qlt, REG_ATIO_OUT_PTR, 0); } qlt->mq_req[0].mq_ndx_to_fw = qlt->mq_req[0].mq_ndx_from_fw = 0; qlt->mq_req[0].mq_available = REQUEST_QUEUE_ENTRIES - 1; if (qlt->qlt_mq_enabled == 1) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { qlt->mq_req[i].mq_ndx_to_fw = 0; qlt->mq_req[i].mq_ndx_from_fw = 0; qlt->mq_req[i].mq_available = REQUEST_QUEUE_MQ_ENTRIES - 1; } } qlt->mq_resp[0].mq_ndx_to_fw = qlt->mq_resp[0].mq_ndx_from_fw = 0; if (qlt->qlt_mq_enabled == 1) { caddr_t resp; for (i = 1; i < qlt->qlt_queue_cnt; i++) { qlt->mq_resp[i].mq_ndx_to_fw = 0; qlt->mq_resp[i].mq_ndx_from_fw = 0; for (j = 0; j < RESPONSE_QUEUE_MQ_ENTRIES; j++) { resp = &qlt->mq_resp[i].mq_ptr[j << 6]; QMEM_WR32_RSPQ(qlt, i, resp+0x3c, 0xdeadbeef); } } } for (i = 0; i < ATIO_QUEUE_ENTRIES; i++) { caddr_t atio; atio = &qlt->atio_ptr[i << 6]; QMEM_WR32(qlt, atio+0x3c, 0xdeadbeef); } qlt->preq_ndx_to_fw = qlt->preq_ndx_from_fw = 0; qlt->atio_ndx_to_fw = qlt->atio_ndx_from_fw = 0; /* * XXX support for tunables. Also should we cache icb ? */ if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_27xx_chip) || ((qlt->qlt_25xx_chip) && (qlt->qlt_mq_enabled))) { /* * allocate extra 64 bytes for Extended init control block, * with separation to allow for a minimal MID section. */ mcp = qlt_alloc_mailbox_command(qlt, 0xE0); } else { mcp = qlt_alloc_mailbox_command(qlt, 0x80); } if (mcp == NULL) { EL(qlt, "qlt_alloc_mailbox_command mcp=null\n"); return (STMF_ALLOC_FAILURE); } icb = (caddr_t)mcp->dbuf->db_sglist[0].seg_addr; if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_27xx_chip) || ((qlt->qlt_25xx_chip) && (qlt->qlt_mq_enabled))) { bzero(icb, 0xE0); } else { bzero(icb, 0x80); } da = qlt->queue_mem_cookie.dmac_laddress; DMEM_WR16(qlt, icb, 1); /* Version */ DMEM_WR16(qlt, icb+4, 2112); /* Max frame length */ DMEM_WR16(qlt, icb+6, 16); /* Execution throttle */ DMEM_WR16(qlt, icb+8, rcount); /* Xchg count */ DMEM_WR16(qlt, icb+0x0a, 0x00); /* Hard address (not used) */ bcopy(qlt->qlt_port->port_pwwn, icb+0x0c, 8); bcopy(qlt->qlt_port->port_nwwn, icb+0x14, 8); DMEM_WR16(qlt, icb+0x20, 3); /* Login retry count */ DMEM_WR16(qlt, icb+0x24, RESPONSE_QUEUE_ENTRIES); DMEM_WR16(qlt, icb+0x26, REQUEST_QUEUE_ENTRIES); if ((!qlt->qlt_83xx_chip) && (!qlt->qlt_81xx_chip) && (!qlt->qlt_27xx_chip)) { DMEM_WR16(qlt, icb+0x28, 100); /* ms of NOS/OLS for Link down */ } if ((!qlt->qlt_83xx_chip) || (!qlt->qlt_27xx_chip)) { DMEM_WR16(qlt, icb+0x2a, PRIORITY_QUEUE_ENTRIES); } DMEM_WR64(qlt, icb+0x2c, (da+REQUEST_QUEUE_OFFSET)); DMEM_WR64(qlt, icb+0x34, (da+RESPONSE_QUEUE_OFFSET)); if ((!qlt->qlt_83xx_chip) || (!qlt->qlt_27xx_chip)) { DMEM_WR64(qlt, icb+0x3c, (da+PRIORITY_QUEUE_OFFSET)); } /* XXX: all hba model atio/resp 0 use vector 0 */ DMEM_WR16(qlt, icb+0x4e, ATIO_QUEUE_ENTRIES); DMEM_WR64(qlt, icb+0x50, (da+ATIO_QUEUE_OFFSET)); DMEM_WR16(qlt, icb+0x58, 2); /* Interrupt delay Timer */ DMEM_WR16(qlt, icb+0x5a, 4); /* Login timeout (secs) */ if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_27xx_chip) || ((qlt->qlt_25xx_chip) && (qlt->qlt_mq_enabled))) { qlt_nvram_81xx_t *qlt81nvr = (qlt_nvram_81xx_t *)qlt->nvram; /* fw options 1 */ if (qlt->qlt_fcoe_enabled) { DMEM_WR32(qlt, icb+0x5c, BIT_5 | BIT_4); } else { DMEM_WR32(qlt, icb+0x5c, BIT_11 | BIT_5 | BIT_4 | BIT_2 | BIT_1 | BIT_0); } /* fw options 2 */ if (qlt->qlt_mq_enabled) { if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { if (qlt->qlt_fcoe_enabled) { DMEM_WR32(qlt, icb+0x60, BIT_26 | BIT_23 | BIT_22); } else { DMEM_WR32(qlt, icb+0x60, BIT_26 | BIT_23 | BIT_22 | BIT_5); } } else { DMEM_WR32(qlt, icb+0x60, BIT_26 | BIT_23 | BIT_22 | BIT_5); } } /* fw options 3 */ if (qlt->qlt_fcoe_enabled) { DMEM_WR32(qlt, icb+0x64, BIT_4); } else { DMEM_WR32(qlt, icb+0x64, BIT_14 | BIT_8 | BIT_7 | BIT_4); } if (qlt->qlt_mq_enabled) { DMEM_WR16(qlt, icb+0x68, 5); /* QoS priority = 5 */ } DMEM_WR32(qlt, icb+0x70, qlt81nvr->enode_mac[0] | (qlt81nvr->enode_mac[1] << 8) | (qlt81nvr->enode_mac[2] << 16) | (qlt81nvr->enode_mac[3] << 24)); DMEM_WR16(qlt, icb+0x74, qlt81nvr->enode_mac[4] | (qlt81nvr->enode_mac[5] << 8)); } else { DMEM_WR32(qlt, icb+0x5c, BIT_11 | BIT_5 | BIT_4 | BIT_2 | BIT_1 | BIT_0); DMEM_WR32(qlt, icb+0x60, BIT_5); DMEM_WR32(qlt, icb+0x64, BIT_14 | BIT_8 | BIT_7 | BIT_4); /* null MID setup */ DMEM_WR16(qlt, icb+0x80, 1); /* VP count 1 */ } if (qlt->qlt_fcoe_enabled) { qlt_dmem_bctl_t *bctl; uint32_t index; caddr_t src; caddr_t dst; qlt_nvram_81xx_t *qlt81nvr; dst = icb+0xA0; qlt81nvr = (qlt_nvram_81xx_t *)qlt->nvram; src = (caddr_t)&qlt81nvr->ext_blk; index = sizeof (qlt_ext_icb_81xx_t); /* Use defaults for cases where we find nothing in NVR */ if ((qlt->qlt_83xx_chip) || (*src == 0)) { if (*src == 0) { EL(qlt, "nvram eicb=null\n"); cmn_err(CE_NOTE, "qlt(%d) NVR eicb is zeroed", instance); } qlt81nvr->ext_blk.version[0] = 1; /* * not yet, for !FIP firmware at least * * qlt81nvr->ext_blk.fcf_vlan_match = 0x81; */ #ifdef _LITTLE_ENDIAN qlt81nvr->ext_blk.fcf_vlan_id[0] = 0xEA; qlt81nvr->ext_blk.fcf_vlan_id[1] = 0x03; #else qlt81nvr->ext_blk.fcf_vlan_id[1] = 0xEA; qlt81nvr->ext_blk.fcf_vlan_id[0] = 0x03; #endif } while (index--) { *dst++ = *src++; } bctl = (qlt_dmem_bctl_t *)mcp->dbuf->db_port_private; da = bctl->bctl_dev_addr + 0xA0; /* base addr of eicb (phys) */ mcp->to_fw[11] = LSW(LSD(da)); mcp->to_fw[10] = MSW(LSD(da)); mcp->to_fw[13] = LSW(MSD(da)); mcp->to_fw[12] = MSW(MSD(da)); mcp->to_fw[14] = (uint16_t)(sizeof (qlt_ext_icb_81xx_t) & 0xffff); /* eicb enable */ mcp->to_fw[1] = (uint16_t)(mcp->to_fw[1] | BIT_0); mcp->to_fw_mask |= BIT_14 | BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_1; } qlt_dmem_dma_sync(mcp->dbuf, DDI_DMA_SYNC_FORDEV); if (((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_27xx_chip) || ((qlt->qlt_25xx_chip) && (qlt->qlt_mq_enabled))) && (qlt->fw_attr & BIT_6)) { mcp->to_fw[0] = MBC_INITIALIZE_MULTI_ID_FW; } else { mcp->to_fw[0] = MBC_INITIALIZE_FIRMWARE; } /* * This is the 1st command after adapter initialize which will * use interrupts and regular mailbox interface. */ qlt->qlt_intr_enabled = 1; qlt->mbox_io_state = MBOX_STATE_READY; REG_WR32(qlt, REG_INTR_CTRL, ENABLE_RISC_INTR); /* Issue mailbox to firmware */ ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=48h/60h status=%llxh\n", ret); cmn_err(CE_NOTE, "qlt(%d) init fw failed %llx, intr status %x", instance, (long long)ret, REG_RD32(qlt, REG_INTR_STATUS)); qlt_free_mailbox_command(qlt, mcp); return (ret); } mcp->to_fw_mask = BIT_0; mcp->from_fw_mask = BIT_0 | BIT_1; mcp->to_fw[0] = 0x28; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=28h status=%llxh\n", ret); cmn_err(CE_NOTE, "qlt(%d) get_fw_options %llx", instance, (long long)ret); qlt_free_mailbox_command(qlt, mcp); return (ret); } if (qlt->qlt_mq_enabled == 1) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { da = qlt->mq_resp[i].queue_mem_mq_cookie.dmac_laddress; mcp->to_fw_mask = BIT_14 | BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0; mcp->from_fw_mask = BIT_0 | BIT_1; /* msix vector setup */ mcp->to_fw[14] = (uint16_t)(i); mcp->to_fw[13] = 0; mcp->to_fw[12] = 0; mcp->to_fw[11] = 0; mcp->to_fw[10] = 0; mcp->to_fw[9] = 0; mcp->to_fw[8] = 0; mcp->to_fw[7] = LSW(MSD(da)); mcp->to_fw[6] = MSW(MSD(da)); mcp->to_fw[5] = RESPONSE_QUEUE_MQ_ENTRIES; mcp->to_fw[4] = (uint16_t)(i); mcp->to_fw[3] = LSW(LSD(da)); mcp->to_fw[2] = MSW(LSD(da)); mcp->to_fw[1] = BIT_6 | BIT_1; mcp->to_fw[0] = 0x1F; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=1fh status=%llxh\n", ret); cmn_err(CE_NOTE, "qlt(%d) queue manage %llx", instance, (long long)ret); qlt_free_mailbox_command(qlt, mcp); return (ret); } da = qlt->mq_req[i].queue_mem_mq_cookie.dmac_laddress; mcp->to_fw_mask = BIT_14 | BIT_13 | BIT_12 | BIT_11 | BIT_10 | BIT_9 | BIT_8 | BIT_7 | BIT_6 | BIT_5 | BIT_4 | BIT_3 | BIT_2 | BIT_1 | BIT_0; mcp->from_fw_mask = BIT_0 | BIT_1; /* * msix vector does not apply for request queue create */ mcp->to_fw[14] = 2; mcp->to_fw[13] = 0; mcp->to_fw[12] = 4; mcp->to_fw[11] = 0; mcp->to_fw[10] = (uint16_t)(i); mcp->to_fw[9] = 0; mcp->to_fw[8] = 0; mcp->to_fw[7] = LSW(MSD(da)); mcp->to_fw[6] = MSW(MSD(da)); mcp->to_fw[5] = REQUEST_QUEUE_MQ_ENTRIES; mcp->to_fw[4] = (uint16_t)(i); mcp->to_fw[3] = LSW(LSD(da)); mcp->to_fw[2] = MSW(LSD(da)); mcp->to_fw[1] = BIT_6; mcp->to_fw[0] = 0x1F; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=1fh status=%llxh\n", ret); cmn_err(CE_NOTE, "qlt(%d) queue manage %llx", instance, (long long)ret); qlt_free_mailbox_command(qlt, mcp); return (ret); } } } /* * Report FW versions for 81xx - MPI rev is useful */ /* if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip)) { */ if (qlt->qlt_fcoe_enabled) { mcp->to_fw_mask = BIT_0; mcp->from_fw_mask = BIT_11 | BIT_10 | BIT_6 | BIT_3 | BIT_2 | BIT_1 | BIT_0; mcp->to_fw[0] = MBC_ABOUT_FIRMWARE; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "about fw failed: %llx\n", (long long)ret); } else { EL(qlt, "Firmware version %d.%d.%d, MPI: %d.%d.%d\n", mcp->from_fw[1], mcp->from_fw[2], mcp->from_fw[3], mcp->from_fw[10] & 0xff, mcp->from_fw[11] >> 8, mcp->from_fw[11] & 0xff); EL(qlt, "Firmware Attributes %x[h]\n", mcp->from_fw[6]); } } qlt_free_mailbox_command(qlt, mcp); for (i = 0; i < 5; i++) { qlt->qlt_bufref[i] = 0; } qlt->qlt_bumpbucket = 0; qlt->qlt_pmintry = 0; qlt->qlt_pmin_ok = 0; if (ret != QLT_SUCCESS) return (ret); return (FCT_SUCCESS); } fct_status_t qlt_port_offline(qlt_state_t *qlt) { int retries; int i; mutex_enter(&qlt->mbox_lock); if (qlt->mbox_io_state == MBOX_STATE_UNKNOWN) { mutex_exit(&qlt->mbox_lock); goto poff_mbox_done; } /* Wait to grab the mailboxes */ for (retries = 0; qlt->mbox_io_state != MBOX_STATE_READY; retries++) { cv_wait(&qlt->mbox_cv, &qlt->mbox_lock); if ((retries > 5) || (qlt->mbox_io_state == MBOX_STATE_UNKNOWN)) { qlt->mbox_io_state = MBOX_STATE_UNKNOWN; mutex_exit(&qlt->mbox_lock); goto poff_mbox_done; } } qlt->mbox_io_state = MBOX_STATE_UNKNOWN; mutex_exit(&qlt->mbox_lock); poff_mbox_done:; qlt->intr_sneak_counter = 10; mutex_enter(&qlt->intr_lock); if (qlt->qlt_mq_enabled == 1) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_enter(&qlt->mq_resp[i].mq_lock); } } (void) qlt_reset_chip(qlt); drv_usecwait(20); qlt->intr_sneak_counter = 0; if (qlt->qlt_mq_enabled == 1) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_exit(&qlt->mq_resp[i].mq_lock); } } mutex_exit(&qlt->intr_lock); return (FCT_SUCCESS); } static fct_status_t qlt_get_link_info(fct_local_port_t *port, fct_link_info_t *li) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; mbox_cmd_t *mcp; fct_status_t fc_ret; fct_status_t ret; clock_t et; et = ddi_get_lbolt() + drv_usectohz(5000000); mcp = qlt_alloc_mailbox_command(qlt, 0); link_info_retry: mcp->to_fw[0] = MBC_GET_ID; mcp->to_fw[9] = 0; mcp->to_fw_mask |= BIT_0 | BIT_9; mcp->from_fw_mask |= BIT_0 | BIT_1 | BIT_2 | BIT_3 | BIT_6 | BIT_7; /* Issue mailbox to firmware */ ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=20h status=%llxh\n", ret); if ((mcp->from_fw[0] == 0x4005) && ((mcp->from_fw[1] == 7) || (mcp->from_fw[1] == 0x1b))) { /* Firmware is not ready */ if (ddi_get_lbolt() < et) { delay(drv_usectohz(50000)); goto link_info_retry; } } EL(qlt, "GET ID mbox failed, ret=%llx mb0=%x mb1=%x", ret, mcp->from_fw[0], mcp->from_fw[1]); stmf_trace(qlt->qlt_port_alias, "GET ID mbox failed, ret=%llx " "mb0=%x mb1=%x", ret, mcp->from_fw[0], mcp->from_fw[1]); fc_ret = FCT_FAILURE; } else { li->portid = ((uint32_t)(mcp->from_fw[2])) | (((uint32_t)(mcp->from_fw[3])) << 16); li->port_speed = qlt->link_speed; switch (mcp->from_fw[6]) { case 1: li->port_topology = PORT_TOPOLOGY_PUBLIC_LOOP; li->port_fca_flogi_done = 1; break; case 0: li->port_topology = PORT_TOPOLOGY_PRIVATE_LOOP; li->port_no_fct_flogi = 1; break; case 3: li->port_topology = PORT_TOPOLOGY_FABRIC_PT_TO_PT; li->port_fca_flogi_done = 1; break; case 2: /*FALLTHROUGH*/ case 4: li->port_topology = PORT_TOPOLOGY_PT_TO_PT; li->port_fca_flogi_done = 1; break; default: li->port_topology = PORT_TOPOLOGY_UNKNOWN; EL(qlt, "Unknown topology=%xh\n", mcp->from_fw[6]); } qlt->cur_topology = li->port_topology; fc_ret = FCT_SUCCESS; EL(qlt, "MBC_GET_ID done, Topology=%x, portid=%xh, " "port speed=%xh\n", li->port_topology, li->portid, li->port_speed); } qlt_free_mailbox_command(qlt, mcp); if ((fc_ret == FCT_SUCCESS) && (li->port_fca_flogi_done)) { mcp = qlt_alloc_mailbox_command(qlt, 64); mcp->to_fw[0] = MBC_GET_PORT_DATABASE; mcp->to_fw[1] = 0x7FE; mcp->to_fw[9] = 0; mcp->to_fw[10] = 0; mcp->to_fw_mask |= BIT_0 | BIT_1 | BIT_9 | BIT_10; fc_ret = qlt_mailbox_command(qlt, mcp); if (fc_ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=64h status=%llxh\n", fc_ret); stmf_trace(qlt->qlt_port_alias, "Attempt to get port " "database for F_port failed, ret = %llx", fc_ret); } else { uint8_t *p; qlt_dmem_dma_sync(mcp->dbuf, DDI_DMA_SYNC_FORCPU); p = mcp->dbuf->db_sglist[0].seg_addr; bcopy(p + 0x18, li->port_rpwwn, 8); bcopy(p + 0x20, li->port_rnwwn, 8); EL(qlt, "qlt_mbox_command=64h, GET_PORT_DATABASE " "complete\n"); } qlt_free_mailbox_command(qlt, mcp); } return (fc_ret); } static int qlt_open(dev_t *devp, int flag, int otype, cred_t *credp) { int instance; qlt_state_t *qlt; if (otype != OTYP_CHR) { return (EINVAL); } /* * Since this is for debugging only, only allow root to issue ioctl now */ if (drv_priv(credp)) { return (EPERM); } instance = (int)getminor(*devp); qlt = ddi_get_soft_state(qlt_state, instance); if (qlt == NULL) { return (ENXIO); } mutex_enter(&qlt->qlt_ioctl_lock); if (qlt->qlt_ioctl_flags & QLT_IOCTL_FLAG_EXCL) { /* * It is already open for exclusive access. * So shut the door on this caller. */ mutex_exit(&qlt->qlt_ioctl_lock); return (EBUSY); } if (flag & FEXCL) { if (qlt->qlt_ioctl_flags & QLT_IOCTL_FLAG_OPEN) { /* * Exclusive operation not possible * as it is already opened */ mutex_exit(&qlt->qlt_ioctl_lock); return (EBUSY); } qlt->qlt_ioctl_flags |= QLT_IOCTL_FLAG_EXCL; } qlt->qlt_ioctl_flags |= QLT_IOCTL_FLAG_OPEN; mutex_exit(&qlt->qlt_ioctl_lock); return (0); } /* ARGSUSED */ static int qlt_close(dev_t dev, int flag, int otype, cred_t *credp) { int instance; qlt_state_t *qlt; if (otype != OTYP_CHR) { return (EINVAL); } instance = (int)getminor(dev); qlt = ddi_get_soft_state(qlt_state, instance); if (qlt == NULL) { return (ENXIO); } mutex_enter(&qlt->qlt_ioctl_lock); if ((qlt->qlt_ioctl_flags & QLT_IOCTL_FLAG_OPEN) == 0) { mutex_exit(&qlt->qlt_ioctl_lock); return (ENODEV); } /* * It looks there's one hole here, maybe there could several concurrent * shareed open session, but we never check this case. * But it will not hurt too much, disregard it now. */ qlt->qlt_ioctl_flags &= ~QLT_IOCTL_FLAG_MASK; mutex_exit(&qlt->qlt_ioctl_lock); return (0); } /* * All of these ioctls are unstable interfaces which are meant to be used * in a controlled lab env. No formal testing will be (or needs to be) done * for these ioctls. Specially note that running with an additional * uploaded firmware is not supported and is provided here for test * purposes only. */ /* ARGSUSED */ static int qlt_ioctl(dev_t dev, int cmd, intptr_t data, int mode, cred_t *credp, int *rval) { qlt_state_t *qlt; int ret = 0; #ifdef _LITTLE_ENDIAN int i; #endif stmf_iocdata_t *iocd; void *ibuf = NULL; void *obuf = NULL; uint32_t *intp; qlt_fw_info_t *fwi; mbox_cmd_t *mcp; fct_status_t st; char info[80]; fct_status_t ret2; if (drv_priv(credp) != 0) return (EPERM); qlt = ddi_get_soft_state(qlt_state, (int32_t)getminor(dev)); ret = stmf_copyin_iocdata(data, mode, &iocd, &ibuf, &obuf); if (ret) return (ret); iocd->stmf_error = 0; switch (cmd) { case QLT_IOCTL_FETCH_FWDUMP: if (iocd->stmf_obuf_size < QLT_FWDUMP_BUFSIZE) { EL(qlt, "FETCH_FWDUMP obuf_size=%d < %d\n", iocd->stmf_obuf_size, QLT_FWDUMP_BUFSIZE); ret = EINVAL; break; } mutex_enter(&qlt->qlt_ioctl_lock); if (!(qlt->qlt_ioctl_flags & QLT_FWDUMP_ISVALID)) { mutex_exit(&qlt->qlt_ioctl_lock); ret = ENODATA; EL(qlt, "no fwdump\n"); iocd->stmf_error = QLTIO_NO_DUMP; break; } if (qlt->qlt_ioctl_flags & QLT_FWDUMP_INPROGRESS) { mutex_exit(&qlt->qlt_ioctl_lock); ret = EBUSY; EL(qlt, "fwdump inprogress\n"); iocd->stmf_error = QLTIO_DUMP_INPROGRESS; break; } if (qlt->qlt_ioctl_flags & QLT_FWDUMP_FETCHED_BY_USER) { mutex_exit(&qlt->qlt_ioctl_lock); ret = EEXIST; EL(qlt, "fwdump already fetched\n"); iocd->stmf_error = QLTIO_ALREADY_FETCHED; break; } bcopy(qlt->qlt_fwdump_buf, obuf, QLT_FWDUMP_BUFSIZE); qlt->qlt_ioctl_flags |= QLT_FWDUMP_FETCHED_BY_USER; mutex_exit(&qlt->qlt_ioctl_lock); break; case QLT_IOCTL_TRIGGER_FWDUMP: if (qlt->qlt_state != FCT_STATE_ONLINE) { ret = EACCES; iocd->stmf_error = QLTIO_NOT_ONLINE; break; } (void) snprintf(info, 80, "qlt_ioctl: qlt-%p, " "user triggered FWDUMP with RFLAG_RESET", (void *)qlt); info[79] = 0; if ((ret2 = fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_USER_REQUEST | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info)) != FCT_SUCCESS) { EL(qlt, "TRIGGER_FWDUMP fct_port_shutdown status=" "%llxh\n", ret2); ret = EIO; } break; case QLT_IOCTL_UPLOAD_FW: if ((iocd->stmf_ibuf_size < 1024) || (iocd->stmf_ibuf_size & 3)) { EL(qlt, "UPLOAD_FW ibuf_size=%d < 1024\n", iocd->stmf_ibuf_size); ret = EINVAL; iocd->stmf_error = QLTIO_INVALID_FW_SIZE; break; } intp = (uint32_t *)ibuf; #ifdef _LITTLE_ENDIAN for (i = 0; (i << 2) < iocd->stmf_ibuf_size; i++) { intp[i] = BSWAP_32(intp[i]); } #endif if (((intp[3] << 2) >= iocd->stmf_ibuf_size) || (((intp[intp[3] + 3] + intp[3]) << 2) != iocd->stmf_ibuf_size)) { EL(qlt, "UPLOAD_FW fw_size=%d >= %d\n", intp[3] << 2, iocd->stmf_ibuf_size); ret = EINVAL; iocd->stmf_error = QLTIO_INVALID_FW_SIZE; break; } if ((qlt->qlt_81xx_chip && ((intp[8] & 8) == 0)) || (qlt->qlt_25xx_chip && ((intp[8] & 4) == 0)) || (!qlt->qlt_25xx_chip && !qlt->qlt_81xx_chip && !qlt->qlt_83xx_chip && !qlt->qlt_27xx_chip && ((intp[8] & 3) == 0))) { EL(qlt, "UPLOAD_FW fw_type=%d\n", intp[8]); ret = EACCES; iocd->stmf_error = QLTIO_INVALID_FW_TYPE; break; } /* Everything looks ok, lets copy this firmware */ if (qlt->fw_code01) { kmem_free(qlt->fw_code01, (qlt->fw_length01 + qlt->fw_length02) << 2); qlt->fw_code01 = NULL; } else { atomic_inc_32(&qlt_loaded_counter); } qlt->fw_length01 = intp[3]; qlt->fw_code01 = (uint32_t *)kmem_alloc(iocd->stmf_ibuf_size, KM_SLEEP); bcopy(intp, qlt->fw_code01, iocd->stmf_ibuf_size); qlt->fw_addr01 = intp[2]; qlt->fw_code02 = &qlt->fw_code01[intp[3]]; qlt->fw_addr02 = qlt->fw_code02[2]; qlt->fw_length02 = qlt->fw_code02[3]; break; case QLT_IOCTL_CLEAR_FW: if (qlt->fw_code01) { kmem_free(qlt->fw_code01, (qlt->fw_length01 + qlt->fw_length02) << 2); qlt->fw_code01 = NULL; atomic_dec_32(&qlt_loaded_counter); } break; case QLT_IOCTL_GET_FW_INFO: if (iocd->stmf_obuf_size != sizeof (qlt_fw_info_t)) { EL(qlt, "GET_FW_INFO obuf_size=%d != %d\n", iocd->stmf_obuf_size, sizeof (qlt_fw_info_t)); ret = EINVAL; break; } fwi = (qlt_fw_info_t *)obuf; if (qlt->qlt_stay_offline) { fwi->fwi_stay_offline = 1; } if (qlt->qlt_state == FCT_STATE_ONLINE) { fwi->fwi_port_active = 1; } fwi->fwi_active_major = qlt->fw_major; fwi->fwi_active_minor = qlt->fw_minor; fwi->fwi_active_subminor = qlt->fw_subminor; fwi->fwi_active_attr = qlt->fw_attr; if (qlt->fw_code01) { fwi->fwi_fw_uploaded = 1; fwi->fwi_loaded_major = (uint16_t)qlt->fw_code01[4]; fwi->fwi_loaded_minor = (uint16_t)qlt->fw_code01[5]; fwi->fwi_loaded_subminor = (uint16_t)qlt->fw_code01[6]; fwi->fwi_loaded_attr = (uint16_t)qlt->fw_code01[7]; } if (qlt->qlt_27xx_chip) { fwi->fwi_default_major = (uint16_t)fw2700_code01[4]; fwi->fwi_default_minor = (uint16_t)fw2700_code01[5]; fwi->fwi_default_subminor = (uint16_t)fw2700_code01[6]; fwi->fwi_default_attr = (uint16_t)fw2700_code01[7]; } else if (qlt->qlt_83xx_chip) { fwi->fwi_default_major = (uint16_t)fw8300fc_code01[4]; fwi->fwi_default_minor = (uint16_t)fw8300fc_code01[5]; fwi->fwi_default_subminor = (uint16_t)fw8300fc_code01[6]; fwi->fwi_default_attr = (uint16_t)fw8300fc_code01[7]; } else if (qlt->qlt_81xx_chip) { fwi->fwi_default_major = (uint16_t)fw8100_code01[4]; fwi->fwi_default_minor = (uint16_t)fw8100_code01[5]; fwi->fwi_default_subminor = (uint16_t)fw8100_code01[6]; fwi->fwi_default_attr = (uint16_t)fw8100_code01[7]; } else if (qlt->qlt_25xx_chip) { fwi->fwi_default_major = (uint16_t)fw2500_code01[4]; fwi->fwi_default_minor = (uint16_t)fw2500_code01[5]; fwi->fwi_default_subminor = (uint16_t)fw2500_code01[6]; fwi->fwi_default_attr = (uint16_t)fw2500_code01[7]; } else { fwi->fwi_default_major = (uint16_t)fw2400_code01[4]; fwi->fwi_default_minor = (uint16_t)fw2400_code01[5]; fwi->fwi_default_subminor = (uint16_t)fw2400_code01[6]; fwi->fwi_default_attr = (uint16_t)fw2400_code01[7]; } break; case QLT_IOCTL_STAY_OFFLINE: if (!iocd->stmf_ibuf_size) { EL(qlt, "STAY_OFFLINE ibuf_size=%d\n", iocd->stmf_ibuf_size); ret = EINVAL; break; } if (*((char *)ibuf)) { qlt->qlt_stay_offline = 1; } else { qlt->qlt_stay_offline = 0; } break; case QLT_IOCTL_MBOX: if ((iocd->stmf_ibuf_size < sizeof (qlt_ioctl_mbox_t)) || (iocd->stmf_obuf_size < sizeof (qlt_ioctl_mbox_t))) { EL(qlt, "IOCTL_MBOX ibuf_size=%d, obuf_size=%d\n", iocd->stmf_ibuf_size, iocd->stmf_obuf_size); ret = EINVAL; break; } mcp = qlt_alloc_mailbox_command(qlt, 0); if (mcp == NULL) { EL(qlt, "IOCTL_MBOX mcp == NULL\n"); ret = ENOMEM; break; } bcopy(ibuf, mcp, sizeof (qlt_ioctl_mbox_t)); st = qlt_mailbox_command(qlt, mcp); bcopy(mcp, obuf, sizeof (qlt_ioctl_mbox_t)); qlt_free_mailbox_command(qlt, mcp); if (st != QLT_SUCCESS) { if ((st & (~((uint64_t)(0xFFFF)))) == QLT_MBOX_FAILED) st = QLT_SUCCESS; } if (st != QLT_SUCCESS) { EL(qlt, "IOCTL_MBOX status=%xh\n", st); ret = EIO; switch (st) { case QLT_MBOX_NOT_INITIALIZED: iocd->stmf_error = QLTIO_MBOX_NOT_INITIALIZED; break; case QLT_MBOX_BUSY: iocd->stmf_error = QLTIO_CANT_GET_MBOXES; break; case QLT_MBOX_TIMEOUT: iocd->stmf_error = QLTIO_MBOX_TIMED_OUT; break; case QLT_MBOX_ABORTED: iocd->stmf_error = QLTIO_MBOX_ABORTED; break; } } break; case QLT_IOCTL_ELOG: EL(qlt, "Not support yet, ioctl-%xh\n", cmd); break; default: EL(qlt, "Unknown ioctl-%xh\n", cmd); ret = ENOTTY; } if (ret == 0) { ret = stmf_copyout_iocdata(data, mode, iocd, obuf); } else if (iocd->stmf_error) { (void) stmf_copyout_iocdata(data, mode, iocd, obuf); } if (obuf) { kmem_free(obuf, iocd->stmf_obuf_size); obuf = NULL; } if (ibuf) { kmem_free(ibuf, iocd->stmf_ibuf_size); ibuf = NULL; } kmem_free(iocd, sizeof (stmf_iocdata_t)); return (ret); } static fct_status_t qlt_force_lip(qlt_state_t *qlt) { mbox_cmd_t *mcp; fct_status_t rval; mcp = qlt_alloc_mailbox_command(qlt, 0); if (qlt->qlt_fcoe_enabled) { mcp->to_fw[0] = MBC_PORT_RESET; } else { mcp->to_fw[0] = MBC_LIP_FULL_LOGIN; mcp->to_fw[1] = BIT_4; mcp->to_fw[3] = 1; mcp->to_fw_mask |= BIT_1 | BIT_3; } rval = qlt_mailbox_command(qlt, mcp); if (rval != FCT_SUCCESS) { EL(qlt, "qlt force lip MB failed: rval=%x\n", rval); } else { if (mcp->from_fw[0] != QLT_MBX_CMD_SUCCESS) { QLT_LOG(qlt->qlt_port_alias, "qlt FLIP: fw[0]=%x", mcp->from_fw[0]); rval = FCT_FAILURE; } } qlt_free_mailbox_command(qlt, mcp); return (rval); } static void qlt_ctl(struct fct_local_port *port, int cmd, void *arg) { stmf_change_status_t st; stmf_state_change_info_t *ssci = (stmf_state_change_info_t *)arg; qlt_state_t *qlt; fct_status_t ret; ASSERT((cmd == FCT_CMD_PORT_ONLINE) || (cmd == FCT_CMD_PORT_OFFLINE) || (cmd == FCT_CMD_FORCE_LIP) || (cmd == FCT_ACK_PORT_ONLINE_COMPLETE) || (cmd == FCT_ACK_PORT_OFFLINE_COMPLETE)); qlt = (qlt_state_t *)port->port_fca_private; st.st_completion_status = FCT_SUCCESS; st.st_additional_info = NULL; EL(qlt, "port (%p) qlt_state (%xh) cmd (%xh) arg (%p)\n", port, qlt->qlt_state, cmd, arg); switch (cmd) { case FCT_CMD_PORT_ONLINE: if (qlt->qlt_state == FCT_STATE_ONLINE) st.st_completion_status = STMF_ALREADY; else if (qlt->qlt_state != FCT_STATE_OFFLINE) st.st_completion_status = FCT_FAILURE; if (st.st_completion_status == FCT_SUCCESS) { qlt->qlt_state = FCT_STATE_ONLINING; qlt->qlt_state_not_acked = 1; st.st_completion_status = qlt_port_online(qlt); if (st.st_completion_status != STMF_SUCCESS) { EL(qlt, "PORT_ONLINE status=%xh\n", st.st_completion_status); qlt->qlt_state = FCT_STATE_OFFLINE; qlt->qlt_state_not_acked = 0; } else { qlt->qlt_state = FCT_STATE_ONLINE; } } fct_ctl(port->port_lport, FCT_CMD_PORT_ONLINE_COMPLETE, &st); qlt->qlt_change_state_flags = 0; break; case FCT_CMD_PORT_OFFLINE: if (qlt->qlt_state == FCT_STATE_OFFLINE) { st.st_completion_status = STMF_ALREADY; } else if (qlt->qlt_state != FCT_STATE_ONLINE) { st.st_completion_status = FCT_FAILURE; } if (st.st_completion_status == FCT_SUCCESS) { qlt->qlt_state = FCT_STATE_OFFLINING; qlt->qlt_state_not_acked = 1; if (ssci->st_rflags & STMF_RFLAG_COLLECT_DEBUG_DUMP) { (void) qlt_firmware_dump(port, ssci); } qlt->qlt_change_state_flags = (uint32_t)ssci->st_rflags; st.st_completion_status = qlt_port_offline(qlt); if (st.st_completion_status != STMF_SUCCESS) { EL(qlt, "PORT_OFFLINE status=%xh\n", st.st_completion_status); qlt->qlt_state = FCT_STATE_ONLINE; qlt->qlt_state_not_acked = 0; } else { qlt->qlt_state = FCT_STATE_OFFLINE; } } fct_ctl(port->port_lport, FCT_CMD_PORT_OFFLINE_COMPLETE, &st); break; case FCT_ACK_PORT_ONLINE_COMPLETE: qlt->qlt_state_not_acked = 0; break; case FCT_ACK_PORT_OFFLINE_COMPLETE: qlt->qlt_state_not_acked = 0; if ((qlt->qlt_change_state_flags & STMF_RFLAG_RESET) && (qlt->qlt_stay_offline == 0)) { if ((ret = fct_port_initialize(port, qlt->qlt_change_state_flags, "qlt_ctl FCT_ACK_PORT_OFFLINE_COMPLETE " "with RLFLAG_RESET")) != FCT_SUCCESS) { EL(qlt, "fct_port_initialize status=%llxh\n", ret); cmn_err(CE_WARN, "qlt_ctl: " "fct_port_initialize failed, please use " "stmfstate to start the port-%s manualy", qlt->qlt_port_alias); } } break; case FCT_CMD_FORCE_LIP: if (qlt->qlt_fcoe_enabled) { EL(qlt, "force lip is an unsupported command " "for this adapter type\n"); } else { if (qlt->qlt_state == FCT_STATE_ONLINE) { *((fct_status_t *)arg) = qlt_force_lip(qlt); EL(qlt, "forcelip done\n"); } } break; default: EL(qlt, "unsupport cmd - 0x%02X\n", cmd); break; } } /* ARGSUSED */ static fct_status_t qlt_do_flogi(fct_local_port_t *port, fct_flogi_xchg_t *fx) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; EL(qlt, "FLOGI requested not supported\n"); cmn_err(CE_WARN, "qlt: FLOGI requested (not supported)"); return (FCT_FAILURE); } /* * Return a pointer to n entries in the request queue. Assumes that * request queue lock is held. Does a very short busy wait if * less/zero entries are available. Retuns NULL if it still cannot * fullfill the request. * **CALL qlt_submit_req_entries() BEFORE DROPPING THE LOCK** */ caddr_t qlt_get_req_entries(qlt_state_t *qlt, uint32_t n, uint16_t qi) { int try = 0; while (qlt->mq_req[qi].mq_available < n) { uint32_t val1, val2, val3; if (qlt->qlt_mq_enabled) { /* debounce */ val1 = MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_OUT); val2 = MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_OUT); val3 = MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_OUT); } else { val1 = REG_RD32(qlt, REG_REQ_OUT_PTR); val2 = REG_RD32(qlt, REG_REQ_OUT_PTR); val3 = REG_RD32(qlt, REG_REQ_OUT_PTR); } if ((val1 != val2) || (val2 != val3)) continue; qlt->mq_req[qi].mq_ndx_from_fw = val1; if (qi != 0) { qlt->mq_req[qi].mq_available = REQUEST_QUEUE_MQ_ENTRIES - 1 - ((qlt->mq_req[qi].mq_ndx_to_fw - qlt->mq_req[qi].mq_ndx_from_fw) & (REQUEST_QUEUE_MQ_ENTRIES - 1)); } else { qlt->mq_req[qi].mq_available = REQUEST_QUEUE_ENTRIES - 1 - ((qlt->mq_req[qi].mq_ndx_to_fw - qlt->mq_req[qi].mq_ndx_from_fw) & (REQUEST_QUEUE_ENTRIES - 1)); } if (qlt->mq_req[qi].mq_available < n) { if (try < 2) { drv_usecwait(100); try++; continue; } else { stmf_trace(qlt->qlt_port_alias, "Req Q# %xh is full", qi); EL(qlt, "Req %xh is full (%d,%d) (%d,%d)\n", qi, qlt->mq_req[qi].mq_ndx_to_fw, qlt->mq_req[qi].mq_ndx_from_fw, n, qlt->mq_req[qi].mq_available); return (NULL); } } break; } /* We dont change anything until the entries are sumitted */ return (&qlt->mq_req[qi].mq_ptr[qlt->mq_req[qi].mq_ndx_to_fw << 6]); } /* * updates the req in ptr to fw. Assumes that req lock is held. */ void qlt_submit_req_entries(qlt_state_t *qlt, uint32_t n, uint16_t qi) { ASSERT(n >= 1); qlt->mq_req[qi].mq_ndx_to_fw += n; if (qi != 0) { qlt->mq_req[qi].mq_ndx_to_fw &= REQUEST_QUEUE_MQ_ENTRIES - 1; } else { qlt->mq_req[qi].mq_ndx_to_fw &= REQUEST_QUEUE_ENTRIES - 1; } qlt->mq_req[qi].mq_available -= n; if (qlt->qlt_mq_enabled) { MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_IN, qlt->mq_req[qi].mq_ndx_to_fw); } else { REG_WR32(qlt, REG_REQ_IN_PTR, qlt->mq_req[0].mq_ndx_to_fw); } } /* * Return a pointer to n entries in the priority request queue. Assumes that * priority request queue lock is held. Does a very short busy wait if * less/zero entries are available. Retuns NULL if it still cannot * fullfill the request. * **CALL qlt_submit_preq_entries() BEFORE DROPPING THE LOCK** */ caddr_t qlt_get_preq_entries(qlt_state_t *qlt, uint32_t n) { int try = 0; uint32_t req_available = PRIORITY_QUEUE_ENTRIES - 1 - ((qlt->preq_ndx_to_fw - qlt->preq_ndx_from_fw) & (PRIORITY_QUEUE_ENTRIES - 1)); while (req_available < n) { uint32_t val1, val2, val3; val1 = REG_RD32(qlt, REG_PREQ_OUT_PTR); val2 = REG_RD32(qlt, REG_PREQ_OUT_PTR); val3 = REG_RD32(qlt, REG_PREQ_OUT_PTR); if ((val1 != val2) || (val2 != val3)) continue; qlt->preq_ndx_from_fw = val1; req_available = PRIORITY_QUEUE_ENTRIES - 1 - ((qlt->preq_ndx_to_fw - qlt->preq_ndx_from_fw) & (PRIORITY_QUEUE_ENTRIES - 1)); if (req_available < n) { if (try < 2) { drv_usecwait(100); try++; continue; } else { return (NULL); } } break; } /* We dont change anything until the entries are sumitted */ return (&qlt->preq_ptr[qlt->preq_ndx_to_fw << 6]); } /* * updates the req in ptr to fw. Assumes that req lock is held. */ void qlt_submit_preq_entries(qlt_state_t *qlt, uint32_t n) { ASSERT(n >= 1); qlt->preq_ndx_to_fw += n; qlt->preq_ndx_to_fw &= PRIORITY_QUEUE_ENTRIES - 1; REG_WR32(qlt, REG_PREQ_IN_PTR, qlt->preq_ndx_to_fw); } /* * - Should not be called from Interrupt. * - A very hardware specific function. Does not touch driver state. * - Assumes that interrupts are disabled or not there. * - Expects that the caller makes sure that all activity has stopped * and its ok now to go ahead and reset the chip. Also the caller * takes care of post reset damage control. * - called by initialize adapter() and dump_fw(for reset only). * - During attach() nothing much is happening and during initialize_adapter() * the function (caller) does all the housekeeping so that this function * can execute in peace. * - Returns 0 on success. */ static fct_status_t qlt_reset_chip(qlt_state_t *qlt) { int cntr; EL(qlt, "initiated\n"); /* XXX: Switch off LEDs */ qlt->qlt_intr_enabled = 0; /* Disable Interrupts */ REG_WR32(qlt, REG_INTR_CTRL, 0); (void) REG_RD32(qlt, REG_INTR_CTRL); /* Stop DMA */ REG_WR32(qlt, REG_CTRL_STATUS, DMA_SHUTDOWN_CTRL | PCI_X_XFER_CTRL); /* Wait for DMA to be stopped */ cntr = 0; while (REG_RD32(qlt, REG_CTRL_STATUS) & DMA_ACTIVE_STATUS) { delay(drv_usectohz(10000)); /* mostly 10ms is 1 tick */ cntr++; /* 3 sec should be more than enough */ if (cntr == 300) return (QLT_DMA_STUCK); } /* need to ensure no one accesses the hw during the reset 100us */ if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_enter(&qlt->mbox_lock); if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_enter(&qlt->mq_req[i].mq_lock); } } mutex_enter(&qlt->mq_req[0].mq_lock); /* * We need to give time for other threads to finsh their * interupts (or we need another lock) */ drv_usecwait(40); } /* Reset the Chip */ REG_WR32(qlt, REG_CTRL_STATUS, DMA_SHUTDOWN_CTRL | PCI_X_XFER_CTRL | CHIP_SOFT_RESET); qlt->qlt_link_up = 0; drv_usecwait(100); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_exit(&qlt->mq_req[0].mq_lock); if (qlt->qlt_mq_enabled == 1) { int i; for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_exit(&qlt->mq_req[i].mq_lock); } } mutex_exit(&qlt->mbox_lock); } /* Wait for ROM firmware to initialize (0x0000) in mailbox 0 */ cntr = 0; while (REG_RD16(qlt, REG_MBOX(0)) != 0) { delay(drv_usectohz(10000)); cntr++; /* 3 sec should be more than enough */ if (cntr == 300) return (QLT_ROM_STUCK); } /* Disable Interrupts (Probably not needed) */ REG_WR32(qlt, REG_INTR_CTRL, 0); return (QLT_SUCCESS); } /* * - Should not be called from Interrupt. * - A very hardware specific function. Does not touch driver state. * - Assumes that interrupts are disabled or not there. * - Expects that the caller makes sure that all activity has stopped * and its ok now to go ahead and reset the chip. Also the caller * takes care of post reset damage control. * - called by initialize adapter() and dump_fw(for reset only). * - During attach() nothing much is happening and during initialize_adapter() * the function (caller) does all the housekeeping so that this function * can execute in peace. * - Returns 0 on success. */ static fct_status_t qlt_download_fw(qlt_state_t *qlt) { uint32_t start_addr; fct_status_t ret; EL(qlt, "initiated\n"); (void) qlt_reset_chip(qlt); if (qlt->qlt_81xx_chip) { qlt_mps_reset(qlt); } /* Load the two segments */ if (qlt->fw_code01 != NULL) { ret = qlt_load_risc_ram(qlt, qlt->fw_code01, qlt->fw_length01, qlt->fw_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, qlt->fw_code02, qlt->fw_length02, qlt->fw_addr02); } start_addr = qlt->fw_addr01; } else if (qlt->qlt_27xx_chip) { (void) qlt_27xx_get_dmp_template(qlt); ret = qlt_load_risc_ram(qlt, fw2700_code01, fw2700_length01, fw2700_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, fw2700_code02, fw2700_length02, fw2700_addr02); } start_addr = fw2700_addr01; } else if (qlt->qlt_83xx_chip) { ret = qlt_load_risc_ram(qlt, fw8300fc_code01, fw8300fc_length01, fw8300fc_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, fw8300fc_code02, fw8300fc_length02, fw8300fc_addr02); } start_addr = fw8300fc_addr01; } else if (qlt->qlt_81xx_chip) { ret = qlt_load_risc_ram(qlt, fw8100_code01, fw8100_length01, fw8100_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, fw8100_code02, fw8100_length02, fw8100_addr02); } start_addr = fw8100_addr01; } else if (qlt->qlt_25xx_chip) { ret = qlt_load_risc_ram(qlt, fw2500_code01, fw2500_length01, fw2500_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, fw2500_code02, fw2500_length02, fw2500_addr02); } start_addr = fw2500_addr01; } else { ret = qlt_load_risc_ram(qlt, fw2400_code01, fw2400_length01, fw2400_addr01); if (ret == QLT_SUCCESS) { ret = qlt_load_risc_ram(qlt, fw2400_code02, fw2400_length02, fw2400_addr02); } start_addr = fw2400_addr01; } if (ret != QLT_SUCCESS) { EL(qlt, "qlt_load_risc_ram status=%llxh\n", ret); return (ret); } /* Verify Checksum */ REG_WR16(qlt, REG_MBOX(0), MBC_VERIFY_CHECKSUM); REG_WR16(qlt, REG_MBOX(1), MSW(start_addr)); REG_WR16(qlt, REG_MBOX(2), LSW(start_addr)); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=7h status=%llxh\n", ret); return (ret); } /* Execute firmware */ REG_WR16(qlt, REG_MBOX(0), MBC_EXECUTE_FIRMWARE); REG_WR16(qlt, REG_MBOX(1), MSW(start_addr)); REG_WR16(qlt, REG_MBOX(2), LSW(start_addr)); REG_WR16(qlt, REG_MBOX(3), 0); #ifdef EXTRA_CREDIT /* enable extra credits (reduces available buffers) */ if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { REG_WR16(qlt, REG_MBOX(4), 1); } else { REG_WR16(qlt, REG_MBOX(4), 0); } #else REG_WR16(qlt, REG_MBOX(4), 0); #endif ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=2h status=%llxh\n", ret); return (ret); } else { if (qlt->qlt_27xx_chip) { qlt->qlt_27xx_speed = (uint32_t) (REG_RD16(qlt, REG_MBOX(3)) << 16 | REG_RD16(qlt, REG_MBOX(2))); } } /* Get revisions (About Firmware) */ REG_WR16(qlt, REG_MBOX(0), MBC_ABOUT_FIRMWARE); ret = qlt_raw_mailbox_command(qlt); qlt->fw_major = REG_RD16(qlt, REG_MBOX(1)); qlt->fw_minor = REG_RD16(qlt, REG_MBOX(2)); qlt->fw_subminor = REG_RD16(qlt, REG_MBOX(3)); qlt->fw_endaddrlo = REG_RD16(qlt, REG_MBOX(4)); qlt->fw_endaddrhi = REG_RD16(qlt, REG_MBOX(5)); qlt->fw_attr = REG_RD16(qlt, REG_MBOX(6)); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=8h status=%llxh\n", ret); return (ret); } if (qlt->qlt_27xx_chip) { qlt->fw_ext_memory_end = SHORT_TO_LONG( REG_RD16(qlt, REG_MBOX(4)), REG_RD16(qlt, REG_MBOX(5))); qlt->fw_shared_ram_start = SHORT_TO_LONG( REG_RD16(qlt, REG_MBOX(18)), REG_RD16(qlt, REG_MBOX(19))); qlt->fw_shared_ram_end = SHORT_TO_LONG( REG_RD16(qlt, REG_MBOX(20)), REG_RD16(qlt, REG_MBOX(21))); qlt->fw_ddr_ram_start = SHORT_TO_LONG( REG_RD16(qlt, REG_MBOX(22)), REG_RD16(qlt, REG_MBOX(23))); qlt->fw_ddr_ram_end = SHORT_TO_LONG( REG_RD16(qlt, REG_MBOX(24)), REG_RD16(qlt, REG_MBOX(25))); } return (QLT_SUCCESS); } /* * Used only from qlt_download_fw(). */ static fct_status_t qlt_load_risc_ram(qlt_state_t *qlt, uint32_t *host_addr, uint32_t word_count, uint32_t risc_addr) { uint32_t words_sent = 0; uint32_t words_being_sent; uint32_t *cur_host_addr; uint32_t cur_risc_addr; uint64_t da; fct_status_t ret; while (words_sent < word_count) { cur_host_addr = &(host_addr[words_sent]); cur_risc_addr = risc_addr + (words_sent << 2); words_being_sent = min(word_count - words_sent, TOTAL_DMA_MEM_SIZE >> 2); ddi_rep_put32(qlt->queue_mem_acc_handle, cur_host_addr, (uint32_t *)qlt->queue_mem_ptr, words_being_sent, DDI_DEV_AUTOINCR); (void) ddi_dma_sync(qlt->queue_mem_dma_handle, 0, words_being_sent << 2, DDI_DMA_SYNC_FORDEV); da = qlt->queue_mem_cookie.dmac_laddress; REG_WR16(qlt, REG_MBOX(0), MBC_LOAD_RAM_EXTENDED); REG_WR16(qlt, REG_MBOX(1), LSW(risc_addr)); REG_WR16(qlt, REG_MBOX(8), MSW(cur_risc_addr)); REG_WR16(qlt, REG_MBOX(3), LSW(LSD(da))); REG_WR16(qlt, REG_MBOX(2), MSW(LSD(da))); REG_WR16(qlt, REG_MBOX(7), LSW(MSD(da))); REG_WR16(qlt, REG_MBOX(6), MSW(MSD(da))); REG_WR16(qlt, REG_MBOX(5), LSW(words_being_sent)); REG_WR16(qlt, REG_MBOX(4), MSW(words_being_sent)); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=0Bh status=%llxh\n", ret); return (ret); } words_sent += words_being_sent; } EL(qlt, "qlt_raw_mailbox_command=0Bh, LOAD_RAM_EXTENDED complete\n"); return (QLT_SUCCESS); } /* * Not used during normal operation. Only during driver init. * Assumes that interrupts are disabled and mailboxes are loaded. * Just triggers the mailbox command an waits for the completion. * Also expects that There is nothing else going on and we will only * get back a mailbox completion from firmware. * ---DOES NOT CLEAR INTERRUPT--- * Used only from the code path originating from * qlt_reset_chip() */ static fct_status_t qlt_raw_mailbox_command(qlt_state_t *qlt) { int cntr = 0; uint32_t status; fct_local_port_t *port = qlt->qlt_port; REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_HOST_TO_RISC_INTR)); retry_raw:; while ((REG_RD32(qlt, REG_INTR_STATUS) & RISC_PCI_INTR_REQUEST) == 0) { cntr++; if (cntr == 3000) { EL(qlt, "polling exhausted, dump fw now..\n"); (void) qlt_firmware_dump(port, (stmf_state_change_info_t *)NULL); return (QLT_MAILBOX_STUCK); } delay(drv_usectohz(10000)); } status = (REG_RD32(qlt, REG_RISC_STATUS) & FW_INTR_STATUS_MASK); if ((status == ROM_MBX_CMD_SUCCESSFUL) || (status == ROM_MBX_CMD_NOT_SUCCESSFUL) || (status == MBX_CMD_SUCCESSFUL) || (status == MBX_CMD_NOT_SUCCESSFUL)) { uint16_t mbox0 = REG_RD16(qlt, REG_MBOX(0)); if (mbox0 == QLT_MBX_CMD_SUCCESS) { return (QLT_SUCCESS); } else { EL(qlt, "mbx cmd failed, dump fw now..\n"); (void) qlt_firmware_dump(port, (stmf_state_change_info_t *)NULL); return (QLT_MBOX_FAILED | mbox0); } } else if (status == ASYNC_EVENT) { uint16_t mbox0, mbox1, mbox2, mbox3; uint16_t mbox4, mbox5, mbox6, mbox7; mbox0 = REG_RD16(qlt, REG_MBOX(0)); mbox1 = REG_RD16(qlt, REG_MBOX(1)); mbox2 = REG_RD16(qlt, REG_MBOX(2)); mbox3 = REG_RD16(qlt, REG_MBOX(3)); mbox4 = REG_RD16(qlt, REG_MBOX(4)); mbox5 = REG_RD16(qlt, REG_MBOX(5)); mbox6 = REG_RD16(qlt, REG_MBOX(6)); mbox7 = REG_RD16(qlt, REG_MBOX(7)); cmn_err(CE_NOTE, "!qlt(%d): Async event %x mb1=%x mb2=%x" "mb3=%x mb4=%x mb5=%x mb6=%x mb7=%x", qlt->instance, mbox0, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6, mbox7); if (mbox0 == 0x8002) { (void) qlt_firmware_dump(port, (stmf_state_change_info_t *)NULL); return (QLT_UNEXPECTED_RESPONSE); } else { REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); cntr = 0; goto retry_raw; } } /* This is unexpected, dump a message */ cmn_err(CE_WARN, "qlt(%d): Unexpect intr status %llx", ddi_get_instance(qlt->dip), (unsigned long long)status); return (QLT_UNEXPECTED_RESPONSE); } static mbox_cmd_t * qlt_alloc_mailbox_command(qlt_state_t *qlt, uint32_t dma_size) { mbox_cmd_t *mcp; mcp = (mbox_cmd_t *)kmem_zalloc(sizeof (mbox_cmd_t), KM_SLEEP); if (dma_size) { qlt_dmem_bctl_t *bctl; uint64_t da; mcp->dbuf = qlt_i_dmem_alloc(qlt, dma_size, &dma_size, 0); if (mcp->dbuf == NULL) { kmem_free(mcp, sizeof (*mcp)); return (NULL); } mcp->dbuf->db_data_size = dma_size; ASSERT(mcp->dbuf->db_sglist_length == 1); bctl = (qlt_dmem_bctl_t *)mcp->dbuf->db_port_private; da = bctl->bctl_dev_addr; /* This is the most common initialization of dma ptrs */ mcp->to_fw[3] = LSW(LSD(da)); mcp->to_fw[2] = MSW(LSD(da)); mcp->to_fw[7] = LSW(MSD(da)); mcp->to_fw[6] = MSW(MSD(da)); mcp->to_fw_mask |= BIT_2 | BIT_3 | BIT_7 | BIT_6; } mcp->to_fw_mask |= BIT_0; mcp->from_fw_mask |= BIT_0; return (mcp); } void qlt_free_mailbox_command(qlt_state_t *qlt, mbox_cmd_t *mcp) { if (mcp->dbuf) qlt_i_dmem_free(qlt, mcp->dbuf); kmem_free(mcp, sizeof (*mcp)); } /* * This can sleep. Should never be called from interrupt context. */ static fct_status_t qlt_mailbox_command(qlt_state_t *qlt, mbox_cmd_t *mcp) { int retries; int i; char info[80]; if (curthread->t_flag & T_INTR_THREAD) { ASSERT(0); return (QLT_MBOX_FAILED); } EL(qlt, "mailbox:[0]=%xh [1]=%xh\n", mcp->to_fw[0], mcp->to_fw[1]); mutex_enter(&qlt->mbox_lock); /* See if mailboxes are still uninitialized */ if (qlt->mbox_io_state == MBOX_STATE_UNKNOWN) { mutex_exit(&qlt->mbox_lock); return (QLT_MBOX_NOT_INITIALIZED); } /* Wait to grab the mailboxes */ for (retries = 0; qlt->mbox_io_state != MBOX_STATE_READY; retries++) { cv_wait(&qlt->mbox_cv, &qlt->mbox_lock); if ((retries > 5) || (qlt->mbox_io_state == MBOX_STATE_UNKNOWN)) { mutex_exit(&qlt->mbox_lock); return (QLT_MBOX_BUSY); } } /* Make sure we always ask for mailbox 0 */ mcp->from_fw_mask |= BIT_0; /* Load mailboxes, set state and generate RISC interrupt */ qlt->mbox_io_state = MBOX_STATE_CMD_RUNNING; qlt->mcp = mcp; for (i = 0; i < MAX_MBOXES; i++) { if (mcp->to_fw_mask & ((uint32_t)1 << i)) REG_WR16(qlt, REG_MBOX(i), mcp->to_fw[i]); } REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_HOST_TO_RISC_INTR)); qlt_mbox_wait_loop:; /* Wait for mailbox command completion */ if (cv_timedwait(&qlt->mbox_cv, &qlt->mbox_lock, ddi_get_lbolt() + drv_usectohz(MBOX_TIMEOUT)) < 0) { (void) snprintf(info, 80, "qlt_mailbox_command: qlt-%p, " "cmd-0x%02X timed out", (void *)qlt, qlt->mcp->to_fw[0]); info[79] = 0; qlt->mcp = NULL; qlt->mbox_io_state = MBOX_STATE_UNKNOWN; mutex_exit(&qlt->mbox_lock); /* * XXX Throw HBA fatal error event */ (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return (QLT_MBOX_TIMEOUT); } if (qlt->mbox_io_state == MBOX_STATE_CMD_RUNNING) goto qlt_mbox_wait_loop; qlt->mcp = NULL; /* Make sure its a completion */ if (qlt->mbox_io_state != MBOX_STATE_CMD_DONE) { ASSERT(qlt->mbox_io_state == MBOX_STATE_UNKNOWN); mutex_exit(&qlt->mbox_lock); return (QLT_MBOX_ABORTED); } /* MBox command completed. Clear state, retuen based on mbox 0 */ /* Mailboxes are already loaded by interrupt routine */ qlt->mbox_io_state = MBOX_STATE_READY; mutex_exit(&qlt->mbox_lock); if (mcp->from_fw[0] != QLT_MBX_CMD_SUCCESS) { EL(qlt, "fw[0] = %xh\n", mcp->from_fw[0]); if ((mcp->from_fw[0] != 0x4005) && (mcp->from_fw[1] != 0x7)) { (void) qlt_firmware_dump(qlt->qlt_port, (stmf_state_change_info_t *)NULL); } return (QLT_MBOX_FAILED | mcp->from_fw[0]); } return (QLT_SUCCESS); } /* * **SHOULD ONLY BE CALLED FROM INTERRUPT CONTEXT. DO NOT CALL ELSEWHERE** */ /* ARGSUSED */ static uint_t qlt_msix_resp_handler(caddr_t arg, caddr_t arg2) { qlt_state_t *qlt = (qlt_state_t *)arg; uint32_t risc_status; uint16_t qi = 0; risc_status = REG_RD32(qlt, REG_RISC_STATUS); if (qlt->qlt_mq_enabled) { /* XXX: */ /* qi = (uint16_t)((unsigned long)arg2); */ qi = (uint16_t)(risc_status >> 16); mutex_enter(&qlt->mq_resp[qi].mq_lock); if (!qlt->qlt_intr_enabled) { /* * No further interrupt since intr disabled. */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); mutex_exit(&qlt->mq_resp[qi].mq_lock); return (DDI_INTR_UNCLAIMED); } qlt->mq_resp[qi].mq_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN); qlt_handle_resp_queue_update(qlt, qi); mutex_exit(&qlt->mq_resp[qi].mq_lock); } else { mutex_enter(&qlt->intr_lock); if (!qlt->qlt_intr_enabled) { /* * No further interrupt since intr disabled. */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); mutex_exit(&qlt->intr_lock); return (DDI_INTR_UNCLAIMED); } qlt->atio_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_ATIO_IN_PTR); qlt_handle_atio_queue_update(qlt); qlt->mq_resp[qi].mq_ndx_from_fw = risc_status >> 16; qlt_handle_resp_queue_update(qlt, qi); mutex_exit(&qlt->intr_lock); } if (risc_status & BIT_15) { REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } return (DDI_INTR_CLAIMED); } /* * **SHOULD ONLY BE CALLED FROM INTERRUPT CONTEXT. DO NOT CALL ELSEWHERE** */ /* ARGSUSED */ static uint_t qlt_msix_default_handler(caddr_t arg, caddr_t arg2) { qlt_state_t *qlt = (qlt_state_t *)arg; uint32_t risc_status, intr_type; int i; char info[80]; risc_status = REG_RD32(qlt, REG_RISC_STATUS); if (!mutex_tryenter(&qlt->intr_lock)) { /* * Normally we will always get this lock. If tryenter is * failing then it means that driver is trying to do * some cleanup and is masking the intr but some intr * has sneaked in between. See if our device has generated * this intr. If so then wait a bit and return claimed. * If not then return claimed if this is the 1st instance * of a interrupt after driver has grabbed the lock. */ if ((risc_status & BIT_15) == 0) { return (DDI_INTR_UNCLAIMED); } else { /* try again */ drv_usecwait(10); if (!mutex_tryenter(&qlt->intr_lock)) { /* really bad! */ return (DDI_INTR_CLAIMED); } } } if (((risc_status & BIT_15) == 0) || (qlt->qlt_intr_enabled == 0)) { /* * This might be a pure coincedence that we are operating * in a interrupt disabled mode and another device * sharing the interrupt line has generated an interrupt * while an interrupt from our device might be pending. Just * ignore it and let the code handling the interrupt * disabled mode handle it. */ mutex_exit(&qlt->intr_lock); return (DDI_INTR_UNCLAIMED); } /* REG_WR32(qlt, REG_INTR_CTRL, 0); */ /* check for risc pause - unlikely */ if (risc_status & BIT_8) { uint32_t hccsr; hccsr = REG_RD32(qlt, REG_HCCR); EL(qlt, "Risc Pause status=%xh hccsr=%x\n", risc_status, hccsr); cmn_err(CE_WARN, "qlt(%d): Risc Pause %08x hccsr:%x", qlt->instance, risc_status, hccsr); (void) snprintf(info, 80, "Risc Pause %08x hccsr:%x", risc_status, hccsr); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); } /* check most likely types first */ intr_type = risc_status & 0xff; if (intr_type == 0x1D) { qlt->atio_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_ATIO_IN_PTR); qlt_handle_atio_queue_update(qlt); qlt->mq_resp[0].mq_ndx_from_fw = risc_status >> 16; qlt_handle_resp_queue_update(qlt, 0); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x1C) { qlt->atio_ndx_from_fw = (uint16_t)(risc_status >> 16); qlt_handle_atio_queue_update(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x1E) { /* 83xx */ qlt->atio_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, MQBAR_ATIO_IN); qlt_handle_atio_queue_update(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x13) { uint16_t qi; qlt->atio_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_ATIO_IN_PTR); qlt_handle_atio_queue_update(qlt); if (qlt->qlt_mq_enabled) { qi = (uint16_t)(risc_status >> 16); qlt->mq_resp[qi].mq_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN); /* FIX THIS to be optional */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else { qi = 0; REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); qlt->mq_resp[qi].mq_ndx_from_fw = risc_status >> 16; } qlt_handle_resp_queue_update(qlt, qi); } else if (intr_type == 0x14) { uint16_t qi = (uint16_t)(risc_status >> 16); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { qlt->atio_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, MQBAR_ATIO_IN); } else { qlt->atio_ndx_from_fw = (uint16_t) REG_RD32(qlt, REG_ATIO_IN_PTR); } qlt_handle_atio_queue_update(qlt); qlt->mq_resp[qi].mq_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN); qlt_handle_resp_queue_update(qlt, qi); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x12) { uint16_t code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6; REG_WR32(qlt, REG_INTR_CTRL, 0); code = (uint16_t)(risc_status >> 16); mbox1 = REG_RD16(qlt, REG_MBOX(1)); mbox2 = REG_RD16(qlt, REG_MBOX(2)); mbox3 = REG_RD16(qlt, REG_MBOX(3)); mbox4 = REG_RD16(qlt, REG_MBOX(4)); mbox5 = REG_RD16(qlt, REG_MBOX(5)); mbox6 = REG_RD16(qlt, REG_MBOX(6)); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); EL(qlt, "Async event: %x mb1=%x mb2=%x," " mb3=%x, mb4=%x, mb5=%x, mb6=%x", code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); stmf_trace(qlt->qlt_port_alias, "Async event: %x mb1=%x mb2=%x," " mb3=%x, mb4=%x, mb5=%x, mb6=%x", code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); cmn_err(CE_NOTE, "!qlt(%d): Async event %x mb1=%x mb2=%x," " mb3=%x, mb4=%x, mb5=%x, mb6=%x", qlt->instance, code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); if ((code == 0x8030) || (code == 0x8010) || (code == 0x8013)) { if (qlt->qlt_link_up) { fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_RESET, 0, 0); } } else if (code == 0x8012) { qlt->qlt_link_up = 0; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_DOWN, 0, 0); } else if (code == 0x8014) { if (mbox1 == 0xffff) { /* global event */ uint8_t reason_code; reason_code = (uint8_t)(mbox3 >> 8); switch (reason_code) { case 0x1d: /* FIP Clear Virtual Link received */ case 0x1a: /* received FLOGO */ case 0x1c: /* FCF configuration changed */ case 0x1e: /* FKA timeout */ if (mbox2 == 7) { qlt->qlt_link_up = 0; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_DOWN, 0, 0); } break; case 0x12: if (mbox2 == 4) { qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); stmf_trace(qlt->qlt_port_alias, "SNS login and SCR done"); } break; case 0: if ((mbox2 == 6) && (!qlt->qlt_link_up)) { qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); stmf_trace(qlt->qlt_port_alias, "Link reinitialised"); } break; default: stmf_trace(qlt->qlt_port_alias, "AEN ignored"); break; } } } else if (code == 0x8011) { switch (mbox1) { case 0: qlt->link_speed = PORT_SPEED_1G; break; case 1: qlt->link_speed = PORT_SPEED_2G; break; case 3: qlt->link_speed = PORT_SPEED_4G; break; case 4: qlt->link_speed = PORT_SPEED_8G; break; case 5: qlt->link_speed = PORT_SPEED_16G; break; case 0x13: qlt->link_speed = PORT_SPEED_10G; break; default: qlt->link_speed = PORT_SPEED_UNKNOWN; } qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); } else if ((code == 0x8002) || (code == 0x8003) || (code == 0x8004) || (code == 0x8005)) { (void) snprintf(info, 80, "Got %04x, mb1=%x mb2=%x mb5=%x mb6=%x", code, mbox1, mbox2, mbox5, mbox6); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); } else if (code == 0x800F) { (void) snprintf(info, 80, "Got 800F, mb1=%x mb2=%x mb3=%x", mbox1, mbox2, mbox3); if (mbox1 != 1) { /* issue "verify fw" */ qlt_verify_fw(qlt); } } else if (code == 0x8101) { (void) snprintf(info, 80, "IDC Req Rcvd:%04x, mb1=%x mb2=%x mb3=%x", code, mbox1, mbox2, mbox3); info[79] = 0; /* check if "ACK" is required (timeout != 0) */ if (mbox1 & 0x0f00) { caddr_t req; /* * Ack the request (queue work to do it?) * using a mailbox iocb * (Only Queue #0 allowed) */ mutex_enter(&qlt->mq_req[0].mq_lock); req = qlt_get_req_entries(qlt, 1, 0); if (req) { bzero(req, IOCB_SIZE); req[0] = 0x39; req[1] = 1; QMEM_WR16(qlt, req+8, 0x101); QMEM_WR16(qlt, req+10, mbox1); QMEM_WR16(qlt, req+12, mbox2); QMEM_WR16(qlt, req+14, mbox3); QMEM_WR16(qlt, req+16, mbox4); QMEM_WR16(qlt, req+18, mbox5); QMEM_WR16(qlt, req+20, mbox6); qlt_submit_req_entries(qlt, 1, 0); } else { (void) snprintf(info, 80, "IDC ACK failed"); info[79] = 0; } mutex_exit(&qlt->mq_req[0].mq_lock); } } else { stmf_trace(qlt->qlt_port_alias, "Async event: 0x%x ignored", code); } REG_WR32(qlt, REG_INTR_CTRL, ENABLE_RISC_INTR); } else if ((intr_type == 0x10) || (intr_type == 0x11)) { /* Handle mailbox completion */ mutex_enter(&qlt->mbox_lock); if (qlt->mbox_io_state != MBOX_STATE_CMD_RUNNING) { cmn_err(CE_WARN, "qlt(%d): mailbox completion received" " when driver wasn't waiting for it %d", qlt->instance, qlt->mbox_io_state); } else { for (i = 0; i < MAX_MBOXES; i++) { if (qlt->mcp->from_fw_mask & (((uint32_t)1) << i)) { qlt->mcp->from_fw[i] = REG_RD16(qlt, REG_MBOX(i)); } } qlt->mbox_io_state = MBOX_STATE_CMD_DONE; } REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); cv_broadcast(&qlt->mbox_cv); mutex_exit(&qlt->mbox_lock); } else { cmn_err(CE_WARN, "qlt(%d): Unknown intr type 0x%x", qlt->instance, intr_type); stmf_trace(qlt->qlt_port_alias, "%s: Unknown intr type 0x%x [%x]", __func__, intr_type, risc_status); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } /* REG_WR32(qlt, REG_INTR_CTRL, ENABLE_RISC_INTR); */ mutex_exit(&qlt->intr_lock); return (DDI_INTR_CLAIMED); } /* * **SHOULD ONLY BE CALLED FROM INTERRUPT CONTEXT. DO NOT CALL ELSEWHERE** */ /* ARGSUSED */ static uint_t qlt_isr(caddr_t arg, caddr_t arg2) { qlt_state_t *qlt = (qlt_state_t *)arg; uint32_t risc_status, intr_type; int i; int intr_loop_count; char info[80]; risc_status = REG_RD32(qlt, REG_RISC_STATUS); if (!mutex_tryenter(&qlt->intr_lock)) { /* * Normally we will always get this lock. If tryenter is * failing then it means that driver is trying to do * some cleanup and is masking the intr but some intr * has sneaked in between. See if our device has generated * this intr. If so then wait a bit and return claimed. * If not then return claimed if this is the 1st instance * of a interrupt after driver has grabbed the lock. */ if (risc_status & BIT_15) { drv_usecwait(10); return (DDI_INTR_CLAIMED); } else if (qlt->intr_sneak_counter) { qlt->intr_sneak_counter--; return (DDI_INTR_CLAIMED); } else { return (DDI_INTR_UNCLAIMED); } } if (((risc_status & BIT_15) == 0) || (qlt->qlt_intr_enabled == 0)) { /* * This might be a pure coincedence that we are operating * in a interrupt disabled mode and another device * sharing the interrupt line has generated an interrupt * while an interrupt from our device might be pending. Just * ignore it and let the code handling the interrupt * disabled mode handle it. */ mutex_exit(&qlt->intr_lock); return (DDI_INTR_UNCLAIMED); } /* * XXX take care for MSI-X case. disable intrs * Its gonna be complicated because of the max iterations. * as hba will have posted the intr which did not go on PCI * but we did not service it either because of max iterations. * Maybe offload the intr on a different thread. */ intr_loop_count = 0; REG_WR32(qlt, REG_INTR_CTRL, 0); intr_again:; /* check for risc pause */ if (risc_status & BIT_8) { EL(qlt, "Risc Pause status=%xh\n", risc_status); cmn_err(CE_WARN, "qlt(%d): Risc Pause %08x", qlt->instance, risc_status); (void) snprintf(info, 80, "Risc Pause %08x", risc_status); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); } /* First check for high performance path */ intr_type = risc_status & 0xff; if (intr_type == 0x1D) { /* process the atio queue first */ qlt->atio_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_ATIO_IN_PTR); qlt_handle_atio_queue_update(qlt); /* process the response queue next */ qlt->mq_resp[0].mq_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_RESP_IN_PTR); qlt_handle_resp_queue_update(qlt, 0); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x1C) { REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); qlt->atio_ndx_from_fw = (uint16_t)(risc_status >> 16); qlt_handle_atio_queue_update(qlt); } else if (intr_type == 0x1E) { /* 83xx Atio Queue update */ qlt->atio_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, MQBAR_ATIO_IN); qlt_handle_atio_queue_update(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x13) { uint16_t qi; qlt->atio_ndx_from_fw = (uint16_t)REG_RD32(qlt, REG_ATIO_IN_PTR); qlt_handle_atio_queue_update(qlt); if (qlt->qlt_mq_enabled) { qi = (uint16_t)(risc_status >> 16); qlt->mq_resp[0].mq_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN); /* FIX THIS to be optional */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else { qi = 0; REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); qlt->mq_resp[qi].mq_ndx_from_fw = risc_status >> 16; REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } qlt_handle_resp_queue_update(qlt, qi); } else if (intr_type == 0x14) { /* MQ */ uint16_t qi = 0; if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { qlt->atio_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, MQBAR_ATIO_IN); } else { qi = (uint16_t)(risc_status >> 16); qlt->atio_ndx_from_fw = (uint16_t) REG_RD32(qlt, REG_ATIO_IN_PTR); } qlt_handle_atio_queue_update(qlt); qlt->mq_resp[qi].mq_ndx_from_fw = (uint16_t)MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN); qlt_handle_resp_queue_update(qlt, qi); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } else if (intr_type == 0x12) { uint16_t code = (uint16_t)(risc_status >> 16); uint16_t mbox1 = REG_RD16(qlt, REG_MBOX(1)); uint16_t mbox2 = REG_RD16(qlt, REG_MBOX(2)); uint16_t mbox3 = REG_RD16(qlt, REG_MBOX(3)); uint16_t mbox4 = REG_RD16(qlt, REG_MBOX(4)); uint16_t mbox5 = REG_RD16(qlt, REG_MBOX(5)); uint16_t mbox6 = REG_RD16(qlt, REG_MBOX(6)); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); EL(qlt, "Async event %x mb1=%x, mb2=%x, mb3=%x, mb4=%x, " "mb5=%x, mb6=%x\n", code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); stmf_trace(qlt->qlt_port_alias, "Async event %x mb1=%x mb2=%x," " mb3=%x, mb4=%x, mb5=%x, mb6=%x", code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); cmn_err(CE_NOTE, "!qlt(%d): Async event %x mb1=%x mb2=%x," " mb3=%x, mb4=%x, mb5=%x, mb6=%x", qlt->instance, code, mbox1, mbox2, mbox3, mbox4, mbox5, mbox6); if ((code == 0x8030) || (code == 0x8010) || (code == 0x8013)) { if (qlt->qlt_link_up) { fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_RESET, 0, 0); } } else if (code == 0x8012) { qlt->qlt_link_up = 0; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_DOWN, 0, 0); } else if (code == 0x8014) { if (mbox1 == 0xffff) { /* global event */ uint8_t reason_code; reason_code = (uint8_t)(mbox3 >> 8); switch (reason_code) { case 0x1d: /* FIP Clear Virtual Link received */ case 0x1a: /* received FLOGO */ case 0x1c: /* FCF configuration changed */ case 0x1e: /* FKA timeout */ if (mbox2 == 7) { qlt->qlt_link_up = 0; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_DOWN, 0, 0); } break; case 0x12: if (mbox2 == 4) { qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); stmf_trace(qlt->qlt_port_alias, "SNS login and SCR done"); } break; case 0: if ((mbox2 == 6) && (!qlt->qlt_link_up)) { qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); stmf_trace(qlt->qlt_port_alias, "Link reinitialised"); } break; default: stmf_trace(qlt->qlt_port_alias, "AEN ignored"); break; } } } else if (code == 0x8011) { switch (mbox1) { case 0: qlt->link_speed = PORT_SPEED_1G; break; case 1: qlt->link_speed = PORT_SPEED_2G; break; case 3: qlt->link_speed = PORT_SPEED_4G; break; case 4: qlt->link_speed = PORT_SPEED_8G; break; case 5: qlt->link_speed = PORT_SPEED_16G; break; case 6: qlt->link_speed = PORT_SPEED_32G; break; case 0x13: qlt->link_speed = PORT_SPEED_10G; break; default: qlt->link_speed = PORT_SPEED_UNKNOWN; } qlt->qlt_link_up = 1; fct_handle_event(qlt->qlt_port, FCT_EVENT_LINK_UP, 0, 0); } else if ((code == 0x8002) || (code == 0x8003) || (code == 0x8004) || (code == 0x8005)) { (void) snprintf(info, 80, "Got %04x, mb1=%x mb2=%x mb5=%x mb6=%x", code, mbox1, mbox2, mbox5, mbox6); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); } else if (code == 0x800F) { (void) snprintf(info, 80, "Got 800F, mb1=%x mb2=%x mb3=%x", mbox1, mbox2, mbox3); if (mbox1 != 1) { /* issue "verify fw" */ qlt_verify_fw(qlt); } } else if (code == 0x8101) { (void) snprintf(info, 80, "IDC Req Rcvd:%04x, mb1=%x mb2=%x mb3=%x", code, mbox1, mbox2, mbox3); info[79] = 0; /* check if "ACK" is required (timeout != 0) */ if (mbox1 & 0x0f00) { caddr_t req; /* * Ack the request (queue work to do it?) * using a mailbox iocb (only Queue 0 allowed) */ mutex_enter(&qlt->mq_req[0].mq_lock); req = qlt_get_req_entries(qlt, 1, 0); if (req) { bzero(req, IOCB_SIZE); req[0] = 0x39; req[1] = 1; QMEM_WR16(qlt, req+8, 0x101); QMEM_WR16(qlt, req+10, mbox1); QMEM_WR16(qlt, req+12, mbox2); QMEM_WR16(qlt, req+14, mbox3); QMEM_WR16(qlt, req+16, mbox4); QMEM_WR16(qlt, req+18, mbox5); QMEM_WR16(qlt, req+20, mbox6); qlt_submit_req_entries(qlt, 1, 0); } else { (void) snprintf(info, 80, "IDC ACK failed"); info[79] = 0; } mutex_exit(&qlt->mq_req[0].mq_lock); } } } else if ((intr_type == 0x10) || (intr_type == 0x11)) { /* Handle mailbox completion */ mutex_enter(&qlt->mbox_lock); if (qlt->mbox_io_state != MBOX_STATE_CMD_RUNNING) { cmn_err(CE_WARN, "qlt(%d): mailbox completion received" " when driver wasn't waiting for it %d", qlt->instance, qlt->mbox_io_state); } else { for (i = 0; i < MAX_MBOXES; i++) { if (qlt->mcp->from_fw_mask & (((uint32_t)1) << i)) { qlt->mcp->from_fw[i] = REG_RD16(qlt, REG_MBOX(i)); } } qlt->mbox_io_state = MBOX_STATE_CMD_DONE; } REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); cv_broadcast(&qlt->mbox_cv); mutex_exit(&qlt->mbox_lock); } else { cmn_err(CE_WARN, "qlt(%d): Unknown intr type 0x%x", qlt->instance, intr_type); stmf_trace(qlt->qlt_port_alias, "%s: Unknown intr type 0x%x [%x]", __func__, intr_type, risc_status); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); } (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting */ if ((qlt->intr_flags & QLT_INTR_MSIX) == 0) { risc_status = REG_RD32(qlt, REG_RISC_STATUS); if ((risc_status & BIT_15) && (++intr_loop_count < QLT_MAX_ITERATIONS_PER_INTR)) { goto intr_again; } REG_WR32(qlt, REG_INTR_CTRL, ENABLE_RISC_INTR); mutex_exit(&qlt->intr_lock); } else { mutex_exit(&qlt->intr_lock); REG_WR32(qlt, REG_INTR_CTRL, ENABLE_RISC_INTR); } return (DDI_INTR_CLAIMED); } /* **************** NVRAM Functions ********************** */ fct_status_t qlt_read_flash_word(qlt_state_t *qlt, uint32_t faddr, uint32_t *bp) { uint32_t timer; /* Clear access error flag */ REG_WR32(qlt, REG_CTRL_STATUS, REG_RD32(qlt, REG_CTRL_STATUS) | FLASH_ERROR); REG_WR32(qlt, REG_FLASH_ADDR, faddr & ~BIT_31); /* Wait for READ cycle to complete. */ for (timer = 3000; timer; timer--) { if (REG_RD32(qlt, REG_FLASH_ADDR) & BIT_31) { break; } drv_usecwait(10); } if (timer == 0) { EL(qlt, "flash timeout\n"); return (QLT_FLASH_TIMEOUT); } else if (REG_RD32(qlt, REG_CTRL_STATUS) & FLASH_ERROR) { EL(qlt, "flash access error\n"); return (QLT_FLASH_ACCESS_ERROR); } *bp = REG_RD32(qlt, REG_FLASH_DATA); return (QLT_SUCCESS); } fct_status_t qlt_read_nvram(qlt_state_t *qlt) { uint32_t index, addr, chksum; uint32_t val, *ptr; fct_status_t ret; qlt_nvram_t *nv; uint64_t empty_node_name = 0; if (qlt->qlt_27xx_chip) { int func; func = ((REG_RD32(qlt, REG_CTRL_STATUS) & 0x0000f000) >> 12); switch (func) { case 0: addr = QLT27_NVRAM_FUNC0_ADDR; break; case 1: addr = QLT27_NVRAM_FUNC1_ADDR; break; case 2: addr = QLT27_NVRAM_FUNC2_ADDR; break; case 3: addr = QLT27_NVRAM_FUNC3_ADDR; break; } } else if (qlt->qlt_83xx_chip) { if (qlt->qlt_fcoe_enabled) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT83FCOE_NVRAM_FUNC1_ADDR : QLT83FCOE_NVRAM_FUNC0_ADDR; } else { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT83FC_NVRAM_FUNC1_ADDR : QLT83FC_NVRAM_FUNC0_ADDR; } } else if (qlt->qlt_81xx_chip) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT81_NVRAM_FUNC1_ADDR : QLT81_NVRAM_FUNC0_ADDR; } else if (qlt->qlt_25xx_chip) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & FUNCTION_NUMBER ? QLT25_NVRAM_FUNC1_ADDR : QLT25_NVRAM_FUNC0_ADDR; } else { addr = REG_RD32(qlt, REG_CTRL_STATUS) & FUNCTION_NUMBER ? NVRAM_FUNC1_ADDR : NVRAM_FUNC0_ADDR; } mutex_enter(&qlt_global_lock); /* Pause RISC. */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ /* Get NVRAM data and calculate checksum. */ ptr = (uint32_t *)qlt->nvram; chksum = 0; for (index = 0; index < sizeof (qlt_nvram_t) / 4; index++) { ret = qlt_read_flash_word(qlt, addr++, &val); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_read_flash_word, status=%llxh\n", ret); mutex_exit(&qlt_global_lock); return (ret); } chksum += val; *ptr = LE_32(val); ptr++; } /* Release RISC Pause */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ mutex_exit(&qlt_global_lock); /* Sanity check NVRAM Data */ nv = qlt->nvram; if (chksum || nv->id[0] != 'I' || nv->id[1] != 'S' || nv->id[2] != 'P' || nv->id[3] != ' ' || (nv->nvram_version[0] | nv->nvram_version[1]) == 0) { EL(qlt, "chksum=%xh, id=%c%c%c%c, ver=%02d%02d\n", chksum, nv->id[0], nv->id[1], nv->id[2], nv->id[3], nv->nvram_version[1], nv->nvram_version[0]); return (QLT_BAD_NVRAM_DATA); } /* If node name is zero, hand craft it from port name */ if (bcmp(nv->node_name, &empty_node_name, 8) == 0) { bcopy(nv->port_name, nv->node_name, 8); nv->node_name[0] = (uint8_t)(nv->node_name[0] & ~BIT_0); nv->port_name[0] = (uint8_t)(nv->node_name[0] | BIT_0); } return (QLT_SUCCESS); } fct_status_t qlt_read_vpd(qlt_state_t *qlt) { uint32_t index, addr, chksum; uint32_t val, *ptr; fct_status_t ret; if (qlt->qlt_27xx_chip) { int func; func = ((REG_RD32(qlt, REG_CTRL_STATUS) & 0x0000f000) >> 12); switch (func) { case 0: addr = QLT27_VPD_FUNC0_ADDR; break; case 1: addr = QLT27_VPD_FUNC1_ADDR; break; case 2: addr = QLT27_VPD_FUNC2_ADDR; break; case 3: addr = QLT27_VPD_FUNC3_ADDR; break; } } else if (qlt->qlt_83xx_chip) { if (qlt->qlt_fcoe_enabled) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT83FCOE_VPD_FUNC1_ADDR : QLT83FCOE_VPD_FUNC0_ADDR; } else { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT83FC_VPD_FUNC1_ADDR : QLT83FC_VPD_FUNC0_ADDR; } } else if (qlt->qlt_81xx_chip) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & BIT_12 ? QLT81_VPD_FUNC1_ADDR : QLT81_VPD_FUNC0_ADDR; } else if (qlt->qlt_25xx_chip) { addr = REG_RD32(qlt, REG_CTRL_STATUS) & FUNCTION_NUMBER ? QLT25_VPD_FUNC1_ADDR : QLT25_VPD_FUNC0_ADDR; } else { addr = REG_RD32(qlt, REG_CTRL_STATUS) & FUNCTION_NUMBER ? QLT24_VPD_FUNC1_ADDR : QLT24_VPD_FUNC0_ADDR; } mutex_enter(&qlt_global_lock); /* Pause RISC. */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ /* Get VPD data and calculate checksum. */ ptr = (uint32_t *)qlt->vpd; chksum = 0; for (index = 0; index < QL_24XX_VPD_SIZE / 4; index++) { ret = qlt_read_flash_word(qlt, addr++, &val); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_read_flash_word, status=%llxh\n", ret); mutex_exit(&qlt_global_lock); return (ret); } chksum += val; *ptr = LE_32(val); ptr++; } /* Release RISC Pause */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ mutex_exit(&qlt_global_lock); return (QLT_SUCCESS); } fct_status_t qlt_read_bfe(qlt_state_t *qlt, uint32_t in_addr, uint32_t *out_addr, uint32_t i) { uint32_t index; uint32_t chksum; fct_status_t ret = QLT_SUCCESS; uint32_t val; uint16_t dataoffset; uint32_t *ptr; uint32_t addr, addr0; uint16_t length; val = chksum = 0; ptr = (uint32_t *)&qlt->rimage[i].header; addr = in_addr; addr0 = addr; /* read rom header first */ for (index = 0; index < sizeof (qlt_rom_header_t)/4; index ++) { ret = qlt_read_flash_word(qlt, addr++, &val); if (ret != QLT_SUCCESS) { EL(qlt, "read flash, status=%llxh\n", ret); return (ret); } chksum += val; *ptr = LE_32(val); ptr++; } /* check the signature */ if (qlt->rimage[i].header.signature[0] != PCI_HEADER0) { EL(qlt, "hdr[%d] sig[1] [0] (%xh) (%xh) is wrong.\n", i, qlt->rimage[i].header.signature[1], qlt->rimage[i].header.signature[0]); return (QLT_SUCCESS); } if ((qlt->rimage[i].header.signature[0] == PCI_HEADER0) && (qlt->rimage[i].header.signature[1] == PCI_HEADER1)) { /* get dataoffset */ dataoffset = (qlt->rimage[i].header.dataoffset[1] | qlt->rimage[i].header.dataoffset[0]); EL(qlt, "dataoffset[0] = %xh\n", dataoffset); ptr = (uint32_t *)&qlt->rimage[i].data; /* adjust addr */ addr = addr0 + (dataoffset/4); for (index = 0; index < sizeof (qlt_rom_data_t)/4; index ++) { ret = qlt_read_flash_word(qlt, addr++, &val); if (ret != QLT_SUCCESS) { EL(qlt, "read flash, status=%llxh\n", ret); return (ret); } chksum += val; *ptr = LE_32(val); ptr++; } /* check signature */ if ((qlt->rimage[i].data.signature[0] != 0x50) && (qlt->rimage[i].data.signature[1] != 0x43) && (qlt->rimage[i].data.signature[2] != 0x49) && (qlt->rimage[i].data.signature[3] != 0x52)) { EL(qlt, "data sig[3] [2] [1] [0] (%xh)(%xh)(%xh)(%xh)\n", qlt->rimage[i].data.signature[3], qlt->rimage[i].data.signature[2], qlt->rimage[i].data.signature[1], qlt->rimage[i].data.signature[0]); return (QLT_SUCCESS); } EL(qlt, "codetype (%xh) revisionlevel[1][0] (%xh)(%xh)\n", qlt->rimage[i].data.codetype, qlt->rimage[i].data.revisionlevel[1], qlt->rimage[i].data.revisionlevel[0]); /* check if this is the last image */ if (qlt->rimage[i].data.indicator == PCI_IND_LAST_IMAGE) { EL(qlt, "last image (%xh)\n", qlt->rimage[i].data.indicator); return (QLT_SUCCESS); } /* Get the image length and adjust the addr according */ length = (qlt->rimage[i].data.imagelength[1] | qlt->rimage[i].data.imagelength[0]); EL(qlt, "image[%d] length[1][0] (%xh) (%xh) in sectors\n", i, length); /* the starting addr of the next image */ addr = addr0 + ((length * 512)/4); *out_addr = addr; } return (QLT_SUCCESS); } fct_status_t qlt_read_rom_image(qlt_state_t *qlt) { uint32_t addr; uint32_t out_addr = 0; uint32_t count = 0; boolean_t last_image = FALSE; fct_status_t ret; if (qlt->qlt_27xx_chip) { addr = FLASH_2700_DATA_ADDR + FLASH_2700_BOOT_CODE_ADDR; } else if (qlt->qlt_83xx_chip) { addr = FLASH_8300_DATA_ADDR + FLASH_8300_BOOT_CODE_ADDR; } else if (qlt->qlt_81xx_chip) { addr = FLASH_8100_DATA_ADDR + FLASH_8100_BOOT_CODE_ADDR; } else if (qlt->qlt_25xx_chip) { addr = FLASH_2500_DATA_ADDR + FLASH_2500_BOOT_CODE_ADDR; } else { addr = FLASH_2400_DATA_ADDR + FLASH_2400_BOOT_CODE_ADDR; } mutex_enter(&qlt_global_lock); /* Pause RISC. */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ do { ret = qlt_read_bfe(qlt, addr, &out_addr, count); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_read_bfe, status=%llxh\n", ret); break; } if (qlt->rimage[count].data.indicator == PCI_IND_LAST_IMAGE) { last_image = TRUE; } else { addr = out_addr; } count ++; } while ((last_image != TRUE) && (count < 6)); /* Release RISC Pause */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_PAUSE)); (void) REG_RD32(qlt, REG_HCCR); /* PCI Posting. */ mutex_exit(&qlt_global_lock); return (QLT_SUCCESS); } uint32_t qlt_sync_atio_queue(qlt_state_t *qlt) { uint32_t total_ent; if (qlt->atio_ndx_from_fw > qlt->atio_ndx_to_fw) { total_ent = qlt->atio_ndx_from_fw - qlt->atio_ndx_to_fw; (void) ddi_dma_sync(qlt->queue_mem_dma_handle, ATIO_QUEUE_OFFSET + (qlt->atio_ndx_to_fw << 6), total_ent << 6, DDI_DMA_SYNC_FORCPU); } else { total_ent = ATIO_QUEUE_ENTRIES - qlt->atio_ndx_to_fw + qlt->atio_ndx_from_fw; (void) ddi_dma_sync(qlt->queue_mem_dma_handle, ATIO_QUEUE_OFFSET + (qlt->atio_ndx_to_fw << 6), (uint_t)(ATIO_QUEUE_ENTRIES - qlt->atio_ndx_to_fw) << 6, DDI_DMA_SYNC_FORCPU); (void) ddi_dma_sync(qlt->queue_mem_dma_handle, ATIO_QUEUE_OFFSET, (uint_t)(qlt->atio_ndx_from_fw << 6), DDI_DMA_SYNC_FORCPU); } return (total_ent); } void qlt_handle_atio_queue_update(qlt_state_t *qlt) { uint32_t total_ent; if (qlt->atio_ndx_to_fw == qlt->atio_ndx_from_fw) return; total_ent = qlt_sync_atio_queue(qlt); do { uint8_t *atio = (uint8_t *)&qlt->atio_ptr[ qlt->atio_ndx_to_fw << 6]; uint32_t ent_cnt; ent_cnt = (uint32_t)(atio[1]); if (ent_cnt > total_ent) { break; } switch ((uint8_t)(atio[0])) { case 0x06: /* ATIO, make performance case the 1st test */ qlt_handle_atio(qlt, atio); break; case 0x0d: /* INOT */ qlt_handle_inot(qlt, atio); break; default: EL(qlt, "atio_queue_update atio[0]=%xh\n", atio[0]); cmn_err(CE_WARN, "qlt_handle_atio_queue_update: " "atio[0] is %x, qlt-%p", atio[0], (void *)qlt); break; } qlt->atio_ndx_to_fw = (uint16_t)( (qlt->atio_ndx_to_fw + ent_cnt) & (ATIO_QUEUE_ENTRIES - 1)); total_ent -= ent_cnt; } while (total_ent > 0); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { MQBAR_WR32(qlt, MQBAR_ATIO_OUT, qlt->atio_ndx_to_fw); } else { REG_WR32(qlt, REG_ATIO_OUT_PTR, qlt->atio_ndx_to_fw); } } uint32_t qlt_sync_resp_queue(qlt_state_t *qlt, uint16_t qi) { uint32_t total_ent; if (qlt->mq_resp[qi].mq_ndx_from_fw > qlt->mq_resp[qi].mq_ndx_to_fw) { total_ent = qlt->mq_resp[qi].mq_ndx_from_fw - qlt->mq_resp[qi].mq_ndx_to_fw; if (qi) { (void) ddi_dma_sync( qlt->mq_resp[qi].queue_mem_mq_dma_handle, (qlt->mq_resp[qi].mq_ndx_to_fw << 6), total_ent << 6, DDI_DMA_SYNC_FORCPU); } else { (void) ddi_dma_sync( qlt->queue_mem_dma_handle, RESPONSE_QUEUE_OFFSET + (qlt->mq_resp[qi].mq_ndx_to_fw << 6), total_ent << 6, DDI_DMA_SYNC_FORCPU); } } else { total_ent = (qi ? RESPONSE_QUEUE_MQ_ENTRIES : RESPONSE_QUEUE_ENTRIES) - qlt->mq_resp[qi].mq_ndx_to_fw + qlt->mq_resp[qi].mq_ndx_from_fw; if (qi) { (void) ddi_dma_sync( qlt->mq_resp[qi].queue_mem_mq_dma_handle, qlt->mq_resp[qi].mq_ndx_to_fw << 6, (RESPONSE_QUEUE_MQ_ENTRIES - qlt->mq_resp[qi].mq_ndx_to_fw) << 6, DDI_DMA_SYNC_FORCPU); (void) ddi_dma_sync( qlt->mq_resp[qi].queue_mem_mq_dma_handle, 0, qlt->mq_resp[qi].mq_ndx_from_fw << 6, DDI_DMA_SYNC_FORCPU); } else { (void) ddi_dma_sync(qlt->queue_mem_dma_handle, RESPONSE_QUEUE_OFFSET + (qlt->mq_resp[qi].mq_ndx_to_fw << 6), (RESPONSE_QUEUE_ENTRIES - qlt->mq_resp[qi].mq_ndx_to_fw) << 6, DDI_DMA_SYNC_FORCPU); (void) ddi_dma_sync(qlt->queue_mem_dma_handle, RESPONSE_QUEUE_OFFSET, qlt->mq_resp[qi].mq_ndx_from_fw << 6, DDI_DMA_SYNC_FORCPU); } } return (total_ent); } void qlt_handle_resp_queue_update(qlt_state_t *qlt, uint16_t qi) { uint32_t total_ent; uint8_t c; if (qlt->mq_resp[qi].mq_ndx_to_fw == qlt->mq_resp[qi].mq_ndx_from_fw) return; total_ent = qlt_sync_resp_queue(qlt, qi); do { uint32_t qe = qlt->mq_resp[qi].mq_ndx_to_fw; caddr_t resp = &qlt->mq_resp[qi].mq_ptr[qe << 6]; uint32_t ent_cnt; ent_cnt = (uint32_t)(resp[0] == 0x51 ? resp[1] : 1); if (ent_cnt > total_ent) { break; } switch ((uint8_t)(resp[0])) { case 0x12: /* CTIO completion */ qlt_handle_ctio_completion(qlt, (uint8_t *)resp, qi); break; case 0x0e: /* NACK */ /* Do Nothing */ break; case 0x1b: /* Verify FW */ qlt_handle_verify_fw_completion(qlt, (uint8_t *)resp); break; case 0x29: /* CT PassThrough */ qlt_handle_ct_completion(qlt, (uint8_t *)resp); break; case 0x32: /* Report ID */ EL(qlt, "report Id received [type %xh]\n", resp[0]); break; case 0x33: /* Abort IO IOCB completion */ qlt_handle_sol_abort_completion(qlt, (uint8_t *)resp); break; case 0x51: /* PUREX */ qlt_handle_purex(qlt, (uint8_t *)resp); break; case 0x52: qlt_handle_dereg_completion(qlt, (uint8_t *)resp); break; case 0x53: /* ELS passthrough */ c = (uint8_t)(((uint8_t)resp[0x1f]) >> 5); if (c == 0) { qlt_handle_sol_els_completion(qlt, (uint8_t *)resp); } else if (c == 3) { qlt_handle_unsol_els_abort_completion(qlt, (uint8_t *)resp); } else { qlt_handle_unsol_els_completion(qlt, (uint8_t *)resp); } break; case 0x54: /* ABTS received */ qlt_handle_rcvd_abts(qlt, (uint8_t *)resp, qi); break; case 0x55: /* ABTS completion */ qlt_handle_abts_completion(qlt, (uint8_t *)resp, qi); break; default: EL(qlt, "response entry=%xh\n", resp[0]); break; } if (qi != 0) { qlt->mq_resp[qi].mq_ndx_to_fw = (qlt->mq_resp[qi].mq_ndx_to_fw + ent_cnt) & (RESPONSE_QUEUE_MQ_ENTRIES - 1); } else { qlt->mq_resp[qi].mq_ndx_to_fw = (qlt->mq_resp[qi].mq_ndx_to_fw + ent_cnt) & (RESPONSE_QUEUE_ENTRIES - 1); } total_ent -= ent_cnt; } while (total_ent > 0); if (qlt->qlt_mq_enabled) { MQBAR_WR32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_OUT, qlt->mq_resp[qi].mq_ndx_to_fw); } else { REG_WR32(qlt, REG_RESP_OUT_PTR, qlt->mq_resp[qi].mq_ndx_to_fw); } } fct_status_t qlt_portid_to_handle(qlt_state_t *qlt, uint32_t id, uint16_t cmd_handle, uint16_t *ret_handle) { fct_status_t ret; mbox_cmd_t *mcp; uint16_t n; uint16_t h; uint32_t ent_id; uint8_t *p; int found = 0; mcp = qlt_alloc_mailbox_command(qlt, 2048 * 8); if (mcp == NULL) { return (STMF_ALLOC_FAILURE); } mcp->to_fw[0] = MBC_GET_ID_LIST; mcp->to_fw[8] = 2048 * 8; mcp->to_fw[9] = 0; mcp->to_fw_mask |= BIT_9 | BIT_8; mcp->from_fw_mask |= BIT_1 | BIT_2; ret = qlt_mailbox_command(qlt, mcp); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_mbox_command=7Ch status=%llxh\n", ret); cmn_err(CE_WARN, "qlt(%d) GET ID list failed, ret = %llx, " "mb0=%x, mb1=%x, mb2=%x", qlt->instance, (long long)ret, mcp->from_fw[0], mcp->from_fw[1], mcp->from_fw[2]); qlt_free_mailbox_command(qlt, mcp); return (ret); } EL(qlt, "mbx cmd=7Ch, GET_ID_LIST id=%x fw[1]=%x\n", id, mcp->from_fw[1]); qlt_dmem_dma_sync(mcp->dbuf, DDI_DMA_SYNC_FORCPU); p = mcp->dbuf->db_sglist[0].seg_addr; for (n = 0; n < mcp->from_fw[1]; n++) { ent_id = LE_32(*((uint32_t *)p)) & 0xFFFFFF; h = (uint16_t)((uint16_t)p[4] | (((uint16_t)p[5]) << 8)); if (ent_id == id) { found = 1; *ret_handle = h; if ((cmd_handle != FCT_HANDLE_NONE) && (cmd_handle != h)) { cmn_err(CE_WARN, "qlt(%d) login for portid %x " "came in with handle %x, while the portid " "was already using a different handle %x", qlt->instance, id, cmd_handle, h); qlt_free_mailbox_command(qlt, mcp); return (QLT_FAILURE); } break; } if ((cmd_handle != FCT_HANDLE_NONE) && (h == cmd_handle)) { cmn_err(CE_WARN, "qlt(%d) login for portid %x came in " "with handle %x, while the handle was already in " "use for portid %x", qlt->instance, id, cmd_handle, ent_id); qlt_free_mailbox_command(qlt, mcp); return (QLT_FAILURE); } p += 8; } if (!found) { *ret_handle = cmd_handle; } qlt_free_mailbox_command(qlt, mcp); return (FCT_SUCCESS); } /* ARGSUSED */ fct_status_t qlt_fill_plogi_req(fct_local_port_t *port, fct_remote_port_t *rp, fct_cmd_t *login) { uint8_t *p; p = ((fct_els_t *)login->cmd_specific)->els_req_payload; p[0] = ELS_OP_PLOGI; *((uint16_t *)(&p[4])) = 0x2020; p[7] = 3; p[8] = 0x88; p[10] = 8; p[13] = 0xff; p[15] = 0x1f; p[18] = 7; p[19] = 0xd0; bcopy(port->port_pwwn, p + 20, 8); bcopy(port->port_nwwn, p + 28, 8); p[68] = 0x80; p[74] = 8; p[77] = 0xff; p[81] = 1; return (FCT_SUCCESS); } /* ARGSUSED */ fct_status_t qlt_fill_plogi_resp(fct_local_port_t *port, fct_remote_port_t *rp, fct_cmd_t *login) { return (FCT_SUCCESS); } fct_status_t qlt_register_remote_port(fct_local_port_t *port, fct_remote_port_t *rp, fct_cmd_t *login) { uint16_t h; fct_status_t ret; qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; switch (rp->rp_id) { case 0xFFFFFC: h = 0x7FC; break; case 0xFFFFFD: h = 0x7FD; break; case 0xFFFFFE: h = 0x7FE; break; case 0xFFFFFF: h = 0x7FF; break; default: ret = qlt_portid_to_handle(qlt, rp->rp_id, login->cmd_rp_handle, &h); if (ret != FCT_SUCCESS) { EL(qlt, "qlt_portid_to_handle, status=%llxh\n", ret); return (ret); } } if (login->cmd_type == FCT_CMD_SOL_ELS) { ret = qlt_fill_plogi_req(port, rp, login); } else { ret = qlt_fill_plogi_resp(port, rp, login); } if (ret != FCT_SUCCESS) { EL(qlt, "qlt_fill_plogi, status=%llxh\n", ret); return (ret); } EL(qlt, "rport id=%xh cmd_type=%xh handle=%xh(%xh)\n", rp->rp_id, login->cmd_type, h, rp->rp_handle); if (h == FCT_HANDLE_NONE) return (FCT_SUCCESS); if (rp->rp_handle == FCT_HANDLE_NONE) { rp->rp_handle = h; return (FCT_SUCCESS); } if (rp->rp_handle == h) return (FCT_SUCCESS); EL(qlt, "failed, rp_handle=%xh != h=%xh\n", rp->rp_handle, h); return (FCT_FAILURE); } /* invoked in single thread */ fct_status_t qlt_deregister_remote_port(fct_local_port_t *port, fct_remote_port_t *rp) { uint8_t *req; qlt_state_t *qlt; clock_t dereg_req_timer; fct_status_t ret; qlt = (qlt_state_t *)port->port_fca_private; if ((qlt->qlt_state == FCT_STATE_OFFLINE) || (qlt->qlt_state == FCT_STATE_OFFLINING)) return (FCT_SUCCESS); ASSERT(qlt->rp_id_in_dereg == 0); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_enter(&qlt->mq_req[0].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, 0); if (req == NULL) { EL(qlt, "req = NULL\n"); mutex_exit(&qlt->mq_req[0].mq_lock); return (FCT_BUSY); } } else { mutex_enter(&qlt->preq_lock); req = (uint8_t *)qlt_get_preq_entries(qlt, 1); if (req == NULL) { EL(qlt, "req = NULL\n"); mutex_exit(&qlt->preq_lock); return (FCT_BUSY); } } bzero(req, IOCB_SIZE); req[0] = 0x52; req[1] = 1; /* QMEM_WR32(qlt, (&req[4]), 0xffffffff); */ QMEM_WR16(qlt, (&req[0xA]), rp->rp_handle); QMEM_WR16(qlt, (&req[0xC]), 0x98); /* implicit logo */ QMEM_WR32(qlt, (&req[0x10]), rp->rp_id); qlt->rp_id_in_dereg = rp->rp_id; if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { qlt_submit_req_entries(qlt, 1, 0); } else { qlt_submit_preq_entries(qlt, 1); } dereg_req_timer = ddi_get_lbolt() + drv_usectohz(DEREG_RP_TIMEOUT); if (cv_timedwait(&qlt->rp_dereg_cv, (((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) ? &qlt->mq_req[0].mq_lock : &qlt->preq_lock), dereg_req_timer) > 0) { ret = qlt->rp_dereg_status; } else { ret = FCT_BUSY; } qlt->rp_dereg_status = 0; qlt->rp_id_in_dereg = 0; if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_exit(&qlt->mq_req[0].mq_lock); } else { mutex_exit(&qlt->preq_lock); } EL(qlt, "Dereg remote port(%Xh), ret=%llxh\n", rp->rp_id, ret); return (ret); } /* * Pass received ELS up to framework. */ static void qlt_handle_purex(qlt_state_t *qlt, uint8_t *resp) { fct_cmd_t *cmd; fct_els_t *els; qlt_cmd_t *qcmd; uint32_t payload_size; uint32_t remote_portid; uint8_t *pldptr, *bndrptr; int i, off; uint16_t iocb_flags; char info[160]; remote_portid = ((uint32_t)(QMEM_RD16(qlt, (&resp[0x18])))) | ((uint32_t)(resp[0x1A])) << 16; iocb_flags = QMEM_RD16(qlt, (&resp[8])); if (iocb_flags & BIT_15) { payload_size = (QMEM_RD16(qlt, (&resp[0x0e])) & 0xfff) - 24; } else { payload_size = QMEM_RD16(qlt, (&resp[0x0c])) - 24; } if (payload_size > ((uint32_t)resp[1] * IOCB_SIZE - 0x2C)) { EL(qlt, "payload is too large = %xh\n", payload_size); cmn_err(CE_WARN, "handle_purex: payload is too large"); goto cmd_null; } cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_RCVD_ELS, (int)(payload_size + GET_STRUCT_SIZE(qlt_cmd_t)), 0); if (cmd == NULL) { EL(qlt, "fct_alloc cmd==NULL\n"); cmd_null:; (void) snprintf(info, 160, "qlt_handle_purex: qlt-%p, can't " "allocate space for fct_cmd", (void *)qlt); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } cmd->cmd_port = qlt->qlt_port; cmd->cmd_rp_handle = QMEM_RD16(qlt, resp+0xa); if (cmd->cmd_rp_handle == 0xFFFF) { cmd->cmd_rp_handle = FCT_HANDLE_NONE; } els = (fct_els_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; els->els_req_size = (uint16_t)payload_size; els->els_req_payload = GET_BYTE_OFFSET(qcmd, GET_STRUCT_SIZE(qlt_cmd_t)); qcmd->fw_xchg_addr = QMEM_RD32(qlt, (&resp[0x10])); cmd->cmd_rportid = remote_portid; cmd->cmd_lportid = ((uint32_t)(QMEM_RD16(qlt, (&resp[0x14])))) | ((uint32_t)(resp[0x16])) << 16; cmd->cmd_oxid = QMEM_RD16(qlt, (&resp[0x26])); cmd->cmd_rxid = QMEM_RD16(qlt, (&resp[0x24])); pldptr = &resp[0x2C]; bndrptr = (uint8_t *)(qlt->mq_resp[0].mq_ptr + (RESPONSE_QUEUE_ENTRIES << 6)); for (i = 0, off = 0x2c; i < payload_size; i += 4) { /* Take care of fw's swapping of payload */ els->els_req_payload[i] = pldptr[3]; els->els_req_payload[i+1] = pldptr[2]; els->els_req_payload[i+2] = pldptr[1]; els->els_req_payload[i+3] = pldptr[0]; pldptr += 4; if (pldptr == bndrptr) pldptr = (uint8_t *)qlt->mq_resp[0].mq_ptr; off += 4; if (off >= IOCB_SIZE) { off = 4; pldptr += 4; } } EL(qlt, "remote portid = %xh logi/o(%xh) to us revd rex1=%xh\n", remote_portid, els->els_req_payload[0], qcmd->fw_xchg_addr); fct_post_rcvd_cmd(cmd, 0); } fct_status_t qlt_send_cmd_response(fct_cmd_t *cmd, uint32_t ioflags) { qlt_state_t *qlt; char info[160]; qlt = (qlt_state_t *)cmd->cmd_port->port_fca_private; if (cmd->cmd_type == FCT_CMD_FCP_XCHG) { if (ioflags & FCT_IOF_FORCE_FCA_DONE) { EL(qlt, "ioflags = %xh\n", ioflags); goto fatal_panic; } else { return (qlt_send_status(qlt, cmd)); } } if (cmd->cmd_type == FCT_CMD_RCVD_ELS) { if (ioflags & FCT_IOF_FORCE_FCA_DONE) { goto fatal_panic; } else { return (qlt_send_els_response(qlt, cmd)); } } if (ioflags & FCT_IOF_FORCE_FCA_DONE) { cmd->cmd_handle = 0; } if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) { return (qlt_send_abts_response(qlt, cmd, 0)); } else { EL(qlt, "cmd->cmd_type=%xh\n", cmd->cmd_type); ASSERT(0); return (FCT_FAILURE); } fatal_panic:; (void) snprintf(info, 160, "qlt_send_cmd_response: can not handle " "FCT_IOF_FORCE_FCA_DONE for cmd %p, ioflags-%x", (void *)cmd, ioflags); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return (FCT_FAILURE); } /* ARGSUSED */ fct_status_t qlt_xfer_scsi_data(fct_cmd_t *cmd, stmf_data_buf_t *dbuf, uint32_t ioflags) { qlt_dmem_bctl_t *bctl = (qlt_dmem_bctl_t *)dbuf->db_port_private; qlt_state_t *qlt = (qlt_state_t *)cmd->cmd_port->port_fca_private; qlt_cmd_t *qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; uint8_t *req, rcnt; uint16_t flags; uint16_t cookie_count; uint32_t ent_cnt; uint16_t qi; qi = qcmd->qid; if (dbuf->db_handle == 0) qcmd->dbuf = dbuf; flags = (uint16_t)(((uint16_t)qcmd->param.atio_byte3 & 0xf0) << 5); if (dbuf->db_flags & DB_DIRECTION_TO_RPORT) { flags = (uint16_t)(flags | 2); qlt_dmem_dma_sync(dbuf, DDI_DMA_SYNC_FORDEV); } else { flags = (uint16_t)(flags | 1); } if (dbuf->db_flags & DB_SEND_STATUS_GOOD) flags = (uint16_t)(flags | BIT_15); if (dbuf->db_flags & DB_LU_DATA_BUF) { /* * Data bufs from LU are in scatter/gather list format. */ cookie_count = qlt_get_cookie_count(dbuf); rcnt = qlt_get_iocb_count(cookie_count); } else { cookie_count = 1; rcnt = 1; } mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = rcnt; req[2] = dbuf->db_handle; QMEM_WR32_REQ(qlt, qi, req+4, cmd->cmd_handle); QMEM_WR16_REQ(qlt, qi, req+8, cmd->cmd_rp->rp_handle); QMEM_WR16_REQ(qlt, qi, req+10, 60); /* 60 seconds timeout */ QMEM_WR16_REQ(qlt, qi, req+12, cookie_count); QMEM_WR32_REQ(qlt, qi, req+0x10, cmd->cmd_rportid); QMEM_WR32_REQ(qlt, qi, req+0x14, qcmd->fw_xchg_addr); QMEM_WR16_REQ(qlt, qi, req+0x1A, flags); QMEM_WR16_REQ(qlt, qi, req+0x20, cmd->cmd_oxid); QMEM_WR32_REQ(qlt, qi, req+0x24, dbuf->db_relative_offset); QMEM_WR32_REQ(qlt, qi, req+0x2C, dbuf->db_data_size); if (dbuf->db_flags & DB_LU_DATA_BUF) { uint8_t *qptr; /* qlt continuation segs */ uint16_t cookie_resid; uint16_t cont_segs; ddi_dma_cookie_t cookie, *ckp; /* * See if the dma cookies are in simple array format. */ ckp = qlt_get_cookie_array(dbuf); /* * Program the first segment into main record. */ if (ckp) { ASSERT(ckp->dmac_size); QMEM_WR64_REQ(qlt, qi, req+0x34, ckp->dmac_laddress); QMEM_WR32_REQ(qlt, qi, req+0x3c, ckp->dmac_size); } else { qlt_ddi_dma_nextcookie(dbuf, &cookie); ASSERT(cookie.dmac_size); QMEM_WR64_REQ(qlt, qi, req+0x34, cookie.dmac_laddress); QMEM_WR32_REQ(qlt, qi, req+0x3c, cookie.dmac_size); } cookie_resid = cookie_count-1; ent_cnt = (qi == 0) ? REQUEST_QUEUE_ENTRIES : REQUEST_QUEUE_MQ_ENTRIES; /* * Program remaining segments into continuation records. */ while (cookie_resid) { req += IOCB_SIZE; if (req >= (uint8_t *)(qlt->mq_req[qi].mq_ptr + (ent_cnt * IOCB_SIZE))) { req = (uint8_t *)(qlt->mq_req[qi].mq_ptr); } req[0] = 0x0a; req[1] = 1; req[2] = req[3] = 0; /* tidy */ qptr = &req[4]; for (cont_segs = CONT_A64_DATA_SEGMENTS; cont_segs && cookie_resid; cont_segs--) { if (ckp) { ++ckp; /* next cookie */ ASSERT(ckp->dmac_size != 0); QMEM_WR64_REQ(qlt, qi, qptr, ckp->dmac_laddress); qptr += 8; /* skip over laddress */ QMEM_WR32_REQ(qlt, qi, qptr, ckp->dmac_size); qptr += 4; /* skip over size */ } else { qlt_ddi_dma_nextcookie(dbuf, &cookie); ASSERT(cookie.dmac_size != 0); QMEM_WR64_REQ(qlt, qi, qptr, cookie.dmac_laddress); qptr += 8; /* skip over laddress */ QMEM_WR32_REQ(qlt, qi, qptr, cookie.dmac_size); qptr += 4; /* skip over size */ } cookie_resid--; } /* * zero unused remainder of IOCB */ if (cont_segs) { size_t resid; resid = (size_t)((uintptr_t)(req+IOCB_SIZE) - (uintptr_t)qptr); ASSERT(resid < IOCB_SIZE); bzero(qptr, resid); } } } else { /* Single, contiguous buffer */ QMEM_WR64_REQ(qlt, qi, req+0x34, bctl->bctl_dev_addr); QMEM_WR32_REQ(qlt, qi, req+0x34+8, dbuf->db_data_size); } qlt_submit_req_entries(qlt, rcnt, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (STMF_SUCCESS); } /* * We must construct proper FCP_RSP_IU now. Here we only focus on * the handling of FCP_SNS_INFO. If there's protocol failures (FCP_RSP_INFO), * we could have caught them before we enter here. */ fct_status_t qlt_send_status(qlt_state_t *qlt, fct_cmd_t *cmd) { qlt_cmd_t *qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; scsi_task_t *task = (scsi_task_t *)cmd->cmd_specific; qlt_dmem_bctl_t *bctl; uint32_t size; uint8_t *req, *fcp_rsp_iu; uint8_t *psd, sensbuf[24]; /* sense data */ uint16_t flags; uint16_t scsi_status; int use_mode2; int ndx; uint16_t qi; qi = qcmd->qid; /* * Enter fast channel for non check condition */ if (task->task_scsi_status != STATUS_CHECK) { /* * We will use mode1 */ flags = (uint16_t)(BIT_6 | BIT_15 | (((uint16_t)qcmd->param.atio_byte3 & 0xf0) << 5)); scsi_status = (uint16_t)task->task_scsi_status; if (task->task_status_ctrl == TASK_SCTRL_OVER) { scsi_status = (uint16_t)(scsi_status | FCP_RESID_OVER); } else if (task->task_status_ctrl == TASK_SCTRL_UNDER) { scsi_status = (uint16_t)(scsi_status | FCP_RESID_UNDER); } qcmd->dbuf_rsp_iu = NULL; /* * Fillout CTIO type 7 IOCB */ mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } /* * Common fields */ bzero(req, IOCB_SIZE); req[0x00] = 0x12; req[0x01] = 0x1; req[0x02] = BIT_7; /* indicate if it's a pure status req */ QMEM_WR32_REQ(qlt, qi, req + 0x04, cmd->cmd_handle); QMEM_WR16_REQ(qlt, qi, req + 0x08, cmd->cmd_rp->rp_handle); QMEM_WR32_REQ(qlt, qi, req + 0x10, cmd->cmd_rportid); QMEM_WR32_REQ(qlt, qi, req + 0x14, qcmd->fw_xchg_addr); /* handle TMF completion - !!! Important FIX */ if (task->task_mgmt_function) { scsi_status = (uint16_t)(scsi_status | FCP_RESP_LEN_VALID); /* no sense length, 4 bytes of resp info */ QMEM_WR16_REQ(qlt, qi, req + 0x24, 4); } /* * Mode-specific fields */ QMEM_WR16_REQ(qlt, qi, req + 0x1A, flags); QMEM_WR32_REQ(qlt, qi, req + 0x1C, task->task_resid); QMEM_WR16_REQ(qlt, qi, req + 0x20, cmd->cmd_oxid); QMEM_WR16_REQ(qlt, qi, req + 0x22, scsi_status); /* * Trigger FW to send SCSI status out */ qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (STMF_SUCCESS); } ASSERT(task->task_scsi_status == STATUS_CHECK); /* * Decide the SCSI status mode, that should be used */ use_mode2 = (task->task_sense_length > 24); /* * Prepare required information per the SCSI status mode */ flags = (uint16_t)(BIT_15 | (((uint16_t)qcmd->param.atio_byte3 & 0xf0) << 5)); if (use_mode2) { flags = (uint16_t)(flags | BIT_7); size = task->task_sense_length; qcmd->dbuf_rsp_iu = qlt_i_dmem_alloc(qlt, task->task_sense_length, &size, 0); if (!qcmd->dbuf_rsp_iu) { return (FCT_ALLOC_FAILURE); } /* * Start to construct FCP_RSP IU */ fcp_rsp_iu = qcmd->dbuf_rsp_iu->db_sglist[0].seg_addr; bzero(fcp_rsp_iu, 24); /* * FCP_RSP IU flags, byte10 */ fcp_rsp_iu[10] = (uint8_t)(fcp_rsp_iu[10] | BIT_1); if (task->task_status_ctrl == TASK_SCTRL_OVER) { fcp_rsp_iu[10] = (uint8_t)(fcp_rsp_iu[10] | BIT_2); } else if (task->task_status_ctrl == TASK_SCTRL_UNDER) { fcp_rsp_iu[10] = (uint8_t)(fcp_rsp_iu[10] | BIT_3); } /* * SCSI status code, byte11 */ fcp_rsp_iu[11] = task->task_scsi_status; /* * FCP_RESID (Overrun or underrun) */ fcp_rsp_iu[12] = (uint8_t)((task->task_resid >> 24) & 0xFF); fcp_rsp_iu[13] = (uint8_t)((task->task_resid >> 16) & 0xFF); fcp_rsp_iu[14] = (uint8_t)((task->task_resid >> 8) & 0xFF); fcp_rsp_iu[15] = (uint8_t)((task->task_resid >> 0) & 0xFF); /* * FCP_SNS_LEN */ fcp_rsp_iu[18] = (uint8_t)((task->task_sense_length >> 8) & 0xFF); fcp_rsp_iu[19] = (uint8_t)((task->task_sense_length >> 0) & 0xFF); /* * FCP_RSP_LEN */ /* * no FCP_RSP_INFO */ /* * FCP_SNS_INFO */ bcopy(task->task_sense_data, fcp_rsp_iu + 24, task->task_sense_length); /* * Ensure dma data consistency */ qlt_dmem_dma_sync(qcmd->dbuf_rsp_iu, DDI_DMA_SYNC_FORDEV); } else { flags = (uint16_t)(flags | BIT_6); scsi_status = (uint16_t)task->task_scsi_status; if (task->task_status_ctrl == TASK_SCTRL_OVER) { scsi_status = (uint16_t)(scsi_status | BIT_10); } else if (task->task_status_ctrl == TASK_SCTRL_UNDER) { scsi_status = (uint16_t)(scsi_status | BIT_11); } if (task->task_sense_length) { scsi_status = (uint16_t)(scsi_status | BIT_9); } bcopy(task->task_sense_data, sensbuf, task->task_sense_length); qcmd->dbuf_rsp_iu = NULL; } /* * Fillout CTIO type 7 IOCB */ mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { mutex_exit(&qlt->mq_req[qi].mq_lock); if (use_mode2) { qlt_dmem_free(cmd->cmd_port->port_fds, qcmd->dbuf_rsp_iu); qcmd->dbuf_rsp_iu = NULL; } return (FCT_BUSY); } /* * Common fields */ bzero(req, IOCB_SIZE); req[0x00] = 0x12; req[0x01] = 0x1; req[0x02] = BIT_7; /* to indicate if it's a pure status req */ QMEM_WR32_REQ(qlt, qi, req + 0x04, cmd->cmd_handle); QMEM_WR16_REQ(qlt, qi, req + 0x08, cmd->cmd_rp->rp_handle); QMEM_WR16_REQ(qlt, qi, req + 0x0A, 0); /* not timed by FW */ if (use_mode2) { /* FCP RSP IU data field */ QMEM_WR16_REQ(qlt, qi, req+0x0C, 1); } QMEM_WR32_REQ(qlt, qi, req + 0x10, cmd->cmd_rportid); QMEM_WR32_REQ(qlt, qi, req + 0x14, qcmd->fw_xchg_addr); /* * Mode-specific fields */ if (!use_mode2) { QMEM_WR16_REQ(qlt, qi, req + 0x18, task->task_sense_length); } QMEM_WR16_REQ(qlt, qi, req + 0x1A, flags); QMEM_WR32_REQ(qlt, qi, req + 0x1C, task->task_resid); QMEM_WR16_REQ(qlt, qi, req + 0x20, cmd->cmd_oxid); if (use_mode2) { bctl = (qlt_dmem_bctl_t *)qcmd->dbuf_rsp_iu->db_port_private; QMEM_WR32_REQ(qlt, qi, req + 0x2C, 24 + task->task_sense_length); QMEM_WR64_REQ(qlt, qi, req + 0x34, bctl->bctl_dev_addr); QMEM_WR32_REQ(qlt, qi, req + 0x3C, 24 + task->task_sense_length); } else { QMEM_WR16_REQ(qlt, qi, req + 0x22, scsi_status); psd = req+0x28; /* * Data in sense buf is always big-endian, data in IOCB * should always be little-endian, so we must do swapping. */ size = ((task->task_sense_length + 3) & (~3)); for (ndx = 0; ndx < size; ndx += 4) { psd[ndx + 0] = sensbuf[ndx + 3]; psd[ndx + 1] = sensbuf[ndx + 2]; psd[ndx + 2] = sensbuf[ndx + 1]; psd[ndx + 3] = sensbuf[ndx + 0]; } } /* * Trigger FW to send SCSI status out */ qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (STMF_SUCCESS); } fct_status_t qlt_send_els_response(qlt_state_t *qlt, fct_cmd_t *cmd) { qlt_cmd_t *qcmd; fct_els_t *els = (fct_els_t *)cmd->cmd_specific; uint8_t *req, *addr; qlt_dmem_bctl_t *bctl; uint32_t minsize; uint8_t elsop, req1f; uint16_t qi = 0; addr = els->els_resp_payload; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; minsize = els->els_resp_size; qcmd->dbuf = qlt_i_dmem_alloc(qlt, els->els_resp_size, &minsize, 0); if (qcmd->dbuf == NULL) return (FCT_BUSY); bctl = (qlt_dmem_bctl_t *)qcmd->dbuf->db_port_private; bcopy(addr, qcmd->dbuf->db_sglist[0].seg_addr, els->els_resp_size); qlt_dmem_dma_sync(qcmd->dbuf, DDI_DMA_SYNC_FORDEV); if (addr[0] == 0x02) { /* ACC */ req1f = BIT_5; } else { req1f = BIT_6; } elsop = els->els_req_payload[0]; if ((elsop == ELS_OP_PRLI) || (elsop == ELS_OP_PRLO) || (elsop == ELS_OP_TPRLO) || (elsop == ELS_OP_LOGO)) { req1f = (uint8_t)(req1f | BIT_4); } mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req = NULL, %xh %xh %p %xh\n", cmd->cmd_oxid, cmd->cmd_rportid, cmd, qcmd->fw_xchg_addr); mutex_exit(&qlt->mq_req[qi].mq_lock); qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; return (FCT_BUSY); } bzero(req, IOCB_SIZE); req[0] = 0x53; req[1] = 1; req[0xf] = 0x10; req[0x16] = elsop; req[0x1f] = req1f; QMEM_WR32(qlt, (&req[4]), cmd->cmd_handle); QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp->rp_handle); QMEM_WR16(qlt, (&req[0xC]), 1); QMEM_WR32(qlt, (&req[0x10]), qcmd->fw_xchg_addr); QMEM_WR32(qlt, (&req[0x18]), cmd->cmd_rportid); if (qlt->cur_topology == PORT_TOPOLOGY_PT_TO_PT) { req[0x1b] = (uint8_t)((cmd->cmd_lportid >> 16) & 0xff); req[0x1c] = (uint8_t)(cmd->cmd_lportid & 0xff); req[0x1d] = (uint8_t)((cmd->cmd_lportid >> 8) & 0xff); } QMEM_WR32(qlt, (&req[0x24]), els->els_resp_size); QMEM_WR64(qlt, (&req[0x28]), bctl->bctl_dev_addr); QMEM_WR32(qlt, (&req[0x30]), els->els_resp_size); EL(qlt, "elsop=%xh req1f=%xh IOCB_TYPE_ELSPASS: rex1=%xh\n", elsop, req1f, qcmd->fw_xchg_addr); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } fct_status_t qlt_send_abts_response(qlt_state_t *qlt, fct_cmd_t *cmd, int terminate) { qlt_abts_cmd_t *qcmd; fct_rcvd_abts_t *abts = (fct_rcvd_abts_t *)cmd->cmd_specific; uint8_t *req; uint32_t lportid; uint32_t fctl; int i; uint16_t qi; uint32_t rex1, rex2; uint8_t temp[64]; qi = 0; qcmd = (qlt_abts_cmd_t *)cmd->cmd_fca_private; mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { bcopy(qcmd->buf, &temp, IOCB_SIZE); for (i = 0; i < 12; i += 4) { /* Take care of firmware's LE requirement */ temp[0x2C+i] = abts->abts_resp_payload[i+3]; temp[0x2C+i+1] = abts->abts_resp_payload[i+2]; temp[0x2C+i+2] = abts->abts_resp_payload[i+1]; temp[0x2C+i+3] = abts->abts_resp_payload[i]; } rex1 = QMEM_RD32(qlt, &temp[0x10]); rex2 = QMEM_RD32(qlt, &temp[0x3C]); EL(qlt, "req = NULL, %xh %xh %p %xh %xh\n", cmd->cmd_oxid, cmd->cmd_rportid, cmd, rex1, rex2); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } bcopy(qcmd->buf, req, IOCB_SIZE); lportid = QMEM_RD32(qlt, req+0x14) & 0xFFFFFF; fctl = QMEM_RD32(qlt, req+0x1C); fctl = ((fctl ^ BIT_23) & ~BIT_22) | (BIT_19 | BIT_16); req[0] = 0x55; req[1] = 1; req[2] = (uint8_t)terminate; QMEM_WR32(qlt, (&req[4]), cmd->cmd_handle); if (cmd->cmd_rp) QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp->rp_handle); else QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp_handle); if (terminate) { QMEM_WR16(qlt, (&req[0xC]), 1); } QMEM_WR32(qlt, req+0x14, cmd->cmd_rportid); req[0x17] = abts->abts_resp_rctl; QMEM_WR32(qlt, req+0x18, lportid); QMEM_WR32(qlt, req+0x1C, fctl); req[0x23]++; for (i = 0; i < 12; i += 4) { /* Take care of firmware's LE requirement */ req[0x2C+i] = abts->abts_resp_payload[i+3]; req[0x2C+i+1] = abts->abts_resp_payload[i+2]; req[0x2C+i+2] = abts->abts_resp_payload[i+1]; req[0x2C+i+3] = abts->abts_resp_payload[i]; } rex1 = QMEM_RD32(qlt, &req[0x10]); rex2 = QMEM_RD32(qlt, &req[0x3C]); EL(qlt, "%xh %xh %d %p %xh %xh\n", QMEM_RD16(qlt, req+0x26), QMEM_RD16(qlt, req+0x24), terminate, cmd, rex1, rex2); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } static void qlt_handle_inot(qlt_state_t *qlt, uint8_t *inot) { int i; uint32_t d; caddr_t req; uint16_t qi; uint8_t *entry = inot; qi = 0; /* Just put it on the request queue */ mutex_enter(&qlt->mq_req[qi].mq_lock); req = qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { mutex_exit(&qlt->mq_req[qi].mq_lock); stmf_trace(qlt->qlt_port_alias, "qlt_handle_inot: can't get a ReqQ entry"); EL(qlt, "req = NULL\n"); return; } for (i = 0; i < 16; i++) { d = QMEM_RD32(qlt, inot); inot += 4; QMEM_WR32(qlt, req, d); req += 4; } req -= 64; req[0] = 0x0e; QMEM_WR32(qlt, entry+0x3c, 0xdeadbeef); EL(qlt, "Issue inot ack\n"); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); } static uint16_t qlt_get_queue_id(qlt_state_t *qlt, int id) { uint16_t qid; if ((!qlt->qlt_mq_enabled) || (qlt->qlt_queue_cnt == 1)) { return (0); } mutex_enter(&qlt->qlock); if ((id == 0) && (qlt->last_qi == 0)) { qlt->last_qi++; } qid = qlt->last_qi; qlt->last_qi++; if (qlt->last_qi >= qlt->qlt_queue_cnt) { qlt->last_qi -= qlt->qlt_queue_cnt; } mutex_exit(&qlt->qlock); return (qid); } static fct_status_t qlt_verify_atio_entry(qlt_state_t *qlt, uint8_t *atio) { uint32_t sig; int i; char info[160]; sig = QMEM_RD32(qlt, atio+0x3c); for (i = 0; ((sig == 0xdeadbeef) && (i < qlt_reprocess_attempt_cnt)); i++) { (void) ddi_dma_sync( qlt->queue_mem_dma_handle, ATIO_QUEUE_OFFSET + (qlt->atio_ndx_to_fw << 6), IOCB_SIZE, DDI_DMA_SYNC_FORCPU); qlt->qlt_atio_reproc_cnt++; drv_usecwait(qlt_reprocess_delay); sig = QMEM_RD32(qlt, atio+0x3c); } if (i) { if (i >= qlt_reprocess_attempt_cnt) { EL(qlt, "atio entry reprocess failed, %x\n", qlt->qlt_atio_reproc_cnt); cmn_err(CE_WARN, "qlt%d: atio entry reprocess" " failed %x\n", qlt->instance, qlt->qlt_atio_reproc_cnt); (void) snprintf(info, 160, "qlt_handle_ctio_completion: atio entry reprocess" " failed, %x rsp-%p", qlt->qlt_atio_reproc_cnt, (void *)atio); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return (QLT_FAILURE); } else { EL(qlt, "atio entry reprocess succeeded, %x %x\n", i, qlt->qlt_atio_reproc_cnt); } } return (QLT_SUCCESS); } uint8_t qlt_task_flags[] = { 1, 3, 2, 1, 4, 0, 1, 1 }; static void qlt_handle_atio(qlt_state_t *qlt, uint8_t *atio) { fct_cmd_t *cmd; scsi_task_t *task; qlt_cmd_t *qcmd; uint32_t rportid, fw_xchg_addr; uint8_t *p, *q, *req, tm; uint16_t cdb_size, flags, oxid; char info[160]; uint16_t qi; if (qlt_verify_atio_entry(qlt, atio) != QLT_SUCCESS) return; /* * If either bidirection xfer is requested of there is extended * CDB, atio[0x20 + 11] will be greater than or equal to 3. */ cdb_size = 16; if (atio[0x20 + 11] >= 3) { uint8_t b = atio[0x20 + 11]; uint16_t b1; if ((b & 3) == 3) { EL(qlt, "bidirectional I/O not supported\n"); cmn_err(CE_WARN, "qlt(%d) CMD with bidirectional I/O " "received, dropping the cmd as bidirectional " " transfers are not yet supported", qlt->instance); /* XXX abort the I/O */ return; } cdb_size = (uint16_t)(cdb_size + (b & 0xfc)); /* * Verify that we have enough entries. Without additional CDB * Everything will fit nicely within the same 64 bytes. So the * additional cdb size is essentially the # of additional bytes * we need. */ b1 = (uint16_t)b; if (((((b1 & 0xfc) + 63) >> 6) + 1) > ((uint16_t)atio[1])) { EL(qlt, "extended cdb received\n"); cmn_err(CE_WARN, "qlt(%d): cmd received with extended " " cdb (cdb size = %d bytes), however the firmware " " did not DMAed the entire FCP_CMD IU, entry count " " is %d while it should be %d", qlt->instance, cdb_size, atio[1], ((((b1 & 0xfc) + 63) >> 6) + 1)); /* XXX abort the I/O */ return; } } rportid = (((uint32_t)atio[8 + 5]) << 16) | (((uint32_t)atio[8 + 6]) << 8) | atio[8+7]; fw_xchg_addr = QMEM_RD32(qlt, atio+4); oxid = (uint16_t)((((uint16_t)atio[8 + 16]) << 8) | atio[8+17]); if (fw_xchg_addr == 0xFFFFFFFF) { EL(qlt, "fw_xchg_addr==0xFFFFFFFF\n"); cmd = NULL; } else { cmd = fct_scsi_task_alloc(qlt->qlt_port, FCT_HANDLE_NONE, rportid, atio+0x20, cdb_size, STMF_TASK_EXT_NONE); if (cmd == NULL) { EL(qlt, "fct_scsi_task_alloc cmd==NULL\n"); } } if (cmd == NULL) { qi = 0; /* just use request queue 0 */ EL(qlt, "fct_scsi_task_alloc cmd==NULL\n"); /* Abort this IO */ flags = (uint16_t)(BIT_14 | ((atio[3] & 0xF0) << 5)); mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { mutex_exit(&qlt->mq_req[0].mq_lock); (void) snprintf(info, 160, "qlt_handle_atio: qlt-%p, can't " "allocate space for scsi_task", (void *)qlt); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = 0x1; QMEM_WR32(qlt, req+4, 0); QMEM_WR16(qlt, req+8, fct_get_rp_handle(qlt->qlt_port, rportid)); QMEM_WR16(qlt, req+10, 60); QMEM_WR32(qlt, req+0x10, rportid); QMEM_WR32(qlt, req+0x14, fw_xchg_addr); QMEM_WR16(qlt, req+0x1A, flags); QMEM_WR16(qlt, req+0x20, oxid); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return; } if (cmd == NULL) { uint32_t res; uint16_t scsi_status = 0; uint16_t rphdl = 0; qi = 0; /* always use request queue 0 */ rphdl = fct_get_rp_handle(qlt->qlt_port, rportid); if ((rphdl != 0xFFFF) && (rphdl >= qlt->qlt_port->port_max_logins)) { rphdl = 0xFFFF; } mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "cannot get reqq\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); (void) snprintf(info, 160, "qlt_handle_atio: qlt-%p, can't " "allocate space for termi-excg", (void *)qlt); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } if (rphdl != 0xFFFF) { /* Driver send scsi qfull status now */ flags = (uint16_t)(BIT_15 | ((uint16_t)(atio[0x3] & 0xF0) << 5)); /* always use SCSI status mode 1 */ flags = (uint16_t)(flags | BIT_6); scsi_status |= (uint16_t)(0x28); /* Build SCSI Status Mode 1, FCP_RSP IU 24-48 byte */ bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = 0x1; /* allocate a special IOCB handle? or donot care */ QMEM_WR32(qlt, req+4, 0); QMEM_WR16(qlt, req+8, rphdl); QMEM_WR16(qlt, req+10, 60); QMEM_WR32(qlt, req+0x10, rportid); QMEM_WR32(qlt, req+0x14, fw_xchg_addr); /* sense_length set to 0 */ QMEM_WR16(qlt, req+0x18, 0); QMEM_WR16(qlt, req+0x1A, flags); /* Residual transfer length */ res = QMEM_RD32(qlt, atio+0x3C); BIG_ENDIAN_32(&res); if (res != 0) { scsi_status |= FCP_RESID_UNDER; } QMEM_WR32_REQ(qlt, qi, req + 0x1C, res); QMEM_WR16(qlt, req+0x20, oxid); QMEM_WR16_REQ(qlt, qi, req + 0x22, scsi_status); EL(qlt, "Send qfull (%Xh) (%Xh)(%Xh)(%Xh) from port " "(%Xh:%Xh)\n", scsi_status, fw_xchg_addr, flags, oxid, rportid, rphdl); } else { /* Terminate exchange because no remote port context */ flags = (uint16_t)(BIT_14 | ((atio[3] & 0xF0) << 5)); bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = 0x1; QMEM_WR32(qlt, req+4, 0); QMEM_WR16(qlt, req+8, rphdl); QMEM_WR16(qlt, req+10, 60); QMEM_WR32(qlt, req+0x10, rportid); QMEM_WR32(qlt, req+0x14, fw_xchg_addr); QMEM_WR16(qlt, req+0x1A, flags); QMEM_WR16(qlt, req+0x20, oxid); EL(qlt, "Termi excg (%Xh)(%Xh)(%Xh) from port (%Xh)\n", fw_xchg_addr, flags, oxid, rportid); EL(qlt, "Termi rp_handle (%Xh)\n", rphdl); } qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return; } qi = qlt_get_queue_id(qlt, 0); task = (scsi_task_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qcmd->fw_xchg_addr = fw_xchg_addr; qcmd->param.atio_byte3 = atio[3]; qcmd->qid = qi; cmd->cmd_oxid = oxid; cmd->cmd_rxid = (uint16_t)((((uint16_t)atio[8 + 18]) << 8) | atio[8+19]); cmd->cmd_rportid = rportid; cmd->cmd_lportid = (((uint32_t)atio[8 + 1]) << 16) | (((uint32_t)atio[8 + 2]) << 8) | atio[8 + 3]; cmd->cmd_rp_handle = FCT_HANDLE_NONE; /* Dont do a 64 byte read as this is IOMMU */ q = atio+0x28; /* XXX Handle fcp_cntl */ task->task_cmd_seq_no = (uint32_t)(*q++); task->task_csn_size = 8; task->task_flags = qlt_task_flags[(*q++) & 7]; tm = *q++; if (tm) { if (tm & BIT_1) task->task_mgmt_function = TM_ABORT_TASK_SET; else if (tm & BIT_2) task->task_mgmt_function = TM_CLEAR_TASK_SET; else if (tm & BIT_4) task->task_mgmt_function = TM_LUN_RESET; else if (tm & BIT_5) task->task_mgmt_function = TM_TARGET_COLD_RESET; else if (tm & BIT_6) task->task_mgmt_function = TM_CLEAR_ACA; else task->task_mgmt_function = TM_ABORT_TASK; } task->task_max_nbufs = STMF_BUFS_MAX; task->task_csn_size = 8; task->task_flags = (uint8_t)(task->task_flags | (((*q++) & 3) << 5)); p = task->task_cdb; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; *p++ = *q++; if (cdb_size > 16) { uint16_t xtra = (uint16_t)(cdb_size - 16); uint16_t i; uint8_t cb[4]; while (xtra) { *p++ = *q++; xtra--; if (q == ((uint8_t *)qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET + (ATIO_QUEUE_ENTRIES * 64))) { q = (uint8_t *)qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET; } } for (i = 0; i < 4; i++) { cb[i] = *q++; if (q == ((uint8_t *)qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET + (ATIO_QUEUE_ENTRIES * 64))) { q = (uint8_t *)qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET; } } task->task_expected_xfer_length = (((uint32_t)cb[0]) << 24) | (((uint32_t)cb[1]) << 16) | (((uint32_t)cb[2]) << 8) | cb[3]; } else { task->task_expected_xfer_length = (((uint32_t)q[0]) << 24) | (((uint32_t)q[1]) << 16) | (((uint32_t)q[2]) << 8) | q[3]; } QMEM_WR32(qlt, atio+0x3c, 0xdeadbeef); fct_post_rcvd_cmd(cmd, 0); } static void qlt_handle_dereg_completion(qlt_state_t *qlt, uint8_t *rsp) { uint16_t status; uint32_t portid; uint32_t subcode1, subcode2; status = QMEM_RD16(qlt, rsp+8); portid = QMEM_RD32(qlt, rsp+0x10) & 0xffffff; subcode1 = QMEM_RD32(qlt, rsp+0x14); subcode2 = QMEM_RD32(qlt, rsp+0x18); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_enter(&qlt->mq_req[0].mq_lock); } else { mutex_enter(&qlt->preq_lock); } if (portid != qlt->rp_id_in_dereg) { int instance = ddi_get_instance(qlt->dip); EL(qlt, "implicit logout reveived portid = %xh\n", portid); cmn_err(CE_WARN, "qlt(%d): implicit logout completion for 0x%x" " received when driver wasn't waiting for it", instance, portid); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_exit(&qlt->mq_req[0].mq_lock); } else { mutex_exit(&qlt->preq_lock); } return; } if (status != 0) { EL(qlt, "implicit logout completed for %xh with status %xh, " "subcode1 %xh subcode2 %xh\n", portid, status, subcode1, subcode2); if (status == 0x31 && subcode1 == 0x0a) { qlt->rp_dereg_status = FCT_SUCCESS; } else { EL(qlt, "implicit logout portid=%xh, status=%xh, " "subcode1=%xh, subcode2=%xh\n", portid, status, subcode1, subcode2); qlt->rp_dereg_status = QLT_FIRMWARE_ERROR(status, subcode1, subcode2); } } else { qlt->rp_dereg_status = FCT_SUCCESS; } cv_signal(&qlt->rp_dereg_cv); if ((qlt->qlt_83xx_chip) || (qlt->qlt_27xx_chip)) { mutex_exit(&qlt->mq_req[0].mq_lock); } else { mutex_exit(&qlt->preq_lock); } } /* * Note that when an ELS is aborted, the regular or aborted completion * (if any) gets posted before the abort IOCB comes back on response queue. */ static void qlt_handle_unsol_els_completion(qlt_state_t *qlt, uint8_t *rsp) { char info[160]; fct_cmd_t *cmd; qlt_cmd_t *qcmd; uint32_t hndl; uint32_t subcode1, subcode2; uint16_t status; uint8_t elsop; hndl = QMEM_RD32(qlt, rsp+4); status = QMEM_RD16(qlt, rsp+8); subcode1 = QMEM_RD32(qlt, rsp+0x24); subcode2 = QMEM_RD32(qlt, rsp+0x28); elsop = rsp[0x16]; if (!CMD_HANDLE_VALID(hndl)) { EL(qlt, "handle = %xh\n", hndl); /* * This cannot happen for unsol els completion. This can * only happen when abort for an unsol els completes. * This condition indicates a firmware bug. */ (void) snprintf(info, 160, "qlt_handle_unsol_els_completion: " "Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p", hndl, status, subcode1, subcode2, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } if (status == 5) { /* * When an unsolicited els is aborted, the abort is done * by a ELSPT iocb with abort control. This is the aborted IOCB * and not the abortee. We will do the cleanup when the * IOCB which caused the abort, returns. */ EL(qlt, "status = %xh\n", status); stmf_trace(0, "--UNSOL ELS returned with status 5 --"); return; } cmd = fct_handle_to_cmd(qlt->qlt_port, hndl); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", hndl); /* * Now why would this happen ??? */ (void) snprintf(info, 160, "qlt_handle_unsol_els_completion: can not " "get cmd, hndl-%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS); qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; if (qcmd->flags & QLT_CMD_ABORTING) { /* * This is the same case as "if (status == 5)" above. The * only difference is that in this case the firmware actually * finished sending the response. So the abort attempt will * come back with status ?. We will handle it there. */ stmf_trace(0, "--UNSOL ELS finished while we are trying to " "abort it"); return; } if (qcmd->dbuf != NULL) { qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; } if (status == 0) { fct_send_response_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE); if ((elsop == ELS_OP_LOGO) && (qlt->cur_topology == PORT_TOPOLOGY_PT_TO_PT)) { EL(qlt, "reset link since this is LOGO and N2N\n"); (void) snprintf(info, 80, "qlt_handle_unsol_els_completion: qlt-%p, " "trigger RFLAG_RESET to recover", (void *)qlt); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); } } else { EL(qlt, "status (0xh) sucode1=%xh subconde2=%xh\n", status, subcode1, subcode2); fct_send_response_done(cmd, QLT_FIRMWARE_ERROR(status, subcode1, subcode2), 0); } } static void qlt_handle_unsol_els_abort_completion(qlt_state_t *qlt, uint8_t *rsp) { char info[160]; fct_cmd_t *cmd; qlt_cmd_t *qcmd; uint32_t hndl; uint32_t subcode1, subcode2; uint16_t status; hndl = QMEM_RD32(qlt, rsp+4); status = QMEM_RD16(qlt, rsp+8); subcode1 = QMEM_RD32(qlt, rsp+0x24); subcode2 = QMEM_RD32(qlt, rsp+0x28); if (!CMD_HANDLE_VALID(hndl)) { EL(qlt, "handle = %xh\n", hndl); ASSERT(hndl == 0); /* * Someone has requested to abort it, but no one is waiting for * this completion. */ if ((status != 0) && (status != 8)) { EL(qlt, "status = %xh\n", status); /* * There could be exchange resource leakage, so * throw HBA fatal error event now */ (void) snprintf(info, 160, "qlt_handle_unsol_els_abort_completion: " "Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p", hndl, status, subcode1, subcode2, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } return; } cmd = fct_handle_to_cmd(qlt->qlt_port, hndl); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", hndl); /* * Why would this happen ?? */ (void) snprintf(info, 160, "qlt_handle_unsol_els_abort_completion: can not get " "cmd, hndl-%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } ASSERT(cmd->cmd_type == FCT_CMD_RCVD_ELS); qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; ASSERT(qcmd->flags & QLT_CMD_ABORTING); if (qcmd->dbuf != NULL) { qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; } if (status == 0) { fct_cmd_fca_aborted(cmd, FCT_ABORT_SUCCESS, FCT_IOF_FCA_DONE); } else if (status == 8) { fct_cmd_fca_aborted(cmd, FCT_NOT_FOUND, FCT_IOF_FCA_DONE); } else { fct_cmd_fca_aborted(cmd, QLT_FIRMWARE_ERROR(status, subcode1, subcode2), 0); } } static void qlt_handle_sol_els_completion(qlt_state_t *qlt, uint8_t *rsp) { char info[160]; fct_cmd_t *cmd; fct_els_t *els; qlt_cmd_t *qcmd; uint32_t hndl; uint32_t subcode1, subcode2; uint16_t status; hndl = QMEM_RD32(qlt, rsp+4); status = QMEM_RD16(qlt, rsp+8); subcode1 = QMEM_RD32(qlt, rsp+0x24); subcode2 = QMEM_RD32(qlt, rsp+0x28); if (!CMD_HANDLE_VALID(hndl)) { EL(qlt, "handle = %xh\n", hndl); /* * This cannot happen for sol els completion. */ (void) snprintf(info, 160, "qlt_handle_sol_els_completion: " "Invalid handle: hndl-%x, status-%x/%x/%x, rsp-%p", hndl, status, subcode1, subcode2, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } cmd = fct_handle_to_cmd(qlt->qlt_port, hndl); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", hndl); (void) snprintf(info, 160, "qlt_handle_sol_els_completion: can not " "get cmd, hndl-%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } ASSERT(cmd->cmd_type == FCT_CMD_SOL_ELS); els = (fct_els_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qcmd->fw_xchg_addr = QMEM_RD32(qlt, (&rsp[0x10])); if (qcmd->flags & QLT_CMD_ABORTING) { /* * We will handle it when the ABORT IO IOCB returns. */ return; } if (qcmd->dbuf != NULL) { if (status == 0) { qlt_dmem_dma_sync(qcmd->dbuf, DDI_DMA_SYNC_FORKERNEL); bcopy(qcmd->dbuf->db_sglist[0].seg_addr + qcmd->param.resp_offset, els->els_resp_payload, els->els_resp_size); } qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; } if (status == 0) { fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE); } else { fct_send_cmd_done(cmd, QLT_FIRMWARE_ERROR(status, subcode1, subcode2), 0); } } static void qlt_handle_ct_completion(qlt_state_t *qlt, uint8_t *rsp) { fct_cmd_t *cmd; fct_sol_ct_t *ct; qlt_cmd_t *qcmd; uint32_t hndl; uint16_t status; char info[160]; hndl = QMEM_RD32(qlt, rsp+4); status = QMEM_RD16(qlt, rsp+8); if (!CMD_HANDLE_VALID(hndl)) { EL(qlt, "handle = %xh\n", hndl); /* * Solicited commands will always have a valid handle. */ (void) snprintf(info, 160, "qlt_handle_ct_completion: hndl-" "%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } cmd = fct_handle_to_cmd(qlt->qlt_port, hndl); EL(qlt, "cmd=%ph hndl=%xh status=%xh\n", cmd, hndl, status); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", hndl); (void) snprintf(info, 160, "qlt_handle_ct_completion: cannot find " "cmd, hndl-%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } ct = (fct_sol_ct_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; ASSERT(cmd->cmd_type == FCT_CMD_SOL_CT); if (qcmd->flags & QLT_CMD_ABORTING) { /* * We will handle it when ABORT IO IOCB returns; */ return; } ASSERT(qcmd->dbuf); if ((status == 0) || (status == 0x15)) { qlt_dmem_dma_sync(qcmd->dbuf, DDI_DMA_SYNC_FORKERNEL); bcopy(qcmd->dbuf->db_sglist[0].seg_addr + qcmd->param.resp_offset, ct->ct_resp_payload, ct->ct_resp_size); } qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; if ((status == 0) || (status == 0x15)) { fct_send_cmd_done(cmd, FCT_SUCCESS, FCT_IOF_FCA_DONE); } else { fct_send_cmd_done(cmd, QLT_FIRMWARE_ERROR(status, 0, 0), 0); } } static fct_status_t qlt_verify_resp_entry(qlt_state_t *qlt, uint8_t *rsp, uint16_t qi) { uint32_t sig; int i; char info[160]; sig = QMEM_RD32_RSPQ(qlt, qi, rsp+0x3c); for (i = 0; ((sig == 0xdeadbeef) && (i < qlt_reprocess_attempt_cnt)); i++) { (void) ddi_dma_sync( qlt->mq_resp[qi].queue_mem_mq_dma_handle, (qlt->mq_resp[qi].mq_ndx_to_fw << 6), IOCB_SIZE, DDI_DMA_SYNC_FORCPU); qlt->qlt_resp_reproc_cnt++; drv_usecwait(qlt_reprocess_delay); sig = QMEM_RD32_RSPQ(qlt, qi, rsp+0x3c); } if (i) { if (i >= qlt_reprocess_attempt_cnt) { EL(qlt, "resp entry reprocess failed, %x\n", qlt->qlt_resp_reproc_cnt); cmn_err(CE_WARN, "qlt%d: resp entry reprocess" " failed %x\n", qlt->instance, qlt->qlt_resp_reproc_cnt); (void) snprintf(info, 160, "qlt_handle_ctio_completion: resp entry reprocess" " failed, %x rsp-%p", qlt->qlt_resp_reproc_cnt, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return (QLT_FAILURE); } else { EL(qlt, "resp entry reprocess succeeded, %x %x\n", i, qlt->qlt_resp_reproc_cnt); } } return (QLT_SUCCESS); } static void qlt_handle_ctio_completion(qlt_state_t *qlt, uint8_t *rsp, uint16_t qi) { fct_cmd_t *cmd; scsi_task_t *task; qlt_cmd_t *qcmd; stmf_data_buf_t *dbuf; fct_status_t fc_st; uint32_t iof = 0; uint32_t hndl; uint32_t rex1; uint16_t oxid; uint16_t status; uint16_t flags; uint8_t abort_req; uint8_t n; char info[160]; if (qlt_verify_resp_entry(qlt, rsp, qi) != QLT_SUCCESS) return; /* write a deadbeef in the last 4 bytes of the IOCB */ QMEM_WR32_RSPQ(qlt, qi, rsp+0x3c, 0xdeadbeef); /* XXX: Check validity of the IOCB by checking 4th byte. */ hndl = QMEM_RD32_RSPQ(qlt, qi, rsp+4); status = QMEM_RD16_RSPQ(qlt, qi, rsp+8); flags = QMEM_RD16_RSPQ(qlt, qi, rsp+0x1a); oxid = QMEM_RD16_RSPQ(qlt, qi, rsp+0x20); rex1 = QMEM_RD32_RSPQ(qlt, qi, rsp+0x14); n = rsp[2]; if (!CMD_HANDLE_VALID(hndl)) { EL(qlt, "handle = %xh\n", hndl); ASSERT(hndl == 0); /* * Someone has requested to abort it, but no one is waiting for * this completion. */ EL(qlt, "hndl-%xh, status-%xh, rsp-%p\n", hndl, status, (void *)rsp); if ((status != 1) && (status != 2)) { EL(qlt, "status = %xh\n", status); if (status == 0x29) { uint8_t *req; /* * The qlt port received an ATIO request from * remote port before it issued a plogi. * The qlt fw returned the CTIO completion * status 0x29 to inform driver to do cleanup * (terminate the IO exchange). The subsequent * ABTS from the initiator can be handled * cleanly. */ qi = 0; mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *) qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "No reqq entry available to " "termi exchg\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); (void) snprintf(info, 160, "qlt_handle_ctio_completion: no " "reqq entry available, status-%x," "rsp-%p", status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } flags &= 0x1E00; flags |= BIT_14; bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = 0x1; QMEM_WR32(qlt, req+4, 0); QMEM_WR16(qlt, req+8, 0xFFFF); QMEM_WR16(qlt, req+10, 60); QMEM_WR32(qlt, req+0x14, rex1); QMEM_WR16(qlt, req+0x1A, flags); QMEM_WR16(qlt, req+0x20, oxid); EL(qlt, "Termi exchg (%Xh)(%Xh)(%Xh) " "rphdl=0xFFFF\n", rex1, flags, oxid); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); } else { /* * There could be exchange resource leakage, * so throw HBA fatal error event now */ (void) snprintf(info, 160, "qlt_handle_ctio_completion: hndl-%x, " "status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); } } return; } if (flags & BIT_14) { abort_req = 1; EL(qlt, "abort: hndl-%x, status-%x, rsp-%p\n", hndl, status, (void *)rsp); } else { abort_req = 0; } cmd = fct_handle_to_cmd(qlt->qlt_port, hndl); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", hndl); (void) snprintf(info, 160, "qlt_handle_ctio_completion: cannot find " "cmd, hndl-%x, status-%x, rsp-%p", hndl, status, (void *)rsp); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, /* STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); */ STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } task = (scsi_task_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; if (qcmd->dbuf_rsp_iu) { ASSERT((flags & (BIT_6 | BIT_7)) == BIT_7); qlt_dmem_free(NULL, qcmd->dbuf_rsp_iu); qcmd->dbuf_rsp_iu = NULL; } if ((status == 1) || (status == 2)) { if (abort_req) { fc_st = FCT_ABORT_SUCCESS; iof = FCT_IOF_FCA_DONE; } else { fc_st = FCT_SUCCESS; if (flags & BIT_15) { iof = FCT_IOF_FCA_DONE; } } } else { EL(qlt, "status = %xh\n", status); if ((status == 8) && abort_req) { fc_st = FCT_NOT_FOUND; iof = FCT_IOF_FCA_DONE; } else { fc_st = QLT_FIRMWARE_ERROR(status, 0, 0); } } dbuf = NULL; if (((n & BIT_7) == 0) && (!abort_req)) { /* A completion of data xfer */ if (n == 0) { dbuf = qcmd->dbuf; } else { dbuf = stmf_handle_to_buf(task, n); } ASSERT(dbuf != NULL); if (dbuf->db_flags & DB_DIRECTION_FROM_RPORT) qlt_dmem_dma_sync(dbuf, DDI_DMA_SYNC_FORCPU); if (flags & BIT_15) { dbuf->db_flags = (uint16_t)(dbuf->db_flags | DB_STATUS_GOOD_SENT); } dbuf->db_xfer_status = fc_st; fct_scsi_data_xfer_done(cmd, dbuf, iof); return; } if (!abort_req) { /* * This was just a pure status xfer. */ fct_send_response_done(cmd, fc_st, iof); return; } fct_cmd_fca_aborted(cmd, fc_st, iof); EL(qlt, "(%d) (%p)(%xh,%xh),%x %x %x\n", qi, cmd, cmd->cmd_oxid, cmd->cmd_rxid, cmd->cmd_handle, qcmd->fw_xchg_addr, fc_st); } static void qlt_handle_sol_abort_completion(qlt_state_t *qlt, uint8_t *rsp) { char info[80]; fct_cmd_t *cmd; qlt_cmd_t *qcmd; uint32_t h; uint16_t status; h = QMEM_RD32(qlt, rsp+4); status = QMEM_RD16(qlt, rsp+8); if (!CMD_HANDLE_VALID(h)) { EL(qlt, "handle = %xh\n", h); /* * Solicited commands always have a valid handle. */ (void) snprintf(info, 80, "qlt_handle_sol_abort_completion: hndl-" "%x, status-%x, rsp-%p", h, status, (void *)rsp); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); return; } cmd = fct_handle_to_cmd(qlt->qlt_port, h); if (cmd == NULL) { EL(qlt, "fct_handle_to_cmd cmd==NULL, hndl=%xh\n", h); /* * What happened to the cmd ?? */ (void) snprintf(info, 80, "qlt_handle_sol_abort_completion: cannot " "find cmd, hndl-%x, status-%x, rsp-%p", h, status, (void *)rsp); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } ASSERT((cmd->cmd_type == FCT_CMD_SOL_ELS) || (cmd->cmd_type == FCT_CMD_SOL_CT)); qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; if (qcmd->dbuf != NULL) { qlt_dmem_free(NULL, qcmd->dbuf); qcmd->dbuf = NULL; } ASSERT(qcmd->flags & QLT_CMD_ABORTING); EL(qlt, "status=%xh\n", status); if (status == 0) { fct_cmd_fca_aborted(cmd, FCT_ABORT_SUCCESS, FCT_IOF_FCA_DONE); } else if (status == 0x31) { fct_cmd_fca_aborted(cmd, FCT_NOT_FOUND, FCT_IOF_FCA_DONE); } else { fct_cmd_fca_aborted(cmd, QLT_FIRMWARE_ERROR(status, 0, 0), 0); } } static void qlt_handle_rcvd_abts(qlt_state_t *qlt, uint8_t *resp, uint16_t qi) { qlt_abts_cmd_t *qcmd; fct_cmd_t *cmd; uint32_t remote_portid; uint32_t rex1; uint32_t rex2; char info[160]; remote_portid = ((uint32_t)(QMEM_RD16(qlt, (&resp[0x18])))) | ((uint32_t)(resp[0x1A])) << 16; cmd = (fct_cmd_t *)fct_alloc(FCT_STRUCT_CMD_RCVD_ABTS, sizeof (qlt_abts_cmd_t), 0); if (cmd == NULL) { EL(qlt, "fct_alloc cmd==NULL\n"); (void) snprintf(info, 160, "qlt_handle_rcvd_abts: qlt-%p, can't " "allocate space for fct_cmd", (void *)qlt); info[159] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET, info); return; } resp[0xC] = resp[0xD] = resp[0xE] = 0; qcmd = (qlt_abts_cmd_t *)cmd->cmd_fca_private; qcmd->qid = qi; bcopy(resp, qcmd->buf, IOCB_SIZE); cmd->cmd_port = qlt->qlt_port; cmd->cmd_rp_handle = QMEM_RD16(qlt, resp+0xA); if (cmd->cmd_rp_handle == 0xFFFF) cmd->cmd_rp_handle = FCT_HANDLE_NONE; cmd->cmd_rportid = remote_portid; cmd->cmd_lportid = ((uint32_t)(QMEM_RD16(qlt, (&resp[0x14])))) | ((uint32_t)(resp[0x16])) << 16; cmd->cmd_oxid = QMEM_RD16(qlt, (&resp[0x26])); cmd->cmd_rxid = QMEM_RD16(qlt, (&resp[0x24])); rex1 = QMEM_RD32(qlt, resp+0x10); rex2 = QMEM_RD32(qlt, resp+0x3C); EL(qlt, "(%d)(%xh %xh) (%xh)(%p) (%xh %xh) (%x)\n", qi, cmd->cmd_oxid, cmd->cmd_rxid, remote_portid, cmd, rex1, rex2, cmd->cmd_handle); fct_post_rcvd_cmd(cmd, 0); } static void qlt_handle_abts_completion(qlt_state_t *qlt, uint8_t *resp, uint16_t qi) { uint16_t status; char info[80]; status = QMEM_RD16(qlt, resp+8); if ((status == 0) || (status == 5)) { EL(qlt, "qi(%d) status =%xh,(%xh %xh)\n", qi, status, QMEM_RD16(qlt, resp+0x26), QMEM_RD16(qlt, resp+0x24)); return; } EL(qlt, "ABTS status=%x/%x/%x resp_off %x", status, QMEM_RD32(qlt, resp+0x34), QMEM_RD32(qlt, resp+0x38), ((uint32_t)(qlt->mq_resp[0].mq_ndx_to_fw)) << 6); (void) snprintf(info, 80, "ABTS completion failed %x/%x/%x resp_off %x", status, QMEM_RD32(qlt, resp+0x34), QMEM_RD32(qlt, resp+0x38), ((uint32_t)(qlt->mq_resp[0].mq_ndx_to_fw)) << 6); info[79] = 0; (void) fct_port_shutdown(qlt->qlt_port, STMF_RFLAG_FATAL_ERROR | STMF_RFLAG_RESET | STMF_RFLAG_COLLECT_DEBUG_DUMP, info); } #ifdef DEBUG uint32_t qlt_drop_abort_counter = 0; #endif fct_status_t qlt_abort_cmd(struct fct_local_port *port, fct_cmd_t *cmd, uint32_t flags) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; if ((qlt->qlt_state == FCT_STATE_OFFLINE) || (qlt->qlt_state == FCT_STATE_OFFLINING)) { return (FCT_NOT_FOUND); } #ifdef DEBUG if (qlt_drop_abort_counter > 0) { if (atomic_dec_32_nv(&qlt_drop_abort_counter) == 1) return (FCT_SUCCESS); } #endif EL(qlt, "cmd_type = %x\n", cmd->cmd_type); if (cmd->cmd_type == FCT_CMD_FCP_XCHG) { return (qlt_abort_unsol_scsi_cmd(qlt, cmd)); } if (flags & FCT_IOF_FORCE_FCA_DONE) { cmd->cmd_handle = 0; } if (cmd->cmd_type == FCT_CMD_RCVD_ABTS) { /* this is retried ABTS, terminate it now */ return (qlt_send_abts_response(qlt, cmd, 1)); } if (cmd->cmd_type == FCT_CMD_RCVD_ELS) { return (qlt_abort_purex(qlt, cmd)); } if ((cmd->cmd_type == FCT_CMD_SOL_ELS) || (cmd->cmd_type == FCT_CMD_SOL_CT)) { return (qlt_abort_sol_cmd(qlt, cmd)); } EL(qlt, "cmd->cmd_type = %x\n", cmd->cmd_type); ASSERT(0); return (FCT_FAILURE); } fct_status_t qlt_abort_sol_cmd(qlt_state_t *qlt, fct_cmd_t *cmd) { uint8_t *req; qlt_cmd_t *qcmd; uint16_t qi; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qcmd->flags = (uint16_t)(qcmd->flags | QLT_CMD_ABORTING); qi = qcmd->qid; EL(qlt, "fctcmd-%p, cmd_handle-%xh rportid=%xh\n", cmd, cmd->cmd_handle, cmd->cmd_rportid); mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req == NULL\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } bzero(req, IOCB_SIZE); req[0] = 0x33; req[1] = 1; QMEM_WR32(qlt, req+4, cmd->cmd_handle); if (cmd->cmd_rp) { QMEM_WR16(qlt, req+8, cmd->cmd_rp->rp_handle); } else { QMEM_WR16(qlt, req+8, 0xFFFF); } QMEM_WR32(qlt, req+0xc, cmd->cmd_handle); QMEM_WR32(qlt, req+0x30, cmd->cmd_rportid); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } fct_status_t qlt_abort_purex(qlt_state_t *qlt, fct_cmd_t *cmd) { uint8_t *req; qlt_cmd_t *qcmd; fct_els_t *els; uint8_t elsop, req1f; uint16_t qi; els = (fct_els_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qi = qcmd->qid; elsop = els->els_req_payload[0]; EL(qlt, "fctcmd-%p, cmd_handle-%xh, elsop-%xh\n", cmd, cmd->cmd_handle, elsop); req1f = 0x60; /* Terminate xchg */ if ((elsop == ELS_OP_PRLI) || (elsop == ELS_OP_PRLO) || (elsop == ELS_OP_TPRLO) || (elsop == ELS_OP_LOGO)) { req1f = (uint8_t)(req1f | BIT_4); } mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req == NULL\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } qcmd->flags = (uint16_t)(qcmd->flags | QLT_CMD_ABORTING); bzero(req, IOCB_SIZE); req[0] = 0x53; req[1] = 1; req[0xf] = 0x10; req[0x16] = elsop; req[0x1f] = req1f; QMEM_WR32(qlt, (&req[4]), cmd->cmd_handle); if (cmd->cmd_rp) { QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp->rp_handle); EL(qlt, "rp_handle-%x\n", cmd->cmd_rp->rp_handle); } else { QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp_handle); EL(qlt, "cmd_rp_handle-%x\n", cmd->cmd_rp_handle); } QMEM_WR32(qlt, (&req[0x10]), qcmd->fw_xchg_addr); QMEM_WR32(qlt, (&req[0x18]), cmd->cmd_rportid); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } fct_status_t qlt_abort_unsol_scsi_cmd(qlt_state_t *qlt, fct_cmd_t *cmd) { qlt_cmd_t *qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; uint8_t *req; uint16_t flags; uint16_t qi; qi = qcmd->qid; flags = (uint16_t)(BIT_14 | (((uint16_t)qcmd->param.atio_byte3 & 0xf0) << 5)); EL(qlt, "(%d) (%x) (%p) (%x)\n", qi, cmd->cmd_oxid, cmd, qcmd->fw_xchg_addr); mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req == NULL\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } qcmd->flags = (uint16_t)(qcmd->flags | QLT_CMD_ABORTING); bzero(req, IOCB_SIZE); req[0] = 0x12; req[1] = 0x1; QMEM_WR32_REQ(qlt, qi, req+4, cmd->cmd_handle); QMEM_WR16_REQ(qlt, qi, req+8, cmd->cmd_rp->rp_handle); QMEM_WR16_REQ(qlt, qi, req+10, 60); /* 60 seconds timeout */ QMEM_WR32_REQ(qlt, qi, req+0x10, cmd->cmd_rportid); QMEM_WR32_REQ(qlt, qi, req+0x14, qcmd->fw_xchg_addr); QMEM_WR16_REQ(qlt, qi, req+0x1A, flags); QMEM_WR16_REQ(qlt, qi, req+0x20, cmd->cmd_oxid); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } fct_status_t qlt_send_cmd(fct_cmd_t *cmd) { qlt_state_t *qlt; qlt = (qlt_state_t *)cmd->cmd_port->port_fca_private; EL(qlt, "cmd->cmd_type = %xh\n", cmd->cmd_type); if (cmd->cmd_type == FCT_CMD_SOL_ELS) { return (qlt_send_els(qlt, cmd)); } else if (cmd->cmd_type == FCT_CMD_SOL_CT) { return (qlt_send_ct(qlt, cmd)); } EL(qlt, "Unknown cmd->cmd_type = %xh\n", cmd->cmd_type); ASSERT(0); return (FCT_FAILURE); } fct_status_t qlt_send_els(qlt_state_t *qlt, fct_cmd_t *cmd) { uint8_t *req; fct_els_t *els; qlt_cmd_t *qcmd; stmf_data_buf_t *buf; qlt_dmem_bctl_t *bctl; uint32_t sz, minsz; uint16_t qi; qi = 0; els = (fct_els_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qcmd->flags = QLT_CMD_TYPE_SOLICITED; qcmd->param.resp_offset = (uint16_t)((els->els_req_size + 7) & ~7); sz = minsz = qcmd->param.resp_offset + els->els_resp_size; buf = qlt_i_dmem_alloc(qlt, sz, &minsz, 0); if (buf == NULL) { return (FCT_BUSY); } bctl = (qlt_dmem_bctl_t *)buf->db_port_private; qcmd->dbuf = buf; bcopy(els->els_req_payload, buf->db_sglist[0].seg_addr, els->els_req_size); qlt_dmem_dma_sync(buf, DDI_DMA_SYNC_FORDEV); mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req = NULL, %xh %xh %p %xh\n", cmd->cmd_oxid, cmd->cmd_rportid, cmd, qcmd->fw_xchg_addr); qlt_dmem_free(NULL, buf); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } bzero(req, IOCB_SIZE); req[0] = 0x53; req[1] = 1; QMEM_WR32(qlt, (&req[4]), cmd->cmd_handle); QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp->rp_handle); QMEM_WR16(qlt, (&req[0xC]), 1); QMEM_WR16(qlt, (&req[0xE]), 0x1000); QMEM_WR16(qlt, (&req[0x14]), 1); req[0x16] = els->els_req_payload[0]; if (qlt->cur_topology == PORT_TOPOLOGY_PT_TO_PT) { req[0x1b] = (uint8_t)((cmd->cmd_lportid >> 16) & 0xff); req[0x1c] = (uint8_t)(cmd->cmd_lportid & 0xff); req[0x1d] = (uint8_t)((cmd->cmd_lportid >> 8) & 0xff); } QMEM_WR32(qlt, (&req[0x18]), cmd->cmd_rp->rp_id); QMEM_WR32(qlt, (&req[0x20]), els->els_resp_size); QMEM_WR32(qlt, (&req[0x24]), els->els_req_size); QMEM_WR64(qlt, (&req[0x28]), bctl->bctl_dev_addr); QMEM_WR32(qlt, (&req[0x30]), els->els_req_size); QMEM_WR64(qlt, (&req[0x34]), (bctl->bctl_dev_addr + qcmd->param.resp_offset)); QMEM_WR32(qlt, (&req[0x3C]), els->els_resp_size); EL(qlt, "ELS opcode %xh to %xh\n", req[0x16], cmd->cmd_rp->rp_id); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } fct_status_t qlt_send_ct(qlt_state_t *qlt, fct_cmd_t *cmd) { uint8_t *req; fct_sol_ct_t *ct; qlt_cmd_t *qcmd; stmf_data_buf_t *buf; qlt_dmem_bctl_t *bctl; uint32_t sz, minsz; uint16_t qi; qi = 0; ct = (fct_sol_ct_t *)cmd->cmd_specific; qcmd = (qlt_cmd_t *)cmd->cmd_fca_private; qcmd->flags = QLT_CMD_TYPE_SOLICITED; qcmd->param.resp_offset = (uint16_t)((ct->ct_req_size + 7) & ~7); sz = minsz = qcmd->param.resp_offset + ct->ct_resp_size; buf = qlt_i_dmem_alloc(qlt, sz, &minsz, 0); if (buf == NULL) { return (FCT_BUSY); } bctl = (qlt_dmem_bctl_t *)buf->db_port_private; qcmd->dbuf = buf; bcopy(ct->ct_req_payload, buf->db_sglist[0].seg_addr, ct->ct_req_size); qlt_dmem_dma_sync(buf, DDI_DMA_SYNC_FORDEV); mutex_enter(&qlt->mq_req[qi].mq_lock); req = (uint8_t *)qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req = NULL, %xh %xh %p %xh\n", cmd->cmd_oxid, cmd->cmd_rportid, cmd, qcmd->fw_xchg_addr); qlt_dmem_free(NULL, buf); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_BUSY); } bzero(req, IOCB_SIZE); req[0] = 0x29; req[1] = 1; QMEM_WR32(qlt, (&req[4]), cmd->cmd_handle); QMEM_WR16(qlt, (&req[0xA]), cmd->cmd_rp->rp_handle); QMEM_WR16(qlt, (&req[0xC]), 1); QMEM_WR16(qlt, (&req[0x10]), 0x20); /* > (2 * RA_TOV) */ QMEM_WR16(qlt, (&req[0x14]), 1); QMEM_WR32(qlt, (&req[0x20]), ct->ct_resp_size); QMEM_WR32(qlt, (&req[0x24]), ct->ct_req_size); QMEM_WR64(qlt, (&req[0x28]), bctl->bctl_dev_addr); /* COMMAND DSD */ QMEM_WR32(qlt, (&req[0x30]), ct->ct_req_size); QMEM_WR64(qlt, (&req[0x34]), (bctl->bctl_dev_addr + qcmd->param.resp_offset)); /* RESPONSE DSD */ QMEM_WR32(qlt, (&req[0x3C]), ct->ct_resp_size); EL(qlt, "%p cmd_hdl=%xh %xh %xh\n", cmd, cmd->cmd_handle, ct->ct_req_size, ct->ct_resp_size); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); return (FCT_SUCCESS); } /*ARGSUSED*/ caddr_t qlt_str_ptr(qlt_state_t *qlt, caddr_t bp, uint32_t *len) { caddr_t sp; uint32_t i = 0; sp = bp; while (*sp++ != 0) i++; if (i > *len || !(*len -= i)) { EL(qlt, "full buffer\n"); return (NULL); } return (bp += i); } static fct_status_t qlt_27xx_fw_dump(fct_local_port_t *port, stmf_state_change_info_t *ssci) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; qlt_dmp_template_t *template_buff; uint32_t tsize, dsize, len; uint32_t cnt, *dp, *bp; uint8_t *fw; caddr_t sp; EL(qlt, "enter...\n"); mutex_enter(&qlt->qlt_ioctl_lock); /* * To make sure that there's no outstanding dumping task */ if (qlt->qlt_ioctl_flags & QLT_FWDUMP_INPROGRESS) { mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "qlt_ioctl_flags=%xh, inprogress\n", qlt->qlt_ioctl_flags); return (FCT_FAILURE); } /* * To make sure not to overwrite existing dump */ if ((qlt->qlt_ioctl_flags & QLT_FWDUMP_ISVALID) && !(qlt->qlt_ioctl_flags & QLT_FWDUMP_TRIGGERED_BY_USER) && !(qlt->qlt_ioctl_flags & QLT_FWDUMP_FETCHED_BY_USER)) { /* * If we have already one dump, but it's not triggered by user * and the user hasn't fetched it, we shouldn't dump again. * But if qlt force a fw dump, then we need to overwrite the * previous one anyway. */ mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "qlt_ioctl_flags=%xh, already done\n", qlt->qlt_ioctl_flags); cmn_err(CE_NOTE, "qlt(%d): Skipping firmware dump as there " "is one already outstanding.", qlt->instance); return (FCT_FAILURE); } if (qlt->dmp_template_addr == NULL) { mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "dmp_template_addr is NULL, can't " "perform firmware dump\n"); cmn_err(CE_WARN, "!qlt(%d) dmp_template_addr is NULL, can't " "perform firmware dump", qlt->instance); return (FCT_FAILURE); } qlt->qlt_ioctl_flags |= QLT_FWDUMP_INPROGRESS; if (ssci != NULL && (ssci->st_rflags & STMF_RFLAG_USER_REQUEST)) { qlt->qlt_ioctl_flags |= QLT_FWDUMP_TRIGGERED_BY_USER; } else { qlt->qlt_ioctl_flags &= ~QLT_FWDUMP_TRIGGERED_BY_USER; } mutex_exit(&qlt->qlt_ioctl_lock); template_buff = (qlt_dmp_template_t *)qlt->dmp_template_addr; tsize = template_buff->hdr.size_of_template; if (qlt->fw_bin_dump_size == 0) { qlt->fw_bin_dump_buf = kmem_zalloc(tsize, KM_NOSLEEP); if (qlt->fw_bin_dump_buf == NULL) { cmn_err(CE_WARN, "!qlt(%d) cannot alloc bin dump buf", qlt->instance); return (FCT_FAILURE); } cnt = (uint32_t)(tsize / sizeof (uint32_t)); dp = (uint32_t *)qlt->fw_bin_dump_buf; bp = (uint32_t *)&template_buff->hdr; while (cnt--) { *dp++ = ddi_get32(qlt->dmp_template_acc_handle, bp++); } qlt->fw_bin_dump_size = qlt_27xx_dmp_parse_template(qlt, (qlt_dt_hdr_t *)qlt->fw_bin_dump_buf, NULL, 0); kmem_free(qlt->fw_bin_dump_buf, tsize); qlt->fw_bin_dump_buf = NULL; if (qlt->fw_bin_dump_size == 0) { return (FCT_FAILURE); } /* * Determine ascii dump file size * 2 ascii bytes per binary byte + a space and * a newline every 16 binary bytes */ qlt->fw_ascii_dump_size = qlt->fw_bin_dump_size << 1; qlt->fw_ascii_dump_size += qlt->fw_bin_dump_size; qlt->fw_ascii_dump_size += qlt->fw_bin_dump_size / 16 + 1; EL(qlt, "fw_bin_dump_size=%xh, " "fw_acsii_dump_size=%xh\n", qlt->fw_bin_dump_size, qlt->fw_ascii_dump_size); } if (qlt->fw_bin_dump_buf != NULL) { /* overwrite the previous fw dump by qlt forced fw dump */ bzero((void *) qlt->fw_bin_dump_buf, qlt->fw_bin_dump_size); } else { qlt->fw_bin_dump_buf = kmem_zalloc(qlt->fw_bin_dump_size, KM_NOSLEEP); if (qlt->fw_bin_dump_buf == NULL) { qlt->fw_bin_dump_size = 0; EL(qlt, "done, failed alloc bin dump buf\n"); return (FCT_FAILURE); } } if ((qlt->fw_dump_size != 0) && (qlt->fw_dump_size != qlt->fw_ascii_dump_size)) { if (qlt->qlt_fwdump_buf != NULL) { /* Release previously allocated buffer */ kmem_free(qlt->qlt_fwdump_buf, qlt->fw_dump_size); qlt->qlt_fwdump_buf = NULL; } } if (qlt->qlt_fwdump_buf == NULL) { qlt->qlt_fwdump_buf = kmem_zalloc(qlt->fw_ascii_dump_size, KM_NOSLEEP); if (qlt->qlt_fwdump_buf == NULL) { EL(qlt, "done, failed alloc ascii fw dump buf\n"); return (FCT_FAILURE); } qlt->fw_dump_size = qlt->fw_ascii_dump_size; } /* Disable ISP interrupts. */ REG_WR32(qlt, 0xc, 0); cnt = (uint32_t)(tsize / sizeof (uint32_t)); dp = (uint32_t *)qlt->fw_bin_dump_buf; bp = (uint32_t *)&template_buff->hdr; while (cnt--) { *dp++ = ddi_get32(qlt->dmp_template_acc_handle, bp++); } (void) qlt_27xx_dmp_parse_template(qlt, (qlt_dt_hdr_t *)qlt->fw_bin_dump_buf, (uint8_t *)dp, qlt->fw_bin_dump_size); #ifdef _BIG_ENDIAN cnt = (uint32_t)(tsize / sizeof (uint32_t)); dp = (uint32_t *)qlt->fw_bin_dump_buf; while (cnt--) { qlt_chg_endian((uint8_t *)dp, 4); dp++; } #endif /* * Build ascii dump */ len = qlt->fw_ascii_dump_size; dsize = qlt->fw_bin_dump_size; fw = (uint8_t *)qlt->fw_bin_dump_buf; sp = qlt->qlt_fwdump_buf; EL(qlt, "fw_dump_buffer=%ph, fw=%ph, fw_ascii_dump_size=%xh, " "dsize=%xh\n", (void *)qlt->qlt_fwdump_buf, (void *)fw, len, dsize); /* * 2 ascii bytes per binary byte + a space and * a newline every 16 binary bytes */ cnt = 0; while (cnt < dsize) { (void) snprintf(sp, len, "%02x ", *fw++); if ((sp = qlt_str_ptr(qlt, sp, &len)) == NULL) { break; } if (++cnt % 16 == 0) { (void) snprintf(sp, len, "\n"); if ((sp = qlt_str_ptr(qlt, sp, &len)) == NULL) { break; } } } if (cnt % 16 != 0) { (void) snprintf(sp, len, "\n"); sp = qlt_str_ptr(qlt, sp, &len); } mutex_enter(&qlt->qlt_ioctl_lock); qlt->qlt_ioctl_flags &= ~(QLT_FWDUMP_INPROGRESS | QLT_FWDUMP_FETCHED_BY_USER); qlt->qlt_ioctl_flags |= QLT_FWDUMP_ISVALID; mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "done...\n"); return (FCT_SUCCESS); } /* * All QLT_FIRMWARE_* will mainly be handled in this function * It can not be called in interrupt context * * FWDUMP's purpose is to serve ioctl, so we will use qlt_ioctl_flags * and qlt_ioctl_lock */ static fct_status_t qlt_firmware_dump(fct_local_port_t *port, stmf_state_change_info_t *ssci) { qlt_state_t *qlt = (qlt_state_t *)port->port_fca_private; int i; int retries, n; uint_t size_left; char c = ' '; uint32_t addr, endaddr, words_to_read; caddr_t buf; fct_status_t ret; if (qlt->qlt_27xx_chip) { return (qlt_27xx_fw_dump(port, ssci)); } mutex_enter(&qlt->qlt_ioctl_lock); /* * To make sure that there's no outstanding dumping task */ if (qlt->qlt_ioctl_flags & QLT_FWDUMP_INPROGRESS) { mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "qlt_ioctl_flags=%xh, inprogress\n", qlt->qlt_ioctl_flags); return (FCT_FAILURE); } /* * To make sure not to overwrite existing dump */ if ((qlt->qlt_ioctl_flags & QLT_FWDUMP_ISVALID) && !(qlt->qlt_ioctl_flags & QLT_FWDUMP_TRIGGERED_BY_USER) && !(qlt->qlt_ioctl_flags & QLT_FWDUMP_FETCHED_BY_USER)) { /* * If we have already one dump, but it's not triggered by user * and the user hasn't fetched it, we shouldn't dump again. */ mutex_exit(&qlt->qlt_ioctl_lock); EL(qlt, "qlt_ioctl_flags=%xh, already done\n", qlt->qlt_ioctl_flags); cmn_err(CE_NOTE, "qlt(%d): Skipping firmware dump as there " "is one already outstanding.", qlt->instance); return (FCT_FAILURE); } qlt->qlt_ioctl_flags |= QLT_FWDUMP_INPROGRESS; if ((ssci != NULL) && (ssci->st_rflags & STMF_RFLAG_USER_REQUEST)) { qlt->qlt_ioctl_flags |= QLT_FWDUMP_TRIGGERED_BY_USER; } else { qlt->qlt_ioctl_flags &= ~QLT_FWDUMP_TRIGGERED_BY_USER; } mutex_exit(&qlt->qlt_ioctl_lock); size_left = QLT_FWDUMP_BUFSIZE; if (qlt->qlt_mq_enabled && qlt->qlt_queue_cnt >= 8) { size_left += 512 * 1024; } qlt->fw_dump_size = size_left; if (!qlt->qlt_fwdump_buf) { ASSERT(!(qlt->qlt_ioctl_flags & QLT_FWDUMP_ISVALID)); /* * It's the only place that we allocate buf for dumping. After * it's allocated, we will use it until the port is detached. */ qlt->qlt_fwdump_buf = kmem_zalloc(size_left, KM_NOSLEEP); if (qlt->qlt_fwdump_buf == NULL) { EL(qlt, "cannot alloc fwdump buffer\n"); cmn_err(CE_WARN, "!qlt(%d): cannot alloc fwdump buf", qlt->instance); return (FCT_FAILURE); } } EL(qlt, "starting firmware dump...\n"); cmn_err(CE_WARN, "!qlt(%d) starting firmware dump...", qlt->instance); /* * Start to dump firmware */ buf = (caddr_t)qlt->qlt_fwdump_buf; /* * Print the ISP firmware revision number and attributes information * Read the RISC to Host Status register */ n = (int)snprintf(buf, size_left, "ISP FW Version %d.%02d.%02d " "Attributes %04x\n\nR2H Status register\n%08x", qlt->fw_major, qlt->fw_minor, qlt->fw_subminor, qlt->fw_attr, REG_RD32(qlt, REG_RISC_STATUS)); buf += n; size_left -= n; /* * Before pausing the RISC, make sure no mailbox can execute */ mutex_enter(&qlt->mbox_lock); if ((qlt->mbox_io_state != MBOX_STATE_UNKNOWN) && (qlt->qlt_intr_enabled)) { /* * Wait to grab the mailboxes */ for (retries = 0; (qlt->mbox_io_state != MBOX_STATE_READY) && (qlt->mbox_io_state != MBOX_STATE_UNKNOWN); retries++) { (void) cv_timedwait(&qlt->mbox_cv, &qlt->mbox_lock, ddi_get_lbolt() + drv_usectohz(1000000)); if (retries > 5) { mutex_exit(&qlt->mbox_lock); EL(qlt, "can't drain out mailbox commands\n"); goto dump_fail; } } qlt->mbox_io_state = MBOX_STATE_UNKNOWN; cv_broadcast(&qlt->mbox_cv); } mutex_exit(&qlt->mbox_lock); /* * Pause the RISC processor */ REG_WR32(qlt, REG_HCCR, HCCR_CMD(SET_RISC_PAUSE)); /* * Wait for the RISC processor to pause */ for (i = 0; i < 200; i++) { if (REG_RD32(qlt, REG_RISC_STATUS) & 0x100) { break; } drv_usecwait(1000); } if (i == 200) { EL(qlt, "can't pause\n"); return (FCT_FAILURE); } if (qlt->qlt_83xx_chip) { /* Disable ECC checks in FB registers */ REG_WR32(qlt, 0x54, 0x6000); REG_WR32(qlt, 0xC0, 0); /* 6000h */ REG_WR32(qlt, 0xCC, 0); /* 6003h */ REG_WR32(qlt, 0x54, 0x6010); REG_WR32(qlt, 0xD4, 0); /* 6015h */ /* disable ECC detection in PCR whilst dumping */ REG_WR32(qlt, 0x54, 0xF70); REG_WR32(qlt, 0xF0, 0x60000000); } if ((!qlt->qlt_25xx_chip) && (!qlt->qlt_81xx_chip) && (!qlt->qlt_83xx_chip) && (!qlt->qlt_27xx_chip)) { goto over_25xx_specific_dump; } n = (int)snprintf(buf, size_left, "\n\nHostRisc registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7000); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7010); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0x7040); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0x7C00); n = (int)snprintf(buf, size_left, "\nPCIe registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0xC0, 0x1); n = qlt_fwdump_dump_regs(qlt, buf, 0xc4, 3, size_left); buf += n; size_left -= n; n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 1, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0xC0, 0x0); /* don't need to do this for 83xx */ if ((!qlt->qlt_83xx_chip) && (qlt->qlt_mq_enabled)) { uint16_t qi; for (qi = 0; qi < qlt->qlt_queue_cnt; qi++) { n = (int)snprintf(buf, size_left, "\n\nQueue Pointers #%x\n", qi); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "%08x ", MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_IN)); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "%08x ", MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_REQ_OUT)); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "%08x ", MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_IN)); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "%08x", MQBAR_RD32(qlt, (qi * MQBAR_REG_OFFSET) + MQBAR_RESP_OUT)); buf += n; size_left -= n; } } over_25xx_specific_dump:; n = (int)snprintf(buf, size_left, "\n\nHost Interface registers\n"); buf += n; size_left -= n; /* * Capture data from 32 registers */ n = qlt_fwdump_dump_regs(qlt, buf, 0, 32, size_left); buf += n; size_left -= n; /* * Disable interrupts */ REG_WR32(qlt, 0xc, 0); EL(qlt, "Disable interrupt\n"); /* * Shadow registers */ n = (int)snprintf(buf, size_left, "\nShadow registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xF70); addr = 0xb0000000; for (i = 0; i < 0xb; i++) { if ((!qlt->qlt_25xx_chip) && (!qlt->qlt_81xx_chip) && (!qlt->qlt_83xx_chip) && (i >= 7)) { break; } if (i && ((i & 7) == 0)) { n = (int)snprintf(buf, size_left, "\n"); buf += n; size_left -= n; } REG_WR32(qlt, 0xF0, addr); n = (int)snprintf(buf, size_left, "%08x ", REG_RD32(qlt, 0xFC)); buf += n; size_left -= n; addr += 0x100000; } if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip)) { REG_WR32(qlt, 0x54, 0x10); n = (int)snprintf(buf, size_left, "\n\nRISC IO register\n%08x", REG_RD32(qlt, 0xC0)); buf += n; size_left -= n; } /* * Mailbox registers */ n = (int)snprintf(buf, size_left, "\n\nMailbox registers\n"); buf += n; size_left -= n; for (i = 0; i < 32; i += 2) { if ((i + 2) & 15) { c = ' '; } else { c = '\n'; } n = (int)snprintf(buf, size_left, "%04x %04x%c", REG_RD16(qlt, 0x80 + (i << 1)), REG_RD16(qlt, 0x80 + ((i+1) << 1)), c); buf += n; size_left -= n; } /* * Transfer sequence registers */ n = (int)snprintf(buf, size_left, "\nXSEQ GP registers\n"); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0xBE00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBE70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0xBF00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBF70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXSEQ-0 registers\n"); buf += n; size_left -= n; if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip)) { REG_WR32(qlt, 0x54, 0xBFC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBFD0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0xBFE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXSEQ-1 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBFF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { n = (int)snprintf(buf, size_left, "\nXSEQ-2 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xBEF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } /* * Receive sequence registers */ n = (int)snprintf(buf, size_left, "\nRSEQ GP registers\n"); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0xFE00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFE70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0xFF00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFF70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRSEQ-0 registers\n"); buf += n; size_left -= n; if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip)) { REG_WR32(qlt, 0x54, 0xFFC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0xFFD0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRSEQ-1 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFFE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRSEQ-2 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFFF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { n = (int)snprintf(buf, size_left, "\nRSEQ-3 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xFEF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } if ((!qlt->qlt_25xx_chip) && (!qlt->qlt_81xx_chip) && (!qlt->qlt_83xx_chip)) goto over_aseq_regs; /* * Auxiliary sequencer registers */ n = (int)snprintf(buf, size_left, "\nASEQ GP registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB000); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB010); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB020); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB030); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB040); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB050); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB060); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB070); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0xB100); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB110); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB120); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB130); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB140); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB150); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB160); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB170); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } n = (int)snprintf(buf, size_left, "\nASEQ-0 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB0C0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB0D0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nASEQ-1 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB0E0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nASEQ-2 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB0F0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { n = (int)snprintf(buf, size_left, "\nASEQ-3 registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0xB1F0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } over_aseq_regs:; /* * Command DMA registers */ n = (int)snprintf(buf, size_left, "\nCommand DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7100); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0x7120); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7130); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x71F0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } /* * Queues */ n = (int)snprintf(buf, size_left, "\nRequest0 Queue DMA Channel registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7200); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 8, size_left); buf += n; size_left -= n; n = qlt_fwdump_dump_regs(qlt, buf, 0xe4, 7, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\n\nResponse0 Queue DMA Channel registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7300); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 8, size_left); buf += n; size_left -= n; n = qlt_fwdump_dump_regs(qlt, buf, 0xe4, 7, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\n\nRequest1 Queue DMA Channel registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7400); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 8, size_left); buf += n; size_left -= n; n = qlt_fwdump_dump_regs(qlt, buf, 0xe4, 7, size_left); buf += n; size_left -= n; /* * Transmit DMA registers */ n = (int)snprintf(buf, size_left, "\n\nXMT0 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7600); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7610); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXMT1 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7620); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7630); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXMT2 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7640); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7650); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXMT3 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7660); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7670); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXMT4 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7680); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7690); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nXMT Data DMA Common registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x76A0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; /* * Receive DMA registers */ n = (int)snprintf(buf, size_left, "\nRCV Thread 0 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7700); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7710); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRCV Thread 1 Data DMA registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7720); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7730); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; /* * RISC registers */ n = (int)snprintf(buf, size_left, "\nRISC GP registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x0F70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; /* * Local memory controller registers */ n = (int)snprintf(buf, size_left, "\nLMC registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3000); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3010); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3020); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3030); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3040); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3050); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x3060); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip)) { REG_WR32(qlt, 0x54, 0x3070); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } /* * Fibre protocol module registers */ n = (int)snprintf(buf, size_left, "\nFPM hardware registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4000); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4010); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4020); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4030); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4040); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4050); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4060); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4070); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4080); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x4090); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x40A0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x40B0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip)) { REG_WR32(qlt, 0x54, 0x40C0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x40D0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0x40E0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x40F0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRQ0 Array registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C80); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5C90); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CA0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CB0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CD0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5CF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRQ1 Array registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D80); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5D90); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DA0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DB0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DD0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5DF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRP0 Array registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E80); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5E90); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5EA0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5EB0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5EC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5ED0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5EE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5EF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nRP1 Array registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F10); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F20); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F30); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F40); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F50); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F60); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F70); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F80); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5F90); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FA0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FB0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FC0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FD0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FE0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x5FF0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; n = (int)snprintf(buf, size_left, "\nQueue Control Registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7800); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } /* * Fibre buffer registers */ n = (int)snprintf(buf, size_left, "\nFB hardware registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6000); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6010); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6020); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6030); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6040); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0x6060); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6070); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } REG_WR32(qlt, 0x54, 0x6100); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6130); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6150); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6170); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6190); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x61B0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; if ((qlt->qlt_83xx_chip) || (qlt->qlt_81xx_chip)) { REG_WR32(qlt, 0x54, 0x61C0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } if (qlt->qlt_83xx_chip) { REG_WR32(qlt, 0x54, 0x6530); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6540); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6550); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6560); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6570); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6580); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x6590); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x65A0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x65B0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x65C0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x65D0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x65E0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } if ((qlt->qlt_25xx_chip) || (qlt->qlt_81xx_chip) || (qlt->qlt_83xx_chip)) { REG_WR32(qlt, 0x54, 0x6F00); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } if (qlt->qlt_83xx_chip) { n = (int)snprintf(buf, size_left, "\nAT0 Array registers\n"); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7080); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x7090); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70A0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70B0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70C0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70D0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70E0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; REG_WR32(qlt, 0x54, 0x70F0); n = qlt_fwdump_dump_regs(qlt, buf, 0xc0, 16, size_left); buf += n; size_left -= n; } EL(qlt, "reset chip\n"); qlt->intr_sneak_counter = 10; mutex_enter(&qlt->intr_lock); if (qlt->qlt_mq_enabled) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_enter(&qlt->mq_resp[i].mq_lock); } } (void) qlt_reset_chip(qlt); drv_usecwait(20); qlt->intr_sneak_counter = 0; if (qlt->qlt_mq_enabled) { for (i = 1; i < qlt->qlt_queue_cnt; i++) { mutex_exit(&qlt->mq_resp[i].mq_lock); } } mutex_exit(&qlt->intr_lock); EL(qlt, "reset chip, done\n"); /* * Memory */ n = (int)snprintf(buf, size_left, "\nCode RAM\n"); buf += n; size_left -= n; addr = 0x20000; endaddr = (qlt->qlt_83xx_chip) ? 0x22400 : 0x22000; words_to_read = 0; while (addr < endaddr) { words_to_read = MBOX_DMA_MEM_SIZE >> 2; if ((words_to_read + addr) > endaddr) { words_to_read = endaddr - addr; } if ((ret = qlt_read_risc_ram(qlt, addr, words_to_read)) != QLT_SUCCESS) { EL(qlt, "Error reading risc ram - CODE RAM status=" "%llxh\n", ret); goto dump_fail; } n = qlt_dump_risc_ram(qlt, addr, words_to_read, buf, size_left); buf += n; size_left -= n; if (size_left < 100000) { EL(qlt, "run out of space - CODE RAM size_left=%d\n", size_left); goto dump_ok; } addr += words_to_read; } n = (int)snprintf(buf, size_left, "\nExternal Memory\n"); buf += n; size_left -= n; addr = 0x100000; endaddr = (((uint32_t)(qlt->fw_endaddrhi)) << 16) | qlt->fw_endaddrlo; endaddr++; if (endaddr & 7) { endaddr = (endaddr + 7) & 0xFFFFFFF8; } words_to_read = 0; while (addr < endaddr) { words_to_read = MBOX_DMA_MEM_SIZE >> 2; if ((words_to_read + addr) > endaddr) { words_to_read = endaddr - addr; } if ((ret = qlt_read_risc_ram(qlt, addr, words_to_read)) != QLT_SUCCESS) { EL(qlt, "Error reading risc ram - EXT RAM status=" "%llxh\n", ret); goto dump_fail; } n = qlt_dump_risc_ram(qlt, addr, words_to_read, buf, size_left); buf += n; size_left -= n; if (size_left < 100000) { EL(qlt, "run out of space - EXT RAM\n"); goto dump_ok; } addr += words_to_read; } /* * Label the end tag */ n = (int)snprintf(buf, size_left, "[<==END] ISP Debug Dump\n"); buf += n; size_left -= n; /* * Queue dumping */ n = (int)snprintf(buf, size_left, "\nRequest Queue\n"); buf += n; size_left -= n; if (qlt->qlt_mq_enabled) { for (i = 0; i < qlt->qlt_queue_cnt; i++) { if (qlt->mq_req[i].queue_mem_mq_base_addr) { n = (int)snprintf(buf, size_left, "\nQueue %d:\n", i); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->mq_req[i].queue_mem_mq_base_addr, REQUEST_QUEUE_MQ_ENTRIES, buf, size_left); buf += n; size_left -= n; } } } else { n = (int)snprintf(buf, size_left, "\nQueue 0:\n"); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->queue_mem_ptr + REQUEST_QUEUE_OFFSET, REQUEST_QUEUE_ENTRIES, buf, size_left); buf += n; size_left -= n; } if (!qlt->qlt_83xx_chip) { n = (int)snprintf(buf, size_left, "\nPriority Queue\n"); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->queue_mem_ptr + PRIORITY_QUEUE_OFFSET, PRIORITY_QUEUE_ENTRIES, buf, size_left); buf += n; size_left -= n; } n = (int)snprintf(buf, size_left, "\nResponse Queue\n"); buf += n; size_left -= n; if (qlt->qlt_mq_enabled) { for (i = 0; i < qlt->qlt_queue_cnt; i++) { if (qlt->mq_resp[i].queue_mem_mq_base_addr) { n = (int)snprintf(buf, size_left, "\nQueue %d:\n", i); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->mq_resp[i].queue_mem_mq_base_addr, RESPONSE_QUEUE_MQ_ENTRIES, buf, size_left); buf += n; size_left -= n; } } } else { n = (int)snprintf(buf, size_left, "\nQueue 0:\n"); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->queue_mem_ptr + RESPONSE_QUEUE_OFFSET, RESPONSE_QUEUE_ENTRIES, buf, size_left); buf += n; size_left -= n; } n = (int)snprintf(buf, size_left, "\nATIO Queue\nQueue 0:\n"); buf += n; size_left -= n; n = qlt_dump_queue(qlt, qlt->queue_mem_ptr + ATIO_QUEUE_OFFSET, ATIO_QUEUE_ENTRIES, buf, size_left); buf += n; size_left -= n; /* * Label dump reason */ if (ssci != NULL) { n = (int)snprintf(buf, size_left, "\nFirmware dump reason: %s-%s\n", qlt->qlt_port_alias, ssci->st_additional_info); } else { n = (int)snprintf(buf, size_left, "\nFirmware dump reason: %s-%s\n", qlt->qlt_port_alias, "no additional infor"); } buf += n; size_left -= n; dump_ok: EL(qlt, "left-%d\n", size_left); mutex_enter(&qlt->qlt_ioctl_lock); qlt->qlt_ioctl_flags &= ~(QLT_FWDUMP_INPROGRESS | QLT_FWDUMP_FETCHED_BY_USER); qlt->qlt_ioctl_flags |= QLT_FWDUMP_ISVALID; mutex_exit(&qlt->qlt_ioctl_lock); return (FCT_SUCCESS); dump_fail: EL(qlt, "dump not done\n"); mutex_enter(&qlt->qlt_ioctl_lock); qlt->qlt_ioctl_flags &= QLT_IOCTL_FLAG_MASK; mutex_exit(&qlt->qlt_ioctl_lock); return (FCT_FAILURE); } static int qlt_fwdump_dump_regs(qlt_state_t *qlt, caddr_t buf, int startaddr, int count, uint_t size_left) { int i; int n; char c = ' '; for (i = 0, n = 0; i < count; i++) { if ((i + 1) & 7) { c = ' '; } else { c = '\n'; } n = (int)(n + (int)snprintf(&buf[n], (uint_t)(size_left - n), "%08x%c", REG_RD32(qlt, startaddr + (i << 2)), c)); } return (n); } static int qlt_dump_risc_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words, caddr_t buf, uint_t size_left) { int i; int n; char c = ' '; uint32_t *ptr; ptr = (uint32_t *)((caddr_t)qlt->queue_mem_ptr + MBOX_DMA_MEM_OFFSET); for (i = 0, n = 0; i < words; i++) { if ((i & 7) == 0) { n = (int)(n + (int)snprintf(&buf[n], (uint_t)(size_left - n), "%08x: ", addr + i)); } if ((i + 1) & 7) { c = ' '; } else { c = '\n'; } n = (int)(n + (int)snprintf(&buf[n], (uint_t)(size_left - n), "%08x%c", ptr[i], c)); } return (n); } static int qlt_dump_queue(qlt_state_t *qlt, caddr_t qadr, int entries, caddr_t buf, uint_t size_left) { int i; int n; char c = ' '; int words; uint32_t *ptr; uint32_t w; words = entries * 16; ptr = (uint32_t *)qadr; for (i = 0, n = 0; i < words; i++) { if ((i & 7) == 0) { n = (int)(n + (int)snprintf(&buf[n], (uint_t)(size_left - n), "%05x: ", i)); } if ((i + 1) & 7) { c = ' '; } else { c = '\n'; } w = QMEM_RD32(qlt, &ptr[i]); n = (int)(n + (int)snprintf(&buf[n], (size_left - n), "%08x%c", w, c)); } return (n); } /* * Only called by debug dump. Interrupts are disabled and mailboxes alongwith * mailbox ram is available. * Copy data from RISC RAM to system memory */ static fct_status_t qlt_read_risc_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words) { uint64_t da; fct_status_t ret; REG_WR16(qlt, REG_MBOX(0), MBC_DUMP_RAM_EXTENDED); da = qlt->queue_mem_cookie.dmac_laddress; da += MBOX_DMA_MEM_OFFSET; /* System destination address */ REG_WR16(qlt, REG_MBOX(3), LSW(LSD(da))); REG_WR16(qlt, REG_MBOX(2), MSW(LSD(da))); REG_WR16(qlt, REG_MBOX(7), LSW(MSD(da))); REG_WR16(qlt, REG_MBOX(6), MSW(MSD(da))); /* Length */ REG_WR16(qlt, REG_MBOX(5), LSW(words)); REG_WR16(qlt, REG_MBOX(4), MSW(words)); /* RISC source address */ REG_WR16(qlt, REG_MBOX(1), LSW(addr)); REG_WR16(qlt, REG_MBOX(8), MSW(addr)); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret == QLT_SUCCESS) { (void) ddi_dma_sync(qlt->queue_mem_dma_handle, MBOX_DMA_MEM_OFFSET, words << 2, DDI_DMA_SYNC_FORCPU); } else { EL(qlt, "qlt_raw_mailbox_command=0x0ch status=%llxh\n", ret); } return (ret); } static fct_status_t qlt_mbx_mpi_ram(qlt_state_t *qlt, uint32_t addr, uint32_t words, uint16_t direction) { uint64_t da; fct_status_t ret; REG_WR16(qlt, REG_MBOX(0), MBC_MPI_RAM); da = qlt->queue_mem_cookie.dmac_laddress; da += MBOX_DMA_MEM_OFFSET; /* System destination address */ REG_WR16(qlt, REG_MBOX(3), LSW(LSD(da))); REG_WR16(qlt, REG_MBOX(2), MSW(LSD(da))); REG_WR16(qlt, REG_MBOX(7), LSW(MSD(da))); REG_WR16(qlt, REG_MBOX(6), MSW(MSD(da))); /* Length */ REG_WR16(qlt, REG_MBOX(5), LSW(words)); REG_WR16(qlt, REG_MBOX(4), MSW(words)); /* RISC source address */ REG_WR16(qlt, REG_MBOX(1), LSW(addr)); REG_WR16(qlt, REG_MBOX(8), MSW(addr)); REG_WR16(qlt, REG_MBOX(9), direction); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret == QLT_SUCCESS) { (void) ddi_dma_sync(qlt->queue_mem_dma_handle, MBOX_DMA_MEM_OFFSET, words << 2, DDI_DMA_SYNC_FORCPU); } else { EL(qlt, "qlt_raw_mailbox_command=0x05h status=%llxh\n", ret); } return (ret); } static void qlt_verify_fw(qlt_state_t *qlt) { caddr_t req; uint16_t qi = 0; /* Just put it on the request queue */ mutex_enter(&qlt->mq_req[qi].mq_lock); req = qlt_get_req_entries(qlt, 1, qi); if (req == NULL) { EL(qlt, "req = NULL\n"); mutex_exit(&qlt->mq_req[qi].mq_lock); return; } bzero(req, IOCB_SIZE); req[0] = 0x1b; req[1] = 1; QMEM_WR32(qlt, (&req[4]), 0xffffffff); QMEM_WR16(qlt, (&req[0x8]), 1); /* options - don't update */ QMEM_WR32(qlt, (&req[0x14]), 0x80010300); qlt_submit_req_entries(qlt, 1, qi); mutex_exit(&qlt->mq_req[qi].mq_lock); } static fct_status_t qlt_mq_destroy(qlt_state_t *qlt) { int idx; for (idx = 1; idx < qlt->qlt_queue_cnt; idx++) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); (void) ddi_dma_unbind_handle( qlt->mq_resp[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_resp[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_resp[idx].queue_mem_mq_dma_handle); } return (QLT_SUCCESS); } static fct_status_t qlt_mq_create(qlt_state_t *qlt, int idx) { ddi_device_acc_attr_t dev_acc_attr; size_t discard; uint_t ncookies; dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; /* * MQ Request queue */ if (ddi_dma_alloc_handle(qlt->dip, &qlt_queue_dma_attr_mq_req1, DDI_DMA_SLEEP, 0, &qlt->mq_req[idx].queue_mem_mq_dma_handle) != DDI_SUCCESS) { return (QLT_FAILURE); } if (ddi_dma_mem_alloc(qlt->mq_req[idx].queue_mem_mq_dma_handle, REQUEST_QUEUE_MQ_SIZE, &dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->mq_req[idx].queue_mem_mq_base_addr, &discard, &qlt->mq_req[idx].queue_mem_mq_acc_handle) != DDI_SUCCESS) { ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } if (ddi_dma_addr_bind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle, NULL, qlt->mq_req[idx].queue_mem_mq_base_addr, REQUEST_QUEUE_MQ_SIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->mq_req[idx].queue_mem_mq_cookie, &ncookies) != DDI_SUCCESS) { ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } if (ncookies != 1) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } /* * MQ Response queue */ if (ddi_dma_alloc_handle(qlt->dip, &qlt_queue_dma_attr_mq_rsp1, DDI_DMA_SLEEP, 0, &qlt->mq_resp[idx].queue_mem_mq_dma_handle) != DDI_SUCCESS) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } if (ddi_dma_mem_alloc(qlt->mq_resp[idx].queue_mem_mq_dma_handle, RESPONSE_QUEUE_MQ_SIZE, &dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->mq_resp[idx].queue_mem_mq_base_addr, &discard, &qlt->mq_resp[idx].queue_mem_mq_acc_handle) != DDI_SUCCESS) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_free_handle(&qlt->mq_resp[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } if (ddi_dma_addr_bind_handle( qlt->mq_resp[idx].queue_mem_mq_dma_handle, NULL, qlt->mq_resp[idx].queue_mem_mq_base_addr, RESPONSE_QUEUE_MQ_SIZE, DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->mq_resp[idx].queue_mem_mq_cookie, &ncookies) != DDI_SUCCESS) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_resp[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_resp[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } if (ncookies != 1) { (void) ddi_dma_unbind_handle( qlt->mq_req[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_req[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_req[idx].queue_mem_mq_dma_handle); (void) ddi_dma_unbind_handle( qlt->mq_resp[idx].queue_mem_mq_dma_handle); ddi_dma_mem_free(&qlt->mq_resp[idx].queue_mem_mq_acc_handle); ddi_dma_free_handle(&qlt->mq_resp[idx].queue_mem_mq_dma_handle); return (QLT_FAILURE); } qlt->mq_req[idx].mq_ptr = qlt->mq_req[idx].queue_mem_mq_base_addr; qlt->mq_req[idx].mq_ndx_to_fw = qlt->mq_req[idx].mq_ndx_from_fw = 0; qlt->mq_req[idx].mq_available = REQUEST_QUEUE_MQ_ENTRIES - 1; bzero(qlt->mq_req[idx].mq_ptr, REQUEST_QUEUE_MQ_SIZE); qlt->mq_resp[idx].mq_ptr = qlt->mq_resp[idx].queue_mem_mq_base_addr; qlt->mq_resp[idx].mq_ndx_to_fw = qlt->mq_resp[idx].mq_ndx_from_fw = 0; bzero(qlt->mq_resp[idx].mq_ptr, RESPONSE_QUEUE_MQ_SIZE); return (QLT_SUCCESS); } static void qlt_handle_verify_fw_completion(qlt_state_t *qlt, uint8_t *rsp) { uint16_t status; char info[80]; status = QMEM_RD16(qlt, rsp+8); if (status != 0) { (void) snprintf(info, 80, "qlt_handle_verify_fw_completion: " "status:%x, rsp:%p", status, (void *)rsp); if (status == 3) { uint16_t error_code; error_code = QMEM_RD16(qlt, rsp+0xA); (void) snprintf(info, 80, "qlt_handle_verify_fw_" "completion: error code:%x", error_code); } } } /* * qlt_el_trace_desc_ctor - Construct an extended logging trace descriptor. * * Input: Pointer to the adapter state structure. * Returns: Success or Failure. * Context: Kernel context. */ static int qlt_el_trace_desc_ctor(qlt_state_t *qlt) { qlt_trace_entry_t *entry; size_t maxsize; qlt->qlt_trace_desc = (qlt_trace_desc_t *)kmem_zalloc( sizeof (qlt_trace_desc_t), KM_SLEEP); qlt->qlt_log_entries = QL_LOG_ENTRIES; maxsize = qlt->qlt_log_entries * sizeof (qlt_trace_entry_t); entry = kmem_zalloc(maxsize, KM_SLEEP); mutex_init(&qlt->qlt_trace_desc->mutex, NULL, MUTEX_DRIVER, NULL); qlt->qlt_trace_desc->trace_buffer = entry; qlt->qlt_trace_desc->trace_buffer_size = maxsize; qlt->qlt_trace_desc->nindex = 0; qlt->qlt_trace_desc->nentries = qlt->qlt_log_entries; qlt->qlt_trace_desc->start = qlt->qlt_trace_desc->end = 0; qlt->qlt_trace_desc->csize = 0; qlt->qlt_trace_desc->count = 0; return (DDI_SUCCESS); } /* * qlt_el_trace_desc_dtor - Destroy an extended logging trace descriptor. * * Input: Pointer to the adapter state structure. * Returns: Success or Failure. * Context: Kernel context. */ static int qlt_el_trace_desc_dtor(qlt_state_t *qlt) { int rval = DDI_SUCCESS; if (qlt->qlt_trace_desc != NULL) { if (qlt->qlt_trace_desc->trace_buffer != NULL) { kmem_free(qlt->qlt_trace_desc->trace_buffer, qlt->qlt_trace_desc->trace_buffer_size); } mutex_destroy(&qlt->qlt_trace_desc->mutex); kmem_free(qlt->qlt_trace_desc, sizeof (qlt_trace_desc_t)); } return (rval); } /* * qlt_el_msg * Extended logging message * * Input: * qlt: adapter state pointer. * fn: function name. * ce: level * ...: Variable argument list. * * Context: * Kernel/Interrupt context. */ void qlt_el_msg(qlt_state_t *qlt, const char *fn, int ce, ...) { char *s, *fmt = 0, *fmt1 = 0; /* * EL_BUFFER_RESERVE 256 is the max # of bytes * that driver's log could be collected. * add 3 more buytes for safely maniplulation. */ char buf[EL_BUFFER_RESERVE + 3]; char buf1[QL_LOG_LENGTH]; size_t tmp; size_t rval, rval1; va_list vl; qlt_trace_desc_t *desc = qlt->qlt_trace_desc; qlt_trace_entry_t *entry; uint32_t cindex; timespec_t time; uint32_t count; size_t left; (void) bzero((void *)&buf[0], EL_BUFFER_RESERVE + 3); fmt1 = &buf[0]; TRACE_BUFFER_LOCK(qlt); /* locate the entry to be filled out */ cindex = desc->nindex; entry = &desc->trace_buffer[cindex]; count = desc->count; desc->end = desc->nindex; desc->nindex++; if (desc->nindex == desc->nentries) { desc->nindex = 0; } if (desc->csize < desc->nentries) { desc->csize ++; } else { /* * once wrapped, csize is fixed. * so we have to adjust start point */ desc->start = desc->nindex; } gethrestime(&time); rval = snprintf(fmt1, (size_t)EL_BUFFER_RESERVE, QL_BANG "%d=>QEL %s(%d,%d):: %s, ", count, QL_NAME, qlt->instance, 0, fn); rval1 = rval; va_start(vl, ce); s = va_arg(vl, char *); fmt = fmt1 + rval; tmp = vsnprintf(fmt, (size_t)(uint32_t)((int)EL_BUFFER_RESERVE - rval), s, vl); va_end(vl); rval += tmp; if (rval > QL_LOG_LENGTH - 1) { left = rval - (QL_LOG_LENGTH - 1); /* store the remaining string */ (void) strncpy(buf1, fmt1 + (QL_LOG_LENGTH - 1), left); (void) strncpy(entry->buf, fmt1, (QL_LOG_LENGTH - 1)); entry->buf[QL_LOG_LENGTH - 1] = '\n'; bcopy((void *)&time, (void *)&entry->hs_time, sizeof (timespec_t)); /* * remaining msg will be stored in the nex entry * with same timestamp and same sequence number */ cindex = desc->nindex; entry = &desc->trace_buffer[cindex]; desc->end = desc->nindex; desc->nindex++; if (desc->nindex == desc->nentries) { desc->nindex = 0; } if (desc->csize < desc->nentries) { desc->csize ++; } else { desc->start = desc->nindex; } (void) strncpy(&entry->buf[0], fmt1, rval1); (void) strncpy(&entry->buf[rval1], &buf1[0], left); entry->buf[rval1 + left] = 0; bcopy((void *)&time, (void *)&entry->hs_time, sizeof (timespec_t)); if (qlt->qlt_eel_level == 1) { cmn_err(ce, fmt1); } desc->count++; TRACE_BUFFER_UNLOCK(qlt); return; } desc->count ++; bcopy((void *)&time, (void *)&entry->hs_time, sizeof (timespec_t)); /* * Here we know that fmt1 will fit within QL_LOG_LENGTH due to the * check above, but smatch identifies a potential problem. */ (void) strncpy(entry->buf, fmt1, rval); entry->buf[rval] = 0; TRACE_BUFFER_UNLOCK(qlt); if (qlt->qlt_eel_level == 1) { cmn_err(ce, fmt1); } } static int qlt_read_int_prop(qlt_state_t *qlt, char *prop, int defval) { return (ddi_getprop(DDI_DEV_T_ANY, qlt->dip, DDI_PROP_DONTPASS | DDI_PROP_CANSLEEP, prop, defval)); } static int qlt_read_string_prop(qlt_state_t *qlt, char *prop, char **prop_val) { return (ddi_prop_lookup_string(DDI_DEV_T_ANY, qlt->dip, DDI_PROP_DONTPASS, prop, prop_val)); } static int qlt_read_int_instance_prop(qlt_state_t *qlt, char *prop, int defval) { char inst_prop[256]; int val; /* * Get adapter instance specific parameters. If the instance * specific parameter isn't there, try the global parameter. */ (void) sprintf(inst_prop, "hba%d-%s", qlt->instance, prop); if ((val = qlt_read_int_prop(qlt, inst_prop, defval)) == defval) { val = qlt_read_int_prop(qlt, prop, defval); } return (val); } static int qlt_read_string_instance_prop(qlt_state_t *qlt, char *prop, char **prop_val) { char instance_prop[256]; /* Get adapter instance specific parameter. */ (void) sprintf(instance_prop, "hba%d-%s", qlt->instance, prop); return (qlt_read_string_prop(qlt, instance_prop, prop_val)); } static int qlt_convert_string_to_ull(char *prop, int radix, u_longlong_t *result) { return (ddi_strtoull((const char *)prop, 0, radix, result)); } static boolean_t qlt_wwn_overload_prop(qlt_state_t *qlt) { char *prop_val = NULL; int rval; int radix; u_longlong_t wwnn = 0, wwpn = 0; boolean_t overloaded = FALSE; radix = 16; rval = qlt_read_string_instance_prop(qlt, "adapter-wwnn", &prop_val); if (rval == DDI_PROP_SUCCESS) { rval = qlt_convert_string_to_ull(prop_val, radix, &wwnn); } if (rval == DDI_PROP_SUCCESS) { rval = qlt_read_string_instance_prop(qlt, "adapter-wwpn", &prop_val); if (rval == DDI_PROP_SUCCESS) { rval = qlt_convert_string_to_ull(prop_val, radix, &wwpn); } } if (rval == DDI_PROP_SUCCESS) { overloaded = TRUE; /* Overload the current node/port name nvram copy */ bcopy((char *)&wwnn, qlt->nvram->node_name, 8); BIG_ENDIAN_64(qlt->nvram->node_name); bcopy((char *)&wwpn, qlt->nvram->port_name, 8); BIG_ENDIAN_64(qlt->nvram->port_name); } return (overloaded); } /* * prop_text - Return a pointer to a string describing the status * * Input: prop_status = the return status from a property function. * Returns: pointer to a string. * Context: Kernel context. */ char * prop_text(int prop_status) { string_table_t *entry = &prop_status_tbl[0]; return (value2string(entry, prop_status, 0xFFFF)); } /* * value2string Return a pointer to a string associated with the value * * Input: entry = the value to string table * value = the value * Returns: pointer to a string. * Context: Kernel context. */ char * value2string(string_table_t *entry, int value, int delimiter) { for (; entry->value != delimiter; entry++) { if (entry->value == value) { break; } } return (entry->string); } /* * qlt_chg_endian Change endianess of byte array. * * Input: buf = array pointer. * size = size of array in bytes. * * Context: Interrupt or Kernel context. */ void qlt_chg_endian(uint8_t buf[], size_t size) { uint8_t byte; size_t cnt1; size_t cnt; cnt1 = size - 1; for (cnt = 0; cnt < size / 2; cnt++) { byte = buf[cnt1]; buf[cnt1] = buf[cnt]; buf[cnt] = byte; cnt1--; } } /* * ql_mps_reset * Reset MPS for FCoE functions. * * Input: * ha = virtual adapter state pointer. * * Context: * Kernel context. */ static void qlt_mps_reset(qlt_state_t *qlt) { uint32_t data, dctl = 1000; do { if (dctl-- == 0 || qlt_raw_wrt_risc_ram_word(qlt, 0x7c00, 1) != QLT_SUCCESS) { EL(qlt, "qlt_mps_reset: semaphore request fail," " cnt=%d\n", dctl); return; } if (qlt_raw_rd_risc_ram_word(qlt, 0x7c00, &data) != QLT_SUCCESS) { (void) qlt_raw_wrt_risc_ram_word(qlt, 0x7c00, 0); EL(qlt, "qlt_mps_reset: semaphore read fail," " cnt=%d\n", dctl); return; } } while (!(data & BIT_0)); if (qlt_raw_rd_risc_ram_word(qlt, 0x7A15, &data) == QLT_SUCCESS) { dctl = (uint16_t)PCICFG_RD16(qlt, 0x54); if ((data & 0xe0) != (dctl & 0xe0)) { data &= 0xff1f; data |= dctl & 0xe0; (void) qlt_raw_wrt_risc_ram_word(qlt, 0x7A15, data); } } else { EL(qlt, "qlt_mps_reset: read 0x7a15 failed.\n"); } (void) qlt_raw_wrt_risc_ram_word(qlt, 0x7c00, 0); } /* * qlt_raw_wrt_risc_ram_word * Write RISC RAM word. * * Input: qlt: adapter state pointer. * risc_address: risc ram word address. * data: data. * * Returns: qlt local function return status code. * * Context: Kernel context. */ static fct_status_t qlt_raw_wrt_risc_ram_word(qlt_state_t *qlt, uint32_t risc_address, uint32_t data) { fct_status_t ret; REG_WR16(qlt, REG_MBOX(0), MBC_WRITE_RAM_EXTENDED); REG_WR16(qlt, REG_MBOX(1), LSW(risc_address)); REG_WR16(qlt, REG_MBOX(2), LSW(data)); REG_WR16(qlt, REG_MBOX(3), MSW(data)); REG_WR16(qlt, REG_MBOX(8), MSW(risc_address)); ret = qlt_raw_mailbox_command(qlt); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=MBC_WRITE_RAM_EXTENDED status" "=%llxh\n", ret); } return (ret); } /* * ql_raw_rd_risc_ram_word * Read RISC RAM word. * * Input: qlt: adapter state pointer. * risc_address: risc ram word address. * data: data pointer. * * Returns: ql local function return status code. * * Context: Kernel context. */ static fct_status_t qlt_raw_rd_risc_ram_word(qlt_state_t *qlt, uint32_t risc_address, uint32_t *data) { fct_status_t ret; REG_WR16(qlt, REG_MBOX(0), MBC_READ_RAM_EXTENDED); REG_WR16(qlt, REG_MBOX(1), LSW(risc_address)); REG_WR16(qlt, REG_MBOX(2), MSW(risc_address)); ret = qlt_raw_mailbox_command(qlt); *data = REG_RD16(qlt, REG_MBOX(2)); *data |= (REG_RD16(qlt, REG_MBOX(3)) << 16); REG_WR32(qlt, REG_HCCR, HCCR_CMD(CLEAR_RISC_TO_PCI_INTR)); if (ret != QLT_SUCCESS) { EL(qlt, "qlt_raw_mailbox_command=MBC_READ_RAM_EXTENDED status" "=%llxh\n", ret); } return (ret); } static void qlt_properties(qlt_state_t *qlt) { int32_t cnt = 0; int32_t defval = 0xffff; if (qlt_wwn_overload_prop(qlt) == TRUE) { EL(qlt, "wwnn overloaded.\n"); } /* configure extended logging from conf file */ if ((cnt = qlt_read_int_instance_prop(qlt, "extended-logging", defval)) != defval) { qlt->qlt_eel_level = (uint8_t)(cnt & 0xff); EL(qlt, "extended error logging=%d\n", cnt); } if ((cnt = qlt_read_int_instance_prop(qlt, "bucketcnt2k", defval)) != defval) { qlt->qlt_bucketcnt[0] = cnt; EL(qlt, "2k bucket o/l=%d\n", cnt); } if ((cnt = qlt_read_int_instance_prop(qlt, "bucketcnt8k", defval)) != defval) { qlt->qlt_bucketcnt[1] = cnt; EL(qlt, "8k bucket o/l=%d\n", cnt); } if ((cnt = qlt_read_int_instance_prop(qlt, "bucketcnt64k", defval)) != defval) { qlt->qlt_bucketcnt[2] = cnt; EL(qlt, "64k bucket o/l=%d\n", cnt); } if ((cnt = qlt_read_int_instance_prop(qlt, "bucketcnt128k", defval)) != defval) { qlt->qlt_bucketcnt[3] = cnt; EL(qlt, "128k bucket o/l=%d\n", cnt); } if ((cnt = qlt_read_int_instance_prop(qlt, "bucketcnt256", defval)) != defval) { qlt->qlt_bucketcnt[4] = cnt; EL(qlt, "256k bucket o/l=%d\n", cnt); } } /* ******************************************************************* */ /* ****************** 27xx Dump Template Functions ******************* */ /* ******************************************************************* */ /* * qlt_get_dmp_template * Get dump template from firmware module * * Input: * qlt: qlt_state_t pointer. * * Returns: * qlt local function return status code. * * Context: * Kernel context. */ static fct_status_t qlt_27xx_get_dmp_template(qlt_state_t *qlt) { ddi_device_acc_attr_t dev_acc_attr; dev_info_t *dip = qlt->dip; uint_t ncookies; size_t discard; uint32_t word_count, cnt, *bp, *dp; if (qlt->dmp_template_dma_handle != NULL) { (void) ddi_dma_unbind_handle(qlt->dmp_template_dma_handle); if (qlt->dmp_template_acc_handle != NULL) { ddi_dma_mem_free(&qlt->dmp_template_acc_handle); } ddi_dma_free_handle(&qlt->dmp_template_dma_handle); } if ((word_count = tmplt2700_length01) == 0) { EL(qlt, "No dump template, length=0\n"); return (QLT_FAILURE); } dev_acc_attr.devacc_attr_version = DDI_DEVICE_ATTR_V0; dev_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; dev_acc_attr.devacc_attr_dataorder = DDI_STRICTORDER_ACC; if (ddi_dma_alloc_handle(dip, &qlt_queue_dma_attr, DDI_DMA_SLEEP, 0, &qlt->dmp_template_dma_handle) != DDI_SUCCESS) { EL(qlt, "Unable to allocate template handle"); return (QLT_FAILURE); } if (ddi_dma_mem_alloc(qlt->dmp_template_dma_handle, (word_count << 2), &dev_acc_attr, DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->dmp_template_addr, &discard, &qlt->dmp_template_acc_handle) != DDI_SUCCESS) { ddi_dma_free_handle(&qlt->dmp_template_dma_handle); EL(qlt, "Unable to allocate template buffer"); return (QLT_FAILURE); } if (ddi_dma_addr_bind_handle(qlt->dmp_template_dma_handle, NULL, qlt->dmp_template_addr, (word_count << 2), DDI_DMA_RDWR | DDI_DMA_CONSISTENT, DDI_DMA_SLEEP, 0, &qlt->dmp_template_cookie, &ncookies) != DDI_SUCCESS) { ddi_dma_mem_free(&qlt->dmp_template_acc_handle); ddi_dma_free_handle(&qlt->dmp_template_dma_handle); EL(qlt, "Unable to bind template handle"); return (QLT_FAILURE); } if (ncookies != 1) { (void) ddi_dma_unbind_handle(qlt->dmp_template_dma_handle); ddi_dma_mem_free(&qlt->dmp_template_acc_handle); ddi_dma_free_handle(&qlt->dmp_template_dma_handle); EL(qlt, "cookies (%d) > 1.\n", ncookies); return (QLT_FAILURE); } /* Get big endian template. */ bp = (uint32_t *)qlt->dmp_template_addr; dp = (uint32_t *)tmplt2700_code01; for (cnt = 0; cnt < word_count; cnt++) { ddi_put32(qlt->dmp_template_acc_handle, bp, *dp++); if (cnt > 6) { qlt_chg_endian((uint8_t *)bp, 4); } bp++; } return (QLT_SUCCESS); } static int qlt_27xx_dt_riob1(qlt_state_t *qlt, qlt_dt_riob1_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t i, cnt; uint8_t *bp = dbuff; uint32_t addr = entry->addr; uint32_t reg = entry->pci_offset; cnt = CHAR_TO_SHORT(entry->reg_count_l, entry->reg_count_h); esize = cnt * 4; /* addr */ esize += cnt * entry->reg_size; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } REG_WR32(qlt, REG_IOBUS_BASE_ADDR, addr); while (cnt--) { *bp++ = LSB(LSW(addr)); *bp++ = MSB(LSW(addr)); *bp++ = LSB(MSW(addr)); *bp++ = MSB(MSW(addr)); for (i = 0; i < entry->reg_size; i++) { *bp++ = REG_RD8(qlt, reg++); } addr++; } return (esize); } static void qlt_27xx_dt_wiob1(qlt_state_t *qlt, qlt_dt_wiob1_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { uint32_t reg = entry->pci_offset; if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } REG_WR32(qlt, REG_IOBUS_BASE_ADDR, entry->addr); REG_WR32(qlt, reg, entry->data); } static int qlt_27xx_dt_riob2(qlt_state_t *qlt, qlt_dt_riob2_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t i, cnt; uint8_t *bp = dbuff; uint32_t reg = entry->pci_offset; uint32_t addr = entry->addr; cnt = CHAR_TO_SHORT(entry->reg_count_l, entry->reg_count_h); esize = cnt * 4; /* addr */ esize += cnt * entry->reg_size; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } REG_WR32(qlt, REG_IOBUS_BASE_ADDR, addr); REG_WR32(qlt, entry->bank_sel_offset, entry->reg_bank); while (cnt--) { *bp++ = LSB(LSW(addr)); *bp++ = MSB(LSW(addr)); *bp++ = LSB(MSW(addr)); *bp++ = MSB(MSW(addr)); for (i = 0; i < entry->reg_size; i++) { *bp++ = REG_RD8(qlt, reg++); } addr++; } return (esize); } static void qlt_27xx_dt_wiob2(qlt_state_t *qlt, qlt_dt_wiob2_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { uint16_t data; uint32_t reg = entry->pci_offset; if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } data = CHAR_TO_SHORT(entry->data_l, entry->data_h); REG_WR32(qlt, REG_IOBUS_BASE_ADDR, entry->addr); REG_WR32(qlt, entry->bank_sel_offset, entry->reg_bank); REG_WR16(qlt, reg, data); } static int qlt_27xx_dt_rpci(qlt_state_t *qlt, qlt_dt_rpci_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t i; uint8_t *bp = dbuff; uint32_t reg = entry->addr; esize = 4; /* addr */ esize += 4; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } *bp++ = LSB(LSW(entry->addr)); *bp++ = MSB(LSW(entry->addr)); *bp++ = LSB(MSW(entry->addr)); *bp++ = MSB(MSW(entry->addr)); for (i = 0; i < 4; i++) { *bp++ = REG_RD8(qlt, reg++); } return (esize); } static void qlt_27xx_dt_wpci(qlt_state_t *qlt, qlt_dt_wpci_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { uint32_t reg = entry->addr; if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } REG_WR32(qlt, reg, entry->data); } static int qlt_27xx_dt_rram(qlt_state_t *qlt, qlt_dt_rram_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize, rval; uint32_t start = entry->start_addr; uint32_t end = entry->end_addr; if (entry->ram_area == 2) { end = qlt->fw_ext_memory_end; } else if (entry->ram_area == 3) { start = qlt->fw_shared_ram_start; end = qlt->fw_shared_ram_end; } else if (entry->ram_area == 4) { start = qlt->fw_ddr_ram_start; end = qlt->fw_ddr_ram_end; } else if (entry->ram_area != 1) { EL(qlt, "skipped, unknown RAM_AREA %d\n", entry->ram_area); start = 0; end = 0; } esize = end > start ? end - start : 0; if (esize) { esize = (esize + 1) * 4; } if (dbuff == NULL) { return (esize); } if (esize == 0 || esize + dbuff >= dbuff_end) { if (esize != 0) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); } else { EL(qlt, "skipped, no ram_area=%xh, start=%xh " "end=%xh\n", entry->ram_area, start, end); } entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } entry->end_addr = end; entry->start_addr = start; if ((rval = qlt_27xx_dump_ram(qlt, MBC_DUMP_RAM_EXTENDED, start, esize / 4, dbuff)) != QLT_SUCCESS) { EL(qlt, "dump_ram failed, rval=%xh, addr=%xh, len=%xh, " "esize=0\n", rval, start, esize / 4); return (0); } return (esize); } static int qlt_27xx_dt_gque(qlt_state_t *qlt, qlt_dt_gque_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t cnt, q_cnt, e_cnt, i; uint8_t *bp = dbuff, *dp; if (entry->queue_type == 1) { e_cnt = qlt->qlt_queue_cnt; esize = e_cnt * 2; /* queue number */ esize += e_cnt * 2; /* queue entries */ /* queue size */ esize += REQUEST_QUEUE_ENTRIES * IOCB_SIZE; for (q_cnt = 1; q_cnt < qlt->qlt_queue_cnt; q_cnt++) { esize += REQUEST_QUEUE_MQ_ENTRIES * IOCB_SIZE; } if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } entry->num_queues = e_cnt; for (q_cnt = 0; q_cnt < entry->num_queues; q_cnt++) { e_cnt = q_cnt == 0 ? REQUEST_QUEUE_ENTRIES : REQUEST_QUEUE_MQ_ENTRIES; dp = (uint8_t *)qlt->mq_req[q_cnt].mq_ptr; *bp++ = LSB(q_cnt); *bp++ = MSB(q_cnt); *bp++ = LSB(e_cnt); *bp++ = MSB(e_cnt); for (cnt = 0; cnt < e_cnt; cnt++) { for (i = 0; i < IOCB_SIZE; i++) { *bp++ = *dp++; } } } } else if (entry->queue_type == 2) { e_cnt = qlt->qlt_queue_cnt; esize = e_cnt * 2; /* queue number */ esize += e_cnt * 2; /* queue entries */ /* queue size */ esize += RESPONSE_QUEUE_ENTRIES * IOCB_SIZE; for (q_cnt = 1; q_cnt < qlt->qlt_queue_cnt; q_cnt++) { esize += RESPONSE_QUEUE_MQ_ENTRIES * IOCB_SIZE; } if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped2, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } entry->num_queues = e_cnt; for (q_cnt = 0; q_cnt < entry->num_queues; q_cnt++) { e_cnt = q_cnt == 0 ? RESPONSE_QUEUE_ENTRIES : RESPONSE_QUEUE_MQ_ENTRIES; dp = (uint8_t *)qlt->mq_resp[q_cnt].mq_ptr; *bp++ = LSB(q_cnt); *bp++ = MSB(q_cnt); *bp++ = LSB(e_cnt); *bp++ = MSB(e_cnt); for (cnt = 0; cnt < e_cnt; cnt++) { for (i = 0; i < IOCB_SIZE; i++) { *bp++ = *dp++; } } } } else if (entry->queue_type == 3) { e_cnt = 1; esize = e_cnt * 2; /* queue number */ esize += e_cnt * 2; /* queue entries */ /* queue size */ esize += RESPONSE_QUEUE_ENTRIES * IOCB_SIZE; if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped2, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } entry->num_queues = e_cnt; for (q_cnt = 0; q_cnt < entry->num_queues; q_cnt++) { e_cnt = ATIO_QUEUE_ENTRIES; dp = (uint8_t *)qlt->atio_ptr; *bp++ = LSB(q_cnt); *bp++ = MSB(q_cnt); *bp++ = LSB(e_cnt); *bp++ = MSB(e_cnt); for (cnt = 0; cnt < e_cnt; cnt++) { for (i = 0; i < IOCB_SIZE; i++) { *bp++ = *dp++; } } } } else { EL(qlt, "skipped, unknown queue_type %d, esize=0\n", entry->queue_type); if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } return (esize); } /*ARGSUSED*/ static int qlt_27xx_dt_gfce(qlt_state_t *qlt, qlt_dt_gfce_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } static void qlt_27xx_dt_prisc(qlt_state_t *qlt, qlt_dt_prisc_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { clock_t timer; if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } /* Pause RISC. */ if ((REG_RD32(qlt, REG_RISC_STATUS) & BIT_8) == 0) { REG_WR32(qlt, REG_HCCR, 0x30000000); for (timer = 30000; (REG_RD32(qlt, REG_RISC_STATUS) & BIT_8) == 0; timer--) { if (timer) { drv_usecwait(100); if (timer % 10000 == 0) { EL(qlt, "risc pause %d\n", timer); } } else { EL(qlt, "risc pause timeout\n"); break; } } } } static void qlt_27xx_dt_rrisc(qlt_state_t *qlt, qlt_dt_rrisc_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { clock_t timer; uint16_t rom_status; if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } /* Shutdown DMA. */ REG_WR32(qlt, REG_CTRL_STATUS, DMA_SHUTDOWN_CTRL); /* Wait for DMA to stop. */ for (timer = 0; timer < 30000; timer++) { if (!(REG_RD32(qlt, REG_CTRL_STATUS) & DMA_ACTIVE_STATUS)) { break; } drv_usecwait(100); } /* Reset the chip. */ REG_WR32(qlt, REG_CTRL_STATUS, CHIP_SOFT_RESET); drv_usecwait(200); /* Wait for RISC to recover from reset. */ for (timer = 30000; timer; timer--) { rom_status = REG_RD16(qlt, REG_MBOX0); if ((rom_status & MBS_ROM_STATUS_MASK) != MBS_ROM_BUSY) { break; } drv_usecwait(100); } /* Wait for reset to finish. */ for (timer = 30000; timer; timer--) { if (!(REG_RD32(qlt, REG_CTRL_STATUS) & CHIP_SOFT_RESET)) { break; } drv_usecwait(100); } /* XXX: Disable Interrupts (Probably not needed) */ REG_WR32(qlt, REG_INTR_CTRL, 0); qlt->qlt_intr_enabled = 0; } static void qlt_27xx_dt_dint(qlt_state_t *qlt, qlt_dt_dint_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } PCICFG_WR32(qlt, entry->pci_offset, entry->data); } /*ARGSUSED*/ static int qlt_27xx_dt_ghbd(qlt_state_t *qlt, qlt_dt_ghbd_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } /*ARGSUSED*/ static int qlt_27xx_dt_scra(qlt_state_t *qlt, qlt_dt_scra_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } static int qlt_27xx_dt_rrreg(qlt_state_t *qlt, qlt_dt_rrreg_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t i; uint8_t *bp = dbuff; uint32_t addr = entry->addr; uint32_t cnt = entry->count; esize = cnt * 4; /* addr */ esize += cnt * 4; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } REG_WR32(qlt, REG_IOBUS_BASE_ADDR, 0x40); while (cnt--) { REG_WR32(qlt, 0xc0, addr | 0x80000000); *bp++ = LSB(LSW(addr)); *bp++ = MSB(LSW(addr)); *bp++ = LSB(MSW(addr)); *bp++ = MSB(MSW(addr)); for (i = 0; i < 4; i++) { *bp++ = REG_RD8(qlt, i); } addr += 4; } return (esize); } static void qlt_27xx_dt_wrreg(qlt_state_t *qlt, qlt_dt_wrreg_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (dbuff == NULL) { return; } if (dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=0\n"); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return; } REG_WR32(qlt, REG_IOBUS_BASE_ADDR, 0x40); REG_WR32(qlt, 0xc4, entry->data); REG_WR32(qlt, 0xc0, entry->addr); } static int qlt_27xx_dt_rrram(qlt_state_t *qlt, qlt_dt_rrram_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int rval, esize; esize = entry->count * 4; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } if ((rval = qlt_27xx_dump_ram(qlt, MBC_MPI_RAM, entry->addr, entry->count, dbuff)) != QLT_SUCCESS) { EL(qlt, "dump_ram failed, rval=%xh, addr=%xh, len=%xh, " "esize=0\n", rval, entry->addr, entry->count); return (0); } return (esize); } static int qlt_27xx_dt_rpcic(qlt_state_t *qlt, qlt_dt_rpcic_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint32_t i; uint8_t *bp = dbuff; uint32_t addr = entry->addr; uint32_t cnt = entry->count; esize = cnt * 4; /* addr */ esize += cnt * 4; /* data */ if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } while (cnt--) { *bp++ = LSB(LSW(addr)); *bp++ = MSB(LSW(addr)); *bp++ = LSB(MSW(addr)); *bp++ = MSB(MSW(addr)); for (i = 0; i < 4; i++) { *bp++ = PCICFG_RD8(qlt, addr++); } } return (esize); } /*ARGSUSED*/ static int qlt_27xx_dt_gques(qlt_state_t *qlt, qlt_dt_gques_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { if (entry->queue_type == 1) { EL(qlt, "skipped, no request queue shadowing, esize=0\n"); if (dbuff != NULL) { entry->num_queues = 0; entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } else if (entry->queue_type == 2) { EL(qlt, "skipped, no response queue shadowing, esize=0\n"); if (dbuff != NULL) { entry->num_queues = 0; entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } else if (entry->queue_type == 3) { EL(qlt, "skipped, no ATIO queue, esize=0\n"); if (dbuff != NULL) { entry->num_queues = 0; entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } else { EL(qlt, "skipped, unknown queue_type %d, esize=0\n", entry->queue_type); if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } return (0); } } static int qlt_27xx_dt_wdmp(qlt_state_t *qlt, qlt_dt_wdmp_t *entry, uint8_t *dbuff, uint8_t *dbuff_end) { int esize; uint8_t *bp = dbuff; uint32_t data, cnt = entry->length, *dp = entry->data; esize = cnt; if (dbuff == NULL) { return (esize); } if (esize + dbuff >= dbuff_end) { EL(qlt, "skipped, no buffer space, needed=%xh\n", esize); entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); return (0); } while (cnt--) { data = *dp++; *bp++ = LSB(LSW(data)); *bp++ = MSB(LSW(data)); *bp++ = LSB(MSW(data)); *bp++ = MSB(MSW(data)); } return (esize); } /* * qlt_27xx_dump_ram * Dumps RAM. * Risc interrupts must be disabled when this routine is called. * * Input: * pi: port info pointer. * cmd: MBC_DUMP_RAM_EXTENDED/MBC_MPI_RAM. * risc_address: RISC code start address. * len: Number of words. * bp: buffer pointer. * * Returns: * qlt local function return status code. * * Context: * Interrupt or Kernel context, no mailbox commands allowed. */ /*ARGSUSED*/ static int qlt_27xx_dump_ram(qlt_state_t *qlt, uint16_t cmd, uint32_t risc_address, uint32_t len, uint8_t *bp) { uint8_t *dp; uint32_t words_to_read, endaddr; uint32_t i; int rval = QLT_SUCCESS; endaddr = risc_address + len; words_to_read = 0; while (risc_address < endaddr) { words_to_read = MBOX_DMA_MEM_SIZE >> 2; if ((words_to_read + risc_address) > endaddr) { words_to_read = endaddr - risc_address; } if (cmd == MBC_DUMP_RAM_EXTENDED) { rval = qlt_read_risc_ram(qlt, risc_address, words_to_read); } else { rval = qlt_mbx_mpi_ram(qlt, risc_address, words_to_read, 0); } if (rval != QLT_SUCCESS) { EL(qlt, "Error reading risc ram = %xh len = %x\n", risc_address, words_to_read); return (rval); } dp = (uint8_t *)(qlt->queue_mem_ptr + MBOX_DMA_MEM_OFFSET); for (i = 0; i < (words_to_read * 4); i++) { *bp++ = *dp++; } risc_address += words_to_read; } return (rval); } static uint32_t qlt_27xx_dmp_parse_template(qlt_state_t *qlt, qlt_dt_hdr_t *template_hdr, uint8_t *dump_buff, uint32_t buff_size) { int e_cnt, esize, num_of_entries; uint32_t bsize; time_t time; uint8_t *dbuff, *dbuff_end; qlt_dt_entry_t *entry; int sane_end = 0; dbuff = dump_buff; /* dbuff = NULL size determination. */ dbuff_end = dump_buff + buff_size; if (template_hdr->type != DT_THDR) { EL(qlt, "Template header not found\n"); return (0); } if (dbuff != NULL) { (void) drv_getparm(TIME, &time); template_hdr->driver_timestamp = LSD(time); } num_of_entries = template_hdr->num_of_entries; entry = (qlt_dt_entry_t *)((caddr_t)template_hdr + template_hdr->first_entry_offset); bsize = template_hdr->size_of_template; for (e_cnt = 0; e_cnt < num_of_entries; e_cnt++) { /* * Decode the entry type and process it accordingly */ esize = 0; switch (entry->h.type) { case DT_NOP: if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } break; case DT_TEND: if (dbuff != NULL) { entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); } sane_end++; break; case DT_RIOB1: esize = qlt_27xx_dt_riob1(qlt, (qlt_dt_riob1_t *)entry, dbuff, dbuff_end); break; case DT_WIOB1: qlt_27xx_dt_wiob1(qlt, (qlt_dt_wiob1_t *)entry, dbuff, dbuff_end); break; case DT_RIOB2: esize = qlt_27xx_dt_riob2(qlt, (qlt_dt_riob2_t *)entry, dbuff, dbuff_end); break; case DT_WIOB2: qlt_27xx_dt_wiob2(qlt, (qlt_dt_wiob2_t *)entry, dbuff, dbuff_end); break; case DT_RPCI: esize = qlt_27xx_dt_rpci(qlt, (qlt_dt_rpci_t *)entry, dbuff, dbuff_end); break; case DT_WPCI: qlt_27xx_dt_wpci(qlt, (qlt_dt_wpci_t *)entry, dbuff, dbuff_end); break; case DT_RRAM: esize = qlt_27xx_dt_rram(qlt, (qlt_dt_rram_t *)entry, dbuff, dbuff_end); break; case DT_GQUE: esize = qlt_27xx_dt_gque(qlt, (qlt_dt_gque_t *)entry, dbuff, dbuff_end); break; case DT_GFCE: esize = qlt_27xx_dt_gfce(qlt, (qlt_dt_gfce_t *)entry, dbuff, dbuff_end); break; case DT_PRISC: qlt_27xx_dt_prisc(qlt, (qlt_dt_prisc_t *)entry, dbuff, dbuff_end); break; case DT_RRISC: qlt_27xx_dt_rrisc(qlt, (qlt_dt_rrisc_t *)entry, dbuff, dbuff_end); break; case DT_DINT: qlt_27xx_dt_dint(qlt, (qlt_dt_dint_t *)entry, dbuff, dbuff_end); break; case DT_GHBD: esize = qlt_27xx_dt_ghbd(qlt, (qlt_dt_ghbd_t *)entry, dbuff, dbuff_end); break; case DT_SCRA: esize = qlt_27xx_dt_scra(qlt, (qlt_dt_scra_t *)entry, dbuff, dbuff_end); break; case DT_RRREG: esize = qlt_27xx_dt_rrreg(qlt, (qlt_dt_rrreg_t *)entry, dbuff, dbuff_end); break; case DT_WRREG: qlt_27xx_dt_wrreg(qlt, (qlt_dt_wrreg_t *)entry, dbuff, dbuff_end); break; case DT_RRRAM: esize = qlt_27xx_dt_rrram(qlt, (qlt_dt_rrram_t *)entry, dbuff, dbuff_end); break; case DT_RPCIC: esize = qlt_27xx_dt_rpcic(qlt, (qlt_dt_rpcic_t *)entry, dbuff, dbuff_end); break; case DT_GQUES: esize = qlt_27xx_dt_gques(qlt, (qlt_dt_gques_t *)entry, dbuff, dbuff_end); break; case DT_WDMP: esize = qlt_27xx_dt_wdmp(qlt, (qlt_dt_wdmp_t *)entry, dbuff, dbuff_end); break; default: entry->h.driver_flags = (uint8_t) (entry->h.driver_flags | SKIPPED_FLAG); EL(qlt, "Entry ID=%d, type=%d unknown\n", e_cnt, entry->h.type); break; } if (dbuff != NULL && esize) { dbuff += esize; } bsize += esize; /* next entry in the template */ entry = (qlt_dt_entry_t *)((caddr_t)entry + entry->h.size); } if (sane_end > 1) { EL(qlt, "Template configuration error. Check Template\n"); } return (bsize); }