blob: 270d09d883c45867720d2c0b9cf07799373efd56 [file] [log] [blame]
Sumit Gargaae35732022-08-04 19:57:10 +05301// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Copyright (C) 2022 Sumit Garg <sumit.garg@linaro.org>
4 *
5 * Based on Linux driver
6 */
7
Sumit Gargaae35732022-08-04 19:57:10 +05308#include <dm.h>
9#include <generic-phy.h>
10#include <linux/bitops.h>
11#include <asm/io.h>
12#include <reset.h>
13#include <clk.h>
14#include <linux/delay.h>
15
16#define PHY_CTRL0 0x6C
17#define PHY_CTRL1 0x70
18#define PHY_CTRL2 0x74
19#define PHY_CTRL4 0x7C
20
21/* PHY_CTRL bits */
22#define REF_PHY_EN BIT(0)
23#define LANE0_PWR_ON BIT(2)
24#define SWI_PCS_CLK_SEL BIT(4)
25#define TST_PWR_DOWN BIT(4)
26#define PHY_RESET BIT(7)
27
28struct ssphy_priv {
29 void __iomem *base;
30 struct clk_bulk clks;
31 struct reset_ctl com_rst;
32 struct reset_ctl phy_rst;
33};
34
35static inline void ssphy_updatel(void __iomem *addr, u32 mask, u32 val)
36{
37 writel((readl(addr) & ~mask) | val, addr);
38}
39
40static int ssphy_do_reset(struct ssphy_priv *priv)
41{
42 int ret;
43
44 ret = reset_assert(&priv->com_rst);
45 if (ret)
46 return ret;
47
48 ret = reset_assert(&priv->phy_rst);
49 if (ret)
50 return ret;
51
52 udelay(10);
53
54 ret = reset_deassert(&priv->com_rst);
55 if (ret)
56 return ret;
57
58 ret = reset_deassert(&priv->phy_rst);
59 if (ret)
60 return ret;
61
62 return 0;
63}
64
65static int ssphy_power_on(struct phy *phy)
66{
67 struct ssphy_priv *priv = dev_get_priv(phy->dev);
68 int ret;
69
70 ret = ssphy_do_reset(priv);
71 if (ret)
72 return ret;
73
74 writeb(SWI_PCS_CLK_SEL, priv->base + PHY_CTRL0);
75 ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, LANE0_PWR_ON);
76 ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, REF_PHY_EN);
77 ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, 0);
78
79 return 0;
80}
81
82static int ssphy_power_off(struct phy *phy)
83{
84 struct ssphy_priv *priv = dev_get_priv(phy->dev);
85
86 ssphy_updatel(priv->base + PHY_CTRL4, LANE0_PWR_ON, 0);
87 ssphy_updatel(priv->base + PHY_CTRL2, REF_PHY_EN, 0);
88 ssphy_updatel(priv->base + PHY_CTRL4, TST_PWR_DOWN, TST_PWR_DOWN);
89
90 return 0;
91}
92
93static int ssphy_clk_init(struct udevice *dev, struct ssphy_priv *priv)
94{
95 int ret;
96
97 ret = clk_get_bulk(dev, &priv->clks);
98 if (ret == -ENOSYS || ret == -ENOENT)
99 return 0;
100 if (ret)
101 return ret;
102
103 ret = clk_enable_bulk(&priv->clks);
104 if (ret) {
105 clk_release_bulk(&priv->clks);
106 return ret;
107 }
108
109 return 0;
110}
111
112static int ssphy_probe(struct udevice *dev)
113{
114 struct ssphy_priv *priv = dev_get_priv(dev);
115 int ret;
116
Johan Jonker8d5d8e02023-03-13 01:32:04 +0100117 priv->base = dev_read_addr_ptr(dev);
118 if (!priv->base)
Sumit Gargaae35732022-08-04 19:57:10 +0530119 return -EINVAL;
120
121 ret = ssphy_clk_init(dev, priv);
122 if (ret)
123 return ret;
124
125 ret = reset_get_by_name(dev, "com", &priv->com_rst);
126 if (ret)
127 return ret;
128
129 ret = reset_get_by_name(dev, "phy", &priv->phy_rst);
130 if (ret)
131 return ret;
132
133 return 0;
134}
135
136static struct phy_ops ssphy_ops = {
137 .power_on = ssphy_power_on,
138 .power_off = ssphy_power_off,
139};
140
141static const struct udevice_id ssphy_ids[] = {
142 { .compatible = "qcom,usb-ss-28nm-phy" },
143 { }
144};
145
146U_BOOT_DRIVER(qcom_usb_ss) = {
147 .name = "qcom-usb-ss",
148 .id = UCLASS_PHY,
149 .of_match = ssphy_ids,
150 .ops = &ssphy_ops,
151 .probe = ssphy_probe,
152 .priv_auto = sizeof(struct ssphy_priv),
153};