aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorMark Lord <liml@rtr.ca>2009-03-15 11:33:19 -0400
committerJeff Garzik <jgarzik@redhat.com>2009-03-24 22:35:47 -0400
commit000b344f4ca7828ee43940255c8bbb32e2c7dbec (patch)
tree8ef1c3eb8269e14b8c9d3f07ecc17c0ac2568527 /drivers
parent6abf4678261218938ccdac90767d34ce9937634f (diff)
sata_mv: fix LED blinking for SoC+NCQ
For Marvell SoC chips, the HDD LED does not blink when there is disk I/O if NCQ is enabled. Add a quirk that enables blink mode for the LED while NCQ is enabled on any port of a SoC host controller. Normal LED function is restored when NCQ is not enabled on any port. The code to enable the blink mode is based on earlier code and suggestions from Frans Pop, Saeed Bishara, and possibly others. Signed-off-by: Mark Lord <mlord@pobox.com> Tested-by: Frans Pop <elendil@planet.nl> Signed-off-by: Jeff Garzik <jgarzik@redhat.com>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/ata/sata_mv.c68
1 files changed, 68 insertions, 0 deletions
diff --git a/drivers/ata/sata_mv.c b/drivers/ata/sata_mv.c
index 47184567248..8a751054c8a 100644
--- a/drivers/ata/sata_mv.c
+++ b/drivers/ata/sata_mv.c
@@ -251,6 +251,11 @@ enum {
251 HC_IRQ_COAL_IO_THRESHOLD_OFS = 0x000c, 251 HC_IRQ_COAL_IO_THRESHOLD_OFS = 0x000c,
252 HC_IRQ_COAL_TIME_THRESHOLD_OFS = 0x0010, 252 HC_IRQ_COAL_TIME_THRESHOLD_OFS = 0x0010,
253 253
254 SOC_LED_CTRL_OFS = 0x2c,
255 SOC_LED_CTRL_BLINK = (1 << 0), /* Active LED blink */
256 SOC_LED_CTRL_ACT_PRESENCE = (1 << 2), /* Multiplex dev presence */
257 /* with dev activity LED */
258
254 /* Shadow block registers */ 259 /* Shadow block registers */
255 SHD_BLK_OFS = 0x100, 260 SHD_BLK_OFS = 0x100,
256 SHD_CTL_AST_OFS = 0x20, /* ofs from SHD_BLK_OFS */ 261 SHD_CTL_AST_OFS = 0x20, /* ofs from SHD_BLK_OFS */
@@ -411,6 +416,7 @@ enum {
411 MV_HP_PCIE = (1 << 9), /* PCIe bus/regs: 7042 */ 416 MV_HP_PCIE = (1 << 9), /* PCIe bus/regs: 7042 */
412 MV_HP_CUT_THROUGH = (1 << 10), /* can use EDMA cut-through */ 417 MV_HP_CUT_THROUGH = (1 << 10), /* can use EDMA cut-through */
413 MV_HP_FLAG_SOC = (1 << 11), /* SystemOnChip, no PCI */ 418 MV_HP_FLAG_SOC = (1 << 11), /* SystemOnChip, no PCI */
419 MV_HP_QUIRK_LED_BLINK_EN = (1 << 12), /* is led blinking enabled? */
414 420
415 /* Port private flags (pp_flags) */ 421 /* Port private flags (pp_flags) */
416 MV_PP_FLAG_EDMA_EN = (1 << 0), /* is EDMA engine enabled? */ 422 MV_PP_FLAG_EDMA_EN = (1 << 0), /* is EDMA engine enabled? */
@@ -1404,6 +1410,61 @@ static void mv_bmdma_enable_iie(struct ata_port *ap, int enable_bmdma)
1404 mv_write_cached_reg(mv_ap_base(ap) + EDMA_UNKNOWN_RSVD_OFS, old, new); 1410 mv_write_cached_reg(mv_ap_base(ap) + EDMA_UNKNOWN_RSVD_OFS, old, new);
1405} 1411}
1406 1412
1413/*
1414 * SOC chips have an issue whereby the HDD LEDs don't always blink
1415 * during I/O when NCQ is enabled. Enabling a special "LED blink" mode
1416 * of the SOC takes care of it, generating a steady blink rate when
1417 * any drive on the chip is active.
1418 *
1419 * Unfortunately, the blink mode is a global hardware setting for the SOC,
1420 * so we must use it whenever at least one port on the SOC has NCQ enabled.
1421 *
1422 * We turn "LED blink" off when NCQ is not in use anywhere, because the normal
1423 * LED operation works then, and provides better (more accurate) feedback.
1424 *
1425 * Note that this code assumes that an SOC never has more than one HC onboard.
1426 */
1427static void mv_soc_led_blink_enable(struct ata_port *ap)
1428{
1429 struct ata_host *host = ap->host;
1430 struct mv_host_priv *hpriv = host->private_data;
1431 void __iomem *hc_mmio;
1432 u32 led_ctrl;
1433
1434 if (hpriv->hp_flags & MV_HP_QUIRK_LED_BLINK_EN)
1435 return;
1436 hpriv->hp_flags |= MV_HP_QUIRK_LED_BLINK_EN;
1437 hc_mmio = mv_hc_base_from_port(mv_host_base(host), ap->port_no);
1438 led_ctrl = readl(hc_mmio + SOC_LED_CTRL_OFS);
1439 writel(led_ctrl | SOC_LED_CTRL_BLINK, hc_mmio + SOC_LED_CTRL_OFS);
1440}
1441
1442static void mv_soc_led_blink_disable(struct ata_port *ap)
1443{
1444 struct ata_host *host = ap->host;
1445 struct mv_host_priv *hpriv = host->private_data;
1446 void __iomem *hc_mmio;
1447 u32 led_ctrl;
1448 unsigned int port;
1449
1450 if (!(hpriv->hp_flags & MV_HP_QUIRK_LED_BLINK_EN))
1451 return;
1452
1453 /* disable led-blink only if no ports are using NCQ */
1454 for (port = 0; port < hpriv->n_ports; port++) {
1455 struct ata_port *this_ap = host->ports[port];
1456 struct mv_port_priv *pp = this_ap->private_data;
1457
1458 if (pp->pp_flags & MV_PP_FLAG_NCQ_EN)
1459 return;
1460 }
1461
1462 hpriv->hp_flags &= ~MV_HP_QUIRK_LED_BLINK_EN;
1463 hc_mmio = mv_hc_base_from_port(mv_host_base(host), ap->port_no);
1464 led_ctrl = readl(hc_mmio + SOC_LED_CTRL_OFS);
1465 writel(led_ctrl & ~SOC_LED_CTRL_BLINK, hc_mmio + SOC_LED_CTRL_OFS);
1466}
1467
1407static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma) 1468static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma)
1408{ 1469{
1409 u32 cfg; 1470 u32 cfg;
@@ -1451,6 +1512,13 @@ static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma)
1451 if (hpriv->hp_flags & MV_HP_CUT_THROUGH) 1512 if (hpriv->hp_flags & MV_HP_CUT_THROUGH)
1452 cfg |= (1 << 17); /* enab cut-thru (dis stor&forwrd) */ 1513 cfg |= (1 << 17); /* enab cut-thru (dis stor&forwrd) */
1453 mv_bmdma_enable_iie(ap, !want_edma); 1514 mv_bmdma_enable_iie(ap, !want_edma);
1515
1516 if (IS_SOC(hpriv)) {
1517 if (want_ncq)
1518 mv_soc_led_blink_enable(ap);
1519 else
1520 mv_soc_led_blink_disable(ap);
1521 }
1454 } 1522 }
1455 1523
1456 if (want_ncq) { 1524 if (want_ncq) {