Merge tag 'ceph-for-5.3-rc1' of git://github.com/ceph/ceph-client
[linux-2.6-microblaze.git] / drivers / scsi / libsas / sas_phy.c
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 };