diff options
author | James Bottomley <James.Bottomley@HansenPartnership.com> | 2006-08-22 13:39:19 -0400 |
---|---|---|
committer | James Bottomley <jejb@mulgrave.localdomain> | 2007-07-18 12:13:23 -0400 |
commit | 1acce1942a32296f7c25ba82776c97e9c04c8e1e (patch) | |
tree | 8ac68e474c3c56aab23435e91bcffd96ee8d9bde /drivers/scsi/libsas/sas_expander.c | |
parent | fa1c1e8f1ece48c7baa3ba529bfd0d10a0bdf4eb (diff) |
[SCSI] libsas: Add SATA support to STP piece for SATA on SAS expanders
This patch adds support for SATA over SAS expanders to the previous two
SATA support in libsas patches.
There were a couple of nasty non trivial things to sort out before this
one could be made to work.
Firstly, I'd like to thank Doug Gilbert for diagnosing a problem with
the LSI expanders where the REPORT_SATA_PHY command was returning the
D2H FIS in the wrong order (Although, here, I think I have to blame the
SAS standards which specifies the FIS "shall be returned in little
endian format" and later on "which means resp[24] shall be FIS type"
The latter, of course, implying big endian format). Just to make sure,
I put a check for the D2H FIS type being in the wrong position and
reverse the FIS data if it is.
The second is a problem outlined in Annex G of the SAS standard (again,
a technical point with D2H FIS ... necessitating a phy reset on certain
conditions).
With the patch, I can now see my SATA-1 disk in a cascaded expander
configuration.
Signed-off-by: James Bottomley <James.Bottomley@SteelEye.com>
Diffstat (limited to 'drivers/scsi/libsas/sas_expander.c')
-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; |