in ahci_imx.c [445:644]
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;
/* configure the hsio for sata */
ret = clk_prepare_enable(imxpriv->phy_pclk0);
if (ret < 0) {
dev_err(dev, "can't enable phy_pclk0.\n");
return ret;
}
ret = clk_prepare_enable(imxpriv->phy_pclk1);
if (ret < 0) {
dev_err(dev, "can't enable phy_pclk1.\n");
goto disable_phy_pclk0;
}
ret = clk_prepare_enable(imxpriv->epcs_tx_clk);
if (ret < 0) {
dev_err(dev, "can't enable epcs_tx_clk.\n");
goto disable_phy_pclk1;
}
ret = clk_prepare_enable(imxpriv->epcs_rx_clk);
if (ret < 0) {
dev_err(dev, "can't enable epcs_rx_clk.\n");
goto disable_epcs_tx_clk;
}
ret = clk_prepare_enable(imxpriv->phy_apbclk);
if (ret < 0) {
dev_err(dev, "can't enable phy_apbclk.\n");
goto disable_epcs_rx_clk;
}
/* Configure PHYx2 PIPE_RSTN */
regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEA_OFFSET +
IMX8QM_CSR_PCIE_CTRL2_OFFSET, &val);
if ((val & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
/* The link of the PCIEA of HSIO is down */
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_PHYX2_OFFSET,
IMX8QM_PHY_PIPE_RSTN_0 |
IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0,
IMX8QM_PHY_PIPE_RSTN_0 |
IMX8QM_PHY_PIPE_RSTN_OVERRIDE_0);
}
regmap_read(imxpriv->gpr, IMX8QM_CSR_PCIEB_OFFSET +
IMX8QM_CSR_PCIE_CTRL2_OFFSET, ®);
if ((reg & IMX8QM_CTRL_LTSSM_ENABLE) == 0) {
/* The link of the PCIEB of HSIO is down */
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_PHYX2_OFFSET,
IMX8QM_PHY_PIPE_RSTN_1 |
IMX8QM_PHY_PIPE_RSTN_OVERRIDE_1,
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);
}
/* set PWR_RST and BT_RST of csr_pciea */
val = IMX8QM_CSR_PCIEA_OFFSET + IMX8QM_CSR_PCIE_CTRL2_OFFSET;
regmap_update_bits(imxpriv->gpr,
val,
IMX8QM_CTRL_BUTTON_RST_N,
IMX8QM_CTRL_BUTTON_RST_N);
regmap_update_bits(imxpriv->gpr,
val,
IMX8QM_CTRL_POWER_UP_RST_N,
IMX8QM_CTRL_POWER_UP_RST_N);
/* PHYX1_MODE to SATA */
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_PHYX1_OFFSET,
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);
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_MISC_OFFSET,
IMX8QM_MISC_PHYX1_EPCS_SEL,
IMX8QM_MISC_PHYX1_EPCS_SEL);
/*
* It is possible, for PCIe and SATA are sharing
* the same clock source, HPLL or external oscillator.
* When PCIe is in low power modes (L1.X or L2 etc),
* the clock source can be turned off. In this case,
* if this clock source is required to be toggling by
* SATA, then SATA functions will be abnormal.
* Set the override here to avoid it.
*/
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_MISC_OFFSET,
IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0,
IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_1 |
IMX8QM_MISC_CLKREQN_OUT_OVERRIDE_0 |
IMX8QM_MISC_CLKREQN_IN_OVERRIDE_1 |
IMX8QM_MISC_CLKREQN_IN_OVERRIDE_0);
/* clear PHY RST, then set it */
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_SATA_OFFSET,
IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
0);
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_SATA_OFFSET,
IMX8QM_SATA_CTRL_EPCS_PHYRESET_N,
IMX8QM_SATA_CTRL_EPCS_PHYRESET_N);
/* CTRL RST: SET -> delay 1 us -> CLEAR -> SET */
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_SATA_OFFSET,
IMX8QM_SATA_CTRL_RESET_N,
IMX8QM_SATA_CTRL_RESET_N);
udelay(1);
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_SATA_OFFSET,
IMX8QM_SATA_CTRL_RESET_N,
0);
regmap_update_bits(imxpriv->gpr,
IMX8QM_CSR_SATA_OFFSET,
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;
regmap_read(imxpriv->gpr, reg, &val);
val &= IMX8QM_STTS0_LANE0_TX_PLL_LOCK;
if (val == IMX8QM_STTS0_LANE0_TX_PLL_LOCK)
break;
udelay(1);
}
if (val != IMX8QM_STTS0_LANE0_TX_PLL_LOCK) {
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);
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);
if (unlikely(reg != imxpriv->imped_ratio))
dev_info(dev, "Can't set PHY TX impedance ratio.\n");
usleep_range(50, 100);
/*
* To reduce the power consumption, gate off
* the PHY clks
*/
clk_disable_unprepare(imxpriv->phy_apbclk);
clk_disable_unprepare(imxpriv->phy_pclk1);
clk_disable_unprepare(imxpriv->phy_pclk0);
return ret;
}
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;
}