12908d778SJames Bottomley /* 22908d778SJames Bottomley * Serial Attached SCSI (SAS) Phy class 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 program is free software; you can redistribute it and/or 102908d778SJames Bottomley * modify it under the terms of the GNU General Public License as 112908d778SJames Bottomley * published by the Free Software Foundation; either version 2 of the 122908d778SJames Bottomley * License, or (at your option) any later version. 132908d778SJames Bottomley * 142908d778SJames Bottomley * This program is distributed in the hope that it will be useful, but 152908d778SJames Bottomley * WITHOUT ANY WARRANTY; without even the implied warranty of 162908d778SJames Bottomley * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 172908d778SJames Bottomley * General Public License for more details. 182908d778SJames Bottomley * 192908d778SJames Bottomley * You should have received a copy of the GNU General Public License 202908d778SJames Bottomley * along with this program; if not, write to the Free Software 212908d778SJames Bottomley * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 222908d778SJames Bottomley * 232908d778SJames Bottomley */ 242908d778SJames Bottomley 252908d778SJames Bottomley #include "sas_internal.h" 262908d778SJames Bottomley #include <scsi/scsi_host.h> 272908d778SJames Bottomley #include <scsi/scsi_transport.h> 282908d778SJames Bottomley #include <scsi/scsi_transport_sas.h> 292908d778SJames Bottomley #include "../scsi_sas_internal.h" 302908d778SJames Bottomley 312908d778SJames Bottomley /* ---------- Phy events ---------- */ 322908d778SJames Bottomley 33c4028958SDavid Howells static void sas_phye_loss_of_signal(struct work_struct *work) 342908d778SJames Bottomley { 3522b9153fSDan Williams struct asd_sas_event *ev = to_asd_sas_event(work); 36c4028958SDavid Howells struct asd_sas_phy *phy = ev->phy; 372908d778SJames Bottomley 38b15ebe0bSDan Williams clear_bit(PHYE_LOSS_OF_SIGNAL, &phy->phy_events_pending); 392908d778SJames Bottomley phy->error = 0; 4090f1e10dSDan Williams sas_deform_port(phy, 1); 412908d778SJames Bottomley } 422908d778SJames Bottomley 43c4028958SDavid Howells static void sas_phye_oob_done(struct work_struct *work) 442908d778SJames Bottomley { 4522b9153fSDan Williams struct asd_sas_event *ev = to_asd_sas_event(work); 46c4028958SDavid Howells struct asd_sas_phy *phy = ev->phy; 472908d778SJames Bottomley 48b15ebe0bSDan Williams clear_bit(PHYE_OOB_DONE, &phy->phy_events_pending); 492908d778SJames Bottomley phy->error = 0; 502908d778SJames Bottomley } 512908d778SJames Bottomley 52c4028958SDavid Howells static void sas_phye_oob_error(struct work_struct *work) 532908d778SJames Bottomley { 5422b9153fSDan Williams struct asd_sas_event *ev = to_asd_sas_event(work); 55c4028958SDavid Howells struct asd_sas_phy *phy = ev->phy; 562908d778SJames Bottomley struct sas_ha_struct *sas_ha = phy->ha; 572908d778SJames Bottomley struct asd_sas_port *port = phy->port; 582908d778SJames Bottomley struct sas_internal *i = 592908d778SJames Bottomley to_sas_internal(sas_ha->core.shost->transportt); 602908d778SJames Bottomley 61b15ebe0bSDan Williams clear_bit(PHYE_OOB_ERROR, &phy->phy_events_pending); 622908d778SJames Bottomley 6390f1e10dSDan Williams sas_deform_port(phy, 1); 642908d778SJames Bottomley 652908d778SJames Bottomley if (!port && phy->enabled && i->dft->lldd_control_phy) { 662908d778SJames Bottomley phy->error++; 672908d778SJames Bottomley switch (phy->error) { 682908d778SJames Bottomley case 1: 692908d778SJames Bottomley case 2: 70a01e70e5SJames Bottomley i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET, 71a01e70e5SJames Bottomley NULL); 722908d778SJames Bottomley break; 732908d778SJames Bottomley case 3: 742908d778SJames Bottomley default: 752908d778SJames Bottomley phy->error = 0; 762908d778SJames Bottomley phy->enabled = 0; 77a01e70e5SJames Bottomley i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); 782908d778SJames Bottomley break; 792908d778SJames Bottomley } 802908d778SJames Bottomley } 812908d778SJames Bottomley } 822908d778SJames Bottomley 83c4028958SDavid Howells static void sas_phye_spinup_hold(struct work_struct *work) 842908d778SJames Bottomley { 8522b9153fSDan Williams struct asd_sas_event *ev = to_asd_sas_event(work); 86c4028958SDavid Howells struct asd_sas_phy *phy = ev->phy; 872908d778SJames Bottomley struct sas_ha_struct *sas_ha = phy->ha; 882908d778SJames Bottomley struct sas_internal *i = 892908d778SJames Bottomley to_sas_internal(sas_ha->core.shost->transportt); 902908d778SJames Bottomley 91b15ebe0bSDan Williams clear_bit(PHYE_SPINUP_HOLD, &phy->phy_events_pending); 922908d778SJames Bottomley 932908d778SJames Bottomley phy->error = 0; 94a01e70e5SJames Bottomley i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); 952908d778SJames Bottomley } 962908d778SJames Bottomley 97303694eeSDan Williams static void sas_phye_resume_timeout(struct work_struct *work) 98303694eeSDan Williams { 99303694eeSDan Williams struct asd_sas_event *ev = to_asd_sas_event(work); 100303694eeSDan Williams struct asd_sas_phy *phy = ev->phy; 101303694eeSDan Williams 102303694eeSDan Williams clear_bit(PHYE_RESUME_TIMEOUT, &phy->phy_events_pending); 103303694eeSDan Williams 104303694eeSDan Williams /* phew, lldd got the phy back in the nick of time */ 105303694eeSDan Williams if (!phy->suspended) { 106303694eeSDan Williams dev_info(&phy->phy->dev, "resume timeout cancelled\n"); 107303694eeSDan Williams return; 108303694eeSDan Williams } 109303694eeSDan Williams 110303694eeSDan Williams phy->error = 0; 111303694eeSDan Williams phy->suspended = 0; 112303694eeSDan Williams sas_deform_port(phy, 1); 113303694eeSDan Williams } 114303694eeSDan Williams 115303694eeSDan Williams 1162908d778SJames Bottomley /* ---------- Phy class registration ---------- */ 1172908d778SJames Bottomley 1182908d778SJames Bottomley int sas_register_phys(struct sas_ha_struct *sas_ha) 1192908d778SJames Bottomley { 1202908d778SJames Bottomley int i; 1212908d778SJames Bottomley 122c4028958SDavid Howells static const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { 1232908d778SJames Bottomley [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, 1242908d778SJames Bottomley [PHYE_OOB_DONE] = sas_phye_oob_done, 1252908d778SJames Bottomley [PHYE_OOB_ERROR] = sas_phye_oob_error, 1262908d778SJames Bottomley [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, 127303694eeSDan Williams [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, 128303694eeSDan Williams 1292908d778SJames Bottomley }; 1302908d778SJames Bottomley 131c4028958SDavid Howells static const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = { 1322908d778SJames Bottomley [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed, 1332908d778SJames Bottomley [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd, 1342908d778SJames Bottomley [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err, 1352908d778SJames Bottomley [PORTE_TIMER_EVENT] = sas_porte_timer_event, 1362908d778SJames Bottomley [PORTE_HARD_RESET] = sas_porte_hard_reset, 1372908d778SJames Bottomley }; 1382908d778SJames Bottomley 1392908d778SJames Bottomley /* Now register the phys. */ 1402908d778SJames Bottomley for (i = 0; i < sas_ha->num_phys; i++) { 1412908d778SJames Bottomley int k; 1422908d778SJames Bottomley struct asd_sas_phy *phy = sas_ha->sas_phy[i]; 1432908d778SJames Bottomley 1442908d778SJames Bottomley phy->error = 0; 1452908d778SJames Bottomley INIT_LIST_HEAD(&phy->port_phy_el); 146c4028958SDavid Howells for (k = 0; k < PORT_NUM_EVENTS; k++) { 14722b9153fSDan Williams INIT_SAS_WORK(&phy->port_events[k].work, sas_port_event_fns[k]); 148c4028958SDavid Howells phy->port_events[k].phy = phy; 149c4028958SDavid Howells } 1502908d778SJames Bottomley 151c4028958SDavid Howells for (k = 0; k < PHY_NUM_EVENTS; k++) { 15222b9153fSDan Williams INIT_SAS_WORK(&phy->phy_events[k].work, sas_phy_event_fns[k]); 153c4028958SDavid Howells phy->phy_events[k].phy = phy; 154c4028958SDavid Howells } 155c4028958SDavid Howells 1562908d778SJames Bottomley phy->port = NULL; 1572908d778SJames Bottomley phy->ha = sas_ha; 1582908d778SJames Bottomley spin_lock_init(&phy->frame_rcvd_lock); 1592908d778SJames Bottomley spin_lock_init(&phy->sas_prim_lock); 1602908d778SJames Bottomley phy->frame_rcvd_size = 0; 1612908d778SJames Bottomley 16222b9153fSDan Williams phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); 1632908d778SJames Bottomley if (!phy->phy) 1642908d778SJames Bottomley return -ENOMEM; 1652908d778SJames Bottomley 1662908d778SJames Bottomley phy->phy->identify.initiator_port_protocols = 1672908d778SJames Bottomley phy->iproto; 1682908d778SJames Bottomley phy->phy->identify.target_port_protocols = phy->tproto; 1692908d778SJames Bottomley phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr); 1702908d778SJames Bottomley phy->phy->identify.phy_identifier = i; 171a01e70e5SJames Bottomley phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 172a01e70e5SJames Bottomley phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 173a01e70e5SJames Bottomley phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; 174a01e70e5SJames Bottomley phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; 1752908d778SJames Bottomley phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; 1762908d778SJames Bottomley 1772908d778SJames Bottomley sas_phy_add(phy->phy); 1782908d778SJames Bottomley } 1792908d778SJames Bottomley 1802908d778SJames Bottomley return 0; 1812908d778SJames Bottomley } 182