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 20*658280b6SDavid Hollister */ 21*658280b6SDavid Hollister /* 22*658280b6SDavid 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 1144c06356bSdh static void pmcs_create_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 */ 3774c06356bSdh pmcs_create_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); 5044c06356bSdh /* 5054c06356bSdh * Create the list for iports 5064c06356bSdh */ 5074c06356bSdh list_create(&pwp->iports, sizeof (pmcs_iport_t), 5084c06356bSdh offsetof(pmcs_iport_t, list_node)); 5094c06356bSdh 5104c06356bSdh pwp->state = STATE_PROBING; 5114c06356bSdh 5124c06356bSdh /* 5134c06356bSdh * Get driver.conf properties 5144c06356bSdh */ 5154c06356bSdh pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5164c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask", 5174c06356bSdh debug_mask); 5184c06356bSdh pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5194c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask", 5204c06356bSdh block_mask); 5214c06356bSdh pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5224c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed); 5234c06356bSdh pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5244c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode); 5254c06356bSdh pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5264c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level); 5274c06356bSdh if (pwp->fwlog > PMCS_FWLOG_MAX) { 5284c06356bSdh pwp->fwlog = PMCS_FWLOG_MAX; 5294c06356bSdh } 5309719310aSDavid Hollister if ((ddi_prop_lookup_string(DDI_DEV_T_ANY, dip, 0, "pmcs-fwlogfile", 5319719310aSDavid Hollister &fwl_file) == DDI_SUCCESS)) { 5329719310aSDavid Hollister if (snprintf(pwp->fwlogfile_aap1, MAXPATHLEN, "%s%d-aap1.0", 5339719310aSDavid Hollister fwl_file, ddi_get_instance(dip)) > MAXPATHLEN) { 5349719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5359719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5369719310aSDavid Hollister } else if (snprintf(pwp->fwlogfile_iop, MAXPATHLEN, 5379719310aSDavid Hollister "%s%d-iop.0", fwl_file, 5389719310aSDavid Hollister ddi_get_instance(dip)) > MAXPATHLEN) { 5399719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5409719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5419719310aSDavid Hollister } 5429719310aSDavid Hollister ddi_prop_free(fwl_file); 5439719310aSDavid Hollister } else { 5449719310aSDavid Hollister pwp->fwlogfile_aap1[0] = '\0'; 5459719310aSDavid Hollister pwp->fwlogfile_iop[0] = '\0'; 5469719310aSDavid Hollister } 5474c06356bSdh 548*658280b6SDavid Hollister pwp->open_retry_interval = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 549*658280b6SDavid Hollister DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-open-retry-interval", 550*658280b6SDavid Hollister OPEN_RETRY_INTERVAL_DEF); 551*658280b6SDavid Hollister if (pwp->open_retry_interval > OPEN_RETRY_INTERVAL_MAX) { 552*658280b6SDavid Hollister pwp->open_retry_interval = OPEN_RETRY_INTERVAL_MAX; 553*658280b6SDavid Hollister } 554*658280b6SDavid Hollister 5554c06356bSdh mutex_enter(&pmcs_trace_lock); 5564c06356bSdh if (pmcs_tbuf == NULL) { 5574c06356bSdh /* Allocate trace buffer */ 5584c06356bSdh pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5594c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems", 5604c06356bSdh PMCS_TBUF_NUM_ELEMS_DEF); 5614c06356bSdh if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) || 5624c06356bSdh (pmcs_tbuf_num_elems == 0)) { 5634c06356bSdh pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF; 5644c06356bSdh } 5654c06356bSdh 5664c06356bSdh pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems * 5674c06356bSdh sizeof (pmcs_tbuf_t), KM_SLEEP); 5684c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 5694c06356bSdh pmcs_tbuf_idx = 0; 5704c06356bSdh } 5714c06356bSdh mutex_exit(&pmcs_trace_lock); 5724c06356bSdh 5739719310aSDavid Hollister if (pwp->fwlog && strlen(pwp->fwlogfile_aap1) > 0) { 5749719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5759719310aSDavid Hollister "%s: firmware event log files: %s, %s", __func__, 5769719310aSDavid Hollister pwp->fwlogfile_aap1, pwp->fwlogfile_iop); 5779719310aSDavid Hollister pwp->fwlog_file = 1; 5789719310aSDavid Hollister } else { 5799719310aSDavid Hollister if (pwp->fwlog == 0) { 5809719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5819719310aSDavid Hollister "%s: No firmware event log will be written " 5829719310aSDavid Hollister "(event log disabled)", __func__); 5839719310aSDavid Hollister } else { 5849719310aSDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5859719310aSDavid Hollister "%s: No firmware event log will be written " 5869719310aSDavid Hollister "(no filename configured - too long?)", __func__); 5879719310aSDavid Hollister } 5889719310aSDavid Hollister pwp->fwlog_file = 0; 5899719310aSDavid Hollister } 5909719310aSDavid Hollister 5914c06356bSdh disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5924c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix", 5934c06356bSdh disable_msix); 5944c06356bSdh disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5954c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi", 5964c06356bSdh disable_msi); 5974c06356bSdh maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5984c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth); 5994c06356bSdh pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6004c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0); 6014c06356bSdh if (pwp->fw_force_update == 0) { 6024c06356bSdh pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6034c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 6044c06356bSdh "pmcs-fw-disable-update", 0); 6054c06356bSdh } 6064c06356bSdh pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 6074c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries", 6084c06356bSdh PMCS_NQENTRY); 6094c06356bSdh 6104c06356bSdh /* 6114c06356bSdh * Initialize FMA 6124c06356bSdh */ 6134c06356bSdh pwp->dev_acc_attr = pwp->reg_acc_attr = rattr; 6144c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = 6154c06356bSdh pwp->regdump_dma_attr = pwp->cip_dma_attr = 6164c06356bSdh pwp->fwlog_dma_attr = pmcs_dattr; 6174c06356bSdh pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip, 6184c06356bSdh DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable", 6194c06356bSdh DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | 6204c06356bSdh DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE); 6214c06356bSdh pmcs_fm_init(pwp); 6224c06356bSdh 6234c06356bSdh /* 6244c06356bSdh * Map registers 6254c06356bSdh */ 6264c06356bSdh if (pci_config_setup(dip, &pwp->pci_acc_handle)) { 627c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 628c3bc407cSdh "pci config setup failed"); 6294c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6304c06356bSdh return (DDI_FAILURE); 6314c06356bSdh } 6324c06356bSdh 6334c06356bSdh /* 6344c06356bSdh * Get the size of register set 3. 6354c06356bSdh */ 6364c06356bSdh if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) { 637c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6384c06356bSdh "unable to get size of register set %d", PMCS_REGSET_3); 6394c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6404c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6414c06356bSdh return (DDI_FAILURE); 6424c06356bSdh } 6434c06356bSdh 6444c06356bSdh /* 6454c06356bSdh * Map registers 6464c06356bSdh */ 6474c06356bSdh pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 6484c06356bSdh 6494c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs, 6504c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) { 651c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6524c06356bSdh "failed to map Message Unit registers"); 6534c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6544c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6554c06356bSdh return (DDI_FAILURE); 6564c06356bSdh } 6574c06356bSdh 6584c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs, 6594c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) { 660c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 661c3bc407cSdh "failed to map TOP registers"); 6624c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6634c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6644c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6654c06356bSdh return (DDI_FAILURE); 6664c06356bSdh } 6674c06356bSdh 6684c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs, 6694c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) { 670c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 671c3bc407cSdh "failed to map GSM registers"); 6724c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6734c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6744c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6754c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6764c06356bSdh return (DDI_FAILURE); 6774c06356bSdh } 6784c06356bSdh 6794c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs, 6804c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) { 681c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 682c3bc407cSdh "failed to map MPI registers"); 6834c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6844c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 6854c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6864c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6874c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6884c06356bSdh return (DDI_FAILURE); 6894c06356bSdh } 6904c06356bSdh pwp->mpibar = 6914c06356bSdh (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size; 6924c06356bSdh 6934c06356bSdh /* 6944c06356bSdh * Make sure we can support this card. 6954c06356bSdh */ 6964c06356bSdh pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION); 6974c06356bSdh 6984c06356bSdh switch (pwp->chiprev) { 6994c06356bSdh case PMCS_PM8001_REV_A: 7004c06356bSdh case PMCS_PM8001_REV_B: 701c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 7024c06356bSdh "Rev A/B Card no longer supported"); 7034c06356bSdh goto failure; 7044c06356bSdh case PMCS_PM8001_REV_C: 7054c06356bSdh break; 7064c06356bSdh default: 707c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 7084c06356bSdh "Unknown chip revision (%d)", pwp->chiprev); 7094c06356bSdh goto failure; 7104c06356bSdh } 7114c06356bSdh 7124c06356bSdh /* 7134c06356bSdh * Allocate DMA addressable area for Inbound and Outbound Queue indices 7144c06356bSdh * that the chip needs to access plus a space for scratch usage 7154c06356bSdh */ 7164c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 7174c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls, 7184c06356bSdh &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip, 7194c06356bSdh &pwp->ciaddr) == B_FALSE) { 720c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7214c06356bSdh "Failed to setup DMA for index/scratch"); 7224c06356bSdh goto failure; 7234c06356bSdh } 7244c06356bSdh 7254c06356bSdh bzero(pwp->cip, ptob(1)); 7264c06356bSdh pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE]; 7274c06356bSdh pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE; 7284c06356bSdh 7294c06356bSdh /* 7304c06356bSdh * Allocate DMA S/G list chunks 7314c06356bSdh */ 7324c06356bSdh (void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES); 7334c06356bSdh 7344c06356bSdh /* 7354c06356bSdh * Allocate a DMA addressable area for the firmware log (if needed) 7364c06356bSdh */ 7374c06356bSdh if (pwp->fwlog) { 7384c06356bSdh /* 7394c06356bSdh * Align to event log header and entry size 7404c06356bSdh */ 7414c06356bSdh pwp->fwlog_dma_attr.dma_attr_align = 32; 7424c06356bSdh if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr, 7434c06356bSdh &pwp->fwlog_acchdl, 7444c06356bSdh &pwp->fwlog_hndl, PMCS_FWLOG_SIZE, 7454c06356bSdh (caddr_t *)&pwp->fwlogp, 7464c06356bSdh &pwp->fwaddr) == B_FALSE) { 747c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7484c06356bSdh "Failed to setup DMA for fwlog area"); 7494c06356bSdh pwp->fwlog = 0; 7504c06356bSdh } else { 7514c06356bSdh bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 7529719310aSDavid Hollister pwp->fwlogp_aap1 = (pmcs_fw_event_hdr_t *)pwp->fwlogp; 7539719310aSDavid Hollister pwp->fwlogp_iop = (pmcs_fw_event_hdr_t *)((void *) 7549719310aSDavid Hollister ((caddr_t)pwp->fwlogp + (PMCS_FWLOG_SIZE / 2))); 7554c06356bSdh } 7564c06356bSdh } 7574c06356bSdh 7584c06356bSdh if (pwp->flash_chunk_addr == NULL) { 7594c06356bSdh pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE; 7604c06356bSdh if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr, 7614c06356bSdh &pwp->regdump_acchdl, 7624c06356bSdh &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE, 7634c06356bSdh (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) == 7644c06356bSdh B_FALSE) { 765c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7664c06356bSdh "Failed to setup DMA for register dump area"); 7674c06356bSdh goto failure; 7684c06356bSdh } 7694c06356bSdh bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE); 7704c06356bSdh } 7714c06356bSdh 7724c06356bSdh /* 7734c06356bSdh * More bits of local initialization... 7744c06356bSdh */ 7754c06356bSdh pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0); 7764c06356bSdh if (pwp->tq == NULL) { 777c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 778c3bc407cSdh "unable to create worker taskq"); 7794c06356bSdh goto failure; 7804c06356bSdh } 7814c06356bSdh 7824c06356bSdh /* 7834c06356bSdh * Cache of structures for dealing with I/O completion callbacks. 7844c06356bSdh */ 7854c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst); 7864c06356bSdh pwp->iocomp_cb_cache = kmem_cache_create(buf, 7874c06356bSdh sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0); 7884c06356bSdh 7894c06356bSdh /* 7904c06356bSdh * Cache of PHY structures 7914c06356bSdh */ 7924c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst); 7934c06356bSdh pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8, 7944c06356bSdh pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp, 7954c06356bSdh NULL, 0); 7964c06356bSdh 7974c06356bSdh /* 7984c06356bSdh * Allocate space for the I/O completion threads 7994c06356bSdh */ 8004c06356bSdh num_threads = ncpus_online; 8014c06356bSdh if (num_threads > PMCS_MAX_CQ_THREADS) { 8024c06356bSdh num_threads = PMCS_MAX_CQ_THREADS; 8034c06356bSdh } 8044c06356bSdh 8054c06356bSdh pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) * 8064c06356bSdh num_threads, KM_SLEEP); 8074c06356bSdh pwp->cq_info.cq_threads = num_threads; 8084c06356bSdh pwp->cq_info.cq_next_disp_thr = 0; 8094c06356bSdh pwp->cq_info.cq_stop = B_FALSE; 8104c06356bSdh 8114c06356bSdh /* 8124c06356bSdh * Set the quantum value in clock ticks for the I/O interrupt 8134c06356bSdh * coalescing timer. 8144c06356bSdh */ 8154c06356bSdh pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS); 8164c06356bSdh 8174c06356bSdh /* 8184c06356bSdh * We have a delicate dance here. We need to set up 8194c06356bSdh * interrupts so we know how to set up some OQC 8204c06356bSdh * tables. However, while we're setting up table 8214c06356bSdh * access, we may need to flash new firmware and 8224c06356bSdh * reset the card, which will take some finessing. 8234c06356bSdh */ 8244c06356bSdh 8254c06356bSdh /* 8264c06356bSdh * Set up interrupts here. 8274c06356bSdh */ 8284c06356bSdh switch (pmcs_setup_intr(pwp)) { 8294c06356bSdh case 0: 8304c06356bSdh break; 8314c06356bSdh case EIO: 8324c06356bSdh pwp->stuck = 1; 8334c06356bSdh /* FALLTHROUGH */ 8344c06356bSdh default: 8354c06356bSdh goto failure; 8364c06356bSdh } 8374c06356bSdh 8384c06356bSdh /* 8394c06356bSdh * Set these up now becuase they are used to initialize the OQC tables. 8404c06356bSdh * 8414c06356bSdh * If we have MSI or MSI-X interrupts set up and we have enough 8424c06356bSdh * vectors for each OQ, the Outbound Queue vectors can all be the 8434c06356bSdh * same as the appropriate interrupt routine will have been called 8444c06356bSdh * and the doorbell register automatically cleared. 8454c06356bSdh * This keeps us from having to check the Outbound Doorbell register 8464c06356bSdh * when the routines for these interrupts are called. 8474c06356bSdh * 8484c06356bSdh * If we have Legacy INT-X interrupts set up or we didn't have enough 8494c06356bSdh * MSI/MSI-X vectors to uniquely identify each OQ, we point these 8504c06356bSdh * vectors to the bits we would like to have set in the Outbound 8514c06356bSdh * Doorbell register because pmcs_all_intr will read the doorbell 8524c06356bSdh * register to find out why we have an interrupt and write the 8534c06356bSdh * corresponding 'clear' bit for that interrupt. 8544c06356bSdh */ 8554c06356bSdh 8564c06356bSdh switch (pwp->intr_cnt) { 8574c06356bSdh case 1: 8584c06356bSdh /* 8594c06356bSdh * Only one vector, so we must check all OQs for MSI. For 8604c06356bSdh * INT-X, there's only one vector anyway, so we can just 8614c06356bSdh * use the outbound queue bits to keep from having to 8624c06356bSdh * check each queue for each interrupt. 8634c06356bSdh */ 8644c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 8654c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8664c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8674c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8684c06356bSdh } else { 8694c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8704c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE; 8714c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE; 8724c06356bSdh } 8734c06356bSdh break; 8744c06356bSdh case 2: 8754c06356bSdh /* With 2, we can at least isolate IODONE */ 8764c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8774c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8784c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL; 8794c06356bSdh break; 8804c06356bSdh case 4: 8814c06356bSdh /* With 4 vectors, everybody gets one */ 8824c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8834c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8844c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8854c06356bSdh break; 8864c06356bSdh } 8874c06356bSdh 8884c06356bSdh /* 8894c06356bSdh * Do the first part of setup 8904c06356bSdh */ 8914c06356bSdh if (pmcs_setup(pwp)) { 8924c06356bSdh goto failure; 8934c06356bSdh } 8944c06356bSdh pmcs_report_fwversion(pwp); 8954c06356bSdh 8964c06356bSdh /* 8974c06356bSdh * Now do some additonal allocations based upon information 8984c06356bSdh * gathered during MPI setup. 8994c06356bSdh */ 9004c06356bSdh pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP); 9014c06356bSdh ASSERT(pwp->nphy < SAS2_PHYNUM_MAX); 9024c06356bSdh phyp = pwp->root_phys; 9034c06356bSdh for (i = 0; i < pwp->nphy; i++) { 9044c06356bSdh if (i < pwp->nphy-1) { 9054c06356bSdh phyp->sibling = (phyp + 1); 9064c06356bSdh } 9074c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 9084c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 9094c06356bSdh phyp->phynum = i & SAS2_PHYNUM_MASK; 9104c06356bSdh pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path)); 9114c06356bSdh phyp->pwp = pwp; 9124c06356bSdh phyp->device_id = PMCS_INVALID_DEVICE_ID; 913601c90f1SSrikanth, Ramana phyp->portid = PMCS_PHY_INVALID_PORT_ID; 9144c06356bSdh phyp++; 9154c06356bSdh } 9164c06356bSdh 9174c06356bSdh pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP); 9184c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 9194c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 9204c06356bSdh mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER, 9214c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 9224c06356bSdh cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL); 9234c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next); 9244c06356bSdh 9254c06356bSdh } 9264c06356bSdh pwp->targets = (pmcs_xscsi_t **) 9274c06356bSdh kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP); 9284c06356bSdh 9294c06356bSdh pwp->iqpt = (pmcs_iqp_trace_t *) 9304c06356bSdh kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP); 9314c06356bSdh pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP); 9324c06356bSdh pwp->iqpt->curpos = pwp->iqpt->head; 9334c06356bSdh pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE; 9344c06356bSdh 9354c06356bSdh /* 9364c06356bSdh * Start MPI communication. 9374c06356bSdh */ 9384c06356bSdh if (pmcs_start_mpi(pwp)) { 9394c06356bSdh if (pmcs_soft_reset(pwp, B_FALSE)) { 9404c06356bSdh goto failure; 9414c06356bSdh } 9425c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_ATTACH; 9434c06356bSdh } 9444c06356bSdh 9454c06356bSdh /* 9464c06356bSdh * Do some initial acceptance tests. 9474c06356bSdh * This tests interrupts and queues. 9484c06356bSdh */ 9494c06356bSdh if (pmcs_echo_test(pwp)) { 9504c06356bSdh goto failure; 9514c06356bSdh } 9524c06356bSdh 9534c06356bSdh /* Read VPD - if it exists */ 9544c06356bSdh if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) { 955c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 956c3bc407cSdh "%s: Unable to read VPD: " 9574c06356bSdh "attempting to fabricate", __func__); 9584c06356bSdh /* 9594c06356bSdh * When we release, this must goto failure and the call 9604c06356bSdh * to pmcs_fabricate_wwid is removed. 9614c06356bSdh */ 9624c06356bSdh /* goto failure; */ 9634c06356bSdh if (!pmcs_fabricate_wwid(pwp)) { 9644c06356bSdh goto failure; 9654c06356bSdh } 9664c06356bSdh } 9674c06356bSdh 9684c06356bSdh /* 9694c06356bSdh * We're now officially running 9704c06356bSdh */ 9714c06356bSdh pwp->state = STATE_RUNNING; 9724c06356bSdh 9734c06356bSdh /* 9744c06356bSdh * Check firmware versions and load new firmware 9754c06356bSdh * if needed and reset. 9764c06356bSdh */ 9774c06356bSdh if (pmcs_firmware_update(pwp)) { 978c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 979c3bc407cSdh "%s: Firmware update failed", __func__); 9804c06356bSdh goto failure; 9814c06356bSdh } 9824c06356bSdh 9834c06356bSdh /* 9844c06356bSdh * Create completion threads. 9854c06356bSdh */ 9864c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 9874c06356bSdh pwp->cq_info.cq_thr_info[i].cq_pwp = pwp; 9884c06356bSdh pwp->cq_info.cq_thr_info[i].cq_thread = 9894c06356bSdh thread_create(NULL, 0, pmcs_scsa_cq_run, 9904c06356bSdh &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri); 9914c06356bSdh } 9924c06356bSdh 9934c06356bSdh /* 9944c06356bSdh * Create one thread to deal with the updating of the interrupt 9954c06356bSdh * coalescing timer. 9964c06356bSdh */ 9974c06356bSdh pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal, 9984c06356bSdh pwp, 0, &p0, TS_RUN, minclsyspri); 9994c06356bSdh 10004c06356bSdh /* 10014c06356bSdh * Kick off the watchdog 10024c06356bSdh */ 10034c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 10044c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 10054c06356bSdh /* 10064c06356bSdh * Do the SCSI attachment code (before starting phys) 10074c06356bSdh */ 10084c06356bSdh if (pmcs_scsa_init(pwp, &pmcs_dattr)) { 10094c06356bSdh goto failure; 10104c06356bSdh } 10114c06356bSdh pwp->hba_attached = 1; 10124c06356bSdh 10134c06356bSdh /* 10144c06356bSdh * Initialize the rwlock for the iport elements. 10154c06356bSdh */ 10164c06356bSdh rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL); 10174c06356bSdh 10184c06356bSdh /* Check all acc & dma handles allocated in attach */ 10194c06356bSdh if (pmcs_check_acc_dma_handle(pwp)) { 10204c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 10214c06356bSdh goto failure; 10224c06356bSdh } 10234c06356bSdh 10244c06356bSdh /* 10254c06356bSdh * Create the phymap for this HBA instance 10264c06356bSdh */ 10274c06356bSdh if (sas_phymap_create(dip, phymap_usec, PHYMAP_MODE_SIMPLE, NULL, 10284c06356bSdh pwp, pmcs_phymap_activate, pmcs_phymap_deactivate, 10294c06356bSdh &pwp->hss_phymap) != DDI_SUCCESS) { 1030c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1031c3bc407cSdh "%s: pmcs%d phymap_create failed", __func__, inst); 10324c06356bSdh goto failure; 10334c06356bSdh } 10344c06356bSdh ASSERT(pwp->hss_phymap); 10354c06356bSdh 10364c06356bSdh /* 10374c06356bSdh * Create the iportmap for this HBA instance 10384c06356bSdh */ 10391b115575SJohn Danielson if (scsi_hba_iportmap_create(dip, iportmap_usec, 10404c06356bSdh &pwp->hss_iportmap) != DDI_SUCCESS) { 1041c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1042c3bc407cSdh "%s: pmcs%d iportmap_create failed", __func__, inst); 10434c06356bSdh goto failure; 10444c06356bSdh } 10454c06356bSdh ASSERT(pwp->hss_iportmap); 10464c06356bSdh 10474c06356bSdh /* 10484c06356bSdh * Start the PHYs. 10494c06356bSdh */ 10504c06356bSdh if (pmcs_start_phys(pwp)) { 10514c06356bSdh goto failure; 10524c06356bSdh } 10534c06356bSdh 10544c06356bSdh /* 10554c06356bSdh * From this point on, we can't fail. 10564c06356bSdh */ 10574c06356bSdh ddi_report_dev(dip); 10584c06356bSdh 10594c06356bSdh /* SM-HBA */ 10604c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED, 10614c06356bSdh &sm_hba); 10624c06356bSdh 10634c06356bSdh /* SM-HBA */ 10644c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION, 10654c06356bSdh pmcs_driver_rev); 10664c06356bSdh 10674c06356bSdh /* SM-HBA */ 10684c06356bSdh chiprev = 'A' + pwp->chiprev; 10694c06356bSdh (void) snprintf(hw_rev, 2, "%s", &chiprev); 10704c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION, 10714c06356bSdh hw_rev); 10724c06356bSdh 10734c06356bSdh /* SM-HBA */ 10744c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 10754c06356bSdh case PMCS_FW_TYPE_RELEASED: 10764c06356bSdh fwsupport = "Released"; 10774c06356bSdh break; 10784c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 10794c06356bSdh fwsupport = "Development"; 10804c06356bSdh break; 10814c06356bSdh case PMCS_FW_TYPE_ALPHA: 10824c06356bSdh fwsupport = "Alpha"; 10834c06356bSdh break; 10844c06356bSdh case PMCS_FW_TYPE_BETA: 10854c06356bSdh fwsupport = "Beta"; 10864c06356bSdh break; 10874c06356bSdh default: 10884c06356bSdh fwsupport = "Special"; 10894c06356bSdh break; 10904c06356bSdh } 10914c06356bSdh (void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s", 10924c06356bSdh PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp), 10934c06356bSdh fwsupport); 10944c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION, 10954c06356bSdh fw_rev); 10964c06356bSdh 10974c06356bSdh /* SM-HBA */ 10984c06356bSdh num_phys = pwp->nphy; 10994c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA, 11004c06356bSdh &num_phys); 11014c06356bSdh 11024c06356bSdh /* SM-HBA */ 11034c06356bSdh protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT; 11044c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL, 11054c06356bSdh &protocol); 11064c06356bSdh 1107*658280b6SDavid Hollister /* Receptacle properties (FMA) */ 1108*658280b6SDavid Hollister pwp->recept_labels[0] = PMCS_RECEPT_LABEL_0; 1109*658280b6SDavid Hollister pwp->recept_pm[0] = PMCS_RECEPT_PM_0; 1110*658280b6SDavid Hollister pwp->recept_labels[1] = PMCS_RECEPT_LABEL_1; 1111*658280b6SDavid Hollister pwp->recept_pm[1] = PMCS_RECEPT_PM_1; 1112*658280b6SDavid Hollister if (ddi_prop_update_string_array(DDI_DEV_T_NONE, dip, 1113*658280b6SDavid Hollister SCSI_HBA_PROP_RECEPTACLE_LABEL, &pwp->recept_labels[0], 1114*658280b6SDavid Hollister PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) { 1115*658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1116*658280b6SDavid Hollister "%s: failed to create %s property", __func__, 1117*658280b6SDavid Hollister "receptacle-label"); 1118*658280b6SDavid Hollister } 1119*658280b6SDavid Hollister if (ddi_prop_update_int_array(DDI_DEV_T_NONE, dip, 1120*658280b6SDavid Hollister SCSI_HBA_PROP_RECEPTACLE_PM, &pwp->recept_pm[0], 1121*658280b6SDavid Hollister PMCS_NUM_RECEPTACLES) != DDI_PROP_SUCCESS) { 1122*658280b6SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1123*658280b6SDavid Hollister "%s: failed to create %s property", __func__, 1124*658280b6SDavid Hollister "receptacle-pm"); 1125*658280b6SDavid Hollister } 1126*658280b6SDavid Hollister 11274c06356bSdh return (DDI_SUCCESS); 11284c06356bSdh 11294c06356bSdh failure: 11304c06356bSdh if (pmcs_unattach(pwp)) { 11314c06356bSdh pwp->stuck = 1; 11324c06356bSdh } 11334c06356bSdh return (DDI_FAILURE); 11344c06356bSdh } 11354c06356bSdh 11364c06356bSdh int 11374c06356bSdh pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 11384c06356bSdh { 11394c06356bSdh int inst = ddi_get_instance(dip); 11404c06356bSdh pmcs_iport_t *iport = NULL; 11414c06356bSdh pmcs_hw_t *pwp = NULL; 11424c06356bSdh scsi_hba_tran_t *tran; 11434c06356bSdh 11444c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 11454c06356bSdh /* iport node */ 11464c06356bSdh iport = ddi_get_soft_state(pmcs_iport_softstate, inst); 11474c06356bSdh ASSERT(iport); 11484c06356bSdh if (iport == NULL) { 11494c06356bSdh return (DDI_FAILURE); 11504c06356bSdh } 11514c06356bSdh pwp = iport->pwp; 11524c06356bSdh } else { 11534c06356bSdh /* hba node */ 11544c06356bSdh pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst); 11554c06356bSdh ASSERT(pwp); 11564c06356bSdh if (pwp == NULL) { 11574c06356bSdh return (DDI_FAILURE); 11584c06356bSdh } 11594c06356bSdh } 11604c06356bSdh 11614c06356bSdh switch (cmd) { 11624c06356bSdh case DDI_DETACH: 11634c06356bSdh if (iport) { 11644c06356bSdh /* iport detach */ 11654c06356bSdh if (pmcs_iport_unattach(iport)) { 11664c06356bSdh return (DDI_FAILURE); 11674c06356bSdh } 1168c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1169c3bc407cSdh "iport%d detached", inst); 11704c06356bSdh return (DDI_SUCCESS); 11714c06356bSdh } else { 11724c06356bSdh /* HBA detach */ 11734c06356bSdh if (pmcs_unattach(pwp)) { 11744c06356bSdh return (DDI_FAILURE); 11754c06356bSdh } 11764c06356bSdh return (DDI_SUCCESS); 11774c06356bSdh } 11784c06356bSdh 11794c06356bSdh case DDI_SUSPEND: 11804c06356bSdh case DDI_PM_SUSPEND: 11814c06356bSdh /* No DDI_SUSPEND on iport nodes */ 11824c06356bSdh if (iport) { 11834c06356bSdh return (DDI_SUCCESS); 11844c06356bSdh } 11854c06356bSdh 11864c06356bSdh if (pwp->stuck) { 11874c06356bSdh return (DDI_FAILURE); 11884c06356bSdh } 11894c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 11904c06356bSdh if (!tran) { 11914c06356bSdh return (DDI_FAILURE); 11924c06356bSdh } 11934c06356bSdh 11944c06356bSdh pwp = TRAN2PMC(tran); 11954c06356bSdh if (pwp == NULL) { 11964c06356bSdh return (DDI_FAILURE); 11974c06356bSdh } 11984c06356bSdh mutex_enter(&pwp->lock); 11994c06356bSdh if (pwp->tq) { 12004c06356bSdh ddi_taskq_suspend(pwp->tq); 12014c06356bSdh } 12024c06356bSdh pwp->suspended = 1; 12034c06356bSdh mutex_exit(&pwp->lock); 1204c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PMC8X6G suspending"); 12054c06356bSdh return (DDI_SUCCESS); 12064c06356bSdh 12074c06356bSdh default: 12084c06356bSdh return (DDI_FAILURE); 12094c06356bSdh } 12104c06356bSdh } 12114c06356bSdh 12124c06356bSdh static int 12134c06356bSdh pmcs_iport_unattach(pmcs_iport_t *iport) 12144c06356bSdh { 12154c06356bSdh pmcs_hw_t *pwp = iport->pwp; 12164c06356bSdh 12174c06356bSdh /* 12184c06356bSdh * First, check if there are still any configured targets on this 12194c06356bSdh * iport. If so, we fail detach. 12204c06356bSdh */ 12214c06356bSdh if (pmcs_iport_has_targets(pwp, iport)) { 1222c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1223c3bc407cSdh "iport%d detach failure: iport has targets (luns)", 1224c3bc407cSdh ddi_get_instance(iport->dip)); 12254c06356bSdh return (DDI_FAILURE); 12264c06356bSdh } 12274c06356bSdh 12284c06356bSdh /* 12294c06356bSdh * Remove this iport from our list if it is inactive in the phymap. 12304c06356bSdh */ 12314c06356bSdh rw_enter(&pwp->iports_lock, RW_WRITER); 12324c06356bSdh mutex_enter(&iport->lock); 12334c06356bSdh 12344c06356bSdh if (iport->ua_state == UA_ACTIVE) { 12354c06356bSdh mutex_exit(&iport->lock); 12364c06356bSdh rw_exit(&pwp->iports_lock); 1237c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1238c3bc407cSdh "iport%d detach failure: " 12394c06356bSdh "iport unit address active in phymap", 12404c06356bSdh ddi_get_instance(iport->dip)); 12414c06356bSdh return (DDI_FAILURE); 12424c06356bSdh } 12434c06356bSdh 12444c06356bSdh /* If it's our only iport, clear iports_attached */ 12454c06356bSdh ASSERT(pwp->num_iports >= 1); 12464c06356bSdh if (--pwp->num_iports == 0) { 12474c06356bSdh pwp->iports_attached = 0; 12484c06356bSdh } 12494c06356bSdh 12504c06356bSdh ASSERT(list_link_active(&iport->list_node)); 12514c06356bSdh list_remove(&pwp->iports, iport); 12524c06356bSdh rw_exit(&pwp->iports_lock); 12534c06356bSdh 12544c06356bSdh /* 12554c06356bSdh * We have removed the iport handle from the HBA's iports list, 12564c06356bSdh * there will be no new references to it. Two things must be 12574c06356bSdh * guarded against here. First, we could have PHY up events, 12584c06356bSdh * adding themselves to the iport->phys list and grabbing ref's 12594c06356bSdh * on our iport handle. Second, we could have existing references 12604c06356bSdh * to this iport handle from a point in time prior to the list 12614c06356bSdh * removal above. 12624c06356bSdh * 12634c06356bSdh * So first, destroy the phys list. Remove any phys that have snuck 12644c06356bSdh * in after the phymap deactivate, dropping the refcnt accordingly. 12654c06356bSdh * If these PHYs are still up if and when the phymap reactivates 12664c06356bSdh * (i.e. when this iport reattaches), we'll populate the list with 12674c06356bSdh * them and bump the refcnt back up. 12684c06356bSdh */ 12694c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 12704c06356bSdh ASSERT(list_is_empty(&iport->phys)); 12714c06356bSdh list_destroy(&iport->phys); 12724c06356bSdh mutex_exit(&iport->lock); 12734c06356bSdh 12744c06356bSdh /* 12754c06356bSdh * Second, wait for any other references to this iport to be 12764c06356bSdh * dropped, then continue teardown. 12774c06356bSdh */ 12784c06356bSdh mutex_enter(&iport->refcnt_lock); 12794c06356bSdh while (iport->refcnt != 0) { 12804c06356bSdh cv_wait(&iport->refcnt_cv, &iport->refcnt_lock); 12814c06356bSdh } 12824c06356bSdh mutex_exit(&iport->refcnt_lock); 12834c06356bSdh 12844c06356bSdh /* Delete kstats */ 12854c06356bSdh pmcs_destroy_phy_stats(iport); 12864c06356bSdh 12874c06356bSdh /* Destroy the iport target map */ 12884c06356bSdh if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) { 12894c06356bSdh return (DDI_FAILURE); 12904c06356bSdh } 12914c06356bSdh 12924c06356bSdh /* Free the tgt soft state */ 12934c06356bSdh if (iport->tgt_sstate != NULL) { 12944c06356bSdh ddi_soft_state_bystr_fini(&iport->tgt_sstate); 12954c06356bSdh } 12964c06356bSdh 12974c06356bSdh /* Free our unit address string */ 12984c06356bSdh strfree(iport->ua); 12994c06356bSdh 13004c06356bSdh /* Finish teardown and free the softstate */ 13014c06356bSdh mutex_destroy(&iport->refcnt_lock); 13026745c559SJesse Butler mutex_destroy(&iport->smp_lock); 13034c06356bSdh ASSERT(iport->refcnt == 0); 13044c06356bSdh cv_destroy(&iport->refcnt_cv); 13056745c559SJesse Butler cv_destroy(&iport->smp_cv); 13064c06356bSdh mutex_destroy(&iport->lock); 13074c06356bSdh ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip)); 13084c06356bSdh 13094c06356bSdh return (DDI_SUCCESS); 13104c06356bSdh } 13114c06356bSdh 13124c06356bSdh static int 13134c06356bSdh pmcs_unattach(pmcs_hw_t *pwp) 13144c06356bSdh { 13154c06356bSdh int i; 13164c06356bSdh enum pwpstate curstate; 13174c06356bSdh pmcs_cq_thr_info_t *cqti; 13184c06356bSdh 13194c06356bSdh /* 13204c06356bSdh * Tear down the interrupt infrastructure. 13214c06356bSdh */ 13224c06356bSdh if (pmcs_teardown_intr(pwp)) { 13234c06356bSdh pwp->stuck = 1; 13244c06356bSdh } 13254c06356bSdh pwp->intr_cnt = 0; 13264c06356bSdh 13274c06356bSdh /* 13284c06356bSdh * Grab a lock, if initted, to set state. 13294c06356bSdh */ 13304c06356bSdh if (pwp->locks_initted) { 13314c06356bSdh mutex_enter(&pwp->lock); 13324c06356bSdh if (pwp->state != STATE_DEAD) { 13334c06356bSdh pwp->state = STATE_UNPROBING; 13344c06356bSdh } 13354c06356bSdh curstate = pwp->state; 13364c06356bSdh mutex_exit(&pwp->lock); 13374c06356bSdh 13384c06356bSdh /* 13394c06356bSdh * Stop the I/O completion threads. 13404c06356bSdh */ 13414c06356bSdh mutex_enter(&pwp->cq_lock); 13424c06356bSdh pwp->cq_info.cq_stop = B_TRUE; 13434c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 13444c06356bSdh if (pwp->cq_info.cq_thr_info[i].cq_thread) { 13454c06356bSdh cqti = &pwp->cq_info.cq_thr_info[i]; 13464c06356bSdh mutex_enter(&cqti->cq_thr_lock); 13474c06356bSdh cv_signal(&cqti->cq_cv); 13484c06356bSdh mutex_exit(&cqti->cq_thr_lock); 13494c06356bSdh mutex_exit(&pwp->cq_lock); 13504c06356bSdh thread_join(cqti->cq_thread->t_did); 13514c06356bSdh mutex_enter(&pwp->cq_lock); 13524c06356bSdh } 13534c06356bSdh } 13544c06356bSdh mutex_exit(&pwp->cq_lock); 13554c06356bSdh 13564c06356bSdh /* 13574c06356bSdh * Stop the interrupt coalescing timer thread 13584c06356bSdh */ 13594c06356bSdh if (pwp->ict_thread) { 13604c06356bSdh mutex_enter(&pwp->ict_lock); 13614c06356bSdh pwp->io_intr_coal.stop_thread = B_TRUE; 13624c06356bSdh cv_signal(&pwp->ict_cv); 13634c06356bSdh mutex_exit(&pwp->ict_lock); 13644c06356bSdh thread_join(pwp->ict_thread->t_did); 13654c06356bSdh } 13664c06356bSdh } else { 13674c06356bSdh if (pwp->state != STATE_DEAD) { 13684c06356bSdh pwp->state = STATE_UNPROBING; 13694c06356bSdh } 13704c06356bSdh curstate = pwp->state; 13714c06356bSdh } 13724c06356bSdh 13734c06356bSdh if (&pwp->iports != NULL) { 13744c06356bSdh /* Destroy the iports lock */ 13754c06356bSdh rw_destroy(&pwp->iports_lock); 13764c06356bSdh /* Destroy the iports list */ 13774c06356bSdh ASSERT(list_is_empty(&pwp->iports)); 13784c06356bSdh list_destroy(&pwp->iports); 13794c06356bSdh } 13804c06356bSdh 13814c06356bSdh if (pwp->hss_iportmap != NULL) { 13824c06356bSdh /* Destroy the iportmap */ 13834c06356bSdh scsi_hba_iportmap_destroy(pwp->hss_iportmap); 13844c06356bSdh } 13854c06356bSdh 13864c06356bSdh if (pwp->hss_phymap != NULL) { 13874c06356bSdh /* Destroy the phymap */ 13884c06356bSdh sas_phymap_destroy(pwp->hss_phymap); 13894c06356bSdh } 13904c06356bSdh 13914c06356bSdh /* 13924c06356bSdh * Make sure that any pending watchdog won't 13934c06356bSdh * be called from this point on out. 13944c06356bSdh */ 13954c06356bSdh (void) untimeout(pwp->wdhandle); 13964c06356bSdh /* 13974c06356bSdh * After the above action, the watchdog 13984c06356bSdh * timer that starts up the worker task 13994c06356bSdh * may trigger but will exit immediately 14004c06356bSdh * on triggering. 14014c06356bSdh * 14024c06356bSdh * Now that this is done, we can destroy 14034c06356bSdh * the task queue, which will wait if we're 14044c06356bSdh * running something on it. 14054c06356bSdh */ 14064c06356bSdh if (pwp->tq) { 14074c06356bSdh ddi_taskq_destroy(pwp->tq); 14084c06356bSdh pwp->tq = NULL; 14094c06356bSdh } 14104c06356bSdh 14114c06356bSdh pmcs_fm_fini(pwp); 14124c06356bSdh 14134c06356bSdh if (pwp->hba_attached) { 14144c06356bSdh (void) scsi_hba_detach(pwp->dip); 14154c06356bSdh pwp->hba_attached = 0; 14164c06356bSdh } 14174c06356bSdh 14184c06356bSdh /* 14194c06356bSdh * If the chip hasn't been marked dead, shut it down now 14204c06356bSdh * to bring it back to a known state without attempting 14214c06356bSdh * a soft reset. 14224c06356bSdh */ 14234c06356bSdh if (curstate != STATE_DEAD && pwp->locks_initted) { 14244c06356bSdh /* 14254c06356bSdh * De-register all registered devices 14264c06356bSdh */ 14274c06356bSdh pmcs_deregister_devices(pwp, pwp->root_phys); 14284c06356bSdh 14294c06356bSdh /* 14304c06356bSdh * Stop all the phys. 14314c06356bSdh */ 14324c06356bSdh pmcs_stop_phys(pwp); 14334c06356bSdh 14344c06356bSdh /* 14354c06356bSdh * Shut Down Message Passing 14364c06356bSdh */ 14374c06356bSdh (void) pmcs_stop_mpi(pwp); 14384c06356bSdh 14394c06356bSdh /* 14404c06356bSdh * Reset chip 14414c06356bSdh */ 14424c06356bSdh (void) pmcs_soft_reset(pwp, B_FALSE); 14435c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_DETACH; 14444c06356bSdh } 14454c06356bSdh 14464c06356bSdh /* 14474c06356bSdh * Turn off interrupts on the chip 14484c06356bSdh */ 14494c06356bSdh if (pwp->mpi_acc_handle) { 14504c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 14514c06356bSdh } 14524c06356bSdh 14534c06356bSdh /* Destroy pwp's lock */ 14544c06356bSdh if (pwp->locks_initted) { 14554c06356bSdh mutex_destroy(&pwp->lock); 14564c06356bSdh mutex_destroy(&pwp->dma_lock); 14574c06356bSdh mutex_destroy(&pwp->axil_lock); 14584c06356bSdh mutex_destroy(&pwp->cq_lock); 14594c06356bSdh mutex_destroy(&pwp->config_lock); 14604c06356bSdh mutex_destroy(&pwp->ict_lock); 14614c06356bSdh mutex_destroy(&pwp->wfree_lock); 14624c06356bSdh mutex_destroy(&pwp->pfree_lock); 14634c06356bSdh mutex_destroy(&pwp->dead_phylist_lock); 14644c06356bSdh #ifdef DEBUG 14654c06356bSdh mutex_destroy(&pwp->dbglock); 14664c06356bSdh #endif 14674c06356bSdh cv_destroy(&pwp->ict_cv); 14684c06356bSdh cv_destroy(&pwp->drain_cv); 14694c06356bSdh pwp->locks_initted = 0; 14704c06356bSdh } 14714c06356bSdh 14724c06356bSdh /* 14734c06356bSdh * Free DMA handles and associated consistent memory 14744c06356bSdh */ 14754c06356bSdh if (pwp->regdump_hndl) { 14764c06356bSdh if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) { 1477c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1478c3bc407cSdh "Condition check failed " 14794c06356bSdh "at %s():%d", __func__, __LINE__); 14804c06356bSdh } 14814c06356bSdh ddi_dma_free_handle(&pwp->regdump_hndl); 14824c06356bSdh ddi_dma_mem_free(&pwp->regdump_acchdl); 14834c06356bSdh pwp->regdump_hndl = 0; 14844c06356bSdh } 14854c06356bSdh if (pwp->fwlog_hndl) { 14864c06356bSdh if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) { 1487c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1488c3bc407cSdh "Condition check failed " 14894c06356bSdh "at %s():%d", __func__, __LINE__); 14904c06356bSdh } 14914c06356bSdh ddi_dma_free_handle(&pwp->fwlog_hndl); 14924c06356bSdh ddi_dma_mem_free(&pwp->fwlog_acchdl); 14934c06356bSdh pwp->fwlog_hndl = 0; 14944c06356bSdh } 14954c06356bSdh if (pwp->cip_handles) { 14964c06356bSdh if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) { 1497c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1498c3bc407cSdh "Condition check failed " 14994c06356bSdh "at %s():%d", __func__, __LINE__); 15004c06356bSdh } 15014c06356bSdh ddi_dma_free_handle(&pwp->cip_handles); 15024c06356bSdh ddi_dma_mem_free(&pwp->cip_acchdls); 15034c06356bSdh pwp->cip_handles = 0; 15044c06356bSdh } 15054c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 15064c06356bSdh if (pwp->oqp_handles[i]) { 15074c06356bSdh if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) != 15084c06356bSdh DDI_SUCCESS) { 1509c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1510c3bc407cSdh "Condition check failed at %s():%d", 1511c3bc407cSdh __func__, __LINE__); 15124c06356bSdh } 15134c06356bSdh ddi_dma_free_handle(&pwp->oqp_handles[i]); 15144c06356bSdh ddi_dma_mem_free(&pwp->oqp_acchdls[i]); 15154c06356bSdh pwp->oqp_handles[i] = 0; 15164c06356bSdh } 15174c06356bSdh } 15184c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 15194c06356bSdh if (pwp->iqp_handles[i]) { 15204c06356bSdh if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) != 15214c06356bSdh DDI_SUCCESS) { 1522c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1523c3bc407cSdh "Condition check failed at %s():%d", 1524c3bc407cSdh __func__, __LINE__); 15254c06356bSdh } 15264c06356bSdh ddi_dma_free_handle(&pwp->iqp_handles[i]); 15274c06356bSdh ddi_dma_mem_free(&pwp->iqp_acchdls[i]); 15284c06356bSdh pwp->iqp_handles[i] = 0; 15294c06356bSdh } 15304c06356bSdh } 15314c06356bSdh 15324c06356bSdh pmcs_free_dma_chunklist(pwp); 15334c06356bSdh 15344c06356bSdh /* 15354c06356bSdh * Unmap registers and destroy access handles 15364c06356bSdh */ 15374c06356bSdh if (pwp->mpi_acc_handle) { 15384c06356bSdh ddi_regs_map_free(&pwp->mpi_acc_handle); 15394c06356bSdh pwp->mpi_acc_handle = 0; 15404c06356bSdh } 15414c06356bSdh if (pwp->top_acc_handle) { 15424c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 15434c06356bSdh pwp->top_acc_handle = 0; 15444c06356bSdh } 15454c06356bSdh if (pwp->gsm_acc_handle) { 15464c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 15474c06356bSdh pwp->gsm_acc_handle = 0; 15484c06356bSdh } 15494c06356bSdh if (pwp->msg_acc_handle) { 15504c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 15514c06356bSdh pwp->msg_acc_handle = 0; 15524c06356bSdh } 15534c06356bSdh if (pwp->pci_acc_handle) { 15544c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 15554c06356bSdh pwp->pci_acc_handle = 0; 15564c06356bSdh } 15574c06356bSdh 15584c06356bSdh /* 15594c06356bSdh * Do memory allocation cleanup. 15604c06356bSdh */ 15614c06356bSdh while (pwp->dma_freelist) { 15624c06356bSdh pmcs_dmachunk_t *this = pwp->dma_freelist; 15634c06356bSdh pwp->dma_freelist = this->nxt; 15644c06356bSdh kmem_free(this, sizeof (pmcs_dmachunk_t)); 15654c06356bSdh } 15664c06356bSdh 15674c06356bSdh /* 15684c06356bSdh * Free pools 15694c06356bSdh */ 15704c06356bSdh if (pwp->iocomp_cb_cache) { 15714c06356bSdh kmem_cache_destroy(pwp->iocomp_cb_cache); 15724c06356bSdh } 15734c06356bSdh 15744c06356bSdh /* 15754c06356bSdh * Free all PHYs (at level > 0), then free the cache 15764c06356bSdh */ 15774c06356bSdh pmcs_free_all_phys(pwp, pwp->root_phys); 15784c06356bSdh if (pwp->phy_cache) { 15794c06356bSdh kmem_cache_destroy(pwp->phy_cache); 15804c06356bSdh } 15814c06356bSdh 15824c06356bSdh /* 15834c06356bSdh * Free root PHYs 15844c06356bSdh */ 15854c06356bSdh if (pwp->root_phys) { 15864c06356bSdh pmcs_phy_t *phyp = pwp->root_phys; 15874c06356bSdh for (i = 0; i < pwp->nphy; i++) { 15884c06356bSdh mutex_destroy(&phyp->phy_lock); 15894c06356bSdh phyp = phyp->sibling; 15904c06356bSdh } 15914c06356bSdh kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t)); 15924c06356bSdh pwp->root_phys = NULL; 15934c06356bSdh pwp->nphy = 0; 15944c06356bSdh } 15954c06356bSdh 15964c06356bSdh /* Free the targets list */ 15974c06356bSdh if (pwp->targets) { 15984c06356bSdh kmem_free(pwp->targets, 15994c06356bSdh sizeof (pmcs_xscsi_t *) * pwp->max_dev); 16004c06356bSdh } 16014c06356bSdh 16024c06356bSdh /* 16034c06356bSdh * Free work structures 16044c06356bSdh */ 16054c06356bSdh 16064c06356bSdh if (pwp->work && pwp->max_cmd) { 16074c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 16084c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 16094c06356bSdh mutex_destroy(&pwrk->lock); 16104c06356bSdh cv_destroy(&pwrk->sleep_cv); 16114c06356bSdh } 16124c06356bSdh kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd); 16134c06356bSdh pwp->work = NULL; 16144c06356bSdh pwp->max_cmd = 0; 16154c06356bSdh } 16164c06356bSdh 16174c06356bSdh /* 16184c06356bSdh * Do last property and SCSA cleanup 16194c06356bSdh */ 16204c06356bSdh if (pwp->tran) { 16214c06356bSdh scsi_hba_tran_free(pwp->tran); 16224c06356bSdh pwp->tran = NULL; 16234c06356bSdh } 16244c06356bSdh if (pwp->reset_notify_listf) { 16254c06356bSdh scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf); 16264c06356bSdh pwp->reset_notify_listf = NULL; 16274c06356bSdh } 16284c06356bSdh ddi_prop_remove_all(pwp->dip); 16294c06356bSdh if (pwp->stuck) { 16304c06356bSdh return (-1); 16314c06356bSdh } 16324c06356bSdh 16334c06356bSdh /* Free register dump area if allocated */ 16344c06356bSdh if (pwp->regdumpp) { 16354c06356bSdh kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE); 16364c06356bSdh pwp->regdumpp = NULL; 16374c06356bSdh } 16384c06356bSdh if (pwp->iqpt && pwp->iqpt->head) { 16394c06356bSdh kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE); 16404c06356bSdh pwp->iqpt->head = pwp->iqpt->curpos = NULL; 16414c06356bSdh } 16424c06356bSdh if (pwp->iqpt) { 16434c06356bSdh kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t)); 16444c06356bSdh pwp->iqpt = NULL; 16454c06356bSdh } 16464c06356bSdh 16474c06356bSdh ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip)); 16484c06356bSdh return (0); 16494c06356bSdh } 16504c06356bSdh 16514c06356bSdh /* 16524c06356bSdh * quiesce (9E) entry point 16534c06356bSdh * 16544c06356bSdh * This function is called when the system is single-threaded at high PIL 16554c06356bSdh * with preemption disabled. Therefore, the function must not block/wait/sleep. 16564c06356bSdh * 16574c06356bSdh * Returns DDI_SUCCESS or DDI_FAILURE. 16584c06356bSdh * 16594c06356bSdh */ 16604c06356bSdh static int 16614c06356bSdh pmcs_quiesce(dev_info_t *dip) 16624c06356bSdh { 16634c06356bSdh pmcs_hw_t *pwp; 16644c06356bSdh scsi_hba_tran_t *tran; 16654c06356bSdh 16664c06356bSdh if ((tran = ddi_get_driver_private(dip)) == NULL) 16674c06356bSdh return (DDI_SUCCESS); 16684c06356bSdh 16694c06356bSdh /* No quiesce necessary on a per-iport basis */ 16704c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 16714c06356bSdh return (DDI_SUCCESS); 16724c06356bSdh } 16734c06356bSdh 16744c06356bSdh if ((pwp = TRAN2PMC(tran)) == NULL) 16754c06356bSdh return (DDI_SUCCESS); 16764c06356bSdh 16774c06356bSdh /* Stop MPI & Reset chip (no need to re-initialize) */ 16784c06356bSdh (void) pmcs_stop_mpi(pwp); 16794c06356bSdh (void) pmcs_soft_reset(pwp, B_TRUE); 16805c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_QUIESCE; 16814c06356bSdh 16824c06356bSdh return (DDI_SUCCESS); 16834c06356bSdh } 16844c06356bSdh 16854c06356bSdh /* 16864c06356bSdh * Called with xp->statlock and PHY lock and scratch acquired. 16874c06356bSdh */ 16884c06356bSdh static int 16894c06356bSdh pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 16904c06356bSdh { 16914c06356bSdh ata_identify_t *ati; 16924c06356bSdh int result, i; 16934c06356bSdh pmcs_phy_t *pptr; 16944c06356bSdh uint16_t *a; 16954c06356bSdh union { 16964c06356bSdh uint8_t nsa[8]; 16974c06356bSdh uint16_t nsb[4]; 16984c06356bSdh } u; 16994c06356bSdh 17004c06356bSdh /* 17014c06356bSdh * Safe defaults - use only if this target is brand new (i.e. doesn't 17024c06356bSdh * already have these settings configured) 17034c06356bSdh */ 17044c06356bSdh if (xp->capacity == 0) { 17054c06356bSdh xp->capacity = (uint64_t)-1; 17064c06356bSdh xp->ca = 1; 17074c06356bSdh xp->qdepth = 1; 17084c06356bSdh xp->pio = 1; 17094c06356bSdh } 17104c06356bSdh 17114c06356bSdh pptr = xp->phy; 17124c06356bSdh 17134c06356bSdh /* 17144c06356bSdh * We only try and issue an IDENTIFY for first level 17154c06356bSdh * (direct attached) devices. We don't try and 17164c06356bSdh * set other quirks here (this will happen later, 17174c06356bSdh * if the device is fully configured) 17184c06356bSdh */ 17194c06356bSdh if (pptr->level) { 17204c06356bSdh return (0); 17214c06356bSdh } 17224c06356bSdh 17234c06356bSdh mutex_exit(&xp->statlock); 17244c06356bSdh result = pmcs_sata_identify(pwp, pptr); 17254c06356bSdh mutex_enter(&xp->statlock); 17264c06356bSdh 17274c06356bSdh if (result) { 17284c06356bSdh return (result); 17294c06356bSdh } 17304c06356bSdh ati = pwp->scratch; 17314c06356bSdh a = &ati->word108; 17324c06356bSdh for (i = 0; i < 4; i++) { 17334c06356bSdh u.nsb[i] = ddi_swap16(*a++); 17344c06356bSdh } 17354c06356bSdh 17364c06356bSdh /* 17374c06356bSdh * Check the returned data for being a valid (NAA=5) WWN. 17384c06356bSdh * If so, use that and override the SAS address we were 17394c06356bSdh * given at Link Up time. 17404c06356bSdh */ 17414c06356bSdh if ((u.nsa[0] >> 4) == 5) { 17424c06356bSdh (void) memcpy(pptr->sas_address, u.nsa, 8); 17434c06356bSdh } 1744c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 1745c3bc407cSdh "%s: %s has SAS ADDRESS " SAS_ADDR_FMT, 17464c06356bSdh __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address)); 17474c06356bSdh return (0); 17484c06356bSdh } 17494c06356bSdh 17504c06356bSdh /* 17514c06356bSdh * Called with PHY lock and target statlock held and scratch acquired 17524c06356bSdh */ 17534c06356bSdh static boolean_t 17544c06356bSdh pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target) 17554c06356bSdh { 17564c06356bSdh ASSERT(target != NULL); 1757c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, "%s: target = 0x%p", 17584c06356bSdh __func__, (void *) target); 17594c06356bSdh 17604c06356bSdh switch (target->phy->dtype) { 17614c06356bSdh case SATA: 17624c06356bSdh if (pmcs_add_sata_device(pwp, target) != 0) { 1763c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, target->phy, 1764c3bc407cSdh target, "%s: add_sata_device failed for tgt 0x%p", 17654c06356bSdh __func__, (void *) target); 17664c06356bSdh return (B_FALSE); 17674c06356bSdh } 17684c06356bSdh break; 17694c06356bSdh case SAS: 17704c06356bSdh target->qdepth = maxqdepth; 17714c06356bSdh break; 17724c06356bSdh case EXPANDER: 17734c06356bSdh target->qdepth = 1; 17744c06356bSdh break; 17754c06356bSdh } 17764c06356bSdh 17774c06356bSdh target->new = 0; 17784c06356bSdh target->assigned = 1; 17794c06356bSdh target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL; 17804c06356bSdh target->dtype = target->phy->dtype; 17814c06356bSdh 17824c06356bSdh /* 17834c06356bSdh * Set the PHY's config stop time to 0. This is one of the final 17844c06356bSdh * stops along the config path, so we're indicating that we 17854c06356bSdh * successfully configured the PHY. 17864c06356bSdh */ 17874c06356bSdh target->phy->config_stop = 0; 17884c06356bSdh 17894c06356bSdh return (B_TRUE); 17904c06356bSdh } 17914c06356bSdh 17924c06356bSdh void 17934c06356bSdh pmcs_worker(void *arg) 17944c06356bSdh { 17954c06356bSdh pmcs_hw_t *pwp = arg; 17964c06356bSdh ulong_t work_flags; 17974c06356bSdh 17984c06356bSdh DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t, 17994c06356bSdh pwp->config_changed); 18004c06356bSdh 18014c06356bSdh if (pwp->state != STATE_RUNNING) { 18024c06356bSdh return; 18034c06356bSdh } 18044c06356bSdh 18054c06356bSdh work_flags = atomic_swap_ulong(&pwp->work_flags, 0); 18064c06356bSdh 1807c280a92bSDavid Hollister if (work_flags & PMCS_WORK_FLAG_DUMP_REGS) { 1808c280a92bSDavid Hollister mutex_enter(&pwp->lock); 1809c280a92bSDavid Hollister pmcs_register_dump_int(pwp); 1810c280a92bSDavid Hollister mutex_exit(&pwp->lock); 1811c280a92bSDavid Hollister } 1812c280a92bSDavid Hollister 18134c06356bSdh if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) { 18144c06356bSdh pmcs_ack_events(pwp); 18154c06356bSdh } 18164c06356bSdh 18174c06356bSdh if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) { 18184c06356bSdh mutex_enter(&pwp->lock); 18194c06356bSdh pmcs_spinup_release(pwp, NULL); 18204c06356bSdh mutex_exit(&pwp->lock); 18214c06356bSdh } 18224c06356bSdh 18234c06356bSdh if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) { 18244c06356bSdh pmcs_ssp_event_recovery(pwp); 18254c06356bSdh } 18264c06356bSdh 18274c06356bSdh if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) { 18284c06356bSdh pmcs_dev_state_recovery(pwp, NULL); 18294c06356bSdh } 18304c06356bSdh 1831601c90f1SSrikanth, Ramana if (work_flags & PMCS_WORK_FLAG_DEREGISTER_DEV) { 1832601c90f1SSrikanth, Ramana pmcs_deregister_device_work(pwp, NULL); 1833601c90f1SSrikanth, Ramana } 1834601c90f1SSrikanth, Ramana 18354c06356bSdh if (work_flags & PMCS_WORK_FLAG_DISCOVER) { 18364c06356bSdh pmcs_discover(pwp); 18374c06356bSdh } 18384c06356bSdh 18394c06356bSdh if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) { 18404c06356bSdh if (pmcs_abort_handler(pwp)) { 18414c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 18424c06356bSdh } 18434c06356bSdh } 18444c06356bSdh 18454c06356bSdh if (work_flags & PMCS_WORK_FLAG_SATA_RUN) { 18464c06356bSdh pmcs_sata_work(pwp); 18474c06356bSdh } 18484c06356bSdh 18494c06356bSdh if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) { 18504c06356bSdh pmcs_scsa_wq_run(pwp); 18514c06356bSdh mutex_enter(&pwp->lock); 18524c06356bSdh PMCS_CQ_RUN(pwp); 18534c06356bSdh mutex_exit(&pwp->lock); 18544c06356bSdh } 18554c06356bSdh 18564c06356bSdh if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) { 18574c06356bSdh if (pmcs_add_more_chunks(pwp, 18584c06356bSdh ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) { 18594c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 18604c06356bSdh } else { 18614c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 18624c06356bSdh } 18634c06356bSdh } 18644c06356bSdh } 18654c06356bSdh 18664c06356bSdh static int 18674c06356bSdh pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize) 18684c06356bSdh { 18694c06356bSdh pmcs_dmachunk_t *dc; 18704c06356bSdh unsigned long dl; 18714c06356bSdh pmcs_chunk_t *pchunk = NULL; 18724c06356bSdh 18734c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 18744c06356bSdh 18754c06356bSdh pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP); 18764c06356bSdh if (pchunk == NULL) { 1877c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18784c06356bSdh "Not enough memory for DMA chunks"); 18794c06356bSdh return (-1); 18804c06356bSdh } 18814c06356bSdh 18824c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle, 18834c06356bSdh &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp, 18844c06356bSdh &pchunk->dma_addr) == B_FALSE) { 1885c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1886c3bc407cSdh "Failed to setup DMA for chunks"); 18874c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 18884c06356bSdh return (-1); 18894c06356bSdh } 18904c06356bSdh 18914c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) || 18924c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) { 18934c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED); 18944c06356bSdh return (-1); 18954c06356bSdh } 18964c06356bSdh 18974c06356bSdh bzero(pchunk->addrp, nsize); 18984c06356bSdh dc = NULL; 18994c06356bSdh for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) { 19004c06356bSdh pmcs_dmachunk_t *tmp; 19014c06356bSdh tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP); 19024c06356bSdh tmp->nxt = dc; 19034c06356bSdh dc = tmp; 19044c06356bSdh } 19054c06356bSdh mutex_enter(&pwp->dma_lock); 19064c06356bSdh pmcs_idma_chunks(pwp, dc, pchunk, nsize); 19074c06356bSdh pwp->nchunks++; 19084c06356bSdh mutex_exit(&pwp->dma_lock); 19094c06356bSdh return (0); 19104c06356bSdh } 19114c06356bSdh 19125c45adf0SJesse Butler static void 19135c45adf0SJesse Butler pmcs_check_forward_progress(pmcs_hw_t *pwp) 19145c45adf0SJesse Butler { 1915d78a6b7eSJesse Butler pmcwork_t *wrkp; 1916d78a6b7eSJesse Butler uint32_t *iqp; 19175c45adf0SJesse Butler uint32_t cur_iqci; 1918d78a6b7eSJesse Butler uint32_t cur_work_idx; 19195c45adf0SJesse Butler uint32_t cur_msgu_tick; 19205c45adf0SJesse Butler uint32_t cur_iop_tick; 19215c45adf0SJesse Butler int i; 19225c45adf0SJesse Butler 19235c45adf0SJesse Butler mutex_enter(&pwp->lock); 19245c45adf0SJesse Butler 19255c45adf0SJesse Butler if (pwp->state == STATE_IN_RESET) { 19265c45adf0SJesse Butler mutex_exit(&pwp->lock); 19275c45adf0SJesse Butler return; 19285c45adf0SJesse Butler } 19295c45adf0SJesse Butler 1930d78a6b7eSJesse Butler /* 1931d78a6b7eSJesse Butler * Ensure that inbound work is getting picked up. First, check to 1932d78a6b7eSJesse Butler * see if new work has been posted. If it has, ensure that the 1933d78a6b7eSJesse Butler * work is moving forward by checking the consumer index and the 1934d78a6b7eSJesse Butler * last_htag for the work being processed against what we saw last 1935d78a6b7eSJesse Butler * time. Note: we use the work structure's 'last_htag' because at 1936d78a6b7eSJesse Butler * any given moment it could be freed back, thus clearing 'htag' 1937d78a6b7eSJesse Butler * and setting 'last_htag' (see pmcs_pwork). 1938d78a6b7eSJesse Butler */ 19395c45adf0SJesse Butler for (i = 0; i < PMCS_NIQ; i++) { 19405c45adf0SJesse Butler cur_iqci = pmcs_rd_iqci(pwp, i); 1941d78a6b7eSJesse Butler iqp = &pwp->iqp[i][cur_iqci * (PMCS_QENTRY_SIZE >> 2)]; 1942d78a6b7eSJesse Butler cur_work_idx = PMCS_TAG_INDEX(LE_32(*(iqp+1))); 1943d78a6b7eSJesse Butler wrkp = &pwp->work[cur_work_idx]; 19445c45adf0SJesse Butler if (cur_iqci == pwp->shadow_iqpi[i]) { 19455c45adf0SJesse Butler pwp->last_iqci[i] = cur_iqci; 1946d78a6b7eSJesse Butler pwp->last_htag[i] = wrkp->last_htag; 19475c45adf0SJesse Butler continue; 19485c45adf0SJesse Butler } 1949d78a6b7eSJesse Butler if ((cur_iqci == pwp->last_iqci[i]) && 1950d78a6b7eSJesse Butler (wrkp->last_htag == pwp->last_htag[i])) { 19515c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19525c45adf0SJesse Butler "Inbound Queue stall detected, issuing reset"); 19535c45adf0SJesse Butler goto hot_reset; 19545c45adf0SJesse Butler } 19555c45adf0SJesse Butler pwp->last_iqci[i] = cur_iqci; 1956d78a6b7eSJesse Butler pwp->last_htag[i] = wrkp->last_htag; 19575c45adf0SJesse Butler } 19585c45adf0SJesse Butler 1959d78a6b7eSJesse Butler /* 1960d78a6b7eSJesse Butler * Check heartbeat on both the MSGU and IOP. It is unlikely that 1961d78a6b7eSJesse Butler * we'd ever fail here, as the inbound queue monitoring code above 1962d78a6b7eSJesse Butler * would detect a stall due to either of these elements being 1963d78a6b7eSJesse Butler * stalled, but we might as well keep an eye on them. 1964d78a6b7eSJesse Butler */ 19655c45adf0SJesse Butler cur_msgu_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_MSGU_TICK); 19665c45adf0SJesse Butler if (cur_msgu_tick == pwp->last_msgu_tick) { 19675c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19685c45adf0SJesse Butler "Stall detected on MSGU, issuing reset"); 19695c45adf0SJesse Butler goto hot_reset; 19705c45adf0SJesse Butler } 19715c45adf0SJesse Butler pwp->last_msgu_tick = cur_msgu_tick; 19725c45adf0SJesse Butler 19735c45adf0SJesse Butler cur_iop_tick = pmcs_rd_gst_tbl(pwp, PMCS_GST_IOP_TICK); 19745c45adf0SJesse Butler if (cur_iop_tick == pwp->last_iop_tick) { 19755c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 19765c45adf0SJesse Butler "Stall detected on IOP, issuing reset"); 19775c45adf0SJesse Butler goto hot_reset; 19785c45adf0SJesse Butler } 19795c45adf0SJesse Butler pwp->last_iop_tick = cur_iop_tick; 19805c45adf0SJesse Butler 19815c45adf0SJesse Butler mutex_exit(&pwp->lock); 19825c45adf0SJesse Butler return; 19835c45adf0SJesse Butler 19845c45adf0SJesse Butler hot_reset: 19855c45adf0SJesse Butler pwp->state = STATE_DEAD; 19865c45adf0SJesse Butler /* 19875c45adf0SJesse Butler * We've detected a stall. Attempt to recover service via hot 19885c45adf0SJesse Butler * reset. In case of failure, pmcs_hot_reset() will handle the 19895c45adf0SJesse Butler * failure and issue any required FM notifications. 19905c45adf0SJesse Butler * See pmcs_subr.c for more details. 19915c45adf0SJesse Butler */ 19925c45adf0SJesse Butler if (pmcs_hot_reset(pwp)) { 19935c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 19945c45adf0SJesse Butler "%s: hot reset failure", __func__); 19955c45adf0SJesse Butler } else { 19965c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 19975c45adf0SJesse Butler "%s: hot reset complete", __func__); 19985c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_STALL; 19995c45adf0SJesse Butler } 20005c45adf0SJesse Butler mutex_exit(&pwp->lock); 20015c45adf0SJesse Butler } 20024c06356bSdh 20034c06356bSdh static void 20044c06356bSdh pmcs_check_commands(pmcs_hw_t *pwp) 20054c06356bSdh { 20064c06356bSdh pmcs_cmd_t *sp; 20074c06356bSdh size_t amt; 20084c06356bSdh char path[32]; 20094c06356bSdh pmcwork_t *pwrk; 20104c06356bSdh pmcs_xscsi_t *target; 20114c06356bSdh pmcs_phy_t *phyp; 2012601c90f1SSrikanth, Ramana int rval; 20134c06356bSdh 20144c06356bSdh for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) { 20154c06356bSdh mutex_enter(&pwrk->lock); 20164c06356bSdh 20174c06356bSdh /* 20184c06356bSdh * If the command isn't active, we can't be timing it still. 20194c06356bSdh * Active means the tag is not free and the state is "on chip". 20204c06356bSdh */ 20214c06356bSdh if (!PMCS_COMMAND_ACTIVE(pwrk)) { 20224c06356bSdh mutex_exit(&pwrk->lock); 20234c06356bSdh continue; 20244c06356bSdh } 20254c06356bSdh 20264c06356bSdh /* 20274c06356bSdh * No timer active for this command. 20284c06356bSdh */ 20294c06356bSdh if (pwrk->timer == 0) { 20304c06356bSdh mutex_exit(&pwrk->lock); 20314c06356bSdh continue; 20324c06356bSdh } 20334c06356bSdh 20344c06356bSdh /* 20354c06356bSdh * Knock off bits for the time interval. 20364c06356bSdh */ 20374c06356bSdh if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) { 20384c06356bSdh pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL); 20394c06356bSdh } else { 20404c06356bSdh pwrk->timer = 0; 20414c06356bSdh } 20424c06356bSdh if (pwrk->timer > 0) { 20434c06356bSdh mutex_exit(&pwrk->lock); 20444c06356bSdh continue; 20454c06356bSdh } 20464c06356bSdh 20474c06356bSdh /* 20484c06356bSdh * The command has now officially timed out. 20494c06356bSdh * Get the path for it. If it doesn't have 20504c06356bSdh * a phy pointer any more, it's really dead 20514c06356bSdh * and can just be put back on the free list. 20524c06356bSdh * There should *not* be any commands associated 20534c06356bSdh * with it any more. 20544c06356bSdh */ 20554c06356bSdh if (pwrk->phy == NULL) { 2056c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20574c06356bSdh "dead command with gone phy being recycled"); 20584c06356bSdh ASSERT(pwrk->xp == NULL); 20594c06356bSdh pmcs_pwork(pwp, pwrk); 20604c06356bSdh continue; 20614c06356bSdh } 20624c06356bSdh amt = sizeof (path); 20634c06356bSdh amt = min(sizeof (pwrk->phy->path), amt); 20644c06356bSdh (void) memcpy(path, pwrk->phy->path, amt); 20654c06356bSdh 20664c06356bSdh /* 20674c06356bSdh * If this is a non-SCSA command, stop here. Eventually 20684c06356bSdh * we might do something with non-SCSA commands here- 20694c06356bSdh * but so far their timeout mechanisms are handled in 20704c06356bSdh * the WAIT_FOR macro. 20714c06356bSdh */ 20724c06356bSdh if (pwrk->xp == NULL) { 2073c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20744c06356bSdh "%s: non-SCSA cmd tag 0x%x timed out", 20754c06356bSdh path, pwrk->htag); 20764c06356bSdh mutex_exit(&pwrk->lock); 20774c06356bSdh continue; 20784c06356bSdh } 20794c06356bSdh 20804c06356bSdh sp = pwrk->arg; 20814c06356bSdh ASSERT(sp != NULL); 20824c06356bSdh 20834c06356bSdh /* 20844c06356bSdh * Mark it as timed out. 20854c06356bSdh */ 20864c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT; 20874c06356bSdh CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 20884c06356bSdh #ifdef DEBUG 2089c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 20904c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d", 20914c06356bSdh path, pwrk->htag, pwrk->state, pwrk->onwire); 20924c06356bSdh #else 2093c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 20944c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x)", 20954c06356bSdh path, pwrk->htag, pwrk->state); 20964c06356bSdh #endif 20974c06356bSdh /* 20984c06356bSdh * Mark the work structure as timed out. 20994c06356bSdh */ 21004c06356bSdh pwrk->state = PMCS_WORK_STATE_TIMED_OUT; 21014c06356bSdh phyp = pwrk->phy; 21024c06356bSdh target = pwrk->xp; 21034c06356bSdh mutex_exit(&pwrk->lock); 21044c06356bSdh 21054c06356bSdh pmcs_lock_phy(phyp); 21064c06356bSdh mutex_enter(&target->statlock); 21074c06356bSdh 21084c06356bSdh /* 21094c06356bSdh * No point attempting recovery if the device is gone 21104c06356bSdh */ 2111145e0143Sdh if (target->dev_gone) { 21124c06356bSdh mutex_exit(&target->statlock); 21134c06356bSdh pmcs_unlock_phy(phyp); 2114c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 21154c06356bSdh "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE " 21164c06356bSdh "for htag 0x%08x", __func__, 2117145e0143Sdh (void *)target, pwrk->htag); 21184c06356bSdh mutex_enter(&pwrk->lock); 21194c06356bSdh if (!PMCS_COMMAND_DONE(pwrk)) { 21204c06356bSdh /* Complete this command here */ 2121c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2122c3bc407cSdh "%s: Completing cmd (htag 0x%08x) " 21234c06356bSdh "anyway", __func__, pwrk->htag); 21244c06356bSdh pwrk->dead = 1; 21254c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 21264c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 21274c06356bSdh pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 21284c06356bSdh } else { 21294c06356bSdh mutex_exit(&pwrk->lock); 21304c06356bSdh } 21314c06356bSdh continue; 21324c06356bSdh } 21334c06356bSdh 2134601c90f1SSrikanth, Ramana mutex_exit(&target->statlock); 2135601c90f1SSrikanth, Ramana rval = pmcs_abort(pwp, phyp, pwrk->htag, 0, 1); 2136601c90f1SSrikanth, Ramana if (rval) { 2137601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2138601c90f1SSrikanth, Ramana "%s: Bad status (%d) on abort of HTAG 0x%08x", 2139601c90f1SSrikanth, Ramana __func__, rval, pwrk->htag); 21404c06356bSdh pmcs_unlock_phy(phyp); 2141601c90f1SSrikanth, Ramana mutex_enter(&pwrk->lock); 2142601c90f1SSrikanth, Ramana if (!PMCS_COMMAND_DONE(pwrk)) { 2143601c90f1SSrikanth, Ramana /* Complete this command here */ 2144601c90f1SSrikanth, Ramana pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 2145601c90f1SSrikanth, Ramana "%s: Completing cmd (htag 0x%08x) " 2146601c90f1SSrikanth, Ramana "anyway", __func__, pwrk->htag); 2147601c90f1SSrikanth, Ramana if (target->dev_gone) { 2148601c90f1SSrikanth, Ramana pwrk->dead = 1; 2149601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 2150601c90f1SSrikanth, Ramana CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 2151601c90f1SSrikanth, Ramana } 2152601c90f1SSrikanth, Ramana pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 2153601c90f1SSrikanth, Ramana } else { 2154601c90f1SSrikanth, Ramana mutex_exit(&pwrk->lock); 2155601c90f1SSrikanth, Ramana } 2156601c90f1SSrikanth, Ramana pmcs_lock_phy(phyp); 2157601c90f1SSrikanth, Ramana /* 2158601c90f1SSrikanth, Ramana * No need to reschedule ABORT if we get any other 2159601c90f1SSrikanth, Ramana * status 2160601c90f1SSrikanth, Ramana */ 2161601c90f1SSrikanth, Ramana if (rval == ENOMEM) { 2162601c90f1SSrikanth, Ramana phyp->abort_sent = 0; 2163601c90f1SSrikanth, Ramana phyp->abort_pending = 1; 2164601c90f1SSrikanth, Ramana SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 2165601c90f1SSrikanth, Ramana } 21664c06356bSdh } 21674c06356bSdh pmcs_unlock_phy(phyp); 21684c06356bSdh } 21694c06356bSdh /* 21704c06356bSdh * Run any completions that may have been queued up. 21714c06356bSdh */ 21724c06356bSdh PMCS_CQ_RUN(pwp); 21734c06356bSdh } 21744c06356bSdh 21754c06356bSdh static void 21764c06356bSdh pmcs_watchdog(void *arg) 21774c06356bSdh { 21784c06356bSdh pmcs_hw_t *pwp = arg; 21794c06356bSdh 21804c06356bSdh DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t, 21814c06356bSdh pwp->config_changed); 21824c06356bSdh 21835c45adf0SJesse Butler /* 21845c45adf0SJesse Butler * Check forward progress on the chip 21855c45adf0SJesse Butler */ 21865c45adf0SJesse Butler if (++pwp->watchdog_count == PMCS_FWD_PROG_TRIGGER) { 21875c45adf0SJesse Butler pwp->watchdog_count = 0; 21885c45adf0SJesse Butler pmcs_check_forward_progress(pwp); 21895c45adf0SJesse Butler } 21905c45adf0SJesse Butler 21919aed1621SDavid Hollister /* 21929aed1621SDavid Hollister * Check to see if we need to kick discovery off again 21939aed1621SDavid Hollister */ 21949aed1621SDavid Hollister mutex_enter(&pwp->config_lock); 21959aed1621SDavid Hollister if (pwp->config_restart && 21969aed1621SDavid Hollister (ddi_get_lbolt() >= pwp->config_restart_time)) { 21979aed1621SDavid Hollister pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 21989aed1621SDavid Hollister "%s: Timer expired for re-enumeration: Start discovery", 21999aed1621SDavid Hollister __func__); 22009aed1621SDavid Hollister pwp->config_restart = B_FALSE; 22019aed1621SDavid Hollister SCHEDULE_WORK(pwp, PMCS_WORK_DISCOVER); 22029aed1621SDavid Hollister } 22039aed1621SDavid Hollister mutex_exit(&pwp->config_lock); 22049aed1621SDavid Hollister 22054c06356bSdh mutex_enter(&pwp->lock); 22064c06356bSdh if (pwp->state != STATE_RUNNING) { 22074c06356bSdh mutex_exit(&pwp->lock); 22084c06356bSdh return; 22094c06356bSdh } 22104c06356bSdh 22114c06356bSdh if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) { 22124c06356bSdh if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp, 22134c06356bSdh DDI_NOSLEEP) != DDI_SUCCESS) { 2214c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22154c06356bSdh "Could not dispatch to worker thread"); 22164c06356bSdh } 22174c06356bSdh } 22184c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 22194c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 22205c45adf0SJesse Butler 22214c06356bSdh mutex_exit(&pwp->lock); 22225c45adf0SJesse Butler 22234c06356bSdh pmcs_check_commands(pwp); 22244c06356bSdh pmcs_handle_dead_phys(pwp); 22254c06356bSdh } 22264c06356bSdh 22274c06356bSdh static int 22284c06356bSdh pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt) 22294c06356bSdh { 22304c06356bSdh int i, r, rslt = 0; 22314c06356bSdh for (i = 0; i < icnt; i++) { 22324c06356bSdh r = ddi_intr_remove_handler(pwp->ih_table[i]); 22334c06356bSdh if (r == DDI_SUCCESS) { 22344c06356bSdh continue; 22354c06356bSdh } 2236c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22374c06356bSdh "%s: unable to remove interrupt handler %d", __func__, i); 22384c06356bSdh rslt = -1; 22394c06356bSdh break; 22404c06356bSdh } 22414c06356bSdh return (rslt); 22424c06356bSdh } 22434c06356bSdh 22444c06356bSdh static int 22454c06356bSdh pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt) 22464c06356bSdh { 22474c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 22484c06356bSdh int r = ddi_intr_block_disable(&pwp->ih_table[0], 22494c06356bSdh pwp->intr_cnt); 22504c06356bSdh if (r != DDI_SUCCESS) { 2251c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22524c06356bSdh "unable to disable interrupt block"); 22534c06356bSdh return (-1); 22544c06356bSdh } 22554c06356bSdh } else { 22564c06356bSdh int i; 22574c06356bSdh for (i = 0; i < icnt; i++) { 22584c06356bSdh if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) { 22594c06356bSdh continue; 22604c06356bSdh } 2261c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22624c06356bSdh "unable to disable interrupt %d", i); 22634c06356bSdh return (-1); 22644c06356bSdh } 22654c06356bSdh } 22664c06356bSdh return (0); 22674c06356bSdh } 22684c06356bSdh 22694c06356bSdh static int 22704c06356bSdh pmcs_free_intrs(pmcs_hw_t *pwp, int icnt) 22714c06356bSdh { 22724c06356bSdh int i; 22734c06356bSdh for (i = 0; i < icnt; i++) { 22744c06356bSdh if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) { 22754c06356bSdh continue; 22764c06356bSdh } 2277c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2278c3bc407cSdh "unable to free interrupt %d", i); 22794c06356bSdh return (-1); 22804c06356bSdh } 22814c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 22824c06356bSdh pwp->ih_table_size = 0; 22834c06356bSdh return (0); 22844c06356bSdh } 22854c06356bSdh 22864c06356bSdh /* 22874c06356bSdh * Try to set up interrupts of type "type" with a minimum number of interrupts 22884c06356bSdh * of "min". 22894c06356bSdh */ 22904c06356bSdh static void 22914c06356bSdh pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min) 22924c06356bSdh { 22934c06356bSdh int rval, avail, count, actual, max; 22944c06356bSdh 22954c06356bSdh rval = ddi_intr_get_nintrs(pwp->dip, type, &count); 22964c06356bSdh if ((rval != DDI_SUCCESS) || (count < min)) { 2297c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 22984c06356bSdh "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d", 22994c06356bSdh __func__, type, rval, count, min); 23004c06356bSdh return; 23014c06356bSdh } 23024c06356bSdh 2303c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23044c06356bSdh "%s: nintrs = %d for type: %d", __func__, count, type); 23054c06356bSdh 23064c06356bSdh rval = ddi_intr_get_navail(pwp->dip, type, &avail); 23074c06356bSdh if ((rval != DDI_SUCCESS) || (avail < min)) { 2308c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23094c06356bSdh "%s: get_navail failed; type: %d rc: %d avail: %d min: %d", 23104c06356bSdh __func__, type, rval, avail, min); 23114c06356bSdh return; 23124c06356bSdh } 23134c06356bSdh 2314c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23154c06356bSdh "%s: navail = %d for type: %d", __func__, avail, type); 23164c06356bSdh 23174c06356bSdh pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t); 23184c06356bSdh pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP); 23194c06356bSdh 23204c06356bSdh switch (type) { 23214c06356bSdh case DDI_INTR_TYPE_MSIX: 23224c06356bSdh pwp->int_type = PMCS_INT_MSIX; 23234c06356bSdh max = PMCS_MAX_MSIX; 23244c06356bSdh break; 23254c06356bSdh case DDI_INTR_TYPE_MSI: 23264c06356bSdh pwp->int_type = PMCS_INT_MSI; 23274c06356bSdh max = PMCS_MAX_MSI; 23284c06356bSdh break; 23294c06356bSdh case DDI_INTR_TYPE_FIXED: 23304c06356bSdh default: 23314c06356bSdh pwp->int_type = PMCS_INT_FIXED; 23324c06356bSdh max = PMCS_MAX_FIXED; 23334c06356bSdh break; 23344c06356bSdh } 23354c06356bSdh 23364c06356bSdh rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual, 23374c06356bSdh DDI_INTR_ALLOC_NORMAL); 23384c06356bSdh if (rval != DDI_SUCCESS) { 2339c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 23404c06356bSdh "%s: ddi_intr_alloc failed; type: %d rc: %d", 23414c06356bSdh __func__, type, rval); 23424c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 23434c06356bSdh pwp->ih_table = NULL; 23444c06356bSdh pwp->ih_table_size = 0; 23454c06356bSdh pwp->intr_cnt = 0; 23464c06356bSdh pwp->int_type = PMCS_INT_NONE; 23474c06356bSdh return; 23484c06356bSdh } 23494c06356bSdh 23504c06356bSdh pwp->intr_cnt = actual; 23514c06356bSdh } 23524c06356bSdh 23534c06356bSdh /* 23544c06356bSdh * Set up interrupts. 23554c06356bSdh * We return one of three values: 23564c06356bSdh * 23574c06356bSdh * 0 - success 23584c06356bSdh * EAGAIN - failure to set up interrupts 23594c06356bSdh * EIO - "" + we're now stuck partly enabled 23604c06356bSdh * 23614c06356bSdh * If EIO is returned, we can't unload the driver. 23624c06356bSdh */ 23634c06356bSdh static int 23644c06356bSdh pmcs_setup_intr(pmcs_hw_t *pwp) 23654c06356bSdh { 23664c06356bSdh int i, r, itypes, oqv_count; 23674c06356bSdh ddi_intr_handler_t **iv_table; 23684c06356bSdh size_t iv_table_size; 23694c06356bSdh uint_t pri; 23704c06356bSdh 23714c06356bSdh if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) { 2372c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2373c3bc407cSdh "cannot get interrupt types"); 23744c06356bSdh return (EAGAIN); 23754c06356bSdh } 23764c06356bSdh 23774c06356bSdh if (disable_msix) { 23784c06356bSdh itypes &= ~DDI_INTR_TYPE_MSIX; 23794c06356bSdh } 23804c06356bSdh if (disable_msi) { 23814c06356bSdh itypes &= ~DDI_INTR_TYPE_MSI; 23824c06356bSdh } 23834c06356bSdh 23844c06356bSdh /* 23854c06356bSdh * We won't know what firmware we're running until we call pmcs_setup, 23864c06356bSdh * and we can't call pmcs_setup until we establish interrupts. 23874c06356bSdh */ 23884c06356bSdh 23894c06356bSdh pwp->int_type = PMCS_INT_NONE; 23904c06356bSdh 23914c06356bSdh /* 23924c06356bSdh * We want PMCS_MAX_MSIX vectors for MSI-X. Anything less would be 23934c06356bSdh * uncivilized. 23944c06356bSdh */ 23954c06356bSdh if (itypes & DDI_INTR_TYPE_MSIX) { 23964c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX); 23974c06356bSdh if (pwp->int_type == PMCS_INT_MSIX) { 23984c06356bSdh itypes = 0; 23994c06356bSdh } 24004c06356bSdh } 24014c06356bSdh 24024c06356bSdh if (itypes & DDI_INTR_TYPE_MSI) { 24034c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1); 24044c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 24054c06356bSdh itypes = 0; 24064c06356bSdh } 24074c06356bSdh } 24084c06356bSdh 24094c06356bSdh if (itypes & DDI_INTR_TYPE_FIXED) { 24104c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1); 24114c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 24124c06356bSdh itypes = 0; 24134c06356bSdh } 24144c06356bSdh } 24154c06356bSdh 24164c06356bSdh if (pwp->intr_cnt == 0) { 2417c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 2418c3bc407cSdh "No interrupts available"); 24194c06356bSdh return (EAGAIN); 24204c06356bSdh } 24214c06356bSdh 24224c06356bSdh iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt; 24234c06356bSdh iv_table = kmem_alloc(iv_table_size, KM_SLEEP); 24244c06356bSdh 24254c06356bSdh /* 24264c06356bSdh * Get iblock cookie and add handlers. 24274c06356bSdh */ 24284c06356bSdh switch (pwp->intr_cnt) { 24294c06356bSdh case 1: 24304c06356bSdh iv_table[0] = pmcs_all_intr; 24314c06356bSdh break; 24324c06356bSdh case 2: 24334c06356bSdh iv_table[0] = pmcs_iodone_ix; 24344c06356bSdh iv_table[1] = pmcs_nonio_ix; 24354c06356bSdh break; 24364c06356bSdh case 4: 24374c06356bSdh iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix; 24384c06356bSdh iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix; 24394c06356bSdh iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix; 24404c06356bSdh iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix; 24414c06356bSdh break; 24424c06356bSdh default: 2443c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 24444c06356bSdh "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt); 24454c06356bSdh kmem_free(iv_table, iv_table_size); 24464c06356bSdh return (EAGAIN); 24474c06356bSdh } 24484c06356bSdh 24494c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 24504c06356bSdh r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i], 24514c06356bSdh (caddr_t)pwp, NULL); 24524c06356bSdh if (r != DDI_SUCCESS) { 24534c06356bSdh kmem_free(iv_table, iv_table_size); 24544c06356bSdh if (pmcs_remove_ihandlers(pwp, i)) { 24554c06356bSdh return (EIO); 24564c06356bSdh } 24574c06356bSdh if (pmcs_free_intrs(pwp, i)) { 24584c06356bSdh return (EIO); 24594c06356bSdh } 24604c06356bSdh pwp->intr_cnt = 0; 24614c06356bSdh return (EAGAIN); 24624c06356bSdh } 24634c06356bSdh } 24644c06356bSdh 24654c06356bSdh kmem_free(iv_table, iv_table_size); 24664c06356bSdh 24674c06356bSdh if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) { 2468c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2469c3bc407cSdh "unable to get int capabilities"); 24704c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 24714c06356bSdh return (EIO); 24724c06356bSdh } 24734c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 24744c06356bSdh return (EIO); 24754c06356bSdh } 24764c06356bSdh pwp->intr_cnt = 0; 24774c06356bSdh return (EAGAIN); 24784c06356bSdh } 24794c06356bSdh 24804c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 24814c06356bSdh r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt); 24824c06356bSdh if (r != DDI_SUCCESS) { 2483c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2484c3bc407cSdh "intr blk enable failed"); 24854c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 24864c06356bSdh return (EIO); 24874c06356bSdh } 24884c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 24894c06356bSdh return (EIO); 24904c06356bSdh } 24914c06356bSdh pwp->intr_cnt = 0; 24924c06356bSdh return (EFAULT); 24934c06356bSdh } 24944c06356bSdh } else { 24954c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 24964c06356bSdh r = ddi_intr_enable(pwp->ih_table[i]); 24974c06356bSdh if (r == DDI_SUCCESS) { 24984c06356bSdh continue; 24994c06356bSdh } 2500c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 25014c06356bSdh "unable to enable interrupt %d", i); 25024c06356bSdh if (pmcs_disable_intrs(pwp, i)) { 25034c06356bSdh return (EIO); 25044c06356bSdh } 25054c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25064c06356bSdh return (EIO); 25074c06356bSdh } 25084c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 25094c06356bSdh return (EIO); 25104c06356bSdh } 25114c06356bSdh pwp->intr_cnt = 0; 25124c06356bSdh return (EAGAIN); 25134c06356bSdh } 25144c06356bSdh } 25154c06356bSdh 25164c06356bSdh /* 25174c06356bSdh * Set up locks. 25184c06356bSdh */ 25194c06356bSdh if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) { 2520c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 25214c06356bSdh "unable to get interrupt priority"); 25224c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 25234c06356bSdh return (EIO); 25244c06356bSdh } 25254c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25264c06356bSdh return (EIO); 25274c06356bSdh } 25284c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 25294c06356bSdh return (EIO); 25304c06356bSdh } 25314c06356bSdh pwp->intr_cnt = 0; 25324c06356bSdh return (EAGAIN); 25334c06356bSdh } 25344c06356bSdh 25354c06356bSdh pwp->locks_initted = 1; 25364c06356bSdh pwp->intr_pri = pri; 25374c06356bSdh mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25384c06356bSdh mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25394c06356bSdh mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25404c06356bSdh mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25414c06356bSdh mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25424c06356bSdh mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25434c06356bSdh mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25444c06356bSdh mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25454c06356bSdh mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER, 25464c06356bSdh DDI_INTR_PRI(pri)); 25474c06356bSdh #ifdef DEBUG 25484c06356bSdh mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 25494c06356bSdh #endif 25504c06356bSdh cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL); 25514c06356bSdh cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL); 25524c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 25534c06356bSdh mutex_init(&pwp->iqp_lock[i], NULL, 25544c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 25554c06356bSdh } 25564c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 25574c06356bSdh mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL, 25584c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 25594c06356bSdh cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL, 25604c06356bSdh CV_DRIVER, NULL); 25614c06356bSdh } 25624c06356bSdh 2563c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%d %s interrup%s configured", 25644c06356bSdh pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" : 25654c06356bSdh ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"), 25664c06356bSdh pwp->intr_cnt == 1? "t" : "ts"); 25674c06356bSdh 25684c06356bSdh 25694c06356bSdh /* 25704c06356bSdh * Enable Interrupts 25714c06356bSdh */ 25724c06356bSdh if (pwp->intr_cnt > PMCS_NOQ) { 25734c06356bSdh oqv_count = pwp->intr_cnt; 25744c06356bSdh } else { 25754c06356bSdh oqv_count = PMCS_NOQ; 25764c06356bSdh } 25774c06356bSdh for (pri = 0xffffffff, i = 0; i < oqv_count; i++) { 25784c06356bSdh pri ^= (1 << i); 25794c06356bSdh } 25804c06356bSdh 25814c06356bSdh mutex_enter(&pwp->lock); 25824c06356bSdh pwp->intr_mask = pri; 25834c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 25844c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 25854c06356bSdh mutex_exit(&pwp->lock); 25864c06356bSdh 25874c06356bSdh return (0); 25884c06356bSdh } 25894c06356bSdh 25904c06356bSdh static int 25914c06356bSdh pmcs_teardown_intr(pmcs_hw_t *pwp) 25924c06356bSdh { 25934c06356bSdh if (pwp->intr_cnt) { 25944c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 25954c06356bSdh return (EIO); 25964c06356bSdh } 25974c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 25984c06356bSdh return (EIO); 25994c06356bSdh } 26004c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 26014c06356bSdh return (EIO); 26024c06356bSdh } 26034c06356bSdh pwp->intr_cnt = 0; 26044c06356bSdh } 26054c06356bSdh return (0); 26064c06356bSdh } 26074c06356bSdh 26084c06356bSdh static uint_t 26094c06356bSdh pmcs_general_ix(caddr_t arg1, caddr_t arg2) 26104c06356bSdh { 26114c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26124c06356bSdh _NOTE(ARGUNUSED(arg2)); 26134c06356bSdh pmcs_general_intr(pwp); 26144c06356bSdh return (DDI_INTR_CLAIMED); 26154c06356bSdh } 26164c06356bSdh 26174c06356bSdh static uint_t 26184c06356bSdh pmcs_event_ix(caddr_t arg1, caddr_t arg2) 26194c06356bSdh { 26204c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26214c06356bSdh _NOTE(ARGUNUSED(arg2)); 26224c06356bSdh pmcs_event_intr(pwp); 26234c06356bSdh return (DDI_INTR_CLAIMED); 26244c06356bSdh } 26254c06356bSdh 26264c06356bSdh static uint_t 26274c06356bSdh pmcs_iodone_ix(caddr_t arg1, caddr_t arg2) 26284c06356bSdh { 26294c06356bSdh _NOTE(ARGUNUSED(arg2)); 26304c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26314c06356bSdh 26324c06356bSdh /* 26334c06356bSdh * It's possible that if we just turned interrupt coalescing off 26344c06356bSdh * (and thus, re-enabled auto clear for interrupts on the I/O outbound 26354c06356bSdh * queue) that there was an interrupt already pending. We use 26364c06356bSdh * io_intr_coal.int_cleared to ensure that we still drop in here and 26374c06356bSdh * clear the appropriate interrupt bit one last time. 26384c06356bSdh */ 26394c06356bSdh mutex_enter(&pwp->ict_lock); 26404c06356bSdh if (pwp->io_intr_coal.timer_on || 26414c06356bSdh (pwp->io_intr_coal.int_cleared == B_FALSE)) { 26424c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 26434c06356bSdh (1 << PMCS_OQ_IODONE)); 26444c06356bSdh pwp->io_intr_coal.int_cleared = B_TRUE; 26454c06356bSdh } 26464c06356bSdh mutex_exit(&pwp->ict_lock); 26474c06356bSdh 26484c06356bSdh pmcs_iodone_intr(pwp); 26494c06356bSdh 26504c06356bSdh return (DDI_INTR_CLAIMED); 26514c06356bSdh } 26524c06356bSdh 26534c06356bSdh static uint_t 26544c06356bSdh pmcs_fatal_ix(caddr_t arg1, caddr_t arg2) 26554c06356bSdh { 26564c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 26574c06356bSdh _NOTE(ARGUNUSED(arg2)); 26584c06356bSdh pmcs_fatal_handler(pwp); 26594c06356bSdh return (DDI_INTR_CLAIMED); 26604c06356bSdh } 26614c06356bSdh 26624c06356bSdh static uint_t 26634c06356bSdh pmcs_nonio_ix(caddr_t arg1, caddr_t arg2) 26644c06356bSdh { 26654c06356bSdh _NOTE(ARGUNUSED(arg2)); 26664c06356bSdh pmcs_hw_t *pwp = (void *)arg1; 26674c06356bSdh uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 26684c06356bSdh 26694c06356bSdh /* 26704c06356bSdh * Check for Fatal Interrupts 26714c06356bSdh */ 26724c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 26734c06356bSdh pmcs_fatal_handler(pwp); 26744c06356bSdh return (DDI_INTR_CLAIMED); 26754c06356bSdh } 26764c06356bSdh 26774c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 26784c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 26794c06356bSdh (1 << PMCS_OQ_GENERAL)); 26804c06356bSdh pmcs_general_intr(pwp); 26814c06356bSdh pmcs_event_intr(pwp); 26824c06356bSdh } 26834c06356bSdh 26844c06356bSdh return (DDI_INTR_CLAIMED); 26854c06356bSdh } 26864c06356bSdh 26874c06356bSdh static uint_t 26884c06356bSdh pmcs_all_intr(caddr_t arg1, caddr_t arg2) 26894c06356bSdh { 26904c06356bSdh _NOTE(ARGUNUSED(arg2)); 26914c06356bSdh pmcs_hw_t *pwp = (void *) arg1; 26924c06356bSdh uint32_t obdb; 26934c06356bSdh int handled = 0; 26944c06356bSdh 26954c06356bSdh obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 26964c06356bSdh 26974c06356bSdh /* 26984c06356bSdh * Check for Fatal Interrupts 26994c06356bSdh */ 27004c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 27014c06356bSdh pmcs_fatal_handler(pwp); 27024c06356bSdh return (DDI_INTR_CLAIMED); 27034c06356bSdh } 27044c06356bSdh 27054c06356bSdh /* 27064c06356bSdh * Check for Outbound Queue service needed 27074c06356bSdh */ 27084c06356bSdh if (obdb & (1 << PMCS_OQ_IODONE)) { 27094c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27104c06356bSdh (1 << PMCS_OQ_IODONE)); 27114c06356bSdh obdb ^= (1 << PMCS_OQ_IODONE); 27124c06356bSdh handled++; 27134c06356bSdh pmcs_iodone_intr(pwp); 27144c06356bSdh } 27154c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 27164c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27174c06356bSdh (1 << PMCS_OQ_GENERAL)); 27184c06356bSdh obdb ^= (1 << PMCS_OQ_GENERAL); 27194c06356bSdh handled++; 27204c06356bSdh pmcs_general_intr(pwp); 27214c06356bSdh } 27224c06356bSdh if (obdb & (1 << PMCS_OQ_EVENTS)) { 27234c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 27244c06356bSdh (1 << PMCS_OQ_EVENTS)); 27254c06356bSdh obdb ^= (1 << PMCS_OQ_EVENTS); 27264c06356bSdh handled++; 27274c06356bSdh pmcs_event_intr(pwp); 27284c06356bSdh } 27294c06356bSdh if (obdb) { 2730c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 27314c06356bSdh "interrupt bits not handled (0x%x)", obdb); 27324c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb); 27334c06356bSdh handled++; 27344c06356bSdh } 27354c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 27364c06356bSdh handled++; 27374c06356bSdh } 27384c06356bSdh return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 27394c06356bSdh } 27404c06356bSdh 27414c06356bSdh void 27424c06356bSdh pmcs_fatal_handler(pmcs_hw_t *pwp) 27434c06356bSdh { 2744c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, "Fatal Interrupt caught"); 27455c45adf0SJesse Butler 27464c06356bSdh mutex_enter(&pwp->lock); 27474c06356bSdh pwp->state = STATE_DEAD; 27484c06356bSdh 27495c45adf0SJesse Butler /* 27505c45adf0SJesse Butler * Attempt a hot reset. In case of failure, pmcs_hot_reset() will 27515c45adf0SJesse Butler * handle the failure and issue any required FM notifications. 27525c45adf0SJesse Butler * See pmcs_subr.c for more details. 27535c45adf0SJesse Butler */ 27545c45adf0SJesse Butler if (pmcs_hot_reset(pwp)) { 27555c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 27565c45adf0SJesse Butler "%s: hot reset failure", __func__); 27575c45adf0SJesse Butler } else { 27585c45adf0SJesse Butler pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 27595c45adf0SJesse Butler "%s: hot reset complete", __func__); 27605c45adf0SJesse Butler pwp->last_reset_reason = PMCS_LAST_RST_FATAL_ERROR; 27615c45adf0SJesse Butler } 27625c45adf0SJesse Butler mutex_exit(&pwp->lock); 27634c06356bSdh } 27644c06356bSdh 27654c06356bSdh /* 27664c06356bSdh * Called with PHY lock and target statlock held and scratch acquired. 27674c06356bSdh */ 27684c06356bSdh boolean_t 27694c06356bSdh pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt) 27704c06356bSdh { 27714c06356bSdh pmcs_phy_t *pptr = tgt->phy; 27724c06356bSdh 27734c06356bSdh switch (pptr->dtype) { 27744c06356bSdh case SAS: 27754c06356bSdh case EXPANDER: 27764c06356bSdh break; 27774c06356bSdh case SATA: 27784c06356bSdh tgt->ca = 1; 27794c06356bSdh break; 27804c06356bSdh default: 2781c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27824c06356bSdh "%s: Target %p has PHY %p with invalid dtype", 27834c06356bSdh __func__, (void *)tgt, (void *)pptr); 27844c06356bSdh return (B_FALSE); 27854c06356bSdh } 27864c06356bSdh 27874c06356bSdh tgt->new = 1; 27884c06356bSdh tgt->dev_gone = 0; 27894c06356bSdh tgt->recover_wait = 0; 27904c06356bSdh 2791c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27924c06356bSdh "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__, 27934c06356bSdh pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 27944c06356bSdh 27954c06356bSdh if (pmcs_add_new_device(pwp, tgt) != B_TRUE) { 2796c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 27974c06356bSdh "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__, 27984c06356bSdh tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 27994c06356bSdh mutex_destroy(&tgt->statlock); 28004c06356bSdh mutex_destroy(&tgt->wqlock); 28014c06356bSdh mutex_destroy(&tgt->aqlock); 28024c06356bSdh return (B_FALSE); 28034c06356bSdh } 28044c06356bSdh 28054c06356bSdh return (B_TRUE); 28064c06356bSdh } 28074c06356bSdh 28084c06356bSdh /* 28094c06356bSdh * Called with softstate lock held 28104c06356bSdh */ 28114c06356bSdh void 28124c06356bSdh pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 28134c06356bSdh { 28144c06356bSdh pmcs_xscsi_t *xp; 28154c06356bSdh unsigned int vtgt; 28164c06356bSdh 28174c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 28184c06356bSdh 28194c06356bSdh for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) { 28204c06356bSdh xp = pwp->targets[vtgt]; 28214c06356bSdh if (xp == NULL) { 28224c06356bSdh continue; 28234c06356bSdh } 28244c06356bSdh 28254c06356bSdh mutex_enter(&xp->statlock); 28264c06356bSdh if (xp->phy == pptr) { 28274c06356bSdh if (xp->new) { 28284c06356bSdh xp->new = 0; 2829c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 28304c06356bSdh "cancel config of vtgt %u", vtgt); 28314c06356bSdh } else { 2832b18a19c2SJesse Butler pmcs_clear_xp(pwp, xp); 2833c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 2834b18a19c2SJesse Butler "Removed tgt 0x%p vtgt %u", 28354c06356bSdh (void *)xp, vtgt); 28364c06356bSdh } 28374c06356bSdh mutex_exit(&xp->statlock); 28384c06356bSdh break; 28394c06356bSdh } 28404c06356bSdh mutex_exit(&xp->statlock); 28414c06356bSdh } 28424c06356bSdh } 28434c06356bSdh 28444c06356bSdh void 2845c3bc407cSdh pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level, 2846c3bc407cSdh pmcs_phy_t *phyp, pmcs_xscsi_t *target, const char *fmt, ...) 28474c06356bSdh { 28484c06356bSdh va_list ap; 28494c06356bSdh int written = 0; 28504c06356bSdh char *ptr; 28514c06356bSdh uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1; 28524c06356bSdh boolean_t system_log; 28534c06356bSdh int system_log_level; 28544c06356bSdh 28554c06356bSdh switch (level) { 28564c06356bSdh case PMCS_PRT_DEBUG_DEVEL: 28574c06356bSdh case PMCS_PRT_DEBUG_DEV_STATE: 28584c06356bSdh case PMCS_PRT_DEBUG_PHY_LOCKING: 28594c06356bSdh case PMCS_PRT_DEBUG_SCSI_STATUS: 28604c06356bSdh case PMCS_PRT_DEBUG_UNDERFLOW: 28614c06356bSdh case PMCS_PRT_DEBUG_CONFIG: 28624c06356bSdh case PMCS_PRT_DEBUG_IPORT: 28634c06356bSdh case PMCS_PRT_DEBUG_MAP: 28644c06356bSdh case PMCS_PRT_DEBUG3: 28654c06356bSdh case PMCS_PRT_DEBUG2: 28664c06356bSdh case PMCS_PRT_DEBUG1: 28674c06356bSdh case PMCS_PRT_DEBUG: 28684c06356bSdh system_log = B_FALSE; 28694c06356bSdh break; 28704c06356bSdh case PMCS_PRT_INFO: 28714c06356bSdh system_log = B_TRUE; 28724c06356bSdh system_log_level = CE_CONT; 28734c06356bSdh break; 28744c06356bSdh case PMCS_PRT_WARN: 28754c06356bSdh system_log = B_TRUE; 28764c06356bSdh system_log_level = CE_NOTE; 28774c06356bSdh break; 28784c06356bSdh case PMCS_PRT_ERR: 28794c06356bSdh system_log = B_TRUE; 28804c06356bSdh system_log_level = CE_WARN; 28814c06356bSdh break; 28824c06356bSdh default: 28834c06356bSdh return; 28844c06356bSdh } 28854c06356bSdh 28864c06356bSdh mutex_enter(&pmcs_trace_lock); 28874c06356bSdh gethrestime(&pmcs_tbuf_ptr->timestamp); 28884c06356bSdh ptr = pmcs_tbuf_ptr->buf; 2889c3bc407cSdh 2890c3bc407cSdh /* 2891c3bc407cSdh * Store the pertinent PHY and target information if there is any 2892c3bc407cSdh */ 2893c3bc407cSdh if (target == NULL) { 2894c3bc407cSdh pmcs_tbuf_ptr->target_num = PMCS_INVALID_TARGET_NUM; 2895c3bc407cSdh pmcs_tbuf_ptr->target_ua[0] = '\0'; 2896c3bc407cSdh } else { 2897c3bc407cSdh pmcs_tbuf_ptr->target_num = target->target_num; 2898c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->target_ua, target->ua, 2899c3bc407cSdh PMCS_TBUF_UA_MAX_SIZE); 2900c3bc407cSdh } 2901c3bc407cSdh 2902c3bc407cSdh if (phyp == NULL) { 2903c3bc407cSdh (void) memset(pmcs_tbuf_ptr->phy_sas_address, 0, 8); 2904c3bc407cSdh pmcs_tbuf_ptr->phy_path[0] = '\0'; 2905c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = NOTHING; 2906c3bc407cSdh } else { 2907c3bc407cSdh (void) memcpy(pmcs_tbuf_ptr->phy_sas_address, 2908c3bc407cSdh phyp->sas_address, 8); 2909c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->phy_path, phyp->path, 32); 2910c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = phyp->dtype; 2911c3bc407cSdh } 2912c3bc407cSdh 29134c06356bSdh written += snprintf(ptr, elem_size, "pmcs%d:%d: ", 29144c06356bSdh ddi_get_instance(pwp->dip), level); 29154c06356bSdh ptr += strlen(ptr); 29164c06356bSdh va_start(ap, fmt); 29174c06356bSdh written += vsnprintf(ptr, elem_size - written, fmt, ap); 29184c06356bSdh va_end(ap); 29194c06356bSdh if (written > elem_size - 1) { 29204c06356bSdh /* Indicate truncation */ 29214c06356bSdh pmcs_tbuf_ptr->buf[elem_size - 1] = '+'; 29224c06356bSdh } 29234c06356bSdh if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) { 29244c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 29254c06356bSdh pmcs_tbuf_wrap = B_TRUE; 29264c06356bSdh pmcs_tbuf_idx = 0; 29274c06356bSdh } else { 29284c06356bSdh ++pmcs_tbuf_ptr; 29294c06356bSdh } 29304c06356bSdh mutex_exit(&pmcs_trace_lock); 29314c06356bSdh 29324c06356bSdh /* 29334c06356bSdh * When pmcs_force_syslog in non-zero, everything goes also 29344c06356bSdh * to syslog, at CE_CONT level. 29354c06356bSdh */ 29364c06356bSdh if (pmcs_force_syslog) { 29374c06356bSdh system_log = B_TRUE; 29384c06356bSdh system_log_level = CE_CONT; 29394c06356bSdh } 29404c06356bSdh 29414c06356bSdh /* 29424c06356bSdh * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also 29434c06356bSdh * goes to syslog. 29444c06356bSdh */ 29454c06356bSdh if (system_log) { 29464c06356bSdh char local[196]; 29474c06356bSdh 29484c06356bSdh switch (system_log_level) { 29494c06356bSdh case CE_CONT: 29504c06356bSdh (void) snprintf(local, sizeof (local), "%sINFO: ", 29514c06356bSdh pmcs_console ? "" : "?"); 29524c06356bSdh break; 29534c06356bSdh case CE_NOTE: 29544c06356bSdh case CE_WARN: 29554c06356bSdh local[0] = 0; 29564c06356bSdh break; 29574c06356bSdh default: 29584c06356bSdh return; 29594c06356bSdh } 29604c06356bSdh 29614c06356bSdh ptr = local; 29624c06356bSdh ptr += strlen(local); 29634c06356bSdh (void) snprintf(ptr, (sizeof (local)) - 29644c06356bSdh ((size_t)ptr - (size_t)local), "pmcs%d: ", 29654c06356bSdh ddi_get_instance(pwp->dip)); 29664c06356bSdh ptr += strlen(ptr); 29674c06356bSdh va_start(ap, fmt); 29684c06356bSdh (void) vsnprintf(ptr, 29694c06356bSdh (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap); 29704c06356bSdh va_end(ap); 29714c06356bSdh if (level == CE_CONT) { 29724c06356bSdh (void) strlcat(local, "\n", sizeof (local)); 29734c06356bSdh } 29744c06356bSdh cmn_err(system_log_level, local); 29754c06356bSdh } 29764c06356bSdh 29774c06356bSdh } 29784c06356bSdh 29794c06356bSdh /* 29804c06356bSdh * pmcs_acquire_scratch 29814c06356bSdh * 29824c06356bSdh * If "wait" is true, the caller will wait until it can acquire the scratch. 29834c06356bSdh * This implies the caller needs to be in a context where spinning for an 29844c06356bSdh * indeterminate amount of time is acceptable. 29854c06356bSdh */ 29864c06356bSdh int 29874c06356bSdh pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait) 29884c06356bSdh { 29894c06356bSdh int rval; 29904c06356bSdh 29914c06356bSdh if (!wait) { 29924c06356bSdh return (atomic_swap_8(&pwp->scratch_locked, 1)); 29934c06356bSdh } 29944c06356bSdh 29954c06356bSdh /* 29964c06356bSdh * Caller will wait for scratch. 29974c06356bSdh */ 29984c06356bSdh while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) { 29994c06356bSdh drv_usecwait(100); 30004c06356bSdh } 30014c06356bSdh 30024c06356bSdh return (rval); 30034c06356bSdh } 30044c06356bSdh 30054c06356bSdh void 30064c06356bSdh pmcs_release_scratch(pmcs_hw_t *pwp) 30074c06356bSdh { 30084c06356bSdh pwp->scratch_locked = 0; 30094c06356bSdh } 30104c06356bSdh 30114c06356bSdh static void 30124c06356bSdh pmcs_create_phy_stats(pmcs_iport_t *iport) 30134c06356bSdh { 30144c06356bSdh sas_phy_stats_t *ps; 30154c06356bSdh pmcs_hw_t *pwp; 30164c06356bSdh pmcs_phy_t *phyp; 30174c06356bSdh int ndata; 30184c06356bSdh char ks_name[KSTAT_STRLEN]; 30194c06356bSdh 30204c06356bSdh ASSERT(iport != NULL); 30214c06356bSdh pwp = iport->pwp; 30224c06356bSdh ASSERT(pwp != NULL); 30234c06356bSdh 30244c06356bSdh mutex_enter(&iport->lock); 30254c06356bSdh 30264c06356bSdh for (phyp = list_head(&iport->phys); 30274c06356bSdh phyp != NULL; 30284c06356bSdh phyp = list_next(&iport->phys, phyp)) { 30294c06356bSdh 30304c06356bSdh pmcs_lock_phy(phyp); 30314c06356bSdh 30324c06356bSdh if (phyp->phy_stats != NULL) { 30334c06356bSdh pmcs_unlock_phy(phyp); 30344c06356bSdh /* We've already created this kstat instance */ 30354c06356bSdh continue; 30364c06356bSdh } 30374c06356bSdh 30384c06356bSdh ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t)); 30394c06356bSdh 30404c06356bSdh (void) snprintf(ks_name, sizeof (ks_name), 30414c06356bSdh "%s.%llx.%d.%d", ddi_driver_name(iport->dip), 30424c06356bSdh (longlong_t)pwp->sas_wwns[0], 30434c06356bSdh ddi_get_instance(iport->dip), phyp->phynum); 30444c06356bSdh 30454c06356bSdh phyp->phy_stats = kstat_create("pmcs", 30464c06356bSdh ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS, 30474c06356bSdh KSTAT_TYPE_NAMED, ndata, 0); 30484c06356bSdh 30494c06356bSdh if (phyp->phy_stats == NULL) { 30504c06356bSdh pmcs_unlock_phy(phyp); 3051c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 30524c06356bSdh "%s: Failed to create %s kstats", __func__, 30534c06356bSdh ks_name); 30544c06356bSdh continue; 30554c06356bSdh } 30564c06356bSdh 30574c06356bSdh ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data; 30584c06356bSdh 30594c06356bSdh kstat_named_init(&ps->seconds_since_last_reset, 30604c06356bSdh "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG); 30614c06356bSdh kstat_named_init(&ps->tx_frames, 30624c06356bSdh "TxFrames", KSTAT_DATA_ULONGLONG); 30634c06356bSdh kstat_named_init(&ps->rx_frames, 30644c06356bSdh "RxFrames", KSTAT_DATA_ULONGLONG); 30654c06356bSdh kstat_named_init(&ps->tx_words, 30664c06356bSdh "TxWords", KSTAT_DATA_ULONGLONG); 30674c06356bSdh kstat_named_init(&ps->rx_words, 30684c06356bSdh "RxWords", KSTAT_DATA_ULONGLONG); 30694c06356bSdh kstat_named_init(&ps->invalid_dword_count, 30704c06356bSdh "InvalidDwordCount", KSTAT_DATA_ULONGLONG); 30714c06356bSdh kstat_named_init(&ps->running_disparity_error_count, 30724c06356bSdh "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG); 30734c06356bSdh kstat_named_init(&ps->loss_of_dword_sync_count, 30744c06356bSdh "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG); 30754c06356bSdh kstat_named_init(&ps->phy_reset_problem_count, 30764c06356bSdh "PhyResetProblemCount", KSTAT_DATA_ULONGLONG); 30774c06356bSdh 30784c06356bSdh phyp->phy_stats->ks_private = phyp; 30794c06356bSdh phyp->phy_stats->ks_update = pmcs_update_phy_stats; 30804c06356bSdh kstat_install(phyp->phy_stats); 30814c06356bSdh pmcs_unlock_phy(phyp); 30824c06356bSdh } 30834c06356bSdh 30844c06356bSdh mutex_exit(&iport->lock); 30854c06356bSdh } 30864c06356bSdh 30874c06356bSdh int 30884c06356bSdh pmcs_update_phy_stats(kstat_t *ks, int rw) 30894c06356bSdh { 30904c06356bSdh int val, ret = DDI_FAILURE; 30914c06356bSdh pmcs_phy_t *pptr = (pmcs_phy_t *)ks->ks_private; 30924c06356bSdh pmcs_hw_t *pwp = pptr->pwp; 30934c06356bSdh sas_phy_stats_t *ps = ks->ks_data; 30944c06356bSdh 30954c06356bSdh _NOTE(ARGUNUSED(rw)); 30964c06356bSdh ASSERT((pptr != NULL) && (pwp != NULL)); 30974c06356bSdh 30984c06356bSdh /* 30994c06356bSdh * We just want to lock against other invocations of kstat; 31004c06356bSdh * we don't need to pmcs_lock_phy() for this. 31014c06356bSdh */ 31024c06356bSdh mutex_enter(&pptr->phy_lock); 31034c06356bSdh 31044c06356bSdh /* Get Stats from Chip */ 31054c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum); 31064c06356bSdh if (val == DDI_FAILURE) 31074c06356bSdh goto fail; 31084c06356bSdh ps->invalid_dword_count.value.ull = (unsigned long long)val; 31094c06356bSdh 31104c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum); 31114c06356bSdh if (val == DDI_FAILURE) 31124c06356bSdh goto fail; 31134c06356bSdh ps->running_disparity_error_count.value.ull = (unsigned long long)val; 31144c06356bSdh 31154c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum); 31164c06356bSdh if (val == DDI_FAILURE) 31174c06356bSdh goto fail; 31184c06356bSdh ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val; 31194c06356bSdh 31204c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum); 31214c06356bSdh if (val == DDI_FAILURE) 31224c06356bSdh goto fail; 31234c06356bSdh ps->phy_reset_problem_count.value.ull = (unsigned long long)val; 31244c06356bSdh 31254c06356bSdh ret = DDI_SUCCESS; 31264c06356bSdh fail: 31274c06356bSdh mutex_exit(&pptr->phy_lock); 31284c06356bSdh return (ret); 31294c06356bSdh } 31304c06356bSdh 31314c06356bSdh static void 31324c06356bSdh pmcs_destroy_phy_stats(pmcs_iport_t *iport) 31334c06356bSdh { 31344c06356bSdh pmcs_phy_t *phyp; 31354c06356bSdh 31364c06356bSdh ASSERT(iport != NULL); 31374c06356bSdh mutex_enter(&iport->lock); 31384c06356bSdh phyp = iport->pptr; 31394c06356bSdh if (phyp == NULL) { 31404c06356bSdh mutex_exit(&iport->lock); 31414c06356bSdh return; 31424c06356bSdh } 31434c06356bSdh 31444c06356bSdh pmcs_lock_phy(phyp); 31454c06356bSdh if (phyp->phy_stats != NULL) { 31464c06356bSdh kstat_delete(phyp->phy_stats); 31474c06356bSdh phyp->phy_stats = NULL; 31484c06356bSdh } 31494c06356bSdh pmcs_unlock_phy(phyp); 31504c06356bSdh 31514c06356bSdh mutex_exit(&iport->lock); 31524c06356bSdh } 31534c06356bSdh 31544c06356bSdh /*ARGSUSED*/ 31554c06356bSdh static int 31564c06356bSdh pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) 31574c06356bSdh { 31584c06356bSdh /* 31594c06356bSdh * as the driver can always deal with an error in any dma or 31604c06356bSdh * access handle, we can just return the fme_status value. 31614c06356bSdh */ 31624c06356bSdh pci_ereport_post(dip, err, NULL); 31634c06356bSdh return (err->fme_status); 31644c06356bSdh } 31654c06356bSdh 31664c06356bSdh static void 31674c06356bSdh pmcs_fm_init(pmcs_hw_t *pwp) 31684c06356bSdh { 31694c06356bSdh ddi_iblock_cookie_t fm_ibc; 31704c06356bSdh 31714c06356bSdh /* Only register with IO Fault Services if we have some capability */ 31724c06356bSdh if (pwp->fm_capabilities) { 31734c06356bSdh /* Adjust access and dma attributes for FMA */ 3174837c1ac4SStephen Hanson pwp->reg_acc_attr.devacc_attr_access = DDI_FLAGERR_ACC; 31754c06356bSdh pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31764c06356bSdh pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31774c06356bSdh pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31784c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 31794c06356bSdh 31804c06356bSdh /* 31814c06356bSdh * Register capabilities with IO Fault Services. 31824c06356bSdh */ 31834c06356bSdh ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc); 31844c06356bSdh 31854c06356bSdh /* 31864c06356bSdh * Initialize pci ereport capabilities if ereport 31874c06356bSdh * capable (should always be.) 31884c06356bSdh */ 31894c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 31904c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 31914c06356bSdh pci_ereport_setup(pwp->dip); 31924c06356bSdh } 31934c06356bSdh 31944c06356bSdh /* 31954c06356bSdh * Register error callback if error callback capable. 31964c06356bSdh */ 31974c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 31984c06356bSdh ddi_fm_handler_register(pwp->dip, 31994c06356bSdh pmcs_fm_error_cb, (void *) pwp); 32004c06356bSdh } 32014c06356bSdh } 32024c06356bSdh } 32034c06356bSdh 32044c06356bSdh static void 32054c06356bSdh pmcs_fm_fini(pmcs_hw_t *pwp) 32064c06356bSdh { 32074c06356bSdh /* Only unregister FMA capabilities if registered */ 32084c06356bSdh if (pwp->fm_capabilities) { 32094c06356bSdh /* 32104c06356bSdh * Un-register error callback if error callback capable. 32114c06356bSdh */ 32124c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32134c06356bSdh ddi_fm_handler_unregister(pwp->dip); 32144c06356bSdh } 32154c06356bSdh 32164c06356bSdh /* 32174c06356bSdh * Release any resources allocated by pci_ereport_setup() 32184c06356bSdh */ 32194c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 32204c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 32214c06356bSdh pci_ereport_teardown(pwp->dip); 32224c06356bSdh } 32234c06356bSdh 32244c06356bSdh /* Unregister from IO Fault Services */ 32254c06356bSdh ddi_fm_fini(pwp->dip); 32264c06356bSdh 32274c06356bSdh /* Adjust access and dma attributes for FMA */ 3228837c1ac4SStephen Hanson pwp->reg_acc_attr.devacc_attr_access = DDI_DEFAULT_ACC; 32294c06356bSdh pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32304c06356bSdh pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32314c06356bSdh pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32324c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 32334c06356bSdh } 32344c06356bSdh } 32354c06356bSdh 32364c06356bSdh static boolean_t 32374c06356bSdh pmcs_fabricate_wwid(pmcs_hw_t *pwp) 32384c06356bSdh { 32394c06356bSdh char *cp, c; 32404c06356bSdh uint64_t adr; 32414c06356bSdh int i; 32424c06356bSdh 32434c06356bSdh cp = &c; 32444c06356bSdh (void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr); 32456745c559SJesse Butler 32464c06356bSdh if (adr == 0) { 3247c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 32484c06356bSdh "%s: No serial number available to fabricate WWN", 32494c06356bSdh __func__); 32506745c559SJesse Butler 32516745c559SJesse Butler adr = (uint64_t)gethrtime(); 32524c06356bSdh } 32536745c559SJesse Butler 32544c06356bSdh adr <<= 8; 32554c06356bSdh adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52); 32564c06356bSdh adr |= (5ULL << 60); 32576745c559SJesse Butler 32584c06356bSdh for (i = 0; i < PMCS_MAX_PORTS; i++) { 32594c06356bSdh pwp->sas_wwns[i] = adr + i; 32604c06356bSdh } 32614c06356bSdh 32624c06356bSdh return (B_TRUE); 32634c06356bSdh } 3264