xref: /openbmc/linux/drivers/scsi/libsas/sas_phy.c (revision 842ed298)
1 // SPDX-License-Identifier: GPL-2.0
2 /*
3  * Serial Attached SCSI (SAS) Phy class
4  *
5  * Copyright (C) 2005 Adaptec, Inc.  All rights reserved.
6  * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
7  */
8 
9 #include "sas_internal.h"
10 #include <scsi/scsi_host.h>
11 #include <scsi/scsi_transport.h>
12 #include <scsi/scsi_transport_sas.h>
13 #include "../scsi_sas_internal.h"
14 
15 /* ---------- Phy events ---------- */
16 
17 static void sas_phye_loss_of_signal(struct work_struct *work)
18 {
19 	struct asd_sas_event *ev = to_asd_sas_event(work);
20 	struct asd_sas_phy *phy = ev->phy;
21 
22 	phy->error = 0;
23 	sas_deform_port(phy, 1);
24 }
25 
26 static void sas_phye_oob_done(struct work_struct *work)
27 {
28 	struct asd_sas_event *ev = to_asd_sas_event(work);
29 	struct asd_sas_phy *phy = ev->phy;
30 
31 	phy->error = 0;
32 }
33 
34 static void sas_phye_oob_error(struct work_struct *work)
35 {
36 	struct asd_sas_event *ev = to_asd_sas_event(work);
37 	struct asd_sas_phy *phy = ev->phy;
38 	struct sas_ha_struct *sas_ha = phy->ha;
39 	struct asd_sas_port *port = phy->port;
40 	struct sas_internal *i =
41 		to_sas_internal(sas_ha->core.shost->transportt);
42 
43 	sas_deform_port(phy, 1);
44 
45 	if (!port && phy->enabled && i->dft->lldd_control_phy) {
46 		phy->error++;
47 		switch (phy->error) {
48 		case 1:
49 		case 2:
50 			i->dft->lldd_control_phy(phy, PHY_FUNC_HARD_RESET,
51 						 NULL);
52 			break;
53 		case 3:
54 		default:
55 			phy->error = 0;
56 			phy->enabled = 0;
57 			i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
58 			break;
59 		}
60 	}
61 }
62 
63 static void sas_phye_spinup_hold(struct work_struct *work)
64 {
65 	struct asd_sas_event *ev = to_asd_sas_event(work);
66 	struct asd_sas_phy *phy = ev->phy;
67 	struct sas_ha_struct *sas_ha = phy->ha;
68 	struct sas_internal *i =
69 		to_sas_internal(sas_ha->core.shost->transportt);
70 
71 	phy->error = 0;
72 	i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
73 }
74 
75 static void sas_phye_resume_timeout(struct work_struct *work)
76 {
77 	struct asd_sas_event *ev = to_asd_sas_event(work);
78 	struct asd_sas_phy *phy = ev->phy;
79 
80 	/* phew, lldd got the phy back in the nick of time */
81 	if (!phy->suspended) {
82 		dev_info(&phy->phy->dev, "resume timeout cancelled\n");
83 		return;
84 	}
85 
86 	phy->error = 0;
87 	phy->suspended = 0;
88 	sas_deform_port(phy, 1);
89 }
90 
91 
92 static void sas_phye_shutdown(struct work_struct *work)
93 {
94 	struct asd_sas_event *ev = to_asd_sas_event(work);
95 	struct asd_sas_phy *phy = ev->phy;
96 	struct sas_ha_struct *sas_ha = phy->ha;
97 	struct sas_internal *i =
98 		to_sas_internal(sas_ha->core.shost->transportt);
99 
100 	if (phy->enabled) {
101 		int ret;
102 
103 		phy->error = 0;
104 		phy->enabled = 0;
105 		ret = i->dft->lldd_control_phy(phy, PHY_FUNC_DISABLE, NULL);
106 		if (ret)
107 			pr_notice("lldd disable phy%d returned %d\n", phy->id,
108 				  ret);
109 	} else
110 		pr_notice("phy%d is not enabled, cannot shutdown\n", phy->id);
111 	phy->in_shutdown = 0;
112 }
113 
114 /* ---------- Phy class registration ---------- */
115 
116 int sas_register_phys(struct sas_ha_struct *sas_ha)
117 {
118 	int i;
119 
120 	/* Now register the phys. */
121 	for (i = 0; i < sas_ha->num_phys; i++) {
122 		struct asd_sas_phy *phy = sas_ha->sas_phy[i];
123 
124 		phy->error = 0;
125 		atomic_set(&phy->event_nr, 0);
126 		INIT_LIST_HEAD(&phy->port_phy_el);
127 
128 		phy->port = NULL;
129 		phy->ha = sas_ha;
130 		spin_lock_init(&phy->frame_rcvd_lock);
131 		spin_lock_init(&phy->sas_prim_lock);
132 		phy->frame_rcvd_size = 0;
133 
134 		phy->phy = sas_phy_alloc(&sas_ha->core.shost->shost_gendev, i);
135 		if (!phy->phy)
136 			return -ENOMEM;
137 
138 		phy->phy->identify.initiator_port_protocols =
139 			phy->iproto;
140 		phy->phy->identify.target_port_protocols = phy->tproto;
141 		phy->phy->identify.sas_address = SAS_ADDR(sas_ha->sas_addr);
142 		phy->phy->identify.phy_identifier = i;
143 		phy->phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
144 		phy->phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN;
145 		phy->phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN;
146 		phy->phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN;
147 		phy->phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN;
148 
149 		sas_phy_add(phy->phy);
150 	}
151 
152 	return 0;
153 }
154 
155 const work_func_t sas_phy_event_fns[PHY_NUM_EVENTS] = {
156 	[PHYE_LOSS_OF_SIGNAL] = sas_phye_loss_of_signal,
157 	[PHYE_OOB_DONE] = sas_phye_oob_done,
158 	[PHYE_OOB_ERROR] = sas_phye_oob_error,
159 	[PHYE_SPINUP_HOLD] = sas_phye_spinup_hold,
160 	[PHYE_RESUME_TIMEOUT] = sas_phye_resume_timeout,
161 	[PHYE_SHUTDOWN] = sas_phye_shutdown,
162 };
163