diff options
author | Mark Lord <liml@rtr.ca> | 2008-04-16 14:59:07 -0400 |
---|---|---|
committer | Jeff Garzik <jgarzik@redhat.com> | 2008-04-17 15:56:19 -0400 |
commit | e49856d82a887ce365637176f9f99ab68076eae8 (patch) | |
tree | 1b50c22bf1b3975eb5c40a5ae70c3b3be5cd32c0 /drivers/ata | |
parent | 02c1f32f1c524d2a389989f2482121f7c7d9b164 (diff) |
sata_mv add basic port multiplier support
Add basic port-multiplier support to sata_mv.
This works in Command-based-switching mode for Gen-II chipsets,
and in FIS-based-switching mode for Gen-IIe chipsets.
Error handling remains at the primary port level for now
(works okay, but not great). This will get fixed in a subsequent
patch series for IRQ/EH handling fixes. There are also some
known NCQ/PMP errata to be dealt with in the near future,
once we have this basic PMP support in place.
Signed-off-by: Mark Lord <mlord@pobox.com>
Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers/ata')
-rw-r--r-- | drivers/ata/sata_mv.c | 93 |
1 files changed, 89 insertions, 4 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c index 6a7420546609..b822b8a40c65 100644 --- a/drivers/ata/sata_mv.c +++ b/drivers/ata/sata_mv.c | |||
@@ -40,7 +40,7 @@ | |||
40 | 40 | ||
41 | 5) Investigate problems with PCI Message Signalled Interrupts (MSI). | 41 | 5) Investigate problems with PCI Message Signalled Interrupts (MSI). |
42 | 42 | ||
43 | 6) Add port multiplier support (intermediate) | 43 | 6) Cache frequently-accessed registers in mv_port_priv to reduce overhead. |
44 | 44 | ||
45 | 7) Fix/reenable hot plug/unplug (should happen as a side-effect of (2) above). | 45 | 7) Fix/reenable hot plug/unplug (should happen as a side-effect of (2) above). |
46 | 46 | ||
@@ -528,6 +528,12 @@ static int mv_stop_edma(struct ata_port *ap); | |||
528 | static int mv_stop_edma_engine(void __iomem *port_mmio); | 528 | static int mv_stop_edma_engine(void __iomem *port_mmio); |
529 | static void mv_edma_cfg(struct ata_port *ap, int want_ncq); | 529 | static void mv_edma_cfg(struct ata_port *ap, int want_ncq); |
530 | 530 | ||
531 | static void mv_pmp_select(struct ata_port *ap, int pmp); | ||
532 | static int mv_pmp_hardreset(struct ata_link *link, unsigned int *class, | ||
533 | unsigned long deadline); | ||
534 | static int mv_softreset(struct ata_link *link, unsigned int *class, | ||
535 | unsigned long deadline); | ||
536 | |||
531 | /* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below | 537 | /* .sg_tablesize is (MV_MAX_SG_CT / 2) in the structures below |
532 | * because we have to allow room for worst case splitting of | 538 | * because we have to allow room for worst case splitting of |
533 | * PRDs for 64K boundaries in mv_fill_sg(). | 539 | * PRDs for 64K boundaries in mv_fill_sg(). |
@@ -566,14 +572,20 @@ static struct ata_port_operations mv5_ops = { | |||
566 | 572 | ||
567 | static struct ata_port_operations mv6_ops = { | 573 | static struct ata_port_operations mv6_ops = { |
568 | .inherits = &mv5_ops, | 574 | .inherits = &mv5_ops, |
569 | .qc_defer = ata_std_qc_defer, | 575 | .qc_defer = sata_pmp_qc_defer_cmd_switch, |
570 | .dev_config = mv6_dev_config, | 576 | .dev_config = mv6_dev_config, |
571 | .scr_read = mv_scr_read, | 577 | .scr_read = mv_scr_read, |
572 | .scr_write = mv_scr_write, | 578 | .scr_write = mv_scr_write, |
579 | |||
580 | .pmp_hardreset = mv_pmp_hardreset, | ||
581 | .pmp_softreset = mv_softreset, | ||
582 | .softreset = mv_softreset, | ||
583 | .error_handler = sata_pmp_error_handler, | ||
573 | }; | 584 | }; |
574 | 585 | ||
575 | static struct ata_port_operations mv_iie_ops = { | 586 | static struct ata_port_operations mv_iie_ops = { |
576 | .inherits = &mv6_ops, | 587 | .inherits = &mv6_ops, |
588 | .qc_defer = ata_std_qc_defer, /* FIS-based switching */ | ||
577 | .dev_config = ATA_OP_NULL, | 589 | .dev_config = ATA_OP_NULL, |
578 | .qc_prep = mv_qc_prep_iie, | 590 | .qc_prep = mv_qc_prep_iie, |
579 | }; | 591 | }; |
@@ -599,6 +611,7 @@ static const struct ata_port_info mv_port_info[] = { | |||
599 | }, | 611 | }, |
600 | { /* chip_604x */ | 612 | { /* chip_604x */ |
601 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | | 613 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | |
614 | ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA | | ||
602 | ATA_FLAG_NCQ, | 615 | ATA_FLAG_NCQ, |
603 | .pio_mask = 0x1f, /* pio0-4 */ | 616 | .pio_mask = 0x1f, /* pio0-4 */ |
604 | .udma_mask = ATA_UDMA6, | 617 | .udma_mask = ATA_UDMA6, |
@@ -606,6 +619,7 @@ static const struct ata_port_info mv_port_info[] = { | |||
606 | }, | 619 | }, |
607 | { /* chip_608x */ | 620 | { /* chip_608x */ |
608 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | | 621 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | |
622 | ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA | | ||
609 | ATA_FLAG_NCQ | MV_FLAG_DUAL_HC, | 623 | ATA_FLAG_NCQ | MV_FLAG_DUAL_HC, |
610 | .pio_mask = 0x1f, /* pio0-4 */ | 624 | .pio_mask = 0x1f, /* pio0-4 */ |
611 | .udma_mask = ATA_UDMA6, | 625 | .udma_mask = ATA_UDMA6, |
@@ -613,6 +627,7 @@ static const struct ata_port_info mv_port_info[] = { | |||
613 | }, | 627 | }, |
614 | { /* chip_6042 */ | 628 | { /* chip_6042 */ |
615 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | | 629 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | |
630 | ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA | | ||
616 | ATA_FLAG_NCQ, | 631 | ATA_FLAG_NCQ, |
617 | .pio_mask = 0x1f, /* pio0-4 */ | 632 | .pio_mask = 0x1f, /* pio0-4 */ |
618 | .udma_mask = ATA_UDMA6, | 633 | .udma_mask = ATA_UDMA6, |
@@ -620,6 +635,7 @@ static const struct ata_port_info mv_port_info[] = { | |||
620 | }, | 635 | }, |
621 | { /* chip_7042 */ | 636 | { /* chip_7042 */ |
622 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | | 637 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | |
638 | ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA | | ||
623 | ATA_FLAG_NCQ, | 639 | ATA_FLAG_NCQ, |
624 | .pio_mask = 0x1f, /* pio0-4 */ | 640 | .pio_mask = 0x1f, /* pio0-4 */ |
625 | .udma_mask = ATA_UDMA6, | 641 | .udma_mask = ATA_UDMA6, |
@@ -627,6 +643,7 @@ static const struct ata_port_info mv_port_info[] = { | |||
627 | }, | 643 | }, |
628 | { /* chip_soc */ | 644 | { /* chip_soc */ |
629 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | | 645 | .flags = MV_COMMON_FLAGS | MV_6XXX_FLAGS | |
646 | ATA_FLAG_PMP | ATA_FLAG_ACPI_SATA | | ||
630 | ATA_FLAG_NCQ | MV_FLAG_SOC, | 647 | ATA_FLAG_NCQ | MV_FLAG_SOC, |
631 | .pio_mask = 0x1f, /* pio0-4 */ | 648 | .pio_mask = 0x1f, /* pio0-4 */ |
632 | .udma_mask = ATA_UDMA6, | 649 | .udma_mask = ATA_UDMA6, |
@@ -1006,12 +1023,42 @@ static int mv_scr_write(struct ata_port *ap, unsigned int sc_reg_in, u32 val) | |||
1006 | static void mv6_dev_config(struct ata_device *adev) | 1023 | static void mv6_dev_config(struct ata_device *adev) |
1007 | { | 1024 | { |
1008 | /* | 1025 | /* |
1026 | * Deal with Gen-II ("mv6") hardware quirks/restrictions: | ||
1027 | * | ||
1028 | * Gen-II does not support NCQ over a port multiplier | ||
1029 | * (no FIS-based switching). | ||
1030 | * | ||
1009 | * We don't have hob_nsect when doing NCQ commands on Gen-II. | 1031 | * We don't have hob_nsect when doing NCQ commands on Gen-II. |
1010 | * See mv_qc_prep() for more info. | 1032 | * See mv_qc_prep() for more info. |
1011 | */ | 1033 | */ |
1012 | if (adev->flags & ATA_DFLAG_NCQ) | 1034 | if (adev->flags & ATA_DFLAG_NCQ) { |
1013 | if (adev->max_sectors > ATA_MAX_SECTORS) | 1035 | if (sata_pmp_attached(adev->link->ap)) |
1036 | adev->flags &= ~ATA_DFLAG_NCQ; | ||
1037 | else if (adev->max_sectors > ATA_MAX_SECTORS) | ||
1014 | adev->max_sectors = ATA_MAX_SECTORS; | 1038 | adev->max_sectors = ATA_MAX_SECTORS; |
1039 | } | ||
1040 | } | ||
1041 | |||
1042 | static void mv_config_fbs(void __iomem *port_mmio, int enable_fbs) | ||
1043 | { | ||
1044 | u32 old_fcfg, new_fcfg, old_ltmode, new_ltmode; | ||
1045 | /* | ||
1046 | * Various bit settings required for operation | ||
1047 | * in FIS-based switching (fbs) mode on GenIIe: | ||
1048 | */ | ||
1049 | old_fcfg = readl(port_mmio + FIS_CFG_OFS); | ||
1050 | old_ltmode = readl(port_mmio + LTMODE_OFS); | ||
1051 | if (enable_fbs) { | ||
1052 | new_fcfg = old_fcfg | FIS_CFG_SINGLE_SYNC; | ||
1053 | new_ltmode = old_ltmode | LTMODE_BIT8; | ||
1054 | } else { /* disable fbs */ | ||
1055 | new_fcfg = old_fcfg & ~FIS_CFG_SINGLE_SYNC; | ||
1056 | new_ltmode = old_ltmode & ~LTMODE_BIT8; | ||
1057 | } | ||
1058 | if (new_fcfg != old_fcfg) | ||
1059 | writelfl(new_fcfg, port_mmio + FIS_CFG_OFS); | ||
1060 | if (new_ltmode != old_ltmode) | ||
1061 | writelfl(new_ltmode, port_mmio + LTMODE_OFS); | ||
1015 | } | 1062 | } |
1016 | 1063 | ||
1017 | static void mv_edma_cfg(struct ata_port *ap, int want_ncq) | 1064 | static void mv_edma_cfg(struct ata_port *ap, int want_ncq) |
@@ -1035,6 +1082,13 @@ static void mv_edma_cfg(struct ata_port *ap, int want_ncq) | |||
1035 | cfg |= (1 << 22); /* enab 4-entry host queue cache */ | 1082 | cfg |= (1 << 22); /* enab 4-entry host queue cache */ |
1036 | cfg |= (1 << 18); /* enab early completion */ | 1083 | cfg |= (1 << 18); /* enab early completion */ |
1037 | cfg |= (1 << 17); /* enab cut-through (dis stor&forwrd) */ | 1084 | cfg |= (1 << 17); /* enab cut-through (dis stor&forwrd) */ |
1085 | |||
1086 | if (want_ncq && sata_pmp_attached(ap)) { | ||
1087 | cfg |= EDMA_CFG_EDMA_FBS; /* FIS-based switching */ | ||
1088 | mv_config_fbs(port_mmio, 1); | ||
1089 | } else { | ||
1090 | mv_config_fbs(port_mmio, 0); | ||
1091 | } | ||
1038 | } | 1092 | } |
1039 | 1093 | ||
1040 | if (want_ncq) { | 1094 | if (want_ncq) { |
@@ -1240,6 +1294,7 @@ static void mv_qc_prep(struct ata_queued_cmd *qc) | |||
1240 | flags |= CRQB_FLAG_READ; | 1294 | flags |= CRQB_FLAG_READ; |
1241 | WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); | 1295 | WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); |
1242 | flags |= qc->tag << CRQB_TAG_SHIFT; | 1296 | flags |= qc->tag << CRQB_TAG_SHIFT; |
1297 | flags |= (qc->dev->link->pmp & 0xf) << CRQB_PMP_SHIFT; | ||
1243 | 1298 | ||
1244 | /* get current queue index from software */ | 1299 | /* get current queue index from software */ |
1245 | in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; | 1300 | in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; |
@@ -1331,6 +1386,7 @@ static void mv_qc_prep_iie(struct ata_queued_cmd *qc) | |||
1331 | WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); | 1386 | WARN_ON(MV_MAX_Q_DEPTH <= qc->tag); |
1332 | flags |= qc->tag << CRQB_TAG_SHIFT; | 1387 | flags |= qc->tag << CRQB_TAG_SHIFT; |
1333 | flags |= qc->tag << CRQB_HOSTQ_SHIFT; | 1388 | flags |= qc->tag << CRQB_HOSTQ_SHIFT; |
1389 | flags |= (qc->dev->link->pmp & 0xf) << CRQB_PMP_SHIFT; | ||
1334 | 1390 | ||
1335 | /* get current queue index from software */ | 1391 | /* get current queue index from software */ |
1336 | in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; | 1392 | in_index = pp->req_idx & MV_MAX_Q_DEPTH_MASK; |
@@ -1394,6 +1450,7 @@ static unsigned int mv_qc_issue(struct ata_queued_cmd *qc) | |||
1394 | * shadow block, etc registers. | 1450 | * shadow block, etc registers. |
1395 | */ | 1451 | */ |
1396 | mv_stop_edma(ap); | 1452 | mv_stop_edma(ap); |
1453 | mv_pmp_select(ap, qc->dev->link->pmp); | ||
1397 | return ata_sff_qc_issue(qc); | 1454 | return ata_sff_qc_issue(qc); |
1398 | } | 1455 | } |
1399 | 1456 | ||
@@ -2289,6 +2346,34 @@ static void mv_reset_channel(struct mv_host_priv *hpriv, void __iomem *mmio, | |||
2289 | mdelay(1); | 2346 | mdelay(1); |
2290 | } | 2347 | } |
2291 | 2348 | ||
2349 | static void mv_pmp_select(struct ata_port *ap, int pmp) | ||
2350 | { | ||
2351 | if (sata_pmp_supported(ap)) { | ||
2352 | void __iomem *port_mmio = mv_ap_base(ap); | ||
2353 | u32 reg = readl(port_mmio + SATA_IFCTL_OFS); | ||
2354 | int old = reg & 0xf; | ||
2355 | |||
2356 | if (old != pmp) { | ||
2357 | reg = (reg & ~0xf) | pmp; | ||
2358 | writelfl(reg, port_mmio + SATA_IFCTL_OFS); | ||
2359 | } | ||
2360 | } | ||
2361 | } | ||
2362 | |||
2363 | static int mv_pmp_hardreset(struct ata_link *link, unsigned int *class, | ||
2364 | unsigned long deadline) | ||
2365 | { | ||
2366 | mv_pmp_select(link->ap, sata_srst_pmp(link)); | ||
2367 | return sata_std_hardreset(link, class, deadline); | ||
2368 | } | ||
2369 | |||
2370 | static int mv_softreset(struct ata_link *link, unsigned int *class, | ||
2371 | unsigned long deadline) | ||
2372 | { | ||
2373 | mv_pmp_select(link->ap, sata_srst_pmp(link)); | ||
2374 | return ata_sff_softreset(link, class, deadline); | ||
2375 | } | ||
2376 | |||
2292 | static int mv_hardreset(struct ata_link *link, unsigned int *class, | 2377 | static int mv_hardreset(struct ata_link *link, unsigned int *class, |
2293 | unsigned long deadline) | 2378 | unsigned long deadline) |
2294 | { | 2379 | { |