1 /* 2 * Serial Attached SCSI (SAS) Phy class 3 * 4 * Copyright (C) 2005 Adaptec, Inc. All rights reserved. 5 * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com> 6 * 7 * This file is licensed under GPLv2. 8 * 9 * This program is free software; you can redistribute it and/or 10 * modify it under the terms of the GNU General Public License as 11 * published by the Free Software Foundation; either version 2 of the 12 * License, or (at your option) any later version. 13 * 14 * This program is distributed in the hope that it will be useful, but 15 * WITHOUT ANY WARRANTY; without even the implied warranty of 16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 17 * General Public License for more details. 18 * 19 * You should have received a copy of the GNU General Public License 20 * along with this program; if not, write to the Free Software 21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA 22 * 23 */ 24 25 #include "sas_internal.h" 26 #include <scsi/scsi_host.h> 27 #include <scsi/scsi_transport.h> 28 #include <scsi/scsi_transport_sas.h> 29 #include "../scsi_sas_internal.h" 30 31 /* ---------- Phy events ---------- */ 32 33 static void sas_phye_loss_of_signal(struct work_struct *work) 34 { 35 struct asd_sas_event *ev = to_asd_sas_event(work); 36 struct asd_sas_phy *phy = ev->phy; 37 38 phy->error = 0; 39 sas_deform_port(phy, 1); 40 } 41 42 static void sas_phye_oob_done(struct work_struct *work) 43 { 44 struct asd_sas_event *ev = to_asd_sas_event(work); 45 struct asd_sas_phy *phy = ev->phy; 46 47 phy->error = 0; 48 } 49 50 static void sas_phye_oob_error(struct work_struct *work) 51 { 52 struct asd_sas_event *ev = to_asd_sas_event(work); 53 struct asd_sas_phy *phy = ev->phy; 54 struct sas_ha_struct *sas_ha = phy->ha; 55 struct asd_sas_port *port = phy->port; 56 struct sas_internal *i = 57 to_sas_internal(sas_ha->core.shost->transportt); 58 59 sas_deform_port(phy, 1); 60 61 if (!port && phy->enabled && i->dft->lldd_control_phy) { 62 phy->error++; 63 switch (phy->error) { 64 case 1: 65 case 2: 66 i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET, 67 NULL); 68 break; 69 case 3: 70 default: 71 phy->error = 0; 72 phy->enabled = 0; 73 i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); 74 break; 75 } 76 } 77 } 78 79 static void sas_phye_spinup_hold(struct work_struct *work) 80 { 81 struct asd_sas_event *ev = to_asd_sas_event(work); 82 struct asd_sas_phy *phy = ev->phy; 83 struct sas_ha_struct *sas_ha = phy->ha; 84 struct sas_internal *i = 85 to_sas_internal(sas_ha->core.shost->transportt); 86 87 phy->error = 0; 88 i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL); 89 } 90 91 static void sas_phye_resume_timeout(struct work_struct *work) 92 { 93 struct asd_sas_event *ev = to_asd_sas_event(work); 94 struct asd_sas_phy *phy = ev->phy; 95 96 /* phew, lldd got the phy back in the nick of time */ 97 if (!phy->suspended) { 98 dev_info(&phy->phy->dev, "resume timeout cancelled\n"); 99 return; 100 } 101 102 phy->error = 0; 103 phy->suspended = 0; 104 sas_deform_port(phy, 1); 105 } 106 107 108 static void sas_phye_shutdown(struct work_struct *work) 109 { 110 struct asd_sas_event *ev = to_asd_sas_event(work); 111 struct asd_sas_phy *phy = ev->phy; 112 struct sas_ha_struct *sas_ha = phy->ha; 113 struct sas_internal *i = 114 to_sas_internal(sas_ha->core.shost->transportt); 115 116 if (phy->enabled) { 117 int ret; 118 119 phy->error = 0; 120 phy->enabled = 0; 121 ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL); 122 if (ret) 123 pr_notice("lldd disable phy%d returned %d\n", phy->id, 124 ret); 125 } else 126 pr_notice("phy%d is not enabled, cannot shutdown\n", phy->id); 127 phy->in_shutdown = 0; 128 } 129 130 /* ---------- Phy class registration ---------- */ 131 132 int sas_register_phys(struct sas_ha_struct *sas_ha) 133 { 134 int i; 135 136 /* Now register the phys. */ 137 for (i = 0; i < sas_ha->num_phys; i++) { 138 struct asd_sas_phy *phy = sas_ha->sas_phy[i]; 139 140 phy->error = 0; 141 atomic_set(&phy->event_nr, 0); 142 INIT_LIST_HEAD(&phy->port_phy_el); 143 144 phy->port = NULL; 145 phy->ha = sas_ha; 146 spin_lock_init(&phy->frame_rcvd_lock); 147 spin_lock_init(&phy->sas_prim_lock); 148 phy->frame_rcvd_size = 0; 149 150 phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i); 151 if (!phy->phy) 152 return -ENOMEM; 153 154 phy->phy->identify.initiator_port_protocols = 155 phy->iproto; 156 phy->phy->identify.target_port_protocols = phy->tproto; 157 phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr); 158 phy->phy->identify.phy_identifier = i; 159 phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 160 phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; 161 phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; 162 phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; 163 phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; 164 165 sas_phy_add(phy->phy); 166 } 167 168 return 0; 169 } 170 171 const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = { 172 [PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal, 173 [PHYE_OOB_DONE] = sas_phye_oob_done, 174 [PHYE_OOB_ERROR] = sas_phye_oob_error, 175 [PHYE_SPINUP_HOLD] = sas_phye_spinup_hold, 176 [PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout, 177 [PHYE_SHUTDOWN] = sas_phye_shutdown, 178 }; 179