xref: /openbmc/linux/drivers/scsi/libsas/sas_phy.c (revision 303694ee)
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