linux/drivers/phy/qualcomm/phy-qcom-usb-ss.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*
   3 * Copyright (c) 2012-2014,2017 The Linux Foundation. All rights reserved.
   4 * Copyright (c) 2018-2020, Linaro Limited
   5 */
   6
   7#include <linux/clk.h>
   8#include <linux/delay.h>
   9#include <linux/err.h>
  10#include <linux/io.h>
  11#include <linux/kernel.h>
  12#include <linux/module.h>
  13#include <linux/of.h>
  14#include <linux/phy/phy.h>
  15#include <linux/platform_device.h>
  16#include <linux/regulator/consumer.h>
  17#include <linux/reset.h>
  18#include <linux/slab.h>
  19
  20#define PHY_CTRL0                       0x6C
  21#define PHY_CTRL1                       0x70
  22#define PHY_CTRL2                       0x74
  23#define PHY_CTRL4                       0x7C
  24
  25/* PHY_CTRL bits */
  26#define REF_PHY_EN                      BIT(0)
  27#define LANE0_PWR_ON                    BIT(2)
  28#define SWI_PCS_CLK_SEL                 BIT(4)
  29#define TST_PWR_DOWN                    BIT(4)
  30#define PHY_RESET                       BIT(7)
  31
  32#define NUM_BULK_CLKS                   3
  33#define NUM_BULK_REGS                   2
  34
  35struct ssphy_priv {
  36        void __iomem *base;
  37        struct device *dev;
  38        struct reset_control *reset_com;
  39        struct reset_control *reset_phy;
  40        struct regulator_bulk_data regs[NUM_BULK_REGS];
  41        struct clk_bulk_data clks[NUM_BULK_CLKS];
  42        enum phy_mode mode;
  43};
  44
  45static inline void qcom_ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
  46{
  47        writel((readl(addr) & ~mask) | val, addr);
  48}
  49
  50static int qcom_ssphy_do_reset(struct ssphy_priv *priv)
  51{
  52        int ret;
  53
  54        if (!priv->reset_com) {
  55                qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET,
  56                                   PHY_RESET);
  57                usleep_range(10, 20);
  58                qcom_ssphy_updatel(priv->base + PHY_CTRL1, PHY_RESET, 0);
  59        } else {
  60                ret = reset_control_assert(priv->reset_com);
  61                if (ret) {
  62                        dev_err(priv->dev, "Failed to assert reset com\n");
  63                        return ret;
  64                }
  65
  66                ret = reset_control_assert(priv->reset_phy);
  67                if (ret) {
  68                        dev_err(priv->dev, "Failed to assert reset phy\n");
  69                        return ret;
  70                }
  71
  72                usleep_range(10, 20);
  73
  74                ret = reset_control_deassert(priv->reset_com);
  75                if (ret) {
  76                        dev_err(priv->dev, "Failed to deassert reset com\n");
  77                        return ret;
  78                }
  79
  80                ret = reset_control_deassert(priv->reset_phy);
  81                if (ret) {
  82                        dev_err(priv->dev, "Failed to deassert reset phy\n");
  83                        return ret;
  84                }
  85        }
  86
  87        return 0;
  88}
  89
  90static int qcom_ssphy_power_on(struct phy *phy)
  91{
  92        struct ssphy_priv *priv = phy_get_drvdata(phy);
  93        int ret;
  94
  95        ret = regulator_bulk_enable(NUM_BULK_REGS, priv->regs);
  96        if (ret)
  97                return ret;
  98
  99        ret = clk_bulk_prepare_enable(NUM_BULK_CLKS, priv->clks);
 100        if (ret)
 101                goto err_disable_regulator;
 102
 103        ret = qcom_ssphy_do_reset(priv);
 104        if (ret)
 105                goto err_disable_clock;
 106
 107        writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
 108        qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
 109        qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
 110        qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
 111
 112        return 0;
 113err_disable_clock:
 114        clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
 115err_disable_regulator:
 116        regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
 117
 118        return ret;
 119}
 120
 121static int qcom_ssphy_power_off(struct phy *phy)
 122{
 123        struct ssphy_priv *priv = phy_get_drvdata(phy);
 124
 125        qcom_ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
 126        qcom_ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
 127        qcom_ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
 128
 129        clk_bulk_disable_unprepare(NUM_BULK_CLKS, priv->clks);
 130        regulator_bulk_disable(NUM_BULK_REGS, priv->regs);
 131
 132        return 0;
 133}
 134
 135static int qcom_ssphy_init_clock(struct ssphy_priv *priv)
 136{
 137        priv->clks[0].id = "ref";
 138        priv->clks[1].id = "ahb";
 139        priv->clks[2].id = "pipe";
 140
 141        return devm_clk_bulk_get(priv->dev, NUM_BULK_CLKS, priv->clks);
 142}
 143
 144static int qcom_ssphy_init_regulator(struct ssphy_priv *priv)
 145{
 146        int ret;
 147
 148        priv->regs[0].supply = "vdd";
 149        priv->regs[1].supply = "vdda1p8";
 150        ret = devm_regulator_bulk_get(priv->dev, NUM_BULK_REGS, priv->regs);
 151        if (ret) {
 152                if (ret != -EPROBE_DEFER)
 153                        dev_err(priv->dev, "Failed to get regulators\n");
 154                return ret;
 155        }
 156
 157        return ret;
 158}
 159
 160static int qcom_ssphy_init_reset(struct ssphy_priv *priv)
 161{
 162        priv->reset_com = devm_reset_control_get_optional_exclusive(priv->dev, "com");
 163        if (IS_ERR(priv->reset_com)) {
 164                dev_err(priv->dev, "Failed to get reset control com\n");
 165                return PTR_ERR(priv->reset_com);
 166        }
 167
 168        if (priv->reset_com) {
 169                /* if reset_com is present, reset_phy is no longer optional */
 170                priv->reset_phy = devm_reset_control_get_exclusive(priv->dev, "phy");
 171                if (IS_ERR(priv->reset_phy)) {
 172                        dev_err(priv->dev, "Failed to get reset control phy\n");
 173                        return PTR_ERR(priv->reset_phy);
 174                }
 175        }
 176
 177        return 0;
 178}
 179
 180static const struct phy_ops qcom_ssphy_ops = {
 181        .power_off = qcom_ssphy_power_off,
 182        .power_on = qcom_ssphy_power_on,
 183        .owner = THIS_MODULE,
 184};
 185
 186static int qcom_ssphy_probe(struct platform_device *pdev)
 187{
 188        struct device *dev = &pdev->dev;
 189        struct phy_provider *provider;
 190        struct ssphy_priv *priv;
 191        struct phy *phy;
 192        int ret;
 193
 194        priv = devm_kzalloc(dev, sizeof(struct ssphy_priv), GFP_KERNEL);
 195        if (!priv)
 196                return -ENOMEM;
 197
 198        priv->dev = dev;
 199        priv->mode = PHY_MODE_INVALID;
 200
 201        priv->base = devm_platform_ioremap_resource(pdev, 0);
 202        if (IS_ERR(priv->base))
 203                return PTR_ERR(priv->base);
 204
 205        ret = qcom_ssphy_init_clock(priv);
 206        if (ret)
 207                return ret;
 208
 209        ret = qcom_ssphy_init_reset(priv);
 210        if (ret)
 211                return ret;
 212
 213        ret = qcom_ssphy_init_regulator(priv);
 214        if (ret)
 215                return ret;
 216
 217        phy = devm_phy_create(dev, dev->of_node, &qcom_ssphy_ops);
 218        if (IS_ERR(phy)) {
 219                dev_err(dev, "Failed to create the SS phy\n");
 220                return PTR_ERR(phy);
 221        }
 222
 223        phy_set_drvdata(phy, priv);
 224
 225        provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
 226
 227        return PTR_ERR_OR_ZERO(provider);
 228}
 229
 230static const struct of_device_id qcom_ssphy_match[] = {
 231        { .compatible = "qcom,usb-ss-28nm-phy", },
 232        { },
 233};
 234MODULE_DEVICE_TABLE(of, qcom_ssphy_match);
 235
 236static struct platform_driver qcom_ssphy_driver = {
 237        .probe          = qcom_ssphy_probe,
 238        .driver = {
 239                .name   = "qcom-usb-ssphy",
 240                .of_match_table = qcom_ssphy_match,
 241        },
 242};
 243module_platform_driver(qcom_ssphy_driver);
 244
 245MODULE_DESCRIPTION("Qualcomm SuperSpeed USB PHY driver");
 246MODULE_LICENSE("GPL v2");
 247