]>
Commit | Line | Data |
---|---|---|
6c223761 KB |
1 | /* |
2 | * driver for Microsemi PQI-based storage controllers | |
b805dbfe | 3 | * Copyright (c) 2016-2017 Microsemi Corporation |
6c223761 KB |
4 | * Copyright (c) 2016 PMC-Sierra, Inc. |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; version 2 of the License. | |
9 | * | |
10 | * This program is distributed in the hope that it will be useful, | |
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 | * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, GOOD TITLE or | |
13 | * NON INFRINGEMENT. See the GNU General Public License for more details. | |
14 | * | |
15 | * Questions/Comments/Bugfixes to esc.storagedev@microsemi.com | |
16 | * | |
17 | */ | |
18 | ||
19 | #include <linux/kernel.h> | |
20 | #include <scsi/scsi_host.h> | |
21 | #include <scsi/scsi_cmnd.h> | |
22 | #include <scsi/scsi_transport_sas.h> | |
23 | #include "smartpqi.h" | |
24 | ||
25 | static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port) | |
26 | { | |
27 | struct pqi_sas_phy *pqi_sas_phy; | |
28 | struct sas_phy *phy; | |
29 | ||
30 | pqi_sas_phy = kzalloc(sizeof(*pqi_sas_phy), GFP_KERNEL); | |
31 | if (!pqi_sas_phy) | |
32 | return NULL; | |
33 | ||
34 | phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev, | |
35 | pqi_sas_port->next_phy_index); | |
36 | if (!phy) { | |
37 | kfree(pqi_sas_phy); | |
38 | return NULL; | |
39 | } | |
40 | ||
41 | pqi_sas_port->next_phy_index++; | |
42 | pqi_sas_phy->phy = phy; | |
43 | pqi_sas_phy->parent_port = pqi_sas_port; | |
44 | ||
45 | return pqi_sas_phy; | |
46 | } | |
47 | ||
48 | static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy) | |
49 | { | |
50 | struct sas_phy *phy = pqi_sas_phy->phy; | |
51 | ||
52 | sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy); | |
53 | sas_phy_free(phy); | |
54 | if (pqi_sas_phy->added_to_port) | |
55 | list_del(&pqi_sas_phy->phy_list_entry); | |
56 | kfree(pqi_sas_phy); | |
57 | } | |
58 | ||
59 | static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy) | |
60 | { | |
61 | int rc; | |
62 | struct pqi_sas_port *pqi_sas_port; | |
63 | struct sas_phy *phy; | |
64 | struct sas_identify *identify; | |
65 | ||
66 | pqi_sas_port = pqi_sas_phy->parent_port; | |
67 | phy = pqi_sas_phy->phy; | |
68 | ||
69 | identify = &phy->identify; | |
70 | memset(identify, 0, sizeof(*identify)); | |
71 | identify->sas_address = pqi_sas_port->sas_address; | |
72 | identify->device_type = SAS_END_DEVICE; | |
73 | identify->initiator_port_protocols = SAS_PROTOCOL_STP; | |
74 | identify->target_port_protocols = SAS_PROTOCOL_STP; | |
75 | phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; | |
76 | phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; | |
77 | phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; | |
78 | phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; | |
79 | phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; | |
80 | ||
81 | rc = sas_phy_add(pqi_sas_phy->phy); | |
82 | if (rc) | |
83 | return rc; | |
84 | ||
85 | sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy); | |
86 | list_add_tail(&pqi_sas_phy->phy_list_entry, | |
87 | &pqi_sas_port->phy_list_head); | |
88 | pqi_sas_phy->added_to_port = true; | |
89 | ||
90 | return 0; | |
91 | } | |
92 | ||
93 | static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port, | |
94 | struct sas_rphy *rphy) | |
95 | { | |
96 | struct sas_identify *identify; | |
97 | ||
98 | identify = &rphy->identify; | |
99 | identify->sas_address = pqi_sas_port->sas_address; | |
100 | identify->initiator_port_protocols = SAS_PROTOCOL_STP; | |
101 | identify->target_port_protocols = SAS_PROTOCOL_STP; | |
102 | ||
103 | return sas_rphy_add(rphy); | |
104 | } | |
105 | ||
106 | static struct pqi_sas_port *pqi_alloc_sas_port( | |
107 | struct pqi_sas_node *pqi_sas_node, u64 sas_address) | |
108 | { | |
109 | int rc; | |
110 | struct pqi_sas_port *pqi_sas_port; | |
111 | struct sas_port *port; | |
112 | ||
113 | pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL); | |
114 | if (!pqi_sas_port) | |
115 | return NULL; | |
116 | ||
117 | INIT_LIST_HEAD(&pqi_sas_port->phy_list_head); | |
118 | pqi_sas_port->parent_node = pqi_sas_node; | |
119 | ||
120 | port = sas_port_alloc_num(pqi_sas_node->parent_dev); | |
121 | if (!port) | |
122 | goto free_pqi_port; | |
123 | ||
124 | rc = sas_port_add(port); | |
125 | if (rc) | |
126 | goto free_sas_port; | |
127 | ||
128 | pqi_sas_port->port = port; | |
129 | pqi_sas_port->sas_address = sas_address; | |
130 | list_add_tail(&pqi_sas_port->port_list_entry, | |
131 | &pqi_sas_node->port_list_head); | |
132 | ||
133 | return pqi_sas_port; | |
134 | ||
135 | free_sas_port: | |
136 | sas_port_free(port); | |
137 | free_pqi_port: | |
138 | kfree(pqi_sas_port); | |
139 | ||
140 | return NULL; | |
141 | } | |
142 | ||
143 | static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port) | |
144 | { | |
145 | struct pqi_sas_phy *pqi_sas_phy; | |
146 | struct pqi_sas_phy *next; | |
147 | ||
148 | list_for_each_entry_safe(pqi_sas_phy, next, | |
149 | &pqi_sas_port->phy_list_head, phy_list_entry) | |
150 | pqi_free_sas_phy(pqi_sas_phy); | |
151 | ||
152 | sas_port_delete(pqi_sas_port->port); | |
153 | list_del(&pqi_sas_port->port_list_entry); | |
154 | kfree(pqi_sas_port); | |
155 | } | |
156 | ||
157 | static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev) | |
158 | { | |
159 | struct pqi_sas_node *pqi_sas_node; | |
160 | ||
161 | pqi_sas_node = kzalloc(sizeof(*pqi_sas_node), GFP_KERNEL); | |
162 | if (pqi_sas_node) { | |
163 | pqi_sas_node->parent_dev = parent_dev; | |
164 | INIT_LIST_HEAD(&pqi_sas_node->port_list_head); | |
165 | } | |
166 | ||
167 | return pqi_sas_node; | |
168 | } | |
169 | ||
170 | static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node) | |
171 | { | |
172 | struct pqi_sas_port *pqi_sas_port; | |
173 | struct pqi_sas_port *next; | |
174 | ||
175 | if (!pqi_sas_node) | |
176 | return; | |
177 | ||
178 | list_for_each_entry_safe(pqi_sas_port, next, | |
179 | &pqi_sas_node->port_list_head, port_list_entry) | |
180 | pqi_free_sas_port(pqi_sas_port); | |
181 | ||
182 | kfree(pqi_sas_node); | |
183 | } | |
184 | ||
185 | struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( | |
186 | struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy) | |
187 | { | |
188 | struct pqi_scsi_dev *device; | |
189 | ||
190 | list_for_each_entry(device, &ctrl_info->scsi_device_list, | |
191 | scsi_device_list_entry) { | |
192 | if (!device->sas_port) | |
193 | continue; | |
194 | if (device->sas_port->rphy == rphy) | |
195 | return device; | |
196 | } | |
197 | ||
198 | return NULL; | |
199 | } | |
200 | ||
201 | int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info) | |
202 | { | |
203 | int rc; | |
204 | struct device *parent_dev; | |
205 | struct pqi_sas_node *pqi_sas_node; | |
206 | struct pqi_sas_port *pqi_sas_port; | |
207 | struct pqi_sas_phy *pqi_sas_phy; | |
208 | ||
209 | parent_dev = &shost->shost_gendev; | |
210 | ||
211 | pqi_sas_node = pqi_alloc_sas_node(parent_dev); | |
212 | if (!pqi_sas_node) | |
213 | return -ENOMEM; | |
214 | ||
215 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, ctrl_info->sas_address); | |
216 | if (!pqi_sas_port) { | |
217 | rc = -ENODEV; | |
218 | goto free_sas_node; | |
219 | } | |
220 | ||
221 | pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port); | |
222 | if (!pqi_sas_phy) { | |
223 | rc = -ENODEV; | |
224 | goto free_sas_port; | |
225 | } | |
226 | ||
227 | rc = pqi_sas_port_add_phy(pqi_sas_phy); | |
228 | if (rc) | |
229 | goto free_sas_phy; | |
230 | ||
231 | ctrl_info->sas_host = pqi_sas_node; | |
232 | ||
233 | return 0; | |
234 | ||
235 | free_sas_phy: | |
236 | pqi_free_sas_phy(pqi_sas_phy); | |
237 | free_sas_port: | |
238 | pqi_free_sas_port(pqi_sas_port); | |
239 | free_sas_node: | |
240 | pqi_free_sas_node(pqi_sas_node); | |
241 | ||
242 | return rc; | |
243 | } | |
244 | ||
245 | void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info) | |
246 | { | |
247 | pqi_free_sas_node(ctrl_info->sas_host); | |
248 | } | |
249 | ||
250 | int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, | |
251 | struct pqi_scsi_dev *device) | |
252 | { | |
253 | int rc; | |
254 | struct pqi_sas_port *pqi_sas_port; | |
255 | struct sas_rphy *rphy; | |
256 | ||
257 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, device->sas_address); | |
258 | if (!pqi_sas_port) | |
259 | return -ENOMEM; | |
260 | ||
261 | rphy = sas_end_device_alloc(pqi_sas_port->port); | |
262 | if (!rphy) { | |
263 | rc = -ENODEV; | |
264 | goto free_sas_port; | |
265 | } | |
266 | ||
267 | pqi_sas_port->rphy = rphy; | |
268 | device->sas_port = pqi_sas_port; | |
269 | ||
270 | rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy); | |
271 | if (rc) | |
272 | goto free_sas_port; | |
273 | ||
274 | return 0; | |
275 | ||
276 | free_sas_port: | |
277 | pqi_free_sas_port(pqi_sas_port); | |
278 | device->sas_port = NULL; | |
279 | ||
280 | return rc; | |
281 | } | |
282 | ||
283 | void pqi_remove_sas_device(struct pqi_scsi_dev *device) | |
284 | { | |
285 | if (device->sas_port) { | |
286 | pqi_free_sas_port(device->sas_port); | |
287 | device->sas_port = NULL; | |
288 | } | |
289 | } | |
290 | ||
291 | static int pqi_sas_get_linkerrors(struct sas_phy *phy) | |
292 | { | |
293 | return 0; | |
294 | } | |
295 | ||
296 | static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy, | |
297 | u64 *identifier) | |
298 | { | |
299 | return 0; | |
300 | } | |
301 | ||
302 | static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) | |
303 | { | |
304 | return -ENXIO; | |
305 | } | |
306 | ||
307 | static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset) | |
308 | { | |
309 | return 0; | |
310 | } | |
311 | ||
312 | static int pqi_sas_phy_enable(struct sas_phy *phy, int enable) | |
313 | { | |
314 | return 0; | |
315 | } | |
316 | ||
317 | static int pqi_sas_phy_setup(struct sas_phy *phy) | |
318 | { | |
319 | return 0; | |
320 | } | |
321 | ||
322 | static void pqi_sas_phy_release(struct sas_phy *phy) | |
323 | { | |
324 | } | |
325 | ||
326 | static int pqi_sas_phy_speed(struct sas_phy *phy, | |
327 | struct sas_phy_linkrates *rates) | |
328 | { | |
329 | return -EINVAL; | |
330 | } | |
331 | ||
332 | /* SMP = Serial Management Protocol */ | |
333 | ||
334 | static int pqi_sas_smp_handler(struct Scsi_Host *shost, struct sas_rphy *rphy, | |
335 | struct request *req) | |
336 | { | |
337 | return -EINVAL; | |
338 | } | |
339 | ||
340 | struct sas_function_template pqi_sas_transport_functions = { | |
341 | .get_linkerrors = pqi_sas_get_linkerrors, | |
342 | .get_enclosure_identifier = pqi_sas_get_enclosure_identifier, | |
343 | .get_bay_identifier = pqi_sas_get_bay_identifier, | |
344 | .phy_reset = pqi_sas_phy_reset, | |
345 | .phy_enable = pqi_sas_phy_enable, | |
346 | .phy_setup = pqi_sas_phy_setup, | |
347 | .phy_release = pqi_sas_phy_release, | |
348 | .set_phy_speed = pqi_sas_phy_speed, | |
349 | .smp_handler = pqi_sas_smp_handler, | |
350 | }; |