From 71fb63c92eae3f9197e2343ed5ed3676440789e1 Mon Sep 17 00:00:00 2001 From: Biwen Li Date: Wed, 17 Apr 2019 18:59:01 +0800 Subject: [PATCH] sata: support layerscape This is an integrated patch of sata for layerscape Signed-off-by: Biwen Li Signed-off-by: Peng Ma Signed-off-by: Tang Yuantian --- drivers/ata/ahci.h | 7 ++ drivers/ata/ahci_qoriq.c | 168 ++++++++++++++++++++++++++++++++++++++ drivers/ata/libata-core.c | 3 + 3 files changed, 178 insertions(+) --- a/drivers/ata/ahci.h +++ b/drivers/ata/ahci.h @@ -445,4 +445,11 @@ static inline int ahci_nr_ports(u32 cap) return (cap & 0x1f) + 1; } +#ifdef CONFIG_AHCI_QORIQ +extern void fsl_sata_errata_379364(struct ata_link *link); +#else +static void fsl_sata_errata_379364(struct ata_link *link) +{} +#endif + #endif /* _AHCI_H */ --- a/drivers/ata/ahci_qoriq.c +++ b/drivers/ata/ahci_qoriq.c @@ -35,6 +35,8 @@ /* port register default value */ #define AHCI_PORT_PHY_1_CFG 0xa003fffe +#define AHCI_PORT_PHY2_CFG 0x28184d1f +#define AHCI_PORT_PHY3_CFG 0x0e081509 #define AHCI_PORT_TRANS_CFG 0x08000029 #define AHCI_PORT_AXICC_CFG 0x3fffffff @@ -49,6 +51,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_LS1043A, @@ -56,6 +79,7 @@ enum ahci_qoriq_type { AHCI_LS1046A, AHCI_LS1088A, AHCI_LS2088A, + AHCI_LX2160A, }; struct ahci_qoriq_priv { @@ -72,6 +96,7 @@ static const struct of_device_id ahci_qo { .compatible = "fsl,ls1046a-ahci", .data = (void *)AHCI_LS1046A}, { .compatible = "fsl,ls1088a-ahci", .data = (void *)AHCI_LS1088A}, { .compatible = "fsl,ls2088a-ahci", .data = (void *)AHCI_LS2088A}, + { .compatible = "fsl,lx2160a-ahci", .data = (void *)AHCI_LX2160A}, {}, }; MODULE_DEVICE_TABLE(of, ahci_qoriq_of_match); @@ -156,6 +181,138 @@ static struct scsi_host_template ahci_qo AHCI_SHT(DRV_NAME), }; +void fsl_sata_errata_379364(struct ata_link *link) +{ + struct ata_port *ap = link->ap; + struct ahci_host_priv *hpriv = ap->host->private_data; + struct ahci_qoriq_priv *qoriq_priv = hpriv->plat_data; + bool lx2160a_workaround = (qoriq_priv->type == AHCI_LX2160A); + + int val = 0; + void __iomem *rcw_base = NULL; + void __iomem *serdes_base = NULL; + void __iomem *dev_con_base = NULL; + + if (!lx2160a_workaround) + return; + else { + dev_con_base = ioremap(DEVICE_CONFIG_REG_BASE, PAGE_SIZE); + if (!dev_con_base) { + ata_link_err(link, "device config ioremap failed\n"); + 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) { + ata_link_err(link, "serdes ioremap failed\n"); + goto dev_unmap; + } + + rcw_base = ioremap(RCWSR29_BASE, PAGE_SIZE); + if (!rcw_base) { + ata_link_err(link, "rcw ioremap failed\n"); + goto serdes_unmap; + } + + ata_msleep(link->ap, 1); + + 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; + } + } + + iounmap(rcw_base); +serdes_unmap: + iounmap(serdes_base); +dev_unmap: + iounmap(dev_con_base); +} + + static int ahci_qoriq_phy_init(struct ahci_host_priv *hpriv) { struct ahci_qoriq_priv *qpriv = hpriv->plat_data; @@ -183,13 +340,18 @@ static int ahci_qoriq_phy_init(struct ah writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2, qpriv->ecc_addr); writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); + writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); break; case AHCI_LS2080A: + case AHCI_LX2160A: writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); + writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); @@ -201,6 +363,8 @@ static int ahci_qoriq_phy_init(struct ah writel(readl(qpriv->ecc_addr) | ECC_DIS_ARMV8_CH2, qpriv->ecc_addr); writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); + writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); @@ -212,6 +376,8 @@ static int ahci_qoriq_phy_init(struct ah writel(readl(qpriv->ecc_addr) | ECC_DIS_LS1088A, qpriv->ecc_addr); writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); + writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); @@ -219,6 +385,8 @@ static int ahci_qoriq_phy_init(struct ah case AHCI_LS2088A: writel(AHCI_PORT_PHY_1_CFG, reg_base + PORT_PHY1); + writel(AHCI_PORT_PHY2_CFG, reg_base + PORT_PHY2); + writel(AHCI_PORT_PHY3_CFG, reg_base + PORT_PHY3); writel(AHCI_PORT_TRANS_CFG, reg_base + PORT_TRANS); if (qpriv->is_dmacoherent) writel(AHCI_PORT_AXICC_CFG, reg_base + PORT_AXICC); --- a/drivers/ata/libata-core.c +++ b/drivers/ata/libata-core.c @@ -75,6 +75,7 @@ #define CREATE_TRACE_POINTS #include +#include "ahci.h" #include "libata.h" #include "libata-transport.h" @@ -4144,6 +4145,8 @@ int sata_link_hardreset(struct ata_link */ ata_msleep(link->ap, 1); + fsl_sata_errata_379364(link); + /* bring link back */ rc = sata_link_resume(link, timing, deadline); if (rc)