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 204c06356bSdh * 214c06356bSdh * 224c06356bSdh * Copyright 2009 Sun Microsystems, Inc. All rights reserved. 234c06356bSdh * Use is subject to license terms. 244c06356bSdh */ 254c06356bSdh #include <sys/scsi/adapters/pmcs/pmcs.h> 264c06356bSdh 2747b47c8cSdh #define PMCS_DRIVER_VERSION "pmcs HBA device driver" 284c06356bSdh 294c06356bSdh static char *pmcs_driver_rev = PMCS_DRIVER_VERSION; 304c06356bSdh 314c06356bSdh /* 324c06356bSdh * Non-DDI Compliant stuff 334c06356bSdh */ 344c06356bSdh extern char hw_serial[]; 354c06356bSdh 364c06356bSdh /* 374c06356bSdh * Global driver data 384c06356bSdh */ 394c06356bSdh void *pmcs_softc_state = NULL; 404c06356bSdh void *pmcs_iport_softstate = NULL; 414c06356bSdh 424c06356bSdh /* 434c06356bSdh * Tracing and Logging info 444c06356bSdh */ 454c06356bSdh pmcs_tbuf_t *pmcs_tbuf = NULL; 464c06356bSdh uint32_t pmcs_tbuf_num_elems = 0; 474c06356bSdh pmcs_tbuf_t *pmcs_tbuf_ptr; 484c06356bSdh uint32_t pmcs_tbuf_idx = 0; 494c06356bSdh boolean_t pmcs_tbuf_wrap = B_FALSE; 504c06356bSdh static kmutex_t pmcs_trace_lock; 514c06356bSdh 524c06356bSdh /* 534c06356bSdh * If pmcs_force_syslog value is non-zero, all messages put in the trace log 544c06356bSdh * will also be sent to system log. 554c06356bSdh */ 564c06356bSdh int pmcs_force_syslog = 0; 574c06356bSdh int pmcs_console = 0; 584c06356bSdh 594c06356bSdh /* 604c06356bSdh * External References 614c06356bSdh */ 624c06356bSdh extern int ncpus_online; 634c06356bSdh 644c06356bSdh /* 654c06356bSdh * Local static data 664c06356bSdh */ 674c06356bSdh static int fwlog_level = 3; 684c06356bSdh static int physpeed = PHY_LINK_ALL; 694c06356bSdh static int phymode = PHY_LM_AUTO; 704c06356bSdh static int block_mask = 0; 714c06356bSdh static int phymap_usec = 3 * MICROSEC; 724c06356bSdh static int iportmap_usec = 2 * MICROSEC; 734c06356bSdh 744c06356bSdh #ifdef DEBUG 754c06356bSdh static int debug_mask = 1; 764c06356bSdh #else 774c06356bSdh static int debug_mask = 0; 784c06356bSdh #endif 794c06356bSdh 804c06356bSdh #ifdef DISABLE_MSIX 814c06356bSdh static int disable_msix = 1; 824c06356bSdh #else 834c06356bSdh static int disable_msix = 0; 844c06356bSdh #endif 854c06356bSdh 864c06356bSdh #ifdef DISABLE_MSI 874c06356bSdh static int disable_msi = 1; 884c06356bSdh #else 894c06356bSdh static int disable_msi = 0; 904c06356bSdh #endif 914c06356bSdh 924c06356bSdh static uint16_t maxqdepth = 0xfffe; 934c06356bSdh 944c06356bSdh /* 954c06356bSdh * Local prototypes 964c06356bSdh */ 974c06356bSdh static int pmcs_attach(dev_info_t *, ddi_attach_cmd_t); 984c06356bSdh static int pmcs_detach(dev_info_t *, ddi_detach_cmd_t); 994c06356bSdh static int pmcs_unattach(pmcs_hw_t *); 1004c06356bSdh static int pmcs_iport_unattach(pmcs_iport_t *); 1014c06356bSdh static int pmcs_add_more_chunks(pmcs_hw_t *, unsigned long); 1024c06356bSdh static void pmcs_watchdog(void *); 1034c06356bSdh static int pmcs_setup_intr(pmcs_hw_t *); 1044c06356bSdh static int pmcs_teardown_intr(pmcs_hw_t *); 1054c06356bSdh 1064c06356bSdh static uint_t pmcs_nonio_ix(caddr_t, caddr_t); 1074c06356bSdh static uint_t pmcs_general_ix(caddr_t, caddr_t); 1084c06356bSdh static uint_t pmcs_event_ix(caddr_t, caddr_t); 1094c06356bSdh static uint_t pmcs_iodone_ix(caddr_t, caddr_t); 1104c06356bSdh static uint_t pmcs_fatal_ix(caddr_t, caddr_t); 1114c06356bSdh static uint_t pmcs_all_intr(caddr_t, caddr_t); 1124c06356bSdh static int pmcs_quiesce(dev_info_t *dip); 1134c06356bSdh static boolean_t pmcs_fabricate_wwid(pmcs_hw_t *); 1144c06356bSdh 1154c06356bSdh static void pmcs_create_phy_stats(pmcs_iport_t *); 1164c06356bSdh int pmcs_update_phy_stats(kstat_t *, int); 1174c06356bSdh static void pmcs_destroy_phy_stats(pmcs_iport_t *); 1184c06356bSdh 1194c06356bSdh static void pmcs_fm_fini(pmcs_hw_t *pwp); 1204c06356bSdh static void pmcs_fm_init(pmcs_hw_t *pwp); 1214c06356bSdh static int pmcs_fm_error_cb(dev_info_t *dip, 1224c06356bSdh ddi_fm_error_t *err, const void *impl_data); 1234c06356bSdh 1244c06356bSdh /* 1254c06356bSdh * Local configuration data 1264c06356bSdh */ 1274c06356bSdh static struct dev_ops pmcs_ops = { 1284c06356bSdh DEVO_REV, /* devo_rev, */ 1294c06356bSdh 0, /* refcnt */ 1304c06356bSdh ddi_no_info, /* info */ 1314c06356bSdh nulldev, /* identify */ 1324c06356bSdh nulldev, /* probe */ 1334c06356bSdh pmcs_attach, /* attach */ 1344c06356bSdh pmcs_detach, /* detach */ 1354c06356bSdh nodev, /* reset */ 1364c06356bSdh NULL, /* driver operations */ 1374c06356bSdh NULL, /* bus operations */ 1384c06356bSdh ddi_power, /* power management */ 1394c06356bSdh pmcs_quiesce /* quiesce */ 1404c06356bSdh }; 1414c06356bSdh 1424c06356bSdh static struct modldrv modldrv = { 1434c06356bSdh &mod_driverops, 1444c06356bSdh PMCS_DRIVER_VERSION, 1454c06356bSdh &pmcs_ops, /* driver ops */ 1464c06356bSdh }; 1474c06356bSdh static struct modlinkage modlinkage = { 1484c06356bSdh MODREV_1, &modldrv, NULL 1494c06356bSdh }; 1504c06356bSdh 1514c06356bSdh const ddi_dma_attr_t pmcs_dattr = { 1524c06356bSdh DMA_ATTR_V0, /* dma_attr version */ 1534c06356bSdh 0x0000000000000000ull, /* dma_attr_addr_lo */ 1544c06356bSdh 0xFFFFFFFFFFFFFFFFull, /* dma_attr_addr_hi */ 1554c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_count_max */ 1564c06356bSdh 0x0000000000000001ull, /* dma_attr_align */ 1574c06356bSdh 0x00000078, /* dma_attr_burstsizes */ 1584c06356bSdh 0x00000001, /* dma_attr_minxfer */ 1594c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_maxxfer */ 1604c06356bSdh 0x00000000FFFFFFFFull, /* dma_attr_seg */ 1614c06356bSdh 1, /* dma_attr_sgllen */ 1624c06356bSdh 512, /* dma_attr_granular */ 1634c06356bSdh 0 /* dma_attr_flags */ 1644c06356bSdh }; 1654c06356bSdh 1664c06356bSdh static ddi_device_acc_attr_t rattr = { 1674c06356bSdh DDI_DEVICE_ATTR_V0, 1684c06356bSdh DDI_STRUCTURE_LE_ACC, 1694c06356bSdh DDI_STRICTORDER_ACC, 1704c06356bSdh DDI_DEFAULT_ACC 1714c06356bSdh }; 1724c06356bSdh 1734c06356bSdh 1744c06356bSdh /* 1754c06356bSdh * Attach/Detach functions 1764c06356bSdh */ 1774c06356bSdh 1784c06356bSdh int 1794c06356bSdh _init(void) 1804c06356bSdh { 1814c06356bSdh int ret; 1824c06356bSdh 1834c06356bSdh ret = ddi_soft_state_init(&pmcs_softc_state, sizeof (pmcs_hw_t), 1); 1844c06356bSdh if (ret != 0) { 1854c06356bSdh cmn_err(CE_WARN, "?soft state init failed for pmcs"); 1864c06356bSdh return (ret); 1874c06356bSdh } 1884c06356bSdh 1894c06356bSdh if ((ret = scsi_hba_init(&modlinkage)) != 0) { 1904c06356bSdh cmn_err(CE_WARN, "?scsi_hba_init failed for pmcs"); 1914c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 1924c06356bSdh return (ret); 1934c06356bSdh } 1944c06356bSdh 1954c06356bSdh /* 1964c06356bSdh * Allocate soft state for iports 1974c06356bSdh */ 1984c06356bSdh ret = ddi_soft_state_init(&pmcs_iport_softstate, 1994c06356bSdh sizeof (pmcs_iport_t), 2); 2004c06356bSdh if (ret != 0) { 2014c06356bSdh cmn_err(CE_WARN, "?iport soft state init failed for pmcs"); 2024c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2034c06356bSdh return (ret); 2044c06356bSdh } 2054c06356bSdh 2064c06356bSdh ret = mod_install(&modlinkage); 2074c06356bSdh if (ret != 0) { 2084c06356bSdh cmn_err(CE_WARN, "?mod_install failed for pmcs (%d)", ret); 2094c06356bSdh scsi_hba_fini(&modlinkage); 2104c06356bSdh ddi_soft_state_fini(&pmcs_iport_softstate); 2114c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2124c06356bSdh return (ret); 2134c06356bSdh } 2144c06356bSdh 2154c06356bSdh /* Initialize the global trace lock */ 2164c06356bSdh mutex_init(&pmcs_trace_lock, NULL, MUTEX_DRIVER, NULL); 2174c06356bSdh 2184c06356bSdh return (0); 2194c06356bSdh } 2204c06356bSdh 2214c06356bSdh int 2224c06356bSdh _fini(void) 2234c06356bSdh { 2244c06356bSdh int ret; 2254c06356bSdh if ((ret = mod_remove(&modlinkage)) != 0) { 2264c06356bSdh return (ret); 2274c06356bSdh } 2284c06356bSdh scsi_hba_fini(&modlinkage); 2294c06356bSdh 2304c06356bSdh /* Free pmcs log buffer and destroy the global lock */ 2314c06356bSdh if (pmcs_tbuf) { 2324c06356bSdh kmem_free(pmcs_tbuf, 2334c06356bSdh pmcs_tbuf_num_elems * sizeof (pmcs_tbuf_t)); 2344c06356bSdh pmcs_tbuf = NULL; 2354c06356bSdh } 2364c06356bSdh mutex_destroy(&pmcs_trace_lock); 2374c06356bSdh 2384c06356bSdh ddi_soft_state_fini(&pmcs_iport_softstate); 2394c06356bSdh ddi_soft_state_fini(&pmcs_softc_state); 2404c06356bSdh return (0); 2414c06356bSdh } 2424c06356bSdh 2434c06356bSdh int 2444c06356bSdh _info(struct modinfo *modinfop) 2454c06356bSdh { 2464c06356bSdh return (mod_info(&modlinkage, modinfop)); 2474c06356bSdh } 2484c06356bSdh 2494c06356bSdh static int 2504c06356bSdh pmcs_iport_attach(dev_info_t *dip) 2514c06356bSdh { 2524c06356bSdh pmcs_iport_t *iport; 2534c06356bSdh pmcs_hw_t *pwp; 2544c06356bSdh scsi_hba_tran_t *tran; 2554c06356bSdh void *ua_priv = NULL; 2564c06356bSdh char *iport_ua; 2574c06356bSdh char *init_port; 2584c06356bSdh int hba_inst; 2594c06356bSdh int inst; 2604c06356bSdh 2614c06356bSdh hba_inst = ddi_get_instance(ddi_get_parent(dip)); 2624c06356bSdh inst = ddi_get_instance(dip); 2634c06356bSdh 2644c06356bSdh pwp = ddi_get_soft_state(pmcs_softc_state, hba_inst); 2654c06356bSdh if (pwp == NULL) { 266*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2674c06356bSdh "%s: iport%d attach invoked with NULL parent (HBA) node)", 2684c06356bSdh __func__, inst); 2694c06356bSdh return (DDI_FAILURE); 2704c06356bSdh } 2714c06356bSdh 2724c06356bSdh if ((pwp->state == STATE_UNPROBING) || (pwp->state == STATE_DEAD)) { 2734c06356bSdh return (DDI_FAILURE); 2744c06356bSdh } 2754c06356bSdh 2764c06356bSdh if ((iport_ua = scsi_hba_iport_unit_address(dip)) == NULL) { 277*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2784c06356bSdh "%s: invoked with NULL unit address, inst (%d)", 2794c06356bSdh __func__, inst); 2804c06356bSdh return (DDI_FAILURE); 2814c06356bSdh } 2824c06356bSdh 2834c06356bSdh if (ddi_soft_state_zalloc(pmcs_iport_softstate, inst) != DDI_SUCCESS) { 284*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2854c06356bSdh "Failed to alloc soft state for iport %d", inst); 2864c06356bSdh return (DDI_FAILURE); 2874c06356bSdh } 2884c06356bSdh 2894c06356bSdh iport = ddi_get_soft_state(pmcs_iport_softstate, inst); 2904c06356bSdh if (iport == NULL) { 291*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2924c06356bSdh "cannot get iport soft state"); 2934c06356bSdh goto iport_attach_fail1; 2944c06356bSdh } 2954c06356bSdh 2964c06356bSdh mutex_init(&iport->lock, NULL, MUTEX_DRIVER, 2974c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 2984c06356bSdh cv_init(&iport->refcnt_cv, NULL, CV_DEFAULT, NULL); 2994c06356bSdh mutex_init(&iport->refcnt_lock, NULL, MUTEX_DRIVER, 3004c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 3014c06356bSdh 3024c06356bSdh /* Set some data on the iport handle */ 3034c06356bSdh iport->dip = dip; 3044c06356bSdh iport->pwp = pwp; 3054c06356bSdh 3064c06356bSdh /* Dup the UA into the iport handle */ 3074c06356bSdh iport->ua = strdup(iport_ua); 3084c06356bSdh 3094c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 3104c06356bSdh tran->tran_hba_private = iport; 3114c06356bSdh 3124c06356bSdh list_create(&iport->phys, sizeof (pmcs_phy_t), 3134c06356bSdh offsetof(pmcs_phy_t, list_node)); 3144c06356bSdh 3154c06356bSdh /* 3164c06356bSdh * If our unit address is active in the phymap, configure our 3174c06356bSdh * iport's phylist. 3184c06356bSdh */ 3194c06356bSdh mutex_enter(&iport->lock); 3204c06356bSdh ua_priv = sas_phymap_lookup_uapriv(pwp->hss_phymap, iport->ua); 3214c06356bSdh if (ua_priv) { 3224c06356bSdh /* Non-NULL private data indicates the unit address is active */ 3234c06356bSdh iport->ua_state = UA_ACTIVE; 3244c06356bSdh if (pmcs_iport_configure_phys(iport) != DDI_SUCCESS) { 325*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 326*c3bc407cSdh "%s: failed to " 3274c06356bSdh "configure phys on iport handle (0x%p), " 3284c06356bSdh " unit address [%s]", __func__, 3294c06356bSdh (void *)iport, iport_ua); 3304c06356bSdh mutex_exit(&iport->lock); 3314c06356bSdh goto iport_attach_fail2; 3324c06356bSdh } 3334c06356bSdh } else { 3344c06356bSdh iport->ua_state = UA_INACTIVE; 3354c06356bSdh } 3364c06356bSdh mutex_exit(&iport->lock); 3374c06356bSdh 3384c06356bSdh /* Allocate string-based soft state pool for targets */ 3394c06356bSdh iport->tgt_sstate = NULL; 3404c06356bSdh if (ddi_soft_state_bystr_init(&iport->tgt_sstate, 3414c06356bSdh sizeof (pmcs_xscsi_t), PMCS_TGT_SSTATE_SZ) != 0) { 342*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 3434c06356bSdh "cannot get iport tgt soft state"); 3444c06356bSdh goto iport_attach_fail2; 3454c06356bSdh } 3464c06356bSdh 3474c06356bSdh /* Create this iport's target map */ 3484c06356bSdh if (pmcs_iport_tgtmap_create(iport) == B_FALSE) { 349*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 3504c06356bSdh "Failed to create tgtmap on iport %d", inst); 3514c06356bSdh goto iport_attach_fail3; 3524c06356bSdh } 3534c06356bSdh 3544c06356bSdh /* Set up the 'initiator-port' DDI property on this iport */ 3554c06356bSdh init_port = kmem_zalloc(PMCS_MAX_UA_SIZE, KM_SLEEP); 3564c06356bSdh if (pwp->separate_ports) { 357*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 358*c3bc407cSdh "%s: separate ports not supported", __func__); 3594c06356bSdh } else { 3604c06356bSdh /* Set initiator-port value to the HBA's base WWN */ 3614c06356bSdh (void) scsi_wwn_to_wwnstr(pwp->sas_wwns[0], 1, 3624c06356bSdh init_port); 3634c06356bSdh } 3644c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_STRING, 3654c06356bSdh SCSI_ADDR_PROP_INITIATOR_PORT, init_port); 3664c06356bSdh kmem_free(init_port, PMCS_MAX_UA_SIZE); 3674c06356bSdh 3684c06356bSdh /* Set up a 'num-phys' DDI property for the iport node */ 3694c06356bSdh pmcs_smhba_add_iport_prop(iport, DATA_TYPE_INT32, PMCS_NUM_PHYS, 3704c06356bSdh &iport->nphy); 3714c06356bSdh 3724c06356bSdh /* Create kstats for each of the phys in this port */ 3734c06356bSdh pmcs_create_phy_stats(iport); 3744c06356bSdh 3754c06356bSdh /* 3764c06356bSdh * Insert this iport handle into our list and set 3774c06356bSdh * iports_attached on the HBA node. 3784c06356bSdh */ 3794c06356bSdh rw_enter(&pwp->iports_lock, RW_WRITER); 3804c06356bSdh ASSERT(!list_link_active(&iport->list_node)); 3814c06356bSdh list_insert_tail(&pwp->iports, iport); 3824c06356bSdh pwp->iports_attached = 1; 3834c06356bSdh pwp->num_iports++; 3844c06356bSdh rw_exit(&pwp->iports_lock); 3854c06356bSdh 386*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 387*c3bc407cSdh "iport%d attached", inst); 3884c06356bSdh ddi_report_dev(dip); 3894c06356bSdh return (DDI_SUCCESS); 3904c06356bSdh 3914c06356bSdh /* teardown and fail */ 3924c06356bSdh iport_attach_fail3: 3934c06356bSdh ddi_soft_state_bystr_fini(&iport->tgt_sstate); 3944c06356bSdh iport_attach_fail2: 3954c06356bSdh list_destroy(&iport->phys); 3964c06356bSdh strfree(iport->ua); 3974c06356bSdh mutex_destroy(&iport->refcnt_lock); 3984c06356bSdh cv_destroy(&iport->refcnt_cv); 3994c06356bSdh mutex_destroy(&iport->lock); 4004c06356bSdh iport_attach_fail1: 4014c06356bSdh ddi_soft_state_free(pmcs_iport_softstate, inst); 4024c06356bSdh return (DDI_FAILURE); 4034c06356bSdh } 4044c06356bSdh 4054c06356bSdh static int 4064c06356bSdh pmcs_attach(dev_info_t *dip, ddi_attach_cmd_t cmd) 4074c06356bSdh { 4084c06356bSdh scsi_hba_tran_t *tran; 4094c06356bSdh char chiprev, *fwsupport, hw_rev[24], fw_rev[24]; 4104c06356bSdh off_t set3size; 4114c06356bSdh int inst, i; 4124c06356bSdh int sm_hba = 1; 4134c06356bSdh int protocol = 0; 4144c06356bSdh int num_phys = 0; 4154c06356bSdh pmcs_hw_t *pwp; 4164c06356bSdh pmcs_phy_t *phyp; 4174c06356bSdh uint32_t num_threads; 4184c06356bSdh char buf[64]; 4194c06356bSdh 4204c06356bSdh switch (cmd) { 4214c06356bSdh case DDI_ATTACH: 4224c06356bSdh break; 4234c06356bSdh 4244c06356bSdh case DDI_PM_RESUME: 4254c06356bSdh case DDI_RESUME: 4264c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 4274c06356bSdh if (!tran) { 4284c06356bSdh return (DDI_FAILURE); 4294c06356bSdh } 4304c06356bSdh /* No DDI_?_RESUME on iport nodes */ 4314c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 4324c06356bSdh return (DDI_SUCCESS); 4334c06356bSdh } 4344c06356bSdh pwp = TRAN2PMC(tran); 4354c06356bSdh if (pwp == NULL) { 4364c06356bSdh return (DDI_FAILURE); 4374c06356bSdh } 4384c06356bSdh 4394c06356bSdh mutex_enter(&pwp->lock); 4404c06356bSdh pwp->suspended = 0; 4414c06356bSdh if (pwp->tq) { 4424c06356bSdh ddi_taskq_resume(pwp->tq); 4434c06356bSdh } 4444c06356bSdh mutex_exit(&pwp->lock); 4454c06356bSdh return (DDI_SUCCESS); 4464c06356bSdh 4474c06356bSdh default: 4484c06356bSdh return (DDI_FAILURE); 4494c06356bSdh } 4504c06356bSdh 4514c06356bSdh /* 4524c06356bSdh * If this is an iport node, invoke iport attach. 4534c06356bSdh */ 4544c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 4554c06356bSdh return (pmcs_iport_attach(dip)); 4564c06356bSdh } 4574c06356bSdh 4584c06356bSdh /* 4594c06356bSdh * From here on is attach for the HBA node 4604c06356bSdh */ 4614c06356bSdh 4624c06356bSdh #ifdef DEBUG 4634c06356bSdh /* 4644c06356bSdh * Check to see if this unit is to be disabled. We can't disable 4654c06356bSdh * on a per-iport node. It's either the entire HBA or nothing. 4664c06356bSdh */ 4674c06356bSdh (void) snprintf(buf, sizeof (buf), 4684c06356bSdh "disable-instance-%d", ddi_get_instance(dip)); 4694c06356bSdh if (ddi_prop_get_int(DDI_DEV_T_ANY, dip, 4704c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, buf, 0)) { 4714c06356bSdh cmn_err(CE_NOTE, "pmcs%d: disabled by configuration", 4724c06356bSdh ddi_get_instance(dip)); 4734c06356bSdh return (DDI_FAILURE); 4744c06356bSdh } 4754c06356bSdh #endif 4764c06356bSdh 4774c06356bSdh /* 4784c06356bSdh * Allocate softstate 4794c06356bSdh */ 4804c06356bSdh inst = ddi_get_instance(dip); 4814c06356bSdh if (ddi_soft_state_zalloc(pmcs_softc_state, inst) != DDI_SUCCESS) { 4824c06356bSdh cmn_err(CE_WARN, "pmcs%d: Failed to alloc soft state", inst); 4834c06356bSdh return (DDI_FAILURE); 4844c06356bSdh } 4854c06356bSdh 4864c06356bSdh pwp = ddi_get_soft_state(pmcs_softc_state, inst); 4874c06356bSdh if (pwp == NULL) { 4884c06356bSdh cmn_err(CE_WARN, "pmcs%d: cannot get soft state", inst); 4894c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 4904c06356bSdh return (DDI_FAILURE); 4914c06356bSdh } 4924c06356bSdh pwp->dip = dip; 4934c06356bSdh STAILQ_INIT(&pwp->dq); 4944c06356bSdh STAILQ_INIT(&pwp->cq); 4954c06356bSdh STAILQ_INIT(&pwp->wf); 4964c06356bSdh STAILQ_INIT(&pwp->pf); 4974c06356bSdh /* 4984c06356bSdh * Create the list for iports 4994c06356bSdh */ 5004c06356bSdh list_create(&pwp->iports, sizeof (pmcs_iport_t), 5014c06356bSdh offsetof(pmcs_iport_t, list_node)); 5024c06356bSdh 5034c06356bSdh pwp->state = STATE_PROBING; 5044c06356bSdh 5054c06356bSdh /* 5064c06356bSdh * Get driver.conf properties 5074c06356bSdh */ 5084c06356bSdh pwp->debug_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5094c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-debug-mask", 5104c06356bSdh debug_mask); 5114c06356bSdh pwp->phyid_block_mask = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5124c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phyid-block-mask", 5134c06356bSdh block_mask); 5144c06356bSdh pwp->physpeed = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5154c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-physpeed", physpeed); 5164c06356bSdh pwp->phymode = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5174c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-phymode", phymode); 5184c06356bSdh pwp->fwlog = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5194c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fwlog", fwlog_level); 5204c06356bSdh if (pwp->fwlog > PMCS_FWLOG_MAX) { 5214c06356bSdh pwp->fwlog = PMCS_FWLOG_MAX; 5224c06356bSdh } 5234c06356bSdh 5244c06356bSdh mutex_enter(&pmcs_trace_lock); 5254c06356bSdh if (pmcs_tbuf == NULL) { 5264c06356bSdh /* Allocate trace buffer */ 5274c06356bSdh pmcs_tbuf_num_elems = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5284c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-tbuf-num-elems", 5294c06356bSdh PMCS_TBUF_NUM_ELEMS_DEF); 5304c06356bSdh if ((pmcs_tbuf_num_elems == DDI_PROP_NOT_FOUND) || 5314c06356bSdh (pmcs_tbuf_num_elems == 0)) { 5324c06356bSdh pmcs_tbuf_num_elems = PMCS_TBUF_NUM_ELEMS_DEF; 5334c06356bSdh } 5344c06356bSdh 5354c06356bSdh pmcs_tbuf = kmem_zalloc(pmcs_tbuf_num_elems * 5364c06356bSdh sizeof (pmcs_tbuf_t), KM_SLEEP); 5374c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 5384c06356bSdh pmcs_tbuf_idx = 0; 5394c06356bSdh } 5404c06356bSdh mutex_exit(&pmcs_trace_lock); 5414c06356bSdh 5424c06356bSdh disable_msix = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5434c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msix", 5444c06356bSdh disable_msix); 5454c06356bSdh disable_msi = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5464c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-disable-msi", 5474c06356bSdh disable_msi); 5484c06356bSdh maxqdepth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5494c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-maxqdepth", maxqdepth); 5504c06356bSdh pwp->fw_force_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5514c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-fw-force-update", 0); 5524c06356bSdh if (pwp->fw_force_update == 0) { 5534c06356bSdh pwp->fw_disable_update = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5544c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, 5554c06356bSdh "pmcs-fw-disable-update", 0); 5564c06356bSdh } 5574c06356bSdh pwp->ioq_depth = ddi_prop_get_int(DDI_DEV_T_ANY, dip, 5584c06356bSdh DDI_PROP_DONTPASS | DDI_PROP_NOTPROM, "pmcs-num-io-qentries", 5594c06356bSdh PMCS_NQENTRY); 5604c06356bSdh 5614c06356bSdh /* 5624c06356bSdh * Initialize FMA 5634c06356bSdh */ 5644c06356bSdh pwp->dev_acc_attr = pwp->reg_acc_attr = rattr; 5654c06356bSdh pwp->iqp_dma_attr = pwp->oqp_dma_attr = 5664c06356bSdh pwp->regdump_dma_attr = pwp->cip_dma_attr = 5674c06356bSdh pwp->fwlog_dma_attr = pmcs_dattr; 5684c06356bSdh pwp->fm_capabilities = ddi_getprop(DDI_DEV_T_ANY, pwp->dip, 5694c06356bSdh DDI_PROP_NOTPROM | DDI_PROP_DONTPASS, "fm-capable", 5704c06356bSdh DDI_FM_EREPORT_CAPABLE | DDI_FM_ACCCHK_CAPABLE | 5714c06356bSdh DDI_FM_DMACHK_CAPABLE | DDI_FM_ERRCB_CAPABLE); 5724c06356bSdh pmcs_fm_init(pwp); 5734c06356bSdh 5744c06356bSdh /* 5754c06356bSdh * Map registers 5764c06356bSdh */ 5774c06356bSdh if (pci_config_setup(dip, &pwp->pci_acc_handle)) { 578*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 579*c3bc407cSdh "pci config setup failed"); 5804c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 5814c06356bSdh return (DDI_FAILURE); 5824c06356bSdh } 5834c06356bSdh 5844c06356bSdh /* 5854c06356bSdh * Get the size of register set 3. 5864c06356bSdh */ 5874c06356bSdh if (ddi_dev_regsize(dip, PMCS_REGSET_3, &set3size) != DDI_SUCCESS) { 588*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 5894c06356bSdh "unable to get size of register set %d", PMCS_REGSET_3); 5904c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 5914c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 5924c06356bSdh return (DDI_FAILURE); 5934c06356bSdh } 5944c06356bSdh 5954c06356bSdh /* 5964c06356bSdh * Map registers 5974c06356bSdh */ 5984c06356bSdh pwp->reg_acc_attr.devacc_attr_endian_flags = DDI_STRUCTURE_LE_ACC; 5994c06356bSdh 6004c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_0, (caddr_t *)&pwp->msg_regs, 6014c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->msg_acc_handle)) { 602*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6034c06356bSdh "failed to map Message Unit registers"); 6044c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6054c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6064c06356bSdh return (DDI_FAILURE); 6074c06356bSdh } 6084c06356bSdh 6094c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_1, (caddr_t *)&pwp->top_regs, 6104c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->top_acc_handle)) { 611*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 612*c3bc407cSdh "failed to map TOP registers"); 6134c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6144c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6154c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6164c06356bSdh return (DDI_FAILURE); 6174c06356bSdh } 6184c06356bSdh 6194c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_2, (caddr_t *)&pwp->gsm_regs, 6204c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->gsm_acc_handle)) { 621*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 622*c3bc407cSdh "failed to map GSM registers"); 6234c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6244c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6254c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6264c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6274c06356bSdh return (DDI_FAILURE); 6284c06356bSdh } 6294c06356bSdh 6304c06356bSdh if (ddi_regs_map_setup(dip, PMCS_REGSET_3, (caddr_t *)&pwp->mpi_regs, 6314c06356bSdh 0, 0, &pwp->reg_acc_attr, &pwp->mpi_acc_handle)) { 632*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 633*c3bc407cSdh "failed to map MPI registers"); 6344c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 6354c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 6364c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 6374c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 6384c06356bSdh ddi_soft_state_free(pmcs_softc_state, inst); 6394c06356bSdh return (DDI_FAILURE); 6404c06356bSdh } 6414c06356bSdh pwp->mpibar = 6424c06356bSdh (((5U << 2) + 0x10) << PMCS_MSGU_MPI_BAR_SHIFT) | set3size; 6434c06356bSdh 6444c06356bSdh /* 6454c06356bSdh * Make sure we can support this card. 6464c06356bSdh */ 6474c06356bSdh pwp->chiprev = pmcs_rd_topunit(pwp, PMCS_DEVICE_REVISION); 6484c06356bSdh 6494c06356bSdh switch (pwp->chiprev) { 6504c06356bSdh case PMCS_PM8001_REV_A: 6514c06356bSdh case PMCS_PM8001_REV_B: 652*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 6534c06356bSdh "Rev A/B Card no longer supported"); 6544c06356bSdh goto failure; 6554c06356bSdh case PMCS_PM8001_REV_C: 6564c06356bSdh break; 6574c06356bSdh default: 658*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 6594c06356bSdh "Unknown chip revision (%d)", pwp->chiprev); 6604c06356bSdh goto failure; 6614c06356bSdh } 6624c06356bSdh 6634c06356bSdh /* 6644c06356bSdh * Allocate DMA addressable area for Inbound and Outbound Queue indices 6654c06356bSdh * that the chip needs to access plus a space for scratch usage 6664c06356bSdh */ 6674c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 6684c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pwp->cip_acchdls, 6694c06356bSdh &pwp->cip_handles, ptob(1), (caddr_t *)&pwp->cip, 6704c06356bSdh &pwp->ciaddr) == B_FALSE) { 671*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6724c06356bSdh "Failed to setup DMA for index/scratch"); 6734c06356bSdh goto failure; 6744c06356bSdh } 6754c06356bSdh 6764c06356bSdh bzero(pwp->cip, ptob(1)); 6774c06356bSdh pwp->scratch = &pwp->cip[PMCS_INDICES_SIZE]; 6784c06356bSdh pwp->scratch_dma = pwp->ciaddr + PMCS_INDICES_SIZE; 6794c06356bSdh 6804c06356bSdh /* 6814c06356bSdh * Allocate DMA S/G list chunks 6824c06356bSdh */ 6834c06356bSdh (void) pmcs_add_more_chunks(pwp, ptob(1) * PMCS_MIN_CHUNK_PAGES); 6844c06356bSdh 6854c06356bSdh /* 6864c06356bSdh * Allocate a DMA addressable area for the firmware log (if needed) 6874c06356bSdh */ 6884c06356bSdh if (pwp->fwlog) { 6894c06356bSdh /* 6904c06356bSdh * Align to event log header and entry size 6914c06356bSdh */ 6924c06356bSdh pwp->fwlog_dma_attr.dma_attr_align = 32; 6934c06356bSdh if (pmcs_dma_setup(pwp, &pwp->fwlog_dma_attr, 6944c06356bSdh &pwp->fwlog_acchdl, 6954c06356bSdh &pwp->fwlog_hndl, PMCS_FWLOG_SIZE, 6964c06356bSdh (caddr_t *)&pwp->fwlogp, 6974c06356bSdh &pwp->fwaddr) == B_FALSE) { 698*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 6994c06356bSdh "Failed to setup DMA for fwlog area"); 7004c06356bSdh pwp->fwlog = 0; 7014c06356bSdh } else { 7024c06356bSdh bzero(pwp->fwlogp, PMCS_FWLOG_SIZE); 7034c06356bSdh } 7044c06356bSdh } 7054c06356bSdh 7064c06356bSdh if (pwp->flash_chunk_addr == NULL) { 7074c06356bSdh pwp->regdump_dma_attr.dma_attr_align = PMCS_FLASH_CHUNK_SIZE; 7084c06356bSdh if (pmcs_dma_setup(pwp, &pwp->regdump_dma_attr, 7094c06356bSdh &pwp->regdump_acchdl, 7104c06356bSdh &pwp->regdump_hndl, PMCS_FLASH_CHUNK_SIZE, 7114c06356bSdh (caddr_t *)&pwp->flash_chunkp, &pwp->flash_chunk_addr) == 7124c06356bSdh B_FALSE) { 713*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 7144c06356bSdh "Failed to setup DMA for register dump area"); 7154c06356bSdh goto failure; 7164c06356bSdh } 7174c06356bSdh bzero(pwp->flash_chunkp, PMCS_FLASH_CHUNK_SIZE); 7184c06356bSdh } 7194c06356bSdh 7204c06356bSdh /* 7214c06356bSdh * More bits of local initialization... 7224c06356bSdh */ 7234c06356bSdh pwp->tq = ddi_taskq_create(dip, "_tq", 4, TASKQ_DEFAULTPRI, 0); 7244c06356bSdh if (pwp->tq == NULL) { 725*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 726*c3bc407cSdh "unable to create worker taskq"); 7274c06356bSdh goto failure; 7284c06356bSdh } 7294c06356bSdh 7304c06356bSdh /* 7314c06356bSdh * Cache of structures for dealing with I/O completion callbacks. 7324c06356bSdh */ 7334c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_iocomp_cb_cache%d", inst); 7344c06356bSdh pwp->iocomp_cb_cache = kmem_cache_create(buf, 7354c06356bSdh sizeof (pmcs_iocomp_cb_t), 16, NULL, NULL, NULL, NULL, NULL, 0); 7364c06356bSdh 7374c06356bSdh /* 7384c06356bSdh * Cache of PHY structures 7394c06356bSdh */ 7404c06356bSdh (void) snprintf(buf, sizeof (buf), "pmcs_phy_cache%d", inst); 7414c06356bSdh pwp->phy_cache = kmem_cache_create(buf, sizeof (pmcs_phy_t), 8, 7424c06356bSdh pmcs_phy_constructor, pmcs_phy_destructor, NULL, (void *)pwp, 7434c06356bSdh NULL, 0); 7444c06356bSdh 7454c06356bSdh /* 7464c06356bSdh * Allocate space for the I/O completion threads 7474c06356bSdh */ 7484c06356bSdh num_threads = ncpus_online; 7494c06356bSdh if (num_threads > PMCS_MAX_CQ_THREADS) { 7504c06356bSdh num_threads = PMCS_MAX_CQ_THREADS; 7514c06356bSdh } 7524c06356bSdh 7534c06356bSdh pwp->cq_info.cq_thr_info = kmem_zalloc(sizeof (pmcs_cq_thr_info_t) * 7544c06356bSdh num_threads, KM_SLEEP); 7554c06356bSdh pwp->cq_info.cq_threads = num_threads; 7564c06356bSdh pwp->cq_info.cq_next_disp_thr = 0; 7574c06356bSdh pwp->cq_info.cq_stop = B_FALSE; 7584c06356bSdh 7594c06356bSdh /* 7604c06356bSdh * Set the quantum value in clock ticks for the I/O interrupt 7614c06356bSdh * coalescing timer. 7624c06356bSdh */ 7634c06356bSdh pwp->io_intr_coal.quantum = drv_usectohz(PMCS_QUANTUM_TIME_USECS); 7644c06356bSdh 7654c06356bSdh /* 7664c06356bSdh * We have a delicate dance here. We need to set up 7674c06356bSdh * interrupts so we know how to set up some OQC 7684c06356bSdh * tables. However, while we're setting up table 7694c06356bSdh * access, we may need to flash new firmware and 7704c06356bSdh * reset the card, which will take some finessing. 7714c06356bSdh */ 7724c06356bSdh 7734c06356bSdh /* 7744c06356bSdh * Set up interrupts here. 7754c06356bSdh */ 7764c06356bSdh switch (pmcs_setup_intr(pwp)) { 7774c06356bSdh case 0: 7784c06356bSdh break; 7794c06356bSdh case EIO: 7804c06356bSdh pwp->stuck = 1; 7814c06356bSdh /* FALLTHROUGH */ 7824c06356bSdh default: 7834c06356bSdh goto failure; 7844c06356bSdh } 7854c06356bSdh 7864c06356bSdh /* 7874c06356bSdh * Set these up now becuase they are used to initialize the OQC tables. 7884c06356bSdh * 7894c06356bSdh * If we have MSI or MSI-X interrupts set up and we have enough 7904c06356bSdh * vectors for each OQ, the Outbound Queue vectors can all be the 7914c06356bSdh * same as the appropriate interrupt routine will have been called 7924c06356bSdh * and the doorbell register automatically cleared. 7934c06356bSdh * This keeps us from having to check the Outbound Doorbell register 7944c06356bSdh * when the routines for these interrupts are called. 7954c06356bSdh * 7964c06356bSdh * If we have Legacy INT-X interrupts set up or we didn't have enough 7974c06356bSdh * MSI/MSI-X vectors to uniquely identify each OQ, we point these 7984c06356bSdh * vectors to the bits we would like to have set in the Outbound 7994c06356bSdh * Doorbell register because pmcs_all_intr will read the doorbell 8004c06356bSdh * register to find out why we have an interrupt and write the 8014c06356bSdh * corresponding 'clear' bit for that interrupt. 8024c06356bSdh */ 8034c06356bSdh 8044c06356bSdh switch (pwp->intr_cnt) { 8054c06356bSdh case 1: 8064c06356bSdh /* 8074c06356bSdh * Only one vector, so we must check all OQs for MSI. For 8084c06356bSdh * INT-X, there's only one vector anyway, so we can just 8094c06356bSdh * use the outbound queue bits to keep from having to 8104c06356bSdh * check each queue for each interrupt. 8114c06356bSdh */ 8124c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 8134c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8144c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8154c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8164c06356bSdh } else { 8174c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8184c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_IODONE; 8194c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_IODONE; 8204c06356bSdh } 8214c06356bSdh break; 8224c06356bSdh case 2: 8234c06356bSdh /* With 2, we can at least isolate IODONE */ 8244c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8254c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8264c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_GENERAL; 8274c06356bSdh break; 8284c06356bSdh case 4: 8294c06356bSdh /* With 4 vectors, everybody gets one */ 8304c06356bSdh pwp->oqvec[PMCS_OQ_IODONE] = PMCS_OQ_IODONE; 8314c06356bSdh pwp->oqvec[PMCS_OQ_GENERAL] = PMCS_OQ_GENERAL; 8324c06356bSdh pwp->oqvec[PMCS_OQ_EVENTS] = PMCS_OQ_EVENTS; 8334c06356bSdh break; 8344c06356bSdh } 8354c06356bSdh 8364c06356bSdh /* 8374c06356bSdh * Do the first part of setup 8384c06356bSdh */ 8394c06356bSdh if (pmcs_setup(pwp)) { 8404c06356bSdh goto failure; 8414c06356bSdh } 8424c06356bSdh pmcs_report_fwversion(pwp); 8434c06356bSdh 8444c06356bSdh /* 8454c06356bSdh * Now do some additonal allocations based upon information 8464c06356bSdh * gathered during MPI setup. 8474c06356bSdh */ 8484c06356bSdh pwp->root_phys = kmem_zalloc(pwp->nphy * sizeof (pmcs_phy_t), KM_SLEEP); 8494c06356bSdh ASSERT(pwp->nphy < SAS2_PHYNUM_MAX); 8504c06356bSdh phyp = pwp->root_phys; 8514c06356bSdh for (i = 0; i < pwp->nphy; i++) { 8524c06356bSdh if (i < pwp->nphy-1) { 8534c06356bSdh phyp->sibling = (phyp + 1); 8544c06356bSdh } 8554c06356bSdh mutex_init(&phyp->phy_lock, NULL, MUTEX_DRIVER, 8564c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 8574c06356bSdh phyp->phynum = i & SAS2_PHYNUM_MASK; 8584c06356bSdh pmcs_phy_name(pwp, phyp, phyp->path, sizeof (phyp->path)); 8594c06356bSdh phyp->pwp = pwp; 8604c06356bSdh phyp->device_id = PMCS_INVALID_DEVICE_ID; 8614c06356bSdh phyp++; 8624c06356bSdh } 8634c06356bSdh 8644c06356bSdh pwp->work = kmem_zalloc(pwp->max_cmd * sizeof (pmcwork_t), KM_SLEEP); 8654c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 8664c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 8674c06356bSdh mutex_init(&pwrk->lock, NULL, MUTEX_DRIVER, 8684c06356bSdh DDI_INTR_PRI(pwp->intr_pri)); 8694c06356bSdh cv_init(&pwrk->sleep_cv, NULL, CV_DRIVER, NULL); 8704c06356bSdh STAILQ_INSERT_TAIL(&pwp->wf, pwrk, next); 8714c06356bSdh 8724c06356bSdh } 8734c06356bSdh pwp->targets = (pmcs_xscsi_t **) 8744c06356bSdh kmem_zalloc(pwp->max_dev * sizeof (pmcs_xscsi_t *), KM_SLEEP); 8754c06356bSdh 8764c06356bSdh pwp->iqpt = (pmcs_iqp_trace_t *) 8774c06356bSdh kmem_zalloc(sizeof (pmcs_iqp_trace_t), KM_SLEEP); 8784c06356bSdh pwp->iqpt->head = kmem_zalloc(PMCS_IQP_TRACE_BUFFER_SIZE, KM_SLEEP); 8794c06356bSdh pwp->iqpt->curpos = pwp->iqpt->head; 8804c06356bSdh pwp->iqpt->size_left = PMCS_IQP_TRACE_BUFFER_SIZE; 8814c06356bSdh 8824c06356bSdh /* 8834c06356bSdh * Start MPI communication. 8844c06356bSdh */ 8854c06356bSdh if (pmcs_start_mpi(pwp)) { 8864c06356bSdh if (pmcs_soft_reset(pwp, B_FALSE)) { 8874c06356bSdh goto failure; 8884c06356bSdh } 8894c06356bSdh } 8904c06356bSdh 8914c06356bSdh /* 8924c06356bSdh * Do some initial acceptance tests. 8934c06356bSdh * This tests interrupts and queues. 8944c06356bSdh */ 8954c06356bSdh if (pmcs_echo_test(pwp)) { 8964c06356bSdh goto failure; 8974c06356bSdh } 8984c06356bSdh 8994c06356bSdh /* Read VPD - if it exists */ 9004c06356bSdh if (pmcs_get_nvmd(pwp, PMCS_NVMD_VPD, PMCIN_NVMD_VPD, 0, NULL, 0)) { 901*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 902*c3bc407cSdh "%s: Unable to read VPD: " 9034c06356bSdh "attempting to fabricate", __func__); 9044c06356bSdh /* 9054c06356bSdh * When we release, this must goto failure and the call 9064c06356bSdh * to pmcs_fabricate_wwid is removed. 9074c06356bSdh */ 9084c06356bSdh /* goto failure; */ 9094c06356bSdh if (!pmcs_fabricate_wwid(pwp)) { 9104c06356bSdh goto failure; 9114c06356bSdh } 9124c06356bSdh } 9134c06356bSdh 9144c06356bSdh /* 9154c06356bSdh * We're now officially running 9164c06356bSdh */ 9174c06356bSdh pwp->state = STATE_RUNNING; 9184c06356bSdh 9194c06356bSdh /* 9204c06356bSdh * Check firmware versions and load new firmware 9214c06356bSdh * if needed and reset. 9224c06356bSdh */ 9234c06356bSdh if (pmcs_firmware_update(pwp)) { 924*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_WARN, NULL, NULL, 925*c3bc407cSdh "%s: Firmware update failed", __func__); 9264c06356bSdh goto failure; 9274c06356bSdh } 9284c06356bSdh 9294c06356bSdh /* 9304c06356bSdh * Create completion threads. 9314c06356bSdh */ 9324c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 9334c06356bSdh pwp->cq_info.cq_thr_info[i].cq_pwp = pwp; 9344c06356bSdh pwp->cq_info.cq_thr_info[i].cq_thread = 9354c06356bSdh thread_create(NULL, 0, pmcs_scsa_cq_run, 9364c06356bSdh &pwp->cq_info.cq_thr_info[i], 0, &p0, TS_RUN, minclsyspri); 9374c06356bSdh } 9384c06356bSdh 9394c06356bSdh /* 9404c06356bSdh * Create one thread to deal with the updating of the interrupt 9414c06356bSdh * coalescing timer. 9424c06356bSdh */ 9434c06356bSdh pwp->ict_thread = thread_create(NULL, 0, pmcs_check_intr_coal, 9444c06356bSdh pwp, 0, &p0, TS_RUN, minclsyspri); 9454c06356bSdh 9464c06356bSdh /* 9474c06356bSdh * Kick off the watchdog 9484c06356bSdh */ 9494c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 9504c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 9514c06356bSdh /* 9524c06356bSdh * Do the SCSI attachment code (before starting phys) 9534c06356bSdh */ 9544c06356bSdh if (pmcs_scsa_init(pwp, &pmcs_dattr)) { 9554c06356bSdh goto failure; 9564c06356bSdh } 9574c06356bSdh pwp->hba_attached = 1; 9584c06356bSdh 9594c06356bSdh /* 9604c06356bSdh * Initialize the rwlock for the iport elements. 9614c06356bSdh */ 9624c06356bSdh rw_init(&pwp->iports_lock, NULL, RW_DRIVER, NULL); 9634c06356bSdh 9644c06356bSdh /* Check all acc & dma handles allocated in attach */ 9654c06356bSdh if (pmcs_check_acc_dma_handle(pwp)) { 9664c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 9674c06356bSdh goto failure; 9684c06356bSdh } 9694c06356bSdh 9704c06356bSdh /* 9714c06356bSdh * Create the phymap for this HBA instance 9724c06356bSdh */ 9734c06356bSdh if (sas_phymap_create(dip, phymap_usec, PHYMAP_MODE_SIMPLE, NULL, 9744c06356bSdh pwp, pmcs_phymap_activate, pmcs_phymap_deactivate, 9754c06356bSdh &pwp->hss_phymap) != DDI_SUCCESS) { 976*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 977*c3bc407cSdh "%s: pmcs%d phymap_create failed", __func__, inst); 9784c06356bSdh goto failure; 9794c06356bSdh } 9804c06356bSdh ASSERT(pwp->hss_phymap); 9814c06356bSdh 9824c06356bSdh /* 9834c06356bSdh * Create the iportmap for this HBA instance 9844c06356bSdh */ 9854c06356bSdh if (scsi_hba_iportmap_create(dip, iportmap_usec, pwp->nphy, 9864c06356bSdh &pwp->hss_iportmap) != DDI_SUCCESS) { 987*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 988*c3bc407cSdh "%s: pmcs%d iportmap_create failed", __func__, inst); 9894c06356bSdh goto failure; 9904c06356bSdh } 9914c06356bSdh ASSERT(pwp->hss_iportmap); 9924c06356bSdh 9934c06356bSdh /* 9944c06356bSdh * Start the PHYs. 9954c06356bSdh */ 9964c06356bSdh if (pmcs_start_phys(pwp)) { 9974c06356bSdh goto failure; 9984c06356bSdh } 9994c06356bSdh 10004c06356bSdh /* 10014c06356bSdh * From this point on, we can't fail. 10024c06356bSdh */ 10034c06356bSdh ddi_report_dev(dip); 10044c06356bSdh 10054c06356bSdh /* SM-HBA */ 10064c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SMHBA_SUPPORTED, 10074c06356bSdh &sm_hba); 10084c06356bSdh 10094c06356bSdh /* SM-HBA */ 10104c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_DRV_VERSION, 10114c06356bSdh pmcs_driver_rev); 10124c06356bSdh 10134c06356bSdh /* SM-HBA */ 10144c06356bSdh chiprev = 'A' + pwp->chiprev; 10154c06356bSdh (void) snprintf(hw_rev, 2, "%s", &chiprev); 10164c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_HWARE_VERSION, 10174c06356bSdh hw_rev); 10184c06356bSdh 10194c06356bSdh /* SM-HBA */ 10204c06356bSdh switch (PMCS_FW_TYPE(pwp)) { 10214c06356bSdh case PMCS_FW_TYPE_RELEASED: 10224c06356bSdh fwsupport = "Released"; 10234c06356bSdh break; 10244c06356bSdh case PMCS_FW_TYPE_DEVELOPMENT: 10254c06356bSdh fwsupport = "Development"; 10264c06356bSdh break; 10274c06356bSdh case PMCS_FW_TYPE_ALPHA: 10284c06356bSdh fwsupport = "Alpha"; 10294c06356bSdh break; 10304c06356bSdh case PMCS_FW_TYPE_BETA: 10314c06356bSdh fwsupport = "Beta"; 10324c06356bSdh break; 10334c06356bSdh default: 10344c06356bSdh fwsupport = "Special"; 10354c06356bSdh break; 10364c06356bSdh } 10374c06356bSdh (void) snprintf(fw_rev, sizeof (fw_rev), "%x.%x.%x %s", 10384c06356bSdh PMCS_FW_MAJOR(pwp), PMCS_FW_MINOR(pwp), PMCS_FW_MICRO(pwp), 10394c06356bSdh fwsupport); 10404c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_STRING, PMCS_FWARE_VERSION, 10414c06356bSdh fw_rev); 10424c06356bSdh 10434c06356bSdh /* SM-HBA */ 10444c06356bSdh num_phys = pwp->nphy; 10454c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_NUM_PHYS_HBA, 10464c06356bSdh &num_phys); 10474c06356bSdh 10484c06356bSdh /* SM-HBA */ 10494c06356bSdh protocol = SAS_SSP_SUPPORT | SAS_SATA_SUPPORT | SAS_SMP_SUPPORT; 10504c06356bSdh pmcs_smhba_add_hba_prop(pwp, DATA_TYPE_INT32, PMCS_SUPPORTED_PROTOCOL, 10514c06356bSdh &protocol); 10524c06356bSdh 10534c06356bSdh return (DDI_SUCCESS); 10544c06356bSdh 10554c06356bSdh failure: 10564c06356bSdh if (pmcs_unattach(pwp)) { 10574c06356bSdh pwp->stuck = 1; 10584c06356bSdh } 10594c06356bSdh return (DDI_FAILURE); 10604c06356bSdh } 10614c06356bSdh 10624c06356bSdh int 10634c06356bSdh pmcs_detach(dev_info_t *dip, ddi_detach_cmd_t cmd) 10644c06356bSdh { 10654c06356bSdh int inst = ddi_get_instance(dip); 10664c06356bSdh pmcs_iport_t *iport = NULL; 10674c06356bSdh pmcs_hw_t *pwp = NULL; 10684c06356bSdh scsi_hba_tran_t *tran; 10694c06356bSdh 10704c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 10714c06356bSdh /* iport node */ 10724c06356bSdh iport = ddi_get_soft_state(pmcs_iport_softstate, inst); 10734c06356bSdh ASSERT(iport); 10744c06356bSdh if (iport == NULL) { 10754c06356bSdh return (DDI_FAILURE); 10764c06356bSdh } 10774c06356bSdh pwp = iport->pwp; 10784c06356bSdh } else { 10794c06356bSdh /* hba node */ 10804c06356bSdh pwp = (pmcs_hw_t *)ddi_get_soft_state(pmcs_softc_state, inst); 10814c06356bSdh ASSERT(pwp); 10824c06356bSdh if (pwp == NULL) { 10834c06356bSdh return (DDI_FAILURE); 10844c06356bSdh } 10854c06356bSdh } 10864c06356bSdh 10874c06356bSdh switch (cmd) { 10884c06356bSdh case DDI_DETACH: 10894c06356bSdh if (iport) { 10904c06356bSdh /* iport detach */ 10914c06356bSdh if (pmcs_iport_unattach(iport)) { 10924c06356bSdh return (DDI_FAILURE); 10934c06356bSdh } 1094*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1095*c3bc407cSdh "iport%d detached", inst); 10964c06356bSdh return (DDI_SUCCESS); 10974c06356bSdh } else { 10984c06356bSdh /* HBA detach */ 10994c06356bSdh if (pmcs_unattach(pwp)) { 11004c06356bSdh return (DDI_FAILURE); 11014c06356bSdh } 11024c06356bSdh return (DDI_SUCCESS); 11034c06356bSdh } 11044c06356bSdh 11054c06356bSdh case DDI_SUSPEND: 11064c06356bSdh case DDI_PM_SUSPEND: 11074c06356bSdh /* No DDI_SUSPEND on iport nodes */ 11084c06356bSdh if (iport) { 11094c06356bSdh return (DDI_SUCCESS); 11104c06356bSdh } 11114c06356bSdh 11124c06356bSdh if (pwp->stuck) { 11134c06356bSdh return (DDI_FAILURE); 11144c06356bSdh } 11154c06356bSdh tran = (scsi_hba_tran_t *)ddi_get_driver_private(dip); 11164c06356bSdh if (!tran) { 11174c06356bSdh return (DDI_FAILURE); 11184c06356bSdh } 11194c06356bSdh 11204c06356bSdh pwp = TRAN2PMC(tran); 11214c06356bSdh if (pwp == NULL) { 11224c06356bSdh return (DDI_FAILURE); 11234c06356bSdh } 11244c06356bSdh mutex_enter(&pwp->lock); 11254c06356bSdh if (pwp->tq) { 11264c06356bSdh ddi_taskq_suspend(pwp->tq); 11274c06356bSdh } 11284c06356bSdh pwp->suspended = 1; 11294c06356bSdh mutex_exit(&pwp->lock); 1130*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "PMC8X6G suspending"); 11314c06356bSdh return (DDI_SUCCESS); 11324c06356bSdh 11334c06356bSdh default: 11344c06356bSdh return (DDI_FAILURE); 11354c06356bSdh } 11364c06356bSdh } 11374c06356bSdh 11384c06356bSdh static int 11394c06356bSdh pmcs_iport_unattach(pmcs_iport_t *iport) 11404c06356bSdh { 11414c06356bSdh pmcs_hw_t *pwp = iport->pwp; 11424c06356bSdh 11434c06356bSdh /* 11444c06356bSdh * First, check if there are still any configured targets on this 11454c06356bSdh * iport. If so, we fail detach. 11464c06356bSdh */ 11474c06356bSdh if (pmcs_iport_has_targets(pwp, iport)) { 1148*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1149*c3bc407cSdh "iport%d detach failure: iport has targets (luns)", 1150*c3bc407cSdh ddi_get_instance(iport->dip)); 11514c06356bSdh return (DDI_FAILURE); 11524c06356bSdh } 11534c06356bSdh 11544c06356bSdh /* 11554c06356bSdh * Remove this iport from our list if it is inactive in the phymap. 11564c06356bSdh */ 11574c06356bSdh rw_enter(&pwp->iports_lock, RW_WRITER); 11584c06356bSdh mutex_enter(&iport->lock); 11594c06356bSdh 11604c06356bSdh if (iport->ua_state == UA_ACTIVE) { 11614c06356bSdh mutex_exit(&iport->lock); 11624c06356bSdh rw_exit(&pwp->iports_lock); 1163*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_IPORT, NULL, NULL, 1164*c3bc407cSdh "iport%d detach failure: " 11654c06356bSdh "iport unit address active in phymap", 11664c06356bSdh ddi_get_instance(iport->dip)); 11674c06356bSdh return (DDI_FAILURE); 11684c06356bSdh } 11694c06356bSdh 11704c06356bSdh /* If it's our only iport, clear iports_attached */ 11714c06356bSdh ASSERT(pwp->num_iports >= 1); 11724c06356bSdh if (--pwp->num_iports == 0) { 11734c06356bSdh pwp->iports_attached = 0; 11744c06356bSdh } 11754c06356bSdh 11764c06356bSdh ASSERT(list_link_active(&iport->list_node)); 11774c06356bSdh list_remove(&pwp->iports, iport); 11784c06356bSdh rw_exit(&pwp->iports_lock); 11794c06356bSdh 11804c06356bSdh /* 11814c06356bSdh * We have removed the iport handle from the HBA's iports list, 11824c06356bSdh * there will be no new references to it. Two things must be 11834c06356bSdh * guarded against here. First, we could have PHY up events, 11844c06356bSdh * adding themselves to the iport->phys list and grabbing ref's 11854c06356bSdh * on our iport handle. Second, we could have existing references 11864c06356bSdh * to this iport handle from a point in time prior to the list 11874c06356bSdh * removal above. 11884c06356bSdh * 11894c06356bSdh * So first, destroy the phys list. Remove any phys that have snuck 11904c06356bSdh * in after the phymap deactivate, dropping the refcnt accordingly. 11914c06356bSdh * If these PHYs are still up if and when the phymap reactivates 11924c06356bSdh * (i.e. when this iport reattaches), we'll populate the list with 11934c06356bSdh * them and bump the refcnt back up. 11944c06356bSdh */ 11954c06356bSdh pmcs_remove_phy_from_iport(iport, NULL); 11964c06356bSdh ASSERT(list_is_empty(&iport->phys)); 11974c06356bSdh list_destroy(&iport->phys); 11984c06356bSdh mutex_exit(&iport->lock); 11994c06356bSdh 12004c06356bSdh /* 12014c06356bSdh * Second, wait for any other references to this iport to be 12024c06356bSdh * dropped, then continue teardown. 12034c06356bSdh */ 12044c06356bSdh mutex_enter(&iport->refcnt_lock); 12054c06356bSdh while (iport->refcnt != 0) { 12064c06356bSdh cv_wait(&iport->refcnt_cv, &iport->refcnt_lock); 12074c06356bSdh } 12084c06356bSdh mutex_exit(&iport->refcnt_lock); 12094c06356bSdh 12104c06356bSdh /* Delete kstats */ 12114c06356bSdh pmcs_destroy_phy_stats(iport); 12124c06356bSdh 12134c06356bSdh /* Destroy the iport target map */ 12144c06356bSdh if (pmcs_iport_tgtmap_destroy(iport) == B_FALSE) { 12154c06356bSdh return (DDI_FAILURE); 12164c06356bSdh } 12174c06356bSdh 12184c06356bSdh /* Free the tgt soft state */ 12194c06356bSdh if (iport->tgt_sstate != NULL) { 12204c06356bSdh ddi_soft_state_bystr_fini(&iport->tgt_sstate); 12214c06356bSdh } 12224c06356bSdh 12234c06356bSdh /* Free our unit address string */ 12244c06356bSdh strfree(iport->ua); 12254c06356bSdh 12264c06356bSdh /* Finish teardown and free the softstate */ 12274c06356bSdh mutex_destroy(&iport->refcnt_lock); 12284c06356bSdh ASSERT(iport->refcnt == 0); 12294c06356bSdh cv_destroy(&iport->refcnt_cv); 12304c06356bSdh mutex_destroy(&iport->lock); 12314c06356bSdh ddi_soft_state_free(pmcs_iport_softstate, ddi_get_instance(iport->dip)); 12324c06356bSdh 12334c06356bSdh return (DDI_SUCCESS); 12344c06356bSdh } 12354c06356bSdh 12364c06356bSdh static int 12374c06356bSdh pmcs_unattach(pmcs_hw_t *pwp) 12384c06356bSdh { 12394c06356bSdh int i; 12404c06356bSdh enum pwpstate curstate; 12414c06356bSdh pmcs_cq_thr_info_t *cqti; 12424c06356bSdh 12434c06356bSdh /* 12444c06356bSdh * Tear down the interrupt infrastructure. 12454c06356bSdh */ 12464c06356bSdh if (pmcs_teardown_intr(pwp)) { 12474c06356bSdh pwp->stuck = 1; 12484c06356bSdh } 12494c06356bSdh pwp->intr_cnt = 0; 12504c06356bSdh 12514c06356bSdh /* 12524c06356bSdh * Grab a lock, if initted, to set state. 12534c06356bSdh */ 12544c06356bSdh if (pwp->locks_initted) { 12554c06356bSdh mutex_enter(&pwp->lock); 12564c06356bSdh if (pwp->state != STATE_DEAD) { 12574c06356bSdh pwp->state = STATE_UNPROBING; 12584c06356bSdh } 12594c06356bSdh curstate = pwp->state; 12604c06356bSdh mutex_exit(&pwp->lock); 12614c06356bSdh 12624c06356bSdh /* 12634c06356bSdh * Stop the I/O completion threads. 12644c06356bSdh */ 12654c06356bSdh mutex_enter(&pwp->cq_lock); 12664c06356bSdh pwp->cq_info.cq_stop = B_TRUE; 12674c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 12684c06356bSdh if (pwp->cq_info.cq_thr_info[i].cq_thread) { 12694c06356bSdh cqti = &pwp->cq_info.cq_thr_info[i]; 12704c06356bSdh mutex_enter(&cqti->cq_thr_lock); 12714c06356bSdh cv_signal(&cqti->cq_cv); 12724c06356bSdh mutex_exit(&cqti->cq_thr_lock); 12734c06356bSdh mutex_exit(&pwp->cq_lock); 12744c06356bSdh thread_join(cqti->cq_thread->t_did); 12754c06356bSdh mutex_enter(&pwp->cq_lock); 12764c06356bSdh } 12774c06356bSdh } 12784c06356bSdh mutex_exit(&pwp->cq_lock); 12794c06356bSdh 12804c06356bSdh /* 12814c06356bSdh * Stop the interrupt coalescing timer thread 12824c06356bSdh */ 12834c06356bSdh if (pwp->ict_thread) { 12844c06356bSdh mutex_enter(&pwp->ict_lock); 12854c06356bSdh pwp->io_intr_coal.stop_thread = B_TRUE; 12864c06356bSdh cv_signal(&pwp->ict_cv); 12874c06356bSdh mutex_exit(&pwp->ict_lock); 12884c06356bSdh thread_join(pwp->ict_thread->t_did); 12894c06356bSdh } 12904c06356bSdh } else { 12914c06356bSdh if (pwp->state != STATE_DEAD) { 12924c06356bSdh pwp->state = STATE_UNPROBING; 12934c06356bSdh } 12944c06356bSdh curstate = pwp->state; 12954c06356bSdh } 12964c06356bSdh 12974c06356bSdh if (&pwp->iports != NULL) { 12984c06356bSdh /* Destroy the iports lock */ 12994c06356bSdh rw_destroy(&pwp->iports_lock); 13004c06356bSdh /* Destroy the iports list */ 13014c06356bSdh ASSERT(list_is_empty(&pwp->iports)); 13024c06356bSdh list_destroy(&pwp->iports); 13034c06356bSdh } 13044c06356bSdh 13054c06356bSdh if (pwp->hss_iportmap != NULL) { 13064c06356bSdh /* Destroy the iportmap */ 13074c06356bSdh scsi_hba_iportmap_destroy(pwp->hss_iportmap); 13084c06356bSdh } 13094c06356bSdh 13104c06356bSdh if (pwp->hss_phymap != NULL) { 13114c06356bSdh /* Destroy the phymap */ 13124c06356bSdh sas_phymap_destroy(pwp->hss_phymap); 13134c06356bSdh } 13144c06356bSdh 13154c06356bSdh /* 13164c06356bSdh * Make sure that any pending watchdog won't 13174c06356bSdh * be called from this point on out. 13184c06356bSdh */ 13194c06356bSdh (void) untimeout(pwp->wdhandle); 13204c06356bSdh /* 13214c06356bSdh * After the above action, the watchdog 13224c06356bSdh * timer that starts up the worker task 13234c06356bSdh * may trigger but will exit immediately 13244c06356bSdh * on triggering. 13254c06356bSdh * 13264c06356bSdh * Now that this is done, we can destroy 13274c06356bSdh * the task queue, which will wait if we're 13284c06356bSdh * running something on it. 13294c06356bSdh */ 13304c06356bSdh if (pwp->tq) { 13314c06356bSdh ddi_taskq_destroy(pwp->tq); 13324c06356bSdh pwp->tq = NULL; 13334c06356bSdh } 13344c06356bSdh 13354c06356bSdh pmcs_fm_fini(pwp); 13364c06356bSdh 13374c06356bSdh if (pwp->hba_attached) { 13384c06356bSdh (void) scsi_hba_detach(pwp->dip); 13394c06356bSdh pwp->hba_attached = 0; 13404c06356bSdh } 13414c06356bSdh 13424c06356bSdh /* 13434c06356bSdh * If the chip hasn't been marked dead, shut it down now 13444c06356bSdh * to bring it back to a known state without attempting 13454c06356bSdh * a soft reset. 13464c06356bSdh */ 13474c06356bSdh if (curstate != STATE_DEAD && pwp->locks_initted) { 13484c06356bSdh /* 13494c06356bSdh * De-register all registered devices 13504c06356bSdh */ 13514c06356bSdh pmcs_deregister_devices(pwp, pwp->root_phys); 13524c06356bSdh 13534c06356bSdh /* 13544c06356bSdh * Stop all the phys. 13554c06356bSdh */ 13564c06356bSdh pmcs_stop_phys(pwp); 13574c06356bSdh 13584c06356bSdh /* 13594c06356bSdh * Shut Down Message Passing 13604c06356bSdh */ 13614c06356bSdh (void) pmcs_stop_mpi(pwp); 13624c06356bSdh 13634c06356bSdh /* 13644c06356bSdh * Reset chip 13654c06356bSdh */ 13664c06356bSdh (void) pmcs_soft_reset(pwp, B_FALSE); 13674c06356bSdh } 13684c06356bSdh 13694c06356bSdh /* 13704c06356bSdh * Turn off interrupts on the chip 13714c06356bSdh */ 13724c06356bSdh if (pwp->mpi_acc_handle) { 13734c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 13744c06356bSdh } 13754c06356bSdh 13764c06356bSdh /* Destroy pwp's lock */ 13774c06356bSdh if (pwp->locks_initted) { 13784c06356bSdh mutex_destroy(&pwp->lock); 13794c06356bSdh mutex_destroy(&pwp->dma_lock); 13804c06356bSdh mutex_destroy(&pwp->axil_lock); 13814c06356bSdh mutex_destroy(&pwp->cq_lock); 13824c06356bSdh mutex_destroy(&pwp->config_lock); 13834c06356bSdh mutex_destroy(&pwp->ict_lock); 13844c06356bSdh mutex_destroy(&pwp->wfree_lock); 13854c06356bSdh mutex_destroy(&pwp->pfree_lock); 13864c06356bSdh mutex_destroy(&pwp->dead_phylist_lock); 13874c06356bSdh #ifdef DEBUG 13884c06356bSdh mutex_destroy(&pwp->dbglock); 13894c06356bSdh #endif 13904c06356bSdh cv_destroy(&pwp->ict_cv); 13914c06356bSdh cv_destroy(&pwp->drain_cv); 13924c06356bSdh pwp->locks_initted = 0; 13934c06356bSdh } 13944c06356bSdh 13954c06356bSdh /* 13964c06356bSdh * Free DMA handles and associated consistent memory 13974c06356bSdh */ 13984c06356bSdh if (pwp->regdump_hndl) { 13994c06356bSdh if (ddi_dma_unbind_handle(pwp->regdump_hndl) != DDI_SUCCESS) { 1400*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1401*c3bc407cSdh "Condition check failed " 14024c06356bSdh "at %s():%d", __func__, __LINE__); 14034c06356bSdh } 14044c06356bSdh ddi_dma_free_handle(&pwp->regdump_hndl); 14054c06356bSdh ddi_dma_mem_free(&pwp->regdump_acchdl); 14064c06356bSdh pwp->regdump_hndl = 0; 14074c06356bSdh } 14084c06356bSdh if (pwp->fwlog_hndl) { 14094c06356bSdh if (ddi_dma_unbind_handle(pwp->fwlog_hndl) != DDI_SUCCESS) { 1410*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1411*c3bc407cSdh "Condition check failed " 14124c06356bSdh "at %s():%d", __func__, __LINE__); 14134c06356bSdh } 14144c06356bSdh ddi_dma_free_handle(&pwp->fwlog_hndl); 14154c06356bSdh ddi_dma_mem_free(&pwp->fwlog_acchdl); 14164c06356bSdh pwp->fwlog_hndl = 0; 14174c06356bSdh } 14184c06356bSdh if (pwp->cip_handles) { 14194c06356bSdh if (ddi_dma_unbind_handle(pwp->cip_handles) != DDI_SUCCESS) { 1420*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1421*c3bc407cSdh "Condition check failed " 14224c06356bSdh "at %s():%d", __func__, __LINE__); 14234c06356bSdh } 14244c06356bSdh ddi_dma_free_handle(&pwp->cip_handles); 14254c06356bSdh ddi_dma_mem_free(&pwp->cip_acchdls); 14264c06356bSdh pwp->cip_handles = 0; 14274c06356bSdh } 14284c06356bSdh for (i = 0; i < PMCS_NOQ; i++) { 14294c06356bSdh if (pwp->oqp_handles[i]) { 14304c06356bSdh if (ddi_dma_unbind_handle(pwp->oqp_handles[i]) != 14314c06356bSdh DDI_SUCCESS) { 1432*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1433*c3bc407cSdh "Condition check failed at %s():%d", 1434*c3bc407cSdh __func__, __LINE__); 14354c06356bSdh } 14364c06356bSdh ddi_dma_free_handle(&pwp->oqp_handles[i]); 14374c06356bSdh ddi_dma_mem_free(&pwp->oqp_acchdls[i]); 14384c06356bSdh pwp->oqp_handles[i] = 0; 14394c06356bSdh } 14404c06356bSdh } 14414c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 14424c06356bSdh if (pwp->iqp_handles[i]) { 14434c06356bSdh if (ddi_dma_unbind_handle(pwp->iqp_handles[i]) != 14444c06356bSdh DDI_SUCCESS) { 1445*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1446*c3bc407cSdh "Condition check failed at %s():%d", 1447*c3bc407cSdh __func__, __LINE__); 14484c06356bSdh } 14494c06356bSdh ddi_dma_free_handle(&pwp->iqp_handles[i]); 14504c06356bSdh ddi_dma_mem_free(&pwp->iqp_acchdls[i]); 14514c06356bSdh pwp->iqp_handles[i] = 0; 14524c06356bSdh } 14534c06356bSdh } 14544c06356bSdh 14554c06356bSdh pmcs_free_dma_chunklist(pwp); 14564c06356bSdh 14574c06356bSdh /* 14584c06356bSdh * Unmap registers and destroy access handles 14594c06356bSdh */ 14604c06356bSdh if (pwp->mpi_acc_handle) { 14614c06356bSdh ddi_regs_map_free(&pwp->mpi_acc_handle); 14624c06356bSdh pwp->mpi_acc_handle = 0; 14634c06356bSdh } 14644c06356bSdh if (pwp->top_acc_handle) { 14654c06356bSdh ddi_regs_map_free(&pwp->top_acc_handle); 14664c06356bSdh pwp->top_acc_handle = 0; 14674c06356bSdh } 14684c06356bSdh if (pwp->gsm_acc_handle) { 14694c06356bSdh ddi_regs_map_free(&pwp->gsm_acc_handle); 14704c06356bSdh pwp->gsm_acc_handle = 0; 14714c06356bSdh } 14724c06356bSdh if (pwp->msg_acc_handle) { 14734c06356bSdh ddi_regs_map_free(&pwp->msg_acc_handle); 14744c06356bSdh pwp->msg_acc_handle = 0; 14754c06356bSdh } 14764c06356bSdh if (pwp->pci_acc_handle) { 14774c06356bSdh pci_config_teardown(&pwp->pci_acc_handle); 14784c06356bSdh pwp->pci_acc_handle = 0; 14794c06356bSdh } 14804c06356bSdh 14814c06356bSdh /* 14824c06356bSdh * Do memory allocation cleanup. 14834c06356bSdh */ 14844c06356bSdh while (pwp->dma_freelist) { 14854c06356bSdh pmcs_dmachunk_t *this = pwp->dma_freelist; 14864c06356bSdh pwp->dma_freelist = this->nxt; 14874c06356bSdh kmem_free(this, sizeof (pmcs_dmachunk_t)); 14884c06356bSdh } 14894c06356bSdh 14904c06356bSdh /* 14914c06356bSdh * Free pools 14924c06356bSdh */ 14934c06356bSdh if (pwp->iocomp_cb_cache) { 14944c06356bSdh kmem_cache_destroy(pwp->iocomp_cb_cache); 14954c06356bSdh } 14964c06356bSdh 14974c06356bSdh /* 14984c06356bSdh * Free all PHYs (at level > 0), then free the cache 14994c06356bSdh */ 15004c06356bSdh pmcs_free_all_phys(pwp, pwp->root_phys); 15014c06356bSdh if (pwp->phy_cache) { 15024c06356bSdh kmem_cache_destroy(pwp->phy_cache); 15034c06356bSdh } 15044c06356bSdh 15054c06356bSdh /* 15064c06356bSdh * Free root PHYs 15074c06356bSdh */ 15084c06356bSdh if (pwp->root_phys) { 15094c06356bSdh pmcs_phy_t *phyp = pwp->root_phys; 15104c06356bSdh for (i = 0; i < pwp->nphy; i++) { 15114c06356bSdh mutex_destroy(&phyp->phy_lock); 15124c06356bSdh phyp = phyp->sibling; 15134c06356bSdh } 15144c06356bSdh kmem_free(pwp->root_phys, pwp->nphy * sizeof (pmcs_phy_t)); 15154c06356bSdh pwp->root_phys = NULL; 15164c06356bSdh pwp->nphy = 0; 15174c06356bSdh } 15184c06356bSdh 15194c06356bSdh /* Free the targets list */ 15204c06356bSdh if (pwp->targets) { 15214c06356bSdh kmem_free(pwp->targets, 15224c06356bSdh sizeof (pmcs_xscsi_t *) * pwp->max_dev); 15234c06356bSdh } 15244c06356bSdh 15254c06356bSdh /* 15264c06356bSdh * Free work structures 15274c06356bSdh */ 15284c06356bSdh 15294c06356bSdh if (pwp->work && pwp->max_cmd) { 15304c06356bSdh for (i = 0; i < pwp->max_cmd - 1; i++) { 15314c06356bSdh pmcwork_t *pwrk = &pwp->work[i]; 15324c06356bSdh mutex_destroy(&pwrk->lock); 15334c06356bSdh cv_destroy(&pwrk->sleep_cv); 15344c06356bSdh } 15354c06356bSdh kmem_free(pwp->work, sizeof (pmcwork_t) * pwp->max_cmd); 15364c06356bSdh pwp->work = NULL; 15374c06356bSdh pwp->max_cmd = 0; 15384c06356bSdh } 15394c06356bSdh 15404c06356bSdh /* 15414c06356bSdh * Do last property and SCSA cleanup 15424c06356bSdh */ 15434c06356bSdh if (pwp->tran) { 15444c06356bSdh scsi_hba_tran_free(pwp->tran); 15454c06356bSdh pwp->tran = NULL; 15464c06356bSdh } 15474c06356bSdh if (pwp->reset_notify_listf) { 15484c06356bSdh scsi_hba_reset_notify_tear_down(pwp->reset_notify_listf); 15494c06356bSdh pwp->reset_notify_listf = NULL; 15504c06356bSdh } 15514c06356bSdh ddi_prop_remove_all(pwp->dip); 15524c06356bSdh if (pwp->stuck) { 15534c06356bSdh return (-1); 15544c06356bSdh } 15554c06356bSdh 15564c06356bSdh /* Free register dump area if allocated */ 15574c06356bSdh if (pwp->regdumpp) { 15584c06356bSdh kmem_free(pwp->regdumpp, PMCS_REG_DUMP_SIZE); 15594c06356bSdh pwp->regdumpp = NULL; 15604c06356bSdh } 15614c06356bSdh if (pwp->iqpt && pwp->iqpt->head) { 15624c06356bSdh kmem_free(pwp->iqpt->head, PMCS_IQP_TRACE_BUFFER_SIZE); 15634c06356bSdh pwp->iqpt->head = pwp->iqpt->curpos = NULL; 15644c06356bSdh } 15654c06356bSdh if (pwp->iqpt) { 15664c06356bSdh kmem_free(pwp->iqpt, sizeof (pmcs_iqp_trace_t)); 15674c06356bSdh pwp->iqpt = NULL; 15684c06356bSdh } 15694c06356bSdh 15704c06356bSdh ddi_soft_state_free(pmcs_softc_state, ddi_get_instance(pwp->dip)); 15714c06356bSdh return (0); 15724c06356bSdh } 15734c06356bSdh 15744c06356bSdh /* 15754c06356bSdh * quiesce (9E) entry point 15764c06356bSdh * 15774c06356bSdh * This function is called when the system is single-threaded at high PIL 15784c06356bSdh * with preemption disabled. Therefore, the function must not block/wait/sleep. 15794c06356bSdh * 15804c06356bSdh * Returns DDI_SUCCESS or DDI_FAILURE. 15814c06356bSdh * 15824c06356bSdh */ 15834c06356bSdh static int 15844c06356bSdh pmcs_quiesce(dev_info_t *dip) 15854c06356bSdh { 15864c06356bSdh pmcs_hw_t *pwp; 15874c06356bSdh scsi_hba_tran_t *tran; 15884c06356bSdh 15894c06356bSdh if ((tran = ddi_get_driver_private(dip)) == NULL) 15904c06356bSdh return (DDI_SUCCESS); 15914c06356bSdh 15924c06356bSdh /* No quiesce necessary on a per-iport basis */ 15934c06356bSdh if (scsi_hba_iport_unit_address(dip) != NULL) { 15944c06356bSdh return (DDI_SUCCESS); 15954c06356bSdh } 15964c06356bSdh 15974c06356bSdh if ((pwp = TRAN2PMC(tran)) == NULL) 15984c06356bSdh return (DDI_SUCCESS); 15994c06356bSdh 16004c06356bSdh /* Stop MPI & Reset chip (no need to re-initialize) */ 16014c06356bSdh (void) pmcs_stop_mpi(pwp); 16024c06356bSdh (void) pmcs_soft_reset(pwp, B_TRUE); 16034c06356bSdh 16044c06356bSdh return (DDI_SUCCESS); 16054c06356bSdh } 16064c06356bSdh 16074c06356bSdh /* 16084c06356bSdh * Called with xp->statlock and PHY lock and scratch acquired. 16094c06356bSdh */ 16104c06356bSdh static int 16114c06356bSdh pmcs_add_sata_device(pmcs_hw_t *pwp, pmcs_xscsi_t *xp) 16124c06356bSdh { 16134c06356bSdh ata_identify_t *ati; 16144c06356bSdh int result, i; 16154c06356bSdh pmcs_phy_t *pptr; 16164c06356bSdh uint16_t *a; 16174c06356bSdh union { 16184c06356bSdh uint8_t nsa[8]; 16194c06356bSdh uint16_t nsb[4]; 16204c06356bSdh } u; 16214c06356bSdh 16224c06356bSdh /* 16234c06356bSdh * Safe defaults - use only if this target is brand new (i.e. doesn't 16244c06356bSdh * already have these settings configured) 16254c06356bSdh */ 16264c06356bSdh if (xp->capacity == 0) { 16274c06356bSdh xp->capacity = (uint64_t)-1; 16284c06356bSdh xp->ca = 1; 16294c06356bSdh xp->qdepth = 1; 16304c06356bSdh xp->pio = 1; 16314c06356bSdh } 16324c06356bSdh 16334c06356bSdh pptr = xp->phy; 16344c06356bSdh 16354c06356bSdh /* 16364c06356bSdh * We only try and issue an IDENTIFY for first level 16374c06356bSdh * (direct attached) devices. We don't try and 16384c06356bSdh * set other quirks here (this will happen later, 16394c06356bSdh * if the device is fully configured) 16404c06356bSdh */ 16414c06356bSdh if (pptr->level) { 16424c06356bSdh return (0); 16434c06356bSdh } 16444c06356bSdh 16454c06356bSdh mutex_exit(&xp->statlock); 16464c06356bSdh result = pmcs_sata_identify(pwp, pptr); 16474c06356bSdh mutex_enter(&xp->statlock); 16484c06356bSdh 16494c06356bSdh if (result) { 16504c06356bSdh return (result); 16514c06356bSdh } 16524c06356bSdh ati = pwp->scratch; 16534c06356bSdh a = &ati->word108; 16544c06356bSdh for (i = 0; i < 4; i++) { 16554c06356bSdh u.nsb[i] = ddi_swap16(*a++); 16564c06356bSdh } 16574c06356bSdh 16584c06356bSdh /* 16594c06356bSdh * Check the returned data for being a valid (NAA=5) WWN. 16604c06356bSdh * If so, use that and override the SAS address we were 16614c06356bSdh * given at Link Up time. 16624c06356bSdh */ 16634c06356bSdh if ((u.nsa[0] >> 4) == 5) { 16644c06356bSdh (void) memcpy(pptr->sas_address, u.nsa, 8); 16654c06356bSdh } 1666*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pptr, xp, 1667*c3bc407cSdh "%s: %s has SAS ADDRESS " SAS_ADDR_FMT, 16684c06356bSdh __func__, pptr->path, SAS_ADDR_PRT(pptr->sas_address)); 16694c06356bSdh return (0); 16704c06356bSdh } 16714c06356bSdh 16724c06356bSdh /* 16734c06356bSdh * Called with PHY lock and target statlock held and scratch acquired 16744c06356bSdh */ 16754c06356bSdh static boolean_t 16764c06356bSdh pmcs_add_new_device(pmcs_hw_t *pwp, pmcs_xscsi_t *target) 16774c06356bSdh { 16784c06356bSdh ASSERT(target != NULL); 1679*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, target, "%s: target = 0x%p", 16804c06356bSdh __func__, (void *) target); 16814c06356bSdh 16824c06356bSdh switch (target->phy->dtype) { 16834c06356bSdh case SATA: 16844c06356bSdh if (pmcs_add_sata_device(pwp, target) != 0) { 1685*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, target->phy, 1686*c3bc407cSdh target, "%s: add_sata_device failed for tgt 0x%p", 16874c06356bSdh __func__, (void *) target); 16884c06356bSdh return (B_FALSE); 16894c06356bSdh } 16904c06356bSdh break; 16914c06356bSdh case SAS: 16924c06356bSdh target->qdepth = maxqdepth; 16934c06356bSdh break; 16944c06356bSdh case EXPANDER: 16954c06356bSdh target->qdepth = 1; 16964c06356bSdh break; 16974c06356bSdh } 16984c06356bSdh 16994c06356bSdh target->new = 0; 17004c06356bSdh target->assigned = 1; 17014c06356bSdh target->dev_state = PMCS_DEVICE_STATE_OPERATIONAL; 17024c06356bSdh target->dtype = target->phy->dtype; 17034c06356bSdh 17044c06356bSdh /* 17054c06356bSdh * Set the PHY's config stop time to 0. This is one of the final 17064c06356bSdh * stops along the config path, so we're indicating that we 17074c06356bSdh * successfully configured the PHY. 17084c06356bSdh */ 17094c06356bSdh target->phy->config_stop = 0; 17104c06356bSdh 17114c06356bSdh return (B_TRUE); 17124c06356bSdh } 17134c06356bSdh 17144c06356bSdh void 17154c06356bSdh pmcs_worker(void *arg) 17164c06356bSdh { 17174c06356bSdh pmcs_hw_t *pwp = arg; 17184c06356bSdh ulong_t work_flags; 17194c06356bSdh 17204c06356bSdh DTRACE_PROBE2(pmcs__worker, ulong_t, pwp->work_flags, boolean_t, 17214c06356bSdh pwp->config_changed); 17224c06356bSdh 17234c06356bSdh if (pwp->state != STATE_RUNNING) { 17244c06356bSdh return; 17254c06356bSdh } 17264c06356bSdh 17274c06356bSdh work_flags = atomic_swap_ulong(&pwp->work_flags, 0); 17284c06356bSdh 17294c06356bSdh if (work_flags & PMCS_WORK_FLAG_SAS_HW_ACK) { 17304c06356bSdh pmcs_ack_events(pwp); 17314c06356bSdh } 17324c06356bSdh 17334c06356bSdh if (work_flags & PMCS_WORK_FLAG_SPINUP_RELEASE) { 17344c06356bSdh mutex_enter(&pwp->lock); 17354c06356bSdh pmcs_spinup_release(pwp, NULL); 17364c06356bSdh mutex_exit(&pwp->lock); 17374c06356bSdh } 17384c06356bSdh 17394c06356bSdh if (work_flags & PMCS_WORK_FLAG_SSP_EVT_RECOVERY) { 17404c06356bSdh pmcs_ssp_event_recovery(pwp); 17414c06356bSdh } 17424c06356bSdh 17434c06356bSdh if (work_flags & PMCS_WORK_FLAG_DS_ERR_RECOVERY) { 17444c06356bSdh pmcs_dev_state_recovery(pwp, NULL); 17454c06356bSdh } 17464c06356bSdh 17474c06356bSdh if (work_flags & PMCS_WORK_FLAG_DISCOVER) { 17484c06356bSdh pmcs_discover(pwp); 17494c06356bSdh } 17504c06356bSdh 17514c06356bSdh if (work_flags & PMCS_WORK_FLAG_ABORT_HANDLE) { 17524c06356bSdh if (pmcs_abort_handler(pwp)) { 17534c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ABORT_HANDLE); 17544c06356bSdh } 17554c06356bSdh } 17564c06356bSdh 17574c06356bSdh if (work_flags & PMCS_WORK_FLAG_SATA_RUN) { 17584c06356bSdh pmcs_sata_work(pwp); 17594c06356bSdh } 17604c06356bSdh 17614c06356bSdh if (work_flags & PMCS_WORK_FLAG_RUN_QUEUES) { 17624c06356bSdh pmcs_scsa_wq_run(pwp); 17634c06356bSdh mutex_enter(&pwp->lock); 17644c06356bSdh PMCS_CQ_RUN(pwp); 17654c06356bSdh mutex_exit(&pwp->lock); 17664c06356bSdh } 17674c06356bSdh 17684c06356bSdh if (work_flags & PMCS_WORK_FLAG_ADD_DMA_CHUNKS) { 17694c06356bSdh if (pmcs_add_more_chunks(pwp, 17704c06356bSdh ptob(1) * PMCS_ADDTL_CHUNK_PAGES)) { 17714c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_ADD_DMA_CHUNKS); 17724c06356bSdh } else { 17734c06356bSdh SCHEDULE_WORK(pwp, PMCS_WORK_RUN_QUEUES); 17744c06356bSdh } 17754c06356bSdh } 17764c06356bSdh } 17774c06356bSdh 17784c06356bSdh static int 17794c06356bSdh pmcs_add_more_chunks(pmcs_hw_t *pwp, unsigned long nsize) 17804c06356bSdh { 17814c06356bSdh pmcs_dmachunk_t *dc; 17824c06356bSdh unsigned long dl; 17834c06356bSdh pmcs_chunk_t *pchunk = NULL; 17844c06356bSdh 17854c06356bSdh pwp->cip_dma_attr.dma_attr_align = sizeof (uint32_t); 17864c06356bSdh 17874c06356bSdh pchunk = kmem_zalloc(sizeof (pmcs_chunk_t), KM_SLEEP); 17884c06356bSdh if (pchunk == NULL) { 1789*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 17904c06356bSdh "Not enough memory for DMA chunks"); 17914c06356bSdh return (-1); 17924c06356bSdh } 17934c06356bSdh 17944c06356bSdh if (pmcs_dma_setup(pwp, &pwp->cip_dma_attr, &pchunk->acc_handle, 17954c06356bSdh &pchunk->dma_handle, nsize, (caddr_t *)&pchunk->addrp, 17964c06356bSdh &pchunk->dma_addr) == B_FALSE) { 1797*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 1798*c3bc407cSdh "Failed to setup DMA for chunks"); 17994c06356bSdh kmem_free(pchunk, sizeof (pmcs_chunk_t)); 18004c06356bSdh return (-1); 18014c06356bSdh } 18024c06356bSdh 18034c06356bSdh if ((pmcs_check_acc_handle(pchunk->acc_handle) != DDI_SUCCESS) || 18044c06356bSdh (pmcs_check_dma_handle(pchunk->dma_handle) != DDI_SUCCESS)) { 18054c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_UNAFFECTED); 18064c06356bSdh return (-1); 18074c06356bSdh } 18084c06356bSdh 18094c06356bSdh bzero(pchunk->addrp, nsize); 18104c06356bSdh dc = NULL; 18114c06356bSdh for (dl = 0; dl < (nsize / PMCS_SGL_CHUNKSZ); dl++) { 18124c06356bSdh pmcs_dmachunk_t *tmp; 18134c06356bSdh tmp = kmem_alloc(sizeof (pmcs_dmachunk_t), KM_SLEEP); 18144c06356bSdh tmp->nxt = dc; 18154c06356bSdh dc = tmp; 18164c06356bSdh } 18174c06356bSdh mutex_enter(&pwp->dma_lock); 18184c06356bSdh pmcs_idma_chunks(pwp, dc, pchunk, nsize); 18194c06356bSdh pwp->nchunks++; 18204c06356bSdh mutex_exit(&pwp->dma_lock); 18214c06356bSdh return (0); 18224c06356bSdh } 18234c06356bSdh 18244c06356bSdh 18254c06356bSdh static void 18264c06356bSdh pmcs_check_commands(pmcs_hw_t *pwp) 18274c06356bSdh { 18284c06356bSdh pmcs_cmd_t *sp; 18294c06356bSdh size_t amt; 18304c06356bSdh char path[32]; 18314c06356bSdh pmcwork_t *pwrk; 18324c06356bSdh pmcs_xscsi_t *target; 18334c06356bSdh pmcs_phy_t *phyp; 18344c06356bSdh 18354c06356bSdh for (pwrk = pwp->work; pwrk < &pwp->work[pwp->max_cmd]; pwrk++) { 18364c06356bSdh mutex_enter(&pwrk->lock); 18374c06356bSdh 18384c06356bSdh /* 18394c06356bSdh * If the command isn't active, we can't be timing it still. 18404c06356bSdh * Active means the tag is not free and the state is "on chip". 18414c06356bSdh */ 18424c06356bSdh if (!PMCS_COMMAND_ACTIVE(pwrk)) { 18434c06356bSdh mutex_exit(&pwrk->lock); 18444c06356bSdh continue; 18454c06356bSdh } 18464c06356bSdh 18474c06356bSdh /* 18484c06356bSdh * No timer active for this command. 18494c06356bSdh */ 18504c06356bSdh if (pwrk->timer == 0) { 18514c06356bSdh mutex_exit(&pwrk->lock); 18524c06356bSdh continue; 18534c06356bSdh } 18544c06356bSdh 18554c06356bSdh /* 18564c06356bSdh * Knock off bits for the time interval. 18574c06356bSdh */ 18584c06356bSdh if (pwrk->timer >= US2WT(PMCS_WATCH_INTERVAL)) { 18594c06356bSdh pwrk->timer -= US2WT(PMCS_WATCH_INTERVAL); 18604c06356bSdh } else { 18614c06356bSdh pwrk->timer = 0; 18624c06356bSdh } 18634c06356bSdh if (pwrk->timer > 0) { 18644c06356bSdh mutex_exit(&pwrk->lock); 18654c06356bSdh continue; 18664c06356bSdh } 18674c06356bSdh 18684c06356bSdh /* 18694c06356bSdh * The command has now officially timed out. 18704c06356bSdh * Get the path for it. If it doesn't have 18714c06356bSdh * a phy pointer any more, it's really dead 18724c06356bSdh * and can just be put back on the free list. 18734c06356bSdh * There should *not* be any commands associated 18744c06356bSdh * with it any more. 18754c06356bSdh */ 18764c06356bSdh if (pwrk->phy == NULL) { 1877*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18784c06356bSdh "dead command with gone phy being recycled"); 18794c06356bSdh ASSERT(pwrk->xp == NULL); 18804c06356bSdh pmcs_pwork(pwp, pwrk); 18814c06356bSdh continue; 18824c06356bSdh } 18834c06356bSdh amt = sizeof (path); 18844c06356bSdh amt = min(sizeof (pwrk->phy->path), amt); 18854c06356bSdh (void) memcpy(path, pwrk->phy->path, amt); 18864c06356bSdh 18874c06356bSdh /* 18884c06356bSdh * If this is a non-SCSA command, stop here. Eventually 18894c06356bSdh * we might do something with non-SCSA commands here- 18904c06356bSdh * but so far their timeout mechanisms are handled in 18914c06356bSdh * the WAIT_FOR macro. 18924c06356bSdh */ 18934c06356bSdh if (pwrk->xp == NULL) { 1894*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 18954c06356bSdh "%s: non-SCSA cmd tag 0x%x timed out", 18964c06356bSdh path, pwrk->htag); 18974c06356bSdh mutex_exit(&pwrk->lock); 18984c06356bSdh continue; 18994c06356bSdh } 19004c06356bSdh 19014c06356bSdh sp = pwrk->arg; 19024c06356bSdh ASSERT(sp != NULL); 19034c06356bSdh 19044c06356bSdh /* 19054c06356bSdh * Mark it as timed out. 19064c06356bSdh */ 19074c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_TIMEOUT; 19084c06356bSdh CMD2PKT(sp)->pkt_statistics |= STAT_TIMEOUT; 19094c06356bSdh #ifdef DEBUG 1910*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 19114c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x) onwire=%d", 19124c06356bSdh path, pwrk->htag, pwrk->state, pwrk->onwire); 19134c06356bSdh #else 1914*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, pwrk->phy, pwrk->xp, 19154c06356bSdh "%s: SCSA cmd tag 0x%x timed out (state %x)", 19164c06356bSdh path, pwrk->htag, pwrk->state); 19174c06356bSdh #endif 19184c06356bSdh /* 19194c06356bSdh * Mark the work structure as timed out. 19204c06356bSdh */ 19214c06356bSdh pwrk->state = PMCS_WORK_STATE_TIMED_OUT; 19224c06356bSdh phyp = pwrk->phy; 19234c06356bSdh target = pwrk->xp; 19244c06356bSdh mutex_exit(&pwrk->lock); 19254c06356bSdh 19264c06356bSdh pmcs_lock_phy(phyp); 19274c06356bSdh mutex_enter(&target->statlock); 19284c06356bSdh 19294c06356bSdh /* 19304c06356bSdh * No point attempting recovery if the device is gone 19314c06356bSdh */ 19324c06356bSdh if (pwrk->xp->dev_gone) { 19334c06356bSdh mutex_exit(&target->statlock); 19344c06356bSdh pmcs_unlock_phy(phyp); 1935*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 19364c06356bSdh "%s: tgt(0x%p) is gone. Returning CMD_DEV_GONE " 19374c06356bSdh "for htag 0x%08x", __func__, 19384c06356bSdh (void *)pwrk->xp, pwrk->htag); 19394c06356bSdh mutex_enter(&pwrk->lock); 19404c06356bSdh if (!PMCS_COMMAND_DONE(pwrk)) { 19414c06356bSdh /* Complete this command here */ 1942*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, target, 1943*c3bc407cSdh "%s: Completing cmd (htag 0x%08x) " 19444c06356bSdh "anyway", __func__, pwrk->htag); 19454c06356bSdh pwrk->dead = 1; 19464c06356bSdh CMD2PKT(sp)->pkt_reason = CMD_DEV_GONE; 19474c06356bSdh CMD2PKT(sp)->pkt_state = STATE_GOT_BUS; 19484c06356bSdh pmcs_complete_work_impl(pwp, pwrk, NULL, 0); 19494c06356bSdh } else { 19504c06356bSdh mutex_exit(&pwrk->lock); 19514c06356bSdh } 19524c06356bSdh continue; 19534c06356bSdh } 19544c06356bSdh 19554c06356bSdh /* 19564c06356bSdh * See if we're already waiting for device state recovery 19574c06356bSdh */ 19584c06356bSdh if (target->recover_wait) { 1959*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_DEV_STATE, phyp, target, 19604c06356bSdh "%s: Target %p already in recovery", __func__, 19614c06356bSdh (void *)target); 19624c06356bSdh mutex_exit(&target->statlock); 19634c06356bSdh pmcs_unlock_phy(phyp); 19644c06356bSdh continue; 19654c06356bSdh } 19664c06356bSdh 19674c06356bSdh pmcs_start_dev_state_recovery(target, phyp); 19684c06356bSdh mutex_exit(&target->statlock); 19694c06356bSdh pmcs_unlock_phy(phyp); 19704c06356bSdh } 19714c06356bSdh /* 19724c06356bSdh * Run any completions that may have been queued up. 19734c06356bSdh */ 19744c06356bSdh PMCS_CQ_RUN(pwp); 19754c06356bSdh } 19764c06356bSdh 19774c06356bSdh static void 19784c06356bSdh pmcs_watchdog(void *arg) 19794c06356bSdh { 19804c06356bSdh pmcs_hw_t *pwp = arg; 19814c06356bSdh 19824c06356bSdh DTRACE_PROBE2(pmcs__watchdog, ulong_t, pwp->work_flags, boolean_t, 19834c06356bSdh pwp->config_changed); 19844c06356bSdh 19854c06356bSdh mutex_enter(&pwp->lock); 19864c06356bSdh 19874c06356bSdh if (pwp->state != STATE_RUNNING) { 19884c06356bSdh mutex_exit(&pwp->lock); 19894c06356bSdh return; 19904c06356bSdh } 19914c06356bSdh 19924c06356bSdh if (atomic_cas_ulong(&pwp->work_flags, 0, 0) != 0) { 19934c06356bSdh if (ddi_taskq_dispatch(pwp->tq, pmcs_worker, pwp, 19944c06356bSdh DDI_NOSLEEP) != DDI_SUCCESS) { 1995*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 19964c06356bSdh "Could not dispatch to worker thread"); 19974c06356bSdh } 19984c06356bSdh } 19994c06356bSdh pwp->wdhandle = timeout(pmcs_watchdog, pwp, 20004c06356bSdh drv_usectohz(PMCS_WATCH_INTERVAL)); 20014c06356bSdh mutex_exit(&pwp->lock); 20024c06356bSdh pmcs_check_commands(pwp); 20034c06356bSdh pmcs_handle_dead_phys(pwp); 20044c06356bSdh } 20054c06356bSdh 20064c06356bSdh static int 20074c06356bSdh pmcs_remove_ihandlers(pmcs_hw_t *pwp, int icnt) 20084c06356bSdh { 20094c06356bSdh int i, r, rslt = 0; 20104c06356bSdh for (i = 0; i < icnt; i++) { 20114c06356bSdh r = ddi_intr_remove_handler(pwp->ih_table[i]); 20124c06356bSdh if (r == DDI_SUCCESS) { 20134c06356bSdh continue; 20144c06356bSdh } 2015*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20164c06356bSdh "%s: unable to remove interrupt handler %d", __func__, i); 20174c06356bSdh rslt = -1; 20184c06356bSdh break; 20194c06356bSdh } 20204c06356bSdh return (rslt); 20214c06356bSdh } 20224c06356bSdh 20234c06356bSdh static int 20244c06356bSdh pmcs_disable_intrs(pmcs_hw_t *pwp, int icnt) 20254c06356bSdh { 20264c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 20274c06356bSdh int r = ddi_intr_block_disable(&pwp->ih_table[0], 20284c06356bSdh pwp->intr_cnt); 20294c06356bSdh if (r != DDI_SUCCESS) { 2030*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20314c06356bSdh "unable to disable interrupt block"); 20324c06356bSdh return (-1); 20334c06356bSdh } 20344c06356bSdh } else { 20354c06356bSdh int i; 20364c06356bSdh for (i = 0; i < icnt; i++) { 20374c06356bSdh if (ddi_intr_disable(pwp->ih_table[i]) == DDI_SUCCESS) { 20384c06356bSdh continue; 20394c06356bSdh } 2040*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 20414c06356bSdh "unable to disable interrupt %d", i); 20424c06356bSdh return (-1); 20434c06356bSdh } 20444c06356bSdh } 20454c06356bSdh return (0); 20464c06356bSdh } 20474c06356bSdh 20484c06356bSdh static int 20494c06356bSdh pmcs_free_intrs(pmcs_hw_t *pwp, int icnt) 20504c06356bSdh { 20514c06356bSdh int i; 20524c06356bSdh for (i = 0; i < icnt; i++) { 20534c06356bSdh if (ddi_intr_free(pwp->ih_table[i]) == DDI_SUCCESS) { 20544c06356bSdh continue; 20554c06356bSdh } 2056*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2057*c3bc407cSdh "unable to free interrupt %d", i); 20584c06356bSdh return (-1); 20594c06356bSdh } 20604c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 20614c06356bSdh pwp->ih_table_size = 0; 20624c06356bSdh return (0); 20634c06356bSdh } 20644c06356bSdh 20654c06356bSdh /* 20664c06356bSdh * Try to set up interrupts of type "type" with a minimum number of interrupts 20674c06356bSdh * of "min". 20684c06356bSdh */ 20694c06356bSdh static void 20704c06356bSdh pmcs_setup_intr_impl(pmcs_hw_t *pwp, int type, int min) 20714c06356bSdh { 20724c06356bSdh int rval, avail, count, actual, max; 20734c06356bSdh 20744c06356bSdh rval = ddi_intr_get_nintrs(pwp->dip, type, &count); 20754c06356bSdh if ((rval != DDI_SUCCESS) || (count < min)) { 2076*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 20774c06356bSdh "%s: get_nintrs failed; type: %d rc: %d count: %d min: %d", 20784c06356bSdh __func__, type, rval, count, min); 20794c06356bSdh return; 20804c06356bSdh } 20814c06356bSdh 2082*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 20834c06356bSdh "%s: nintrs = %d for type: %d", __func__, count, type); 20844c06356bSdh 20854c06356bSdh rval = ddi_intr_get_navail(pwp->dip, type, &avail); 20864c06356bSdh if ((rval != DDI_SUCCESS) || (avail < min)) { 2087*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 20884c06356bSdh "%s: get_navail failed; type: %d rc: %d avail: %d min: %d", 20894c06356bSdh __func__, type, rval, avail, min); 20904c06356bSdh return; 20914c06356bSdh } 20924c06356bSdh 2093*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 20944c06356bSdh "%s: navail = %d for type: %d", __func__, avail, type); 20954c06356bSdh 20964c06356bSdh pwp->ih_table_size = avail * sizeof (ddi_intr_handle_t); 20974c06356bSdh pwp->ih_table = kmem_alloc(pwp->ih_table_size, KM_SLEEP); 20984c06356bSdh 20994c06356bSdh switch (type) { 21004c06356bSdh case DDI_INTR_TYPE_MSIX: 21014c06356bSdh pwp->int_type = PMCS_INT_MSIX; 21024c06356bSdh max = PMCS_MAX_MSIX; 21034c06356bSdh break; 21044c06356bSdh case DDI_INTR_TYPE_MSI: 21054c06356bSdh pwp->int_type = PMCS_INT_MSI; 21064c06356bSdh max = PMCS_MAX_MSI; 21074c06356bSdh break; 21084c06356bSdh case DDI_INTR_TYPE_FIXED: 21094c06356bSdh default: 21104c06356bSdh pwp->int_type = PMCS_INT_FIXED; 21114c06356bSdh max = PMCS_MAX_FIXED; 21124c06356bSdh break; 21134c06356bSdh } 21144c06356bSdh 21154c06356bSdh rval = ddi_intr_alloc(pwp->dip, pwp->ih_table, type, 0, max, &actual, 21164c06356bSdh DDI_INTR_ALLOC_NORMAL); 21174c06356bSdh if (rval != DDI_SUCCESS) { 2118*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, NULL, NULL, 21194c06356bSdh "%s: ddi_intr_alloc failed; type: %d rc: %d", 21204c06356bSdh __func__, type, rval); 21214c06356bSdh kmem_free(pwp->ih_table, pwp->ih_table_size); 21224c06356bSdh pwp->ih_table = NULL; 21234c06356bSdh pwp->ih_table_size = 0; 21244c06356bSdh pwp->intr_cnt = 0; 21254c06356bSdh pwp->int_type = PMCS_INT_NONE; 21264c06356bSdh return; 21274c06356bSdh } 21284c06356bSdh 21294c06356bSdh pwp->intr_cnt = actual; 21304c06356bSdh } 21314c06356bSdh 21324c06356bSdh /* 21334c06356bSdh * Set up interrupts. 21344c06356bSdh * We return one of three values: 21354c06356bSdh * 21364c06356bSdh * 0 - success 21374c06356bSdh * EAGAIN - failure to set up interrupts 21384c06356bSdh * EIO - "" + we're now stuck partly enabled 21394c06356bSdh * 21404c06356bSdh * If EIO is returned, we can't unload the driver. 21414c06356bSdh */ 21424c06356bSdh static int 21434c06356bSdh pmcs_setup_intr(pmcs_hw_t *pwp) 21444c06356bSdh { 21454c06356bSdh int i, r, itypes, oqv_count; 21464c06356bSdh ddi_intr_handler_t **iv_table; 21474c06356bSdh size_t iv_table_size; 21484c06356bSdh uint_t pri; 21494c06356bSdh 21504c06356bSdh if (ddi_intr_get_supported_types(pwp->dip, &itypes) != DDI_SUCCESS) { 2151*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2152*c3bc407cSdh "cannot get interrupt types"); 21534c06356bSdh return (EAGAIN); 21544c06356bSdh } 21554c06356bSdh 21564c06356bSdh if (disable_msix) { 21574c06356bSdh itypes &= ~DDI_INTR_TYPE_MSIX; 21584c06356bSdh } 21594c06356bSdh if (disable_msi) { 21604c06356bSdh itypes &= ~DDI_INTR_TYPE_MSI; 21614c06356bSdh } 21624c06356bSdh 21634c06356bSdh /* 21644c06356bSdh * We won't know what firmware we're running until we call pmcs_setup, 21654c06356bSdh * and we can't call pmcs_setup until we establish interrupts. 21664c06356bSdh */ 21674c06356bSdh 21684c06356bSdh pwp->int_type = PMCS_INT_NONE; 21694c06356bSdh 21704c06356bSdh /* 21714c06356bSdh * We want PMCS_MAX_MSIX vectors for MSI-X. Anything less would be 21724c06356bSdh * uncivilized. 21734c06356bSdh */ 21744c06356bSdh if (itypes & DDI_INTR_TYPE_MSIX) { 21754c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSIX, PMCS_MAX_MSIX); 21764c06356bSdh if (pwp->int_type == PMCS_INT_MSIX) { 21774c06356bSdh itypes = 0; 21784c06356bSdh } 21794c06356bSdh } 21804c06356bSdh 21814c06356bSdh if (itypes & DDI_INTR_TYPE_MSI) { 21824c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_MSI, 1); 21834c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 21844c06356bSdh itypes = 0; 21854c06356bSdh } 21864c06356bSdh } 21874c06356bSdh 21884c06356bSdh if (itypes & DDI_INTR_TYPE_FIXED) { 21894c06356bSdh pmcs_setup_intr_impl(pwp, DDI_INTR_TYPE_FIXED, 1); 21904c06356bSdh if (pwp->int_type == PMCS_INT_FIXED) { 21914c06356bSdh itypes = 0; 21924c06356bSdh } 21934c06356bSdh } 21944c06356bSdh 21954c06356bSdh if (pwp->intr_cnt == 0) { 2196*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, 2197*c3bc407cSdh "No interrupts available"); 21984c06356bSdh return (EAGAIN); 21994c06356bSdh } 22004c06356bSdh 22014c06356bSdh iv_table_size = sizeof (ddi_intr_handler_t *) * pwp->intr_cnt; 22024c06356bSdh iv_table = kmem_alloc(iv_table_size, KM_SLEEP); 22034c06356bSdh 22044c06356bSdh /* 22054c06356bSdh * Get iblock cookie and add handlers. 22064c06356bSdh */ 22074c06356bSdh switch (pwp->intr_cnt) { 22084c06356bSdh case 1: 22094c06356bSdh iv_table[0] = pmcs_all_intr; 22104c06356bSdh break; 22114c06356bSdh case 2: 22124c06356bSdh iv_table[0] = pmcs_iodone_ix; 22134c06356bSdh iv_table[1] = pmcs_nonio_ix; 22144c06356bSdh break; 22154c06356bSdh case 4: 22164c06356bSdh iv_table[PMCS_MSIX_GENERAL] = pmcs_general_ix; 22174c06356bSdh iv_table[PMCS_MSIX_IODONE] = pmcs_iodone_ix; 22184c06356bSdh iv_table[PMCS_MSIX_EVENTS] = pmcs_event_ix; 22194c06356bSdh iv_table[PMCS_MSIX_FATAL] = pmcs_fatal_ix; 22204c06356bSdh break; 22214c06356bSdh default: 2222*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22234c06356bSdh "%s: intr_cnt = %d - unexpected", __func__, pwp->intr_cnt); 22244c06356bSdh kmem_free(iv_table, iv_table_size); 22254c06356bSdh return (EAGAIN); 22264c06356bSdh } 22274c06356bSdh 22284c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 22294c06356bSdh r = ddi_intr_add_handler(pwp->ih_table[i], iv_table[i], 22304c06356bSdh (caddr_t)pwp, NULL); 22314c06356bSdh if (r != DDI_SUCCESS) { 22324c06356bSdh kmem_free(iv_table, iv_table_size); 22334c06356bSdh if (pmcs_remove_ihandlers(pwp, i)) { 22344c06356bSdh return (EIO); 22354c06356bSdh } 22364c06356bSdh if (pmcs_free_intrs(pwp, i)) { 22374c06356bSdh return (EIO); 22384c06356bSdh } 22394c06356bSdh pwp->intr_cnt = 0; 22404c06356bSdh return (EAGAIN); 22414c06356bSdh } 22424c06356bSdh } 22434c06356bSdh 22444c06356bSdh kmem_free(iv_table, iv_table_size); 22454c06356bSdh 22464c06356bSdh if (ddi_intr_get_cap(pwp->ih_table[0], &pwp->intr_cap) != DDI_SUCCESS) { 2247*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2248*c3bc407cSdh "unable to get int capabilities"); 22494c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 22504c06356bSdh return (EIO); 22514c06356bSdh } 22524c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 22534c06356bSdh return (EIO); 22544c06356bSdh } 22554c06356bSdh pwp->intr_cnt = 0; 22564c06356bSdh return (EAGAIN); 22574c06356bSdh } 22584c06356bSdh 22594c06356bSdh if (pwp->intr_cap & DDI_INTR_FLAG_BLOCK) { 22604c06356bSdh r = ddi_intr_block_enable(&pwp->ih_table[0], pwp->intr_cnt); 22614c06356bSdh if (r != DDI_SUCCESS) { 2262*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 2263*c3bc407cSdh "intr blk enable failed"); 22644c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 22654c06356bSdh return (EIO); 22664c06356bSdh } 22674c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 22684c06356bSdh return (EIO); 22694c06356bSdh } 22704c06356bSdh pwp->intr_cnt = 0; 22714c06356bSdh return (EFAULT); 22724c06356bSdh } 22734c06356bSdh } else { 22744c06356bSdh for (i = 0; i < pwp->intr_cnt; i++) { 22754c06356bSdh r = ddi_intr_enable(pwp->ih_table[i]); 22764c06356bSdh if (r == DDI_SUCCESS) { 22774c06356bSdh continue; 22784c06356bSdh } 2279*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 22804c06356bSdh "unable to enable interrupt %d", i); 22814c06356bSdh if (pmcs_disable_intrs(pwp, i)) { 22824c06356bSdh return (EIO); 22834c06356bSdh } 22844c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 22854c06356bSdh return (EIO); 22864c06356bSdh } 22874c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 22884c06356bSdh return (EIO); 22894c06356bSdh } 22904c06356bSdh pwp->intr_cnt = 0; 22914c06356bSdh return (EAGAIN); 22924c06356bSdh } 22934c06356bSdh } 22944c06356bSdh 22954c06356bSdh /* 22964c06356bSdh * Set up locks. 22974c06356bSdh */ 22984c06356bSdh if (ddi_intr_get_pri(pwp->ih_table[0], &pri) != DDI_SUCCESS) { 2299*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 23004c06356bSdh "unable to get interrupt priority"); 23014c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 23024c06356bSdh return (EIO); 23034c06356bSdh } 23044c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 23054c06356bSdh return (EIO); 23064c06356bSdh } 23074c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 23084c06356bSdh return (EIO); 23094c06356bSdh } 23104c06356bSdh pwp->intr_cnt = 0; 23114c06356bSdh return (EAGAIN); 23124c06356bSdh } 23134c06356bSdh 23144c06356bSdh pwp->locks_initted = 1; 23154c06356bSdh pwp->intr_pri = pri; 23164c06356bSdh mutex_init(&pwp->lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23174c06356bSdh mutex_init(&pwp->dma_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23184c06356bSdh mutex_init(&pwp->axil_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23194c06356bSdh mutex_init(&pwp->cq_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23204c06356bSdh mutex_init(&pwp->ict_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23214c06356bSdh mutex_init(&pwp->config_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23224c06356bSdh mutex_init(&pwp->wfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23234c06356bSdh mutex_init(&pwp->pfree_lock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23244c06356bSdh mutex_init(&pwp->dead_phylist_lock, NULL, MUTEX_DRIVER, 23254c06356bSdh DDI_INTR_PRI(pri)); 23264c06356bSdh #ifdef DEBUG 23274c06356bSdh mutex_init(&pwp->dbglock, NULL, MUTEX_DRIVER, DDI_INTR_PRI(pri)); 23284c06356bSdh #endif 23294c06356bSdh cv_init(&pwp->ict_cv, NULL, CV_DRIVER, NULL); 23304c06356bSdh cv_init(&pwp->drain_cv, NULL, CV_DRIVER, NULL); 23314c06356bSdh for (i = 0; i < PMCS_NIQ; i++) { 23324c06356bSdh mutex_init(&pwp->iqp_lock[i], NULL, 23334c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 23344c06356bSdh } 23354c06356bSdh for (i = 0; i < pwp->cq_info.cq_threads; i++) { 23364c06356bSdh mutex_init(&pwp->cq_info.cq_thr_info[i].cq_thr_lock, NULL, 23374c06356bSdh MUTEX_DRIVER, DDI_INTR_PRI(pwp->intr_pri)); 23384c06356bSdh cv_init(&pwp->cq_info.cq_thr_info[i].cq_cv, NULL, 23394c06356bSdh CV_DRIVER, NULL); 23404c06356bSdh } 23414c06356bSdh 2342*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_INFO, NULL, NULL, "%d %s interrup%s configured", 23434c06356bSdh pwp->intr_cnt, (pwp->int_type == PMCS_INT_MSIX)? "MSI-X" : 23444c06356bSdh ((pwp->int_type == PMCS_INT_MSI)? "MSI" : "INT-X"), 23454c06356bSdh pwp->intr_cnt == 1? "t" : "ts"); 23464c06356bSdh 23474c06356bSdh 23484c06356bSdh /* 23494c06356bSdh * Enable Interrupts 23504c06356bSdh */ 23514c06356bSdh if (pwp->intr_cnt > PMCS_NOQ) { 23524c06356bSdh oqv_count = pwp->intr_cnt; 23534c06356bSdh } else { 23544c06356bSdh oqv_count = PMCS_NOQ; 23554c06356bSdh } 23564c06356bSdh for (pri = 0xffffffff, i = 0; i < oqv_count; i++) { 23574c06356bSdh pri ^= (1 << i); 23584c06356bSdh } 23594c06356bSdh 23604c06356bSdh mutex_enter(&pwp->lock); 23614c06356bSdh pwp->intr_mask = pri; 23624c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, pwp->intr_mask); 23634c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 23644c06356bSdh mutex_exit(&pwp->lock); 23654c06356bSdh 23664c06356bSdh return (0); 23674c06356bSdh } 23684c06356bSdh 23694c06356bSdh static int 23704c06356bSdh pmcs_teardown_intr(pmcs_hw_t *pwp) 23714c06356bSdh { 23724c06356bSdh if (pwp->intr_cnt) { 23734c06356bSdh if (pmcs_disable_intrs(pwp, pwp->intr_cnt)) { 23744c06356bSdh return (EIO); 23754c06356bSdh } 23764c06356bSdh if (pmcs_remove_ihandlers(pwp, pwp->intr_cnt)) { 23774c06356bSdh return (EIO); 23784c06356bSdh } 23794c06356bSdh if (pmcs_free_intrs(pwp, pwp->intr_cnt)) { 23804c06356bSdh return (EIO); 23814c06356bSdh } 23824c06356bSdh pwp->intr_cnt = 0; 23834c06356bSdh } 23844c06356bSdh return (0); 23854c06356bSdh } 23864c06356bSdh 23874c06356bSdh static uint_t 23884c06356bSdh pmcs_general_ix(caddr_t arg1, caddr_t arg2) 23894c06356bSdh { 23904c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 23914c06356bSdh _NOTE(ARGUNUSED(arg2)); 23924c06356bSdh pmcs_general_intr(pwp); 23934c06356bSdh return (DDI_INTR_CLAIMED); 23944c06356bSdh } 23954c06356bSdh 23964c06356bSdh static uint_t 23974c06356bSdh pmcs_event_ix(caddr_t arg1, caddr_t arg2) 23984c06356bSdh { 23994c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 24004c06356bSdh _NOTE(ARGUNUSED(arg2)); 24014c06356bSdh pmcs_event_intr(pwp); 24024c06356bSdh return (DDI_INTR_CLAIMED); 24034c06356bSdh } 24044c06356bSdh 24054c06356bSdh static uint_t 24064c06356bSdh pmcs_iodone_ix(caddr_t arg1, caddr_t arg2) 24074c06356bSdh { 24084c06356bSdh _NOTE(ARGUNUSED(arg2)); 24094c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 24104c06356bSdh 24114c06356bSdh /* 24124c06356bSdh * It's possible that if we just turned interrupt coalescing off 24134c06356bSdh * (and thus, re-enabled auto clear for interrupts on the I/O outbound 24144c06356bSdh * queue) that there was an interrupt already pending. We use 24154c06356bSdh * io_intr_coal.int_cleared to ensure that we still drop in here and 24164c06356bSdh * clear the appropriate interrupt bit one last time. 24174c06356bSdh */ 24184c06356bSdh mutex_enter(&pwp->ict_lock); 24194c06356bSdh if (pwp->io_intr_coal.timer_on || 24204c06356bSdh (pwp->io_intr_coal.int_cleared == B_FALSE)) { 24214c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 24224c06356bSdh (1 << PMCS_OQ_IODONE)); 24234c06356bSdh pwp->io_intr_coal.int_cleared = B_TRUE; 24244c06356bSdh } 24254c06356bSdh mutex_exit(&pwp->ict_lock); 24264c06356bSdh 24274c06356bSdh pmcs_iodone_intr(pwp); 24284c06356bSdh 24294c06356bSdh return (DDI_INTR_CLAIMED); 24304c06356bSdh } 24314c06356bSdh 24324c06356bSdh static uint_t 24334c06356bSdh pmcs_fatal_ix(caddr_t arg1, caddr_t arg2) 24344c06356bSdh { 24354c06356bSdh pmcs_hw_t *pwp = (pmcs_hw_t *)((void *)arg1); 24364c06356bSdh _NOTE(ARGUNUSED(arg2)); 24374c06356bSdh pmcs_fatal_handler(pwp); 24384c06356bSdh return (DDI_INTR_CLAIMED); 24394c06356bSdh } 24404c06356bSdh 24414c06356bSdh static uint_t 24424c06356bSdh pmcs_nonio_ix(caddr_t arg1, caddr_t arg2) 24434c06356bSdh { 24444c06356bSdh _NOTE(ARGUNUSED(arg2)); 24454c06356bSdh pmcs_hw_t *pwp = (void *)arg1; 24464c06356bSdh uint32_t obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 24474c06356bSdh 24484c06356bSdh /* 24494c06356bSdh * Check for Fatal Interrupts 24504c06356bSdh */ 24514c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 24524c06356bSdh pmcs_fatal_handler(pwp); 24534c06356bSdh return (DDI_INTR_CLAIMED); 24544c06356bSdh } 24554c06356bSdh 24564c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 24574c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 24584c06356bSdh (1 << PMCS_OQ_GENERAL)); 24594c06356bSdh pmcs_general_intr(pwp); 24604c06356bSdh pmcs_event_intr(pwp); 24614c06356bSdh } 24624c06356bSdh 24634c06356bSdh return (DDI_INTR_CLAIMED); 24644c06356bSdh } 24654c06356bSdh 24664c06356bSdh static uint_t 24674c06356bSdh pmcs_all_intr(caddr_t arg1, caddr_t arg2) 24684c06356bSdh { 24694c06356bSdh _NOTE(ARGUNUSED(arg2)); 24704c06356bSdh pmcs_hw_t *pwp = (void *) arg1; 24714c06356bSdh uint32_t obdb; 24724c06356bSdh int handled = 0; 24734c06356bSdh 24744c06356bSdh obdb = pmcs_rd_msgunit(pwp, PMCS_MSGU_OBDB); 24754c06356bSdh 24764c06356bSdh /* 24774c06356bSdh * Check for Fatal Interrupts 24784c06356bSdh */ 24794c06356bSdh if (obdb & (1 << PMCS_FATAL_INTERRUPT)) { 24804c06356bSdh pmcs_fatal_handler(pwp); 24814c06356bSdh return (DDI_INTR_CLAIMED); 24824c06356bSdh } 24834c06356bSdh 24844c06356bSdh /* 24854c06356bSdh * Check for Outbound Queue service needed 24864c06356bSdh */ 24874c06356bSdh if (obdb & (1 << PMCS_OQ_IODONE)) { 24884c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 24894c06356bSdh (1 << PMCS_OQ_IODONE)); 24904c06356bSdh obdb ^= (1 << PMCS_OQ_IODONE); 24914c06356bSdh handled++; 24924c06356bSdh pmcs_iodone_intr(pwp); 24934c06356bSdh } 24944c06356bSdh if (obdb & (1 << PMCS_OQ_GENERAL)) { 24954c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 24964c06356bSdh (1 << PMCS_OQ_GENERAL)); 24974c06356bSdh obdb ^= (1 << PMCS_OQ_GENERAL); 24984c06356bSdh handled++; 24994c06356bSdh pmcs_general_intr(pwp); 25004c06356bSdh } 25014c06356bSdh if (obdb & (1 << PMCS_OQ_EVENTS)) { 25024c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 25034c06356bSdh (1 << PMCS_OQ_EVENTS)); 25044c06356bSdh obdb ^= (1 << PMCS_OQ_EVENTS); 25054c06356bSdh handled++; 25064c06356bSdh pmcs_event_intr(pwp); 25074c06356bSdh } 25084c06356bSdh if (obdb) { 2509*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 25104c06356bSdh "interrupt bits not handled (0x%x)", obdb); 25114c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, obdb); 25124c06356bSdh handled++; 25134c06356bSdh } 25144c06356bSdh if (pwp->int_type == PMCS_INT_MSI) { 25154c06356bSdh handled++; 25164c06356bSdh } 25174c06356bSdh return (handled? DDI_INTR_CLAIMED : DDI_INTR_UNCLAIMED); 25184c06356bSdh } 25194c06356bSdh 25204c06356bSdh void 25214c06356bSdh pmcs_fatal_handler(pmcs_hw_t *pwp) 25224c06356bSdh { 2523*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_ERR, NULL, NULL, "Fatal Interrupt caught"); 25244c06356bSdh mutex_enter(&pwp->lock); 25254c06356bSdh pwp->state = STATE_DEAD; 25264c06356bSdh pmcs_register_dump_int(pwp); 25274c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_MASK, 0xffffffff); 25284c06356bSdh pmcs_wr_msgunit(pwp, PMCS_MSGU_OBDB_CLEAR, 0xffffffff); 25294c06356bSdh mutex_exit(&pwp->lock); 25304c06356bSdh pmcs_fm_ereport(pwp, DDI_FM_DEVICE_NO_RESPONSE); 25314c06356bSdh ddi_fm_service_impact(pwp->dip, DDI_SERVICE_LOST); 25324c06356bSdh 25334c06356bSdh #ifdef DEBUG 25344c06356bSdh cmn_err(CE_PANIC, "PMCS Fatal Firmware Error"); 25354c06356bSdh #endif 25364c06356bSdh } 25374c06356bSdh 25384c06356bSdh /* 25394c06356bSdh * Called with PHY lock and target statlock held and scratch acquired. 25404c06356bSdh */ 25414c06356bSdh boolean_t 25424c06356bSdh pmcs_assign_device(pmcs_hw_t *pwp, pmcs_xscsi_t *tgt) 25434c06356bSdh { 25444c06356bSdh pmcs_phy_t *pptr = tgt->phy; 25454c06356bSdh 25464c06356bSdh switch (pptr->dtype) { 25474c06356bSdh case SAS: 25484c06356bSdh case EXPANDER: 25494c06356bSdh break; 25504c06356bSdh case SATA: 25514c06356bSdh tgt->ca = 1; 25524c06356bSdh break; 25534c06356bSdh default: 2554*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 25554c06356bSdh "%s: Target %p has PHY %p with invalid dtype", 25564c06356bSdh __func__, (void *)tgt, (void *)pptr); 25574c06356bSdh return (B_FALSE); 25584c06356bSdh } 25594c06356bSdh 25604c06356bSdh tgt->new = 1; 25614c06356bSdh tgt->dev_gone = 0; 25624c06356bSdh tgt->recover_wait = 0; 25634c06356bSdh 2564*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 25654c06356bSdh "%s: config %s vtgt %u for " SAS_ADDR_FMT, __func__, 25664c06356bSdh pptr->path, tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 25674c06356bSdh 25684c06356bSdh if (pmcs_add_new_device(pwp, tgt) != B_TRUE) { 2569*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, tgt, 25704c06356bSdh "%s: Failed for vtgt %u / WWN " SAS_ADDR_FMT, __func__, 25714c06356bSdh tgt->target_num, SAS_ADDR_PRT(pptr->sas_address)); 25724c06356bSdh mutex_destroy(&tgt->statlock); 25734c06356bSdh mutex_destroy(&tgt->wqlock); 25744c06356bSdh mutex_destroy(&tgt->aqlock); 25754c06356bSdh return (B_FALSE); 25764c06356bSdh } 25774c06356bSdh 25784c06356bSdh return (B_TRUE); 25794c06356bSdh } 25804c06356bSdh 25814c06356bSdh /* 25824c06356bSdh * Called with softstate lock held 25834c06356bSdh */ 25844c06356bSdh void 25854c06356bSdh pmcs_remove_device(pmcs_hw_t *pwp, pmcs_phy_t *pptr) 25864c06356bSdh { 25874c06356bSdh pmcs_xscsi_t *xp; 25884c06356bSdh unsigned int vtgt; 25894c06356bSdh 25904c06356bSdh ASSERT(mutex_owned(&pwp->lock)); 25914c06356bSdh 25924c06356bSdh for (vtgt = 0; vtgt < pwp->max_dev; vtgt++) { 25934c06356bSdh xp = pwp->targets[vtgt]; 25944c06356bSdh if (xp == NULL) { 25954c06356bSdh continue; 25964c06356bSdh } 25974c06356bSdh 25984c06356bSdh mutex_enter(&xp->statlock); 25994c06356bSdh if (xp->phy == pptr) { 26004c06356bSdh if (xp->new) { 26014c06356bSdh xp->new = 0; 2602*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 26034c06356bSdh "cancel config of vtgt %u", vtgt); 26044c06356bSdh } else { 2605b18a19c2SJesse Butler pmcs_clear_xp(pwp, xp); 2606*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG_CONFIG, pptr, xp, 2607b18a19c2SJesse Butler "Removed tgt 0x%p vtgt %u", 26084c06356bSdh (void *)xp, vtgt); 26094c06356bSdh } 26104c06356bSdh mutex_exit(&xp->statlock); 26114c06356bSdh break; 26124c06356bSdh } 26134c06356bSdh mutex_exit(&xp->statlock); 26144c06356bSdh } 26154c06356bSdh } 26164c06356bSdh 26174c06356bSdh void 2618*c3bc407cSdh pmcs_prt_impl(pmcs_hw_t *pwp, pmcs_prt_level_t level, 2619*c3bc407cSdh pmcs_phy_t *phyp, pmcs_xscsi_t *target, const char *fmt, ...) 26204c06356bSdh { 26214c06356bSdh va_list ap; 26224c06356bSdh int written = 0; 26234c06356bSdh char *ptr; 26244c06356bSdh uint32_t elem_size = PMCS_TBUF_ELEM_SIZE - 1; 26254c06356bSdh boolean_t system_log; 26264c06356bSdh int system_log_level; 26274c06356bSdh 26284c06356bSdh switch (level) { 26294c06356bSdh case PMCS_PRT_DEBUG_DEVEL: 26304c06356bSdh case PMCS_PRT_DEBUG_DEV_STATE: 26314c06356bSdh case PMCS_PRT_DEBUG_PHY_LOCKING: 26324c06356bSdh case PMCS_PRT_DEBUG_SCSI_STATUS: 26334c06356bSdh case PMCS_PRT_DEBUG_UNDERFLOW: 26344c06356bSdh case PMCS_PRT_DEBUG_CONFIG: 26354c06356bSdh case PMCS_PRT_DEBUG_IPORT: 26364c06356bSdh case PMCS_PRT_DEBUG_MAP: 26374c06356bSdh case PMCS_PRT_DEBUG3: 26384c06356bSdh case PMCS_PRT_DEBUG2: 26394c06356bSdh case PMCS_PRT_DEBUG1: 26404c06356bSdh case PMCS_PRT_DEBUG: 26414c06356bSdh system_log = B_FALSE; 26424c06356bSdh break; 26434c06356bSdh case PMCS_PRT_INFO: 26444c06356bSdh system_log = B_TRUE; 26454c06356bSdh system_log_level = CE_CONT; 26464c06356bSdh break; 26474c06356bSdh case PMCS_PRT_WARN: 26484c06356bSdh system_log = B_TRUE; 26494c06356bSdh system_log_level = CE_NOTE; 26504c06356bSdh break; 26514c06356bSdh case PMCS_PRT_ERR: 26524c06356bSdh system_log = B_TRUE; 26534c06356bSdh system_log_level = CE_WARN; 26544c06356bSdh break; 26554c06356bSdh default: 26564c06356bSdh return; 26574c06356bSdh } 26584c06356bSdh 26594c06356bSdh mutex_enter(&pmcs_trace_lock); 26604c06356bSdh gethrestime(&pmcs_tbuf_ptr->timestamp); 26614c06356bSdh ptr = pmcs_tbuf_ptr->buf; 2662*c3bc407cSdh 2663*c3bc407cSdh /* 2664*c3bc407cSdh * Store the pertinent PHY and target information if there is any 2665*c3bc407cSdh */ 2666*c3bc407cSdh if (target == NULL) { 2667*c3bc407cSdh pmcs_tbuf_ptr->target_num = PMCS_INVALID_TARGET_NUM; 2668*c3bc407cSdh pmcs_tbuf_ptr->target_ua[0] = '\0'; 2669*c3bc407cSdh } else { 2670*c3bc407cSdh pmcs_tbuf_ptr->target_num = target->target_num; 2671*c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->target_ua, target->ua, 2672*c3bc407cSdh PMCS_TBUF_UA_MAX_SIZE); 2673*c3bc407cSdh } 2674*c3bc407cSdh 2675*c3bc407cSdh if (phyp == NULL) { 2676*c3bc407cSdh (void) memset(pmcs_tbuf_ptr->phy_sas_address, 0, 8); 2677*c3bc407cSdh pmcs_tbuf_ptr->phy_path[0] = '\0'; 2678*c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = NOTHING; 2679*c3bc407cSdh } else { 2680*c3bc407cSdh (void) memcpy(pmcs_tbuf_ptr->phy_sas_address, 2681*c3bc407cSdh phyp->sas_address, 8); 2682*c3bc407cSdh (void) strncpy(pmcs_tbuf_ptr->phy_path, phyp->path, 32); 2683*c3bc407cSdh pmcs_tbuf_ptr->phy_dtype = phyp->dtype; 2684*c3bc407cSdh } 2685*c3bc407cSdh 26864c06356bSdh written += snprintf(ptr, elem_size, "pmcs%d:%d: ", 26874c06356bSdh ddi_get_instance(pwp->dip), level); 26884c06356bSdh ptr += strlen(ptr); 26894c06356bSdh va_start(ap, fmt); 26904c06356bSdh written += vsnprintf(ptr, elem_size - written, fmt, ap); 26914c06356bSdh va_end(ap); 26924c06356bSdh if (written > elem_size - 1) { 26934c06356bSdh /* Indicate truncation */ 26944c06356bSdh pmcs_tbuf_ptr->buf[elem_size - 1] = '+'; 26954c06356bSdh } 26964c06356bSdh if (++pmcs_tbuf_idx == pmcs_tbuf_num_elems) { 26974c06356bSdh pmcs_tbuf_ptr = pmcs_tbuf; 26984c06356bSdh pmcs_tbuf_wrap = B_TRUE; 26994c06356bSdh pmcs_tbuf_idx = 0; 27004c06356bSdh } else { 27014c06356bSdh ++pmcs_tbuf_ptr; 27024c06356bSdh } 27034c06356bSdh mutex_exit(&pmcs_trace_lock); 27044c06356bSdh 27054c06356bSdh /* 27064c06356bSdh * When pmcs_force_syslog in non-zero, everything goes also 27074c06356bSdh * to syslog, at CE_CONT level. 27084c06356bSdh */ 27094c06356bSdh if (pmcs_force_syslog) { 27104c06356bSdh system_log = B_TRUE; 27114c06356bSdh system_log_level = CE_CONT; 27124c06356bSdh } 27134c06356bSdh 27144c06356bSdh /* 27154c06356bSdh * Anything that comes in with PMCS_PRT_INFO, WARN, or ERR also 27164c06356bSdh * goes to syslog. 27174c06356bSdh */ 27184c06356bSdh if (system_log) { 27194c06356bSdh char local[196]; 27204c06356bSdh 27214c06356bSdh switch (system_log_level) { 27224c06356bSdh case CE_CONT: 27234c06356bSdh (void) snprintf(local, sizeof (local), "%sINFO: ", 27244c06356bSdh pmcs_console ? "" : "?"); 27254c06356bSdh break; 27264c06356bSdh case CE_NOTE: 27274c06356bSdh case CE_WARN: 27284c06356bSdh local[0] = 0; 27294c06356bSdh break; 27304c06356bSdh default: 27314c06356bSdh return; 27324c06356bSdh } 27334c06356bSdh 27344c06356bSdh ptr = local; 27354c06356bSdh ptr += strlen(local); 27364c06356bSdh (void) snprintf(ptr, (sizeof (local)) - 27374c06356bSdh ((size_t)ptr - (size_t)local), "pmcs%d: ", 27384c06356bSdh ddi_get_instance(pwp->dip)); 27394c06356bSdh ptr += strlen(ptr); 27404c06356bSdh va_start(ap, fmt); 27414c06356bSdh (void) vsnprintf(ptr, 27424c06356bSdh (sizeof (local)) - ((size_t)ptr - (size_t)local), fmt, ap); 27434c06356bSdh va_end(ap); 27444c06356bSdh if (level == CE_CONT) { 27454c06356bSdh (void) strlcat(local, "\n", sizeof (local)); 27464c06356bSdh } 27474c06356bSdh cmn_err(system_log_level, local); 27484c06356bSdh } 27494c06356bSdh 27504c06356bSdh } 27514c06356bSdh 27524c06356bSdh /* 27534c06356bSdh * pmcs_acquire_scratch 27544c06356bSdh * 27554c06356bSdh * If "wait" is true, the caller will wait until it can acquire the scratch. 27564c06356bSdh * This implies the caller needs to be in a context where spinning for an 27574c06356bSdh * indeterminate amount of time is acceptable. 27584c06356bSdh */ 27594c06356bSdh int 27604c06356bSdh pmcs_acquire_scratch(pmcs_hw_t *pwp, boolean_t wait) 27614c06356bSdh { 27624c06356bSdh int rval; 27634c06356bSdh 27644c06356bSdh if (!wait) { 27654c06356bSdh return (atomic_swap_8(&pwp->scratch_locked, 1)); 27664c06356bSdh } 27674c06356bSdh 27684c06356bSdh /* 27694c06356bSdh * Caller will wait for scratch. 27704c06356bSdh */ 27714c06356bSdh while ((rval = atomic_swap_8(&pwp->scratch_locked, 1)) != 0) { 27724c06356bSdh drv_usecwait(100); 27734c06356bSdh } 27744c06356bSdh 27754c06356bSdh return (rval); 27764c06356bSdh } 27774c06356bSdh 27784c06356bSdh void 27794c06356bSdh pmcs_release_scratch(pmcs_hw_t *pwp) 27804c06356bSdh { 27814c06356bSdh pwp->scratch_locked = 0; 27824c06356bSdh } 27834c06356bSdh 27844c06356bSdh static void 27854c06356bSdh pmcs_create_phy_stats(pmcs_iport_t *iport) 27864c06356bSdh { 27874c06356bSdh sas_phy_stats_t *ps; 27884c06356bSdh pmcs_hw_t *pwp; 27894c06356bSdh pmcs_phy_t *phyp; 27904c06356bSdh int ndata; 27914c06356bSdh char ks_name[KSTAT_STRLEN]; 27924c06356bSdh 27934c06356bSdh ASSERT(iport != NULL); 27944c06356bSdh pwp = iport->pwp; 27954c06356bSdh ASSERT(pwp != NULL); 27964c06356bSdh 27974c06356bSdh mutex_enter(&iport->lock); 27984c06356bSdh 27994c06356bSdh for (phyp = list_head(&iport->phys); 28004c06356bSdh phyp != NULL; 28014c06356bSdh phyp = list_next(&iport->phys, phyp)) { 28024c06356bSdh 28034c06356bSdh pmcs_lock_phy(phyp); 28044c06356bSdh 28054c06356bSdh if (phyp->phy_stats != NULL) { 28064c06356bSdh pmcs_unlock_phy(phyp); 28074c06356bSdh /* We've already created this kstat instance */ 28084c06356bSdh continue; 28094c06356bSdh } 28104c06356bSdh 28114c06356bSdh ndata = (sizeof (sas_phy_stats_t)/sizeof (kstat_named_t)); 28124c06356bSdh 28134c06356bSdh (void) snprintf(ks_name, sizeof (ks_name), 28144c06356bSdh "%s.%llx.%d.%d", ddi_driver_name(iport->dip), 28154c06356bSdh (longlong_t)pwp->sas_wwns[0], 28164c06356bSdh ddi_get_instance(iport->dip), phyp->phynum); 28174c06356bSdh 28184c06356bSdh phyp->phy_stats = kstat_create("pmcs", 28194c06356bSdh ddi_get_instance(iport->dip), ks_name, KSTAT_SAS_PHY_CLASS, 28204c06356bSdh KSTAT_TYPE_NAMED, ndata, 0); 28214c06356bSdh 28224c06356bSdh if (phyp->phy_stats == NULL) { 28234c06356bSdh pmcs_unlock_phy(phyp); 2824*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, phyp, NULL, 28254c06356bSdh "%s: Failed to create %s kstats", __func__, 28264c06356bSdh ks_name); 28274c06356bSdh continue; 28284c06356bSdh } 28294c06356bSdh 28304c06356bSdh ps = (sas_phy_stats_t *)phyp->phy_stats->ks_data; 28314c06356bSdh 28324c06356bSdh kstat_named_init(&ps->seconds_since_last_reset, 28334c06356bSdh "SecondsSinceLastReset", KSTAT_DATA_ULONGLONG); 28344c06356bSdh kstat_named_init(&ps->tx_frames, 28354c06356bSdh "TxFrames", KSTAT_DATA_ULONGLONG); 28364c06356bSdh kstat_named_init(&ps->rx_frames, 28374c06356bSdh "RxFrames", KSTAT_DATA_ULONGLONG); 28384c06356bSdh kstat_named_init(&ps->tx_words, 28394c06356bSdh "TxWords", KSTAT_DATA_ULONGLONG); 28404c06356bSdh kstat_named_init(&ps->rx_words, 28414c06356bSdh "RxWords", KSTAT_DATA_ULONGLONG); 28424c06356bSdh kstat_named_init(&ps->invalid_dword_count, 28434c06356bSdh "InvalidDwordCount", KSTAT_DATA_ULONGLONG); 28444c06356bSdh kstat_named_init(&ps->running_disparity_error_count, 28454c06356bSdh "RunningDisparityErrorCount", KSTAT_DATA_ULONGLONG); 28464c06356bSdh kstat_named_init(&ps->loss_of_dword_sync_count, 28474c06356bSdh "LossofDwordSyncCount", KSTAT_DATA_ULONGLONG); 28484c06356bSdh kstat_named_init(&ps->phy_reset_problem_count, 28494c06356bSdh "PhyResetProblemCount", KSTAT_DATA_ULONGLONG); 28504c06356bSdh 28514c06356bSdh phyp->phy_stats->ks_private = phyp; 28524c06356bSdh phyp->phy_stats->ks_update = pmcs_update_phy_stats; 28534c06356bSdh kstat_install(phyp->phy_stats); 28544c06356bSdh pmcs_unlock_phy(phyp); 28554c06356bSdh } 28564c06356bSdh 28574c06356bSdh mutex_exit(&iport->lock); 28584c06356bSdh } 28594c06356bSdh 28604c06356bSdh int 28614c06356bSdh pmcs_update_phy_stats(kstat_t *ks, int rw) 28624c06356bSdh { 28634c06356bSdh int val, ret = DDI_FAILURE; 28644c06356bSdh pmcs_phy_t *pptr = (pmcs_phy_t *)ks->ks_private; 28654c06356bSdh pmcs_hw_t *pwp = pptr->pwp; 28664c06356bSdh sas_phy_stats_t *ps = ks->ks_data; 28674c06356bSdh 28684c06356bSdh _NOTE(ARGUNUSED(rw)); 28694c06356bSdh ASSERT((pptr != NULL) && (pwp != NULL)); 28704c06356bSdh 28714c06356bSdh /* 28724c06356bSdh * We just want to lock against other invocations of kstat; 28734c06356bSdh * we don't need to pmcs_lock_phy() for this. 28744c06356bSdh */ 28754c06356bSdh mutex_enter(&pptr->phy_lock); 28764c06356bSdh 28774c06356bSdh /* Get Stats from Chip */ 28784c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_INVALID_DWORD_CNT, pptr->phynum); 28794c06356bSdh if (val == DDI_FAILURE) 28804c06356bSdh goto fail; 28814c06356bSdh ps->invalid_dword_count.value.ull = (unsigned long long)val; 28824c06356bSdh 28834c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_DISPARITY_ERR_CNT, pptr->phynum); 28844c06356bSdh if (val == DDI_FAILURE) 28854c06356bSdh goto fail; 28864c06356bSdh ps->running_disparity_error_count.value.ull = (unsigned long long)val; 28874c06356bSdh 28884c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_LOST_DWORD_SYNC_CNT, pptr->phynum); 28894c06356bSdh if (val == DDI_FAILURE) 28904c06356bSdh goto fail; 28914c06356bSdh ps->loss_of_dword_sync_count.value.ull = (unsigned long long)val; 28924c06356bSdh 28934c06356bSdh val = pmcs_get_diag_report(pwp, PMCS_RESET_FAILED_CNT, pptr->phynum); 28944c06356bSdh if (val == DDI_FAILURE) 28954c06356bSdh goto fail; 28964c06356bSdh ps->phy_reset_problem_count.value.ull = (unsigned long long)val; 28974c06356bSdh 28984c06356bSdh ret = DDI_SUCCESS; 28994c06356bSdh fail: 29004c06356bSdh mutex_exit(&pptr->phy_lock); 29014c06356bSdh return (ret); 29024c06356bSdh } 29034c06356bSdh 29044c06356bSdh static void 29054c06356bSdh pmcs_destroy_phy_stats(pmcs_iport_t *iport) 29064c06356bSdh { 29074c06356bSdh pmcs_phy_t *phyp; 29084c06356bSdh 29094c06356bSdh ASSERT(iport != NULL); 29104c06356bSdh mutex_enter(&iport->lock); 29114c06356bSdh phyp = iport->pptr; 29124c06356bSdh if (phyp == NULL) { 29134c06356bSdh mutex_exit(&iport->lock); 29144c06356bSdh return; 29154c06356bSdh } 29164c06356bSdh 29174c06356bSdh pmcs_lock_phy(phyp); 29184c06356bSdh if (phyp->phy_stats != NULL) { 29194c06356bSdh kstat_delete(phyp->phy_stats); 29204c06356bSdh phyp->phy_stats = NULL; 29214c06356bSdh } 29224c06356bSdh pmcs_unlock_phy(phyp); 29234c06356bSdh 29244c06356bSdh mutex_exit(&iport->lock); 29254c06356bSdh } 29264c06356bSdh 29274c06356bSdh /*ARGSUSED*/ 29284c06356bSdh static int 29294c06356bSdh pmcs_fm_error_cb(dev_info_t *dip, ddi_fm_error_t *err, const void *impl_data) 29304c06356bSdh { 29314c06356bSdh /* 29324c06356bSdh * as the driver can always deal with an error in any dma or 29334c06356bSdh * access handle, we can just return the fme_status value. 29344c06356bSdh */ 29354c06356bSdh pci_ereport_post(dip, err, NULL); 29364c06356bSdh return (err->fme_status); 29374c06356bSdh } 29384c06356bSdh 29394c06356bSdh static void 29404c06356bSdh pmcs_fm_init(pmcs_hw_t *pwp) 29414c06356bSdh { 29424c06356bSdh ddi_iblock_cookie_t fm_ibc; 29434c06356bSdh 29444c06356bSdh /* Only register with IO Fault Services if we have some capability */ 29454c06356bSdh if (pwp->fm_capabilities) { 29464c06356bSdh /* Adjust access and dma attributes for FMA */ 29474c06356bSdh pwp->reg_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC; 29484c06356bSdh pwp->dev_acc_attr.devacc_attr_access |= DDI_FLAGERR_ACC; 29494c06356bSdh pwp->iqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 29504c06356bSdh pwp->oqp_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 29514c06356bSdh pwp->cip_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 29524c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags |= DDI_DMA_FLAGERR; 29534c06356bSdh 29544c06356bSdh /* 29554c06356bSdh * Register capabilities with IO Fault Services. 29564c06356bSdh */ 29574c06356bSdh ddi_fm_init(pwp->dip, &pwp->fm_capabilities, &fm_ibc); 29584c06356bSdh 29594c06356bSdh /* 29604c06356bSdh * Initialize pci ereport capabilities if ereport 29614c06356bSdh * capable (should always be.) 29624c06356bSdh */ 29634c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 29644c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 29654c06356bSdh pci_ereport_setup(pwp->dip); 29664c06356bSdh } 29674c06356bSdh 29684c06356bSdh /* 29694c06356bSdh * Register error callback if error callback capable. 29704c06356bSdh */ 29714c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 29724c06356bSdh ddi_fm_handler_register(pwp->dip, 29734c06356bSdh pmcs_fm_error_cb, (void *) pwp); 29744c06356bSdh } 29754c06356bSdh } 29764c06356bSdh } 29774c06356bSdh 29784c06356bSdh static void 29794c06356bSdh pmcs_fm_fini(pmcs_hw_t *pwp) 29804c06356bSdh { 29814c06356bSdh /* Only unregister FMA capabilities if registered */ 29824c06356bSdh if (pwp->fm_capabilities) { 29834c06356bSdh /* 29844c06356bSdh * Un-register error callback if error callback capable. 29854c06356bSdh */ 29864c06356bSdh if (DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 29874c06356bSdh ddi_fm_handler_unregister(pwp->dip); 29884c06356bSdh } 29894c06356bSdh 29904c06356bSdh /* 29914c06356bSdh * Release any resources allocated by pci_ereport_setup() 29924c06356bSdh */ 29934c06356bSdh if (DDI_FM_EREPORT_CAP(pwp->fm_capabilities) || 29944c06356bSdh DDI_FM_ERRCB_CAP(pwp->fm_capabilities)) { 29954c06356bSdh pci_ereport_teardown(pwp->dip); 29964c06356bSdh } 29974c06356bSdh 29984c06356bSdh /* Unregister from IO Fault Services */ 29994c06356bSdh ddi_fm_fini(pwp->dip); 30004c06356bSdh 30014c06356bSdh /* Adjust access and dma attributes for FMA */ 30024c06356bSdh pwp->reg_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC; 30034c06356bSdh pwp->dev_acc_attr.devacc_attr_access &= ~DDI_FLAGERR_ACC; 30044c06356bSdh pwp->iqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 30054c06356bSdh pwp->oqp_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 30064c06356bSdh pwp->cip_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 30074c06356bSdh pwp->fwlog_dma_attr.dma_attr_flags &= ~DDI_DMA_FLAGERR; 30084c06356bSdh } 30094c06356bSdh } 30104c06356bSdh 30114c06356bSdh static boolean_t 30124c06356bSdh pmcs_fabricate_wwid(pmcs_hw_t *pwp) 30134c06356bSdh { 30144c06356bSdh char *cp, c; 30154c06356bSdh uint64_t adr; 30164c06356bSdh int i; 30174c06356bSdh 30184c06356bSdh cp = &c; 30194c06356bSdh (void) ddi_strtoul(hw_serial, &cp, 10, (unsigned long *)&adr); 30204c06356bSdh if (adr == 0) { 30214c06356bSdh static const char foo[] = __DATE__ __TIME__; 30224c06356bSdh /* Oh, dear, we're toast */ 3023*c3bc407cSdh pmcs_prt(pwp, PMCS_PRT_DEBUG, NULL, NULL, 30244c06356bSdh "%s: No serial number available to fabricate WWN", 30254c06356bSdh __func__); 30264c06356bSdh for (i = 0; foo[i]; i++) { 30274c06356bSdh adr += foo[i]; 30284c06356bSdh } 30294c06356bSdh } 30304c06356bSdh adr <<= 8; 30314c06356bSdh adr |= ((uint64_t)ddi_get_instance(pwp->dip) << 52); 30324c06356bSdh adr |= (5ULL << 60); 30334c06356bSdh for (i = 0; i < PMCS_MAX_PORTS; i++) { 30344c06356bSdh pwp->sas_wwns[i] = adr + i; 30354c06356bSdh } 30364c06356bSdh 30374c06356bSdh return (B_TRUE); 30384c06356bSdh } 3039