xref: /openbmc/linux/drivers/phy/qualcomm/phy-qcom-ipq806x-sata.c (revision 1ac731c529cd4d6adbce134754b51ff7d822b145)
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>
70b56e9a7SVivek Gautam #include <linux/kernel.h>
80b56e9a7SVivek Gautam #include <linux/module.h>
90b56e9a7SVivek Gautam #include <linux/of.h>
100b56e9a7SVivek Gautam #include <linux/of_address.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 struct qcom_ipq806x_sata_phy {
190b56e9a7SVivek Gautam 	void __iomem *mmio;
200b56e9a7SVivek Gautam 	struct clk *cfg_clk;
210b56e9a7SVivek Gautam 	struct device *dev;
220b56e9a7SVivek Gautam };
230b56e9a7SVivek Gautam 
240b56e9a7SVivek Gautam #define __set(v, a, b)	(((v) << (b)) & GENMASK(a, b))
250b56e9a7SVivek Gautam 
260b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0		0x200
270b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x)	__set(x, 17, 12)
280b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK	GENMASK(17, 12)
290b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x)	__set(x, 11, 6)
300b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK	GENMASK(11, 6)
310b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x)	__set(x, 5, 0)
320b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK	GENMASK(5, 0)
330b56e9a7SVivek Gautam 
340b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1		0x204
350b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x)	__set(x, 31, 21)
360b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x)	__set(x, 20, 14)
370b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK	GENMASK(20, 14)
380b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x)	__set(x, 13, 7)
390b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK	GENMASK(13, 7)
400b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x)	__set(x, 6, 0)
410b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK	GENMASK(6, 0)
420b56e9a7SVivek Gautam 
430b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM2		0x208
440b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM2_RX_EQ(x)	__set(x, 20, 18)
450b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM2_RX_EQ_MASK	GENMASK(20, 18)
460b56e9a7SVivek Gautam 
470b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM3		0x20C
480b56e9a7SVivek Gautam #define SATA_PHY_SSC_EN			0x8
490b56e9a7SVivek Gautam #define SATA_PHY_P0_PARAM4		0x210
500b56e9a7SVivek Gautam #define SATA_PHY_REF_SSP_EN		0x2
510b56e9a7SVivek Gautam #define SATA_PHY_RESET			0x1
520b56e9a7SVivek Gautam 
qcom_ipq806x_sata_phy_init(struct phy * generic_phy)530b56e9a7SVivek Gautam static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)
540b56e9a7SVivek Gautam {
550b56e9a7SVivek Gautam 	struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
560b56e9a7SVivek Gautam 	u32 reg;
570b56e9a7SVivek Gautam 
580b56e9a7SVivek Gautam 	/* Setting SSC_EN to 1 */
590b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);
600b56e9a7SVivek Gautam 	reg = reg | SATA_PHY_SSC_EN;
610b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);
620b56e9a7SVivek Gautam 
630b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &
640b56e9a7SVivek Gautam 			~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |
650b56e9a7SVivek Gautam 			  SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |
660b56e9a7SVivek Gautam 			  SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);
670b56e9a7SVivek Gautam 	reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);
680b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);
690b56e9a7SVivek Gautam 
700b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &
710b56e9a7SVivek Gautam 			~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |
720b56e9a7SVivek Gautam 			  SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |
730b56e9a7SVivek Gautam 			  SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);
740b56e9a7SVivek Gautam 	reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |
750b56e9a7SVivek Gautam 		SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |
760b56e9a7SVivek Gautam 		SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);
770b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);
780b56e9a7SVivek Gautam 
790b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &
800b56e9a7SVivek Gautam 		~SATA_PHY_P0_PARAM2_RX_EQ_MASK;
810b56e9a7SVivek Gautam 	reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);
820b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);
830b56e9a7SVivek Gautam 
840b56e9a7SVivek Gautam 	/* Setting PHY_RESET to 1 */
850b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
860b56e9a7SVivek Gautam 	reg = reg | SATA_PHY_RESET;
870b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
880b56e9a7SVivek Gautam 
890b56e9a7SVivek Gautam 	/* Setting REF_SSP_EN to 1 */
900b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
910b56e9a7SVivek Gautam 	reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;
920b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
930b56e9a7SVivek Gautam 
940b56e9a7SVivek Gautam 	/* make sure all changes complete before we let the PHY out of reset */
950b56e9a7SVivek Gautam 	mb();
960b56e9a7SVivek Gautam 
970b56e9a7SVivek Gautam 	/* sleep for max. 50us more to combine processor wakeups */
980b56e9a7SVivek Gautam 	usleep_range(20, 20 + 50);
990b56e9a7SVivek Gautam 
1000b56e9a7SVivek Gautam 	/* Clearing PHY_RESET to 0 */
1010b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
1020b56e9a7SVivek Gautam 	reg = reg & ~SATA_PHY_RESET;
1030b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
1040b56e9a7SVivek Gautam 
1050b56e9a7SVivek Gautam 	return 0;
1060b56e9a7SVivek Gautam }
1070b56e9a7SVivek Gautam 
qcom_ipq806x_sata_phy_exit(struct phy * generic_phy)1080b56e9a7SVivek Gautam static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)
1090b56e9a7SVivek Gautam {
1100b56e9a7SVivek Gautam 	struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);
1110b56e9a7SVivek Gautam 	u32 reg;
1120b56e9a7SVivek Gautam 
1130b56e9a7SVivek Gautam 	/* Setting PHY_RESET to 1 */
1140b56e9a7SVivek Gautam 	reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);
1150b56e9a7SVivek Gautam 	reg = reg | SATA_PHY_RESET;
1160b56e9a7SVivek Gautam 	writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);
1170b56e9a7SVivek Gautam 
1180b56e9a7SVivek Gautam 	return 0;
1190b56e9a7SVivek Gautam }
1200b56e9a7SVivek Gautam 
1210b56e9a7SVivek Gautam static const struct phy_ops qcom_ipq806x_sata_phy_ops = {
1220b56e9a7SVivek Gautam 	.init		= qcom_ipq806x_sata_phy_init,
1230b56e9a7SVivek Gautam 	.exit		= qcom_ipq806x_sata_phy_exit,
1240b56e9a7SVivek Gautam 	.owner		= THIS_MODULE,
1250b56e9a7SVivek Gautam };
1260b56e9a7SVivek Gautam 
qcom_ipq806x_sata_phy_probe(struct platform_device * pdev)1270b56e9a7SVivek Gautam static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)
1280b56e9a7SVivek Gautam {
1290b56e9a7SVivek Gautam 	struct qcom_ipq806x_sata_phy *phy;
1300b56e9a7SVivek Gautam 	struct device *dev = &pdev->dev;
1310b56e9a7SVivek Gautam 	struct phy_provider *phy_provider;
1320b56e9a7SVivek Gautam 	struct phy *generic_phy;
1330b56e9a7SVivek Gautam 	int ret;
1340b56e9a7SVivek Gautam 
1350b56e9a7SVivek Gautam 	phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
1360b56e9a7SVivek Gautam 	if (!phy)
1370b56e9a7SVivek Gautam 		return -ENOMEM;
1380b56e9a7SVivek Gautam 
1398a7772cdSChunfeng Yun 	phy->mmio = devm_platform_ioremap_resource(pdev, 0);
1400b56e9a7SVivek Gautam 	if (IS_ERR(phy->mmio))
1410b56e9a7SVivek Gautam 		return PTR_ERR(phy->mmio);
1420b56e9a7SVivek Gautam 
1430b56e9a7SVivek Gautam 	generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);
1440b56e9a7SVivek Gautam 	if (IS_ERR(generic_phy)) {
1450b56e9a7SVivek Gautam 		dev_err(dev, "%s: failed to create phy\n", __func__);
1460b56e9a7SVivek Gautam 		return PTR_ERR(generic_phy);
1470b56e9a7SVivek Gautam 	}
1480b56e9a7SVivek Gautam 
1490b56e9a7SVivek Gautam 	phy->dev = dev;
1500b56e9a7SVivek Gautam 	phy_set_drvdata(generic_phy, phy);
1510b56e9a7SVivek Gautam 	platform_set_drvdata(pdev, phy);
1520b56e9a7SVivek Gautam 
1530b56e9a7SVivek Gautam 	phy->cfg_clk = devm_clk_get(dev, "cfg");
1540b56e9a7SVivek Gautam 	if (IS_ERR(phy->cfg_clk)) {
1550b56e9a7SVivek Gautam 		dev_err(dev, "Failed to get sata cfg clock\n");
1560b56e9a7SVivek Gautam 		return PTR_ERR(phy->cfg_clk);
1570b56e9a7SVivek Gautam 	}
1580b56e9a7SVivek Gautam 
1590b56e9a7SVivek Gautam 	ret = clk_prepare_enable(phy->cfg_clk);
1600b56e9a7SVivek Gautam 	if (ret)
1610b56e9a7SVivek Gautam 		return ret;
1620b56e9a7SVivek Gautam 
1630b56e9a7SVivek Gautam 	phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
1640b56e9a7SVivek Gautam 	if (IS_ERR(phy_provider)) {
1650b56e9a7SVivek Gautam 		clk_disable_unprepare(phy->cfg_clk);
1660b56e9a7SVivek Gautam 		dev_err(dev, "%s: failed to register phy\n", __func__);
1670b56e9a7SVivek Gautam 		return PTR_ERR(phy_provider);
1680b56e9a7SVivek Gautam 	}
1690b56e9a7SVivek Gautam 
1700b56e9a7SVivek Gautam 	return 0;
1710b56e9a7SVivek Gautam }
1720b56e9a7SVivek Gautam 
qcom_ipq806x_sata_phy_remove(struct platform_device * pdev)173*de6862d1SUwe Kleine-König static void qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)
1740b56e9a7SVivek Gautam {
1750b56e9a7SVivek Gautam 	struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);
1760b56e9a7SVivek Gautam 
1770b56e9a7SVivek Gautam 	clk_disable_unprepare(phy->cfg_clk);
1780b56e9a7SVivek Gautam }
1790b56e9a7SVivek Gautam 
1800b56e9a7SVivek Gautam static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {
1810b56e9a7SVivek Gautam 	{ .compatible = "qcom,ipq806x-sata-phy" },
1820b56e9a7SVivek Gautam 	{ },
1830b56e9a7SVivek Gautam };
1840b56e9a7SVivek Gautam MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);
1850b56e9a7SVivek Gautam 
1860b56e9a7SVivek Gautam static struct platform_driver qcom_ipq806x_sata_phy_driver = {
1870b56e9a7SVivek Gautam 	.probe	= qcom_ipq806x_sata_phy_probe,
188*de6862d1SUwe Kleine-König 	.remove_new = qcom_ipq806x_sata_phy_remove,
1890b56e9a7SVivek Gautam 	.driver = {
1900b56e9a7SVivek Gautam 		.name	= "qcom-ipq806x-sata-phy",
1910b56e9a7SVivek Gautam 		.of_match_table	= qcom_ipq806x_sata_phy_of_match,
1920b56e9a7SVivek Gautam 	}
1930b56e9a7SVivek Gautam };
1940b56e9a7SVivek Gautam module_platform_driver(qcom_ipq806x_sata_phy_driver);
1950b56e9a7SVivek Gautam 
1960b56e9a7SVivek Gautam MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");
1970b56e9a7SVivek Gautam MODULE_LICENSE("GPL v2");
198