Loading...
Note: File does not exist in v3.1.
1// SPDX-License-Identifier: GPL-2.0
2/*
3 * Copyright (C) 2016-2018 Broadcom
4 */
5
6#include <linux/delay.h>
7#include <linux/io.h>
8#include <linux/module.h>
9#include <linux/of.h>
10#include <linux/phy/phy.h>
11#include <linux/platform_device.h>
12
13enum bcm_usb_phy_version {
14 BCM_SR_USB_COMBO_PHY,
15 BCM_SR_USB_HS_PHY,
16};
17
18enum bcm_usb_phy_reg {
19 PLL_CTRL,
20 PHY_CTRL,
21 PHY_PLL_CTRL,
22};
23
24/* USB PHY registers */
25
26static const u8 bcm_usb_combo_phy_ss[] = {
27 [PLL_CTRL] = 0x18,
28 [PHY_CTRL] = 0x14,
29};
30
31static const u8 bcm_usb_combo_phy_hs[] = {
32 [PLL_CTRL] = 0x0c,
33 [PHY_CTRL] = 0x10,
34};
35
36static const u8 bcm_usb_hs_phy[] = {
37 [PLL_CTRL] = 0x8,
38 [PHY_CTRL] = 0xc,
39};
40
41enum pll_ctrl_bits {
42 PLL_RESETB,
43 SSPLL_SUSPEND_EN,
44 PLL_SEQ_START,
45 PLL_LOCK,
46};
47
48static const u8 u3pll_ctrl[] = {
49 [PLL_RESETB] = 0,
50 [SSPLL_SUSPEND_EN] = 1,
51 [PLL_SEQ_START] = 2,
52 [PLL_LOCK] = 3,
53};
54
55#define HSPLL_PDIV_MASK 0xF
56#define HSPLL_PDIV_VAL 0x1
57
58static const u8 u2pll_ctrl[] = {
59 [PLL_RESETB] = 5,
60 [PLL_LOCK] = 6,
61};
62
63enum bcm_usb_phy_ctrl_bits {
64 CORERDY,
65 PHY_RESETB,
66 PHY_PCTL,
67};
68
69#define PHY_PCTL_MASK 0xffff
70#define SSPHY_PCTL_VAL 0x0006
71
72static const u8 u3phy_ctrl[] = {
73 [PHY_RESETB] = 1,
74 [PHY_PCTL] = 2,
75};
76
77static const u8 u2phy_ctrl[] = {
78 [CORERDY] = 0,
79 [PHY_RESETB] = 5,
80 [PHY_PCTL] = 6,
81};
82
83struct bcm_usb_phy_cfg {
84 uint32_t type;
85 uint32_t version;
86 void __iomem *regs;
87 struct phy *phy;
88 const u8 *offset;
89};
90
91#define PLL_LOCK_RETRY_COUNT 1000
92
93enum bcm_usb_phy_type {
94 USB_HS_PHY,
95 USB_SS_PHY,
96};
97
98#define NUM_BCM_SR_USB_COMBO_PHYS 2
99
100static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)
101{
102 writel(readl(addr) & ~clear, addr);
103}
104
105static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
106{
107 writel(readl(addr) | set, addr);
108}
109
110static int bcm_usb_pll_lock_check(void __iomem *addr, u32 bit)
111{
112 int retry;
113 u32 rd_data;
114
115 retry = PLL_LOCK_RETRY_COUNT;
116 do {
117 rd_data = readl(addr);
118 if (rd_data & bit)
119 return 0;
120 udelay(1);
121 } while (--retry > 0);
122
123 pr_err("%s: FAIL\n", __func__);
124 return -ETIMEDOUT;
125}
126
127static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
128{
129 int ret = 0;
130 void __iomem *regs = phy_cfg->regs;
131 const u8 *offset;
132 u32 rd_data;
133
134 offset = phy_cfg->offset;
135
136 /* Set pctl with mode and soft reset */
137 rd_data = readl(regs + offset[PHY_CTRL]);
138 rd_data &= ~(PHY_PCTL_MASK << u3phy_ctrl[PHY_PCTL]);
139 rd_data |= (SSPHY_PCTL_VAL << u3phy_ctrl[PHY_PCTL]);
140 writel(rd_data, regs + offset[PHY_CTRL]);
141
142 bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
143 BIT(u3pll_ctrl[SSPLL_SUSPEND_EN]));
144 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
145 BIT(u3pll_ctrl[PLL_SEQ_START]));
146 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
147 BIT(u3pll_ctrl[PLL_RESETB]));
148
149 /* Maximum timeout for PLL reset done */
150 msleep(30);
151
152 ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
153 BIT(u3pll_ctrl[PLL_LOCK]));
154
155 return ret;
156}
157
158static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg *phy_cfg)
159{
160 int ret = 0;
161 void __iomem *regs = phy_cfg->regs;
162 const u8 *offset;
163
164 offset = phy_cfg->offset;
165
166 bcm_usb_reg32_clrbits(regs + offset[PLL_CTRL],
167 BIT(u2pll_ctrl[PLL_RESETB]));
168 bcm_usb_reg32_setbits(regs + offset[PLL_CTRL],
169 BIT(u2pll_ctrl[PLL_RESETB]));
170
171 ret = bcm_usb_pll_lock_check(regs + offset[PLL_CTRL],
172 BIT(u2pll_ctrl[PLL_LOCK]));
173
174 return ret;
175}
176
177static int bcm_usb_phy_reset(struct phy *phy)
178{
179 struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
180 void __iomem *regs = phy_cfg->regs;
181 const u8 *offset;
182
183 offset = phy_cfg->offset;
184
185 if (phy_cfg->type == USB_HS_PHY) {
186 bcm_usb_reg32_clrbits(regs + offset[PHY_CTRL],
187 BIT(u2phy_ctrl[CORERDY]));
188 bcm_usb_reg32_setbits(regs + offset[PHY_CTRL],
189 BIT(u2phy_ctrl[CORERDY]));
190 }
191
192 return 0;
193}
194
195static int bcm_usb_phy_init(struct phy *phy)
196{
197 struct bcm_usb_phy_cfg *phy_cfg = phy_get_drvdata(phy);
198 int ret = -EINVAL;
199
200 if (phy_cfg->type == USB_SS_PHY)
201 ret = bcm_usb_ss_phy_init(phy_cfg);
202 else if (phy_cfg->type == USB_HS_PHY)
203 ret = bcm_usb_hs_phy_init(phy_cfg);
204
205 return ret;
206}
207
208static const struct phy_ops sr_phy_ops = {
209 .init = bcm_usb_phy_init,
210 .reset = bcm_usb_phy_reset,
211 .owner = THIS_MODULE,
212};
213
214static struct phy *bcm_usb_phy_xlate(struct device *dev,
215 struct of_phandle_args *args)
216{
217 struct bcm_usb_phy_cfg *phy_cfg;
218 int phy_idx;
219
220 phy_cfg = dev_get_drvdata(dev);
221 if (!phy_cfg)
222 return ERR_PTR(-EINVAL);
223
224 if (phy_cfg->version == BCM_SR_USB_COMBO_PHY) {
225 phy_idx = args->args[0];
226
227 if (WARN_ON(phy_idx > 1))
228 return ERR_PTR(-ENODEV);
229
230 return phy_cfg[phy_idx].phy;
231 } else
232 return phy_cfg->phy;
233}
234
235static int bcm_usb_phy_create(struct device *dev, struct device_node *node,
236 void __iomem *regs, uint32_t version)
237{
238 struct bcm_usb_phy_cfg *phy_cfg;
239 int idx;
240
241 if (version == BCM_SR_USB_COMBO_PHY) {
242 phy_cfg = devm_kzalloc(dev, NUM_BCM_SR_USB_COMBO_PHYS *
243 sizeof(struct bcm_usb_phy_cfg),
244 GFP_KERNEL);
245 if (!phy_cfg)
246 return -ENOMEM;
247
248 for (idx = 0; idx < NUM_BCM_SR_USB_COMBO_PHYS; idx++) {
249 phy_cfg[idx].regs = regs;
250 phy_cfg[idx].version = version;
251 if (idx == 0) {
252 phy_cfg[idx].offset = bcm_usb_combo_phy_hs;
253 phy_cfg[idx].type = USB_HS_PHY;
254 } else {
255 phy_cfg[idx].offset = bcm_usb_combo_phy_ss;
256 phy_cfg[idx].type = USB_SS_PHY;
257 }
258 phy_cfg[idx].phy = devm_phy_create(dev, node,
259 &sr_phy_ops);
260 if (IS_ERR(phy_cfg[idx].phy))
261 return PTR_ERR(phy_cfg[idx].phy);
262
263 phy_set_drvdata(phy_cfg[idx].phy, &phy_cfg[idx]);
264 }
265 } else if (version == BCM_SR_USB_HS_PHY) {
266 phy_cfg = devm_kzalloc(dev, sizeof(struct bcm_usb_phy_cfg),
267 GFP_KERNEL);
268 if (!phy_cfg)
269 return -ENOMEM;
270
271 phy_cfg->regs = regs;
272 phy_cfg->version = version;
273 phy_cfg->offset = bcm_usb_hs_phy;
274 phy_cfg->type = USB_HS_PHY;
275 phy_cfg->phy = devm_phy_create(dev, node, &sr_phy_ops);
276 if (IS_ERR(phy_cfg->phy))
277 return PTR_ERR(phy_cfg->phy);
278
279 phy_set_drvdata(phy_cfg->phy, phy_cfg);
280 } else
281 return -ENODEV;
282
283 dev_set_drvdata(dev, phy_cfg);
284
285 return 0;
286}
287
288static const struct of_device_id bcm_usb_phy_of_match[] = {
289 {
290 .compatible = "brcm,sr-usb-combo-phy",
291 .data = (void *)BCM_SR_USB_COMBO_PHY,
292 },
293 {
294 .compatible = "brcm,sr-usb-hs-phy",
295 .data = (void *)BCM_SR_USB_HS_PHY,
296 },
297 { /* sentinel */ },
298};
299MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);
300
301static int bcm_usb_phy_probe(struct platform_device *pdev)
302{
303 struct device *dev = &pdev->dev;
304 struct device_node *dn = dev->of_node;
305 const struct of_device_id *of_id;
306 struct resource *res;
307 void __iomem *regs;
308 int ret;
309 enum bcm_usb_phy_version version;
310 struct phy_provider *phy_provider;
311
312 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
313 regs = devm_ioremap_resource(dev, res);
314 if (IS_ERR(regs))
315 return PTR_ERR(regs);
316
317 of_id = of_match_node(bcm_usb_phy_of_match, dn);
318 if (of_id)
319 version = (enum bcm_usb_phy_version)of_id->data;
320 else
321 return -ENODEV;
322
323 ret = bcm_usb_phy_create(dev, dn, regs, version);
324 if (ret)
325 return ret;
326
327 phy_provider = devm_of_phy_provider_register(dev, bcm_usb_phy_xlate);
328
329 return PTR_ERR_OR_ZERO(phy_provider);
330}
331
332static struct platform_driver bcm_usb_phy_driver = {
333 .driver = {
334 .name = "phy-bcm-sr-usb",
335 .of_match_table = bcm_usb_phy_of_match,
336 },
337 .probe = bcm_usb_phy_probe,
338};
339module_platform_driver(bcm_usb_phy_driver);
340
341MODULE_AUTHOR("Broadcom");
342MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");
343MODULE_LICENSE("GPL v2");