summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeng Ma <peng.ma@nxp.com>2020-02-29 19:14:54 +0000
committerRussell King (Oracle) <rmk+kernel@armlinux.org.uk>2022-06-21 10:58:20 +0100
commit188ae2c16031b0e1af2ee6cb2f6d6275a669d8a8 (patch)
tree575d5b57bc960df5d87470543360f0e0bdc749ad
parent06559cb3ed2bc2a573905b51264d46736da8471e (diff)
ahci: qoriq: workaround for errata A-379364 on lx2160a
There is a erratum on lx2160a which is: "SATA link is going down sometime during sata initialization" The workaround for it is to reset the lane. This patch implements this workaround. This erratum only exists on lx2160 Rev1, will be addressed on Rev2 and later. Signed-off-by: Peng Ma <peng.ma@nxp.com> Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
-rw-r--r--drivers/ata/ahci_qoriq.c144
1 files changed, 144 insertions, 0 deletions
diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c
index 6cd61842ad48..76700045dea6 100644
--- a/drivers/ata/ahci_qoriq.c
+++ b/drivers/ata/ahci_qoriq.c
@@ -48,6 +48,27 @@
#define ECC_DIS_ARMV8_CH2 0x80000000
#define ECC_DIS_LS1088A 0x40000000
+/* errata for lx2160 */
+#define RCWSR29_BASE 0x1E00170
+#define SERDES2_BASE 0x1EB0000
+#define DEVICE_CONFIG_REG_BASE 0x1E00000
+#define SERDES2_LNAX_RX_CR(x) (0x840 + (0x100 * (x)))
+#define SERDES2_LNAX_RX_CBR(x) (0x8C0 + (0x100 * (x)))
+#define SYS_VER_REG 0xA4
+#define LN_RX_RST 0x80000010
+#define LN_RX_RST_DONE 0x3
+#define LN_RX_MASK 0xf
+#define LX2160A_VER1 0x1
+
+#define SERDES2_LNAA 0
+#define SERDES2_LNAB 1
+#define SERDES2_LNAC 2
+#define SERDES2_LNAD 3
+#define SERDES2_LNAE 4
+#define SERDES2_LNAF 5
+#define SERDES2_LNAG 6
+#define SERDES2_LNAH 7
+
enum ahci_qoriq_type {
AHCI_LS1021A,
AHCI_LS1028A,
@@ -87,6 +108,126 @@ static const struct acpi_device_id ahci_qoriq_acpi_match[] = {
};
MODULE_DEVICE_TABLE(acpi, ahci_qoriq_acpi_match);
+static void fsl_sata_errata_379364(bool select)
+{
+ int val = 0;
+ void __iomem *rcw_base = NULL;
+ void __iomem *serdes_base = NULL;
+ void __iomem *dev_con_base = NULL;
+
+ if (select) {
+ dev_con_base = ioremap(DEVICE_CONFIG_REG_BASE, PAGE_SIZE);
+ if (!dev_con_base)
+ return;
+
+ val = (readl(dev_con_base + SYS_VER_REG) & GENMASK(7, 4)) >> 4;
+ if (val != LX2160A_VER1)
+ goto dev_unmap;
+
+ /*
+ * Add few msec delay.
+ * Check for corresponding serdes lane RST_DONE .
+ * apply lane reset.
+ */
+
+ serdes_base = ioremap(SERDES2_BASE, PAGE_SIZE);
+ if (!serdes_base)
+ goto dev_unmap;
+
+ rcw_base = ioremap(RCWSR29_BASE, PAGE_SIZE);
+ if (!rcw_base)
+ goto serdes_unmap;
+
+ msleep(20);
+
+ val = (readl(rcw_base) & GENMASK(25, 21)) >> 21;
+
+ switch (val) {
+ case 1:
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAC)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAC));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAD)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAD));
+ break;
+
+ case 4:
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+ break;
+
+ case 5:
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAE)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAE));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAF)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAF));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+ break;
+
+ case 8:
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAC)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAC));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAD)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAD));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAE)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAE));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAF)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAF));
+ break;
+
+ case 12:
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAG)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAG));
+ if ((readl(serdes_base + SERDES2_LNAX_RX_CBR(SERDES2_LNAH)) &
+ LN_RX_MASK) != LN_RX_RST_DONE)
+ writel(LN_RX_RST, serdes_base +
+ SERDES2_LNAX_RX_CR(SERDES2_LNAH));
+ break;
+
+ default:
+ break;
+ }
+ } else {
+ return;
+ }
+
+ iounmap(rcw_base);
+serdes_unmap:
+ iounmap(serdes_base);
+dev_unmap:
+ iounmap(dev_con_base);
+}
+
static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
unsigned long deadline)
{
@@ -102,6 +243,7 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
bool online;
int rc;
bool ls1021a_workaround = (qoriq_priv->type == AHCI_LS1021A);
+ bool lx2160a_workaround = (qoriq_priv->type == AHCI_LX2160A);
hpriv->stop_engine(ap);
@@ -126,6 +268,8 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class,
tf.status = ATA_BUSY;
ata_tf_to_fis(&tf, 0, 0, d2h_fis);
+ fsl_sata_errata_379364(lx2160a_workaround);
+
rc = sata_link_hardreset(link, timing, deadline, &online,
ahci_check_ready);