// SPDX-License-Identifier: GPL-2.0-only /* * copyright (c) 2013 Freescale Semiconductor, Inc. * Freescale IMX AHCI SATA platform driver * * based on the AHCI SATA platform driver by Jeff Garzik and Anton Vorontsov
*/
/* Assert or deassert the bit */
crval = readl(mmio + IMX_P0PHYCR); if (assert)
crval |= bit; else
crval &= ~bit;
writel(crval, mmio + IMX_P0PHYCR);
/* Wait for the cr_ack signal */ do {
srval = readl(mmio + IMX_P0PHYSR); if ((assert ? srval : ~srval) & IMX_P0PHYSR_CR_ACK) break;
usleep_range(100, 200);
} while (--timeout);
/* Supply the data on cr_data_in */
writel(crval, mmio + IMX_P0PHYCR);
/* Assert the cr_cap_data signal */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, true); if (ret) return ret;
/* Deassert cr_cap_data */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_CAP_DATA, false); if (ret) return ret;
if (val & IMX_CLOCK_RESET_RESET) { /* * In case we're resetting the phy, it's unable to acknowledge, * so we return immediately here.
*/
crval |= IMX_P0PHYCR_CR_WRITE;
writel(crval, mmio + IMX_P0PHYCR); goto out;
}
/* Assert the cr_write signal */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, true); if (ret) return ret;
/* Deassert cr_write */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_WRITE, false); if (ret) return ret;
out: return 0;
}
staticint imx_phy_reg_read(u16 *val, void __iomem *mmio)
{ int ret;
/* Assert the cr_read signal */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, true); if (ret) return ret;
/* Capture the data from cr_data_out[] */
*val = readl(mmio + IMX_P0PHYSR) & IMX_P0PHYSR_CR_DATA_OUT;
/* Deassert cr_read */
ret = imx_phy_crbit_assert(mmio, IMX_P0PHYCR_CR_READ, false); if (ret) return ret;
if (imxpriv->type == AHCI_IMX6QP) { /* 6qp adds the sata reset mechanism, use it for 6qp sata */
regmap_update_bits(imxpriv->gpr, IOMUXC_GPR5,
IMX6Q_GPR5_SATA_SW_PD, 0);
/* Reset SATA PHY by setting RESET bit of PHY register CLOCK_RESET */
ret = imx_phy_reg_addressing(IMX_CLOCK_RESET, mmio); if (ret) return ret;
ret = imx_phy_reg_write(IMX_CLOCK_RESET_RESET, mmio); if (ret) return ret;
/* Wait for PHY RX_PLL to be stable */ do {
usleep_range(100, 200);
ret = imx_phy_reg_addressing(IMX_LANE0_OUT_STAT, mmio); if (ret) return ret;
ret = imx_phy_reg_read(&val, mmio); if (ret) return ret; if (val & IMX_LANE0_OUT_STAT_RX_PLL_STATE) break;
} while (--timeout);
/* * Since "REXT" pin is only present for first lane of i.MX8QM * PHY, its calibration results will be stored, passed through * to the second lane PHY, and shared with all three lane PHYs. * * Initialize the first two lane PHYs here, although only the * third lane PHY is used by SATA.
*/
ret = phy_init(imxpriv->cali_phy0); if (ret) {
dev_err(dev, "cali PHY init failed\n"); return ret;
}
ret = phy_power_on(imxpriv->cali_phy0); if (ret) {
dev_err(dev, "cali PHY power on failed\n"); goto err_cali_phy0_exit;
}
ret = phy_init(imxpriv->cali_phy1); if (ret) {
dev_err(dev, "cali PHY1 init failed\n"); goto err_cali_phy0_off;
}
ret = phy_power_on(imxpriv->cali_phy1); if (ret) {
dev_err(dev, "cali PHY1 power on failed\n"); goto err_cali_phy1_exit;
}
ret = phy_init(imxpriv->sata_phy); if (ret) {
dev_err(dev, "sata PHY init failed\n"); goto err_cali_phy1_off;
}
ret = phy_set_mode(imxpriv->sata_phy, PHY_MODE_SATA); if (ret) {
dev_err(dev, "unable to set SATA PHY mode\n"); goto err_sata_phy_exit;
}
ret = phy_power_on(imxpriv->sata_phy); if (ret) {
dev_err(dev, "sata PHY power up failed\n"); goto err_sata_phy_exit;
}
/* The cali_phy# can be turned off after SATA PHY is initialized. */
phy_power_off(imxpriv->cali_phy1);
phy_exit(imxpriv->cali_phy1);
phy_power_off(imxpriv->cali_phy0);
phy_exit(imxpriv->cali_phy0);
/* RxWaterMark setting */
val = readl(hpriv->mmio + IMX8QM_SATA_AHCI_PTC);
val &= ~IMX8QM_SATA_AHCI_PTC_RXWM_MASK;
val |= IMX8QM_SATA_AHCI_PTC_RXWM;
writel(val, hpriv->mmio + IMX8QM_SATA_AHCI_PTC);
ret = ahci_platform_enable_regulators(hpriv); if (ret) return ret;
ret = clk_prepare_enable(imxpriv->sata_ref_clk); if (ret < 0) goto disable_regulator;
if (imxpriv->type == AHCI_IMX6Q || imxpriv->type == AHCI_IMX6QP) { /* * set PHY Parameters, two steps to configure the GPR13, * one write for rest of parameters, mask of first write * is 0x07ffffff, and the other one write for setting * the mpll_clk_en.
*/
regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
IMX6Q_GPR13_SATA_RX_EQ_VAL_MASK |
IMX6Q_GPR13_SATA_RX_LOS_LVL_MASK |
IMX6Q_GPR13_SATA_RX_DPLL_MODE_MASK |
IMX6Q_GPR13_SATA_SPD_MODE_MASK |
IMX6Q_GPR13_SATA_MPLL_SS_EN |
IMX6Q_GPR13_SATA_TX_ATTEN_MASK |
IMX6Q_GPR13_SATA_TX_BOOST_MASK |
IMX6Q_GPR13_SATA_TX_LVL_MASK |
IMX6Q_GPR13_SATA_MPLL_CLK_EN |
IMX6Q_GPR13_SATA_TX_EDGE_RATE,
imxpriv->phy_params);
regmap_update_bits(imxpriv->gpr, IOMUXC_GPR13,
IMX6Q_GPR13_SATA_MPLL_CLK_EN,
IMX6Q_GPR13_SATA_MPLL_CLK_EN);
usleep_range(100, 200);
ret = imx_sata_phy_reset(hpriv); if (ret) {
dev_err(dev, "failed to reset phy: %d\n", ret); goto disable_clk;
}
} elseif (imxpriv->type == AHCI_IMX8QM) {
ret = imx8_sata_enable(hpriv); if (ret) goto disable_clk;
if (!(imxpriv->first_time) || ahci_imx_hotplug) return;
imxpriv->first_time = false;
ata_for_each_dev(dev, &ap->link, ENABLED) return; /* * Disable link to save power. An imx ahci port can't be recovered * without full reset once the pddq mode is enabled making it * impossible to use as part of libata LPM.
*/
reg_val = readl(mmio + IMX_P0PHYCR);
writel(reg_val | IMX_P0PHYCR_TEST_PDDQ, mmio + IMX_P0PHYCR);
imx_sata_disable(hpriv);
imxpriv->no_device = true;
dev_info(ap->dev, "no device found, disabling link.\n");
dev_info(ap->dev, "pass " MODULE_PARAM_PREFIX ".hotplug=1 to enable hotplug\n");
}
if (imxpriv->type == AHCI_IMX53)
ret = ahci_pmp_retry_srst_ops.reset.softreset(link, class,
deadline); else
ret = ahci_ops.reset.softreset(link, class, deadline);
staticint imx8_sata_probe(struct device *dev, struct imx_ahci_priv *imxpriv)
{
imxpriv->sata_phy = devm_phy_get(dev, "sata-phy"); if (IS_ERR(imxpriv->sata_phy)) return dev_err_probe(dev, PTR_ERR(imxpriv->sata_phy), "Failed to get sata_phy\n");
imxpriv->cali_phy0 = devm_phy_get(dev, "cali-phy0"); if (IS_ERR(imxpriv->cali_phy0)) return dev_err_probe(dev, PTR_ERR(imxpriv->cali_phy0), "Failed to get cali_phy0\n");
imxpriv->cali_phy1 = devm_phy_get(dev, "cali-phy1"); if (IS_ERR(imxpriv->cali_phy1)) return dev_err_probe(dev, PTR_ERR(imxpriv->cali_phy1), "Failed to get cali_phy1\n"); return 0;
}
imxpriv->sata_clk = devm_clk_get(dev, "sata"); if (IS_ERR(imxpriv->sata_clk)) {
dev_err(dev, "can't get sata clock.\n"); return PTR_ERR(imxpriv->sata_clk);
}
imxpriv->sata_ref_clk = devm_clk_get(dev, "sata_ref"); if (IS_ERR(imxpriv->sata_ref_clk)) {
dev_err(dev, "can't get sata_ref clock.\n"); return PTR_ERR(imxpriv->sata_ref_clk);
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.