]> Git Repo - linux.git/blob - drivers/phy/broadcom/phy-bcm-sr-usb.c
drm/nouveau/kms: Don't change EDID when it hasn't actually changed
[linux.git] / drivers / phy / broadcom / phy-bcm-sr-usb.c
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
13 enum bcm_usb_phy_version {
14         BCM_SR_USB_COMBO_PHY,
15         BCM_SR_USB_HS_PHY,
16 };
17
18 enum bcm_usb_phy_reg {
19         PLL_CTRL,
20         PHY_CTRL,
21         PHY_PLL_CTRL,
22 };
23
24 /* USB PHY registers */
25
26 static const u8 bcm_usb_combo_phy_ss[] = {
27         [PLL_CTRL]              = 0x18,
28         [PHY_CTRL]              = 0x14,
29 };
30
31 static const u8 bcm_usb_combo_phy_hs[] = {
32         [PLL_CTRL]      = 0x0c,
33         [PHY_CTRL]      = 0x10,
34 };
35
36 static const u8 bcm_usb_hs_phy[] = {
37         [PLL_CTRL]      = 0x8,
38         [PHY_CTRL]      = 0xc,
39 };
40
41 enum pll_ctrl_bits {
42         PLL_RESETB,
43         SSPLL_SUSPEND_EN,
44         PLL_SEQ_START,
45         PLL_LOCK,
46 };
47
48 static 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
58 static const u8 u2pll_ctrl[] = {
59         [PLL_RESETB]    = 5,
60         [PLL_LOCK]      = 6,
61 };
62
63 enum 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
72 static const u8 u3phy_ctrl[] = {
73         [PHY_RESETB]    = 1,
74         [PHY_PCTL]      = 2,
75 };
76
77 static const u8 u2phy_ctrl[] = {
78         [CORERDY]               = 0,
79         [PHY_RESETB]            = 5,
80         [PHY_PCTL]              = 6,
81 };
82
83 struct 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
93 enum bcm_usb_phy_type {
94         USB_HS_PHY,
95         USB_SS_PHY,
96 };
97
98 #define NUM_BCM_SR_USB_COMBO_PHYS       2
99
100 static inline void bcm_usb_reg32_clrbits(void __iomem *addr, uint32_t clear)
101 {
102         writel(readl(addr) & ~clear, addr);
103 }
104
105 static inline void bcm_usb_reg32_setbits(void __iomem *addr, uint32_t set)
106 {
107         writel(readl(addr) | set, addr);
108 }
109
110 static 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
127 static 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
158 static 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
177 static 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
195 static 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
208 static 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
214 static 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
235 static 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
288 static 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 };
299 MODULE_DEVICE_TABLE(of, bcm_usb_phy_of_match);
300
301 static 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
332 static 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 };
339 module_platform_driver(bcm_usb_phy_driver);
340
341 MODULE_AUTHOR("Broadcom");
342 MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");
343 MODULE_LICENSE("GPL v2");
This page took 0.054156 seconds and 4 git commands to generate.