|
@@ -251,6 +251,11 @@ enum {
|
|
|
HC_IRQ_COAL_IO_THRESHOLD_OFS = 0x000c,
|
|
|
HC_IRQ_COAL_TIME_THRESHOLD_OFS = 0x0010,
|
|
|
|
|
|
+ SOC_LED_CTRL_OFS = 0x2c,
|
|
|
+ SOC_LED_CTRL_BLINK = (1 << 0), /* Active LED blink */
|
|
|
+ SOC_LED_CTRL_ACT_PRESENCE = (1 << 2), /* Multiplex dev presence */
|
|
|
+ /* with dev activity LED */
|
|
|
+
|
|
|
/* Shadow block registers */
|
|
|
SHD_BLK_OFS = 0x100,
|
|
|
SHD_CTL_AST_OFS = 0x20, /* ofs from SHD_BLK_OFS */
|
|
@@ -411,6 +416,7 @@ enum {
|
|
|
MV_HP_PCIE = (1 << 9), /* PCIe bus/regs: 7042 */
|
|
|
MV_HP_CUT_THROUGH = (1 << 10), /* can use EDMA cut-through */
|
|
|
MV_HP_FLAG_SOC = (1 << 11), /* SystemOnChip, no PCI */
|
|
|
+ MV_HP_QUIRK_LED_BLINK_EN = (1 << 12), /* is led blinking enabled? */
|
|
|
|
|
|
/* Port private flags (pp_flags) */
|
|
|
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)
|
|
|
mv_write_cached_reg(mv_ap_base(ap) + EDMA_UNKNOWN_RSVD_OFS, old, new);
|
|
|
}
|
|
|
|
|
|
+/*
|
|
|
+ * SOC chips have an issue whereby the HDD LEDs don't always blink
|
|
|
+ * during I/O when NCQ is enabled. Enabling a special "LED blink" mode
|
|
|
+ * of the SOC takes care of it, generating a steady blink rate when
|
|
|
+ * any drive on the chip is active.
|
|
|
+ *
|
|
|
+ * Unfortunately, the blink mode is a global hardware setting for the SOC,
|
|
|
+ * so we must use it whenever at least one port on the SOC has NCQ enabled.
|
|
|
+ *
|
|
|
+ * We turn "LED blink" off when NCQ is not in use anywhere, because the normal
|
|
|
+ * LED operation works then, and provides better (more accurate) feedback.
|
|
|
+ *
|
|
|
+ * Note that this code assumes that an SOC never has more than one HC onboard.
|
|
|
+ */
|
|
|
+static void mv_soc_led_blink_enable(struct ata_port *ap)
|
|
|
+{
|
|
|
+ struct ata_host *host = ap->host;
|
|
|
+ struct mv_host_priv *hpriv = host->private_data;
|
|
|
+ void __iomem *hc_mmio;
|
|
|
+ u32 led_ctrl;
|
|
|
+
|
|
|
+ if (hpriv->hp_flags & MV_HP_QUIRK_LED_BLINK_EN)
|
|
|
+ return;
|
|
|
+ hpriv->hp_flags |= MV_HP_QUIRK_LED_BLINK_EN;
|
|
|
+ hc_mmio = mv_hc_base_from_port(mv_host_base(host), ap->port_no);
|
|
|
+ led_ctrl = readl(hc_mmio + SOC_LED_CTRL_OFS);
|
|
|
+ writel(led_ctrl | SOC_LED_CTRL_BLINK, hc_mmio + SOC_LED_CTRL_OFS);
|
|
|
+}
|
|
|
+
|
|
|
+static void mv_soc_led_blink_disable(struct ata_port *ap)
|
|
|
+{
|
|
|
+ struct ata_host *host = ap->host;
|
|
|
+ struct mv_host_priv *hpriv = host->private_data;
|
|
|
+ void __iomem *hc_mmio;
|
|
|
+ u32 led_ctrl;
|
|
|
+ unsigned int port;
|
|
|
+
|
|
|
+ if (!(hpriv->hp_flags & MV_HP_QUIRK_LED_BLINK_EN))
|
|
|
+ return;
|
|
|
+
|
|
|
+ /* disable led-blink only if no ports are using NCQ */
|
|
|
+ for (port = 0; port < hpriv->n_ports; port++) {
|
|
|
+ struct ata_port *this_ap = host->ports[port];
|
|
|
+ struct mv_port_priv *pp = this_ap->private_data;
|
|
|
+
|
|
|
+ if (pp->pp_flags & MV_PP_FLAG_NCQ_EN)
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ hpriv->hp_flags &= ~MV_HP_QUIRK_LED_BLINK_EN;
|
|
|
+ hc_mmio = mv_hc_base_from_port(mv_host_base(host), ap->port_no);
|
|
|
+ led_ctrl = readl(hc_mmio + SOC_LED_CTRL_OFS);
|
|
|
+ writel(led_ctrl & ~SOC_LED_CTRL_BLINK, hc_mmio + SOC_LED_CTRL_OFS);
|
|
|
+}
|
|
|
+
|
|
|
static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma)
|
|
|
{
|
|
|
u32 cfg;
|
|
@@ -1451,6 +1512,13 @@ static void mv_edma_cfg(struct ata_port *ap, int want_ncq, int want_edma)
|
|
|
if (hpriv->hp_flags & MV_HP_CUT_THROUGH)
|
|
|
cfg |= (1 << 17); /* enab cut-thru (dis stor&forwrd) */
|
|
|
mv_bmdma_enable_iie(ap, !want_edma);
|
|
|
+
|
|
|
+ if (IS_SOC(hpriv)) {
|
|
|
+ if (want_ncq)
|
|
|
+ mv_soc_led_blink_enable(ap);
|
|
|
+ else
|
|
|
+ mv_soc_led_blink_disable(ap);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
if (want_ncq) {
|