14c06356bSdh /* 24c06356bSdh * CDDL HEADER START 34c06356bSdh * 44c06356bSdh * The contents of this file are subject to the terms of the 54c06356bSdh * Common Development and Distribution License (the "License"). 64c06356bSdh * You may not use this file except in compliance with the License. 74c06356bSdh * 84c06356bSdh * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 94c06356bSdh * or http://www.opensolaris.org/os/licensing. 104c06356bSdh * See the License for the specific language governing permissions 114c06356bSdh * and limitations under the License. 124c06356bSdh * 134c06356bSdh * When distributing Covered Code, include this CDDL HEADER in each 144c06356bSdh * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 154c06356bSdh * If applicable, add the following below this CDDL HEADER, with the 164c06356bSdh * fields enclosed by brackets "[]" replaced with your own identifying 174c06356bSdh * information: Portions Copyright [yyyy] [name of copyright owner] 184c06356bSdh * 194c06356bSdh * CDDL HEADER END 20658280b6SDavid Hollister */ 21658280b6SDavid Hollister /* 22658280b6SDavid Hollister * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 234c06356bSdh */ 244c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h> 254c06356bSdh 2647b47c8cSdh #define PMCS_DRIVER_VERSION "pmcs HBA device driver" 274c06356bSdh 284c06356bSdh static char *pmcs_driver_rev = PMCS_DRIVER_VERSION; 294c06356bSdh 304c06356bSdh /* 314c06356bSdh * Non-DDI Compliant stuff 324c06356bSdh */ 334c06356bSdh extern char hw_serial[]; 344c06356bSdh 354c06356bSdh /* 364c06356bSdh * Global driver data 374c06356bSdh */ 384c06356bSdh void *pmcs_softc_state = NULL; 394c06356bSdh void *pmcs_iport_softstate = NULL; 404c06356bSdh 414c06356bSdh /* 424c06356bSdh * Tracing and Logging info 434c06356bSdh */ 444c06356bSdh pmcs_tbuf_t *pmcs_tbuf = NULL; 454c06356bSdh uint32_t pmcs_tbuf_num_elems = 0; 464c06356bSdh pmcs_tbuf_t *pmcs_tbuf_ptr; 474c06356bSdh uint32_t pmcs_tbuf_idx = 0; 484c06356bSdh boolean_t pmcs_tbuf_wrap = B_FALSE; 494c06356bSdh static kmutex_t pmcs_trace_lock; 504c06356bSdh 514c06356bSdh /* 524c06356bSdh * If pmcs_force_syslog value is non-zero, all messages put in the trace log 534c06356bSdh * will also be sent to system log. 544c06356bSdh */ 554c06356bSdh int pmcs_force_syslog = 0; 564c06356bSdh int pmcs_console = 0; 574c06356bSdh 584c06356bSdh /* 594c06356bSdh * External References 604c06356bSdh */ 614c06356bSdh extern int ncpus_online; 624c06356bSdh 634c06356bSdh /* 644c06356bSdh * Local static data 654c06356bSdh */ 664c06356bSdh static int fwlog_level = 3; 674c06356bSdh static int physpeed = PHY_LINK_ALL; 684c06356bSdh static int phymode = PHY_LM_AUTO; 694c06356bSdh static int block_mask = 0; 704c06356bSdh static int phymap_usec = 3 * MICROSEC; 714c06356bSdh static int iportmap_usec = 2 * MICROSEC; 724c06356bSdh 734c06356bSdh #ifdef DEBUG 744c06356bSdh static int debug_mask = 1; 754c06356bSdh #else 764c06356bSdh static int debug_mask = 0; 774c06356bSdh #endif 784c06356bSdh 794c06356bSdh #ifdef DISABLE_MSIX 804c06356bSdh static int disable_msix = 1; 814c06356bSdh #else 824c06356bSdh static int disable_msix = 0; 834c06356bSdh #endif 844c06356bSdh 854c06356bSdh #ifdef DISABLE_MSI 864c06356bSdh static int disable_msi = 1; 874c06356bSdh #else 884c06356bSdh static int disable_msi = 0; 894c06356bSdh #endif 904c06356bSdh 914c06356bSdh static uint16_t maxqdepth = 0xfffe; 924c06356bSdh 934c06356bSdh /* 944c06356bSdh * Local prototypes 954c06356bSdh */ 964c06356bSdh static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t); 974c06356bSdh static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t); 984c06356bSdh static int pmcs_unattach(pmcs_hw_t *); 994c06356bSdh static int pmcs_iport_unattach(pmcs_iport_t *); 1004c06356bSdh static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long); 1014c06356bSdh static void pmcs_watchdog(void *); 1024c06356bSdh static int pmcs_setup_intr(pmcs_hw_t *); 1034c06356bSdh static int pmcs_teardown_intr(pmcs_hw_t *); 1044c06356bSdh 1054c06356bSdh static uint_t pmcs_nonio_ix(caddr_t, caddr_t); 1064c06356bSdh static uint_t pmcs_general_ix(caddr_t, caddr_t); 1074c06356bSdh static uint_t pmcs_event_ix(caddr_t, caddr_t); 1084c06356bSdh static uint_t pmcs_iodone_ix(caddr_t, caddr_t); 1094c06356bSdh static uint_t pmcs_fatal_ix(caddr_t, caddr_t); 1104c06356bSdh static uint_t pmcs_all_intr(caddr_t, caddr_t); 1114c06356bSdh static int pmcs_quiesce(dev_info_t *dip); 1124c06356bSdh static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *); 1134c06356bSdh 11435dae232SSrikanth Suravajhala static void pmcs_create_all_phy_stats(pmcs_iport_t *); 1154c06356bSdh int pmcs_update_phy_stats(kstat_t *, int); 1164c06356bSdh static void pmcs_destroy_phy_stats(pmcs_iport_t *); 1174c06356bSdh 1184c06356bSdh static void pmcs_fm_fini(pmcs_hw_t *pwp); 1194c06356bSdh static void pmcs_fm_init(pmcs_hw_t *pwp); 1204c06356bSdh static int pmcs_fm_error_cb(dev_info_t *dip, 1214c06356bSdh ddi_fm_error_t *err, const void *impl_data); 1224c06356bSdh 1234c06356bSdh /* 1244c06356bSdh * Local configuration data 1254c06356bSdh */ 1264c06356bSdh static struct dev_ops pmcs_ops = { 1274c06356bSdh DEVO_REV, /* devo_rev, */ 1284c06356bSdh 0, /* refcnt */ 1294c06356bSdh ddi_no_info, /* info */ 1304c06356bSdh nulldev, /* identify */ 1314c06356bSdh nulldev, /* probe */ 1324c06356bSdh pmcs_attach, /* attach */ 1334c06356bSdh pmcs_detach, /* detach */ 1344c06356bSdh nodev, /* reset */ 1354c06356bSdh NULL, /* driver operations */ 1364c06356bSdh NULL, /* bus operations */ 1374c06356bSdh ddi_power, /* power management */ 1384c06356bSdh pmcs_quiesce /* quiesce */ 1394c06356bSdh }; 1404c06356bSdh 1414c06356bSdh static struct modldrv modldrv = { 1424c06356bSdh &mod_driverops, 1434c06356bSdh PMCS_DRIVER_VERSION, 1444c06356bSdh &pmcs_ops, /* driver ops */ 1454c06356bSdh }; 1464c06356bSdh static struct modlinkage modlinkage = { 1474c06356bSdh MODREV_1, &modldrv, NULL 1484c06356bSdh }; 1494c06356bSdh 1504c06356bSdh const ddi_dma_attr_t pmcs_dattr = { 1514c06356bSdh DMA_ATTR_V0, /* dma_attr version */ 1524c06356bSdh 0x0000000000000000ull, /* dma_attr_addr_lo */ 1534c06356bSdh 0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */ 1544c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_count_max */ 1554c06356bSdh 0x0000000000000001ull, /* dma_attr_align */ 1564c06356bSdh 0x00000078, /* dma_attr_burstsizes */ 1574c06356bSdh 0x00000001, /* dma_attr_minxfer */ 1584c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_maxxfer */ 1594c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_seg */ 1604c06356bSdh 1, /* dma_attr_sgllen */ 1614c06356bSdh 512, /* dma_attr_granular */ 1624c06356bSdh 0 /* dma_attr_flags */ 1634c06356bSdh }; 1644c06356bSdh 1654c06356bSdh static ddi_device_acc_attr_t rattr = { 166837c1ac4SStephen Hanson DDI_DEVICE_ATTR_V1, 1674c06356bSdh DDI_STRUCTURE_LE_ACC, 1684c06356bSdh DDI_STRICTORDER_ACC, 1694c06356bSdh DDI_DEFAULT_ACC 1704c06356bSdh }; 1714c06356bSdh 1724c06356bSdh 1734c06356bSdh /* 1744c06356bSdh * Attach/Detach functions 1754c06356bSdh */ 1764c06356bSdh 1774c06356bSdh int 1784c06356bSdh _init(void) 1794c06356bSdh { 1804c06356bSdh int ret; 1814c06356bSdh 1824c06356bSdh ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1); 1834c06356bSdh if (ret != 0) { 1844c06356bSdh cmn_err(CE_WARN, "?soft state init failed for pmcs"); 1854c06356bSdh return (ret); 1864c06356bSdh } 1874c06356bSdh 1884c06356bSdh if ((ret = scsi_hba_init(&modlinkage)) != 0) { 1894c06356bSdh cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs"); 1904c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 1914c06356bSdh return (ret); 1924c06356bSdh } 1934c06356bSdh 1944c06356bSdh /* 1954c06356bSdh * Allocate soft state for iports 1964c06356bSdh */ 1974c06356bSdh ret = ddi_soft_state_init(&pmcs_iport_softstate, 1984c06356bSdh sizeof (pmcs_iport_t), 2); 1994c06356bSdh if (ret != 0) { 2004c06356bSdh cmn_err(CE_WARN, "?iport soft state init failed for pmcs"); 2014c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2024c06356bSdh return (ret); 2034c06356bSdh } 2044c06356bSdh 2054c06356bSdh ret = mod_install(&modlinkage); 2064c06356bSdh if (ret != 0) { 2074c06356bSdh cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret); 2084c06356bSdh scsi_hba_fini(&modlinkage); 2094c06356bSdh ddi_soft_state_fini(&pmcs_iport_softstate); 2104c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2114c06356bSdh return (ret); 2124c06356bSdh } 2134c06356bSdh 2144c06356bSdh /* Initialize the global trace lock */ 2154c06356bSdh mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL); 2164c06356bSdh 2174c06356bSdh return (0); 2184c06356bSdh } 2194c06356bSdh 2204c06356bSdh int 2214c06356bSdh _fini(void) 2224c06356bSdh { 2234c06356bSdh int ret; 2244c06356bSdh if ((ret = mod_remove(&modlinkage)) != 0) { 2254c06356bSdh return (ret); 2264c06356bSdh } 2274c06356bSdh scsi_hba_fini(&modlinkage); 2284c06356bSdh 2294c06356bSdh /* Free pmcs log buffer and destroy the global lock */ 2304c06356bSdh if (pmcs_tbuf) { 2314c06356bSdh kmem_free(pmcs_tbuf, 2324c06356bSdh pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t)); 2334c06356bSdh pmcs_tbuf = NULL; 2344c06356bSdh } 2354c06356bSdh mutex_destroy(&pmcs_trace_lock); 2364c06356bSdh 2374c06356bSdh ddi_soft_state_fini(&pmcs_iport_softstate); 2384c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2394c06356bSdh return (0); 2404c06356bSdh } 2414c06356bSdh 2424c06356bSdh int 2434c06356bSdh _info(struct modinfo *modinfop) 2444c06356bSdh { 2454c06356bSdh return (mod_info(&modlinkage, modinfop)); 2464c06356bSdh } 2474c06356bSdh 2484c06356bSdh static int 2494c06356bSdh pmcs_iport_attach(dev_info_t *dip) 2504c06356bSdh { 2514c06356bSdh pmcs_iport_t *iport; 2524c06356bSdh pmcs_hw_t *pwp; 2534c06356bSdh scsi_hba_tran_t *tran; 2544c06356bSdh void *ua_priv = NULL; 2554c06356bSdh char *iport_ua; 2564c06356bSdh char *init_port; 2574c06356bSdh int hba_inst; 2584c06356bSdh int inst; 2594c06356bSdh 2604c06356bSdh hba_inst = ddi_get_instance(ddi_get_parent(dip)); 2614c06356bSdh inst = ddi_get_instance(dip); 2624c06356bSdh 2634c06356bSdh pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst); 2644c06356bSdh if (pwp == NULL) { 265a25672a1SDavid Hollister cmn_err(CE_WARN, "%s: No HBA softstate for instance %d", 2664c06356bSdh __func__, inst); 2674c06356bSdh return (DDI_FAILURE); 2684c06356bSdh } 2694c06356bSdh 2704c06356bSdh if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) { 2714c06356bSdh return (DDI_FAILURE); 2724c06356bSdh } 2734c06356bSdh 2744c06356bSdh if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) { 275c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2764c06356bSdh "%s: invoked with NULL unit address, inst (%d)", 2774c06356bSdh __func__, inst); 2784c06356bSdh return (DDI_FAILURE); 2794c06356bSdh } 2804c06356bSdh 2814c06356bSdh if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) { 282c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2834c06356bSdh "Failed to alloc soft state for iport %d", inst); 2844c06356bSdh return (DDI_FAILURE); 2854c06356bSdh } 2864c06356bSdh 2874c06356bSdh iport = ddi_get_soft_state(pmcs_iport_softstate, inst); 2884c06356bSdh if (iport == NULL) { 289c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2904c06356bSdh "cannot get iport soft state"); 2914c06356bSdh goto iport_attach_fail1; 2924c06356bSdh } 2934c06356bSdh 2944c06356bSdh mutex_init(&iport->lock, NULL, MUTEX_DRIVER, 2954c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 2964c06356bSdh cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL); 2976745c559SJesse Butler cv_init(&iport->smp_cv, NULL, CV_DEFAULT, NULL); 2984c06356bSdh mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER, 2994c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 3006745c559SJesse Butler mutex_init(&iport->smp_lock, NULL, MUTEX_DRIVER, 3016745c559SJesse Butler DDI_INTR_PRI(pwp->intr_pri)); 3024c06356bSdh 3034c06356bSdh /* Set some data on the iport handle */ 3044c06356bSdh iport->dip = dip; 3054c06356bSdh iport->pwp = pwp; 3064c06356bSdh 3074c06356bSdh /* Dup the UA into the iport handle */ 3084c06356bSdh iport->ua = strdup(iport_ua); 3094c06356bSdh 3104c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 3114c06356bSdh tran->tran_hba_private = iport; 3124c06356bSdh 3134c06356bSdh list_create(&iport->phys, sizeof (pmcs_phy_t), 3144c06356bSdh offsetof(pmcs_phy_t, list_node)); 3154c06356bSdh 3164c06356bSdh /* 3174c06356bSdh * If our unit address is active in the phymap, configure our 3184c06356bSdh * iport's phylist. 3194c06356bSdh */ 3204c06356bSdh mutex_enter(&iport->lock); 3214c06356bSdh ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua); 3224c06356bSdh if (ua_priv) { 3234c06356bSdh /* Non-NULL private data indicates the unit address is active */ 3244c06356bSdh iport->ua_state = UA_ACTIVE; 3254c06356bSdh if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) { 326c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 327c3bc407cSdh "%s: failed to " 3284c06356bSdh "configure phys on iport handle (0x%p), " 3294c06356bSdh " unit address [%s]", __func__, 3304c06356bSdh (void *)iport, iport_ua); 3314c06356bSdh mutex_exit(&iport->lock); 3324c06356bSdh goto iport_attach_fail2; 3334c06356bSdh } 3344c06356bSdh } else { 3354c06356bSdh iport->ua_state = UA_INACTIVE; 3364c06356bSdh } 3374c06356bSdh mutex_exit(&iport->lock); 3384c06356bSdh 3394c06356bSdh /* Allocate string-based soft state pool for targets */ 3404c06356bSdh iport->tgt_sstate = NULL; 3414c06356bSdh if (ddi_soft_state_bystr_init(&iport->tgt_sstate, 3424c06356bSdh sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) { 343c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 3444c06356bSdh "cannot get iport tgt soft state"); 3454c06356bSdh goto iport_attach_fail2; 3464c06356bSdh } 3474c06356bSdh 3484c06356bSdh /* Create this iport's target map */ 3494c06356bSdh if (pmcs_iport_tgtmap_create(iport) == B_FALSE) { 350c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 3514c06356bSdh "Failed to create tgtmap on iport %d", inst); 3524c06356bSdh goto iport_attach_fail3; 3534c06356bSdh } 3544c06356bSdh 3554c06356bSdh /* Set up the 'initiator-port' DDI property on this iport */ 3564c06356bSdh init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP); 3574c06356bSdh if (pwp->separate_ports) { 358c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 359c3bc407cSdh "%s: separate ports not supported", __func__); 3604c06356bSdh } else { 3614c06356bSdh /* Set initiator-port value to the HBA's base WWN */ 3624c06356bSdh (void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1, 3634c06356bSdh init_port); 3644c06356bSdh } 365145e0143Sdh 366145e0143Sdh mutex_enter(&iport->lock); 3674c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING, 3684c06356bSdh SCSI_ADDR_PROP_INITIATOR_PORT, init_port); 3694c06356bSdh kmem_free(init_port, PMCS_MAX_UA_SIZE); 3704c06356bSdh 3714c06356bSdh /* Set up a 'num-phys' DDI property for the iport node */ 3724c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 3734c06356bSdh &iport->nphy); 374145e0143Sdh mutex_exit(&iport->lock); 3754c06356bSdh 3764c06356bSdh /* Create kstats for each of the phys in this port */ 37735dae232SSrikanth Suravajhala pmcs_create_all_phy_stats(iport); 3784c06356bSdh 3794c06356bSdh /* 3804c06356bSdh * Insert this iport handle into our list and set 3814c06356bSdh * iports_attached on the HBA node. 3824c06356bSdh */ 3834c06356bSdh rw_enter(&pwp->iports_lock, RW_WRITER); 3844c06356bSdh ASSERT(!list_link_active(&iport->list_node)); 3854c06356bSdh list_insert_tail(&pwp->iports, iport); 3864c06356bSdh pwp->iports_attached = 1; 3874c06356bSdh pwp->num_iports++; 3884c06356bSdh rw_exit(&pwp->iports_lock); 3894c06356bSdh 390c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 391c3bc407cSdh "iport%d attached", inst); 3924c06356bSdh ddi_report_dev(dip); 3934c06356bSdh return (DDI_SUCCESS); 3944c06356bSdh 3954c06356bSdh /* teardown and fail */ 3964c06356bSdh iport_attach_fail3: 3974c06356bSdh ddi_soft_state_bystr_fini(&iport->tgt_sstate); 3984c06356bSdh iport_attach_fail2: 3994c06356bSdh list_destroy(&iport->phys); 4004c06356bSdh strfree(iport->ua); 4014c06356bSdh mutex_destroy(&iport->refcnt_lock); 4026745c559SJesse Butler mutex_destroy(&iport->smp_lock); 4034c06356bSdh cv_destroy(&iport->refcnt_cv); 4046745c559SJesse Butler cv_destroy(&iport->smp_cv); 4054c06356bSdh mutex_destroy(&iport->lock); 4064c06356bSdh iport_attach_fail1: 4074c06356bSdh ddi_soft_state_free(pmcs_iport_softstate, inst); 4084c06356bSdh return (DDI_FAILURE); 4094c06356bSdh } 4104c06356bSdh 4114c06356bSdh static int 4124c06356bSdh pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4134c06356bSdh { 4144c06356bSdh scsi_hba_tran_t *tran; 4154c06356bSdh char chiprev, *fwsupport, hw_rev[24], fw_rev[24]; 4164c06356bSdh off_t set3size; 4174c06356bSdh int inst, i; 4184c06356bSdh int sm_hba = 1; 4194c06356bSdh int protocol = 0; 4204c06356bSdh int num_phys = 0; 4214c06356bSdh pmcs_hw_t *pwp; 4224c06356bSdh pmcs_phy_t *phyp; 4234c06356bSdh uint32_t num_threads; 4244c06356bSdh char buf[64]; 4259719310aSDavid Hollister char *fwl_file; 4264c06356bSdh 4274c06356bSdh switch (cmd) { 4284c06356bSdh case DDI_ATTACH: 4294c06356bSdh break; 4304c06356bSdh 4314c06356bSdh case DDI_PM_RESUME: 4324c06356bSdh case DDI_RESUME: 4334c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 4344c06356bSdh if (!tran) { 4354c06356bSdh return (DDI_FAILURE); 4364c06356bSdh } 4374c06356bSdh /* No DDI_?_RESUME on iport nodes */ 4384c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 4394c06356bSdh return (DDI_SUCCESS); 4404c06356bSdh } 4414c06356bSdh pwp = TRAN2PMC(tran); 4424c06356bSdh if (pwp == NULL) { 4434c06356bSdh return (DDI_FAILURE); 4444c06356bSdh } 4454c06356bSdh 4464c06356bSdh mutex_enter(&pwp->lock); 4474c06356bSdh pwp->suspended = 0; 4484c06356bSdh if (pwp->tq) { 4494c06356bSdh ddi_taskq_resume(pwp->tq); 4504c06356bSdh } 4514c06356bSdh mutex_exit(&pwp->lock); 4524c06356bSdh return (DDI_SUCCESS); 4534c06356bSdh 4544c06356bSdh default: 4554c06356bSdh return (DDI_FAILURE); 4564c06356bSdh } 4574c06356bSdh 4584c06356bSdh /* 4594c06356bSdh * If this is an iport node, invoke iport attach. 4604c06356bSdh */ 4614c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 4624c06356bSdh return (pmcs_iport_attach(dip)); 4634c06356bSdh } 4644c06356bSdh 4654c06356bSdh /* 4664c06356bSdh * From here on is attach for the HBA node 4674c06356bSdh */ 4684c06356bSdh 4694c06356bSdh #ifdef DEBUG 4704c06356bSdh /* 4714c06356bSdh * Check to see if this unit is to be disabled. We can't disable 4724c06356bSdh * on a per-iport node. It's either the entire HBA or nothing. 4734c06356bSdh */ 4744c06356bSdh (void) snprintf(buf, sizeof (buf), 4754c06356bSdh "disable-instance-%d", ddi_get_instance(dip)); 4764c06356bSdh if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, 4774c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) { 4784c06356bSdh cmn_err(CE_NOTE, "pmcs%d: disabled by configuration", 4794c06356bSdh ddi_get_instance(dip)); 4804c06356bSdh return (DDI_FAILURE); 4814c06356bSdh } 4824c06356bSdh #endif 4834c06356bSdh 4844c06356bSdh /* 4854c06356bSdh * Allocate softstate 4864c06356bSdh */ 4874c06356bSdh inst = ddi_get_instance(dip); 4884c06356bSdh if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) { 4894c06356bSdh cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst); 4904c06356bSdh return (DDI_FAILURE); 4914c06356bSdh } 4924c06356bSdh 4934c06356bSdh pwp = ddi_get_soft_state(pmcs_softc_state, inst); 4944c06356bSdh if (pwp == NULL) { 4954c06356bSdh cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst); 4964c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 4974c06356bSdh return (DDI_FAILURE); 4984c06356bSdh } 4994c06356bSdh pwp->dip = dip; 5004c06356bSdh STAILQ_INIT(&pwp->dq); 5014c06356bSdh STAILQ_INIT(&pwp->cq); 5024c06356bSdh STAILQ_INIT(&pwp->wf); 5034c06356bSdh STAILQ_INIT(&pwp->pf); 504*32b54db7SJesse Butler 5054c06356bSdh /* 506*32b54db7SJesse Butler * Create the list for iports and init its lock. 5074c06356bSdh */ 5084c06356bSdh list_create(&pwp->iports, sizeof (pmcs_iport_t), 5094c06356bSdh offsetof(pmcs_iport_t, list_node)); 510*32b54db7SJesse Butler rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL); 5114c06356bSdh 5124c06356bSdh pwp->state = STATE_PROBING; 5134c06356bSdh 5144c06356bSdh /* 5154c06356bSdh * Get driver.conf properties 5164c06356bSdh */ 5174c06356bSdh pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5184c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask", 5194c06356bSdh debug_mask); 5204c06356bSdh pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5214c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask", 5224c06356bSdh block_mask); 5234c06356bSdh pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5244c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed); 5254c06356bSdh pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5264c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode); 5274c06356bSdh pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5284c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level); 5294c06356bSdh if (pwp->fwlog > PMCS_FWLOG_MAX) { 5304c06356bSdh pwp->fwlog = PMCS_FWLOG_MAX; 5314c06356bSdh } 5329719310aSDavid Hollister if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "pmcs-fwlogfile", 5339719310aSDavid Hollister &fwl_file) == DDI_SUCCESS)) { 5349719310aSDavid Hollister if (snprintf(pwp->fwlogfile_aap1, MAXPATHLEN, "%s%d-aap1.0", 5359719310aSDavid Hollister fwl_file, ddi_get_instance(dip)) > MAXPATHLEN) { 5369719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5379719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5389719310aSDavid Hollister } else if (snprintf(pwp->fwlogfile_iop, MAXPATHLEN, 5399719310aSDavid Hollister "%s%d-iop.0", fwl_file, 5409719310aSDavid Hollister ddi_get_instance(dip)) > MAXPATHLEN) { 5419719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5429719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5439719310aSDavid Hollister } 5449719310aSDavid Hollister ddi_prop_free(fwl_file); 5459719310aSDavid Hollister } else { 5469719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5479719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5489719310aSDavid Hollister } 5494c06356bSdh 550658280b6SDavid Hollister pwp->open_retry_interval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 551658280b6SDavid Hollister DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-open-retry-interval", 552658280b6SDavid Hollister OPEN_RETRY_INTERVAL_DEF); 553658280b6SDavid Hollister if (pwp->open_retry_interval > OPEN_RETRY_INTERVAL_MAX) { 554658280b6SDavid Hollister pwp->open_retry_interval = OPEN_RETRY_INTERVAL_MAX; 555658280b6SDavid Hollister } 556658280b6SDavid Hollister 5574c06356bSdh mutex_enter(&pmcs_trace_lock); 5584c06356bSdh if (pmcs_tbuf == NULL) { 5594c06356bSdh /* Allocate trace buffer */ 5604c06356bSdh pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5614c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems", 5624c06356bSdh PMCS_TBUF_NUM_ELEMS_DEF); 5634c06356bSdh if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) || 5644c06356bSdh (pmcs_tbuf_num_elems == 0)) { 5654c06356bSdh pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF; 5664c06356bSdh } 5674c06356bSdh 5684c06356bSdh pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems * 5694c06356bSdh sizeof (pmcs_tbuf_t), KM_SLEEP); 5704c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 5714c06356bSdh pmcs_tbuf_idx = 0; 5724c06356bSdh } 5734c06356bSdh mutex_exit(&pmcs_trace_lock); 5744c06356bSdh 5759719310aSDavid Hollister if (pwp->fwlog && strlen(pwp->fwlogfile_aap1) > 0) { 5769719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5779719310aSDavid Hollister "%s: firmware event log files: %s, %s", __func__, 5789719310aSDavid Hollister pwp->fwlogfile_aap1, pwp->fwlogfile_iop); 5799719310aSDavid Hollister pwp->fwlog_file = 1; 5809719310aSDavid Hollister } else { 5819719310aSDavid Hollister if (pwp->fwlog == 0) { 5829719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5839719310aSDavid Hollister "%s: No firmware event log will be written " 5849719310aSDavid Hollister "(event log disabled)", __func__); 5859719310aSDavid Hollister } else { 5869719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5879719310aSDavid Hollister "%s: No firmware event log will be written " 5889719310aSDavid Hollister "(no filename configured - too long?)", __func__); 5899719310aSDavid Hollister } 5909719310aSDavid Hollister pwp->fwlog_file = 0; 5919719310aSDavid Hollister } 5929719310aSDavid Hollister 5934c06356bSdh disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5944c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix", 5954c06356bSdh disable_msix); 5964c06356bSdh disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5974c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi", 5984c06356bSdh disable_msi); 5994c06356bSdh maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6004c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth); 6014c06356bSdh pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6024c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0); 6034c06356bSdh if (pwp->fw_force_update == 0) { 6044c06356bSdh pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6054c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 6064c06356bSdh "pmcs-fw-disable-update", 0); 6074c06356bSdh } 6084c06356bSdh pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6094c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries", 6104c06356bSdh PMCS_NQENTRY); 6114c06356bSdh 6124c06356bSdh /* 6134c06356bSdh * Initialize FMA 6144c06356bSdh */ 6154c06356bSdh pwp->dev_acc_attr = pwp->reg_acc_attr = rattr; 6164c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = 6174c06356bSdh pwp->regdump_dma_attr = pwp->cip_dma_attr = 6184c06356bSdh pwp->fwlog_dma_attr = pmcs_dattr; 6194c06356bSdh pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip, 6204c06356bSdh DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable", 6214c06356bSdh DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | 6224c06356bSdh DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE); 6234c06356bSdh pmcs_fm_init(pwp); 6244c06356bSdh 6254c06356bSdh /* 6264c06356bSdh * Map registers 6274c06356bSdh */ 6284c06356bSdh if (pci_config_setup(dip, &pwp->pci_acc_handle)) { 629c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 630c3bc407cSdh "pci config setup failed"); 6314c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6324c06356bSdh return (DDI_FAILURE); 6334c06356bSdh } 6344c06356bSdh 6354c06356bSdh /* 6364c06356bSdh * Get the size of register set 3. 6374c06356bSdh */ 6384c06356bSdh if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) { 639c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6404c06356bSdh "unable to get size of register set %d", PMCS_REGSET_3); 6414c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6424c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6434c06356bSdh return (DDI_FAILURE); 6444c06356bSdh } 6454c06356bSdh 6464c06356bSdh /* 6474c06356bSdh * Map registers 6484c06356bSdh */ 6494c06356bSdh pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 6504c06356bSdh 6514c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs, 6524c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) { 653c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6544c06356bSdh "failed to map Message Unit registers"); 6554c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6564c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6574c06356bSdh return (DDI_FAILURE); 6584c06356bSdh } 6594c06356bSdh 6604c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs, 6614c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) { 662c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 663c3bc407cSdh "failed to map TOP registers"); 6644c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6654c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6664c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6674c06356bSdh return (DDI_FAILURE); 6684c06356bSdh } 6694c06356bSdh 6704c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs, 6714c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) { 672c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 673c3bc407cSdh "failed to map GSM registers"); 6744c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6754c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6764c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6774c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6784c06356bSdh return (DDI_FAILURE); 6794c06356bSdh } 6804c06356bSdh 6814c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs, 6824c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) { 683c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 684c3bc407cSdh "failed to map MPI registers"); 6854c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6864c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 6874c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6884c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6894c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6904c06356bSdh return (DDI_FAILURE); 6914c06356bSdh } 6924c06356bSdh pwp->mpibar = 6934c06356bSdh (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size; 6944c06356bSdh 6954c06356bSdh /* 6964c06356bSdh * Make sure we can support this card. 6974c06356bSdh */ 6984c06356bSdh pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION); 6994c06356bSdh 7004c06356bSdh switch (pwp->chiprev) { 7014c06356bSdh case PMCS_PM8001_REV_A: 7024c06356bSdh case PMCS_PM8001_REV_B: 703c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 7044c06356bSdh "Rev A/B Card no longer supported"); 7054c06356bSdh goto failure; 7064c06356bSdh case PMCS_PM8001_REV_C: 7074c06356bSdh break; 7084c06356bSdh default: 709c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 7104c06356bSdh "Unknown chip revision (%d)", pwp->chiprev); 7114c06356bSdh goto failure; 7124c06356bSdh } 7134c06356bSdh 7144c06356bSdh /* 7154c06356bSdh * Allocate DMA addressable area for Inbound and Outbound Queue indices 7164c06356bSdh * that the chip needs to access plus a space for scratch usage 7174c06356bSdh */ 7184c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 7194c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls, 7204c06356bSdh &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip, 7214c06356bSdh &pwp->ciaddr) == B_FALSE) { 722c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7234c06356bSdh "Failed to setup DMA for index/scratch"); 7244c06356bSdh goto failure; 7254c06356bSdh } 7264c06356bSdh 7274c06356bSdh bzero(pwp->cip, ptob(1)); 7284c06356bSdh pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE]; 7294c06356bSdh pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE; 7304c06356bSdh 7314c06356bSdh /* 7324c06356bSdh * Allocate DMA S/G list chunks 7334c06356bSdh */ 7344c06356bSdh (void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES); 7354c06356bSdh 7364c06356bSdh /* 7374c06356bSdh * Allocate a DMA addressable area for the firmware log (if needed) 7384c06356bSdh */ 7394c06356bSdh if (pwp->fwlog) { 7404c06356bSdh /* 7414c06356bSdh * Align to event log header and entry size 7424c06356bSdh */ 7434c06356bSdh pwp->fwlog_dma_attr.dma_attr_align = 32; 7444c06356bSdh if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr, 7454c06356bSdh &pwp->fwlog_acchdl, 7464c06356bSdh &pwp->fwlog_hndl, PMCS_FWLOG_SIZE, 7474c06356bSdh (caddr_t *)&pwp->fwlogp, 7484c06356bSdh &pwp->fwaddr) == B_FALSE) { 749c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7504c06356bSdh "Failed to setup DMA for fwlog area"); 7514c06356bSdh pwp->fwlog = 0; 7524c06356bSdh } else { 7534c06356bSdh bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 7549719310aSDavid Hollister pwp->fwlogp_aap1 = (pmcs_fw_event_hdr_t *)pwp->fwlogp; 7559719310aSDavid Hollister pwp->fwlogp_iop = (pmcs_fw_event_hdr_t *)((void *) 7569719310aSDavid Hollister ((caddr_t)pwp->fwlogp + (PMCS_FWLOG_SIZE / 2))); 7574c06356bSdh } 7584c06356bSdh } 7594c06356bSdh 7604c06356bSdh if (pwp->flash_chunk_addr == NULL) { 7614c06356bSdh pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE; 7624c06356bSdh if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr, 7634c06356bSdh &pwp->regdump_acchdl, 7644c06356bSdh &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE, 7654c06356bSdh (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) == 7664c06356bSdh B_FALSE) { 767c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7684c06356bSdh "Failed to setup DMA for register dump area"); 7694c06356bSdh goto failure; 7704c06356bSdh } 7714c06356bSdh bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE); 7724c06356bSdh } 7734c06356bSdh 7744c06356bSdh /* 7754c06356bSdh * More bits of local initialization... 7764c06356bSdh */ 7774c06356bSdh pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0); 7784c06356bSdh if (pwp->tq == NULL) { 779c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 780c3bc407cSdh "unable to create worker taskq"); 7814c06356bSdh goto failure; 7824c06356bSdh } 7834c06356bSdh 7844c06356bSdh /* 7854c06356bSdh * Cache of structures for dealing with I/O completion callbacks. 7864c06356bSdh */ 7874c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst); 7884c06356bSdh pwp->iocomp_cb_cache = kmem_cache_create(buf, 7894c06356bSdh sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0); 7904c06356bSdh 7914c06356bSdh /* 7924c06356bSdh * Cache of PHY structures 7934c06356bSdh */ 7944c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst); 7954c06356bSdh pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8, 7964c06356bSdh pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp, 7974c06356bSdh NULL, 0); 7984c06356bSdh 7994c06356bSdh /* 8004c06356bSdh * Allocate space for the I/O completion threads 8014c06356bSdh */ 8024c06356bSdh num_threads = ncpus_online; 8034c06356bSdh if (num_threads > PMCS_MAX_CQ_THREADS) { 8044c06356bSdh num_threads = PMCS_MAX_CQ_THREADS; 8054c06356bSdh } 8064c06356bSdh 8074c06356bSdh pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) * 8084c06356bSdh num_threads, KM_SLEEP); 8094c06356bSdh pwp->cq_info.cq_threads = num_threads; 8104c06356bSdh pwp->cq_info.cq_next_disp_thr = 0; 8114c06356bSdh pwp->cq_info.cq_stop = B_FALSE; 8124c06356bSdh 8134c06356bSdh /* 8144c06356bSdh * Set the quantum value in clock ticks for the I/O interrupt 8154c06356bSdh * coalescing timer. 8164c06356bSdh */ 8174c06356bSdh pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS); 8184c06356bSdh 8194c06356bSdh /* 8204c06356bSdh * We have a delicate dance here. We need to set up 8214c06356bSdh * interrupts so we know how to set up some OQC 8224c06356bSdh * tables. However, while we're setting up table 8234c06356bSdh * access, we may need to flash new firmware and 8244c06356bSdh * reset the card, which will take some finessing. 8254c06356bSdh */ 8264c06356bSdh 8274c06356bSdh /* 8284c06356bSdh * Set up interrupts here. 8294c06356bSdh */ 8304c06356bSdh switch (pmcs_setup_intr(pwp)) { 8314c06356bSdh case 0: 8324c06356bSdh break; 8334c06356bSdh case EIO: 8344c06356bSdh pwp->stuck = 1; 8354c06356bSdh /* FALLTHROUGH */ 8364c06356bSdh default: 8374c06356bSdh goto failure; 8384c06356bSdh } 8394c06356bSdh 8404c06356bSdh /* 8414c06356bSdh * Set these up now becuase they are used to initialize the OQC tables. 8424c06356bSdh * 8434c06356bSdh * If we have MSI or MSI-X interrupts set up and we have enough 8444c06356bSdh * vectors for each OQ, the Outbound Queue vectors can all be the 8454c06356bSdh * same as the appropriate interrupt routine will have been called 8464c06356bSdh * and the doorbell register automatically cleared. 8474c06356bSdh * This keeps us from having to check the Outbound Doorbell register 8484c06356bSdh * when the routines for these interrupts are called. 8494c06356bSdh * 8504c06356bSdh * If we have Legacy INT-X interrupts set up or we didn't have enough 8514c06356bSdh * MSI/MSI-X vectors to uniquely identify each OQ, we point these 8524c06356bSdh * vectors to the bits we would like to have set in the Outbound 8534c06356bSdh * Doorbell register because pmcs_all_intr will read the doorbell 8544c06356bSdh * register to find out why we have an interrupt and write the 8554c06356bSdh * corresponding 'clear' bit for that interrupt. 8564c06356bSdh */ 8574c06356bSdh 8584c06356bSdh switch (pwp->intr_cnt) { 8594c06356bSdh case 1: 8604c06356bSdh /* 8614c06356bSdh * Only one vector, so we must check all OQs for MSI. For 8624c06356bSdh * INT-X, there's only one vector anyway, so we can just 8634c06356bSdh * use the outbound queue bits to keep from having to 8644c06356bSdh * check each queue for each interrupt. 8654c06356bSdh */ 8664c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 8674c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8684c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8694c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8704c06356bSdh } else { 8714c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8724c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE; 8734c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE; 8744c06356bSdh } 8754c06356bSdh break; 8764c06356bSdh case 2: 8774c06356bSdh /* With 2, we can at least isolate IODONE */ 8784c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8794c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8804c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL; 8814c06356bSdh break; 8824c06356bSdh case 4: 8834c06356bSdh /* With 4 vectors, everybody gets one */ 8844c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8854c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8864c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8874c06356bSdh break; 8884c06356bSdh } 8894c06356bSdh 8904c06356bSdh /* 8914c06356bSdh * Do the first part of setup 8924c06356bSdh */ 8934c06356bSdh if (pmcs_setup(pwp)) { 8944c06356bSdh goto failure; 8954c06356bSdh } 8964c06356bSdh pmcs_report_fwversion(pwp); 8974c06356bSdh 8984c06356bSdh /* 8994c06356bSdh * Now do some additonal allocations based upon information 9004c06356bSdh * gathered during MPI setup. 9014c06356bSdh */ 9024c06356bSdh pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP); 9034c06356bSdh ASSERT(pwp->nphy < SAS2_PHYNUM_MAX); 9044c06356bSdh phyp = pwp->root_phys; 9054c06356bSdh for (i = 0; i < pwp->nphy; i++) { 9064c06356bSdh if (i < pwp->nphy-1) { 9074c06356bSdh phyp->sibling = (phyp + 1); 9084c06356bSdh } 9094c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 9104c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 9114c06356bSdh phyp->phynum = i & SAS2_PHYNUM_MASK; 9124c06356bSdh pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path)); 9134c06356bSdh phyp->pwp = pwp; 9144c06356bSdh phyp->device_id = PMCS_INVALID_DEVICE_ID; 915601c90f1SSrikanth, Ramana phyp->portid = PMCS_PHY_INVALID_PORT_ID; 9164c06356bSdh phyp++; 9174c06356bSdh } 9184c06356bSdh 9194c06356bSdh pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP); 9204c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 9214c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 9224c06356bSdh mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER, 9234c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 9244c06356bSdh cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL); 9254c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next); 9264c06356bSdh 9274c06356bSdh } 9284c06356bSdh pwp->targets = (pmcs_xscsi_t **) 9294c06356bSdh kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP); 9304c06356bSdh 9314c06356bSdh pwp->iqpt = (pmcs_iqp_trace_t *) 9324c06356bSdh kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP); 9334c06356bSdh pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP); 9344c06356bSdh pwp->iqpt->curpos = pwp->iqpt->head; 9354c06356bSdh pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE; 9364c06356bSdh 9374c06356bSdh /* 9384c06356bSdh * Start MPI communication. 9394c06356bSdh */ 9404c06356bSdh if (pmcs_start_mpi(pwp)) { 9414c06356bSdh if (pmcs_soft_reset(pwp, B_FALSE)) { 9424c06356bSdh goto failure; 9434c06356bSdh } 9445c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_ATTACH; 9454c06356bSdh } 9464c06356bSdh 9474c06356bSdh /* 9484c06356bSdh * Do some initial acceptance tests. 9494c06356bSdh * This tests interrupts and queues. 9504c06356bSdh */ 9514c06356bSdh if (pmcs_echo_test(pwp)) { 9524c06356bSdh goto failure; 9534c06356bSdh } 9544c06356bSdh 9554c06356bSdh /* Read VPD - if it exists */ 9564c06356bSdh if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) { 957c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 958c3bc407cSdh "%s: Unable to read VPD: " 9594c06356bSdh "attempting to fabricate", __func__); 9604c06356bSdh /* 9614c06356bSdh * When we release, this must goto failure and the call 9624c06356bSdh * to pmcs_fabricate_wwid is removed. 9634c06356bSdh */ 9644c06356bSdh /* goto failure; */ 9654c06356bSdh if (!pmcs_fabricate_wwid(pwp)) { 9664c06356bSdh goto failure; 9674c06356bSdh } 9684c06356bSdh } 9694c06356bSdh 9704c06356bSdh /* 9714c06356bSdh * We're now officially running 9724c06356bSdh */ 9734c06356bSdh pwp->state = STATE_RUNNING; 9744c06356bSdh 9754c06356bSdh /* 9764c06356bSdh * Check firmware versions and load new firmware 9774c06356bSdh * if needed and reset. 9784c06356bSdh */ 9794c06356bSdh if (pmcs_firmware_update(pwp)) { 980c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 981c3bc407cSdh "%s: Firmware update failed", __func__); 9824c06356bSdh goto failure; 9834c06356bSdh } 9844c06356bSdh 9854c06356bSdh /* 9864c06356bSdh * Create completion threads. 9874c06356bSdh */ 9884c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 9894c06356bSdh pwp->cq_info.cq_thr_info[i].cq_pwp = pwp; 9904c06356bSdh pwp->cq_info.cq_thr_info[i].cq_thread = 9914c06356bSdh thread_create(NULL, 0, pmcs_scsa_cq_run, 9924c06356bSdh &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri); 9934c06356bSdh } 9944c06356bSdh 9954c06356bSdh /* 9964c06356bSdh * Create one thread to deal with the updating of the interrupt 9974c06356bSdh * coalescing timer. 9984c06356bSdh */ 9994c06356bSdh pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal, 10004c06356bSdh pwp, 0, &p0, TS_RUN, minclsyspri); 10014c06356bSdh 10024c06356bSdh /* 10034c06356bSdh * Kick off the watchdog 10044c06356bSdh */ 10054c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 10064c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 10074c06356bSdh /* 10084c06356bSdh * Do the SCSI attachment code (before starting phys) 10094c06356bSdh */ 10104c06356bSdh if (pmcs_scsa_init(pwp, &pmcs_dattr)) { 10114c06356bSdh goto failure; 10124c06356bSdh } 10134c06356bSdh pwp->hba_attached = 1; 10144c06356bSdh 10154c06356bSdh /* Check all acc & dma handles allocated in attach */ 10164c06356bSdh if (pmcs_check_acc_dma_handle(pwp)) { 10174c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 10184c06356bSdh goto failure; 10194c06356bSdh } 10204c06356bSdh 10214c06356bSdh /* 10224c06356bSdh * Create the phymap for this HBA instance 10234c06356bSdh */ 10244c06356bSdh if (sas_phymap_create(dip, phymap_usec, PHYMAP_MODE_SIMPLE, NULL, 10254c06356bSdh pwp, pmcs_phymap_activate, pmcs_phymap_deactivate, 10264c06356bSdh &pwp->hss_phymap) != DDI_SUCCESS) { 1027c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1028c3bc407cSdh "%s: pmcs%d phymap_create failed", __func__, inst); 10294c06356bSdh goto failure; 10304c06356bSdh } 10314c06356bSdh ASSERT(pwp->hss_phymap); 10324c06356bSdh 10334c06356bSdh /* 10344c06356bSdh * Create the iportmap for this HBA instance 10354c06356bSdh */ 10361b115575SJohn Danielson if (scsi_hba_iportmap_create(dip, iportmap_usec, 10374c06356bSdh &pwp->hss_iportmap) != DDI_SUCCESS) { 1038c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1039c3bc407cSdh "%s: pmcs%d iportmap_create failed", __func__, inst); 10404c06356bSdh goto failure; 10414c06356bSdh } 10424c06356bSdh ASSERT(pwp->hss_iportmap); 10434c06356bSdh 10444c06356bSdh /* 10454c06356bSdh * Start the PHYs. 10464c06356bSdh */ 10474c06356bSdh if (pmcs_start_phys(pwp)) { 10484c06356bSdh goto failure; 10494c06356bSdh } 10504c06356bSdh 10514c06356bSdh /* 10524c06356bSdh * From this point on, we can't fail. 10534c06356bSdh */ 10544c06356bSdh ddi_report_dev(dip); 10554c06356bSdh 10564c06356bSdh /* SM-HBA */ 10574c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED, 10584c06356bSdh &sm_hba); 10594c06356bSdh 10604c06356bSdh /* SM-HBA */ 10614c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION, 10624c06356bSdh pmcs_driver_rev); 10634c06356bSdh 10644c06356bSdh /* SM-HBA */ 10654c06356bSdh chiprev = 'A' + pwp->chiprev; 10664c06356bSdh (void) snprintf(hw_rev, 2, "%s", &chiprev); 10674c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION, 10684c06356bSdh hw_rev); 10694c06356bSdh 10704c06356bSdh /* SM-HBA */ 10714c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 10724c06356bSdh case PMCS_FW_TYPE_RELEASED: 10734c06356bSdh fwsupport = "Released"; 10744c06356bSdh break; 10754c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 10764c06356bSdh fwsupport = "Development"; 10774c06356bSdh break; 10784c06356bSdh case PMCS_FW_TYPE_ALPHA: 10794c06356bSdh fwsupport = "Alpha"; 10804c06356bSdh break; 10814c06356bSdh case PMCS_FW_TYPE_BETA: 10824c06356bSdh fwsupport = "Beta"; 10834c06356bSdh break; 10844c06356bSdh default: 10854c06356bSdh fwsupport = "Special"; 10864c06356bSdh break; 10874c06356bSdh } 10884c06356bSdh (void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s", 10894c06356bSdh PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp), 10904c06356bSdh fwsupport); 10914c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION, 10924c06356bSdh fw_rev); 10934c06356bSdh 10944c06356bSdh /* SM-HBA */ 10954c06356bSdh num_phys = pwp->nphy; 10964c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA, 10974c06356bSdh &num_phys); 10984c06356bSdh 10994c06356bSdh /* SM-HBA */ 11004c06356bSdh protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT; 11014c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL, 11024c06356bSdh &protocol); 11034c06356bSdh 1104658280b6SDavid Hollister /* Receptacle properties (FMA) */ 1105658280b6SDavid Hollister pwp->recept_labels[0] = PMCS_RECEPT_LABEL_0; 1106658280b6SDavid Hollister pwp->recept_pm[0] = PMCS_RECEPT_PM_0; 1107658280b6SDavid Hollister pwp->recept_labels[1] = PMCS_RECEPT_LABEL_1; 1108658280b6SDavid Hollister pwp->recept_pm[1] = PMCS_RECEPT_PM_1; 1109658280b6SDavid Hollister if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 1110658280b6SDavid Hollister SCSI_HBA_PROP_RECEPTACLE_LABEL, &pwp->recept_labels[0], 1111658280b6SDavid Hollister PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) { 1112658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1113658280b6SDavid Hollister "%s: failed to create %s property", __func__, 1114658280b6SDavid Hollister "receptacle-label"); 1115658280b6SDavid Hollister } 1116658280b6SDavid Hollister if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, 1117658280b6SDavid Hollister SCSI_HBA_PROP_RECEPTACLE_PM, &pwp->recept_pm[0], 1118658280b6SDavid Hollister PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) { 1119658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1120658280b6SDavid Hollister "%s: failed to create %s property", __func__, 1121658280b6SDavid Hollister "receptacle-pm"); 1122658280b6SDavid Hollister } 1123658280b6SDavid Hollister 11244c06356bSdh return (DDI_SUCCESS); 11254c06356bSdh 11264c06356bSdh failure: 11274c06356bSdh if (pmcs_unattach(pwp)) { 11284c06356bSdh pwp->stuck = 1; 11294c06356bSdh } 11304c06356bSdh return (DDI_FAILURE); 11314c06356bSdh } 11324c06356bSdh 11334c06356bSdh int 11344c06356bSdh pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 11354c06356bSdh { 11364c06356bSdh int inst = ddi_get_instance(dip); 11374c06356bSdh pmcs_iport_t *iport = NULL; 11384c06356bSdh pmcs_hw_t *pwp = NULL; 11394c06356bSdh scsi_hba_tran_t *tran; 11404c06356bSdh 11414c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 11424c06356bSdh /* iport node */ 11434c06356bSdh iport = ddi_get_soft_state(pmcs_iport_softstate, inst); 11444c06356bSdh ASSERT(iport); 11454c06356bSdh if (iport == NULL) { 11464c06356bSdh return (DDI_FAILURE); 11474c06356bSdh } 11484c06356bSdh pwp = iport->pwp; 11494c06356bSdh } else { 11504c06356bSdh /* hba node */ 11514c06356bSdh pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst); 11524c06356bSdh ASSERT(pwp); 11534c06356bSdh if (pwp == NULL) { 11544c06356bSdh return (DDI_FAILURE); 11554c06356bSdh } 11564c06356bSdh } 11574c06356bSdh 11584c06356bSdh switch (cmd) { 11594c06356bSdh case DDI_DETACH: 11604c06356bSdh if (iport) { 11614c06356bSdh /* iport detach */ 11624c06356bSdh if (pmcs_iport_unattach(iport)) { 11634c06356bSdh return (DDI_FAILURE); 11644c06356bSdh } 1165c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1166c3bc407cSdh "iport%d detached", inst); 11674c06356bSdh return (DDI_SUCCESS); 11684c06356bSdh } else { 11694c06356bSdh /* HBA detach */ 11704c06356bSdh if (pmcs_unattach(pwp)) { 11714c06356bSdh return (DDI_FAILURE); 11724c06356bSdh } 11734c06356bSdh return (DDI_SUCCESS); 11744c06356bSdh } 11754c06356bSdh 11764c06356bSdh case DDI_SUSPEND: 11774c06356bSdh case DDI_PM_SUSPEND: 11784c06356bSdh /* No DDI_SUSPEND on iport nodes */ 11794c06356bSdh if (iport) { 11804c06356bSdh return (DDI_SUCCESS); 11814c06356bSdh } 11824c06356bSdh 11834c06356bSdh if (pwp->stuck) { 11844c06356bSdh return (DDI_FAILURE); 11854c06356bSdh } 11864c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 11874c06356bSdh if (!tran) { 11884c06356bSdh return (DDI_FAILURE); 11894c06356bSdh } 11904c06356bSdh 11914c06356bSdh pwp = TRAN2PMC(tran); 11924c06356bSdh if (pwp == NULL) { 11934c06356bSdh return (DDI_FAILURE); 11944c06356bSdh } 11954c06356bSdh mutex_enter(&pwp->lock); 11964c06356bSdh if (pwp->tq) { 11974c06356bSdh ddi_taskq_suspend(pwp->tq); 11984c06356bSdh } 11994c06356bSdh pwp->suspended = 1; 12004c06356bSdh mutex_exit(&pwp->lock); 1201c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PMC8X6G suspending"); 12024c06356bSdh return (DDI_SUCCESS); 12034c06356bSdh 12044c06356bSdh default: 12054c06356bSdh return (DDI_FAILURE); 12064c06356bSdh } 12074c06356bSdh } 12084c06356bSdh 12094c06356bSdh static int 12104c06356bSdh pmcs_iport_unattach(pmcs_iport_t *iport) 12114c06356bSdh { 12124c06356bSdh pmcs_hw_t *pwp = iport->pwp; 12134c06356bSdh 12144c06356bSdh /* 12154c06356bSdh * First, check if there are still any configured targets on this 12164c06356bSdh * iport. If so, we fail detach. 12174c06356bSdh */ 12184c06356bSdh if (pmcs_iport_has_targets(pwp, iport)) { 1219c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1220c3bc407cSdh "iport%d detach failure: iport has targets (luns)", 1221c3bc407cSdh ddi_get_instance(iport->dip)); 12224c06356bSdh return (DDI_FAILURE); 12234c06356bSdh } 12244c06356bSdh 12254c06356bSdh /* 12264c06356bSdh * Remove this iport from our list if it is inactive in the phymap. 12274c06356bSdh */ 12284c06356bSdh rw_enter(&pwp->iports_lock, RW_WRITER); 12294c06356bSdh mutex_enter(&iport->lock); 12304c06356bSdh 12314c06356bSdh if (iport->ua_state == UA_ACTIVE) { 12324c06356bSdh mutex_exit(&iport->lock); 12334c06356bSdh rw_exit(&pwp->iports_lock); 1234c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1235c3bc407cSdh "iport%d detach failure: " 12364c06356bSdh "iport unit address active in phymap", 12374c06356bSdh ddi_get_instance(iport->dip)); 12384c06356bSdh return (DDI_FAILURE); 12394c06356bSdh } 12404c06356bSdh 12414c06356bSdh /* If it's our only iport, clear iports_attached */ 12424c06356bSdh ASSERT(pwp->num_iports >= 1); 12434c06356bSdh if (--pwp->num_iports == 0) { 12444c06356bSdh pwp->iports_attached = 0; 12454c06356bSdh } 12464c06356bSdh 12474c06356bSdh ASSERT(list_link_active(&iport->list_node)); 12484c06356bSdh list_remove(&pwp->iports, iport); 12494c06356bSdh rw_exit(&pwp->iports_lock); 12504c06356bSdh 12514c06356bSdh /* 12524c06356bSdh * We have removed the iport handle from the HBA's iports list, 12534c06356bSdh * there will be no new references to it. Two things must be 12544c06356bSdh * guarded against here. First, we could have PHY up events, 12554c06356bSdh * adding themselves to the iport->phys list and grabbing ref's 12564c06356bSdh * on our iport handle. Second, we could have existing references 12574c06356bSdh * to this iport handle from a point in time prior to the list 12584c06356bSdh * removal above. 12594c06356bSdh * 12604c06356bSdh * So first, destroy the phys list. Remove any phys that have snuck 12614c06356bSdh * in after the phymap deactivate, dropping the refcnt accordingly. 12624c06356bSdh * If these PHYs are still up if and when the phymap reactivates 12634c06356bSdh * (i.e. when this iport reattaches), we'll populate the list with 12644c06356bSdh * them and bump the refcnt back up. 12654c06356bSdh */ 12664c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 12674c06356bSdh ASSERT(list_is_empty(&iport->phys)); 12684c06356bSdh list_destroy(&iport->phys); 12694c06356bSdh mutex_exit(&iport->lock); 12704c06356bSdh 12714c06356bSdh /* 12724c06356bSdh * Second, wait for any other references to this iport to be 12734c06356bSdh * dropped, then continue teardown. 12744c06356bSdh */ 12754c06356bSdh mutex_enter(&iport->refcnt_lock); 12764c06356bSdh while (iport->refcnt != 0) { 12774c06356bSdh cv_wait(&iport->refcnt_cv, &iport->refcnt_lock); 12784c06356bSdh } 12794c06356bSdh mutex_exit(&iport->refcnt_lock); 12804c06356bSdh 12814c06356bSdh /* Delete kstats */ 12824c06356bSdh pmcs_destroy_phy_stats(iport); 12834c06356bSdh 12844c06356bSdh /* Destroy the iport target map */ 12854c06356bSdh if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) { 12864c06356bSdh return (DDI_FAILURE); 12874c06356bSdh } 12884c06356bSdh 12894c06356bSdh /* Free the tgt soft state */ 12904c06356bSdh if (iport->tgt_sstate != NULL) { 12914c06356bSdh ddi_soft_state_bystr_fini(&iport->tgt_sstate); 12924c06356bSdh } 12934c06356bSdh 12944c06356bSdh /* Free our unit address string */ 12954c06356bSdh strfree(iport->ua); 12964c06356bSdh 12974c06356bSdh /* Finish teardown and free the softstate */ 12984c06356bSdh mutex_destroy(&iport->refcnt_lock); 12996745c559SJesse Butler mutex_destroy(&iport->smp_lock); 13004c06356bSdh ASSERT(iport->refcnt == 0); 13014c06356bSdh cv_destroy(&iport->refcnt_cv); 13026745c559SJesse Butler cv_destroy(&iport->smp_cv); 13034c06356bSdh mutex_destroy(&iport->lock); 13044c06356bSdh ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip)); 13054c06356bSdh 13064c06356bSdh return (DDI_SUCCESS); 13074c06356bSdh } 13084c06356bSdh 13094c06356bSdh static int 13104c06356bSdh pmcs_unattach(pmcs_hw_t *pwp) 13114c06356bSdh { 13124c06356bSdh int i; 13134c06356bSdh enum pwpstate curstate; 13144c06356bSdh pmcs_cq_thr_info_t *cqti; 13154c06356bSdh 13164c06356bSdh /* 13174c06356bSdh * Tear down the interrupt infrastructure. 13184c06356bSdh */ 13194c06356bSdh if (pmcs_teardown_intr(pwp)) { 13204c06356bSdh pwp->stuck = 1; 13214c06356bSdh } 13224c06356bSdh pwp->intr_cnt = 0; 13234c06356bSdh 13244c06356bSdh /* 13254c06356bSdh * Grab a lock, if initted, to set state. 13264c06356bSdh */ 13274c06356bSdh if (pwp->locks_initted) { 13284c06356bSdh mutex_enter(&pwp->lock); 13294c06356bSdh if (pwp->state != STATE_DEAD) { 13304c06356bSdh pwp->state = STATE_UNPROBING; 13314c06356bSdh } 13324c06356bSdh curstate = pwp->state; 13334c06356bSdh mutex_exit(&pwp->lock); 13344c06356bSdh 13354c06356bSdh /* 13364c06356bSdh * Stop the I/O completion threads. 13374c06356bSdh */ 13384c06356bSdh mutex_enter(&pwp->cq_lock); 13394c06356bSdh pwp->cq_info.cq_stop = B_TRUE; 13404c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 13414c06356bSdh if (pwp->cq_info.cq_thr_info[i].cq_thread) { 13424c06356bSdh cqti = &pwp->cq_info.cq_thr_info[i]; 13434c06356bSdh mutex_enter(&cqti->cq_thr_lock); 13444c06356bSdh cv_signal(&cqti->cq_cv); 13454c06356bSdh mutex_exit(&cqti->cq_thr_lock); 13464c06356bSdh mutex_exit(&pwp->cq_lock); 13474c06356bSdh thread_join(cqti->cq_thread->t_did); 13484c06356bSdh mutex_enter(&pwp->cq_lock); 13494c06356bSdh } 13504c06356bSdh } 13514c06356bSdh mutex_exit(&pwp->cq_lock); 13524c06356bSdh 13534c06356bSdh /* 13544c06356bSdh * Stop the interrupt coalescing timer thread 13554c06356bSdh */ 13564c06356bSdh if (pwp->ict_thread) { 13574c06356bSdh mutex_enter(&pwp->ict_lock); 13584c06356bSdh pwp->io_intr_coal.stop_thread = B_TRUE; 13594c06356bSdh cv_signal(&pwp->ict_cv); 13604c06356bSdh mutex_exit(&pwp->ict_lock); 13614c06356bSdh thread_join(pwp->ict_thread->t_did); 13624c06356bSdh } 13634c06356bSdh } else { 13644c06356bSdh if (pwp->state != STATE_DEAD) { 13654c06356bSdh pwp->state = STATE_UNPROBING; 13664c06356bSdh } 13674c06356bSdh curstate = pwp->state; 13684c06356bSdh } 13694c06356bSdh 13704c06356bSdh /* 13714c06356bSdh * Make sure that any pending watchdog won't 13724c06356bSdh * be called from this point on out. 13734c06356bSdh */ 13744c06356bSdh (void) untimeout(pwp->wdhandle); 13754c06356bSdh /* 13764c06356bSdh * After the above action, the watchdog 13774c06356bSdh * timer that starts up the worker task 13784c06356bSdh * may trigger but will exit immediately 13794c06356bSdh * on triggering. 13804c06356bSdh * 13814c06356bSdh * Now that this is done, we can destroy 13824c06356bSdh * the task queue, which will wait if we're 13834c06356bSdh * running something on it. 13844c06356bSdh */ 13854c06356bSdh if (pwp->tq) { 13864c06356bSdh ddi_taskq_destroy(pwp->tq); 13874c06356bSdh pwp->tq = NULL; 13884c06356bSdh } 13894c06356bSdh 13904c06356bSdh pmcs_fm_fini(pwp); 13914c06356bSdh 13924c06356bSdh if (pwp->hba_attached) { 13934c06356bSdh (void) scsi_hba_detach(pwp->dip); 13944c06356bSdh pwp->hba_attached = 0; 13954c06356bSdh } 13964c06356bSdh 13974c06356bSdh /* 13984c06356bSdh * If the chip hasn't been marked dead, shut it down now 13994c06356bSdh * to bring it back to a known state without attempting 14004c06356bSdh * a soft reset. 14014c06356bSdh */ 14024c06356bSdh if (curstate != STATE_DEAD && pwp->locks_initted) { 14034c06356bSdh /* 14044c06356bSdh * De-register all registered devices 14054c06356bSdh */ 14064c06356bSdh pmcs_deregister_devices(pwp, pwp->root_phys); 14074c06356bSdh 14084c06356bSdh /* 14094c06356bSdh * Stop all the phys. 14104c06356bSdh */ 14114c06356bSdh pmcs_stop_phys(pwp); 14124c06356bSdh 14134c06356bSdh /* 14144c06356bSdh * Shut Down Message Passing 14154c06356bSdh */ 14164c06356bSdh (void) pmcs_stop_mpi(pwp); 14174c06356bSdh 14184c06356bSdh /* 14194c06356bSdh * Reset chip 14204c06356bSdh */ 14214c06356bSdh (void) pmcs_soft_reset(pwp, B_FALSE); 14225c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_DETACH; 14234c06356bSdh } 14244c06356bSdh 14254c06356bSdh /* 14264c06356bSdh * Turn off interrupts on the chip 14274c06356bSdh */ 14284c06356bSdh if (pwp->mpi_acc_handle) { 14294c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 14304c06356bSdh } 14314c06356bSdh 1432*32b54db7SJesse Butler if (pwp->hss_iportmap != NULL) { 1433*32b54db7SJesse Butler /* Destroy the iportmap */ 1434*32b54db7SJesse Butler scsi_hba_iportmap_destroy(pwp->hss_iportmap); 1435*32b54db7SJesse Butler } 1436*32b54db7SJesse Butler 1437*32b54db7SJesse Butler if (pwp->hss_phymap != NULL) { 1438*32b54db7SJesse Butler /* Destroy the phymap */ 1439*32b54db7SJesse Butler sas_phymap_destroy(pwp->hss_phymap); 1440*32b54db7SJesse Butler } 1441*32b54db7SJesse Butler 1442*32b54db7SJesse Butler /* Destroy the iports lock and list */ 1443*32b54db7SJesse Butler rw_destroy(&pwp->iports_lock); 1444*32b54db7SJesse Butler ASSERT(list_is_empty(&pwp->iports)); 1445*32b54db7SJesse Butler list_destroy(&pwp->iports); 1446*32b54db7SJesse Butler 14474c06356bSdh /* Destroy pwp's lock */ 14484c06356bSdh if (pwp->locks_initted) { 14494c06356bSdh mutex_destroy(&pwp->lock); 14504c06356bSdh mutex_destroy(&pwp->dma_lock); 14514c06356bSdh mutex_destroy(&pwp->axil_lock); 14524c06356bSdh mutex_destroy(&pwp->cq_lock); 14534c06356bSdh mutex_destroy(&pwp->config_lock); 14544c06356bSdh mutex_destroy(&pwp->ict_lock); 14554c06356bSdh mutex_destroy(&pwp->wfree_lock); 14564c06356bSdh mutex_destroy(&pwp->pfree_lock); 14574c06356bSdh mutex_destroy(&pwp->dead_phylist_lock); 14584c06356bSdh #ifdef DEBUG 14594c06356bSdh mutex_destroy(&pwp->dbglock); 14604c06356bSdh #endif 1461*32b54db7SJesse Butler cv_destroy(&pwp->config_cv); 14624c06356bSdh cv_destroy(&pwp->ict_cv); 14634c06356bSdh cv_destroy(&pwp->drain_cv); 14644c06356bSdh pwp->locks_initted = 0; 14654c06356bSdh } 14664c06356bSdh 14674c06356bSdh /* 14684c06356bSdh * Free DMA handles and associated consistent memory 14694c06356bSdh */ 14704c06356bSdh if (pwp->regdump_hndl) { 14714c06356bSdh if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) { 1472c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1473c3bc407cSdh "Condition check failed " 14744c06356bSdh "at %s():%d", __func__, __LINE__); 14754c06356bSdh } 14764c06356bSdh ddi_dma_free_handle(&pwp->regdump_hndl); 14774c06356bSdh ddi_dma_mem_free(&pwp->regdump_acchdl); 14784c06356bSdh pwp->regdump_hndl = 0; 14794c06356bSdh } 14804c06356bSdh if (pwp->fwlog_hndl) { 14814c06356bSdh if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) { 1482c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1483c3bc407cSdh "Condition check failed " 14844c06356bSdh "at %s():%d", __func__, __LINE__); 14854c06356bSdh } 14864c06356bSdh ddi_dma_free_handle(&pwp->fwlog_hndl); 14874c06356bSdh ddi_dma_mem_free(&pwp->fwlog_acchdl); 14884c06356bSdh pwp->fwlog_hndl = 0; 14894c06356bSdh } 14904c06356bSdh if (pwp->cip_handles) { 14914c06356bSdh if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) { 1492c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1493c3bc407cSdh "Condition check failed " 14944c06356bSdh "at %s():%d", __func__, __LINE__); 14954c06356bSdh } 14964c06356bSdh ddi_dma_free_handle(&pwp->cip_handles); 14974c06356bSdh ddi_dma_mem_free(&pwp->cip_acchdls); 14984c06356bSdh pwp->cip_handles = 0; 14994c06356bSdh } 15004c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 15014c06356bSdh if (pwp->oqp_handles[i]) { 15024c06356bSdh if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) != 15034c06356bSdh DDI_SUCCESS) { 1504c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1505c3bc407cSdh "Condition check failed at %s():%d", 1506c3bc407cSdh __func__, __LINE__); 15074c06356bSdh } 15084c06356bSdh ddi_dma_free_handle(&pwp->oqp_handles[i]); 15094c06356bSdh ddi_dma_mem_free(&pwp->oqp_acchdls[i]); 15104c06356bSdh pwp->oqp_handles[i] = 0; 15114c06356bSdh } 15124c06356bSdh } 15134c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 15144c06356bSdh if (pwp->iqp_handles[i]) { 15154c06356bSdh if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) != 15164c06356bSdh DDI_SUCCESS) { 1517c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1518c3bc407cSdh "Condition check failed at %s():%d", 1519c3bc407cSdh __func__, __LINE__); 15204c06356bSdh } 15214c06356bSdh ddi_dma_free_handle(&pwp->iqp_handles[i]); 15224c06356bSdh ddi_dma_mem_free(&pwp->iqp_acchdls[i]); 15234c06356bSdh pwp->iqp_handles[i] = 0; 15244c06356bSdh } 15254c06356bSdh } 15264c06356bSdh 15274c06356bSdh pmcs_free_dma_chunklist(pwp); 15284c06356bSdh 15294c06356bSdh /* 15304c06356bSdh * Unmap registers and destroy access handles 15314c06356bSdh */ 15324c06356bSdh if (pwp->mpi_acc_handle) { 15334c06356bSdh ddi_regs_map_free(&pwp->mpi_acc_handle); 15344c06356bSdh pwp->mpi_acc_handle = 0; 15354c06356bSdh } 15364c06356bSdh if (pwp->top_acc_handle) { 15374c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 15384c06356bSdh pwp->top_acc_handle = 0; 15394c06356bSdh } 15404c06356bSdh if (pwp->gsm_acc_handle) { 15414c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 15424c06356bSdh pwp->gsm_acc_handle = 0; 15434c06356bSdh } 15444c06356bSdh if (pwp->msg_acc_handle) { 15454c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 15464c06356bSdh pwp->msg_acc_handle = 0; 15474c06356bSdh } 15484c06356bSdh if (pwp->pci_acc_handle) { 15494c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 15504c06356bSdh pwp->pci_acc_handle = 0; 15514c06356bSdh } 15524c06356bSdh 15534c06356bSdh /* 15544c06356bSdh * Do memory allocation cleanup. 15554c06356bSdh */ 15564c06356bSdh while (pwp->dma_freelist) { 15574c06356bSdh pmcs_dmachunk_t *this = pwp->dma_freelist; 15584c06356bSdh pwp->dma_freelist = this->nxt; 15594c06356bSdh kmem_free(this, sizeof (pmcs_dmachunk_t)); 15604c06356bSdh } 15614c06356bSdh 15624c06356bSdh /* 15634c06356bSdh * Free pools 15644c06356bSdh */ 15654c06356bSdh if (pwp->iocomp_cb_cache) { 15664c06356bSdh kmem_cache_destroy(pwp->iocomp_cb_cache); 15674c06356bSdh } 15684c06356bSdh 15694c06356bSdh /* 15704c06356bSdh * Free all PHYs (at level > 0), then free the cache 15714c06356bSdh */ 15724c06356bSdh pmcs_free_all_phys(pwp, pwp->root_phys); 15734c06356bSdh if (pwp->phy_cache) { 15744c06356bSdh kmem_cache_destroy(pwp->phy_cache); 15754c06356bSdh } 15764c06356bSdh 15774c06356bSdh /* 15784c06356bSdh * Free root PHYs 15794c06356bSdh */ 15804c06356bSdh if (pwp->root_phys) { 15814c06356bSdh pmcs_phy_t *phyp = pwp->root_phys; 15824c06356bSdh for (i = 0; i < pwp->nphy; i++) { 15834c06356bSdh mutex_destroy(&phyp->phy_lock); 15844c06356bSdh phyp = phyp->sibling; 15854c06356bSdh } 15864c06356bSdh kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t)); 15874c06356bSdh pwp->root_phys = NULL; 15884c06356bSdh pwp->nphy = 0; 15894c06356bSdh } 15904c06356bSdh 15914c06356bSdh /* Free the targets list */ 15924c06356bSdh if (pwp->targets) { 15934c06356bSdh kmem_free(pwp->targets, 15944c06356bSdh sizeof (pmcs_xscsi_t *) * pwp->max_dev); 15954c06356bSdh } 15964c06356bSdh 15974c06356bSdh /* 15984c06356bSdh * Free work structures 15994c06356bSdh */ 16004c06356bSdh 16014c06356bSdh if (pwp->work && pwp->max_cmd) { 16024c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 16034c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 16044c06356bSdh mutex_destroy(&pwrk->lock); 16054c06356bSdh cv_destroy(&pwrk->sleep_cv); 16064c06356bSdh } 16074c06356bSdh kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd); 16084c06356bSdh pwp->work = NULL; 16094c06356bSdh pwp->max_cmd = 0; 16104c06356bSdh } 16114c06356bSdh 16124c06356bSdh /* 16134c06356bSdh * Do last property and SCSA cleanup 16144c06356bSdh */ 16154c06356bSdh if (pwp->tran) { 16164c06356bSdh scsi_hba_tran_free(pwp->tran); 16174c06356bSdh pwp->tran = NULL; 16184c06356bSdh } 16194c06356bSdh if (pwp->reset_notify_listf) { 16204c06356bSdh scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf); 16214c06356bSdh pwp->reset_notify_listf = NULL; 16224c06356bSdh } 16234c06356bSdh ddi_prop_remove_all(pwp->dip); 16244c06356bSdh if (pwp->stuck) { 16254c06356bSdh return (-1); 16264c06356bSdh } 16274c06356bSdh 16284c06356bSdh /* Free register dump area if allocated */ 16294c06356bSdh if (pwp->regdumpp) { 16304c06356bSdh kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE); 16314c06356bSdh pwp->regdumpp = NULL; 16324c06356bSdh } 16334c06356bSdh if (pwp->iqpt && pwp->iqpt->head) { 16344c06356bSdh kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE); 16354c06356bSdh pwp->iqpt->head = pwp->iqpt->curpos = NULL; 16364c06356bSdh } 16374c06356bSdh if (pwp->iqpt) { 16384c06356bSdh kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t)); 16394c06356bSdh pwp->iqpt = NULL; 16404c06356bSdh } 16414c06356bSdh 16424c06356bSdh ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip)); 16434c06356bSdh return (0); 16444c06356bSdh } 16454c06356bSdh 16464c06356bSdh /* 16474c06356bSdh * quiesce (9E) entry point 16484c06356bSdh * 16494c06356bSdh * This function is called when the system is single-threaded at high PIL 16504c06356bSdh * with preemption disabled. Therefore, the function must not block/wait/sleep. 16514c06356bSdh * 16524c06356bSdh * Returns DDI_SUCCESS or DDI_FAILURE. 16534c06356bSdh * 16544c06356bSdh */ 16554c06356bSdh static int 16564c06356bSdh pmcs_quiesce(dev_info_t *dip) 16574c06356bSdh { 16584c06356bSdh pmcs_hw_t *pwp; 16594c06356bSdh scsi_hba_tran_t *tran; 16604c06356bSdh 16614c06356bSdh if ((tran = ddi_get_driver_private(dip)) == NULL) 16624c06356bSdh return (DDI_SUCCESS); 16634c06356bSdh 16644c06356bSdh /* No quiesce necessary on a per-iport basis */ 16654c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 16664c06356bSdh return (DDI_SUCCESS); 16674c06356bSdh } 16684c06356bSdh 16694c06356bSdh if ((pwp = TRAN2PMC(tran)) == NULL) 16704c06356bSdh return (DDI_SUCCESS); 16714c06356bSdh 16724c06356bSdh /* Stop MPI & Reset chip (no need to re-initialize) */ 16734c06356bSdh (void) pmcs_stop_mpi(pwp); 16744c06356bSdh (void) pmcs_soft_reset(pwp, B_TRUE); 16755c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_QUIESCE; 16764c06356bSdh 16774c06356bSdh return (DDI_SUCCESS); 16784c06356bSdh } 16794c06356bSdh 16804c06356bSdh /* 16814c06356bSdh * Called with xp->statlock and PHY lock and scratch acquired. 16824c06356bSdh */ 16834c06356bSdh static int 16844c06356bSdh pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 16854c06356bSdh { 16864c06356bSdh ata_identify_t *ati; 16874c06356bSdh int result, i; 16884c06356bSdh pmcs_phy_t *pptr; 16894c06356bSdh uint16_t *a; 16904c06356bSdh union { 16914c06356bSdh uint8_t nsa[8]; 16924c06356bSdh uint16_t nsb[4]; 16934c06356bSdh } u; 16944c06356bSdh 16954c06356bSdh /* 16964c06356bSdh * Safe defaults - use only if this target is brand new (i.e. doesn't 16974c06356bSdh * already have these settings configured) 16984c06356bSdh */ 16994c06356bSdh if (xp->capacity == 0) { 17004c06356bSdh xp->capacity = (uint64_t)-1; 17014c06356bSdh xp->ca = 1; 17024c06356bSdh xp->qdepth = 1; 17034c06356bSdh xp->pio = 1; 17044c06356bSdh } 17054c06356bSdh 17064c06356bSdh pptr = xp->phy; 17074c06356bSdh 17084c06356bSdh /* 17094c06356bSdh * We only try and issue an IDENTIFY for first level 17104c06356bSdh * (direct attached) devices. We don't try and 17114c06356bSdh * set other quirks here (this will happen later, 17124c06356bSdh * if the device is fully configured) 17134c06356bSdh */ 17144c06356bSdh if (pptr->level) { 17154c06356bSdh return (0); 17164c06356bSdh } 17174c06356bSdh 17184c06356bSdh mutex_exit(&xp->statlock); 17194c06356bSdh result = pmcs_sata_identify(pwp, pptr); 17204c06356bSdh mutex_enter(&xp->statlock); 17214c06356bSdh 17224c06356bSdh if (result) { 17234c06356bSdh return (result); 17244c06356bSdh } 17254c06356bSdh ati = pwp->scratch; 17264c06356bSdh a = &ati->word108; 17274c06356bSdh for (i = 0; i < 4; i++) { 17284c06356bSdh u.nsb[i] = ddi_swap16(*a++); 17294c06356bSdh } 17304c06356bSdh 17314c06356bSdh /* 17324c06356bSdh * Check the returned data for being a valid (NAA=5) WWN. 17334c06356bSdh * If so, use that and override the SAS address we were 17344c06356bSdh * given at Link Up time. 17354c06356bSdh */ 17364c06356bSdh if ((u.nsa[0] >> 4) == 5) { 17374c06356bSdh (void) memcpy(pptr->sas_address, u.nsa, 8); 17384c06356bSdh } 1739c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 1740c3bc407cSdh "%s: %s has SAS ADDRESS " SAS_ADDR_FMT, 17414c06356bSdh __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address)); 17424c06356bSdh return (0); 17434c06356bSdh } 17444c06356bSdh 17454c06356bSdh /* 17464c06356bSdh * Called with PHY lock and target statlock held and scratch acquired 17474c06356bSdh */ 17484c06356bSdh static boolean_t 17494c06356bSdh pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target) 17504c06356bSdh { 17514c06356bSdh ASSERT(target != NULL); 1752c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, "%s: target = 0x%p", 17534c06356bSdh __func__, (void *) target); 17544c06356bSdh 17554c06356bSdh switch (target->phy->dtype) { 17564c06356bSdh case SATA: 17574c06356bSdh if (pmcs_add_sata_device(pwp, target) != 0) { 1758c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, target->phy, 1759c3bc407cSdh target, "%s: add_sata_device failed for tgt 0x%p", 17604c06356bSdh __func__, (void *) target); 17614c06356bSdh return (B_FALSE); 17624c06356bSdh } 17634c06356bSdh break; 17644c06356bSdh case SAS: 17654c06356bSdh target->qdepth = maxqdepth; 17664c06356bSdh break; 17674c06356bSdh case EXPANDER: 17684c06356bSdh target->qdepth = 1; 17694c06356bSdh break; 17704c06356bSdh } 17714c06356bSdh 17724c06356bSdh target->new = 0; 17734c06356bSdh target->assigned = 1; 17744c06356bSdh target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL; 17754c06356bSdh target->dtype = target->phy->dtype; 17764c06356bSdh 17774c06356bSdh /* 17784c06356bSdh * Set the PHY's config stop time to 0. This is one of the final 17794c06356bSdh * stops along the config path, so we're indicating that we 17804c06356bSdh * successfully configured the PHY. 17814c06356bSdh */ 17824c06356bSdh target->phy->config_stop = 0; 17834c06356bSdh 17844c06356bSdh return (B_TRUE); 17854c06356bSdh } 17864c06356bSdh 17874c06356bSdh void 17884c06356bSdh pmcs_worker(void *arg) 17894c06356bSdh { 17904c06356bSdh pmcs_hw_t *pwp = arg; 17914c06356bSdh ulong_t work_flags; 17924c06356bSdh 17934c06356bSdh DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t, 17944c06356bSdh pwp->config_changed); 17954c06356bSdh 17964c06356bSdh if (pwp->state != STATE_RUNNING) { 17974c06356bSdh return; 17984c06356bSdh } 17994c06356bSdh 18004c06356bSdh work_flags = atomic_swap_ulong(&pwp->work_flags, 0); 18014c06356bSdh 1802c280a92bSDavid Hollister if (work_flags & PMCS_WORK_FLAG_DUMP_REGS) { 1803c280a92bSDavid Hollister mutex_enter(&pwp->lock); 1804c280a92bSDavid Hollister pmcs_register_dump_int(pwp); 1805c280a92bSDavid Hollister mutex_exit(&pwp->lock); 1806c280a92bSDavid Hollister } 1807c280a92bSDavid Hollister 18084c06356bSdh if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) { 18094c06356bSdh pmcs_ack_events(pwp); 18104c06356bSdh } 18114c06356bSdh 18124c06356bSdh if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) { 18134c06356bSdh mutex_enter(&pwp->lock); 18144c06356bSdh pmcs_spinup_release(pwp, NULL); 18154c06356bSdh mutex_exit(&pwp->lock); 18164c06356bSdh } 18174c06356bSdh 18184c06356bSdh if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) { 18194c06356bSdh pmcs_ssp_event_recovery(pwp); 18204c06356bSdh } 18214c06356bSdh 18224c06356bSdh if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) { 18234c06356bSdh pmcs_dev_state_recovery(pwp, NULL); 18244c06356bSdh } 18254c06356bSdh 1826601c90f1SSrikanth, Ramana if (work_flags & PMCS_WORK_FLAG_DEREGISTER_DEV) { 1827601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pwp, NULL); 1828601c90f1SSrikanth, Ramana } 1829601c90f1SSrikanth, Ramana 18304c06356bSdh if (work_flags & PMCS_WORK_FLAG_DISCOVER) { 18314c06356bSdh pmcs_discover(pwp); 18324c06356bSdh } 18334c06356bSdh 18344c06356bSdh if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) { 18354c06356bSdh if (pmcs_abort_handler(pwp)) { 18364c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 18374c06356bSdh } 18384c06356bSdh } 18394c06356bSdh 18404c06356bSdh if (work_flags & PMCS_WORK_FLAG_SATA_RUN) { 18414c06356bSdh pmcs_sata_work(pwp); 18424c06356bSdh } 18434c06356bSdh 18444c06356bSdh if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) { 18454c06356bSdh pmcs_scsa_wq_run(pwp); 18464c06356bSdh mutex_enter(&pwp->lock); 18474c06356bSdh PMCS_CQ_RUN(pwp); 18484c06356bSdh mutex_exit(&pwp->lock); 18494c06356bSdh } 18504c06356bSdh 18514c06356bSdh if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) { 18524c06356bSdh if (pmcs_add_more_chunks(pwp, 18534c06356bSdh ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) { 18544c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 18554c06356bSdh } else { 18564c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 18574c06356bSdh } 18584c06356bSdh } 18594c06356bSdh } 18604c06356bSdh 18614c06356bSdh static int 18624c06356bSdh pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize) 18634c06356bSdh { 18644c06356bSdh pmcs_dmachunk_t *dc; 18654c06356bSdh unsigned long dl; 18664c06356bSdh pmcs_chunk_t *pchunk = NULL; 18674c06356bSdh 18684c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 18694c06356bSdh 18704c06356bSdh pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP); 18714c06356bSdh if (pchunk == NULL) { 1872c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18734c06356bSdh "Not enough memory for DMA chunks"); 18744c06356bSdh return (-1); 18754c06356bSdh } 18764c06356bSdh 18774c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle, 18784c06356bSdh &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp, 18794c06356bSdh &pchunk->dma_addr) == B_FALSE) { 1880c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1881c3bc407cSdh "Failed to setup DMA for chunks"); 18824c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 18834c06356bSdh return (-1); 18844c06356bSdh } 18854c06356bSdh 18864c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) || 18874c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) { 18884c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED); 18894c06356bSdh return (-1); 18904c06356bSdh } 18914c06356bSdh 18924c06356bSdh bzero(pchunk->addrp, nsize); 18934c06356bSdh dc = NULL; 18944c06356bSdh for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) { 18954c06356bSdh pmcs_dmachunk_t *tmp; 18964c06356bSdh tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP); 18974c06356bSdh tmp->nxt = dc; 18984c06356bSdh dc = tmp; 18994c06356bSdh } 19004c06356bSdh mutex_enter(&pwp->dma_lock); 19014c06356bSdh pmcs_idma_chunks(pwp, dc, pchunk, nsize); 19024c06356bSdh pwp->nchunks++; 19034c06356bSdh mutex_exit(&pwp->dma_lock); 19044c06356bSdh return (0); 19054c06356bSdh } 19064c06356bSdh 19075c45adf0SJesse Butler static void 19085c45adf0SJesse Butler pmcs_check_forward_progress(pmcs_hw_t *pwp) 19095c45adf0SJesse Butler { 1910d78a6b7eSJesse Butler pmcwork_t *wrkp; 1911d78a6b7eSJesse Butler uint32_t *iqp; 19125c45adf0SJesse Butler uint32_t cur_iqci; 1913d78a6b7eSJesse Butler uint32_t cur_work_idx; 19145c45adf0SJesse Butler uint32_t cur_msgu_tick; 19155c45adf0SJesse Butler uint32_t cur_iop_tick; 19165c45adf0SJesse Butler int i; 19175c45adf0SJesse Butler 19185c45adf0SJesse Butler mutex_enter(&pwp->lock); 19195c45adf0SJesse Butler 19205c45adf0SJesse Butler if (pwp->state == STATE_IN_RESET) { 19215c45adf0SJesse Butler mutex_exit(&pwp->lock); 19225c45adf0SJesse Butler return; 19235c45adf0SJesse Butler } 19245c45adf0SJesse Butler 1925d78a6b7eSJesse Butler /* 1926d78a6b7eSJesse Butler * Ensure that inbound work is getting picked up. First, check to 1927d78a6b7eSJesse Butler * see if new work has been posted. If it has, ensure that the 1928d78a6b7eSJesse Butler * work is moving forward by checking the consumer index and the 1929d78a6b7eSJesse Butler * last_htag for the work being processed against what we saw last 1930d78a6b7eSJesse Butler * time. Note: we use the work structure's 'last_htag' because at 1931d78a6b7eSJesse Butler * any given moment it could be freed back, thus clearing 'htag' 1932d78a6b7eSJesse Butler * and setting 'last_htag' (see pmcs_pwork). 1933d78a6b7eSJesse Butler */ 19345c45adf0SJesse Butler for (i = 0; i < PMCS_NIQ; i++) { 19355c45adf0SJesse Butler cur_iqci = pmcs_rd_iqci(pwp, i); 1936d78a6b7eSJesse Butler iqp = &pwp->iqp[i][cur_iqci * (PMCS_QENTRY_SIZE >> 2)]; 1937d78a6b7eSJesse Butler cur_work_idx = PMCS_TAG_INDEX(LE_32(*(iqp+1))); 1938d78a6b7eSJesse Butler wrkp = &pwp->work[cur_work_idx]; 19395c45adf0SJesse Butler if (cur_iqci == pwp->shadow_iqpi[i]) { 19405c45adf0SJesse Butler pwp->last_iqci[i] = cur_iqci; 1941d78a6b7eSJesse Butler pwp->last_htag[i] = wrkp->last_htag; 19425c45adf0SJesse Butler continue; 19435c45adf0SJesse Butler } 1944d78a6b7eSJesse Butler if ((cur_iqci == pwp->last_iqci[i]) && 1945d78a6b7eSJesse Butler (wrkp->last_htag == pwp->last_htag[i])) { 19465c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19475c45adf0SJesse Butler "Inbound Queue stall detected, issuing reset"); 19485c45adf0SJesse Butler goto hot_reset; 19495c45adf0SJesse Butler } 19505c45adf0SJesse Butler pwp->last_iqci[i] = cur_iqci; 1951d78a6b7eSJesse Butler pwp->last_htag[i] = wrkp->last_htag; 19525c45adf0SJesse Butler } 19535c45adf0SJesse Butler 1954d78a6b7eSJesse Butler /* 1955d78a6b7eSJesse Butler * Check heartbeat on both the MSGU and IOP. It is unlikely that 1956d78a6b7eSJesse Butler * we'd ever fail here, as the inbound queue monitoring code above 1957d78a6b7eSJesse Butler * would detect a stall due to either of these elements being 1958d78a6b7eSJesse Butler * stalled, but we might as well keep an eye on them. 1959d78a6b7eSJesse Butler */ 19605c45adf0SJesse Butler cur_msgu_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK); 19615c45adf0SJesse Butler if (cur_msgu_tick == pwp->last_msgu_tick) { 19625c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19635c45adf0SJesse Butler "Stall detected on MSGU, issuing reset"); 19645c45adf0SJesse Butler goto hot_reset; 19655c45adf0SJesse Butler } 19665c45adf0SJesse Butler pwp->last_msgu_tick = cur_msgu_tick; 19675c45adf0SJesse Butler 19685c45adf0SJesse Butler cur_iop_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK); 19695c45adf0SJesse Butler if (cur_iop_tick == pwp->last_iop_tick) { 19705c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19715c45adf0SJesse Butler "Stall detected on IOP, issuing reset"); 19725c45adf0SJesse Butler goto hot_reset; 19735c45adf0SJesse Butler } 19745c45adf0SJesse Butler pwp->last_iop_tick = cur_iop_tick; 19755c45adf0SJesse Butler 19765c45adf0SJesse Butler mutex_exit(&pwp->lock); 19775c45adf0SJesse Butler return; 19785c45adf0SJesse Butler 19795c45adf0SJesse Butler hot_reset: 19805c45adf0SJesse Butler pwp->state = STATE_DEAD; 19815c45adf0SJesse Butler /* 19825c45adf0SJesse Butler * We've detected a stall. Attempt to recover service via hot 19835c45adf0SJesse Butler * reset. In case of failure, pmcs_hot_reset() will handle the 19845c45adf0SJesse Butler * failure and issue any required FM notifications. 19855c45adf0SJesse Butler * See pmcs_subr.c for more details. 19865c45adf0SJesse Butler */ 19875c45adf0SJesse Butler if (pmcs_hot_reset(pwp)) { 19885c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 19895c45adf0SJesse Butler "%s: hot reset failure", __func__); 19905c45adf0SJesse Butler } else { 19915c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 19925c45adf0SJesse Butler "%s: hot reset complete", __func__); 19935c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_STALL; 19945c45adf0SJesse Butler } 19955c45adf0SJesse Butler mutex_exit(&pwp->lock); 19965c45adf0SJesse Butler } 19974c06356bSdh 19984c06356bSdh static void 19994c06356bSdh pmcs_check_commands(pmcs_hw_t *pwp) 20004c06356bSdh { 20014c06356bSdh pmcs_cmd_t *sp; 20024c06356bSdh size_t amt; 20034c06356bSdh char path[32]; 20044c06356bSdh pmcwork_t *pwrk; 20054c06356bSdh pmcs_xscsi_t *target; 20064c06356bSdh pmcs_phy_t *phyp; 2007601c90f1SSrikanth, Ramana int rval; 20084c06356bSdh 20094c06356bSdh for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) { 20104c06356bSdh mutex_enter(&pwrk->lock); 20114c06356bSdh 20124c06356bSdh /* 20134c06356bSdh * If the command isn't active, we can't be timing it still. 20144c06356bSdh * Active means the tag is not free and the state is "on chip". 20154c06356bSdh */ 20164c06356bSdh if (!PMCS_COMMAND_ACTIVE(pwrk)) { 20174c06356bSdh mutex_exit(&pwrk->lock); 20184c06356bSdh continue; 20194c06356bSdh } 20204c06356bSdh 20214c06356bSdh /* 20224c06356bSdh * No timer active for this command. 20234c06356bSdh */ 20244c06356bSdh if (pwrk->timer == 0) { 20254c06356bSdh mutex_exit(&pwrk->lock); 20264c06356bSdh continue; 20274c06356bSdh } 20284c06356bSdh 20294c06356bSdh /* 20304c06356bSdh * Knock off bits for the time interval. 20314c06356bSdh */ 20324c06356bSdh if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) { 20334c06356bSdh pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL); 20344c06356bSdh } else { 20354c06356bSdh pwrk->timer = 0; 20364c06356bSdh } 20374c06356bSdh if (pwrk->timer > 0) { 20384c06356bSdh mutex_exit(&pwrk->lock); 20394c06356bSdh continue; 20404c06356bSdh } 20414c06356bSdh 20424c06356bSdh /* 20434c06356bSdh * The command has now officially timed out. 20444c06356bSdh * Get the path for it. If it doesn't have 20454c06356bSdh * a phy pointer any more, it's really dead 20464c06356bSdh * and can just be put back on the free list. 20474c06356bSdh * There should *not* be any commands associated 20484c06356bSdh * with it any more. 20494c06356bSdh */ 20504c06356bSdh if (pwrk->phy == NULL) { 2051c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20524c06356bSdh "dead command with gone phy being recycled"); 20534c06356bSdh ASSERT(pwrk->xp == NULL); 20544c06356bSdh pmcs_pwork(pwp, pwrk); 20554c06356bSdh continue; 20564c06356bSdh } 20574c06356bSdh amt = sizeof (path); 20584c06356bSdh amt = min(sizeof (pwrk->phy->path), amt); 20594c06356bSdh (void) memcpy(path, pwrk->phy->path, amt); 20604c06356bSdh 20614c06356bSdh /* 20624c06356bSdh * If this is a non-SCSA command, stop here. Eventually 20634c06356bSdh * we might do something with non-SCSA commands here- 20644c06356bSdh * but so far their timeout mechanisms are handled in 20654c06356bSdh * the WAIT_FOR macro. 20664c06356bSdh */ 20674c06356bSdh if (pwrk->xp == NULL) { 2068c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20694c06356bSdh "%s: non-SCSA cmd tag 0x%x timed out", 20704c06356bSdh path, pwrk->htag); 20714c06356bSdh mutex_exit(&pwrk->lock); 20724c06356bSdh continue; 20734c06356bSdh } 20744c06356bSdh 20754c06356bSdh sp = pwrk->arg; 20764c06356bSdh ASSERT(sp != NULL); 20774c06356bSdh 20784c06356bSdh /* 20794c06356bSdh * Mark it as timed out. 20804c06356bSdh */ 20814c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT; 20824c06356bSdh CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 20834c06356bSdh #ifdef DEBUG 2084c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 20854c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d", 20864c06356bSdh path, pwrk->htag, pwrk->state, pwrk->onwire); 20874c06356bSdh #else 2088c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 20894c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x)", 20904c06356bSdh path, pwrk->htag, pwrk->state); 20914c06356bSdh #endif 20924c06356bSdh /* 20934c06356bSdh * Mark the work structure as timed out. 20944c06356bSdh */ 20954c06356bSdh pwrk->state = PMCS_WORK_STATE_TIMED_OUT; 20964c06356bSdh phyp = pwrk->phy; 20974c06356bSdh target = pwrk->xp; 20984c06356bSdh mutex_exit(&pwrk->lock); 20994c06356bSdh 21004c06356bSdh pmcs_lock_phy(phyp); 21014c06356bSdh mutex_enter(&target->statlock); 21024c06356bSdh 21034c06356bSdh /* 21044c06356bSdh * No point attempting recovery if the device is gone 21054c06356bSdh */ 2106145e0143Sdh if (target->dev_gone) { 21074c06356bSdh mutex_exit(&target->statlock); 21084c06356bSdh pmcs_unlock_phy(phyp); 2109c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 21104c06356bSdh "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE " 21114c06356bSdh "for htag 0x%08x", __func__, 2112145e0143Sdh (void *)target, pwrk->htag); 21134c06356bSdh mutex_enter(&pwrk->lock); 21144c06356bSdh if (!PMCS_COMMAND_DONE(pwrk)) { 21154c06356bSdh /* Complete this command here */ 2116c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2117c3bc407cSdh "%s: Completing cmd (htag 0x%08x) " 21184c06356bSdh "anyway", __func__, pwrk->htag); 21194c06356bSdh pwrk->dead = 1; 21204c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 21214c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 21224c06356bSdh pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 21234c06356bSdh } else { 21244c06356bSdh mutex_exit(&pwrk->lock); 21254c06356bSdh } 21264c06356bSdh continue; 21274c06356bSdh } 21284c06356bSdh 2129601c90f1SSrikanth, Ramana mutex_exit(&target->statlock); 2130601c90f1SSrikanth, Ramana rval = pmcs_abort(pwp, phyp, pwrk->htag, 0, 1); 2131601c90f1SSrikanth, Ramana if (rval) { 2132601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2133601c90f1SSrikanth, Ramana "%s: Bad status (%d) on abort of HTAG 0x%08x", 2134601c90f1SSrikanth, Ramana __func__, rval, pwrk->htag); 21354c06356bSdh pmcs_unlock_phy(phyp); 2136601c90f1SSrikanth, Ramana mutex_enter(&pwrk->lock); 2137601c90f1SSrikanth, Ramana if (!PMCS_COMMAND_DONE(pwrk)) { 2138601c90f1SSrikanth, Ramana /* Complete this command here */ 2139601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2140601c90f1SSrikanth, Ramana "%s: Completing cmd (htag 0x%08x) " 2141601c90f1SSrikanth, Ramana "anyway", __func__, pwrk->htag); 2142601c90f1SSrikanth, Ramana if (target->dev_gone) { 2143601c90f1SSrikanth, Ramana pwrk->dead = 1; 2144601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 2145601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 2146601c90f1SSrikanth, Ramana } 2147601c90f1SSrikanth, Ramana pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 2148601c90f1SSrikanth, Ramana } else { 2149601c90f1SSrikanth, Ramana mutex_exit(&pwrk->lock); 2150601c90f1SSrikanth, Ramana } 2151601c90f1SSrikanth, Ramana pmcs_lock_phy(phyp); 2152601c90f1SSrikanth, Ramana /* 2153601c90f1SSrikanth, Ramana * No need to reschedule ABORT if we get any other 2154601c90f1SSrikanth, Ramana * status 2155601c90f1SSrikanth, Ramana */ 2156601c90f1SSrikanth, Ramana if (rval == ENOMEM) { 2157601c90f1SSrikanth, Ramana phyp->abort_sent = 0; 2158601c90f1SSrikanth, Ramana phyp->abort_pending = 1; 2159601c90f1SSrikanth, Ramana SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 2160601c90f1SSrikanth, Ramana } 21614c06356bSdh } 21624c06356bSdh pmcs_unlock_phy(phyp); 21634c06356bSdh } 21644c06356bSdh /* 21654c06356bSdh * Run any completions that may have been queued up. 21664c06356bSdh */ 21674c06356bSdh PMCS_CQ_RUN(pwp); 21684c06356bSdh } 21694c06356bSdh 21704c06356bSdh static void 21714c06356bSdh pmcs_watchdog(void *arg) 21724c06356bSdh { 21734c06356bSdh pmcs_hw_t *pwp = arg; 21744c06356bSdh 21754c06356bSdh DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t, 21764c06356bSdh pwp->config_changed); 21774c06356bSdh 21785c45adf0SJesse Butler /* 21795c45adf0SJesse Butler * Check forward progress on the chip 21805c45adf0SJesse Butler */ 21815c45adf0SJesse Butler if (++pwp->watchdog_count == PMCS_FWD_PROG_TRIGGER) { 21825c45adf0SJesse Butler pwp->watchdog_count = 0; 21835c45adf0SJesse Butler pmcs_check_forward_progress(pwp); 21845c45adf0SJesse Butler } 21855c45adf0SJesse Butler 21869aed1621SDavid Hollister /* 21879aed1621SDavid Hollister * Check to see if we need to kick discovery off again 21889aed1621SDavid Hollister */ 21899aed1621SDavid Hollister mutex_enter(&pwp->config_lock); 21909aed1621SDavid Hollister if (pwp->config_restart && 21919aed1621SDavid Hollister (ddi_get_lbolt() >= pwp->config_restart_time)) { 21929aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 21939aed1621SDavid Hollister "%s: Timer expired for re-enumeration: Start discovery", 21949aed1621SDavid Hollister __func__); 21959aed1621SDavid Hollister pwp->config_restart = B_FALSE; 21969aed1621SDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 21979aed1621SDavid Hollister } 21989aed1621SDavid Hollister mutex_exit(&pwp->config_lock); 21999aed1621SDavid Hollister 22004c06356bSdh mutex_enter(&pwp->lock); 22014c06356bSdh if (pwp->state != STATE_RUNNING) { 22024c06356bSdh mutex_exit(&pwp->lock); 22034c06356bSdh return; 22044c06356bSdh } 22054c06356bSdh 22064c06356bSdh if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) { 22074c06356bSdh if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp, 22084c06356bSdh DDI_NOSLEEP) != DDI_SUCCESS) { 2209c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22104c06356bSdh "Could not dispatch to worker thread"); 22114c06356bSdh } 22124c06356bSdh } 22134c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 22144c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 22155c45adf0SJesse Butler 22164c06356bSdh mutex_exit(&pwp->lock); 22175c45adf0SJesse Butler 22184c06356bSdh pmcs_check_commands(pwp); 22194c06356bSdh pmcs_handle_dead_phys(pwp); 22204c06356bSdh } 22214c06356bSdh 22224c06356bSdh static int 22234c06356bSdh pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt) 22244c06356bSdh { 22254c06356bSdh int i, r, rslt = 0; 22264c06356bSdh for (i = 0; i < icnt; i++) { 22274c06356bSdh r = ddi_intr_remove_handler(pwp->ih_table[i]); 22284c06356bSdh if (r == DDI_SUCCESS) { 22294c06356bSdh continue; 22304c06356bSdh } 2231c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22324c06356bSdh "%s: unable to remove interrupt handler %d", __func__, i); 22334c06356bSdh rslt = -1; 22344c06356bSdh break; 22354c06356bSdh } 22364c06356bSdh return (rslt); 22374c06356bSdh } 22384c06356bSdh 22394c06356bSdh static int 22404c06356bSdh pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt) 22414c06356bSdh { 22424c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 22434c06356bSdh int r = ddi_intr_block_disable(&pwp->ih_table[0], 22444c06356bSdh pwp->intr_cnt); 22454c06356bSdh if (r != DDI_SUCCESS) { 2246c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22474c06356bSdh "unable to disable interrupt block"); 22484c06356bSdh return (-1); 22494c06356bSdh } 22504c06356bSdh } else { 22514c06356bSdh int i; 22524c06356bSdh for (i = 0; i < icnt; i++) { 22534c06356bSdh if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) { 22544c06356bSdh continue; 22554c06356bSdh } 2256c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22574c06356bSdh "unable to disable interrupt %d", i); 22584c06356bSdh return (-1); 22594c06356bSdh } 22604c06356bSdh } 22614c06356bSdh return (0); 22624c06356bSdh } 22634c06356bSdh 22644c06356bSdh static int 22654c06356bSdh pmcs_free_intrs(pmcs_hw_t *pwp, int icnt) 22664c06356bSdh { 22674c06356bSdh int i; 22684c06356bSdh for (i = 0; i < icnt; i++) { 22694c06356bSdh if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) { 22704c06356bSdh continue; 22714c06356bSdh } 2272c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2273c3bc407cSdh "unable to free interrupt %d", i); 22744c06356bSdh return (-1); 22754c06356bSdh } 22764c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 22774c06356bSdh pwp->ih_table_size = 0; 22784c06356bSdh return (0); 22794c06356bSdh } 22804c06356bSdh 22814c06356bSdh /* 22824c06356bSdh * Try to set up interrupts of type "type" with a minimum number of interrupts 22834c06356bSdh * of "min". 22844c06356bSdh */ 22854c06356bSdh static void 22864c06356bSdh pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min) 22874c06356bSdh { 22884c06356bSdh int rval, avail, count, actual, max; 22894c06356bSdh 22904c06356bSdh rval = ddi_intr_get_nintrs(pwp->dip, type, &count); 22914c06356bSdh if ((rval != DDI_SUCCESS) || (count < min)) { 2292c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 22934c06356bSdh "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d", 22944c06356bSdh __func__, type, rval, count, min); 22954c06356bSdh return; 22964c06356bSdh } 22974c06356bSdh 2298c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 22994c06356bSdh "%s: nintrs = %d for type: %d", __func__, count, type); 23004c06356bSdh 23014c06356bSdh rval = ddi_intr_get_navail(pwp->dip, type, &avail); 23024c06356bSdh if ((rval != DDI_SUCCESS) || (avail < min)) { 2303c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23044c06356bSdh "%s: get_navail failed; type: %d rc: %d avail: %d min: %d", 23054c06356bSdh __func__, type, rval, avail, min); 23064c06356bSdh return; 23074c06356bSdh } 23084c06356bSdh 2309c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23104c06356bSdh "%s: navail = %d for type: %d", __func__, avail, type); 23114c06356bSdh 23124c06356bSdh pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t); 23134c06356bSdh pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP); 23144c06356bSdh 23154c06356bSdh switch (type) { 23164c06356bSdh case DDI_INTR_TYPE_MSIX: 23174c06356bSdh pwp->int_type = PMCS_INT_MSIX; 23184c06356bSdh max = PMCS_MAX_MSIX; 23194c06356bSdh break; 23204c06356bSdh case DDI_INTR_TYPE_MSI: 23214c06356bSdh pwp->int_type = PMCS_INT_MSI; 23224c06356bSdh max = PMCS_MAX_MSI; 23234c06356bSdh break; 23244c06356bSdh case DDI_INTR_TYPE_FIXED: 23254c06356bSdh default: 23264c06356bSdh pwp->int_type = PMCS_INT_FIXED; 23274c06356bSdh max = PMCS_MAX_FIXED; 23284c06356bSdh break; 23294c06356bSdh } 23304c06356bSdh 23314c06356bSdh rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual, 23324c06356bSdh DDI_INTR_ALLOC_NORMAL); 23334c06356bSdh if (rval != DDI_SUCCESS) { 2334c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23354c06356bSdh "%s: ddi_intr_alloc failed; type: %d rc: %d", 23364c06356bSdh __func__, type, rval); 23374c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 23384c06356bSdh pwp->ih_table = NULL; 23394c06356bSdh pwp->ih_table_size = 0; 23404c06356bSdh pwp->intr_cnt = 0; 23414c06356bSdh pwp->int_type = PMCS_INT_NONE; 23424c06356bSdh return; 23434c06356bSdh } 23444c06356bSdh 23454c06356bSdh pwp->intr_cnt = actual; 23464c06356bSdh } 23474c06356bSdh 23484c06356bSdh /* 23494c06356bSdh * Set up interrupts. 23504c06356bSdh * We return one of three values: 23514c06356bSdh * 23524c06356bSdh * 0 - success 23534c06356bSdh * EAGAIN - failure to set up interrupts 23544c06356bSdh * EIO - "" + we're now stuck partly enabled 23554c06356bSdh * 23564c06356bSdh * If EIO is returned, we can't unload the driver. 23574c06356bSdh */ 23584c06356bSdh static int 23594c06356bSdh pmcs_setup_intr(pmcs_hw_t *pwp) 23604c06356bSdh { 23614c06356bSdh int i, r, itypes, oqv_count; 23624c06356bSdh ddi_intr_handler_t **iv_table; 23634c06356bSdh size_t iv_table_size; 23644c06356bSdh uint_t pri; 23654c06356bSdh 23664c06356bSdh if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) { 2367c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2368c3bc407cSdh "cannot get interrupt types"); 23694c06356bSdh return (EAGAIN); 23704c06356bSdh } 23714c06356bSdh 23724c06356bSdh if (disable_msix) { 23734c06356bSdh itypes &= ~DDI_INTR_TYPE_MSIX; 23744c06356bSdh } 23754c06356bSdh if (disable_msi) { 23764c06356bSdh itypes &= ~DDI_INTR_TYPE_MSI; 23774c06356bSdh } 23784c06356bSdh 23794c06356bSdh /* 23804c06356bSdh * We won't know what firmware we're running until we call pmcs_setup, 23814c06356bSdh * and we can't call pmcs_setup until we establish interrupts. 23824c06356bSdh */ 23834c06356bSdh 23844c06356bSdh pwp->int_type = PMCS_INT_NONE; 23854c06356bSdh 23864c06356bSdh /* 23874c06356bSdh * We want PMCS_MAX_MSIX vectors for MSI-X. Anything less would be 23884c06356bSdh * uncivilized. 23894c06356bSdh */ 23904c06356bSdh if (itypes & DDI_INTR_TYPE_MSIX) { 23914c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX); 23924c06356bSdh if (pwp->int_type == PMCS_INT_MSIX) { 23934c06356bSdh itypes = 0; 23944c06356bSdh } 23954c06356bSdh } 23964c06356bSdh 23974c06356bSdh if (itypes & DDI_INTR_TYPE_MSI) { 23984c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1); 23994c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 24004c06356bSdh itypes = 0; 24014c06356bSdh } 24024c06356bSdh } 24034c06356bSdh 24044c06356bSdh if (itypes & DDI_INTR_TYPE_FIXED) { 24054c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1); 24064c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 24074c06356bSdh itypes = 0; 24084c06356bSdh } 24094c06356bSdh } 24104c06356bSdh 24114c06356bSdh if (pwp->intr_cnt == 0) { 2412c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 2413c3bc407cSdh "No interrupts available"); 24144c06356bSdh return (EAGAIN); 24154c06356bSdh } 24164c06356bSdh 24174c06356bSdh iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt; 24184c06356bSdh iv_table = kmem_alloc(iv_table_size, KM_SLEEP); 24194c06356bSdh 24204c06356bSdh /* 24214c06356bSdh * Get iblock cookie and add handlers. 24224c06356bSdh */ 24234c06356bSdh switch (pwp->intr_cnt) { 24244c06356bSdh case 1: 24254c06356bSdh iv_table[0] = pmcs_all_intr; 24264c06356bSdh break; 24274c06356bSdh case 2: 24284c06356bSdh iv_table[0] = pmcs_iodone_ix; 24294c06356bSdh iv_table[1] = pmcs_nonio_ix; 24304c06356bSdh break; 24314c06356bSdh case 4: 24324c06356bSdh iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix; 24334c06356bSdh iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix; 24344c06356bSdh iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix; 24354c06356bSdh iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix; 24364c06356bSdh break; 24374c06356bSdh default: 2438c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 24394c06356bSdh "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt); 24404c06356bSdh kmem_free(iv_table, iv_table_size); 24414c06356bSdh return (EAGAIN); 24424c06356bSdh } 24434c06356bSdh 24444c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 24454c06356bSdh r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i], 24464c06356bSdh (caddr_t)pwp, NULL); 24474c06356bSdh if (r != DDI_SUCCESS) { 24484c06356bSdh kmem_free(iv_table, iv_table_size); 24494c06356bSdh if (pmcs_remove_ihandlers(pwp, i)) { 24504c06356bSdh return (EIO); 24514c06356bSdh } 24524c06356bSdh if (pmcs_free_intrs(pwp, i)) { 24534c06356bSdh return (EIO); 24544c06356bSdh } 24554c06356bSdh pwp->intr_cnt = 0; 24564c06356bSdh return (EAGAIN); 24574c06356bSdh } 24584c06356bSdh } 24594c06356bSdh 24604c06356bSdh kmem_free(iv_table, iv_table_size); 24614c06356bSdh 24624c06356bSdh if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) { 2463c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2464c3bc407cSdh "unable to get int capabilities"); 24654c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 24664c06356bSdh return (EIO); 24674c06356bSdh } 24684c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 24694c06356bSdh return (EIO); 24704c06356bSdh } 24714c06356bSdh pwp->intr_cnt = 0; 24724c06356bSdh return (EAGAIN); 24734c06356bSdh } 24744c06356bSdh 24754c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 24764c06356bSdh r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt); 24774c06356bSdh if (r != DDI_SUCCESS) { 2478c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2479c3bc407cSdh "intr blk enable failed"); 24804c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 24814c06356bSdh return (EIO); 24824c06356bSdh } 24834c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 24844c06356bSdh return (EIO); 24854c06356bSdh } 24864c06356bSdh pwp->intr_cnt = 0; 24874c06356bSdh return (EFAULT); 24884c06356bSdh } 24894c06356bSdh } else { 24904c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 24914c06356bSdh r = ddi_intr_enable(pwp->ih_table[i]); 24924c06356bSdh if (r == DDI_SUCCESS) { 24934c06356bSdh continue; 24944c06356bSdh } 2495c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 24964c06356bSdh "unable to enable interrupt %d", i); 24974c06356bSdh if (pmcs_disable_intrs(pwp, i)) { 24984c06356bSdh return (EIO); 24994c06356bSdh } 25004c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25014c06356bSdh return (EIO); 25024c06356bSdh } 25034c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 25044c06356bSdh return (EIO); 25054c06356bSdh } 25064c06356bSdh pwp->intr_cnt = 0; 25074c06356bSdh return (EAGAIN); 25084c06356bSdh } 25094c06356bSdh } 25104c06356bSdh 25114c06356bSdh /* 25124c06356bSdh * Set up locks. 25134c06356bSdh */ 25144c06356bSdh if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) { 2515c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 25164c06356bSdh "unable to get interrupt priority"); 25174c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 25184c06356bSdh return (EIO); 25194c06356bSdh } 25204c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25214c06356bSdh return (EIO); 25224c06356bSdh } 25234c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 25244c06356bSdh return (EIO); 25254c06356bSdh } 25264c06356bSdh pwp->intr_cnt = 0; 25274c06356bSdh return (EAGAIN); 25284c06356bSdh } 25294c06356bSdh 25304c06356bSdh pwp->locks_initted = 1; 25314c06356bSdh pwp->intr_pri = pri; 25324c06356bSdh mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25334c06356bSdh mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25344c06356bSdh mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25354c06356bSdh mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25364c06356bSdh mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25374c06356bSdh mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25384c06356bSdh mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25394c06356bSdh mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25404c06356bSdh mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER, 25414c06356bSdh DDI_INTR_PRI(pri)); 25424c06356bSdh #ifdef DEBUG 25434c06356bSdh mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25444c06356bSdh #endif 25454c06356bSdh cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL); 25464c06356bSdh cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL); 2547*32b54db7SJesse Butler cv_init(&pwp->config_cv, NULL, CV_DRIVER, NULL); 25484c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 25494c06356bSdh mutex_init(&pwp->iqp_lock[i], NULL, 25504c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 25514c06356bSdh } 25524c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 25534c06356bSdh mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL, 25544c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 25554c06356bSdh cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL, 25564c06356bSdh CV_DRIVER, NULL); 25574c06356bSdh } 25584c06356bSdh 2559c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%d %s interrup%s configured", 25604c06356bSdh pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" : 25614c06356bSdh ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"), 25624c06356bSdh pwp->intr_cnt == 1? "t" : "ts"); 25634c06356bSdh 25644c06356bSdh 25654c06356bSdh /* 25664c06356bSdh * Enable Interrupts 25674c06356bSdh */ 25684c06356bSdh if (pwp->intr_cnt > PMCS_NOQ) { 25694c06356bSdh oqv_count = pwp->intr_cnt; 25704c06356bSdh } else { 25714c06356bSdh oqv_count = PMCS_NOQ; 25724c06356bSdh } 25734c06356bSdh for (pri = 0xffffffff, i = 0; i < oqv_count; i++) { 25744c06356bSdh pri ^= (1 << i); 25754c06356bSdh } 25764c06356bSdh 25774c06356bSdh mutex_enter(&pwp->lock); 25784c06356bSdh pwp->intr_mask = pri; 25794c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 25804c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 25814c06356bSdh mutex_exit(&pwp->lock); 25824c06356bSdh 25834c06356bSdh return (0); 25844c06356bSdh } 25854c06356bSdh 25864c06356bSdh static int 25874c06356bSdh pmcs_teardown_intr(pmcs_hw_t *pwp) 25884c06356bSdh { 25894c06356bSdh if (pwp->intr_cnt) { 25904c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 25914c06356bSdh return (EIO); 25924c06356bSdh } 25934c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25944c06356bSdh return (EIO); 25954c06356bSdh } 25964c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 25974c06356bSdh return (EIO); 25984c06356bSdh } 25994c06356bSdh pwp->intr_cnt = 0; 26004c06356bSdh } 26014c06356bSdh return (0); 26024c06356bSdh } 26034c06356bSdh 26044c06356bSdh static uint_t 26054c06356bSdh pmcs_general_ix(caddr_t arg1, caddr_t arg2) 26064c06356bSdh { 26074c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26084c06356bSdh _NOTE(ARGUNUSED(arg2)); 26094c06356bSdh pmcs_general_intr(pwp); 26104c06356bSdh return (DDI_INTR_CLAIMED); 26114c06356bSdh } 26124c06356bSdh 26134c06356bSdh static uint_t 26144c06356bSdh pmcs_event_ix(caddr_t arg1, caddr_t arg2) 26154c06356bSdh { 26164c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26174c06356bSdh _NOTE(ARGUNUSED(arg2)); 26184c06356bSdh pmcs_event_intr(pwp); 26194c06356bSdh return (DDI_INTR_CLAIMED); 26204c06356bSdh } 26214c06356bSdh 26224c06356bSdh static uint_t 26234c06356bSdh pmcs_iodone_ix(caddr_t arg1, caddr_t arg2) 26244c06356bSdh { 26254c06356bSdh _NOTE(ARGUNUSED(arg2)); 26264c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26274c06356bSdh 26284c06356bSdh /* 26294c06356bSdh * It's possible that if we just turned interrupt coalescing off 26304c06356bSdh * (and thus, re-enabled auto clear for interrupts on the I/O outbound 26314c06356bSdh * queue) that there was an interrupt already pending. We use 26324c06356bSdh * io_intr_coal.int_cleared to ensure that we still drop in here and 26334c06356bSdh * clear the appropriate interrupt bit one last time. 26344c06356bSdh */ 26354c06356bSdh mutex_enter(&pwp->ict_lock); 26364c06356bSdh if (pwp->io_intr_coal.timer_on || 26374c06356bSdh (pwp->io_intr_coal.int_cleared == B_FALSE)) { 26384c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 26394c06356bSdh (1 << PMCS_OQ_IODONE)); 26404c06356bSdh pwp->io_intr_coal.int_cleared = B_TRUE; 26414c06356bSdh } 26424c06356bSdh mutex_exit(&pwp->ict_lock); 26434c06356bSdh 26444c06356bSdh pmcs_iodone_intr(pwp); 26454c06356bSdh 26464c06356bSdh return (DDI_INTR_CLAIMED); 26474c06356bSdh } 26484c06356bSdh 26494c06356bSdh static uint_t 26504c06356bSdh pmcs_fatal_ix(caddr_t arg1, caddr_t arg2) 26514c06356bSdh { 26524c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26534c06356bSdh _NOTE(ARGUNUSED(arg2)); 26544c06356bSdh pmcs_fatal_handler(pwp); 26554c06356bSdh return (DDI_INTR_CLAIMED); 26564c06356bSdh } 26574c06356bSdh 26584c06356bSdh static uint_t 26594c06356bSdh pmcs_nonio_ix(caddr_t arg1, caddr_t arg2) 26604c06356bSdh { 26614c06356bSdh _NOTE(ARGUNUSED(arg2)); 26624c06356bSdh pmcs_hw_t *pwp = (void *)arg1; 26634c06356bSdh uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 26644c06356bSdh 26654c06356bSdh /* 26664c06356bSdh * Check for Fatal Interrupts 26674c06356bSdh */ 26684c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 26694c06356bSdh pmcs_fatal_handler(pwp); 26704c06356bSdh return (DDI_INTR_CLAIMED); 26714c06356bSdh } 26724c06356bSdh 26734c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 26744c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 26754c06356bSdh (1 << PMCS_OQ_GENERAL)); 26764c06356bSdh pmcs_general_intr(pwp); 26774c06356bSdh pmcs_event_intr(pwp); 26784c06356bSdh } 26794c06356bSdh 26804c06356bSdh return (DDI_INTR_CLAIMED); 26814c06356bSdh } 26824c06356bSdh 26834c06356bSdh static uint_t 26844c06356bSdh pmcs_all_intr(caddr_t arg1, caddr_t arg2) 26854c06356bSdh { 26864c06356bSdh _NOTE(ARGUNUSED(arg2)); 26874c06356bSdh pmcs_hw_t *pwp = (void *) arg1; 26884c06356bSdh uint32_t obdb; 26894c06356bSdh int handled = 0; 26904c06356bSdh 26914c06356bSdh obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 26924c06356bSdh 26934c06356bSdh /* 26944c06356bSdh * Check for Fatal Interrupts 26954c06356bSdh */ 26964c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 26974c06356bSdh pmcs_fatal_handler(pwp); 26984c06356bSdh return (DDI_INTR_CLAIMED); 26994c06356bSdh } 27004c06356bSdh 27014c06356bSdh /* 27024c06356bSdh * Check for Outbound Queue service needed 27034c06356bSdh */ 27044c06356bSdh if (obdb & (1 << PMCS_OQ_IODONE)) { 27054c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27064c06356bSdh (1 << PMCS_OQ_IODONE)); 27074c06356bSdh obdb ^= (1 << PMCS_OQ_IODONE); 27084c06356bSdh handled++; 27094c06356bSdh pmcs_iodone_intr(pwp); 27104c06356bSdh } 27114c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 27124c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27134c06356bSdh (1 << PMCS_OQ_GENERAL)); 27144c06356bSdh obdb ^= (1 << PMCS_OQ_GENERAL); 27154c06356bSdh handled++; 27164c06356bSdh pmcs_general_intr(pwp); 27174c06356bSdh } 27184c06356bSdh if (obdb & (1 << PMCS_OQ_EVENTS)) { 27194c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27204c06356bSdh (1 << PMCS_OQ_EVENTS)); 27214c06356bSdh obdb ^= (1 << PMCS_OQ_EVENTS); 27224c06356bSdh handled++; 27234c06356bSdh pmcs_event_intr(pwp); 27244c06356bSdh } 27254c06356bSdh if (obdb) { 2726c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 27274c06356bSdh "interrupt bits not handled (0x%x)", obdb); 27284c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb); 27294c06356bSdh handled++; 27304c06356bSdh } 27314c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 27324c06356bSdh handled++; 27334c06356bSdh } 27344c06356bSdh return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 27354c06356bSdh } 27364c06356bSdh 27374c06356bSdh void 27384c06356bSdh pmcs_fatal_handler(pmcs_hw_t *pwp) 27394c06356bSdh { 2740c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, "Fatal Interrupt caught"); 27415c45adf0SJesse Butler 27424c06356bSdh mutex_enter(&pwp->lock); 27434c06356bSdh pwp->state = STATE_DEAD; 27444c06356bSdh 27455c45adf0SJesse Butler /* 27465c45adf0SJesse Butler * Attempt a hot reset. In case of failure, pmcs_hot_reset() will 27475c45adf0SJesse Butler * handle the failure and issue any required FM notifications. 27485c45adf0SJesse Butler * See pmcs_subr.c for more details. 27495c45adf0SJesse Butler */ 27505c45adf0SJesse Butler if (pmcs_hot_reset(pwp)) { 27515c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 27525c45adf0SJesse Butler "%s: hot reset failure", __func__); 27535c45adf0SJesse Butler } else { 27545c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 27555c45adf0SJesse Butler "%s: hot reset complete", __func__); 27565c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_FATAL_ERROR; 27575c45adf0SJesse Butler } 27585c45adf0SJesse Butler mutex_exit(&pwp->lock); 27594c06356bSdh } 27604c06356bSdh 27614c06356bSdh /* 27624c06356bSdh * Called with PHY lock and target statlock held and scratch acquired. 27634c06356bSdh */ 27644c06356bSdh boolean_t 27654c06356bSdh pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt) 27664c06356bSdh { 27674c06356bSdh pmcs_phy_t *pptr = tgt->phy; 27684c06356bSdh 27694c06356bSdh switch (pptr->dtype) { 27704c06356bSdh case SAS: 27714c06356bSdh case EXPANDER: 27724c06356bSdh break; 27734c06356bSdh case SATA: 27744c06356bSdh tgt->ca = 1; 27754c06356bSdh break; 27764c06356bSdh default: 2777c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27784c06356bSdh "%s: Target %p has PHY %p with invalid dtype", 27794c06356bSdh __func__, (void *)tgt, (void *)pptr); 27804c06356bSdh return (B_FALSE); 27814c06356bSdh } 27824c06356bSdh 27834c06356bSdh tgt->new = 1; 27844c06356bSdh tgt->dev_gone = 0; 27854c06356bSdh tgt->recover_wait = 0; 27864c06356bSdh 2787c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27884c06356bSdh "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__, 27894c06356bSdh pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 27904c06356bSdh 27914c06356bSdh if (pmcs_add_new_device(pwp, tgt) != B_TRUE) { 2792c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27934c06356bSdh "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__, 27944c06356bSdh tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 27954c06356bSdh mutex_destroy(&tgt->statlock); 27964c06356bSdh mutex_destroy(&tgt->wqlock); 27974c06356bSdh mutex_destroy(&tgt->aqlock); 27984c06356bSdh return (B_FALSE); 27994c06356bSdh } 28004c06356bSdh 28014c06356bSdh return (B_TRUE); 28024c06356bSdh } 28034c06356bSdh 28044c06356bSdh /* 28054c06356bSdh * Called with softstate lock held 28064c06356bSdh */ 28074c06356bSdh void 28084c06356bSdh pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 28094c06356bSdh { 28104c06356bSdh pmcs_xscsi_t *xp; 28114c06356bSdh unsigned int vtgt; 28124c06356bSdh 28134c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 28144c06356bSdh 28154c06356bSdh for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) { 28164c06356bSdh xp = pwp->targets[vtgt]; 28174c06356bSdh if (xp == NULL) { 28184c06356bSdh continue; 28194c06356bSdh } 28204c06356bSdh 28214c06356bSdh mutex_enter(&xp->statlock); 28224c06356bSdh if (xp->phy == pptr) { 28234c06356bSdh if (xp->new) { 28244c06356bSdh xp->new = 0; 2825c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 28264c06356bSdh "cancel config of vtgt %u", vtgt); 28274c06356bSdh } else { 2828b18a19c2SJesse Butler pmcs_clear_xp(pwp, xp); 2829c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 2830b18a19c2SJesse Butler "Removed tgt 0x%p vtgt %u", 28314c06356bSdh (void *)xp, vtgt); 28324c06356bSdh } 28334c06356bSdh mutex_exit(&xp->statlock); 28344c06356bSdh break; 28354c06356bSdh } 28364c06356bSdh mutex_exit(&xp->statlock); 28374c06356bSdh } 28384c06356bSdh } 28394c06356bSdh 28404c06356bSdh void 2841c3bc407cSdh pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level, 2842c3bc407cSdh pmcs_phy_t *phyp, pmcs_xscsi_t *target, const char *fmt, ...) 28434c06356bSdh { 28444c06356bSdh va_list ap; 28454c06356bSdh int written = 0; 28464c06356bSdh char *ptr; 28474c06356bSdh uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1; 28484c06356bSdh boolean_t system_log; 28494c06356bSdh int system_log_level; 28504c06356bSdh 28514c06356bSdh switch (level) { 28524c06356bSdh case PMCS_PRT_DEBUG_DEVEL: 28534c06356bSdh case PMCS_PRT_DEBUG_DEV_STATE: 28544c06356bSdh case PMCS_PRT_DEBUG_PHY_LOCKING: 28554c06356bSdh case PMCS_PRT_DEBUG_SCSI_STATUS: 28564c06356bSdh case PMCS_PRT_DEBUG_UNDERFLOW: 28574c06356bSdh case PMCS_PRT_DEBUG_CONFIG: 28584c06356bSdh case PMCS_PRT_DEBUG_IPORT: 28594c06356bSdh case PMCS_PRT_DEBUG_MAP: 28604c06356bSdh case PMCS_PRT_DEBUG3: 28614c06356bSdh case PMCS_PRT_DEBUG2: 28624c06356bSdh case PMCS_PRT_DEBUG1: 28634c06356bSdh case PMCS_PRT_DEBUG: 28644c06356bSdh system_log = B_FALSE; 28654c06356bSdh break; 28664c06356bSdh case PMCS_PRT_INFO: 28674c06356bSdh system_log = B_TRUE; 28684c06356bSdh system_log_level = CE_CONT; 28694c06356bSdh break; 28704c06356bSdh case PMCS_PRT_WARN: 28714c06356bSdh system_log = B_TRUE; 28724c06356bSdh system_log_level = CE_NOTE; 28734c06356bSdh break; 28744c06356bSdh case PMCS_PRT_ERR: 28754c06356bSdh system_log = B_TRUE; 28764c06356bSdh system_log_level = CE_WARN; 28774c06356bSdh break; 28784c06356bSdh default: 28794c06356bSdh return; 28804c06356bSdh } 28814c06356bSdh 28824c06356bSdh mutex_enter(&pmcs_trace_lock); 28834c06356bSdh gethrestime(&pmcs_tbuf_ptr->timestamp); 28844c06356bSdh ptr = pmcs_tbuf_ptr->buf; 2885c3bc407cSdh 2886c3bc407cSdh /* 2887c3bc407cSdh * Store the pertinent PHY and target information if there is any 2888c3bc407cSdh */ 2889c3bc407cSdh if (target == NULL) { 2890c3bc407cSdh pmcs_tbuf_ptr->target_num = PMCS_INVALID_TARGET_NUM; 2891c3bc407cSdh pmcs_tbuf_ptr->target_ua[0] = '\0'; 2892c3bc407cSdh } else { 2893c3bc407cSdh pmcs_tbuf_ptr->target_num = target->target_num; 2894c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->target_ua, target->ua, 2895c3bc407cSdh PMCS_TBUF_UA_MAX_SIZE); 2896c3bc407cSdh } 2897c3bc407cSdh 2898c3bc407cSdh if (phyp == NULL) { 2899c3bc407cSdh (void) memset(pmcs_tbuf_ptr->phy_sas_address, 0, 8); 2900c3bc407cSdh pmcs_tbuf_ptr->phy_path[0] = '\0'; 2901c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = NOTHING; 2902c3bc407cSdh } else { 2903c3bc407cSdh (void) memcpy(pmcs_tbuf_ptr->phy_sas_address, 2904c3bc407cSdh phyp->sas_address, 8); 2905c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->phy_path, phyp->path, 32); 2906c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = phyp->dtype; 2907c3bc407cSdh } 2908c3bc407cSdh 29094c06356bSdh written += snprintf(ptr, elem_size, "pmcs%d:%d: ", 29104c06356bSdh ddi_get_instance(pwp->dip), level); 29114c06356bSdh ptr += strlen(ptr); 29124c06356bSdh va_start(ap, fmt); 29134c06356bSdh written += vsnprintf(ptr, elem_size - written, fmt, ap); 29144c06356bSdh va_end(ap); 29154c06356bSdh if (written > elem_size - 1) { 29164c06356bSdh /* Indicate truncation */ 29174c06356bSdh pmcs_tbuf_ptr->buf[elem_size - 1] = '+'; 29184c06356bSdh } 29194c06356bSdh if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) { 29204c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 29214c06356bSdh pmcs_tbuf_wrap = B_TRUE; 29224c06356bSdh pmcs_tbuf_idx = 0; 29234c06356bSdh } else { 29244c06356bSdh ++pmcs_tbuf_ptr; 29254c06356bSdh } 29264c06356bSdh mutex_exit(&pmcs_trace_lock); 29274c06356bSdh 29284c06356bSdh /* 29294c06356bSdh * When pmcs_force_syslog in non-zero, everything goes also 29304c06356bSdh * to syslog, at CE_CONT level. 29314c06356bSdh */ 29324c06356bSdh if (pmcs_force_syslog) { 29334c06356bSdh system_log = B_TRUE; 29344c06356bSdh system_log_level = CE_CONT; 29354c06356bSdh } 29364c06356bSdh 29374c06356bSdh /* 29384c06356bSdh * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also 29394c06356bSdh * goes to syslog. 29404c06356bSdh */ 29414c06356bSdh if (system_log) { 29424c06356bSdh char local[196]; 29434c06356bSdh 29444c06356bSdh switch (system_log_level) { 29454c06356bSdh case CE_CONT: 29464c06356bSdh (void) snprintf(local, sizeof (local), "%sINFO: ", 29474c06356bSdh pmcs_console ? "" : "?"); 29484c06356bSdh break; 29494c06356bSdh case CE_NOTE: 29504c06356bSdh case CE_WARN: 29514c06356bSdh local[0] = 0; 29524c06356bSdh break; 29534c06356bSdh default: 29544c06356bSdh return; 29554c06356bSdh } 29564c06356bSdh 29574c06356bSdh ptr = local; 29584c06356bSdh ptr += strlen(local); 29594c06356bSdh (void) snprintf(ptr, (sizeof (local)) - 29604c06356bSdh ((size_t)ptr - (size_t)local), "pmcs%d: ", 29614c06356bSdh ddi_get_instance(pwp->dip)); 29624c06356bSdh ptr += strlen(ptr); 29634c06356bSdh va_start(ap, fmt); 29644c06356bSdh (void) vsnprintf(ptr, 29654c06356bSdh (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap); 29664c06356bSdh va_end(ap); 29674c06356bSdh if (level == CE_CONT) { 29684c06356bSdh (void) strlcat(local, "\n", sizeof (local)); 29694c06356bSdh } 29704c06356bSdh cmn_err(system_log_level, local); 29714c06356bSdh } 29724c06356bSdh 29734c06356bSdh } 29744c06356bSdh 29754c06356bSdh /* 29764c06356bSdh * pmcs_acquire_scratch 29774c06356bSdh * 29784c06356bSdh * If "wait" is true, the caller will wait until it can acquire the scratch. 29794c06356bSdh * This implies the caller needs to be in a context where spinning for an 29804c06356bSdh * indeterminate amount of time is acceptable. 29814c06356bSdh */ 29824c06356bSdh int 29834c06356bSdh pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait) 29844c06356bSdh { 29854c06356bSdh int rval; 29864c06356bSdh 29874c06356bSdh if (!wait) { 29884c06356bSdh return (atomic_swap_8(&pwp->scratch_locked, 1)); 29894c06356bSdh } 29904c06356bSdh 29914c06356bSdh /* 29924c06356bSdh * Caller will wait for scratch. 29934c06356bSdh */ 29944c06356bSdh while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) { 29954c06356bSdh drv_usecwait(100); 29964c06356bSdh } 29974c06356bSdh 29984c06356bSdh return (rval); 29994c06356bSdh } 30004c06356bSdh 30014c06356bSdh void 30024c06356bSdh pmcs_release_scratch(pmcs_hw_t *pwp) 30034c06356bSdh { 30044c06356bSdh pwp->scratch_locked = 0; 30054c06356bSdh } 30064c06356bSdh 300735dae232SSrikanth Suravajhala /* Called with iport_lock and phy lock held */ 300835dae232SSrikanth Suravajhala void 300935dae232SSrikanth Suravajhala pmcs_create_one_phy_stats(pmcs_iport_t *iport, pmcs_phy_t *phyp) 30104c06356bSdh { 30114c06356bSdh sas_phy_stats_t *ps; 30124c06356bSdh pmcs_hw_t *pwp; 30134c06356bSdh int ndata; 30144c06356bSdh char ks_name[KSTAT_STRLEN]; 30154c06356bSdh 301635dae232SSrikanth Suravajhala ASSERT(mutex_owned(&iport->lock)); 301735dae232SSrikanth Suravajhala pwp = iport->pwp; 301835dae232SSrikanth Suravajhala ASSERT(pwp != NULL); 301935dae232SSrikanth Suravajhala ASSERT(mutex_owned(&phyp->phy_lock)); 302035dae232SSrikanth Suravajhala 302135dae232SSrikanth Suravajhala if (phyp->phy_stats != NULL) { 302235dae232SSrikanth Suravajhala /* 302335dae232SSrikanth Suravajhala * Delete existing kstats with name containing 302435dae232SSrikanth Suravajhala * old iport instance# and allow creation of 302535dae232SSrikanth Suravajhala * new kstats with new iport instance# in the name. 302635dae232SSrikanth Suravajhala */ 302735dae232SSrikanth Suravajhala kstat_delete(phyp->phy_stats); 302835dae232SSrikanth Suravajhala } 302935dae232SSrikanth Suravajhala 303035dae232SSrikanth Suravajhala ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t)); 303135dae232SSrikanth Suravajhala 303235dae232SSrikanth Suravajhala (void) snprintf(ks_name, sizeof (ks_name), 303335dae232SSrikanth Suravajhala "%s.%llx.%d.%d", ddi_driver_name(iport->dip), 303435dae232SSrikanth Suravajhala (longlong_t)pwp->sas_wwns[0], 303535dae232SSrikanth Suravajhala ddi_get_instance(iport->dip), phyp->phynum); 303635dae232SSrikanth Suravajhala 303735dae232SSrikanth Suravajhala phyp->phy_stats = kstat_create("pmcs", 303835dae232SSrikanth Suravajhala ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS, 303935dae232SSrikanth Suravajhala KSTAT_TYPE_NAMED, ndata, 0); 304035dae232SSrikanth Suravajhala 304135dae232SSrikanth Suravajhala if (phyp->phy_stats == NULL) { 304235dae232SSrikanth Suravajhala pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 304335dae232SSrikanth Suravajhala "%s: Failed to create %s kstats for PHY(0x%p) at %s", 304435dae232SSrikanth Suravajhala __func__, ks_name, (void *)phyp, phyp->path); 304535dae232SSrikanth Suravajhala } 304635dae232SSrikanth Suravajhala 304735dae232SSrikanth Suravajhala ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data; 304835dae232SSrikanth Suravajhala 304935dae232SSrikanth Suravajhala kstat_named_init(&ps->seconds_since_last_reset, 305035dae232SSrikanth Suravajhala "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG); 305135dae232SSrikanth Suravajhala kstat_named_init(&ps->tx_frames, 305235dae232SSrikanth Suravajhala "TxFrames", KSTAT_DATA_ULONGLONG); 305335dae232SSrikanth Suravajhala kstat_named_init(&ps->rx_frames, 305435dae232SSrikanth Suravajhala "RxFrames", KSTAT_DATA_ULONGLONG); 305535dae232SSrikanth Suravajhala kstat_named_init(&ps->tx_words, 305635dae232SSrikanth Suravajhala "TxWords", KSTAT_DATA_ULONGLONG); 305735dae232SSrikanth Suravajhala kstat_named_init(&ps->rx_words, 305835dae232SSrikanth Suravajhala "RxWords", KSTAT_DATA_ULONGLONG); 305935dae232SSrikanth Suravajhala kstat_named_init(&ps->invalid_dword_count, 306035dae232SSrikanth Suravajhala "InvalidDwordCount", KSTAT_DATA_ULONGLONG); 306135dae232SSrikanth Suravajhala kstat_named_init(&ps->running_disparity_error_count, 306235dae232SSrikanth Suravajhala "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG); 306335dae232SSrikanth Suravajhala kstat_named_init(&ps->loss_of_dword_sync_count, 306435dae232SSrikanth Suravajhala "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG); 306535dae232SSrikanth Suravajhala kstat_named_init(&ps->phy_reset_problem_count, 306635dae232SSrikanth Suravajhala "PhyResetProblemCount", KSTAT_DATA_ULONGLONG); 306735dae232SSrikanth Suravajhala 306835dae232SSrikanth Suravajhala phyp->phy_stats->ks_private = phyp; 306935dae232SSrikanth Suravajhala phyp->phy_stats->ks_update = pmcs_update_phy_stats; 307035dae232SSrikanth Suravajhala kstat_install(phyp->phy_stats); 307135dae232SSrikanth Suravajhala } 307235dae232SSrikanth Suravajhala 307335dae232SSrikanth Suravajhala static void 307435dae232SSrikanth Suravajhala pmcs_create_all_phy_stats(pmcs_iport_t *iport) 307535dae232SSrikanth Suravajhala { 307635dae232SSrikanth Suravajhala pmcs_hw_t *pwp; 307735dae232SSrikanth Suravajhala pmcs_phy_t *phyp; 307835dae232SSrikanth Suravajhala 30794c06356bSdh ASSERT(iport != NULL); 30804c06356bSdh pwp = iport->pwp; 30814c06356bSdh ASSERT(pwp != NULL); 30824c06356bSdh 30834c06356bSdh mutex_enter(&iport->lock); 30844c06356bSdh 30854c06356bSdh for (phyp = list_head(&iport->phys); 30864c06356bSdh phyp != NULL; 30874c06356bSdh phyp = list_next(&iport->phys, phyp)) { 30884c06356bSdh 308935dae232SSrikanth Suravajhala mutex_enter(&phyp->phy_lock); 309035dae232SSrikanth Suravajhala pmcs_create_one_phy_stats(iport, phyp); 309135dae232SSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 30924c06356bSdh } 30934c06356bSdh 30944c06356bSdh mutex_exit(&iport->lock); 30954c06356bSdh } 30964c06356bSdh 30974c06356bSdh int 30984c06356bSdh pmcs_update_phy_stats(kstat_t *ks, int rw) 30994c06356bSdh { 31004c06356bSdh int val, ret = DDI_FAILURE; 31014c06356bSdh pmcs_phy_t *pptr = (pmcs_phy_t *)ks->ks_private; 31024c06356bSdh pmcs_hw_t *pwp = pptr->pwp; 31034c06356bSdh sas_phy_stats_t *ps = ks->ks_data; 31044c06356bSdh 31054c06356bSdh _NOTE(ARGUNUSED(rw)); 31064c06356bSdh ASSERT((pptr != NULL) && (pwp != NULL)); 31074c06356bSdh 31084c06356bSdh /* 31094c06356bSdh * We just want to lock against other invocations of kstat; 31104c06356bSdh * we don't need to pmcs_lock_phy() for this. 31114c06356bSdh */ 31124c06356bSdh mutex_enter(&pptr->phy_lock); 31134c06356bSdh 31144c06356bSdh /* Get Stats from Chip */ 31154c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum); 31164c06356bSdh if (val == DDI_FAILURE) 31174c06356bSdh goto fail; 31184c06356bSdh ps->invalid_dword_count.value.ull = (unsigned long long)val; 31194c06356bSdh 31204c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum); 31214c06356bSdh if (val == DDI_FAILURE) 31224c06356bSdh goto fail; 31234c06356bSdh ps->running_disparity_error_count.value.ull = (unsigned long long)val; 31244c06356bSdh 31254c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum); 31264c06356bSdh if (val == DDI_FAILURE) 31274c06356bSdh goto fail; 31284c06356bSdh ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val; 31294c06356bSdh 31304c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum); 31314c06356bSdh if (val == DDI_FAILURE) 31324c06356bSdh goto fail; 31334c06356bSdh ps->phy_reset_problem_count.value.ull = (unsigned long long)val; 31344c06356bSdh 31354c06356bSdh ret = DDI_SUCCESS; 31364c06356bSdh fail: 31374c06356bSdh mutex_exit(&pptr->phy_lock); 31384c06356bSdh return (ret); 31394c06356bSdh } 31404c06356bSdh 31414c06356bSdh static void 31424c06356bSdh pmcs_destroy_phy_stats(pmcs_iport_t *iport) 31434c06356bSdh { 31444c06356bSdh pmcs_phy_t *phyp; 31454c06356bSdh 31464c06356bSdh ASSERT(iport != NULL); 31474c06356bSdh mutex_enter(&iport->lock); 31484c06356bSdh phyp = iport->pptr; 31494c06356bSdh if (phyp == NULL) { 31504c06356bSdh mutex_exit(&iport->lock); 31514c06356bSdh return; 31524c06356bSdh } 31534c06356bSdh 315435dae232SSrikanth Suravajhala for (phyp = list_head(&iport->phys); 315535dae232SSrikanth Suravajhala phyp != NULL; 315635dae232SSrikanth Suravajhala phyp = list_next(&iport->phys, phyp)) { 315735dae232SSrikanth Suravajhala 315835dae232SSrikanth Suravajhala mutex_enter(&phyp->phy_lock); 315935dae232SSrikanth Suravajhala if (phyp->phy_stats != NULL) { 316035dae232SSrikanth Suravajhala kstat_delete(phyp->phy_stats); 316135dae232SSrikanth Suravajhala phyp->phy_stats = NULL; 316235dae232SSrikanth Suravajhala } 316335dae232SSrikanth Suravajhala mutex_exit(&phyp->phy_lock); 31644c06356bSdh } 31654c06356bSdh 31664c06356bSdh mutex_exit(&iport->lock); 31674c06356bSdh } 31684c06356bSdh 31694c06356bSdh /*ARGSUSED*/ 31704c06356bSdh static int 31714c06356bSdh pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) 31724c06356bSdh { 31734c06356bSdh /* 31744c06356bSdh * as the driver can always deal with an error in any dma or 31754c06356bSdh * access handle, we can just return the fme_status value. 31764c06356bSdh */ 31774c06356bSdh pci_ereport_post(dip, err, NULL); 31784c06356bSdh return (err->fme_status); 31794c06356bSdh } 31804c06356bSdh 31814c06356bSdh static void 31824c06356bSdh pmcs_fm_init(pmcs_hw_t *pwp) 31834c06356bSdh { 31844c06356bSdh ddi_iblock_cookie_t fm_ibc; 31854c06356bSdh 31864c06356bSdh /* Only register with IO Fault Services if we have some capability */ 31874c06356bSdh if (pwp->fm_capabilities) { 31884c06356bSdh /* Adjust access and dma attributes for FMA */ 3189837c1ac4SStephen Hanson pwp->reg_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC; 31904c06356bSdh pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31914c06356bSdh pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31924c06356bSdh pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31934c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31944c06356bSdh 31954c06356bSdh /* 31964c06356bSdh * Register capabilities with IO Fault Services. 31974c06356bSdh */ 31984c06356bSdh ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc); 31994c06356bSdh 32004c06356bSdh /* 32014c06356bSdh * Initialize pci ereport capabilities if ereport 32024c06356bSdh * capable (should always be.) 32034c06356bSdh */ 32044c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 32054c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32064c06356bSdh pci_ereport_setup(pwp->dip); 32074c06356bSdh } 32084c06356bSdh 32094c06356bSdh /* 32104c06356bSdh * Register error callback if error callback capable. 32114c06356bSdh */ 32124c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32134c06356bSdh ddi_fm_handler_register(pwp->dip, 32144c06356bSdh pmcs_fm_error_cb, (void *) pwp); 32154c06356bSdh } 32164c06356bSdh } 32174c06356bSdh } 32184c06356bSdh 32194c06356bSdh static void 32204c06356bSdh pmcs_fm_fini(pmcs_hw_t *pwp) 32214c06356bSdh { 32224c06356bSdh /* Only unregister FMA capabilities if registered */ 32234c06356bSdh if (pwp->fm_capabilities) { 32244c06356bSdh /* 32254c06356bSdh * Un-register error callback if error callback capable. 32264c06356bSdh */ 32274c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32284c06356bSdh ddi_fm_handler_unregister(pwp->dip); 32294c06356bSdh } 32304c06356bSdh 32314c06356bSdh /* 32324c06356bSdh * Release any resources allocated by pci_ereport_setup() 32334c06356bSdh */ 32344c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 32354c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32364c06356bSdh pci_ereport_teardown(pwp->dip); 32374c06356bSdh } 32384c06356bSdh 32394c06356bSdh /* Unregister from IO Fault Services */ 32404c06356bSdh ddi_fm_fini(pwp->dip); 32414c06356bSdh 32424c06356bSdh /* Adjust access and dma attributes for FMA */ 3243837c1ac4SStephen Hanson pwp->reg_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC; 32444c06356bSdh pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32454c06356bSdh pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32464c06356bSdh pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32474c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32484c06356bSdh } 32494c06356bSdh } 32504c06356bSdh 32514c06356bSdh static boolean_t 32524c06356bSdh pmcs_fabricate_wwid(pmcs_hw_t *pwp) 32534c06356bSdh { 32544c06356bSdh char *cp, c; 32554c06356bSdh uint64_t adr; 32564c06356bSdh int i; 32574c06356bSdh 32584c06356bSdh cp = &c; 32594c06356bSdh (void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr); 32606745c559SJesse Butler 32614c06356bSdh if (adr == 0) { 3262c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 32634c06356bSdh "%s: No serial number available to fabricate WWN", 32644c06356bSdh __func__); 32656745c559SJesse Butler 32666745c559SJesse Butler adr = (uint64_t)gethrtime(); 32674c06356bSdh } 32686745c559SJesse Butler 32694c06356bSdh adr <<= 8; 32704c06356bSdh adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52); 32714c06356bSdh adr |= (5ULL << 60); 32726745c559SJesse Butler 32734c06356bSdh for (i = 0; i < PMCS_MAX_PORTS; i++) { 32744c06356bSdh pwp->sas_wwns[i] = adr + i; 32754c06356bSdh } 32764c06356bSdh 32774c06356bSdh return (B_TRUE); 32784c06356bSdh } 3279