phy-qcom-usb-ss.c (5888B)
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");