xref: /openbmc/linux/drivers/phy/qualcomm/phy-qcom-apq8064-sata.c (revision fe3cfe869d5e0453754cf2b4c75110276b5e8527)
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