commit d8eadb29f407e27f769c8e50b52a3c01e671acbe Author: zhaoxc0502 Date: Thu Jun 16 17:16:39 2022 +0800 linux_drivers_ata Change-Id: I4bec2b3114e101143470dc3704e3addbefcb19db diff --git a/drivers/ata/ahci_imx.c b/drivers/ata/ahci_imx.c index 388baf528..de4bb99d6 100644 --- a/drivers/ata/ahci_imx.c +++ b/drivers/ata/ahci_imx.c @@ -44,9 +44,23 @@ enum { IMX_CLOCK_RESET = 0x7f3f, IMX_CLOCK_RESET_RESET = 1 << 0, /* IMX8QM HSIO AHCI definitions */ - IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET = 0x03, - IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET = 0x09, - IMX8QM_SATA_PHY_IMPED_RATIO_85OHM = 0x6c, + IMX8QM_SATA_PHY_REG03_RX_IMPED_RATIO = 0x03, + IMX8QM_SATA_PHY_REG09_TX_IMPED_RATIO = 0x09, + IMX8QM_SATA_PHY_REG10_TX_POST_CURSOR_RATIO = 0x0a, + IMX8QM_SATA_PHY_GEN1_TX_POST_CURSOR_RATIO = 0x15, + IMX8QM_SATA_PHY_IMPED_RATIO_100OHM = 0x5d, + IMX8QM_SATA_PHY_REG22_TX_POST_CURSOR_RATIO = 0x16, + IMX8QM_SATA_PHY_GEN2_TX_POST_CURSOR_RATIO = 0x00, + IMX8QM_SATA_PHY_REG24_TX_AMP_RATIO_MARGIN0 = 0x18, + IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN0 = 0x64, + IMX8QM_SATA_PHY_REG25_TX_AMP_RATIO_MARGIN1 = 0x19, + IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN1 = 0x70, + IMX8QM_SATA_PHY_REG26_TX_AMP_RATIO_MARGIN2 = 0x1a, + IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN2 = 0x69, + IMX8QM_SATA_PHY_REG48_PMA_STATUS = 0x30, + IMX8QM_SATA_PHY_REG48_PMA_RDY = BIT(7), + IMX8QM_SATA_PHY_REG128_UPDATE_SETTING = 0x80, + IMX8QM_SATA_PHY_UPDATE_SETTING = 0x01, IMX8QM_LPCG_PHYX2_OFFSET = 0x00000, IMX8QM_CSR_PHYX2_OFFSET = 0x90000, IMX8QM_CSR_PHYX1_OFFSET = 0xa0000, @@ -56,10 +70,31 @@ enum { IMX8QM_CSR_SATA_OFFSET = 0xd0000, IMX8QM_CSR_PCIE_CTRL2_OFFSET = 0x8, IMX8QM_CSR_MISC_OFFSET = 0xe0000, + /* IMX8QM SATA specific control registers */ + IMX8QM_SATA_PPCFG_OFFSET = 0xa8, + IMX8QM_SATA_PPCFG_FORCE_PHY_RDY = BIT(20), + IMX8QM_SATA_PPCFG_BIST_PATTERN_MASK = 0x7 << 21, + IMX8QM_SATA_PPCFG_BIST_PATTERN_OFFSET = 21, + IMX8QM_SATA_PPCFG_BIST_PATTERN_EN = BIT(24), + IMX8QM_SATA_PPCFG_BIST_PATTERN_NOALIGNS = BIT(26), + IMX8QM_SATA_PP2CFG_OFFSET = 0xac, + IMX8QM_SATA_PP2CFG_COMINIT_NEGATE_MIN = 0x28 << 24, + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP = 0x18 << 16, + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP_MAX = 0x2b << 8, + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP_MIN = 0x1b << 0, + IMX8QM_SATA_PP3CFG_OFFSET = 0xb0, + IMX8QM_SATA_PP3CFG_COMWAKE_NEGATE_MIN = 0x0e << 24, + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP = 0x08 << 16, + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP_MAX = 0x0f << 8, + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP_MIN = 0x01 << 0, + IMX8QM_SATA_AHCI_VEND_PTC = 0xc8, + IMX8QM_SATA_AHCI_VEND_PTC_RXWM_MASK = 0x7f, + IMX8QM_SATA_AHCI_VEND_PTC_RXWM = 0x29, IMX8QM_LPCG_PHYX2_PCLK0_MASK = (0x3 << 16), IMX8QM_LPCG_PHYX2_PCLK1_MASK = (0x3 << 20), IMX8QM_PHY_APB_RSTN_0 = BIT(0), + IMX8QM_PHY_APB_RSTN_1 = BIT(1), IMX8QM_PHY_MODE_SATA = BIT(19), IMX8QM_PHY_MODE_MASK = (0xf << 17), IMX8QM_PHY_PIPE_RSTN_0 = BIT(24), @@ -76,7 +111,10 @@ enum { IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0 = BIT(29), IMX8QM_SATA_CTRL_RESET_N = BIT(12), IMX8QM_SATA_CTRL_EPCS_PHYRESET_N = BIT(7), + IMX8QM_SATA_CTRL_EPCS_TXDEEMP_SEL = BIT(6), + IMX8QM_SATA_CTRL_EPCS_TXDEEMP = BIT(5), IMX8QM_CTRL_BUTTON_RST_N = BIT(21), + IMX8QM_CTRL_PERST_N = BIT(22), IMX8QM_CTRL_POWER_UP_RST_N = BIT(23), IMX8QM_CTRL_LTSSM_ENABLE = BIT(4), }; @@ -99,6 +137,12 @@ struct imx_ahci_priv { struct clk *phy_apbclk; struct clk *phy_pclk0; struct clk *phy_pclk1; + struct clk *per_clk0; + struct clk *per_clk1; + struct clk *per_clk2; + struct clk *per_clk3; + struct clk *per_clk4; + struct clk *per_clk5; void __iomem *phy_base; struct gpio_desc *clkreq_gpiod; struct regmap *gpr; @@ -106,14 +150,70 @@ struct imx_ahci_priv { bool first_time; u32 phy_params; u32 imped_ratio; + u32 ext_osc; }; +void *sg_io_buffer_hack; + static int ahci_imx_hotplug; module_param_named(hotplug, ahci_imx_hotplug, int, 0644); MODULE_PARM_DESC(hotplug, "AHCI IMX hot-plug support (0=Don't support, 1=support)"); +static int bist_enable; +module_param_named(bist, bist_enable, int, 0644); +MODULE_PARM_DESC(bist, "AHCI IMX bist mode enable(1 = enable)"); + static void ahci_imx_host_stop(struct ata_host *host); +static bool imx_sata_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case IMX8QM_LPCG_PHYX2_OFFSET: + case IMX8QM_CSR_PHYX2_OFFSET: + case IMX8QM_CSR_PHYX1_OFFSET: + case IMX8QM_CSR_PCIEA_OFFSET: + case IMX8QM_CSR_PCIEB_OFFSET: + case IMX8QM_CSR_SATA_OFFSET: + case IMX8QM_CSR_MISC_OFFSET: + case IMX8QM_CSR_PHYX2_OFFSET + IMX8QM_CSR_PHYX_STTS0_OFFSET: + case IMX8QM_CSR_PHYX1_OFFSET + IMX8QM_CSR_PHYX_STTS0_OFFSET: + case IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET: + case IMX8QM_CSR_PCIEB_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET: + return true; + default: + return false; + } +} + +static bool imx_sata_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case IMX8QM_LPCG_PHYX2_OFFSET: + case IMX8QM_CSR_PHYX2_OFFSET: + case IMX8QM_CSR_PHYX1_OFFSET: + case IMX8QM_CSR_PCIEA_OFFSET: + case IMX8QM_CSR_PCIEB_OFFSET: + case IMX8QM_CSR_SATA_OFFSET: + case IMX8QM_CSR_MISC_OFFSET: + case IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET: + case IMX8QM_CSR_PCIEB_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET: + return true; + default: + return false; + } +} + +static const struct regmap_config imx_sata_regconfig = { + .max_register = IMX8QM_CSR_MISC_OFFSET, + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .val_format_endian = REGMAP_ENDIAN_NATIVE, + .num_reg_defaults_raw = IMX8QM_CSR_MISC_OFFSET / sizeof(uint32_t) + 1, + .readable_reg = imx_sata_readable_reg, + .writeable_reg = imx_sata_writeable_reg, + .cache_type = REGCACHE_NONE, +}; static int imx_phy_crbit_assert(void __iomem *mmio, u32 bit, bool assert) { int timeout = 10; @@ -442,11 +542,9 @@ static struct attribute *fsl_sata_ahci_attrs[] = { }; ATTRIBUTE_GROUPS(fsl_sata_ahci); -static int imx8_sata_enable(struct ahci_host_priv *hpriv) +static int imx8_sata_clk_enable(struct imx_ahci_priv *imxpriv) { - u32 val, reg; - int i, ret; - struct imx_ahci_priv *imxpriv = hpriv->plat_data; + int ret; struct device *dev = &imxpriv->ahci_pdev->dev; /* configure the hsio for sata */ @@ -475,6 +573,92 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) dev_err(dev, "can't enable phy_apbclk.\n"); goto disable_epcs_rx_clk; } + ret = clk_prepare_enable(imxpriv->per_clk0); + if (ret < 0) { + dev_err(dev, "can't enable per_clk.\n"); + goto disable_phy_apbclk; + } + ret = clk_prepare_enable(imxpriv->per_clk1); + if (ret < 0) { + dev_err(dev, "can't enable per_clk.\n"); + goto disable_per_clk0; + } + ret = clk_prepare_enable(imxpriv->per_clk2); + if (ret < 0) { + dev_err(dev, "can't enable per_clk.\n"); + goto disable_per_clk1; + } + ret = clk_prepare_enable(imxpriv->per_clk3); + if (ret < 0) { + dev_err(dev, "can't enable per_clk.\n"); + goto disable_per_clk2; + } + ret = clk_prepare_enable(imxpriv->per_clk4); + if (ret < 0) { + dev_err(dev, "can't enable per_clk.\n"); + goto disable_per_clk3; + } + ret = clk_prepare_enable(imxpriv->per_clk5); + if (ret < 0) + dev_err(dev, "can't enable per_clk.\n"); + else + return 0; + + clk_disable_unprepare(imxpriv->per_clk4); +disable_per_clk3: + clk_disable_unprepare(imxpriv->per_clk3); +disable_per_clk2: + clk_disable_unprepare(imxpriv->per_clk2); +disable_per_clk1: + clk_disable_unprepare(imxpriv->per_clk1); +disable_per_clk0: + clk_disable_unprepare(imxpriv->per_clk0); +disable_phy_apbclk: + clk_disable_unprepare(imxpriv->phy_apbclk); +disable_epcs_rx_clk: + clk_disable_unprepare(imxpriv->epcs_rx_clk); +disable_epcs_tx_clk: + clk_disable_unprepare(imxpriv->epcs_tx_clk); +disable_phy_pclk1: + clk_disable_unprepare(imxpriv->phy_pclk1); +disable_phy_pclk0: + clk_disable_unprepare(imxpriv->phy_pclk0); + return ret; +} + +static void imx8_sata_clk_disable(struct imx_ahci_priv *imxpriv) +{ + regmap_update_bits(imxpriv->gpr, + IMX8QM_LPCG_PHYX2_OFFSET, + IMX8QM_LPCG_PHYX2_PCLK0_MASK | + IMX8QM_LPCG_PHYX2_PCLK1_MASK, 0); + clk_disable_unprepare(imxpriv->epcs_rx_clk); + clk_disable_unprepare(imxpriv->epcs_tx_clk); + clk_disable_unprepare(imxpriv->per_clk5); + clk_disable_unprepare(imxpriv->per_clk4); + clk_disable_unprepare(imxpriv->per_clk3); + clk_disable_unprepare(imxpriv->per_clk2); + clk_disable_unprepare(imxpriv->per_clk1); + clk_disable_unprepare(imxpriv->per_clk0); +} + +static int imx8_sata_enable(struct ahci_host_priv *hpriv) +{ + u32 val, reg; + int i, ret; + struct imx_ahci_priv *imxpriv = hpriv->plat_data; + struct device *dev = &imxpriv->ahci_pdev->dev; + + ret = imx8_sata_clk_enable(imxpriv); + if (ret) + return ret; + + /* PHYX2 APB reset */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX2_OFFSET, + IMX8QM_PHY_APB_RSTN_0 | IMX8QM_PHY_APB_RSTN_1, + IMX8QM_PHY_APB_RSTN_0 | IMX8QM_PHY_APB_RSTN_1); + /* Configure PHYx2 PIPE_RSTN */ regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET, &val); @@ -498,14 +682,13 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) IMX8QM_PHY_PIPE_RSTN_1 | IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1); } - if (((reg | val) & IMX8QM_CTRL_LTSSM_ENABLE) == 0) { - /* The links of both PCIA and PCIEB of HSIO are down */ - regmap_update_bits(imxpriv->gpr, - IMX8QM_LPCG_PHYX2_OFFSET, - IMX8QM_LPCG_PHYX2_PCLK0_MASK | - IMX8QM_LPCG_PHYX2_PCLK1_MASK, - 0); - } + + regmap_update_bits(imxpriv->gpr, + IMX8QM_LPCG_PHYX2_OFFSET, + IMX8QM_LPCG_PHYX2_PCLK0_MASK | + IMX8QM_LPCG_PHYX2_PCLK1_MASK, + IMX8QM_LPCG_PHYX2_PCLK0_MASK | + IMX8QM_LPCG_PHYX2_PCLK1_MASK); /* set PWR_RST and BT_RST of csr_pciea */ val = IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET; @@ -513,6 +696,8 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) val, IMX8QM_CTRL_BUTTON_RST_N, IMX8QM_CTRL_BUTTON_RST_N); + regmap_update_bits(imxpriv->gpr, val, IMX8QM_CTRL_PERST_N, + IMX8QM_CTRL_PERST_N); regmap_update_bits(imxpriv->gpr, val, IMX8QM_CTRL_POWER_UP_RST_N, @@ -524,18 +709,32 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) IMX8QM_PHY_MODE_MASK, IMX8QM_PHY_MODE_SATA); - /* - * BIT0 RXENA 1, BIT1 TXENA 0 - * BIT12 PHY_X1_EPCS_SEL 1. - */ - regmap_update_bits(imxpriv->gpr, - IMX8QM_CSR_MISC_OFFSET, - IMX8QM_MISC_IOB_RXENA, - IMX8QM_MISC_IOB_RXENA); - regmap_update_bits(imxpriv->gpr, - IMX8QM_CSR_MISC_OFFSET, - IMX8QM_MISC_IOB_TXENA, - 0); + if (imxpriv->ext_osc) { + dev_info(dev, "external osc is used.\n"); + /* + * bit0 rx ena 1, bit1 tx ena 0 + * bit12 PHY_X1_EPCS_SEL 1. + */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_RXENA, + IMX8QM_MISC_IOB_RXENA); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_TXENA, + 0); + } else { + dev_info(dev, "internal pll is used.\n"); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_RXENA, + 0); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_MISC_OFFSET, + IMX8QM_MISC_IOB_TXENA, + IMX8QM_MISC_IOB_TXENA); + + } regmap_update_bits(imxpriv->gpr, IMX8QM_CSR_MISC_OFFSET, IMX8QM_MISC_PHYX1_EPCS_SEL, @@ -560,6 +759,21 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 | IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0); + /* APB reset */ + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_PHYX1_OFFSET, + IMX8QM_PHY_APB_RSTN_0, + IMX8QM_PHY_APB_RSTN_0); + + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_EPCS_TXDEEMP, + IMX8QM_SATA_CTRL_EPCS_TXDEEMP); + regmap_update_bits(imxpriv->gpr, + IMX8QM_CSR_SATA_OFFSET, + IMX8QM_SATA_CTRL_EPCS_TXDEEMP_SEL, + IMX8QM_SATA_CTRL_EPCS_TXDEEMP_SEL); + /* clear PHY RST, then set it */ regmap_update_bits(imxpriv->gpr, IMX8QM_CSR_SATA_OFFSET, @@ -586,12 +800,6 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) IMX8QM_SATA_CTRL_RESET_N, IMX8QM_SATA_CTRL_RESET_N); - /* APB reset */ - regmap_update_bits(imxpriv->gpr, - IMX8QM_CSR_PHYX1_OFFSET, - IMX8QM_PHY_APB_RSTN_0, - IMX8QM_PHY_APB_RSTN_0); - for (i = 0; i < 100; i++) { reg = IMX8QM_CSR_PHYX1_OFFSET + IMX8QM_CSR_PHYX_STTS0_OFFSET; @@ -606,18 +814,68 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) dev_err(dev, "TX PLL of the PHY is not locked\n"); ret = -ENODEV; } else { - writeb(imxpriv->imped_ratio, imxpriv->phy_base + - IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET); - writeb(imxpriv->imped_ratio, imxpriv->phy_base + - IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET); - reg = readb(imxpriv->phy_base + - IMX8QM_SATA_PHY_RX_IMPED_RATIO_OFFSET); + for (i = 0; i < 1000; i++) { + reg = readb(imxpriv->phy_base + + IMX8QM_SATA_PHY_REG48_PMA_STATUS); + if (reg & IMX8QM_SATA_PHY_REG48_PMA_RDY) + break; + udelay(10); + } + if ((reg & IMX8QM_SATA_PHY_REG48_PMA_RDY) == 0) { + dev_err(dev, "Calibration is NOT finished.\n"); + ret = -ENODEV; + goto err_out; + } + + writeb(imxpriv->imped_ratio, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG03_RX_IMPED_RATIO); + writeb(imxpriv->imped_ratio, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG09_TX_IMPED_RATIO); + reg = readb(imxpriv->phy_base + + IMX8QM_SATA_PHY_REG03_RX_IMPED_RATIO); if (unlikely(reg != imxpriv->imped_ratio)) dev_info(dev, "Can't set PHY RX impedance ratio.\n"); - reg = readb(imxpriv->phy_base + - IMX8QM_SATA_PHY_TX_IMPED_RATIO_OFFSET); + reg = readb(imxpriv->phy_base + + IMX8QM_SATA_PHY_REG09_TX_IMPED_RATIO); if (unlikely(reg != imxpriv->imped_ratio)) dev_info(dev, "Can't set PHY TX impedance ratio.\n"); + + /* RxWaterMark setting */ + val = readl(hpriv->mmio + IMX8QM_SATA_AHCI_VEND_PTC); + val &= ~IMX8QM_SATA_AHCI_VEND_PTC_RXWM_MASK; + val |= IMX8QM_SATA_AHCI_VEND_PTC_RXWM; + writel(val, hpriv->mmio + IMX8QM_SATA_AHCI_VEND_PTC); + + /* Configure the tx_amplitude to pass the tests. */ + writeb(IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN0, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG24_TX_AMP_RATIO_MARGIN0); + writeb(IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN1, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG25_TX_AMP_RATIO_MARGIN1); + writeb(IMX8QM_SATA_PHY_TX_AMP_RATIO_MARGIN2, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG26_TX_AMP_RATIO_MARGIN2); + + /* Adjust the OOB COMINIT/COMWAKE to pass the tests. */ + writeb(IMX8QM_SATA_PHY_GEN1_TX_POST_CURSOR_RATIO, + imxpriv->phy_base + + IMX8QM_SATA_PHY_REG10_TX_POST_CURSOR_RATIO); + writeb(IMX8QM_SATA_PHY_GEN2_TX_POST_CURSOR_RATIO, + imxpriv->phy_base + + IMX8QM_SATA_PHY_REG22_TX_POST_CURSOR_RATIO); + + writeb(IMX8QM_SATA_PHY_UPDATE_SETTING, imxpriv->phy_base + + IMX8QM_SATA_PHY_REG128_UPDATE_SETTING); + + reg = IMX8QM_SATA_PP2CFG_COMINIT_NEGATE_MIN | + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP | + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP_MAX | + IMX8QM_SATA_PP2CFG_COMINT_BURST_GAP_MIN; + writel(reg, hpriv->mmio + IMX8QM_SATA_PP2CFG_OFFSET); + reg = IMX8QM_SATA_PP3CFG_COMWAKE_NEGATE_MIN | + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP | + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP_MAX | + IMX8QM_SATA_PP3CFG_COMWAKE_BURST_GAP_MIN; + writel(reg, hpriv->mmio + IMX8QM_SATA_PP3CFG_OFFSET); + usleep_range(50, 100); /* @@ -630,15 +888,11 @@ static int imx8_sata_enable(struct ahci_host_priv *hpriv) return ret; } +err_out: clk_disable_unprepare(imxpriv->phy_apbclk); -disable_epcs_rx_clk: - clk_disable_unprepare(imxpriv->epcs_rx_clk); -disable_epcs_tx_clk: - clk_disable_unprepare(imxpriv->epcs_tx_clk); -disable_phy_pclk1: clk_disable_unprepare(imxpriv->phy_pclk1); -disable_phy_pclk0: clk_disable_unprepare(imxpriv->phy_pclk0); + imx8_sata_clk_disable(imxpriv); return ret; } @@ -692,6 +946,9 @@ static int imx_sata_enable(struct ahci_host_priv *hpriv) } } else if (imxpriv->type == AHCI_IMX8QM) { ret = imx8_sata_enable(hpriv); + if (ret) + goto disable_clk; + } usleep_range(1000, 2000); @@ -730,8 +987,7 @@ static void imx_sata_disable(struct ahci_host_priv *hpriv) break; case AHCI_IMX8QM: - clk_disable_unprepare(imxpriv->epcs_rx_clk); - clk_disable_unprepare(imxpriv->epcs_tx_clk); + imx8_sata_clk_disable(imxpriv); break; default: @@ -754,7 +1010,8 @@ static void ahci_imx_error_handler(struct ata_port *ap) ahci_error_handler(ap); - if (!(imxpriv->first_time) || ahci_imx_hotplug) + if (!(imxpriv->first_time) || ahci_imx_hotplug || + (imxpriv->type == AHCI_IMX8QM)) return; imxpriv->first_time = false; @@ -980,12 +1237,26 @@ static struct scsi_host_template ahci_platform_sht = { static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) { - struct resource *phy_res; + void __iomem *iomem; + struct resource *res, *phy_res; struct platform_device *pdev = imxpriv->ahci_pdev; struct device_node *np = dev->of_node; + struct regmap_config regconfig = imx_sata_regconfig; + + if (!(dev->bus_dma_limit)) { + dev->bus_dma_limit = DMA_BIT_MASK(32); + dev_info(dev, "imx8qm sata only supports 32bit dma.\n"); + } + if (of_property_read_u32(np, "ext_osc", &imxpriv->ext_osc) < 0) { + dev_info(dev, "ext_osc is not specified.\n"); + /* Use the external osc as ref clk defaultly. */ + imxpriv->ext_osc = 1; + } - if (of_property_read_u32(np, "fsl,phy-imp", &imxpriv->imped_ratio)) - imxpriv->imped_ratio = IMX8QM_SATA_PHY_IMPED_RATIO_85OHM; + if (of_property_read_u32(np, "fsl,phy-imp", &imxpriv->imped_ratio)) { + dev_info(dev, "phy impedance ratio is not specified.\n"); + imxpriv->imped_ratio = IMX8QM_SATA_PHY_IMPED_RATIO_100OHM; + } phy_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "phy"); if (phy_res) { imxpriv->phy_base = devm_ioremap(dev, phy_res->start, @@ -998,11 +1269,19 @@ static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) dev_err(dev, "missing *phy* reg region.\n"); return -ENOMEM; } - imxpriv->gpr = - syscon_regmap_lookup_by_phandle(np, "hsio"); - if (IS_ERR(imxpriv->gpr)) { - dev_err(dev, "unable to find gpr registers\n"); - return PTR_ERR(imxpriv->gpr); + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsio"); + if (res) { + iomem = devm_ioremap(dev, res->start, resource_size(res)); + if (IS_ERR(iomem)) + return PTR_ERR(iomem); + imxpriv->gpr = devm_regmap_init_mmio(dev, iomem, ®config); + if (IS_ERR(imxpriv->gpr)) { + dev_err(dev, "failed to init register map\n"); + return PTR_ERR(imxpriv->gpr); + } + } else { + dev_err(dev, "missing *hsio* reg region.\n"); + return -ENOMEM; } imxpriv->epcs_tx_clk = devm_clk_get(dev, "epcs_tx"); @@ -1030,6 +1309,36 @@ static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) dev_err(dev, "can't get phy_apbclk clock.\n"); return PTR_ERR(imxpriv->phy_apbclk); } + imxpriv->per_clk0 = devm_clk_get(dev, "per_clk0"); + if (IS_ERR(imxpriv->per_clk0)) { + dev_err(dev, "can't get per_clk0 clock.\n"); + return PTR_ERR(imxpriv->per_clk0); + } + imxpriv->per_clk1 = devm_clk_get(dev, "per_clk1"); + if (IS_ERR(imxpriv->per_clk1)) { + dev_err(dev, "can't get per_clk1 clock.\n"); + return PTR_ERR(imxpriv->per_clk1); + } + imxpriv->per_clk2 = devm_clk_get(dev, "per_clk2"); + if (IS_ERR(imxpriv->per_clk2)) { + dev_err(dev, "can't get per_clk2 clock.\n"); + return PTR_ERR(imxpriv->per_clk2); + } + imxpriv->per_clk3 = devm_clk_get(dev, "per_clk3"); + if (IS_ERR(imxpriv->per_clk3)) { + dev_err(dev, "can't get per_clk3 clock.\n"); + return PTR_ERR(imxpriv->per_clk3); + } + imxpriv->per_clk4 = devm_clk_get(dev, "per_clk4"); + if (IS_ERR(imxpriv->per_clk4)) { + dev_err(dev, "can't get per_clk4 clock.\n"); + return PTR_ERR(imxpriv->per_clk4); + } + imxpriv->per_clk5 = devm_clk_get(dev, "per_clk5"); + if (IS_ERR(imxpriv->per_clk5)) { + dev_err(dev, "can't get per_clk5 clock.\n"); + return PTR_ERR(imxpriv->per_clk5); + } /* Fetch GPIO, then enable the external OSC */ imxpriv->clkreq_gpiod = devm_gpiod_get_optional(dev, "clkreq", @@ -1042,6 +1351,104 @@ static int imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv) return 0; } +static ssize_t ahci_bist_pattern_show(struct device *dev, + struct device_attribute *devattr, char *buf) +{ + u32 bist_pattern; + struct ahci_host_priv *hpriv = dev_get_drvdata(dev); + + bist_pattern = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + bist_pattern = bist_pattern & IMX8QM_SATA_PPCFG_BIST_PATTERN_MASK; + bist_pattern = bist_pattern >> IMX8QM_SATA_PPCFG_BIST_PATTERN_OFFSET; + return sprintf(buf, "imx-ahci-bist-pattern %s%s%s%s.\n", + (BIT(0) << bist_pattern) & BIT(0) ? "LBP " : "", + (BIT(0) << bist_pattern) & BIT(1) ? "LFTP " : "", + (BIT(0) << bist_pattern) & BIT(2) ? "MFTP " : "", + (BIT(0) << bist_pattern) & BIT(3) ? "HFTP " : ""); +} + +static ssize_t ahci_bist_pattern_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + int ret; + u32 bist_pattern, val, timeout; + struct ahci_host_priv *hpriv = dev_get_drvdata(dev); + + ret = sscanf(buf, "%x\n", &bist_pattern); + if (ret != 1) + return -EINVAL; + if ((bist_pattern > 3)) { + dev_err(dev, "LBP 0, LFTP 1, MFTP 2, HFTP 3.\n"); + return -1; + } + dev_info(dev, "Try to enable %s%s%s%s pattern.\n", + (BIT(0) << bist_pattern) & BIT(0) ? "LBP " : "", + (BIT(0) << bist_pattern) & BIT(1) ? "LFTP " : "", + (BIT(0) << bist_pattern) & BIT(2) ? "MFTP " : "", + (BIT(0) << bist_pattern) & BIT(3) ? "HFTP " : ""); + + dev_info(dev, "Clear BIST enable.\n"); + val = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + writel(val & (~IMX8QM_SATA_PPCFG_BIST_PATTERN_EN), + hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + + /* put device into listen mode, first set PxSCTL.DET to 0 */ + dev_info(dev, "Turn off device detection.\n"); + val = readl(hpriv->mmio + 0x100 + PORT_SCR_CTL); + writel(val & ~0xf, hpriv->mmio + 0x100 + PORT_SCR_CTL); + + dev_info(dev, "Force phy ready, then wait.\n"); + val = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + writel(val | IMX8QM_SATA_PPCFG_FORCE_PHY_RDY, + hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + + timeout = 1000; + do { + val = readl(hpriv->mmio + 0x100 + PORT_SCR_STAT); + if ((val & 0xf) > 1) + break; + mdelay(1); + } while (--timeout); + if (timeout == 0) + dev_info(dev, "Error, wait for phy ready timeout.\n"); + else + dev_info(dev, "Get phy ready, and Gen%d mode is set.\n", + (val & 0xF0) >> 4); + + /* clear SError */ + dev_info(dev, "Clear error reg.\n"); + val = readl(hpriv->mmio + 0x100 + PORT_SCR_ERR); + writel(val, hpriv->mmio + 0x100 + PORT_SCR_ERR); + + dev_info(dev, "Select BIST pattern.\n"); + val = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + val &= (~IMX8QM_SATA_PPCFG_BIST_PATTERN_MASK); + val |= (bist_pattern << IMX8QM_SATA_PPCFG_BIST_PATTERN_OFFSET); + writel(val, hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + + dev_info(dev, "Set no aligns in BIST pattern.\n"); + val = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + writel(val | IMX8QM_SATA_PPCFG_BIST_PATTERN_NOALIGNS, + hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + + dev_info(dev, "BIST enable.\n"); + val = readl(hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + writel(val | IMX8QM_SATA_PPCFG_BIST_PATTERN_EN, + hpriv->mmio + IMX8QM_SATA_PPCFG_OFFSET); + + return count; +} + +static DEVICE_ATTR_RW(ahci_bist_pattern); + +static struct attribute *imx_ahci_attrs[] = { + &dev_attr_ahci_bist_pattern.attr, + NULL +}; + +static struct attribute_group imx_ahci_attrgroup = { + .attrs = imx_ahci_attrs, +}; static int imx_ahci_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; @@ -1076,12 +1483,6 @@ static int imx_ahci_probe(struct platform_device *pdev) return PTR_ERR(imxpriv->sata_ref_clk); } - imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); - if (IS_ERR(imxpriv->ahb_clk)) { - dev_err(dev, "can't get ahb clock.\n"); - return PTR_ERR(imxpriv->ahb_clk); - } - if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { u32 reg_value; @@ -1158,20 +1559,68 @@ static int imx_ahci_probe(struct platform_device *pdev) writel(reg_val, hpriv->mmio + HOST_PORTS_IMPL); } - reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; - writel(reg_val, hpriv->mmio + IMX_TIMER1MS); + imxpriv->ahb_clk = devm_clk_get(dev, "ahb"); + if (IS_ERR(imxpriv->ahb_clk)) { + dev_info(dev, "no ahb clock.\n"); + } else { + /* + * AHB clock is only used to configure the vendor specified + * TIMER1MS register. Set it if the AHB clock is defined. + */ + reg_val = clk_get_rate(imxpriv->ahb_clk) / 1000; + writel(reg_val, hpriv->mmio + IMX_TIMER1MS); + } - ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, - &ahci_platform_sht); - if (ret) - goto disable_sata; + /* + * Due to IP bug on the Synopsis 3.00 SATA version, + * which is present on mx6q, and not on mx53, + * we should use sg_tablesize = 1 for reliable operation + */ + if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { + dma_addr_t dma; - return 0; + ahci_platform_sht.sg_tablesize = 1; + + sg_io_buffer_hack = dma_alloc_coherent(dev, 0x10000, + &dma, GFP_KERNEL); + if (!sg_io_buffer_hack) { + ret = -ENOMEM; + goto disable_sata; + } + } + + if (imxpriv->type == AHCI_IMX8QM && bist_enable) { + dev_info(dev, "AHCI SATA compliance test patterns.\n"); + ret = clk_prepare_enable(imxpriv->phy_pclk0); + if (ret < 0) + dev_err(dev, "can't enable phy pclk0.\n"); + ret = clk_prepare_enable(imxpriv->phy_pclk1); + if (ret < 0) + dev_err(dev, "can't enable phy pclk1.\n"); + ret = clk_prepare_enable(imxpriv->phy_apbclk); + if (ret < 0) + dev_err(dev, "can't get sata_phy_apbclk clock.\n"); + + dev_set_drvdata(dev, hpriv); + ret = sysfs_create_group(&pdev->dev.kobj, &imx_ahci_attrgroup); + if (ret) + ret = -EINVAL; + dev_info(dev, "Register AHCI SATA BIST sysfile callback.\n"); + } else { + + ret = ahci_platform_init_host(pdev, hpriv, &ahci_imx_port_info, + &ahci_platform_sht); + if (ret) + goto disable_sata; + } + + return ret; disable_sata: imx_sata_disable(hpriv); disable_clk: clk_disable_unprepare(imxpriv->sata_clk); + return ret; } @@ -1225,7 +1674,21 @@ static struct platform_driver imx_ahci_driver = { .pm = &ahci_imx_pm_ops, }, }; -module_platform_driver(imx_ahci_driver); + +static int __init imx_ahci_init(void) +{ + int ret; + + ret = platform_driver_register(&imx_ahci_driver); + if (ret) + pr_err("Unable to initialize imx ahci driver\n"); + else + pr_info("imx ahci driver is registered.\n"); + + return ret; +} + +device_initcall(imx_ahci_init); MODULE_DESCRIPTION("Freescale i.MX AHCI SATA platform driver"); MODULE_AUTHOR("Richard Zhu "); diff --git a/drivers/ata/ahci_qoriq.c b/drivers/ata/ahci_qoriq.c index 5b46fc9ae..e057164ac 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); DPRINTK("ENTER\n"); @@ -128,6 +270,8 @@ static int ahci_qoriq_hardreset(struct ata_link *link, unsigned int *class, tf.command = 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); @@ -289,9 +433,7 @@ static int ahci_qoriq_probe(struct platform_device *pdev) qoriq_priv->type = (enum ahci_qoriq_type)acpi_id->driver_data; if (unlikely(!ecc_initialized)) { - res = platform_get_resource_byname(pdev, - IORESOURCE_MEM, - "sata-ecc"); + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (res) { qoriq_priv->ecc_addr = devm_ioremap_resource(dev, res);