xref: /openbmc/linux/drivers/scsi/libsas/sas_phy.c (revision 3c236f8c)
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 
38f12486e0SJason Yan 	phy->in_shutdown = 0;
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 
48f12486e0SJason Yan 	phy->in_shutdown = 0;
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 
6190f1e10dSDan Williams 	sas_deform_port(phy, 1);
622908d778SJames Bottomley 
632908d778SJames Bottomley 	if (!port && phy->enabled && i->dft->lldd_control_phy) {
642908d778SJames Bottomley 		phy->error++;
652908d778SJames Bottomley 		switch (phy->error) {
662908d778SJames Bottomley 		case 1:
672908d778SJames Bottomley 		case 2:
68a01e70e5SJames Bottomley 			i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
69a01e70e5SJames Bottomley 						 NULL);
702908d778SJames Bottomley 			break;
712908d778SJames Bottomley 		case 3:
722908d778SJames Bottomley 		default:
732908d778SJames Bottomley 			phy->error = 0;
742908d778SJames Bottomley 			phy->enabled = 0;
75a01e70e5SJames Bottomley 			i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
762908d778SJames Bottomley 			break;
772908d778SJames Bottomley 		}
782908d778SJames Bottomley 	}
792908d778SJames Bottomley }
802908d778SJames Bottomley 
81c4028958SDavid Howells static void sas_phye_spinup_hold(struct work_struct *work)
822908d778SJames Bottomley {
8322b9153fSDan Williams 	struct asd_sas_event *ev = to_asd_sas_event(work);
84c4028958SDavid Howells 	struct asd_sas_phy *phy = ev->phy;
852908d778SJames Bottomley 	struct sas_ha_struct *sas_ha = phy->ha;
862908d778SJames Bottomley 	struct sas_internal *i =
872908d778SJames Bottomley 		to_sas_internal(sas_ha->core.shost->transportt);
882908d778SJames Bottomley 
892908d778SJames Bottomley 	phy->error = 0;
90a01e70e5SJames Bottomley 	i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
912908d778SJames Bottomley }
922908d778SJames Bottomley 
93303694eeSDan Williams static void sas_phye_resume_timeout(struct work_struct *work)
94303694eeSDan Williams {
95303694eeSDan Williams 	struct asd_sas_event *ev = to_asd_sas_event(work);
96303694eeSDan Williams 	struct asd_sas_phy *phy = ev->phy;
97303694eeSDan Williams 
98303694eeSDan Williams 	/* phew, lldd got the phy back in the nick of time */
99303694eeSDan Williams 	if (!phy->suspended) {
100303694eeSDan Williams 		dev_info(&phy->phy->dev, "resume timeout cancelled\n");
101303694eeSDan Williams 		return;
102303694eeSDan Williams 	}
103303694eeSDan Williams 
104303694eeSDan Williams 	phy->error = 0;
105303694eeSDan Williams 	phy->suspended = 0;
106303694eeSDan Williams 	sas_deform_port(phy, 1);
107303694eeSDan Williams }
108303694eeSDan Williams 
109303694eeSDan Williams 
110f12486e0SJason Yan static void sas_phye_shutdown(struct work_struct *work)
111f12486e0SJason Yan {
112f12486e0SJason Yan 	struct asd_sas_event *ev = to_asd_sas_event(work);
113f12486e0SJason Yan 	struct asd_sas_phy *phy = ev->phy;
114f12486e0SJason Yan 	struct sas_ha_struct *sas_ha = phy->ha;
115f12486e0SJason Yan 	struct sas_internal *i =
116f12486e0SJason Yan 		to_sas_internal(sas_ha->core.shost->transportt);
117f12486e0SJason Yan 
118f12486e0SJason Yan 	if (phy->enabled) {
119f12486e0SJason Yan 		int ret;
120f12486e0SJason Yan 
121f12486e0SJason Yan 		phy->error = 0;
122f12486e0SJason Yan 		phy->enabled = 0;
123f12486e0SJason Yan 		ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
124f12486e0SJason Yan 		if (ret)
1253c236f8cSJohn Garry 			pr_notice("lldd disable phy%d returned %d\n", phy->id,
1263c236f8cSJohn Garry 				  ret);
127f12486e0SJason Yan 	} else
1283c236f8cSJohn Garry 		pr_notice("phy%d is not enabled, cannot shutdown\n", phy->id);
129f12486e0SJason Yan }
130f12486e0SJason Yan 
1312908d778SJames Bottomley /* ---------- Phy class registration ---------- */
1322908d778SJames Bottomley 
1332908d778SJames Bottomley int sas_register_phys(struct sas_ha_struct *sas_ha)
1342908d778SJames Bottomley {
1352908d778SJames Bottomley 	int i;
1362908d778SJames Bottomley 
1372908d778SJames Bottomley 	/* Now register the phys. */
1382908d778SJames Bottomley 	for (i = 0; i < sas_ha->num_phys; i++) {
1392908d778SJames Bottomley 		struct asd_sas_phy *phy = sas_ha->sas_phy[i];
1402908d778SJames Bottomley 
1412908d778SJames Bottomley 		phy->error = 0;
142f12486e0SJason Yan 		atomic_set(&phy->event_nr, 0);
1432908d778SJames Bottomley 		INIT_LIST_HEAD(&phy->port_phy_el);
144c4028958SDavid Howells 
1452908d778SJames Bottomley 		phy->port = NULL;
1462908d778SJames Bottomley 		phy->ha = sas_ha;
1472908d778SJames Bottomley 		spin_lock_init(&phy->frame_rcvd_lock);
1482908d778SJames Bottomley 		spin_lock_init(&phy->sas_prim_lock);
1492908d778SJames Bottomley 		phy->frame_rcvd_size = 0;
1502908d778SJames Bottomley 
15122b9153fSDan Williams 		phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
1522908d778SJames Bottomley 		if (!phy->phy)
1532908d778SJames Bottomley 			return -ENOMEM;
1542908d778SJames Bottomley 
1552908d778SJames Bottomley 		phy->phy->identify.initiator_port_protocols =
1562908d778SJames Bottomley 			phy->iproto;
1572908d778SJames Bottomley 		phy->phy->identify.target_port_protocols = phy->tproto;
1582908d778SJames Bottomley 		phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
1592908d778SJames Bottomley 		phy->phy->identify.phy_identifier = i;
160a01e70e5SJames Bottomley 		phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
161a01e70e5SJames Bottomley 		phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
162a01e70e5SJames Bottomley 		phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
163a01e70e5SJames Bottomley 		phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
1642908d778SJames Bottomley 		phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
1652908d778SJames Bottomley 
1662908d778SJames Bottomley 		sas_phy_add(phy->phy);
1672908d778SJames Bottomley 	}
1682908d778SJames Bottomley 
1692908d778SJames Bottomley 	return 0;
1702908d778SJames Bottomley }
1711c393b97SJason Yan 
1721c393b97SJason Yan const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
1731c393b97SJason Yan 	[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
1741c393b97SJason Yan 	[PHYE_OOB_DONE] = sas_phye_oob_done,
1751c393b97SJason Yan 	[PHYE_OOB_ERROR] = sas_phye_oob_error,
1761c393b97SJason Yan 	[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
1771c393b97SJason Yan 	[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
178f12486e0SJason Yan 	[PHYE_SHUTDOWN] = sas_phye_shutdown,
1791c393b97SJason Yan };
180