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