12908d778SJames Bottomley /* 22908d778SJames Bottomley * Aic94xx SAS/SATA driver SCB management. 32908d778SJames Bottomley * 42908d778SJames Bottomley * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 52908d778SJames Bottomley * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 62908d778SJames Bottomley * 72908d778SJames Bottomley * This file is licensed under GPLv2. 82908d778SJames Bottomley * 92908d778SJames Bottomley * This file is part of the aic94xx driver. 102908d778SJames Bottomley * 112908d778SJames Bottomley * The aic94xx driver is free software; you can redistribute it and/or 122908d778SJames Bottomley * modify it under the terms of the GNU General Public License as 132908d778SJames Bottomley * published by the Free Software Foundation; version 2 of the 142908d778SJames Bottomley * License. 152908d778SJames Bottomley * 162908d778SJames Bottomley * The aic94xx driver is distributed in the hope that it will be useful, 172908d778SJames Bottomley * but WITHOUT ANY WARRANTY; without even the implied warranty of 182908d778SJames Bottomley * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 192908d778SJames Bottomley * General Public License for more details. 202908d778SJames Bottomley * 212908d778SJames Bottomley * You should have received a copy of the GNU General Public License 222908d778SJames Bottomley * along with the aic94xx driver; if not, write to the Free Software 232908d778SJames Bottomley * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 242908d778SJames Bottomley * 252908d778SJames Bottomley */ 262908d778SJames Bottomley 272908d778SJames Bottomley #include <linux/pci.h> 28fe4a36cfSDarrick J. Wong #include <scsi/scsi_host.h> 292908d778SJames Bottomley 302908d778SJames Bottomley #include "aic94xx.h" 312908d778SJames Bottomley #include "aic94xx_reg.h" 322908d778SJames Bottomley #include "aic94xx_hwi.h" 332908d778SJames Bottomley #include "aic94xx_seq.h" 342908d778SJames Bottomley 352908d778SJames Bottomley #include "aic94xx_dump.h" 362908d778SJames Bottomley 372908d778SJames Bottomley /* ---------- EMPTY SCB ---------- */ 382908d778SJames Bottomley 392908d778SJames Bottomley #define DL_PHY_MASK 7 402908d778SJames Bottomley #define BYTES_DMAED 0 412908d778SJames Bottomley #define PRIMITIVE_RECVD 0x08 422908d778SJames Bottomley #define PHY_EVENT 0x10 432908d778SJames Bottomley #define LINK_RESET_ERROR 0x18 442908d778SJames Bottomley #define TIMER_EVENT 0x20 452908d778SJames Bottomley #define REQ_TASK_ABORT 0xF0 462908d778SJames Bottomley #define REQ_DEVICE_RESET 0xF1 472908d778SJames Bottomley #define SIGNAL_NCQ_ERROR 0xF2 482908d778SJames Bottomley #define CLEAR_NCQ_ERROR 0xF3 492908d778SJames Bottomley 502908d778SJames Bottomley #define PHY_EVENTS_STATUS (CURRENT_LOSS_OF_SIGNAL | CURRENT_OOB_DONE \ 512908d778SJames Bottomley | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \ 522908d778SJames Bottomley | CURRENT_OOB_ERROR) 532908d778SJames Bottomley 542908d778SJames Bottomley static inline void get_lrate_mode(struct asd_phy *phy, u8 oob_mode) 552908d778SJames Bottomley { 56a01e70e5SJames Bottomley struct sas_phy *sas_phy = phy->sas_phy.phy; 57a01e70e5SJames Bottomley 582908d778SJames Bottomley switch (oob_mode & 7) { 592908d778SJames Bottomley case PHY_SPEED_60: 602908d778SJames Bottomley /* FIXME: sas transport class doesn't have this */ 6188edf746SJames Bottomley phy->sas_phy.linkrate = SAS_LINK_RATE_6_0_GBPS; 622908d778SJames Bottomley phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_6_0_GBPS; 632908d778SJames Bottomley break; 642908d778SJames Bottomley case PHY_SPEED_30: 6588edf746SJames Bottomley phy->sas_phy.linkrate = SAS_LINK_RATE_3_0_GBPS; 662908d778SJames Bottomley phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_3_0_GBPS; 672908d778SJames Bottomley break; 682908d778SJames Bottomley case PHY_SPEED_15: 6988edf746SJames Bottomley phy->sas_phy.linkrate = SAS_LINK_RATE_1_5_GBPS; 702908d778SJames Bottomley phy->sas_phy.phy->negotiated_linkrate = SAS_LINK_RATE_1_5_GBPS; 712908d778SJames Bottomley break; 722908d778SJames Bottomley } 73a01e70e5SJames Bottomley sas_phy->negotiated_linkrate = phy->sas_phy.linkrate; 74a01e70e5SJames Bottomley sas_phy->maximum_linkrate_hw = SAS_LINK_RATE_3_0_GBPS; 75a01e70e5SJames Bottomley sas_phy->minimum_linkrate_hw = SAS_LINK_RATE_1_5_GBPS; 76a01e70e5SJames Bottomley sas_phy->maximum_linkrate = phy->phy_desc->max_sas_lrate; 77a01e70e5SJames Bottomley sas_phy->minimum_linkrate = phy->phy_desc->min_sas_lrate; 78a01e70e5SJames Bottomley 792908d778SJames Bottomley if (oob_mode & SAS_MODE) 802908d778SJames Bottomley phy->sas_phy.oob_mode = SAS_OOB_MODE; 812908d778SJames Bottomley else if (oob_mode & SATA_MODE) 822908d778SJames Bottomley phy->sas_phy.oob_mode = SATA_OOB_MODE; 832908d778SJames Bottomley } 842908d778SJames Bottomley 852908d778SJames Bottomley static inline void asd_phy_event_tasklet(struct asd_ascb *ascb, 862908d778SJames Bottomley struct done_list_struct *dl) 872908d778SJames Bottomley { 882908d778SJames Bottomley struct asd_ha_struct *asd_ha = ascb->ha; 892908d778SJames Bottomley struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; 902908d778SJames Bottomley int phy_id = dl->status_block[0] & DL_PHY_MASK; 912908d778SJames Bottomley struct asd_phy *phy = &asd_ha->phys[phy_id]; 922908d778SJames Bottomley 932908d778SJames Bottomley u8 oob_status = dl->status_block[1] & PHY_EVENTS_STATUS; 942908d778SJames Bottomley u8 oob_mode = dl->status_block[2]; 952908d778SJames Bottomley 962908d778SJames Bottomley switch (oob_status) { 972908d778SJames Bottomley case CURRENT_LOSS_OF_SIGNAL: 982908d778SJames Bottomley /* directly attached device was removed */ 992908d778SJames Bottomley ASD_DPRINTK("phy%d: device unplugged\n", phy_id); 1002908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 1012908d778SJames Bottomley sas_phy_disconnected(&phy->sas_phy); 1022908d778SJames Bottomley sas_ha->notify_phy_event(&phy->sas_phy, PHYE_LOSS_OF_SIGNAL); 1032908d778SJames Bottomley break; 1042908d778SJames Bottomley case CURRENT_OOB_DONE: 1052908d778SJames Bottomley /* hot plugged device */ 1062908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 1); 1072908d778SJames Bottomley get_lrate_mode(phy, oob_mode); 1082908d778SJames Bottomley ASD_DPRINTK("phy%d device plugged: lrate:0x%x, proto:0x%x\n", 1092908d778SJames Bottomley phy_id, phy->sas_phy.linkrate, phy->sas_phy.iproto); 1102908d778SJames Bottomley sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_DONE); 1112908d778SJames Bottomley break; 1122908d778SJames Bottomley case CURRENT_SPINUP_HOLD: 1132908d778SJames Bottomley /* hot plug SATA, no COMWAKE sent */ 1142908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 1); 1152908d778SJames Bottomley sas_ha->notify_phy_event(&phy->sas_phy, PHYE_SPINUP_HOLD); 1162908d778SJames Bottomley break; 1172908d778SJames Bottomley case CURRENT_GTO_TIMEOUT: 1182908d778SJames Bottomley case CURRENT_OOB_ERROR: 1192908d778SJames Bottomley ASD_DPRINTK("phy%d error while OOB: oob status:0x%x\n", phy_id, 1202908d778SJames Bottomley dl->status_block[1]); 1212908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 1222908d778SJames Bottomley sas_phy_disconnected(&phy->sas_phy); 1232908d778SJames Bottomley sas_ha->notify_phy_event(&phy->sas_phy, PHYE_OOB_ERROR); 1242908d778SJames Bottomley break; 1252908d778SJames Bottomley } 1262908d778SJames Bottomley } 1272908d778SJames Bottomley 1282908d778SJames Bottomley /* If phys are enabled sparsely, this will do the right thing. */ 1292908d778SJames Bottomley static inline unsigned ord_phy(struct asd_ha_struct *asd_ha, 1302908d778SJames Bottomley struct asd_phy *phy) 1312908d778SJames Bottomley { 1322908d778SJames Bottomley u8 enabled_mask = asd_ha->hw_prof.enabled_phys; 1332908d778SJames Bottomley int i, k = 0; 1342908d778SJames Bottomley 1352908d778SJames Bottomley for_each_phy(enabled_mask, enabled_mask, i) { 1362908d778SJames Bottomley if (&asd_ha->phys[i] == phy) 1372908d778SJames Bottomley return k; 1382908d778SJames Bottomley k++; 1392908d778SJames Bottomley } 1402908d778SJames Bottomley return 0; 1412908d778SJames Bottomley } 1422908d778SJames Bottomley 1432908d778SJames Bottomley /** 1442908d778SJames Bottomley * asd_get_attached_sas_addr -- extract/generate attached SAS address 1452908d778SJames Bottomley * phy: pointer to asd_phy 1462908d778SJames Bottomley * sas_addr: pointer to buffer where the SAS address is to be written 1472908d778SJames Bottomley * 1482908d778SJames Bottomley * This function extracts the SAS address from an IDENTIFY frame 1492908d778SJames Bottomley * received. If OOB is SATA, then a SAS address is generated from the 1502908d778SJames Bottomley * HA tables. 1512908d778SJames Bottomley * 1522908d778SJames Bottomley * LOCKING: the frame_rcvd_lock needs to be held since this parses the frame 1532908d778SJames Bottomley * buffer. 1542908d778SJames Bottomley */ 1552908d778SJames Bottomley static inline void asd_get_attached_sas_addr(struct asd_phy *phy, u8 *sas_addr) 1562908d778SJames Bottomley { 1572908d778SJames Bottomley if (phy->sas_phy.frame_rcvd[0] == 0x34 1582908d778SJames Bottomley && phy->sas_phy.oob_mode == SATA_OOB_MODE) { 1592908d778SJames Bottomley struct asd_ha_struct *asd_ha = phy->sas_phy.ha->lldd_ha; 1602908d778SJames Bottomley /* FIS device-to-host */ 1612908d778SJames Bottomley u64 addr = be64_to_cpu(*(__be64 *)phy->phy_desc->sas_addr); 1622908d778SJames Bottomley 1632908d778SJames Bottomley addr += asd_ha->hw_prof.sata_name_base + ord_phy(asd_ha, phy); 1642908d778SJames Bottomley *(__be64 *)sas_addr = cpu_to_be64(addr); 1652908d778SJames Bottomley } else { 1662908d778SJames Bottomley struct sas_identify_frame *idframe = 1672908d778SJames Bottomley (void *) phy->sas_phy.frame_rcvd; 1682908d778SJames Bottomley memcpy(sas_addr, idframe->sas_addr, SAS_ADDR_SIZE); 1692908d778SJames Bottomley } 1702908d778SJames Bottomley } 1712908d778SJames Bottomley 1723f048109Smalahal@us.ibm.com static void asd_form_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy) 1733f048109Smalahal@us.ibm.com { 1743f048109Smalahal@us.ibm.com int i; 1753f048109Smalahal@us.ibm.com struct asd_port *free_port = NULL; 1763f048109Smalahal@us.ibm.com struct asd_port *port; 1773f048109Smalahal@us.ibm.com struct asd_sas_phy *sas_phy = &phy->sas_phy; 1783f048109Smalahal@us.ibm.com unsigned long flags; 1793f048109Smalahal@us.ibm.com 1803f048109Smalahal@us.ibm.com spin_lock_irqsave(&asd_ha->asd_ports_lock, flags); 1813f048109Smalahal@us.ibm.com if (!phy->asd_port) { 1823f048109Smalahal@us.ibm.com for (i = 0; i < ASD_MAX_PHYS; i++) { 1833f048109Smalahal@us.ibm.com port = &asd_ha->asd_ports[i]; 1843f048109Smalahal@us.ibm.com 1853f048109Smalahal@us.ibm.com /* Check for wide port */ 1863f048109Smalahal@us.ibm.com if (port->num_phys > 0 && 1873f048109Smalahal@us.ibm.com memcmp(port->sas_addr, sas_phy->sas_addr, 1883f048109Smalahal@us.ibm.com SAS_ADDR_SIZE) == 0 && 1893f048109Smalahal@us.ibm.com memcmp(port->attached_sas_addr, 1903f048109Smalahal@us.ibm.com sas_phy->attached_sas_addr, 1913f048109Smalahal@us.ibm.com SAS_ADDR_SIZE) == 0) { 1923f048109Smalahal@us.ibm.com break; 1933f048109Smalahal@us.ibm.com } 1943f048109Smalahal@us.ibm.com 1953f048109Smalahal@us.ibm.com /* Find a free port */ 1963f048109Smalahal@us.ibm.com if (port->num_phys == 0 && free_port == NULL) { 1973f048109Smalahal@us.ibm.com free_port = port; 1983f048109Smalahal@us.ibm.com } 1993f048109Smalahal@us.ibm.com } 2003f048109Smalahal@us.ibm.com 2013f048109Smalahal@us.ibm.com /* Use a free port if this doesn't form a wide port */ 2023f048109Smalahal@us.ibm.com if (i >= ASD_MAX_PHYS) { 2033f048109Smalahal@us.ibm.com port = free_port; 2043f048109Smalahal@us.ibm.com BUG_ON(!port); 2053f048109Smalahal@us.ibm.com memcpy(port->sas_addr, sas_phy->sas_addr, 2063f048109Smalahal@us.ibm.com SAS_ADDR_SIZE); 2073f048109Smalahal@us.ibm.com memcpy(port->attached_sas_addr, 2083f048109Smalahal@us.ibm.com sas_phy->attached_sas_addr, 2093f048109Smalahal@us.ibm.com SAS_ADDR_SIZE); 2103f048109Smalahal@us.ibm.com } 2113f048109Smalahal@us.ibm.com port->num_phys++; 2123f048109Smalahal@us.ibm.com port->phy_mask |= (1U << sas_phy->id); 2133f048109Smalahal@us.ibm.com phy->asd_port = port; 2143f048109Smalahal@us.ibm.com } 2153f048109Smalahal@us.ibm.com ASD_DPRINTK("%s: updating phy_mask 0x%x for phy%d\n", 2163f048109Smalahal@us.ibm.com __FUNCTION__, phy->asd_port->phy_mask, sas_phy->id); 2173f048109Smalahal@us.ibm.com asd_update_port_links(asd_ha, phy); 2183f048109Smalahal@us.ibm.com spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags); 2193f048109Smalahal@us.ibm.com } 2203f048109Smalahal@us.ibm.com 2213f048109Smalahal@us.ibm.com static void asd_deform_port(struct asd_ha_struct *asd_ha, struct asd_phy *phy) 2223f048109Smalahal@us.ibm.com { 2233f048109Smalahal@us.ibm.com struct asd_port *port = phy->asd_port; 2243f048109Smalahal@us.ibm.com struct asd_sas_phy *sas_phy = &phy->sas_phy; 2253f048109Smalahal@us.ibm.com unsigned long flags; 2263f048109Smalahal@us.ibm.com 2273f048109Smalahal@us.ibm.com spin_lock_irqsave(&asd_ha->asd_ports_lock, flags); 2283f048109Smalahal@us.ibm.com if (port) { 2293f048109Smalahal@us.ibm.com port->num_phys--; 2303f048109Smalahal@us.ibm.com port->phy_mask &= ~(1U << sas_phy->id); 2313f048109Smalahal@us.ibm.com phy->asd_port = NULL; 2323f048109Smalahal@us.ibm.com } 2333f048109Smalahal@us.ibm.com spin_unlock_irqrestore(&asd_ha->asd_ports_lock, flags); 2343f048109Smalahal@us.ibm.com } 2353f048109Smalahal@us.ibm.com 2362908d778SJames Bottomley static inline void asd_bytes_dmaed_tasklet(struct asd_ascb *ascb, 2372908d778SJames Bottomley struct done_list_struct *dl, 2382908d778SJames Bottomley int edb_id, int phy_id) 2392908d778SJames Bottomley { 2402908d778SJames Bottomley unsigned long flags; 2412908d778SJames Bottomley int edb_el = edb_id + ascb->edb_index; 2422908d778SJames Bottomley struct asd_dma_tok *edb = ascb->ha->seq.edb_arr[edb_el]; 2432908d778SJames Bottomley struct asd_phy *phy = &ascb->ha->phys[phy_id]; 2442908d778SJames Bottomley struct sas_ha_struct *sas_ha = phy->sas_phy.ha; 2452908d778SJames Bottomley u16 size = ((dl->status_block[3] & 7) << 8) | dl->status_block[2]; 2462908d778SJames Bottomley 2472908d778SJames Bottomley size = min(size, (u16) sizeof(phy->frame_rcvd)); 2482908d778SJames Bottomley 2492908d778SJames Bottomley spin_lock_irqsave(&phy->sas_phy.frame_rcvd_lock, flags); 2502908d778SJames Bottomley memcpy(phy->sas_phy.frame_rcvd, edb->vaddr, size); 2512908d778SJames Bottomley phy->sas_phy.frame_rcvd_size = size; 2522908d778SJames Bottomley asd_get_attached_sas_addr(phy, phy->sas_phy.attached_sas_addr); 2532908d778SJames Bottomley spin_unlock_irqrestore(&phy->sas_phy.frame_rcvd_lock, flags); 2542908d778SJames Bottomley asd_dump_frame_rcvd(phy, dl); 2553f048109Smalahal@us.ibm.com asd_form_port(ascb->ha, phy); 2562908d778SJames Bottomley sas_ha->notify_port_event(&phy->sas_phy, PORTE_BYTES_DMAED); 2572908d778SJames Bottomley } 2582908d778SJames Bottomley 2592908d778SJames Bottomley static inline void asd_link_reset_err_tasklet(struct asd_ascb *ascb, 2602908d778SJames Bottomley struct done_list_struct *dl, 2612908d778SJames Bottomley int phy_id) 2622908d778SJames Bottomley { 2632908d778SJames Bottomley struct asd_ha_struct *asd_ha = ascb->ha; 2642908d778SJames Bottomley struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; 2652908d778SJames Bottomley struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; 2663f048109Smalahal@us.ibm.com struct asd_phy *phy = &asd_ha->phys[phy_id]; 2672908d778SJames Bottomley u8 lr_error = dl->status_block[1]; 2682908d778SJames Bottomley u8 retries_left = dl->status_block[2]; 2692908d778SJames Bottomley 2702908d778SJames Bottomley switch (lr_error) { 2712908d778SJames Bottomley case 0: 2722908d778SJames Bottomley ASD_DPRINTK("phy%d: Receive ID timer expired\n", phy_id); 2732908d778SJames Bottomley break; 2742908d778SJames Bottomley case 1: 2752908d778SJames Bottomley ASD_DPRINTK("phy%d: Loss of signal\n", phy_id); 2762908d778SJames Bottomley break; 2772908d778SJames Bottomley case 2: 2782908d778SJames Bottomley ASD_DPRINTK("phy%d: Loss of dword sync\n", phy_id); 2792908d778SJames Bottomley break; 2802908d778SJames Bottomley case 3: 2812908d778SJames Bottomley ASD_DPRINTK("phy%d: Receive FIS timeout\n", phy_id); 2822908d778SJames Bottomley break; 2832908d778SJames Bottomley default: 2842908d778SJames Bottomley ASD_DPRINTK("phy%d: unknown link reset error code: 0x%x\n", 2852908d778SJames Bottomley phy_id, lr_error); 2862908d778SJames Bottomley break; 2872908d778SJames Bottomley } 2882908d778SJames Bottomley 2892908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 2902908d778SJames Bottomley sas_phy_disconnected(sas_phy); 2913f048109Smalahal@us.ibm.com asd_deform_port(asd_ha, phy); 2922908d778SJames Bottomley sas_ha->notify_port_event(sas_phy, PORTE_LINK_RESET_ERR); 2932908d778SJames Bottomley 2942908d778SJames Bottomley if (retries_left == 0) { 2952908d778SJames Bottomley int num = 1; 2962908d778SJames Bottomley struct asd_ascb *cp = asd_ascb_alloc_list(ascb->ha, &num, 2972908d778SJames Bottomley GFP_ATOMIC); 2982908d778SJames Bottomley if (!cp) { 2992908d778SJames Bottomley asd_printk("%s: out of memory\n", __FUNCTION__); 3002908d778SJames Bottomley goto out; 3012908d778SJames Bottomley } 3022908d778SJames Bottomley ASD_DPRINTK("phy%d: retries:0 performing link reset seq\n", 3032908d778SJames Bottomley phy_id); 3042908d778SJames Bottomley asd_build_control_phy(cp, phy_id, ENABLE_PHY); 3052908d778SJames Bottomley if (asd_post_ascb_list(ascb->ha, cp, 1) != 0) 3062908d778SJames Bottomley asd_ascb_free(cp); 3072908d778SJames Bottomley } 3082908d778SJames Bottomley out: 3092908d778SJames Bottomley ; 3102908d778SJames Bottomley } 3112908d778SJames Bottomley 3122908d778SJames Bottomley static inline void asd_primitive_rcvd_tasklet(struct asd_ascb *ascb, 3132908d778SJames Bottomley struct done_list_struct *dl, 3142908d778SJames Bottomley int phy_id) 3152908d778SJames Bottomley { 3162908d778SJames Bottomley unsigned long flags; 3172908d778SJames Bottomley struct sas_ha_struct *sas_ha = &ascb->ha->sas_ha; 3182908d778SJames Bottomley struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; 3193f048109Smalahal@us.ibm.com struct asd_ha_struct *asd_ha = ascb->ha; 3203f048109Smalahal@us.ibm.com struct asd_phy *phy = &asd_ha->phys[phy_id]; 3212908d778SJames Bottomley u8 reg = dl->status_block[1]; 3222908d778SJames Bottomley u32 cont = dl->status_block[2] << ((reg & 3)*8); 3232908d778SJames Bottomley 3242908d778SJames Bottomley reg &= ~3; 3252908d778SJames Bottomley switch (reg) { 3262908d778SJames Bottomley case LmPRMSTAT0BYTE0: 3272908d778SJames Bottomley switch (cont) { 3282908d778SJames Bottomley case LmBROADCH: 3292908d778SJames Bottomley case LmBROADRVCH0: 3302908d778SJames Bottomley case LmBROADRVCH1: 3312908d778SJames Bottomley case LmBROADSES: 3322908d778SJames Bottomley ASD_DPRINTK("phy%d: BROADCAST change received:%d\n", 3332908d778SJames Bottomley phy_id, cont); 3342908d778SJames Bottomley spin_lock_irqsave(&sas_phy->sas_prim_lock, flags); 3352908d778SJames Bottomley sas_phy->sas_prim = ffs(cont); 3362908d778SJames Bottomley spin_unlock_irqrestore(&sas_phy->sas_prim_lock, flags); 3372908d778SJames Bottomley sas_ha->notify_port_event(sas_phy,PORTE_BROADCAST_RCVD); 3382908d778SJames Bottomley break; 3392908d778SJames Bottomley 3402908d778SJames Bottomley case LmUNKNOWNP: 3412908d778SJames Bottomley ASD_DPRINTK("phy%d: unknown BREAK\n", phy_id); 3422908d778SJames Bottomley break; 3432908d778SJames Bottomley 3442908d778SJames Bottomley default: 3452908d778SJames Bottomley ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n", 3462908d778SJames Bottomley phy_id, reg, cont); 3472908d778SJames Bottomley break; 3482908d778SJames Bottomley } 3492908d778SJames Bottomley break; 3502908d778SJames Bottomley case LmPRMSTAT1BYTE0: 3512908d778SJames Bottomley switch (cont) { 3522908d778SJames Bottomley case LmHARDRST: 3532908d778SJames Bottomley ASD_DPRINTK("phy%d: HARD_RESET primitive rcvd\n", 3542908d778SJames Bottomley phy_id); 3552908d778SJames Bottomley /* The sequencer disables all phys on that port. 3562908d778SJames Bottomley * We have to re-enable the phys ourselves. */ 3573f048109Smalahal@us.ibm.com asd_deform_port(asd_ha, phy); 3582908d778SJames Bottomley sas_ha->notify_port_event(sas_phy, PORTE_HARD_RESET); 3592908d778SJames Bottomley break; 3602908d778SJames Bottomley 3612908d778SJames Bottomley default: 3622908d778SJames Bottomley ASD_DPRINTK("phy%d: primitive reg:0x%x, cont:0x%04x\n", 3632908d778SJames Bottomley phy_id, reg, cont); 3642908d778SJames Bottomley break; 3652908d778SJames Bottomley } 3662908d778SJames Bottomley break; 3672908d778SJames Bottomley default: 3682908d778SJames Bottomley ASD_DPRINTK("unknown primitive register:0x%x\n", 3692908d778SJames Bottomley dl->status_block[1]); 3702908d778SJames Bottomley break; 3712908d778SJames Bottomley } 3722908d778SJames Bottomley } 3732908d778SJames Bottomley 3742908d778SJames Bottomley /** 3752908d778SJames Bottomley * asd_invalidate_edb -- invalidate an EDB and if necessary post the ESCB 3762908d778SJames Bottomley * @ascb: pointer to Empty SCB 3772908d778SJames Bottomley * @edb_id: index [0,6] to the empty data buffer which is to be invalidated 3782908d778SJames Bottomley * 3792908d778SJames Bottomley * After an EDB has been invalidated, if all EDBs in this ESCB have been 3802908d778SJames Bottomley * invalidated, the ESCB is posted back to the sequencer. 3812908d778SJames Bottomley * Context is tasklet/IRQ. 3822908d778SJames Bottomley */ 3832908d778SJames Bottomley void asd_invalidate_edb(struct asd_ascb *ascb, int edb_id) 3842908d778SJames Bottomley { 3852908d778SJames Bottomley struct asd_seq_data *seq = &ascb->ha->seq; 3862908d778SJames Bottomley struct empty_scb *escb = &ascb->scb->escb; 3872908d778SJames Bottomley struct sg_el *eb = &escb->eb[edb_id]; 3882908d778SJames Bottomley struct asd_dma_tok *edb = seq->edb_arr[ascb->edb_index + edb_id]; 3892908d778SJames Bottomley 3902908d778SJames Bottomley memset(edb->vaddr, 0, ASD_EDB_SIZE); 3912908d778SJames Bottomley eb->flags |= ELEMENT_NOT_VALID; 3922908d778SJames Bottomley escb->num_valid--; 3932908d778SJames Bottomley 3942908d778SJames Bottomley if (escb->num_valid == 0) { 3952908d778SJames Bottomley int i; 3962908d778SJames Bottomley /* ASD_DPRINTK("reposting escb: vaddr: 0x%p, " 3972908d778SJames Bottomley "dma_handle: 0x%08llx, next: 0x%08llx, " 3982908d778SJames Bottomley "index:%d, opcode:0x%02x\n", 3992908d778SJames Bottomley ascb->dma_scb.vaddr, 4002908d778SJames Bottomley (u64)ascb->dma_scb.dma_handle, 4012908d778SJames Bottomley le64_to_cpu(ascb->scb->header.next_scb), 4022908d778SJames Bottomley le16_to_cpu(ascb->scb->header.index), 4032908d778SJames Bottomley ascb->scb->header.opcode); 4042908d778SJames Bottomley */ 4052908d778SJames Bottomley escb->num_valid = ASD_EDBS_PER_SCB; 4062908d778SJames Bottomley for (i = 0; i < ASD_EDBS_PER_SCB; i++) 4072908d778SJames Bottomley escb->eb[i].flags = 0; 4082908d778SJames Bottomley if (!list_empty(&ascb->list)) 4092908d778SJames Bottomley list_del_init(&ascb->list); 4102908d778SJames Bottomley i = asd_post_escb_list(ascb->ha, ascb, 1); 4112908d778SJames Bottomley if (i) 4122908d778SJames Bottomley asd_printk("couldn't post escb, err:%d\n", i); 4132908d778SJames Bottomley } 4142908d778SJames Bottomley } 4152908d778SJames Bottomley 4162908d778SJames Bottomley static void escb_tasklet_complete(struct asd_ascb *ascb, 4172908d778SJames Bottomley struct done_list_struct *dl) 4182908d778SJames Bottomley { 4192908d778SJames Bottomley struct asd_ha_struct *asd_ha = ascb->ha; 4202908d778SJames Bottomley struct sas_ha_struct *sas_ha = &asd_ha->sas_ha; 4212908d778SJames Bottomley int edb = (dl->opcode & DL_PHY_MASK) - 1; /* [0xc1,0xc7] -> [0,6] */ 4222908d778SJames Bottomley u8 sb_opcode = dl->status_block[0]; 4232908d778SJames Bottomley int phy_id = sb_opcode & DL_PHY_MASK; 4242908d778SJames Bottomley struct asd_sas_phy *sas_phy = sas_ha->sas_phy[phy_id]; 4253f048109Smalahal@us.ibm.com struct asd_phy *phy = &asd_ha->phys[phy_id]; 4262908d778SJames Bottomley 4272908d778SJames Bottomley if (edb > 6 || edb < 0) { 4282908d778SJames Bottomley ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", 4292908d778SJames Bottomley edb, dl->opcode); 4302908d778SJames Bottomley ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n", 4312908d778SJames Bottomley sb_opcode, phy_id); 4322908d778SJames Bottomley ASD_DPRINTK("escb: vaddr: 0x%p, " 4332908d778SJames Bottomley "dma_handle: 0x%llx, next: 0x%llx, " 4342908d778SJames Bottomley "index:%d, opcode:0x%02x\n", 4352908d778SJames Bottomley ascb->dma_scb.vaddr, 4362908d778SJames Bottomley (unsigned long long)ascb->dma_scb.dma_handle, 4372908d778SJames Bottomley (unsigned long long) 4382908d778SJames Bottomley le64_to_cpu(ascb->scb->header.next_scb), 4392908d778SJames Bottomley le16_to_cpu(ascb->scb->header.index), 4402908d778SJames Bottomley ascb->scb->header.opcode); 4412908d778SJames Bottomley } 4422908d778SJames Bottomley 443fe4a36cfSDarrick J. Wong /* Catch these before we mask off the sb_opcode bits */ 444fe4a36cfSDarrick J. Wong switch (sb_opcode) { 445fe4a36cfSDarrick J. Wong case REQ_TASK_ABORT: { 446fe4a36cfSDarrick J. Wong struct asd_ascb *a, *b; 447fe4a36cfSDarrick J. Wong u16 tc_abort; 448*3cd041fbSDarrick J. Wong struct domain_device *failed_dev = NULL; 449fe4a36cfSDarrick J. Wong 450fe4a36cfSDarrick J. Wong ASD_DPRINTK("%s: REQ_TASK_ABORT, reason=0x%X\n", 451fe4a36cfSDarrick J. Wong __FUNCTION__, dl->status_block[3]); 452fe4a36cfSDarrick J. Wong 453*3cd041fbSDarrick J. Wong /* 454*3cd041fbSDarrick J. Wong * Find the task that caused the abort and abort it first. 455*3cd041fbSDarrick J. Wong * The sequencer won't put anything on the done list until 456*3cd041fbSDarrick J. Wong * that happens. 457*3cd041fbSDarrick J. Wong */ 458*3cd041fbSDarrick J. Wong tc_abort = *((u16*)(&dl->status_block[1])); 459*3cd041fbSDarrick J. Wong tc_abort = le16_to_cpu(tc_abort); 460*3cd041fbSDarrick J. Wong 461*3cd041fbSDarrick J. Wong list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { 462*3cd041fbSDarrick J. Wong struct sas_task *task = ascb->uldd_task; 463*3cd041fbSDarrick J. Wong 464*3cd041fbSDarrick J. Wong if (task && a->tc_index == tc_abort) { 465*3cd041fbSDarrick J. Wong failed_dev = task->dev; 466*3cd041fbSDarrick J. Wong sas_task_abort(task); 467fe4a36cfSDarrick J. Wong break; 468fe4a36cfSDarrick J. Wong } 469*3cd041fbSDarrick J. Wong } 470*3cd041fbSDarrick J. Wong 471*3cd041fbSDarrick J. Wong if (!failed_dev) { 472*3cd041fbSDarrick J. Wong ASD_DPRINTK("%s: Can't find task (tc=%d) to abort!\n", 473*3cd041fbSDarrick J. Wong __FUNCTION__, tc_abort); 474*3cd041fbSDarrick J. Wong goto out; 475*3cd041fbSDarrick J. Wong } 476*3cd041fbSDarrick J. Wong 477*3cd041fbSDarrick J. Wong /* 478*3cd041fbSDarrick J. Wong * Now abort everything else for that device (hba?) so 479*3cd041fbSDarrick J. Wong * that the EH will wake up and do something. 480*3cd041fbSDarrick J. Wong */ 481*3cd041fbSDarrick J. Wong list_for_each_entry_safe(a, b, &asd_ha->seq.pend_q, list) { 482*3cd041fbSDarrick J. Wong struct sas_task *task = ascb->uldd_task; 483*3cd041fbSDarrick J. Wong 484*3cd041fbSDarrick J. Wong if (task && 485*3cd041fbSDarrick J. Wong task->dev == failed_dev && 486*3cd041fbSDarrick J. Wong a->tc_index != tc_abort) 487*3cd041fbSDarrick J. Wong sas_task_abort(task); 488*3cd041fbSDarrick J. Wong } 489*3cd041fbSDarrick J. Wong 490fe4a36cfSDarrick J. Wong goto out; 491fe4a36cfSDarrick J. Wong } 492fe4a36cfSDarrick J. Wong case REQ_DEVICE_RESET: { 493dea22214SDarrick J. Wong struct asd_ascb *a; 494fe4a36cfSDarrick J. Wong u16 conn_handle; 495*3cd041fbSDarrick J. Wong unsigned long flags; 496*3cd041fbSDarrick J. Wong struct sas_task *last_dev_task = NULL; 497fe4a36cfSDarrick J. Wong 498fe4a36cfSDarrick J. Wong conn_handle = *((u16*)(&dl->status_block[1])); 499fe4a36cfSDarrick J. Wong conn_handle = le16_to_cpu(conn_handle); 500fe4a36cfSDarrick J. Wong 501fe4a36cfSDarrick J. Wong ASD_DPRINTK("%s: REQ_DEVICE_RESET, reason=0x%X\n", __FUNCTION__, 502fe4a36cfSDarrick J. Wong dl->status_block[3]); 503fe4a36cfSDarrick J. Wong 504*3cd041fbSDarrick J. Wong /* Find the last pending task for the device... */ 505dea22214SDarrick J. Wong list_for_each_entry(a, &asd_ha->seq.pend_q, list) { 506fe4a36cfSDarrick J. Wong u16 x; 507*3cd041fbSDarrick J. Wong struct domain_device *dev; 508*3cd041fbSDarrick J. Wong struct sas_task *task = a->uldd_task; 509fe4a36cfSDarrick J. Wong 510dea22214SDarrick J. Wong if (!task) 511dea22214SDarrick J. Wong continue; 512dea22214SDarrick J. Wong dev = task->dev; 513dea22214SDarrick J. Wong 514e138a5d2SJames Bottomley x = (unsigned long)dev->lldd_dev; 515*3cd041fbSDarrick J. Wong if (x == conn_handle) 516*3cd041fbSDarrick J. Wong last_dev_task = task; 517dea22214SDarrick J. Wong } 518fe4a36cfSDarrick J. Wong 519*3cd041fbSDarrick J. Wong if (!last_dev_task) { 520*3cd041fbSDarrick J. Wong ASD_DPRINTK("%s: Device reset for idle device %d?\n", 521*3cd041fbSDarrick J. Wong __FUNCTION__, conn_handle); 522dea22214SDarrick J. Wong goto out; 523dea22214SDarrick J. Wong } 524*3cd041fbSDarrick J. Wong 525*3cd041fbSDarrick J. Wong /* ...and set the reset flag */ 526*3cd041fbSDarrick J. Wong spin_lock_irqsave(&last_dev_task->task_state_lock, flags); 527*3cd041fbSDarrick J. Wong last_dev_task->task_state_flags |= SAS_TASK_NEED_DEV_RESET; 528*3cd041fbSDarrick J. Wong spin_unlock_irqrestore(&last_dev_task->task_state_lock, flags); 529*3cd041fbSDarrick J. Wong 530*3cd041fbSDarrick J. Wong /* Kill all pending tasks for the device */ 531*3cd041fbSDarrick J. Wong list_for_each_entry(a, &asd_ha->seq.pend_q, list) { 532*3cd041fbSDarrick J. Wong u16 x; 533*3cd041fbSDarrick J. Wong struct domain_device *dev; 534*3cd041fbSDarrick J. Wong struct sas_task *task = a->uldd_task; 535*3cd041fbSDarrick J. Wong 536*3cd041fbSDarrick J. Wong if (!task) 537*3cd041fbSDarrick J. Wong continue; 538*3cd041fbSDarrick J. Wong dev = task->dev; 539*3cd041fbSDarrick J. Wong 540*3cd041fbSDarrick J. Wong x = (unsigned long)dev->lldd_dev; 541*3cd041fbSDarrick J. Wong if (x == conn_handle) 542*3cd041fbSDarrick J. Wong sas_task_abort(task); 543*3cd041fbSDarrick J. Wong } 544*3cd041fbSDarrick J. Wong 545fe4a36cfSDarrick J. Wong goto out; 546fe4a36cfSDarrick J. Wong } 547fe4a36cfSDarrick J. Wong case SIGNAL_NCQ_ERROR: 548fe4a36cfSDarrick J. Wong ASD_DPRINTK("%s: SIGNAL_NCQ_ERROR\n", __FUNCTION__); 549fe4a36cfSDarrick J. Wong goto out; 550fe4a36cfSDarrick J. Wong case CLEAR_NCQ_ERROR: 551fe4a36cfSDarrick J. Wong ASD_DPRINTK("%s: CLEAR_NCQ_ERROR\n", __FUNCTION__); 552fe4a36cfSDarrick J. Wong goto out; 553fe4a36cfSDarrick J. Wong } 554fe4a36cfSDarrick J. Wong 5552908d778SJames Bottomley sb_opcode &= ~DL_PHY_MASK; 5562908d778SJames Bottomley 5572908d778SJames Bottomley switch (sb_opcode) { 5582908d778SJames Bottomley case BYTES_DMAED: 5592908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: BYTES_DMAED\n", __FUNCTION__, phy_id); 5602908d778SJames Bottomley asd_bytes_dmaed_tasklet(ascb, dl, edb, phy_id); 5612908d778SJames Bottomley break; 5622908d778SJames Bottomley case PRIMITIVE_RECVD: 5632908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: PRIMITIVE_RECVD\n", __FUNCTION__, 5642908d778SJames Bottomley phy_id); 5652908d778SJames Bottomley asd_primitive_rcvd_tasklet(ascb, dl, phy_id); 5662908d778SJames Bottomley break; 5672908d778SJames Bottomley case PHY_EVENT: 5682908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: PHY_EVENT\n", __FUNCTION__, phy_id); 5692908d778SJames Bottomley asd_phy_event_tasklet(ascb, dl); 5702908d778SJames Bottomley break; 5712908d778SJames Bottomley case LINK_RESET_ERROR: 5722908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: LINK_RESET_ERROR\n", __FUNCTION__, 5732908d778SJames Bottomley phy_id); 5742908d778SJames Bottomley asd_link_reset_err_tasklet(ascb, dl, phy_id); 5752908d778SJames Bottomley break; 5762908d778SJames Bottomley case TIMER_EVENT: 5772908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: TIMER_EVENT, lost dw sync\n", 5782908d778SJames Bottomley __FUNCTION__, phy_id); 5792908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 5802908d778SJames Bottomley /* the device is gone */ 5812908d778SJames Bottomley sas_phy_disconnected(sas_phy); 5823f048109Smalahal@us.ibm.com asd_deform_port(asd_ha, phy); 5832908d778SJames Bottomley sas_ha->notify_port_event(sas_phy, PORTE_TIMER_EVENT); 5842908d778SJames Bottomley break; 5852908d778SJames Bottomley default: 5862908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: unknown event:0x%x\n", __FUNCTION__, 5872908d778SJames Bottomley phy_id, sb_opcode); 5882908d778SJames Bottomley ASD_DPRINTK("edb is 0x%x! dl->opcode is 0x%x\n", 5892908d778SJames Bottomley edb, dl->opcode); 5902908d778SJames Bottomley ASD_DPRINTK("sb_opcode : 0x%x, phy_id: 0x%x\n", 5912908d778SJames Bottomley sb_opcode, phy_id); 5922908d778SJames Bottomley ASD_DPRINTK("escb: vaddr: 0x%p, " 5932908d778SJames Bottomley "dma_handle: 0x%llx, next: 0x%llx, " 5942908d778SJames Bottomley "index:%d, opcode:0x%02x\n", 5952908d778SJames Bottomley ascb->dma_scb.vaddr, 5962908d778SJames Bottomley (unsigned long long)ascb->dma_scb.dma_handle, 5972908d778SJames Bottomley (unsigned long long) 5982908d778SJames Bottomley le64_to_cpu(ascb->scb->header.next_scb), 5992908d778SJames Bottomley le16_to_cpu(ascb->scb->header.index), 6002908d778SJames Bottomley ascb->scb->header.opcode); 6012908d778SJames Bottomley 6022908d778SJames Bottomley break; 6032908d778SJames Bottomley } 604fe4a36cfSDarrick J. Wong out: 6052908d778SJames Bottomley asd_invalidate_edb(ascb, edb); 6062908d778SJames Bottomley } 6072908d778SJames Bottomley 6082908d778SJames Bottomley int asd_init_post_escbs(struct asd_ha_struct *asd_ha) 6092908d778SJames Bottomley { 6102908d778SJames Bottomley struct asd_seq_data *seq = &asd_ha->seq; 6112908d778SJames Bottomley int i; 6122908d778SJames Bottomley 6132908d778SJames Bottomley for (i = 0; i < seq->num_escbs; i++) 6142908d778SJames Bottomley seq->escb_arr[i]->tasklet_complete = escb_tasklet_complete; 6152908d778SJames Bottomley 6162908d778SJames Bottomley ASD_DPRINTK("posting %d escbs\n", i); 6172908d778SJames Bottomley return asd_post_escb_list(asd_ha, seq->escb_arr[0], seq->num_escbs); 6182908d778SJames Bottomley } 6192908d778SJames Bottomley 6202908d778SJames Bottomley /* ---------- CONTROL PHY ---------- */ 6212908d778SJames Bottomley 6222908d778SJames Bottomley #define CONTROL_PHY_STATUS (CURRENT_DEVICE_PRESENT | CURRENT_OOB_DONE \ 6232908d778SJames Bottomley | CURRENT_SPINUP_HOLD | CURRENT_GTO_TIMEOUT \ 6242908d778SJames Bottomley | CURRENT_OOB_ERROR) 6252908d778SJames Bottomley 6262908d778SJames Bottomley /** 6272908d778SJames Bottomley * control_phy_tasklet_complete -- tasklet complete for CONTROL PHY ascb 6282908d778SJames Bottomley * @ascb: pointer to an ascb 6292908d778SJames Bottomley * @dl: pointer to the done list entry 6302908d778SJames Bottomley * 6312908d778SJames Bottomley * This function completes a CONTROL PHY scb and frees the ascb. 6322908d778SJames Bottomley * A note on LEDs: 6332908d778SJames Bottomley * - an LED blinks if there is IO though it, 6342908d778SJames Bottomley * - if a device is connected to the LED, it is lit, 6352908d778SJames Bottomley * - if no device is connected to the LED, is is dimmed (off). 6362908d778SJames Bottomley */ 6372908d778SJames Bottomley static void control_phy_tasklet_complete(struct asd_ascb *ascb, 6382908d778SJames Bottomley struct done_list_struct *dl) 6392908d778SJames Bottomley { 6402908d778SJames Bottomley struct asd_ha_struct *asd_ha = ascb->ha; 6412908d778SJames Bottomley struct scb *scb = ascb->scb; 6422908d778SJames Bottomley struct control_phy *control_phy = &scb->control_phy; 6432908d778SJames Bottomley u8 phy_id = control_phy->phy_id; 6442908d778SJames Bottomley struct asd_phy *phy = &ascb->ha->phys[phy_id]; 6452908d778SJames Bottomley 6462908d778SJames Bottomley u8 status = dl->status_block[0]; 6472908d778SJames Bottomley u8 oob_status = dl->status_block[1]; 6482908d778SJames Bottomley u8 oob_mode = dl->status_block[2]; 6492908d778SJames Bottomley /* u8 oob_signals= dl->status_block[3]; */ 6502908d778SJames Bottomley 6512908d778SJames Bottomley if (status != 0) { 6522908d778SJames Bottomley ASD_DPRINTK("%s: phy%d status block opcode:0x%x\n", 6532908d778SJames Bottomley __FUNCTION__, phy_id, status); 6542908d778SJames Bottomley goto out; 6552908d778SJames Bottomley } 6562908d778SJames Bottomley 6572908d778SJames Bottomley switch (control_phy->sub_func) { 6582908d778SJames Bottomley case DISABLE_PHY: 6592908d778SJames Bottomley asd_ha->hw_prof.enabled_phys &= ~(1 << phy_id); 6602908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 6612908d778SJames Bottomley asd_control_led(asd_ha, phy_id, 0); 6622908d778SJames Bottomley ASD_DPRINTK("%s: disable phy%d\n", __FUNCTION__, phy_id); 6632908d778SJames Bottomley break; 6642908d778SJames Bottomley 6652908d778SJames Bottomley case ENABLE_PHY: 6662908d778SJames Bottomley asd_control_led(asd_ha, phy_id, 1); 6672908d778SJames Bottomley if (oob_status & CURRENT_OOB_DONE) { 6682908d778SJames Bottomley asd_ha->hw_prof.enabled_phys |= (1 << phy_id); 6692908d778SJames Bottomley get_lrate_mode(phy, oob_mode); 6702908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 1); 6712908d778SJames Bottomley ASD_DPRINTK("%s: phy%d, lrate:0x%x, proto:0x%x\n", 6722908d778SJames Bottomley __FUNCTION__, phy_id,phy->sas_phy.linkrate, 6732908d778SJames Bottomley phy->sas_phy.iproto); 6742908d778SJames Bottomley } else if (oob_status & CURRENT_SPINUP_HOLD) { 6752908d778SJames Bottomley asd_ha->hw_prof.enabled_phys |= (1 << phy_id); 6762908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 1); 6772908d778SJames Bottomley ASD_DPRINTK("%s: phy%d, spinup hold\n", __FUNCTION__, 6782908d778SJames Bottomley phy_id); 6792908d778SJames Bottomley } else if (oob_status & CURRENT_ERR_MASK) { 6802908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 6812908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: error: oob status:0x%02x\n", 6822908d778SJames Bottomley __FUNCTION__, phy_id, oob_status); 6832908d778SJames Bottomley } else if (oob_status & (CURRENT_HOT_PLUG_CNCT 6842908d778SJames Bottomley | CURRENT_DEVICE_PRESENT)) { 6852908d778SJames Bottomley asd_ha->hw_prof.enabled_phys |= (1 << phy_id); 6862908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 1); 6872908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: hot plug or device present\n", 6882908d778SJames Bottomley __FUNCTION__, phy_id); 6892908d778SJames Bottomley } else { 6902908d778SJames Bottomley asd_ha->hw_prof.enabled_phys |= (1 << phy_id); 6912908d778SJames Bottomley asd_turn_led(asd_ha, phy_id, 0); 6922908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: no device present: " 6932908d778SJames Bottomley "oob_status:0x%x\n", 6942908d778SJames Bottomley __FUNCTION__, phy_id, oob_status); 6952908d778SJames Bottomley } 6962908d778SJames Bottomley break; 6972908d778SJames Bottomley case RELEASE_SPINUP_HOLD: 6982908d778SJames Bottomley case PHY_NO_OP: 6992908d778SJames Bottomley case EXECUTE_HARD_RESET: 7002908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: sub_func:0x%x\n", __FUNCTION__, 7012908d778SJames Bottomley phy_id, control_phy->sub_func); 7022908d778SJames Bottomley /* XXX finish */ 7032908d778SJames Bottomley break; 7042908d778SJames Bottomley default: 7052908d778SJames Bottomley ASD_DPRINTK("%s: phy%d: sub_func:0x%x?\n", __FUNCTION__, 7062908d778SJames Bottomley phy_id, control_phy->sub_func); 7072908d778SJames Bottomley break; 7082908d778SJames Bottomley } 7092908d778SJames Bottomley out: 7102908d778SJames Bottomley asd_ascb_free(ascb); 7112908d778SJames Bottomley } 7122908d778SJames Bottomley 7132908d778SJames Bottomley static inline void set_speed_mask(u8 *speed_mask, struct asd_phy_desc *pd) 7142908d778SJames Bottomley { 7152908d778SJames Bottomley /* disable all speeds, then enable defaults */ 7162908d778SJames Bottomley *speed_mask = SAS_SPEED_60_DIS | SAS_SPEED_30_DIS | SAS_SPEED_15_DIS 7172908d778SJames Bottomley | SATA_SPEED_30_DIS | SATA_SPEED_15_DIS; 7182908d778SJames Bottomley 7192908d778SJames Bottomley switch (pd->max_sas_lrate) { 72088edf746SJames Bottomley case SAS_LINK_RATE_6_0_GBPS: 7212908d778SJames Bottomley *speed_mask &= ~SAS_SPEED_60_DIS; 7222908d778SJames Bottomley default: 72388edf746SJames Bottomley case SAS_LINK_RATE_3_0_GBPS: 7242908d778SJames Bottomley *speed_mask &= ~SAS_SPEED_30_DIS; 72588edf746SJames Bottomley case SAS_LINK_RATE_1_5_GBPS: 7262908d778SJames Bottomley *speed_mask &= ~SAS_SPEED_15_DIS; 7272908d778SJames Bottomley } 7282908d778SJames Bottomley 7292908d778SJames Bottomley switch (pd->min_sas_lrate) { 73088edf746SJames Bottomley case SAS_LINK_RATE_6_0_GBPS: 7312908d778SJames Bottomley *speed_mask |= SAS_SPEED_30_DIS; 73288edf746SJames Bottomley case SAS_LINK_RATE_3_0_GBPS: 7332908d778SJames Bottomley *speed_mask |= SAS_SPEED_15_DIS; 7342908d778SJames Bottomley default: 73588edf746SJames Bottomley case SAS_LINK_RATE_1_5_GBPS: 7362908d778SJames Bottomley /* nothing to do */ 7372908d778SJames Bottomley ; 7382908d778SJames Bottomley } 7392908d778SJames Bottomley 7402908d778SJames Bottomley switch (pd->max_sata_lrate) { 74188edf746SJames Bottomley case SAS_LINK_RATE_3_0_GBPS: 7422908d778SJames Bottomley *speed_mask &= ~SATA_SPEED_30_DIS; 7432908d778SJames Bottomley default: 74488edf746SJames Bottomley case SAS_LINK_RATE_1_5_GBPS: 7452908d778SJames Bottomley *speed_mask &= ~SATA_SPEED_15_DIS; 7462908d778SJames Bottomley } 7472908d778SJames Bottomley 7482908d778SJames Bottomley switch (pd->min_sata_lrate) { 74988edf746SJames Bottomley case SAS_LINK_RATE_3_0_GBPS: 7502908d778SJames Bottomley *speed_mask |= SATA_SPEED_15_DIS; 7512908d778SJames Bottomley default: 75288edf746SJames Bottomley case SAS_LINK_RATE_1_5_GBPS: 7532908d778SJames Bottomley /* nothing to do */ 7542908d778SJames Bottomley ; 7552908d778SJames Bottomley } 7562908d778SJames Bottomley } 7572908d778SJames Bottomley 7582908d778SJames Bottomley /** 7592908d778SJames Bottomley * asd_build_control_phy -- build a CONTROL PHY SCB 7602908d778SJames Bottomley * @ascb: pointer to an ascb 7612908d778SJames Bottomley * @phy_id: phy id to control, integer 7622908d778SJames Bottomley * @subfunc: subfunction, what to actually to do the phy 7632908d778SJames Bottomley * 7642908d778SJames Bottomley * This function builds a CONTROL PHY scb. No allocation of any kind 7652908d778SJames Bottomley * is performed. @ascb is allocated with the list function. 7662908d778SJames Bottomley * The caller can override the ascb->tasklet_complete to point 7672908d778SJames Bottomley * to its own callback function. It must call asd_ascb_free() 7682908d778SJames Bottomley * at its tasklet complete function. 7692908d778SJames Bottomley * See the default implementation. 7702908d778SJames Bottomley */ 7712908d778SJames Bottomley void asd_build_control_phy(struct asd_ascb *ascb, int phy_id, u8 subfunc) 7722908d778SJames Bottomley { 7732908d778SJames Bottomley struct asd_phy *phy = &ascb->ha->phys[phy_id]; 7742908d778SJames Bottomley struct scb *scb = ascb->scb; 7752908d778SJames Bottomley struct control_phy *control_phy = &scb->control_phy; 7762908d778SJames Bottomley 7772908d778SJames Bottomley scb->header.opcode = CONTROL_PHY; 7782908d778SJames Bottomley control_phy->phy_id = (u8) phy_id; 7792908d778SJames Bottomley control_phy->sub_func = subfunc; 7802908d778SJames Bottomley 7812908d778SJames Bottomley switch (subfunc) { 7822908d778SJames Bottomley case EXECUTE_HARD_RESET: /* 0x81 */ 7832908d778SJames Bottomley case ENABLE_PHY: /* 0x01 */ 7842908d778SJames Bottomley /* decide hot plug delay */ 7852908d778SJames Bottomley control_phy->hot_plug_delay = HOTPLUG_DELAY_TIMEOUT; 7862908d778SJames Bottomley 7872908d778SJames Bottomley /* decide speed mask */ 7882908d778SJames Bottomley set_speed_mask(&control_phy->speed_mask, phy->phy_desc); 7892908d778SJames Bottomley 7902908d778SJames Bottomley /* initiator port settings are in the hi nibble */ 7912908d778SJames Bottomley if (phy->sas_phy.role == PHY_ROLE_INITIATOR) 7922908d778SJames Bottomley control_phy->port_type = SAS_PROTO_ALL << 4; 7932908d778SJames Bottomley else if (phy->sas_phy.role == PHY_ROLE_TARGET) 7942908d778SJames Bottomley control_phy->port_type = SAS_PROTO_ALL; 7952908d778SJames Bottomley else 7962908d778SJames Bottomley control_phy->port_type = 7972908d778SJames Bottomley (SAS_PROTO_ALL << 4) | SAS_PROTO_ALL; 7982908d778SJames Bottomley 7992908d778SJames Bottomley /* link reset retries, this should be nominal */ 8002908d778SJames Bottomley control_phy->link_reset_retries = 10; 8012908d778SJames Bottomley 8022908d778SJames Bottomley case RELEASE_SPINUP_HOLD: /* 0x02 */ 8032908d778SJames Bottomley /* decide the func_mask */ 8042908d778SJames Bottomley control_phy->func_mask = FUNCTION_MASK_DEFAULT; 8052908d778SJames Bottomley if (phy->phy_desc->flags & ASD_SATA_SPINUP_HOLD) 8062908d778SJames Bottomley control_phy->func_mask &= ~SPINUP_HOLD_DIS; 8072908d778SJames Bottomley else 8082908d778SJames Bottomley control_phy->func_mask |= SPINUP_HOLD_DIS; 8092908d778SJames Bottomley } 8102908d778SJames Bottomley 8112908d778SJames Bottomley control_phy->conn_handle = cpu_to_le16(0xFFFF); 8122908d778SJames Bottomley 8132908d778SJames Bottomley ascb->tasklet_complete = control_phy_tasklet_complete; 8142908d778SJames Bottomley } 8152908d778SJames Bottomley 8162908d778SJames Bottomley /* ---------- INITIATE LINK ADM TASK ---------- */ 8172908d778SJames Bottomley 8182908d778SJames Bottomley static void link_adm_tasklet_complete(struct asd_ascb *ascb, 8192908d778SJames Bottomley struct done_list_struct *dl) 8202908d778SJames Bottomley { 8212908d778SJames Bottomley u8 opcode = dl->opcode; 8222908d778SJames Bottomley struct initiate_link_adm *link_adm = &ascb->scb->link_adm; 8232908d778SJames Bottomley u8 phy_id = link_adm->phy_id; 8242908d778SJames Bottomley 8252908d778SJames Bottomley if (opcode != TC_NO_ERROR) { 8262908d778SJames Bottomley asd_printk("phy%d: link adm task 0x%x completed with error " 8272908d778SJames Bottomley "0x%x\n", phy_id, link_adm->sub_func, opcode); 8282908d778SJames Bottomley } 8292908d778SJames Bottomley ASD_DPRINTK("phy%d: link adm task 0x%x: 0x%x\n", 8302908d778SJames Bottomley phy_id, link_adm->sub_func, opcode); 8312908d778SJames Bottomley 8322908d778SJames Bottomley asd_ascb_free(ascb); 8332908d778SJames Bottomley } 8342908d778SJames Bottomley 8352908d778SJames Bottomley void asd_build_initiate_link_adm_task(struct asd_ascb *ascb, int phy_id, 8362908d778SJames Bottomley u8 subfunc) 8372908d778SJames Bottomley { 8382908d778SJames Bottomley struct scb *scb = ascb->scb; 8392908d778SJames Bottomley struct initiate_link_adm *link_adm = &scb->link_adm; 8402908d778SJames Bottomley 8412908d778SJames Bottomley scb->header.opcode = INITIATE_LINK_ADM_TASK; 8422908d778SJames Bottomley 8432908d778SJames Bottomley link_adm->phy_id = phy_id; 8442908d778SJames Bottomley link_adm->sub_func = subfunc; 8452908d778SJames Bottomley link_adm->conn_handle = cpu_to_le16(0xFFFF); 8462908d778SJames Bottomley 8472908d778SJames Bottomley ascb->tasklet_complete = link_adm_tasklet_complete; 8482908d778SJames Bottomley } 8492908d778SJames Bottomley 8502908d778SJames Bottomley /* ---------- SCB timer ---------- */ 8512908d778SJames Bottomley 8522908d778SJames Bottomley /** 8532908d778SJames Bottomley * asd_ascb_timedout -- called when a pending SCB's timer has expired 8542908d778SJames Bottomley * @data: unsigned long, a pointer to the ascb in question 8552908d778SJames Bottomley * 8562908d778SJames Bottomley * This is the default timeout function which does the most necessary. 8572908d778SJames Bottomley * Upper layers can implement their own timeout function, say to free 8582908d778SJames Bottomley * resources they have with this SCB, and then call this one at the 8592908d778SJames Bottomley * end of their timeout function. To do this, one should initialize 8602908d778SJames Bottomley * the ascb->timer.{function, data, expires} prior to calling the post 8612908d778SJames Bottomley * funcion. The timer is started by the post function. 8622908d778SJames Bottomley */ 8632908d778SJames Bottomley void asd_ascb_timedout(unsigned long data) 8642908d778SJames Bottomley { 8652908d778SJames Bottomley struct asd_ascb *ascb = (void *) data; 8662908d778SJames Bottomley struct asd_seq_data *seq = &ascb->ha->seq; 8672908d778SJames Bottomley unsigned long flags; 8682908d778SJames Bottomley 8692908d778SJames Bottomley ASD_DPRINTK("scb:0x%x timed out\n", ascb->scb->header.opcode); 8702908d778SJames Bottomley 8712908d778SJames Bottomley spin_lock_irqsave(&seq->pend_q_lock, flags); 8722908d778SJames Bottomley seq->pending--; 8732908d778SJames Bottomley list_del_init(&ascb->list); 8742908d778SJames Bottomley spin_unlock_irqrestore(&seq->pend_q_lock, flags); 8752908d778SJames Bottomley 8762908d778SJames Bottomley asd_ascb_free(ascb); 8772908d778SJames Bottomley } 8782908d778SJames Bottomley 8792908d778SJames Bottomley /* ---------- CONTROL PHY ---------- */ 8802908d778SJames Bottomley 8812908d778SJames Bottomley /* Given the spec value, return a driver value. */ 8822908d778SJames Bottomley static const int phy_func_table[] = { 8832908d778SJames Bottomley [PHY_FUNC_NOP] = PHY_NO_OP, 8842908d778SJames Bottomley [PHY_FUNC_LINK_RESET] = ENABLE_PHY, 8852908d778SJames Bottomley [PHY_FUNC_HARD_RESET] = EXECUTE_HARD_RESET, 8862908d778SJames Bottomley [PHY_FUNC_DISABLE] = DISABLE_PHY, 8872908d778SJames Bottomley [PHY_FUNC_RELEASE_SPINUP_HOLD] = RELEASE_SPINUP_HOLD, 8882908d778SJames Bottomley }; 8892908d778SJames Bottomley 890a01e70e5SJames Bottomley int asd_control_phy(struct asd_sas_phy *phy, enum phy_func func, void *arg) 8912908d778SJames Bottomley { 8922908d778SJames Bottomley struct asd_ha_struct *asd_ha = phy->ha->lldd_ha; 893a01e70e5SJames Bottomley struct asd_phy_desc *pd = asd_ha->phys[phy->id].phy_desc; 8942908d778SJames Bottomley struct asd_ascb *ascb; 895a01e70e5SJames Bottomley struct sas_phy_linkrates *rates; 8962908d778SJames Bottomley int res = 1; 8972908d778SJames Bottomley 898a01e70e5SJames Bottomley switch (func) { 899a01e70e5SJames Bottomley case PHY_FUNC_CLEAR_ERROR_LOG: 9002908d778SJames Bottomley return -ENOSYS; 901a01e70e5SJames Bottomley case PHY_FUNC_SET_LINK_RATE: 902a01e70e5SJames Bottomley rates = arg; 903a01e70e5SJames Bottomley if (rates->minimum_linkrate) { 904a01e70e5SJames Bottomley pd->min_sas_lrate = rates->minimum_linkrate; 905a01e70e5SJames Bottomley pd->min_sata_lrate = rates->minimum_linkrate; 906a01e70e5SJames Bottomley } 907a01e70e5SJames Bottomley if (rates->maximum_linkrate) { 908a01e70e5SJames Bottomley pd->max_sas_lrate = rates->maximum_linkrate; 909a01e70e5SJames Bottomley pd->max_sata_lrate = rates->maximum_linkrate; 910a01e70e5SJames Bottomley } 911a01e70e5SJames Bottomley func = PHY_FUNC_LINK_RESET; 912a01e70e5SJames Bottomley break; 913a01e70e5SJames Bottomley default: 914a01e70e5SJames Bottomley break; 915a01e70e5SJames Bottomley } 9162908d778SJames Bottomley 9172908d778SJames Bottomley ascb = asd_ascb_alloc_list(asd_ha, &res, GFP_KERNEL); 9182908d778SJames Bottomley if (!ascb) 9192908d778SJames Bottomley return -ENOMEM; 9202908d778SJames Bottomley 9212908d778SJames Bottomley asd_build_control_phy(ascb, phy->id, phy_func_table[func]); 9222908d778SJames Bottomley res = asd_post_ascb_list(asd_ha, ascb , 1); 9232908d778SJames Bottomley if (res) 9242908d778SJames Bottomley asd_ascb_free(ascb); 9252908d778SJames Bottomley 9262908d778SJames Bottomley return res; 9272908d778SJames Bottomley } 928