1*4882a593Smuzhiyun // SPDX-License-Identifier: GPL-2.0
2*4882a593Smuzhiyun /*
3*4882a593Smuzhiyun * Serial Attached SCSI (SAS) Port class
4*4882a593Smuzhiyun *
5*4882a593Smuzhiyun * Copyright (C) 2005 Adaptec, Inc. All rights reserved.
6*4882a593Smuzhiyun * Copyright (C) 2005 Luben Tuikov <luben_tuikov@adaptec.com>
7*4882a593Smuzhiyun */
8*4882a593Smuzhiyun
9*4882a593Smuzhiyun #include "sas_internal.h"
10*4882a593Smuzhiyun
11*4882a593Smuzhiyun #include <scsi/scsi_transport.h>
12*4882a593Smuzhiyun #include <scsi/scsi_transport_sas.h>
13*4882a593Smuzhiyun #include "../scsi_sas_internal.h"
14*4882a593Smuzhiyun
phy_is_wideport_member(struct asd_sas_port * port,struct asd_sas_phy * phy)15*4882a593Smuzhiyun static bool phy_is_wideport_member(struct asd_sas_port *port, struct asd_sas_phy *phy)
16*4882a593Smuzhiyun {
17*4882a593Smuzhiyun struct sas_ha_struct *sas_ha = phy->ha;
18*4882a593Smuzhiyun
19*4882a593Smuzhiyun if (memcmp(port->attached_sas_addr, phy->attached_sas_addr,
20*4882a593Smuzhiyun SAS_ADDR_SIZE) != 0 || (sas_ha->strict_wide_ports &&
21*4882a593Smuzhiyun memcmp(port->sas_addr, phy->sas_addr, SAS_ADDR_SIZE) != 0))
22*4882a593Smuzhiyun return false;
23*4882a593Smuzhiyun return true;
24*4882a593Smuzhiyun }
25*4882a593Smuzhiyun
sas_resume_port(struct asd_sas_phy * phy)26*4882a593Smuzhiyun static void sas_resume_port(struct asd_sas_phy *phy)
27*4882a593Smuzhiyun {
28*4882a593Smuzhiyun struct domain_device *dev, *n;
29*4882a593Smuzhiyun struct asd_sas_port *port = phy->port;
30*4882a593Smuzhiyun struct sas_ha_struct *sas_ha = phy->ha;
31*4882a593Smuzhiyun struct sas_internal *si = to_sas_internal(sas_ha->core.shost->transportt);
32*4882a593Smuzhiyun
33*4882a593Smuzhiyun if (si->dft->lldd_port_formed)
34*4882a593Smuzhiyun si->dft->lldd_port_formed(phy);
35*4882a593Smuzhiyun
36*4882a593Smuzhiyun if (port->suspended)
37*4882a593Smuzhiyun port->suspended = 0;
38*4882a593Smuzhiyun else {
39*4882a593Smuzhiyun /* we only need to handle "link returned" actions once */
40*4882a593Smuzhiyun return;
41*4882a593Smuzhiyun }
42*4882a593Smuzhiyun
43*4882a593Smuzhiyun /* if the port came back:
44*4882a593Smuzhiyun * 1/ presume every device came back
45*4882a593Smuzhiyun * 2/ force the next revalidation to check all expander phys
46*4882a593Smuzhiyun */
47*4882a593Smuzhiyun list_for_each_entry_safe(dev, n, &port->dev_list, dev_list_node) {
48*4882a593Smuzhiyun int i, rc;
49*4882a593Smuzhiyun
50*4882a593Smuzhiyun rc = sas_notify_lldd_dev_found(dev);
51*4882a593Smuzhiyun if (rc) {
52*4882a593Smuzhiyun sas_unregister_dev(port, dev);
53*4882a593Smuzhiyun sas_destruct_devices(port);
54*4882a593Smuzhiyun continue;
55*4882a593Smuzhiyun }
56*4882a593Smuzhiyun
57*4882a593Smuzhiyun if (dev_is_expander(dev->dev_type)) {
58*4882a593Smuzhiyun dev->ex_dev.ex_change_count = -1;
59*4882a593Smuzhiyun for (i = 0; i < dev->ex_dev.num_phys; i++) {
60*4882a593Smuzhiyun struct ex_phy *phy = &dev->ex_dev.ex_phy[i];
61*4882a593Smuzhiyun
62*4882a593Smuzhiyun phy->phy_change_count = -1;
63*4882a593Smuzhiyun }
64*4882a593Smuzhiyun }
65*4882a593Smuzhiyun }
66*4882a593Smuzhiyun
67*4882a593Smuzhiyun sas_discover_event(port, DISCE_RESUME);
68*4882a593Smuzhiyun }
69*4882a593Smuzhiyun
70*4882a593Smuzhiyun /**
71*4882a593Smuzhiyun * sas_form_port - add this phy to a port
72*4882a593Smuzhiyun * @phy: the phy of interest
73*4882a593Smuzhiyun *
74*4882a593Smuzhiyun * This function adds this phy to an existing port, thus creating a wide
75*4882a593Smuzhiyun * port, or it creates a port and adds the phy to the port.
76*4882a593Smuzhiyun */
sas_form_port(struct asd_sas_phy * phy)77*4882a593Smuzhiyun static void sas_form_port(struct asd_sas_phy *phy)
78*4882a593Smuzhiyun {
79*4882a593Smuzhiyun int i;
80*4882a593Smuzhiyun struct sas_ha_struct *sas_ha = phy->ha;
81*4882a593Smuzhiyun struct asd_sas_port *port = phy->port;
82*4882a593Smuzhiyun struct domain_device *port_dev;
83*4882a593Smuzhiyun struct sas_internal *si =
84*4882a593Smuzhiyun to_sas_internal(sas_ha->core.shost->transportt);
85*4882a593Smuzhiyun unsigned long flags;
86*4882a593Smuzhiyun
87*4882a593Smuzhiyun if (port) {
88*4882a593Smuzhiyun if (!phy_is_wideport_member(port, phy))
89*4882a593Smuzhiyun sas_deform_port(phy, 0);
90*4882a593Smuzhiyun else if (phy->suspended) {
91*4882a593Smuzhiyun phy->suspended = 0;
92*4882a593Smuzhiyun sas_resume_port(phy);
93*4882a593Smuzhiyun
94*4882a593Smuzhiyun /* phy came back, try to cancel the timeout */
95*4882a593Smuzhiyun wake_up(&sas_ha->eh_wait_q);
96*4882a593Smuzhiyun return;
97*4882a593Smuzhiyun } else {
98*4882a593Smuzhiyun pr_info("%s: phy%d belongs to port%d already(%d)!\n",
99*4882a593Smuzhiyun __func__, phy->id, phy->port->id,
100*4882a593Smuzhiyun phy->port->num_phys);
101*4882a593Smuzhiyun return;
102*4882a593Smuzhiyun }
103*4882a593Smuzhiyun }
104*4882a593Smuzhiyun
105*4882a593Smuzhiyun /* see if the phy should be part of a wide port */
106*4882a593Smuzhiyun spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
107*4882a593Smuzhiyun for (i = 0; i < sas_ha->num_phys; i++) {
108*4882a593Smuzhiyun port = sas_ha->sas_port[i];
109*4882a593Smuzhiyun spin_lock(&port->phy_list_lock);
110*4882a593Smuzhiyun if (*(u64 *) port->sas_addr &&
111*4882a593Smuzhiyun phy_is_wideport_member(port, phy) && port->num_phys > 0) {
112*4882a593Smuzhiyun /* wide port */
113*4882a593Smuzhiyun pr_debug("phy%d matched wide port%d\n", phy->id,
114*4882a593Smuzhiyun port->id);
115*4882a593Smuzhiyun break;
116*4882a593Smuzhiyun }
117*4882a593Smuzhiyun spin_unlock(&port->phy_list_lock);
118*4882a593Smuzhiyun }
119*4882a593Smuzhiyun /* The phy does not match any existing port, create a new one */
120*4882a593Smuzhiyun if (i == sas_ha->num_phys) {
121*4882a593Smuzhiyun for (i = 0; i < sas_ha->num_phys; i++) {
122*4882a593Smuzhiyun port = sas_ha->sas_port[i];
123*4882a593Smuzhiyun spin_lock(&port->phy_list_lock);
124*4882a593Smuzhiyun if (*(u64 *)port->sas_addr == 0
125*4882a593Smuzhiyun && port->num_phys == 0) {
126*4882a593Smuzhiyun memcpy(port->sas_addr, phy->sas_addr,
127*4882a593Smuzhiyun SAS_ADDR_SIZE);
128*4882a593Smuzhiyun break;
129*4882a593Smuzhiyun }
130*4882a593Smuzhiyun spin_unlock(&port->phy_list_lock);
131*4882a593Smuzhiyun }
132*4882a593Smuzhiyun }
133*4882a593Smuzhiyun
134*4882a593Smuzhiyun if (i >= sas_ha->num_phys) {
135*4882a593Smuzhiyun pr_err("%s: couldn't find a free port, bug?\n", __func__);
136*4882a593Smuzhiyun spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
137*4882a593Smuzhiyun return;
138*4882a593Smuzhiyun }
139*4882a593Smuzhiyun
140*4882a593Smuzhiyun /* add the phy to the port */
141*4882a593Smuzhiyun port_dev = port->port_dev;
142*4882a593Smuzhiyun list_add_tail(&phy->port_phy_el, &port->phy_list);
143*4882a593Smuzhiyun sas_phy_set_target(phy, port_dev);
144*4882a593Smuzhiyun phy->port = port;
145*4882a593Smuzhiyun port->num_phys++;
146*4882a593Smuzhiyun port->phy_mask |= (1U << phy->id);
147*4882a593Smuzhiyun
148*4882a593Smuzhiyun if (*(u64 *)port->attached_sas_addr == 0) {
149*4882a593Smuzhiyun port->class = phy->class;
150*4882a593Smuzhiyun memcpy(port->attached_sas_addr, phy->attached_sas_addr,
151*4882a593Smuzhiyun SAS_ADDR_SIZE);
152*4882a593Smuzhiyun port->iproto = phy->iproto;
153*4882a593Smuzhiyun port->tproto = phy->tproto;
154*4882a593Smuzhiyun port->oob_mode = phy->oob_mode;
155*4882a593Smuzhiyun port->linkrate = phy->linkrate;
156*4882a593Smuzhiyun } else
157*4882a593Smuzhiyun port->linkrate = max(port->linkrate, phy->linkrate);
158*4882a593Smuzhiyun spin_unlock(&port->phy_list_lock);
159*4882a593Smuzhiyun spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
160*4882a593Smuzhiyun
161*4882a593Smuzhiyun if (!port->port) {
162*4882a593Smuzhiyun port->port = sas_port_alloc(phy->phy->dev.parent, port->id);
163*4882a593Smuzhiyun BUG_ON(!port->port);
164*4882a593Smuzhiyun sas_port_add(port->port);
165*4882a593Smuzhiyun }
166*4882a593Smuzhiyun sas_port_add_phy(port->port, phy->phy);
167*4882a593Smuzhiyun
168*4882a593Smuzhiyun pr_debug("%s added to %s, phy_mask:0x%x (%016llx)\n",
169*4882a593Smuzhiyun dev_name(&phy->phy->dev), dev_name(&port->port->dev),
170*4882a593Smuzhiyun port->phy_mask,
171*4882a593Smuzhiyun SAS_ADDR(port->attached_sas_addr));
172*4882a593Smuzhiyun
173*4882a593Smuzhiyun if (port_dev)
174*4882a593Smuzhiyun port_dev->pathways = port->num_phys;
175*4882a593Smuzhiyun
176*4882a593Smuzhiyun /* Tell the LLDD about this port formation. */
177*4882a593Smuzhiyun if (si->dft->lldd_port_formed)
178*4882a593Smuzhiyun si->dft->lldd_port_formed(phy);
179*4882a593Smuzhiyun
180*4882a593Smuzhiyun sas_discover_event(phy->port, DISCE_DISCOVER_DOMAIN);
181*4882a593Smuzhiyun /* Only insert a revalidate event after initial discovery */
182*4882a593Smuzhiyun if (port_dev && dev_is_expander(port_dev->dev_type)) {
183*4882a593Smuzhiyun struct expander_device *ex_dev = &port_dev->ex_dev;
184*4882a593Smuzhiyun
185*4882a593Smuzhiyun ex_dev->ex_change_count = -1;
186*4882a593Smuzhiyun sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
187*4882a593Smuzhiyun }
188*4882a593Smuzhiyun flush_workqueue(sas_ha->disco_q);
189*4882a593Smuzhiyun }
190*4882a593Smuzhiyun
191*4882a593Smuzhiyun /**
192*4882a593Smuzhiyun * sas_deform_port - remove this phy from the port it belongs to
193*4882a593Smuzhiyun * @phy: the phy of interest
194*4882a593Smuzhiyun * @gone: whether or not the PHY is gone
195*4882a593Smuzhiyun *
196*4882a593Smuzhiyun * This is called when the physical link to the other phy has been
197*4882a593Smuzhiyun * lost (on this phy), in Event thread context. We cannot delay here.
198*4882a593Smuzhiyun */
sas_deform_port(struct asd_sas_phy * phy,int gone)199*4882a593Smuzhiyun void sas_deform_port(struct asd_sas_phy *phy, int gone)
200*4882a593Smuzhiyun {
201*4882a593Smuzhiyun struct sas_ha_struct *sas_ha = phy->ha;
202*4882a593Smuzhiyun struct asd_sas_port *port = phy->port;
203*4882a593Smuzhiyun struct sas_internal *si =
204*4882a593Smuzhiyun to_sas_internal(sas_ha->core.shost->transportt);
205*4882a593Smuzhiyun struct domain_device *dev;
206*4882a593Smuzhiyun unsigned long flags;
207*4882a593Smuzhiyun
208*4882a593Smuzhiyun if (!port)
209*4882a593Smuzhiyun return; /* done by a phy event */
210*4882a593Smuzhiyun
211*4882a593Smuzhiyun dev = port->port_dev;
212*4882a593Smuzhiyun if (dev)
213*4882a593Smuzhiyun dev->pathways--;
214*4882a593Smuzhiyun
215*4882a593Smuzhiyun if (port->num_phys == 1) {
216*4882a593Smuzhiyun sas_unregister_domain_devices(port, gone);
217*4882a593Smuzhiyun sas_destruct_devices(port);
218*4882a593Smuzhiyun sas_port_delete(port->port);
219*4882a593Smuzhiyun port->port = NULL;
220*4882a593Smuzhiyun } else {
221*4882a593Smuzhiyun sas_port_delete_phy(port->port, phy->phy);
222*4882a593Smuzhiyun sas_device_set_phy(dev, port->port);
223*4882a593Smuzhiyun }
224*4882a593Smuzhiyun
225*4882a593Smuzhiyun if (si->dft->lldd_port_deformed)
226*4882a593Smuzhiyun si->dft->lldd_port_deformed(phy);
227*4882a593Smuzhiyun
228*4882a593Smuzhiyun spin_lock_irqsave(&sas_ha->phy_port_lock, flags);
229*4882a593Smuzhiyun spin_lock(&port->phy_list_lock);
230*4882a593Smuzhiyun
231*4882a593Smuzhiyun list_del_init(&phy->port_phy_el);
232*4882a593Smuzhiyun sas_phy_set_target(phy, NULL);
233*4882a593Smuzhiyun phy->port = NULL;
234*4882a593Smuzhiyun port->num_phys--;
235*4882a593Smuzhiyun port->phy_mask &= ~(1U << phy->id);
236*4882a593Smuzhiyun
237*4882a593Smuzhiyun if (port->num_phys == 0) {
238*4882a593Smuzhiyun INIT_LIST_HEAD(&port->phy_list);
239*4882a593Smuzhiyun memset(port->sas_addr, 0, SAS_ADDR_SIZE);
240*4882a593Smuzhiyun memset(port->attached_sas_addr, 0, SAS_ADDR_SIZE);
241*4882a593Smuzhiyun port->class = 0;
242*4882a593Smuzhiyun port->iproto = 0;
243*4882a593Smuzhiyun port->tproto = 0;
244*4882a593Smuzhiyun port->oob_mode = 0;
245*4882a593Smuzhiyun port->phy_mask = 0;
246*4882a593Smuzhiyun }
247*4882a593Smuzhiyun spin_unlock(&port->phy_list_lock);
248*4882a593Smuzhiyun spin_unlock_irqrestore(&sas_ha->phy_port_lock, flags);
249*4882a593Smuzhiyun
250*4882a593Smuzhiyun /* Only insert revalidate event if the port still has members */
251*4882a593Smuzhiyun if (port->port && dev && dev_is_expander(dev->dev_type)) {
252*4882a593Smuzhiyun struct expander_device *ex_dev = &dev->ex_dev;
253*4882a593Smuzhiyun
254*4882a593Smuzhiyun ex_dev->ex_change_count = -1;
255*4882a593Smuzhiyun sas_discover_event(port, DISCE_REVALIDATE_DOMAIN);
256*4882a593Smuzhiyun }
257*4882a593Smuzhiyun flush_workqueue(sas_ha->disco_q);
258*4882a593Smuzhiyun
259*4882a593Smuzhiyun return;
260*4882a593Smuzhiyun }
261*4882a593Smuzhiyun
262*4882a593Smuzhiyun /* ---------- SAS port events ---------- */
263*4882a593Smuzhiyun
sas_porte_bytes_dmaed(struct work_struct * work)264*4882a593Smuzhiyun void sas_porte_bytes_dmaed(struct work_struct *work)
265*4882a593Smuzhiyun {
266*4882a593Smuzhiyun struct asd_sas_event *ev = to_asd_sas_event(work);
267*4882a593Smuzhiyun struct asd_sas_phy *phy = ev->phy;
268*4882a593Smuzhiyun
269*4882a593Smuzhiyun sas_form_port(phy);
270*4882a593Smuzhiyun }
271*4882a593Smuzhiyun
sas_porte_broadcast_rcvd(struct work_struct * work)272*4882a593Smuzhiyun void sas_porte_broadcast_rcvd(struct work_struct *work)
273*4882a593Smuzhiyun {
274*4882a593Smuzhiyun struct asd_sas_event *ev = to_asd_sas_event(work);
275*4882a593Smuzhiyun struct asd_sas_phy *phy = ev->phy;
276*4882a593Smuzhiyun unsigned long flags;
277*4882a593Smuzhiyun u32 prim;
278*4882a593Smuzhiyun
279*4882a593Smuzhiyun spin_lock_irqsave(&phy->sas_prim_lock, flags);
280*4882a593Smuzhiyun prim = phy->sas_prim;
281*4882a593Smuzhiyun spin_unlock_irqrestore(&phy->sas_prim_lock, flags);
282*4882a593Smuzhiyun
283*4882a593Smuzhiyun pr_debug("broadcast received: %d\n", prim);
284*4882a593Smuzhiyun sas_discover_event(phy->port, DISCE_REVALIDATE_DOMAIN);
285*4882a593Smuzhiyun
286*4882a593Smuzhiyun if (phy->port)
287*4882a593Smuzhiyun flush_workqueue(phy->port->ha->disco_q);
288*4882a593Smuzhiyun }
289*4882a593Smuzhiyun
sas_porte_link_reset_err(struct work_struct * work)290*4882a593Smuzhiyun void sas_porte_link_reset_err(struct work_struct *work)
291*4882a593Smuzhiyun {
292*4882a593Smuzhiyun struct asd_sas_event *ev = to_asd_sas_event(work);
293*4882a593Smuzhiyun struct asd_sas_phy *phy = ev->phy;
294*4882a593Smuzhiyun
295*4882a593Smuzhiyun sas_deform_port(phy, 1);
296*4882a593Smuzhiyun }
297*4882a593Smuzhiyun
sas_porte_timer_event(struct work_struct * work)298*4882a593Smuzhiyun void sas_porte_timer_event(struct work_struct *work)
299*4882a593Smuzhiyun {
300*4882a593Smuzhiyun struct asd_sas_event *ev = to_asd_sas_event(work);
301*4882a593Smuzhiyun struct asd_sas_phy *phy = ev->phy;
302*4882a593Smuzhiyun
303*4882a593Smuzhiyun sas_deform_port(phy, 1);
304*4882a593Smuzhiyun }
305*4882a593Smuzhiyun
sas_porte_hard_reset(struct work_struct * work)306*4882a593Smuzhiyun void sas_porte_hard_reset(struct work_struct *work)
307*4882a593Smuzhiyun {
308*4882a593Smuzhiyun struct asd_sas_event *ev = to_asd_sas_event(work);
309*4882a593Smuzhiyun struct asd_sas_phy *phy = ev->phy;
310*4882a593Smuzhiyun
311*4882a593Smuzhiyun sas_deform_port(phy, 1);
312*4882a593Smuzhiyun }
313*4882a593Smuzhiyun
314*4882a593Smuzhiyun /* ---------- SAS port registration ---------- */
315*4882a593Smuzhiyun
sas_init_port(struct asd_sas_port * port,struct sas_ha_struct * sas_ha,int i)316*4882a593Smuzhiyun static void sas_init_port(struct asd_sas_port *port,
317*4882a593Smuzhiyun struct sas_ha_struct *sas_ha, int i)
318*4882a593Smuzhiyun {
319*4882a593Smuzhiyun memset(port, 0, sizeof(*port));
320*4882a593Smuzhiyun port->id = i;
321*4882a593Smuzhiyun INIT_LIST_HEAD(&port->dev_list);
322*4882a593Smuzhiyun INIT_LIST_HEAD(&port->disco_list);
323*4882a593Smuzhiyun INIT_LIST_HEAD(&port->destroy_list);
324*4882a593Smuzhiyun INIT_LIST_HEAD(&port->sas_port_del_list);
325*4882a593Smuzhiyun spin_lock_init(&port->phy_list_lock);
326*4882a593Smuzhiyun INIT_LIST_HEAD(&port->phy_list);
327*4882a593Smuzhiyun port->ha = sas_ha;
328*4882a593Smuzhiyun
329*4882a593Smuzhiyun spin_lock_init(&port->dev_list_lock);
330*4882a593Smuzhiyun }
331*4882a593Smuzhiyun
sas_register_ports(struct sas_ha_struct * sas_ha)332*4882a593Smuzhiyun int sas_register_ports(struct sas_ha_struct *sas_ha)
333*4882a593Smuzhiyun {
334*4882a593Smuzhiyun int i;
335*4882a593Smuzhiyun
336*4882a593Smuzhiyun /* initialize the ports and discovery */
337*4882a593Smuzhiyun for (i = 0; i < sas_ha->num_phys; i++) {
338*4882a593Smuzhiyun struct asd_sas_port *port = sas_ha->sas_port[i];
339*4882a593Smuzhiyun
340*4882a593Smuzhiyun sas_init_port(port, sas_ha, i);
341*4882a593Smuzhiyun sas_init_disc(&port->disc, port);
342*4882a593Smuzhiyun }
343*4882a593Smuzhiyun return 0;
344*4882a593Smuzhiyun }
345*4882a593Smuzhiyun
sas_unregister_ports(struct sas_ha_struct * sas_ha)346*4882a593Smuzhiyun void sas_unregister_ports(struct sas_ha_struct *sas_ha)
347*4882a593Smuzhiyun {
348*4882a593Smuzhiyun int i;
349*4882a593Smuzhiyun
350*4882a593Smuzhiyun for (i = 0; i < sas_ha->num_phys; i++)
351*4882a593Smuzhiyun if (sas_ha->sas_phy[i]->port)
352*4882a593Smuzhiyun sas_deform_port(sas_ha->sas_phy[i], 0);
353*4882a593Smuzhiyun
354*4882a593Smuzhiyun }
355*4882a593Smuzhiyun
356*4882a593Smuzhiyun const work_func_t sas_port_event_fns[PORT_NUM_EVENTS] = {
357*4882a593Smuzhiyun [PORTE_BYTES_DMAED] = sas_porte_bytes_dmaed,
358*4882a593Smuzhiyun [PORTE_BROADCAST_RCVD] = sas_porte_broadcast_rcvd,
359*4882a593Smuzhiyun [PORTE_LINK_RESET_ERR] = sas_porte_link_reset_err,
360*4882a593Smuzhiyun [PORTE_TIMER_EVENT] = sas_porte_timer_event,
361*4882a593Smuzhiyun [PORTE_HARD_RESET] = sas_porte_hard_reset,
362*4882a593Smuzhiyun };
363