197fb5e8dSThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
20b56e9a7SVivek Gautam /*
30b56e9a7SVivek Gautam * Copyright (c) 2014, The Linux Foundation. All rights reserved.
40b56e9a7SVivek Gautam */
50b56e9a7SVivek Gautam
60b56e9a7SVivek Gautam #include <linux/io.h>
738af68cbSChunfeng Yun #include <linux/iopoll.h>
80b56e9a7SVivek Gautam #include <linux/kernel.h>
90b56e9a7SVivek Gautam #include <linux/module.h>
100b56e9a7SVivek Gautam #include <linux/of.h>
110b56e9a7SVivek Gautam #include <linux/time.h>
120b56e9a7SVivek Gautam #include <linux/delay.h>
130b56e9a7SVivek Gautam #include <linux/clk.h>
140b56e9a7SVivek Gautam #include <linux/slab.h>
150b56e9a7SVivek Gautam #include <linux/platform_device.h>
160b56e9a7SVivek Gautam #include <linux/phy/phy.h>
170b56e9a7SVivek Gautam
180b56e9a7SVivek Gautam /* PHY registers */
190b56e9a7SVivek Gautam #define UNIPHY_PLL_REFCLK_CFG 0x000
200b56e9a7SVivek Gautam #define UNIPHY_PLL_PWRGEN_CFG 0x014
210b56e9a7SVivek Gautam #define UNIPHY_PLL_GLB_CFG 0x020
220b56e9a7SVivek Gautam #define UNIPHY_PLL_SDM_CFG0 0x038
230b56e9a7SVivek Gautam #define UNIPHY_PLL_SDM_CFG1 0x03C
240b56e9a7SVivek Gautam #define UNIPHY_PLL_SDM_CFG2 0x040
250b56e9a7SVivek Gautam #define UNIPHY_PLL_SDM_CFG3 0x044
260b56e9a7SVivek Gautam #define UNIPHY_PLL_SDM_CFG4 0x048
270b56e9a7SVivek Gautam #define UNIPHY_PLL_SSC_CFG0 0x04C
280b56e9a7SVivek Gautam #define UNIPHY_PLL_SSC_CFG1 0x050
290b56e9a7SVivek Gautam #define UNIPHY_PLL_SSC_CFG2 0x054
300b56e9a7SVivek Gautam #define UNIPHY_PLL_SSC_CFG3 0x058
310b56e9a7SVivek Gautam #define UNIPHY_PLL_LKDET_CFG0 0x05C
320b56e9a7SVivek Gautam #define UNIPHY_PLL_LKDET_CFG1 0x060
330b56e9a7SVivek Gautam #define UNIPHY_PLL_LKDET_CFG2 0x064
340b56e9a7SVivek Gautam #define UNIPHY_PLL_CAL_CFG0 0x06C
350b56e9a7SVivek Gautam #define UNIPHY_PLL_CAL_CFG8 0x08C
360b56e9a7SVivek Gautam #define UNIPHY_PLL_CAL_CFG9 0x090
370b56e9a7SVivek Gautam #define UNIPHY_PLL_CAL_CFG10 0x094
380b56e9a7SVivek Gautam #define UNIPHY_PLL_CAL_CFG11 0x098
390b56e9a7SVivek Gautam #define UNIPHY_PLL_STATUS 0x0C0
400b56e9a7SVivek Gautam
410b56e9a7SVivek Gautam #define SATA_PHY_SER_CTRL 0x100
420b56e9a7SVivek Gautam #define SATA_PHY_TX_DRIV_CTRL0 0x104
430b56e9a7SVivek Gautam #define SATA_PHY_TX_DRIV_CTRL1 0x108
440b56e9a7SVivek Gautam #define SATA_PHY_TX_IMCAL0 0x11C
450b56e9a7SVivek Gautam #define SATA_PHY_TX_IMCAL2 0x124
460b56e9a7SVivek Gautam #define SATA_PHY_RX_IMCAL0 0x128
470b56e9a7SVivek Gautam #define SATA_PHY_EQUAL 0x13C
480b56e9a7SVivek Gautam #define SATA_PHY_OOB_TERM 0x144
490b56e9a7SVivek Gautam #define SATA_PHY_CDR_CTRL0 0x148
500b56e9a7SVivek Gautam #define SATA_PHY_CDR_CTRL1 0x14C
510b56e9a7SVivek Gautam #define SATA_PHY_CDR_CTRL2 0x150
520b56e9a7SVivek Gautam #define SATA_PHY_CDR_CTRL3 0x154
530b56e9a7SVivek Gautam #define SATA_PHY_PI_CTRL0 0x168
540b56e9a7SVivek Gautam #define SATA_PHY_POW_DWN_CTRL0 0x180
550b56e9a7SVivek Gautam #define SATA_PHY_POW_DWN_CTRL1 0x184
560b56e9a7SVivek Gautam #define SATA_PHY_TX_DATA_CTRL 0x188
570b56e9a7SVivek Gautam #define SATA_PHY_ALIGNP 0x1A4
580b56e9a7SVivek Gautam #define SATA_PHY_TX_IMCAL_STAT 0x1E4
590b56e9a7SVivek Gautam #define SATA_PHY_RX_IMCAL_STAT 0x1E8
600b56e9a7SVivek Gautam
610b56e9a7SVivek Gautam #define UNIPHY_PLL_LOCK BIT(0)
620b56e9a7SVivek Gautam #define SATA_PHY_TX_CAL BIT(0)
630b56e9a7SVivek Gautam #define SATA_PHY_RX_CAL BIT(0)
640b56e9a7SVivek Gautam
650b56e9a7SVivek Gautam /* default timeout set to 1 sec */
660b56e9a7SVivek Gautam #define TIMEOUT_MS 10000
670b56e9a7SVivek Gautam #define DELAY_INTERVAL_US 100
680b56e9a7SVivek Gautam
690b56e9a7SVivek Gautam struct qcom_apq8064_sata_phy {
700b56e9a7SVivek Gautam void __iomem *mmio;
710b56e9a7SVivek Gautam struct clk *cfg_clk;
720b56e9a7SVivek Gautam struct device *dev;
730b56e9a7SVivek Gautam };
740b56e9a7SVivek Gautam
750b56e9a7SVivek Gautam /* Helper function to do poll and timeout */
poll_timeout(void __iomem * addr,u32 mask)7638af68cbSChunfeng Yun static int poll_timeout(void __iomem *addr, u32 mask)
770b56e9a7SVivek Gautam {
7838af68cbSChunfeng Yun u32 val;
790b56e9a7SVivek Gautam
8038af68cbSChunfeng Yun return readl_relaxed_poll_timeout(addr, val, (val & mask),
8138af68cbSChunfeng Yun DELAY_INTERVAL_US, TIMEOUT_MS * 1000);
820b56e9a7SVivek Gautam }
830b56e9a7SVivek Gautam
qcom_apq8064_sata_phy_init(struct phy * generic_phy)840b56e9a7SVivek Gautam static int qcom_apq8064_sata_phy_init(struct phy *generic_phy)
850b56e9a7SVivek Gautam {
860b56e9a7SVivek Gautam struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
870b56e9a7SVivek Gautam void __iomem *base = phy->mmio;
880b56e9a7SVivek Gautam int ret = 0;
890b56e9a7SVivek Gautam
900b56e9a7SVivek Gautam /* SATA phy initialization */
910b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_SER_CTRL);
920b56e9a7SVivek Gautam writel_relaxed(0xB1, base + SATA_PHY_POW_DWN_CTRL0);
930b56e9a7SVivek Gautam /* Make sure the power down happens before power up */
940b56e9a7SVivek Gautam mb();
950b56e9a7SVivek Gautam usleep_range(10, 60);
960b56e9a7SVivek Gautam
970b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
980b56e9a7SVivek Gautam writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
990b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
1000b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
1010b56e9a7SVivek Gautam writel_relaxed(0x02, base + SATA_PHY_TX_IMCAL2);
1020b56e9a7SVivek Gautam
1030b56e9a7SVivek Gautam /* Write UNIPHYPLL registers to configure PLL */
1040b56e9a7SVivek Gautam writel_relaxed(0x04, base + UNIPHY_PLL_REFCLK_CFG);
1050b56e9a7SVivek Gautam writel_relaxed(0x00, base + UNIPHY_PLL_PWRGEN_CFG);
1060b56e9a7SVivek Gautam
1070b56e9a7SVivek Gautam writel_relaxed(0x0A, base + UNIPHY_PLL_CAL_CFG0);
1080b56e9a7SVivek Gautam writel_relaxed(0xF3, base + UNIPHY_PLL_CAL_CFG8);
1090b56e9a7SVivek Gautam writel_relaxed(0x01, base + UNIPHY_PLL_CAL_CFG9);
1100b56e9a7SVivek Gautam writel_relaxed(0xED, base + UNIPHY_PLL_CAL_CFG10);
1110b56e9a7SVivek Gautam writel_relaxed(0x02, base + UNIPHY_PLL_CAL_CFG11);
1120b56e9a7SVivek Gautam
1130b56e9a7SVivek Gautam writel_relaxed(0x36, base + UNIPHY_PLL_SDM_CFG0);
1140b56e9a7SVivek Gautam writel_relaxed(0x0D, base + UNIPHY_PLL_SDM_CFG1);
1150b56e9a7SVivek Gautam writel_relaxed(0xA3, base + UNIPHY_PLL_SDM_CFG2);
1160b56e9a7SVivek Gautam writel_relaxed(0xF0, base + UNIPHY_PLL_SDM_CFG3);
1170b56e9a7SVivek Gautam writel_relaxed(0x00, base + UNIPHY_PLL_SDM_CFG4);
1180b56e9a7SVivek Gautam
1190b56e9a7SVivek Gautam writel_relaxed(0x19, base + UNIPHY_PLL_SSC_CFG0);
1200b56e9a7SVivek Gautam writel_relaxed(0xE1, base + UNIPHY_PLL_SSC_CFG1);
1210b56e9a7SVivek Gautam writel_relaxed(0x00, base + UNIPHY_PLL_SSC_CFG2);
1220b56e9a7SVivek Gautam writel_relaxed(0x11, base + UNIPHY_PLL_SSC_CFG3);
1230b56e9a7SVivek Gautam
1240b56e9a7SVivek Gautam writel_relaxed(0x04, base + UNIPHY_PLL_LKDET_CFG0);
1250b56e9a7SVivek Gautam writel_relaxed(0xFF, base + UNIPHY_PLL_LKDET_CFG1);
1260b56e9a7SVivek Gautam
1270b56e9a7SVivek Gautam writel_relaxed(0x02, base + UNIPHY_PLL_GLB_CFG);
1280b56e9a7SVivek Gautam /* make sure global config LDO power down happens before power up */
1290b56e9a7SVivek Gautam mb();
1300b56e9a7SVivek Gautam
1310b56e9a7SVivek Gautam writel_relaxed(0x03, base + UNIPHY_PLL_GLB_CFG);
1320b56e9a7SVivek Gautam writel_relaxed(0x05, base + UNIPHY_PLL_LKDET_CFG2);
1330b56e9a7SVivek Gautam
1340b56e9a7SVivek Gautam /* PLL Lock wait */
13538af68cbSChunfeng Yun ret = poll_timeout(base + UNIPHY_PLL_STATUS, UNIPHY_PLL_LOCK);
1360b56e9a7SVivek Gautam if (ret) {
1370b56e9a7SVivek Gautam dev_err(phy->dev, "poll timeout UNIPHY_PLL_STATUS\n");
1380b56e9a7SVivek Gautam return ret;
1390b56e9a7SVivek Gautam }
1400b56e9a7SVivek Gautam
1410b56e9a7SVivek Gautam /* TX Calibration */
14238af68cbSChunfeng Yun ret = poll_timeout(base + SATA_PHY_TX_IMCAL_STAT, SATA_PHY_TX_CAL);
1430b56e9a7SVivek Gautam if (ret) {
1440b56e9a7SVivek Gautam dev_err(phy->dev, "poll timeout SATA_PHY_TX_IMCAL_STAT\n");
1450b56e9a7SVivek Gautam return ret;
1460b56e9a7SVivek Gautam }
1470b56e9a7SVivek Gautam
1480b56e9a7SVivek Gautam /* RX Calibration */
14938af68cbSChunfeng Yun ret = poll_timeout(base + SATA_PHY_RX_IMCAL_STAT, SATA_PHY_RX_CAL);
1500b56e9a7SVivek Gautam if (ret) {
1510b56e9a7SVivek Gautam dev_err(phy->dev, "poll timeout SATA_PHY_RX_IMCAL_STAT\n");
1520b56e9a7SVivek Gautam return ret;
1530b56e9a7SVivek Gautam }
1540b56e9a7SVivek Gautam
155*11395c32SBo Liu /* SATA phy calibrated successfully, power up to functional mode */
1560b56e9a7SVivek Gautam writel_relaxed(0x3E, base + SATA_PHY_POW_DWN_CTRL1);
1570b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_RX_IMCAL0);
1580b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_TX_IMCAL0);
1590b56e9a7SVivek Gautam
1600b56e9a7SVivek Gautam writel_relaxed(0x00, base + SATA_PHY_POW_DWN_CTRL1);
1610b56e9a7SVivek Gautam writel_relaxed(0x59, base + SATA_PHY_CDR_CTRL0);
1620b56e9a7SVivek Gautam writel_relaxed(0x04, base + SATA_PHY_CDR_CTRL1);
1630b56e9a7SVivek Gautam writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL2);
1640b56e9a7SVivek Gautam writel_relaxed(0x00, base + SATA_PHY_PI_CTRL0);
1650b56e9a7SVivek Gautam writel_relaxed(0x00, base + SATA_PHY_CDR_CTRL3);
1660b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_POW_DWN_CTRL0);
1670b56e9a7SVivek Gautam
1680b56e9a7SVivek Gautam writel_relaxed(0x11, base + SATA_PHY_TX_DATA_CTRL);
1690b56e9a7SVivek Gautam writel_relaxed(0x43, base + SATA_PHY_ALIGNP);
1700b56e9a7SVivek Gautam writel_relaxed(0x04, base + SATA_PHY_OOB_TERM);
1710b56e9a7SVivek Gautam
1720b56e9a7SVivek Gautam writel_relaxed(0x01, base + SATA_PHY_EQUAL);
1730b56e9a7SVivek Gautam writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL0);
1740b56e9a7SVivek Gautam writel_relaxed(0x09, base + SATA_PHY_TX_DRIV_CTRL1);
1750b56e9a7SVivek Gautam
1760b56e9a7SVivek Gautam return 0;
1770b56e9a7SVivek Gautam }
1780b56e9a7SVivek Gautam
qcom_apq8064_sata_phy_exit(struct phy * generic_phy)1790b56e9a7SVivek Gautam static int qcom_apq8064_sata_phy_exit(struct phy *generic_phy)
1800b56e9a7SVivek Gautam {
1810b56e9a7SVivek Gautam struct qcom_apq8064_sata_phy *phy = phy_get_drvdata(generic_phy);
1820b56e9a7SVivek Gautam void __iomem *base = phy->mmio;
1830b56e9a7SVivek Gautam
1840b56e9a7SVivek Gautam /* Power down PHY */
1850b56e9a7SVivek Gautam writel_relaxed(0xF8, base + SATA_PHY_POW_DWN_CTRL0);
1860b56e9a7SVivek Gautam writel_relaxed(0xFE, base + SATA_PHY_POW_DWN_CTRL1);
1870b56e9a7SVivek Gautam
1880b56e9a7SVivek Gautam /* Power down PLL block */
1890b56e9a7SVivek Gautam writel_relaxed(0x00, base + UNIPHY_PLL_GLB_CFG);
1900b56e9a7SVivek Gautam
1910b56e9a7SVivek Gautam return 0;
1920b56e9a7SVivek Gautam }
1930b56e9a7SVivek Gautam
1940b56e9a7SVivek Gautam static const struct phy_ops qcom_apq8064_sata_phy_ops = {
1950b56e9a7SVivek Gautam .init = qcom_apq8064_sata_phy_init,
1960b56e9a7SVivek Gautam .exit = qcom_apq8064_sata_phy_exit,
1970b56e9a7SVivek Gautam .owner = THIS_MODULE,
1980b56e9a7SVivek Gautam };
1990b56e9a7SVivek Gautam
qcom_apq8064_sata_phy_probe(struct platform_device * pdev)2000b56e9a7SVivek Gautam static int qcom_apq8064_sata_phy_probe(struct platform_device *pdev)
2010b56e9a7SVivek Gautam {
2020b56e9a7SVivek Gautam struct qcom_apq8064_sata_phy *phy;
2030b56e9a7SVivek Gautam struct device *dev = &pdev->dev;
2040b56e9a7SVivek Gautam struct phy_provider *phy_provider;
2050b56e9a7SVivek Gautam struct phy *generic_phy;
2060b56e9a7SVivek Gautam int ret;
2070b56e9a7SVivek Gautam
2080b56e9a7SVivek Gautam phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
2090b56e9a7SVivek Gautam if (!phy)
2100b56e9a7SVivek Gautam return -ENOMEM;
2110b56e9a7SVivek Gautam
2128a7772cdSChunfeng Yun phy->mmio = devm_platform_ioremap_resource(pdev, 0);
2130b56e9a7SVivek Gautam if (IS_ERR(phy->mmio))
2140b56e9a7SVivek Gautam return PTR_ERR(phy->mmio);
2150b56e9a7SVivek Gautam
2160b56e9a7SVivek Gautam generic_phy = devm_phy_create(dev, NULL, &qcom_apq8064_sata_phy_ops);
2170b56e9a7SVivek Gautam if (IS_ERR(generic_phy)) {
2180b56e9a7SVivek Gautam dev_err(dev, "%s: failed to create phy\n", __func__);
2190b56e9a7SVivek Gautam return PTR_ERR(generic_phy);
2200b56e9a7SVivek Gautam }
2210b56e9a7SVivek Gautam
2220b56e9a7SVivek Gautam phy->dev = dev;
2230b56e9a7SVivek Gautam phy_set_drvdata(generic_phy, phy);
2240b56e9a7SVivek Gautam platform_set_drvdata(pdev, phy);
2250b56e9a7SVivek Gautam
2260b56e9a7SVivek Gautam phy->cfg_clk = devm_clk_get(dev, "cfg");
2270b56e9a7SVivek Gautam if (IS_ERR(phy->cfg_clk)) {
2280b56e9a7SVivek Gautam dev_err(dev, "Failed to get sata cfg clock\n");
2290b56e9a7SVivek Gautam return PTR_ERR(phy->cfg_clk);
2300b56e9a7SVivek Gautam }
2310b56e9a7SVivek Gautam
2320b56e9a7SVivek Gautam ret = clk_prepare_enable(phy->cfg_clk);
2330b56e9a7SVivek Gautam if (ret)
2340b56e9a7SVivek Gautam return ret;
2350b56e9a7SVivek Gautam
2360b56e9a7SVivek Gautam phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
2370b56e9a7SVivek Gautam if (IS_ERR(phy_provider)) {
2380b56e9a7SVivek Gautam clk_disable_unprepare(phy->cfg_clk);
2390b56e9a7SVivek Gautam dev_err(dev, "%s: failed to register phy\n", __func__);
2400b56e9a7SVivek Gautam return PTR_ERR(phy_provider);
2410b56e9a7SVivek Gautam }
2420b56e9a7SVivek Gautam
2430b56e9a7SVivek Gautam return 0;
2440b56e9a7SVivek Gautam }
2450b56e9a7SVivek Gautam
qcom_apq8064_sata_phy_remove(struct platform_device * pdev)24664299241SUwe Kleine-König static void qcom_apq8064_sata_phy_remove(struct platform_device *pdev)
2470b56e9a7SVivek Gautam {
2480b56e9a7SVivek Gautam struct qcom_apq8064_sata_phy *phy = platform_get_drvdata(pdev);
2490b56e9a7SVivek Gautam
2500b56e9a7SVivek Gautam clk_disable_unprepare(phy->cfg_clk);
2510b56e9a7SVivek Gautam }
2520b56e9a7SVivek Gautam
2530b56e9a7SVivek Gautam static const struct of_device_id qcom_apq8064_sata_phy_of_match[] = {
2540b56e9a7SVivek Gautam { .compatible = "qcom,apq8064-sata-phy" },
2550b56e9a7SVivek Gautam { },
2560b56e9a7SVivek Gautam };
2570b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, qcom_apq8064_sata_phy_of_match);
2580b56e9a7SVivek Gautam
2590b56e9a7SVivek Gautam static struct platform_driver qcom_apq8064_sata_phy_driver = {
2600b56e9a7SVivek Gautam .probe = qcom_apq8064_sata_phy_probe,
26164299241SUwe Kleine-König .remove_new = qcom_apq8064_sata_phy_remove,
2620b56e9a7SVivek Gautam .driver = {
2630b56e9a7SVivek Gautam .name = "qcom-apq8064-sata-phy",
2640b56e9a7SVivek Gautam .of_match_table = qcom_apq8064_sata_phy_of_match,
2650b56e9a7SVivek Gautam }
2660b56e9a7SVivek Gautam };
2670b56e9a7SVivek Gautam module_platform_driver(qcom_apq8064_sata_phy_driver);
2680b56e9a7SVivek Gautam
2690b56e9a7SVivek Gautam MODULE_DESCRIPTION("QCOM apq8064 SATA PHY driver");
2700b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
271