Commit | Line | Data |
---|---|---|
2cc37b15 | 1 | // SPDX-License-Identifier: GPL-2.0 |
6c223761 | 2 | /* |
889653ec | 3 | * driver for Microchip PQI-based storage controllers |
49fd52d4 | 4 | * Copyright (c) 2019-2023 Microchip Technology Inc. and its subsidiaries |
2f4c4b92 | 5 | * Copyright (c) 2016-2018 Microsemi Corporation |
6c223761 KB |
6 | * Copyright (c) 2016 PMC-Sierra, Inc. |
7 | * | |
2f4c4b92 | 8 | * Questions/Comments/Bugfixes to storagedev@microchip.com |
6c223761 KB |
9 | * |
10 | */ | |
11 | ||
12 | #include <linux/kernel.h> | |
3d46a59a | 13 | #include <linux/bsg-lib.h> |
6c223761 KB |
14 | #include <scsi/scsi_host.h> |
15 | #include <scsi/scsi_cmnd.h> | |
16 | #include <scsi/scsi_transport_sas.h> | |
3d46a59a | 17 | #include <asm/unaligned.h> |
6c223761 KB |
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(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(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); | |
6c223761 KB |
48 | if (pqi_sas_phy->added_to_port) |
49 | list_del(&pqi_sas_phy->phy_list_entry); | |
b9692611 | 50 | sas_phy_delete(phy); |
6c223761 KB |
51 | kfree(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; | |
55732a46 MB |
68 | identify->initiator_port_protocols = SAS_PROTOCOL_ALL; |
69 | identify->target_port_protocols = SAS_PROTOCOL_ALL; | |
6c223761 KB |
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(&pqi_sas_phy->phy_list_entry, | |
82 | &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; | |
889cda36 | 95 | identify->phy_identifier = pqi_sas_port->device->phy_id; |
3d46a59a | 96 | |
55732a46 MB |
97 | identify->initiator_port_protocols = SAS_PROTOCOL_ALL; |
98 | identify->target_port_protocols = SAS_PROTOCOL_STP; | |
99 | ||
889cda36 KB |
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; | |
3d46a59a | 112 | } |
6c223761 KB |
113 | |
114 | return sas_rphy_add(rphy); | |
115 | } | |
116 | ||
3d46a59a DB |
117 | static struct sas_rphy *pqi_sas_rphy_alloc(struct pqi_sas_port *pqi_sas_port) |
118 | { | |
583891c9 | 119 | if (pqi_sas_port->device && pqi_sas_port->device->is_expander_smp_device) |
3d46a59a DB |
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 | ||
6c223761 | 126 | static struct pqi_sas_port *pqi_alloc_sas_port( |
3d46a59a DB |
127 | struct pqi_sas_node *pqi_sas_node, u64 sas_address, |
128 | struct pqi_scsi_dev *device) | |
6c223761 KB |
129 | { |
130 | int rc; | |
131 | struct pqi_sas_port *pqi_sas_port; | |
132 | struct sas_port *port; | |
133 | ||
134 | pqi_sas_port = kzalloc(sizeof(*pqi_sas_port), GFP_KERNEL); | |
135 | if (!pqi_sas_port) | |
136 | return NULL; | |
137 | ||
138 | INIT_LIST_HEAD(&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; | |
3d46a59a | 151 | pqi_sas_port->device = device; |
6c223761 KB |
152 | list_add_tail(&pqi_sas_port->port_list_entry, |
153 | &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(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, | |
3d46a59a | 171 | &pqi_sas_port->phy_list_head, phy_list_entry) |
583891c9 | 172 | pqi_free_sas_phy(pqi_sas_phy); |
6c223761 KB |
173 | |
174 | sas_port_delete(pqi_sas_port->port); | |
175 | list_del(&pqi_sas_port->port_list_entry); | |
176 | kfree(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(sizeof(*pqi_sas_node), GFP_KERNEL); | |
184 | if (pqi_sas_node) { | |
185 | pqi_sas_node->parent_dev = parent_dev; | |
186 | INIT_LIST_HEAD(&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, | |
3d46a59a | 201 | &pqi_sas_node->port_list_head, port_list_entry) |
583891c9 | 202 | pqi_free_sas_port(pqi_sas_port); |
6c223761 KB |
203 | |
204 | kfree(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 | ||
3d46a59a | 231 | parent_dev = &shost->shost_dev; |
6c223761 KB |
232 | |
233 | pqi_sas_node = pqi_alloc_sas_node(parent_dev); | |
234 | if (!pqi_sas_node) | |
235 | return -ENOMEM; | |
236 | ||
3d46a59a DB |
237 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, |
238 | ctrl_info->sas_address, NULL); | |
6c223761 KB |
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(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 | ||
3d46a59a DB |
280 | pqi_sas_port = pqi_alloc_sas_port(pqi_sas_node, |
281 | device->sas_address, device); | |
6c223761 KB |
282 | if (!pqi_sas_port) |
283 | return -ENOMEM; | |
284 | ||
3d46a59a | 285 | rphy = pqi_sas_rphy_alloc(pqi_sas_port); |
6c223761 KB |
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) | |
2312e844 | 296 | goto free_sas_rphy; |
6c223761 KB |
297 | |
298 | return 0; | |
299 | ||
2312e844 DB |
300 | free_sas_rphy: |
301 | sas_rphy_free(rphy); | |
6c223761 KB |
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(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 | { | |
2d2ad4bc GW |
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) { | |
28ca6d87 | 346 | *identifier = get_unaligned_be64(&found_device->wwid[8]); |
2d2ad4bc GW |
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(device->phys_connector, | |
365 | found_device->phys_connector, 2) == 0) { | |
366 | *identifier = | |
28ca6d87 | 367 | get_unaligned_be64(&device->wwid[8]); |
2d2ad4bc GW |
368 | rc = 0; |
369 | goto out; | |
370 | } | |
371 | } | |
372 | ||
694c5d5b | 373 | if (found_device->phy_connected_dev_type != SA_DEVICE_TYPE_CONTROLLER) { |
2d2ad4bc GW |
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) { | |
28ca6d87 | 383 | *identifier = get_unaligned_be64(&device->wwid[8]); |
2d2ad4bc GW |
384 | rc = 0; |
385 | goto out; | |
386 | } | |
387 | } | |
388 | ||
389 | rc = -EINVAL; | |
390 | out: | |
391 | spin_unlock_irqrestore(&ctrl_info->scsi_device_list_lock, flags); | |
392 | ||
393 | return rc; | |
6c223761 KB |
394 | } |
395 | ||
396 | static int pqi_sas_get_bay_identifier(struct sas_rphy *rphy) | |
397 | { | |
2d2ad4bc GW |
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(&ctrl_info->scsi_device_list_lock, flags); | |
424 | ||
425 | return rc; | |
6c223761 KB |
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 | ||
3d46a59a DB |
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 *ioctl_header; | |
462 | struct bmic_csmi_smp_passthru *parameters; | |
463 | u32 req_size; | |
464 | u32 resp_size; | |
465 | ||
466 | smp_buf = kzalloc(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(sizeof(smp_buf->ioctl_header), | |
475 | &ioctl_header->header_length); | |
476 | put_unaligned_le32(CSMI_IOCTL_TIMEOUT, &ioctl_header->timeout); | |
477 | put_unaligned_le32(CSMI_CC_SAS_SMP_PASSTHRU, | |
478 | &ioctl_header->control_code); | |
479 | put_unaligned_le32(sizeof(smp_buf->parameters), &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(rphy->identify.sas_address, | |
486 | ¶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(req_size, ¶meters->request_length); | |
3d46a59a DB |
492 | put_unaligned_le32(resp_size, ¶meters->response_length); |
493 | ||
494 | sg_copy_to_buffer(job->request_payload.sg_list, | |
495 | job->reply_payload.sg_cnt, ¶meters->request, | |
496 | 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(job->reply_payload.sg_list, | |
506 | job->reply_payload.sg_cnt, &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, | |
583891c9 | 511 | le16_to_cpu(error_info->sense_data_length)); |
3d46a59a DB |
512 | |
513 | return job->reply_payload.payload_len - | |
514 | get_unaligned_le32(&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; | |
694c5d5b | 521 | struct pqi_ctrl_info *ctrl_info; |
3d46a59a DB |
522 | struct bmic_csmi_smp_passthru_buffer *smp_buf; |
523 | struct pqi_raid_error_info error_info; | |
524 | unsigned int reslen = 0; | |
525 | ||
694c5d5b | 526 | ctrl_info = shost_to_hba(shost); |
3d46a59a DB |
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 | ||
3d46a59a DB |
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, smp_buf, sizeof(*smp_buf), | |
555 | &error_info); | |
556 | if (rc) | |
557 | goto out; | |
558 | ||
559 | reslen = pqi_build_sas_smp_handler_reply(smp_buf, job, &error_info); | |
583891c9 | 560 | |
3d46a59a DB |
561 | out: |
562 | bsg_job_done(job, rc, reslen); | |
3d46a59a | 563 | } |
6c223761 KB |
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, | |
3d46a59a | 573 | .smp_handler = pqi_sas_smp_handler, |
6c223761 | 574 | }; |