198 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Copyright (c) 2014, The Linux Foundation. All rights reserved.4 */5 6#include <linux/io.h>7#include <linux/kernel.h>8#include <linux/module.h>9#include <linux/of.h>10#include <linux/of_address.h>11#include <linux/time.h>12#include <linux/delay.h>13#include <linux/clk.h>14#include <linux/slab.h>15#include <linux/platform_device.h>16#include <linux/phy/phy.h>17 18struct qcom_ipq806x_sata_phy {19 void __iomem *mmio;20 struct clk *cfg_clk;21 struct device *dev;22};23 24#define __set(v, a, b) (((v) << (b)) & GENMASK(a, b))25 26#define SATA_PHY_P0_PARAM0 0x20027#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(x) __set(x, 17, 12)28#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK GENMASK(17, 12)29#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2(x) __set(x, 11, 6)30#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK GENMASK(11, 6)31#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1(x) __set(x, 5, 0)32#define SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK GENMASK(5, 0)33 34#define SATA_PHY_P0_PARAM1 0x20435#define SATA_PHY_P0_PARAM1_RESERVED_BITS31_21(x) __set(x, 31, 21)36#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(x) __set(x, 20, 14)37#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK GENMASK(20, 14)38#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(x) __set(x, 13, 7)39#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK GENMASK(13, 7)40#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(x) __set(x, 6, 0)41#define SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK GENMASK(6, 0)42 43#define SATA_PHY_P0_PARAM2 0x20844#define SATA_PHY_P0_PARAM2_RX_EQ(x) __set(x, 20, 18)45#define SATA_PHY_P0_PARAM2_RX_EQ_MASK GENMASK(20, 18)46 47#define SATA_PHY_P0_PARAM3 0x20C48#define SATA_PHY_SSC_EN 0x849#define SATA_PHY_P0_PARAM4 0x21050#define SATA_PHY_REF_SSP_EN 0x251#define SATA_PHY_RESET 0x152 53static int qcom_ipq806x_sata_phy_init(struct phy *generic_phy)54{55 struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);56 u32 reg;57 58 /* Setting SSC_EN to 1 */59 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM3);60 reg = reg | SATA_PHY_SSC_EN;61 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM3);62 63 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM0) &64 ~(SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3_MASK |65 SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN2_MASK |66 SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN1_MASK);67 reg |= SATA_PHY_P0_PARAM0_P0_TX_PREEMPH_GEN3(0xf);68 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM0);69 70 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM1) &71 ~(SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3_MASK |72 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2_MASK |73 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1_MASK);74 reg |= SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN3(0x55) |75 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN2(0x55) |76 SATA_PHY_P0_PARAM1_P0_TX_AMPLITUDE_GEN1(0x55);77 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM1);78 79 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM2) &80 ~SATA_PHY_P0_PARAM2_RX_EQ_MASK;81 reg |= SATA_PHY_P0_PARAM2_RX_EQ(0x3);82 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM2);83 84 /* Setting PHY_RESET to 1 */85 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);86 reg = reg | SATA_PHY_RESET;87 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);88 89 /* Setting REF_SSP_EN to 1 */90 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);91 reg = reg | SATA_PHY_REF_SSP_EN | SATA_PHY_RESET;92 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);93 94 /* make sure all changes complete before we let the PHY out of reset */95 mb();96 97 /* sleep for max. 50us more to combine processor wakeups */98 usleep_range(20, 20 + 50);99 100 /* Clearing PHY_RESET to 0 */101 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);102 reg = reg & ~SATA_PHY_RESET;103 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);104 105 return 0;106}107 108static int qcom_ipq806x_sata_phy_exit(struct phy *generic_phy)109{110 struct qcom_ipq806x_sata_phy *phy = phy_get_drvdata(generic_phy);111 u32 reg;112 113 /* Setting PHY_RESET to 1 */114 reg = readl_relaxed(phy->mmio + SATA_PHY_P0_PARAM4);115 reg = reg | SATA_PHY_RESET;116 writel_relaxed(reg, phy->mmio + SATA_PHY_P0_PARAM4);117 118 return 0;119}120 121static const struct phy_ops qcom_ipq806x_sata_phy_ops = {122 .init = qcom_ipq806x_sata_phy_init,123 .exit = qcom_ipq806x_sata_phy_exit,124 .owner = THIS_MODULE,125};126 127static int qcom_ipq806x_sata_phy_probe(struct platform_device *pdev)128{129 struct qcom_ipq806x_sata_phy *phy;130 struct device *dev = &pdev->dev;131 struct phy_provider *phy_provider;132 struct phy *generic_phy;133 int ret;134 135 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);136 if (!phy)137 return -ENOMEM;138 139 phy->mmio = devm_platform_ioremap_resource(pdev, 0);140 if (IS_ERR(phy->mmio))141 return PTR_ERR(phy->mmio);142 143 generic_phy = devm_phy_create(dev, NULL, &qcom_ipq806x_sata_phy_ops);144 if (IS_ERR(generic_phy)) {145 dev_err(dev, "%s: failed to create phy\n", __func__);146 return PTR_ERR(generic_phy);147 }148 149 phy->dev = dev;150 phy_set_drvdata(generic_phy, phy);151 platform_set_drvdata(pdev, phy);152 153 phy->cfg_clk = devm_clk_get(dev, "cfg");154 if (IS_ERR(phy->cfg_clk)) {155 dev_err(dev, "Failed to get sata cfg clock\n");156 return PTR_ERR(phy->cfg_clk);157 }158 159 ret = clk_prepare_enable(phy->cfg_clk);160 if (ret)161 return ret;162 163 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);164 if (IS_ERR(phy_provider)) {165 clk_disable_unprepare(phy->cfg_clk);166 dev_err(dev, "%s: failed to register phy\n", __func__);167 return PTR_ERR(phy_provider);168 }169 170 return 0;171}172 173static void qcom_ipq806x_sata_phy_remove(struct platform_device *pdev)174{175 struct qcom_ipq806x_sata_phy *phy = platform_get_drvdata(pdev);176 177 clk_disable_unprepare(phy->cfg_clk);178}179 180static const struct of_device_id qcom_ipq806x_sata_phy_of_match[] = {181 { .compatible = "qcom,ipq806x-sata-phy" },182 { },183};184MODULE_DEVICE_TABLE(of, qcom_ipq806x_sata_phy_of_match);185 186static struct platform_driver qcom_ipq806x_sata_phy_driver = {187 .probe = qcom_ipq806x_sata_phy_probe,188 .remove_new = qcom_ipq806x_sata_phy_remove,189 .driver = {190 .name = "qcom-ipq806x-sata-phy",191 .of_match_table = qcom_ipq806x_sata_phy_of_match,192 }193};194module_platform_driver(qcom_ipq806x_sata_phy_driver);195 196MODULE_DESCRIPTION("QCOM IPQ806x SATA PHY driver");197MODULE_LICENSE("GPL v2");198