1 | // SPDX-License-Identifier: GPL-2.0 |
2 | /* |
3 | * driver for Microchip PQI-based storage controllers |
4 | * Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries |
5 | * Copyright (c) 2016-2018 Microsemi Corporation |
6 | * Copyright (c) 2016 PMC-Sierra, Inc. |
7 | * |
8 | * Questions/Comments/Bugfixes to storagedev@microchip.com |
9 | * |
10 | */ |
11 | |
12 | #include <linux/kernel.h> |
13 | #include <linux/bsg-lib.h> |
14 | #include <scsi/scsi_host.h> |
15 | #include <scsi/scsi_cmnd.h> |
16 | #include <scsi/scsi_transport_sas.h> |
17 | #include <asm/unaligned.h> |
18 | #include "smartpqi.h" |
19 | |
20 | static struct pqi_sas_phy *pqi_alloc_sas_phy(struct pqi_sas_port *pqi_sas_port) |
21 | { |
22 | struct pqi_sas_phy *pqi_sas_phy; |
23 | struct sas_phy *phy; |
24 | |
25 | pqi_sas_phy = kzalloc(size: sizeof(*pqi_sas_phy), GFP_KERNEL); |
26 | if (!pqi_sas_phy) |
27 | return NULL; |
28 | |
29 | phy = sas_phy_alloc(pqi_sas_port->parent_node->parent_dev, |
30 | pqi_sas_port->next_phy_index); |
31 | if (!phy) { |
32 | kfree(objp: pqi_sas_phy); |
33 | return NULL; |
34 | } |
35 | |
36 | pqi_sas_port->next_phy_index++; |
37 | pqi_sas_phy->phy = phy; |
38 | pqi_sas_phy->parent_port = pqi_sas_port; |
39 | |
40 | return pqi_sas_phy; |
41 | } |
42 | |
43 | static void pqi_free_sas_phy(struct pqi_sas_phy *pqi_sas_phy) |
44 | { |
45 | struct sas_phy *phy = pqi_sas_phy->phy; |
46 | |
47 | sas_port_delete_phy(pqi_sas_phy->parent_port->port, phy); |
48 | if (pqi_sas_phy->added_to_port) |
49 | list_del(entry: &pqi_sas_phy->phy_list_entry); |
50 | sas_phy_delete(phy); |
51 | kfree(objp: pqi_sas_phy); |
52 | } |
53 | |
54 | static int pqi_sas_port_add_phy(struct pqi_sas_phy *pqi_sas_phy) |
55 | { |
56 | int rc; |
57 | struct pqi_sas_port *pqi_sas_port; |
58 | struct sas_phy *phy; |
59 | struct sas_identify *identify; |
60 | |
61 | pqi_sas_port = pqi_sas_phy->parent_port; |
62 | phy = pqi_sas_phy->phy; |
63 | |
64 | identify = &phy->identify; |
65 | memset(identify, 0, sizeof(*identify)); |
66 | identify->sas_address = pqi_sas_port->sas_address; |
67 | identify->device_type = SAS_END_DEVICE; |
68 | identify->initiator_port_protocols = SAS_PROTOCOL_ALL; |
69 | identify->target_port_protocols = SAS_PROTOCOL_ALL; |
70 | phy->minimum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; |
71 | phy->maximum_linkrate_hw = SAS_LINK_RATE_UNKNOWN; |
72 | phy->minimum_linkrate = SAS_LINK_RATE_UNKNOWN; |
73 | phy->maximum_linkrate = SAS_LINK_RATE_UNKNOWN; |
74 | phy->negotiated_linkrate = SAS_LINK_RATE_UNKNOWN; |
75 | |
76 | rc = sas_phy_add(pqi_sas_phy->phy); |
77 | if (rc) |
78 | return rc; |
79 | |
80 | sas_port_add_phy(pqi_sas_port->port, pqi_sas_phy->phy); |
81 | list_add_tail(new: &pqi_sas_phy->phy_list_entry, |
82 | head: &pqi_sas_port->phy_list_head); |
83 | pqi_sas_phy->added_to_port = true; |
84 | |
85 | return 0; |
86 | } |
87 | |
88 | static int pqi_sas_port_add_rphy(struct pqi_sas_port *pqi_sas_port, |
89 | struct sas_rphy *rphy) |
90 | { |
91 | struct sas_identify *identify; |
92 | |
93 | identify = &rphy->identify; |
94 | identify->sas_address = pqi_sas_port->sas_address; |
95 | identify->phy_identifier = pqi_sas_port->device->phy_id; |
96 | |
97 | identify->initiator_port_protocols = SAS_PROTOCOL_ALL; |
98 | identify->target_port_protocols = SAS_PROTOCOL_STP; |
99 | |
100 | switch (pqi_sas_port->device->device_type) { |
101 | case SA_DEVICE_TYPE_SAS: |
102 | case SA_DEVICE_TYPE_SES: |
103 | case SA_DEVICE_TYPE_NVME: |
104 | identify->target_port_protocols = SAS_PROTOCOL_SSP; |
105 | break; |
106 | case SA_DEVICE_TYPE_EXPANDER_SMP: |
107 | identify->target_port_protocols = SAS_PROTOCOL_SMP; |
108 | break; |
109 | case SA_DEVICE_TYPE_SATA: |
110 | default: |
111 | break; |
112 | } |
113 | |
114 | return sas_rphy_add(rphy); |
115 | } |
116 | |
117 | static struct sas_rphy *pqi_sas_rphy_alloc(struct pqi_sas_port *pqi_sas_port) |
118 | { |
119 | if (pqi_sas_port->device && pqi_sas_port->device->is_expander_smp_device) |
120 | return sas_expander_alloc(pqi_sas_port->port, |
121 | SAS_FANOUT_EXPANDER_DEVICE); |
122 | |
123 | return sas_end_device_alloc(pqi_sas_port->port); |
124 | } |
125 | |
126 | static struct pqi_sas_port *pqi_alloc_sas_port( |
127 | struct pqi_sas_node *pqi_sas_node, u64 sas_address, |
128 | struct pqi_scsi_dev *device) |
129 | { |
130 | int rc; |
131 | struct pqi_sas_port *pqi_sas_port; |
132 | struct sas_port *port; |
133 | |
134 | pqi_sas_port = kzalloc(size: sizeof(*pqi_sas_port), GFP_KERNEL); |
135 | if (!pqi_sas_port) |
136 | return NULL; |
137 | |
138 | INIT_LIST_HEAD(list: &pqi_sas_port->phy_list_head); |
139 | pqi_sas_port->parent_node = pqi_sas_node; |
140 | |
141 | port = sas_port_alloc_num(pqi_sas_node->parent_dev); |
142 | if (!port) |
143 | goto free_pqi_port; |
144 | |
145 | rc = sas_port_add(port); |
146 | if (rc) |
147 | goto free_sas_port; |
148 | |
149 | pqi_sas_port->port = port; |
150 | pqi_sas_port->sas_address = sas_address; |
151 | pqi_sas_port->device = device; |
152 | list_add_tail(new: &pqi_sas_port->port_list_entry, |
153 | head: &pqi_sas_node->port_list_head); |
154 | |
155 | return pqi_sas_port; |
156 | |
157 | free_sas_port: |
158 | sas_port_free(port); |
159 | free_pqi_port: |
160 | kfree(objp: pqi_sas_port); |
161 | |
162 | return NULL; |
163 | } |
164 | |
165 | static void pqi_free_sas_port(struct pqi_sas_port *pqi_sas_port) |
166 | { |
167 | struct pqi_sas_phy *pqi_sas_phy; |
168 | struct pqi_sas_phy *next; |
169 | |
170 | list_for_each_entry_safe(pqi_sas_phy, next, |
171 | &pqi_sas_port->phy_list_head, phy_list_entry) |
172 | pqi_free_sas_phy(pqi_sas_phy); |
173 | |
174 | sas_port_delete(pqi_sas_port->port); |
175 | list_del(entry: &pqi_sas_port->port_list_entry); |
176 | kfree(objp: pqi_sas_port); |
177 | } |
178 | |
179 | static struct pqi_sas_node *pqi_alloc_sas_node(struct device *parent_dev) |
180 | { |
181 | struct pqi_sas_node *pqi_sas_node; |
182 | |
183 | pqi_sas_node = kzalloc(size: sizeof(*pqi_sas_node), GFP_KERNEL); |
184 | if (pqi_sas_node) { |
185 | pqi_sas_node->parent_dev = parent_dev; |
186 | INIT_LIST_HEAD(list: &pqi_sas_node->port_list_head); |
187 | } |
188 | |
189 | return pqi_sas_node; |
190 | } |
191 | |
192 | static void pqi_free_sas_node(struct pqi_sas_node *pqi_sas_node) |
193 | { |
194 | struct pqi_sas_port *pqi_sas_port; |
195 | struct pqi_sas_port *next; |
196 | |
197 | if (!pqi_sas_node) |
198 | return; |
199 | |
200 | list_for_each_entry_safe(pqi_sas_port, next, |
201 | &pqi_sas_node->port_list_head, port_list_entry) |
202 | pqi_free_sas_port(pqi_sas_port); |
203 | |
204 | kfree(objp: pqi_sas_node); |
205 | } |
206 | |
207 | struct pqi_scsi_dev *pqi_find_device_by_sas_rphy( |
208 | struct pqi_ctrl_info *ctrl_info, struct sas_rphy *rphy) |
209 | { |
210 | struct pqi_scsi_dev *device; |
211 | |
212 | list_for_each_entry(device, &ctrl_info->scsi_device_list, |
213 | scsi_device_list_entry) { |
214 | if (!device->sas_port) |
215 | continue; |
216 | if (device->sas_port->rphy == rphy) |
217 | return device; |
218 | } |
219 | |
220 | return NULL; |
221 | } |
222 | |
223 | int pqi_add_sas_host(struct Scsi_Host *shost, struct pqi_ctrl_info *ctrl_info) |
224 | { |
225 | int rc; |
226 | struct device *parent_dev; |
227 | struct pqi_sas_node *pqi_sas_node; |
228 | struct pqi_sas_port *pqi_sas_port; |
229 | struct pqi_sas_phy *pqi_sas_phy; |
230 | |
231 | parent_dev = &shost->shost_dev; |
232 | |
233 | pqi_sas_node = pqi_alloc_sas_node(parent_dev); |
234 | if (!pqi_sas_node) |
235 | return -ENOMEM; |
236 | |
237 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, |
238 | sas_address: ctrl_info->sas_address, NULL); |
239 | if (!pqi_sas_port) { |
240 | rc = -ENODEV; |
241 | goto free_sas_node; |
242 | } |
243 | |
244 | pqi_sas_phy = pqi_alloc_sas_phy(pqi_sas_port); |
245 | if (!pqi_sas_phy) { |
246 | rc = -ENODEV; |
247 | goto free_sas_port; |
248 | } |
249 | |
250 | rc = pqi_sas_port_add_phy(pqi_sas_phy); |
251 | if (rc) |
252 | goto free_sas_phy; |
253 | |
254 | ctrl_info->sas_host = pqi_sas_node; |
255 | |
256 | return 0; |
257 | |
258 | free_sas_phy: |
259 | pqi_free_sas_phy(pqi_sas_phy); |
260 | free_sas_port: |
261 | pqi_free_sas_port(pqi_sas_port); |
262 | free_sas_node: |
263 | pqi_free_sas_node(pqi_sas_node); |
264 | |
265 | return rc; |
266 | } |
267 | |
268 | void pqi_delete_sas_host(struct pqi_ctrl_info *ctrl_info) |
269 | { |
270 | pqi_free_sas_node(pqi_sas_node: ctrl_info->sas_host); |
271 | } |
272 | |
273 | int pqi_add_sas_device(struct pqi_sas_node *pqi_sas_node, |
274 | struct pqi_scsi_dev *device) |
275 | { |
276 | int rc; |
277 | struct pqi_sas_port *pqi_sas_port; |
278 | struct sas_rphy *rphy; |
279 | |
280 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, |
281 | sas_address: device->sas_address, device); |
282 | if (!pqi_sas_port) |
283 | return -ENOMEM; |
284 | |
285 | rphy = pqi_sas_rphy_alloc(pqi_sas_port); |
286 | if (!rphy) { |
287 | rc = -ENODEV; |
288 | goto free_sas_port; |
289 | } |
290 | |
291 | pqi_sas_port->rphy = rphy; |
292 | device->sas_port = pqi_sas_port; |
293 | |
294 | rc = pqi_sas_port_add_rphy(pqi_sas_port, rphy); |
295 | if (rc) |
296 | goto free_sas_rphy; |
297 | |
298 | return 0; |
299 | |
300 | free_sas_rphy: |
301 | sas_rphy_free(rphy); |
302 | free_sas_port: |
303 | pqi_free_sas_port(pqi_sas_port); |
304 | device->sas_port = NULL; |
305 | |
306 | return rc; |
307 | } |
308 | |
309 | void pqi_remove_sas_device(struct pqi_scsi_dev *device) |
310 | { |
311 | if (device->sas_port) { |
312 | pqi_free_sas_port(pqi_sas_port: device->sas_port); |
313 | device->sas_port = NULL; |
314 | } |
315 | } |
316 | |
317 | static int pqi_sas_get_linkerrors(struct sas_phy *phy) |
318 | { |
319 | return 0; |
320 | } |
321 | |
322 | static int pqi_sas_get_enclosure_identifier(struct sas_rphy *rphy, |
323 | u64 *identifier) |
324 | { |
325 | int rc; |
326 | unsigned long flags; |
327 | struct Scsi_Host *shost; |
328 | struct pqi_ctrl_info *ctrl_info; |
329 | struct pqi_scsi_dev *found_device; |
330 | struct pqi_scsi_dev *device; |
331 | |
332 | if (!rphy) |
333 | return -ENODEV; |
334 | |
335 | shost = rphy_to_shost(rphy); |
336 | ctrl_info = shost_to_hba(shost); |
337 | spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); |
338 | found_device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); |
339 | |
340 | if (!found_device) { |
341 | rc = -ENODEV; |
342 | goto out; |
343 | } |
344 | |
345 | if (found_device->devtype == TYPE_ENCLOSURE) { |
346 | *identifier = get_unaligned_be64(p: &found_device->wwid[8]); |
347 | rc = 0; |
348 | goto out; |
349 | } |
350 | |
351 | if (found_device->box_index == 0xff || |
352 | found_device->phys_box_on_bus == 0 || |
353 | found_device->bay == 0xff) { |
354 | rc = -EINVAL; |
355 | goto out; |
356 | } |
357 | |
358 | list_for_each_entry(device, &ctrl_info->scsi_device_list, |
359 | scsi_device_list_entry) { |
360 | if (device->devtype == TYPE_ENCLOSURE && |
361 | device->box_index == found_device->box_index && |
362 | device->phys_box_on_bus == |
363 | found_device->phys_box_on_bus && |
364 | memcmp(p: device->phys_connector, |
365 | q: found_device->phys_connector, size: 2) == 0) { |
366 | *identifier = |
367 | get_unaligned_be64(p: &device->wwid[8]); |
368 | rc = 0; |
369 | goto out; |
370 | } |
371 | } |
372 | |
373 | if (found_device->phy_connected_dev_type != SA_DEVICE_TYPE_CONTROLLER) { |
374 | rc = -EINVAL; |
375 | goto out; |
376 | } |
377 | |
378 | list_for_each_entry(device, &ctrl_info->scsi_device_list, |
379 | scsi_device_list_entry) { |
380 | if (device->devtype == TYPE_ENCLOSURE && |
381 | CISS_GET_DRIVE_NUMBER(device->scsi3addr) == |
382 | PQI_VSEP_CISS_BTL) { |
383 | *identifier = get_unaligned_be64(p: &device->wwid[8]); |
384 | rc = 0; |
385 | goto out; |
386 | } |
387 | } |
388 | |
389 | rc = -EINVAL; |
390 | out: |
391 | spin_unlock_irqrestore(lock: &ctrl_info->scsi_device_list_lock, flags); |
392 | |
393 | return rc; |
394 | } |
395 | |
396 | static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) |
397 | { |
398 | int rc; |
399 | unsigned long flags; |
400 | struct pqi_ctrl_info *ctrl_info; |
401 | struct pqi_scsi_dev *device; |
402 | struct Scsi_Host *shost; |
403 | |
404 | if (!rphy) |
405 | return -ENODEV; |
406 | |
407 | shost = rphy_to_shost(rphy); |
408 | ctrl_info = shost_to_hba(shost); |
409 | spin_lock_irqsave(&ctrl_info->scsi_device_list_lock, flags); |
410 | device = pqi_find_device_by_sas_rphy(ctrl_info, rphy); |
411 | |
412 | if (!device) { |
413 | rc = -ENODEV; |
414 | goto out; |
415 | } |
416 | |
417 | if (device->bay == 0xff) |
418 | rc = -EINVAL; |
419 | else |
420 | rc = device->bay; |
421 | |
422 | out: |
423 | spin_unlock_irqrestore(lock: &ctrl_info->scsi_device_list_lock, flags); |
424 | |
425 | return rc; |
426 | } |
427 | |
428 | static int pqi_sas_phy_reset(struct sas_phy *phy, int hard_reset) |
429 | { |
430 | return 0; |
431 | } |
432 | |
433 | static int pqi_sas_phy_enable(struct sas_phy *phy, int enable) |
434 | { |
435 | return 0; |
436 | } |
437 | |
438 | static int pqi_sas_phy_setup(struct sas_phy *phy) |
439 | { |
440 | return 0; |
441 | } |
442 | |
443 | static void pqi_sas_phy_release(struct sas_phy *phy) |
444 | { |
445 | } |
446 | |
447 | static int pqi_sas_phy_speed(struct sas_phy *phy, |
448 | struct sas_phy_linkrates *rates) |
449 | { |
450 | return -EINVAL; |
451 | } |
452 | |
453 | #define CSMI_IOCTL_TIMEOUT 60 |
454 | #define SMP_CRC_FIELD_LENGTH 4 |
455 | |
456 | static struct bmic_csmi_smp_passthru_buffer * |
457 | pqi_build_csmi_smp_passthru_buffer(struct sas_rphy *rphy, |
458 | struct bsg_job *job) |
459 | { |
460 | struct bmic_csmi_smp_passthru_buffer *smp_buf; |
461 | struct bmic_csmi_ioctl_header *; |
462 | struct bmic_csmi_smp_passthru *parameters; |
463 | u32 req_size; |
464 | u32 resp_size; |
465 | |
466 | smp_buf = kzalloc(size: sizeof(*smp_buf), GFP_KERNEL); |
467 | if (!smp_buf) |
468 | return NULL; |
469 | |
470 | req_size = job->request_payload.payload_len; |
471 | resp_size = job->reply_payload.payload_len; |
472 | |
473 | ioctl_header = &smp_buf->ioctl_header; |
474 | put_unaligned_le32(val: sizeof(smp_buf->ioctl_header), |
475 | p: &ioctl_header->header_length); |
476 | put_unaligned_le32(CSMI_IOCTL_TIMEOUT, p: &ioctl_header->timeout); |
477 | put_unaligned_le32(CSMI_CC_SAS_SMP_PASSTHRU, |
478 | p: &ioctl_header->control_code); |
479 | put_unaligned_le32(val: sizeof(smp_buf->parameters), p: &ioctl_header->length); |
480 | |
481 | parameters = &smp_buf->parameters; |
482 | parameters->phy_identifier = rphy->identify.phy_identifier; |
483 | parameters->port_identifier = 0; |
484 | parameters->connection_rate = 0; |
485 | put_unaligned_be64(val: rphy->identify.sas_address, |
486 | p: ¶meters->destination_sas_address); |
487 | |
488 | if (req_size > SMP_CRC_FIELD_LENGTH) |
489 | req_size -= SMP_CRC_FIELD_LENGTH; |
490 | |
491 | put_unaligned_le32(val: req_size, p: ¶meters->request_length); |
492 | put_unaligned_le32(val: resp_size, p: ¶meters->response_length); |
493 | |
494 | sg_copy_to_buffer(sgl: job->request_payload.sg_list, |
495 | nents: job->reply_payload.sg_cnt, buf: ¶meters->request, |
496 | buflen: req_size); |
497 | |
498 | return smp_buf; |
499 | } |
500 | |
501 | static unsigned int pqi_build_sas_smp_handler_reply( |
502 | struct bmic_csmi_smp_passthru_buffer *smp_buf, struct bsg_job *job, |
503 | struct pqi_raid_error_info *error_info) |
504 | { |
505 | sg_copy_from_buffer(sgl: job->reply_payload.sg_list, |
506 | nents: job->reply_payload.sg_cnt, buf: &smp_buf->parameters.response, |
507 | le32_to_cpu(smp_buf->parameters.response_length)); |
508 | |
509 | job->reply_len = le16_to_cpu(error_info->sense_data_length); |
510 | memcpy(job->reply, error_info->data, |
511 | le16_to_cpu(error_info->sense_data_length)); |
512 | |
513 | return job->reply_payload.payload_len - |
514 | get_unaligned_le32(p: &error_info->data_in_transferred); |
515 | } |
516 | |
517 | void pqi_sas_smp_handler(struct bsg_job *job, struct Scsi_Host *shost, |
518 | struct sas_rphy *rphy) |
519 | { |
520 | int rc; |
521 | struct pqi_ctrl_info *ctrl_info; |
522 | struct bmic_csmi_smp_passthru_buffer *smp_buf; |
523 | struct pqi_raid_error_info error_info; |
524 | unsigned int reslen = 0; |
525 | |
526 | ctrl_info = shost_to_hba(shost); |
527 | |
528 | if (job->reply_payload.payload_len == 0) { |
529 | rc = -ENOMEM; |
530 | goto out; |
531 | } |
532 | |
533 | if (!rphy) { |
534 | rc = -EINVAL; |
535 | goto out; |
536 | } |
537 | |
538 | if (rphy->identify.device_type != SAS_FANOUT_EXPANDER_DEVICE) { |
539 | rc = -EINVAL; |
540 | goto out; |
541 | } |
542 | |
543 | if (job->request_payload.sg_cnt > 1 || job->reply_payload.sg_cnt > 1) { |
544 | rc = -EINVAL; |
545 | goto out; |
546 | } |
547 | |
548 | smp_buf = pqi_build_csmi_smp_passthru_buffer(rphy, job); |
549 | if (!smp_buf) { |
550 | rc = -ENOMEM; |
551 | goto out; |
552 | } |
553 | |
554 | rc = pqi_csmi_smp_passthru(ctrl_info, buffer: smp_buf, buffer_length: sizeof(*smp_buf), |
555 | error_info: &error_info); |
556 | if (rc) |
557 | goto out; |
558 | |
559 | reslen = pqi_build_sas_smp_handler_reply(smp_buf, job, error_info: &error_info); |
560 | |
561 | out: |
562 | bsg_job_done(job, result: rc, reply_payload_rcv_len: reslen); |
563 | } |
564 | struct sas_function_template pqi_sas_transport_functions = { |
565 | .get_linkerrors = pqi_sas_get_linkerrors, |
566 | .get_enclosure_identifier = pqi_sas_get_enclosure_identifier, |
567 | .get_bay_identifier = pqi_sas_get_bay_identifier, |
568 | .phy_reset = pqi_sas_phy_reset, |
569 | .phy_enable = pqi_sas_phy_enable, |
570 | .phy_setup = pqi_sas_phy_setup, |
571 | .phy_release = pqi_sas_phy_release, |
572 | .set_phy_speed = pqi_sas_phy_speed, |
573 | .smp_handler = pqi_sas_smp_handler, |
574 | }; |
575 | |