diff options
-rw-r--r-- | drivers/scsi/libsas/sas_expander.c | 81 |
1 files changed, 68 insertions, 13 deletions
diff --git a/drivers/scsi/libsas/sas_expander.c b/drivers/scsi/libsas/sas_expander.c index 23e90c5f8f35..0c4e3a977915 100644 --- a/drivers/scsi/libsas/sas_expander.c +++ b/drivers/scsi/libsas/sas_expander.c | |||
@@ -220,6 +220,36 @@ static void sas_set_ex_phy(struct domain_device *dev, int phy_id, | |||
220 | #define DISCOVER_REQ_SIZE 16 | 220 | #define DISCOVER_REQ_SIZE 16 |
221 | #define DISCOVER_RESP_SIZE 56 | 221 | #define DISCOVER_RESP_SIZE 56 |
222 | 222 | ||
223 | static int sas_ex_phy_discover_helper(struct domain_device *dev, u8 *disc_req, | ||
224 | u8 *disc_resp, int single) | ||
225 | { | ||
226 | int i, res; | ||
227 | |||
228 | disc_req[9] = single; | ||
229 | for (i = 1 ; i < 3; i++) { | ||
230 | struct discover_resp *dr; | ||
231 | |||
232 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | ||
233 | disc_resp, DISCOVER_RESP_SIZE); | ||
234 | if (res) | ||
235 | return res; | ||
236 | /* This is detecting a failure to transmit inital | ||
237 | * dev to host FIS as described in section G.5 of | ||
238 | * sas-2 r 04b */ | ||
239 | dr = &((struct smp_resp *)disc_resp)->disc; | ||
240 | if (!(dr->attached_dev_type == 0 && | ||
241 | dr->attached_sata_dev)) | ||
242 | break; | ||
243 | /* In order to generate the dev to host FIS, we | ||
244 | * send a link reset to the expander port */ | ||
245 | sas_smp_phy_control(dev, single, PHY_FUNC_LINK_RESET); | ||
246 | /* Wait for the reset to trigger the negotiation */ | ||
247 | msleep(500); | ||
248 | } | ||
249 | sas_set_ex_phy(dev, single, disc_resp); | ||
250 | return 0; | ||
251 | } | ||
252 | |||
223 | static int sas_ex_phy_discover(struct domain_device *dev, int single) | 253 | static int sas_ex_phy_discover(struct domain_device *dev, int single) |
224 | { | 254 | { |
225 | struct expander_device *ex = &dev->ex_dev; | 255 | struct expander_device *ex = &dev->ex_dev; |
@@ -240,23 +270,15 @@ static int sas_ex_phy_discover(struct domain_device *dev, int single) | |||
240 | disc_req[1] = SMP_DISCOVER; | 270 | disc_req[1] = SMP_DISCOVER; |
241 | 271 | ||
242 | if (0 <= single && single < ex->num_phys) { | 272 | if (0 <= single && single < ex->num_phys) { |
243 | disc_req[9] = single; | 273 | res = sas_ex_phy_discover_helper(dev, disc_req, disc_resp, single); |
244 | res = smp_execute_task(dev, disc_req, DISCOVER_REQ_SIZE, | ||
245 | disc_resp, DISCOVER_RESP_SIZE); | ||
246 | if (res) | ||
247 | goto out_err; | ||
248 | sas_set_ex_phy(dev, single, disc_resp); | ||
249 | } else { | 274 | } else { |
250 | int i; | 275 | int i; |
251 | 276 | ||
252 | for (i = 0; i < ex->num_phys; i++) { | 277 | for (i = 0; i < ex->num_phys; i++) { |
253 | disc_req[9] = i; | 278 | res = sas_ex_phy_discover_helper(dev, disc_req, |
254 | res = smp_execute_task(dev, disc_req, | 279 | disc_resp, i); |
255 | DISCOVER_REQ_SIZE, disc_resp, | ||
256 | DISCOVER_RESP_SIZE); | ||
257 | if (res) | 280 | if (res) |
258 | goto out_err; | 281 | goto out_err; |
259 | sas_set_ex_phy(dev, i, disc_resp); | ||
260 | } | 282 | } |
261 | } | 283 | } |
262 | out_err: | 284 | out_err: |
@@ -529,6 +551,7 @@ static int sas_get_report_phy_sata(struct domain_device *dev, | |||
529 | { | 551 | { |
530 | int res; | 552 | int res; |
531 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); | 553 | u8 *rps_req = alloc_smp_req(RPS_REQ_SIZE); |
554 | u8 *resp = (u8 *)rps_resp; | ||
532 | 555 | ||
533 | if (!rps_req) | 556 | if (!rps_req) |
534 | return -ENOMEM; | 557 | return -ENOMEM; |
@@ -539,8 +562,28 @@ static int sas_get_report_phy_sata(struct domain_device *dev, | |||
539 | res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, | 562 | res = smp_execute_task(dev, rps_req, RPS_REQ_SIZE, |
540 | rps_resp, RPS_RESP_SIZE); | 563 | rps_resp, RPS_RESP_SIZE); |
541 | 564 | ||
565 | /* 0x34 is the FIS type for the D2H fis. There's a potential | ||
566 | * standards cockup here. sas-2 explicitly specifies the FIS | ||
567 | * should be encoded so that FIS type is in resp[24]. | ||
568 | * However, some expanders endian reverse this. Undo the | ||
569 | * reversal here */ | ||
570 | if (!res && resp[27] == 0x34 && resp[24] != 0x34) { | ||
571 | int i; | ||
572 | |||
573 | for (i = 0; i < 5; i++) { | ||
574 | int j = 24 + (i*4); | ||
575 | u8 a, b; | ||
576 | a = resp[j + 0]; | ||
577 | b = resp[j + 1]; | ||
578 | resp[j + 0] = resp[j + 3]; | ||
579 | resp[j + 1] = resp[j + 2]; | ||
580 | resp[j + 2] = b; | ||
581 | resp[j + 3] = a; | ||
582 | } | ||
583 | } | ||
584 | |||
542 | kfree(rps_req); | 585 | kfree(rps_req); |
543 | return 0; | 586 | return res; |
544 | } | 587 | } |
545 | 588 | ||
546 | static void sas_ex_get_linkrate(struct domain_device *parent, | 589 | static void sas_ex_get_linkrate(struct domain_device *parent, |
@@ -625,14 +668,26 @@ static struct domain_device *sas_ex_discover_end_dev( | |||
625 | } | 668 | } |
626 | memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, | 669 | memcpy(child->frame_rcvd, &child->sata_dev.rps_resp.rps.fis, |
627 | sizeof(struct dev_to_host_fis)); | 670 | sizeof(struct dev_to_host_fis)); |
671 | |||
672 | rphy = sas_end_device_alloc(phy->port); | ||
673 | /* FIXME: error handling */ | ||
674 | BUG_ON(!rphy); | ||
675 | |||
628 | sas_init_dev(child); | 676 | sas_init_dev(child); |
677 | |||
678 | child->rphy = rphy; | ||
679 | |||
680 | spin_lock(&parent->port->dev_list_lock); | ||
681 | list_add_tail(&child->dev_list_node, &parent->port->dev_list); | ||
682 | spin_unlock(&parent->port->dev_list_lock); | ||
683 | |||
629 | res = sas_discover_sata(child); | 684 | res = sas_discover_sata(child); |
630 | if (res) { | 685 | if (res) { |
631 | SAS_DPRINTK("sas_discover_sata() for device %16llx at " | 686 | SAS_DPRINTK("sas_discover_sata() for device %16llx at " |
632 | "%016llx:0x%x returned 0x%x\n", | 687 | "%016llx:0x%x returned 0x%x\n", |
633 | SAS_ADDR(child->sas_addr), | 688 | SAS_ADDR(child->sas_addr), |
634 | SAS_ADDR(parent->sas_addr), phy_id, res); | 689 | SAS_ADDR(parent->sas_addr), phy_id, res); |
635 | goto out_free; | 690 | goto out_list_del; |
636 | } | 691 | } |
637 | } else if (phy->attached_tproto & SAS_PROTO_SSP) { | 692 | } else if (phy->attached_tproto & SAS_PROTO_SSP) { |
638 | child->dev_type = SAS_END_DEV; | 693 | child->dev_type = SAS_END_DEV; |