aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/scsi/libsas
diff options
context:
space:
mode:
authorJames Bottomley <James.Bottomley@HansenPartnership.com>2006-08-22 13:39:19 -0400
committerJames Bottomley <jejb@mulgrave.localdomain>2007-07-18 12:13:23 -0400
commit1acce1942a32296f7c25ba82776c97e9c04c8e1e (patch)
tree8ac68e474c3c56aab23435e91bcffd96ee8d9bde /drivers/scsi/libsas
parentfa1c1e8f1ece48c7baa3ba529bfd0d10a0bdf4eb (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')
-rw-r--r--drivers/scsi/libsas/sas_expander.c81
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
223static 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
223static int sas_ex_phy_discover(struct domain_device *dev, int single) 253static 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 }
262out_err: 284out_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
546static void sas_ex_get_linkrate(struct domain_device *parent, 589static 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;