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
17static 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, gone: 1);
24}
25
26static 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
34static 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->shost->transportt);
42
43 sas_deform_port(phy, gone: 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
63static 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->shost->transportt);
70
71 phy->error = 0;
72 i->dft->lldd_control_phy(phy, PHY_FUNC_RELEASE_SPINUP_HOLD, NULL);
73}
74
75static 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, gone: 1);
89}
90
91
92static 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->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
116int 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(v: &phy->event_nr, i: 0);
126 INIT_LIST_HEAD(list: &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->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
155const 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

source code of linux/drivers/scsi/libsas/sas_phy.c