1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2016-2018 Broadcom
6 #include <linux/delay.h>
8 #include <linux/iopoll.h>
9 #include <linux/module.h>
11 #include <linux/phy/phy.h>
12 #include <linux/platform_device.h>
14 enum bcm_usb_phy_version
{
19 enum bcm_usb_phy_reg
{
25 /* USB PHY registers */
27 static const u8 bcm_usb_combo_phy_ss
[] = {
32 static const u8 bcm_usb_combo_phy_hs
[] = {
37 static const u8 bcm_usb_hs_phy
[] = {
49 static const u8 u3pll_ctrl
[] = {
51 [SSPLL_SUSPEND_EN
] = 1,
56 #define HSPLL_PDIV_MASK 0xF
57 #define HSPLL_PDIV_VAL 0x1
59 static const u8 u2pll_ctrl
[] = {
64 enum bcm_usb_phy_ctrl_bits
{
70 #define PHY_PCTL_MASK 0xffff
71 #define SSPHY_PCTL_VAL 0x0006
73 static const u8 u3phy_ctrl
[] = {
78 static const u8 u2phy_ctrl
[] = {
84 struct bcm_usb_phy_cfg
{
92 #define PLL_LOCK_RETRY_COUNT 1000
94 enum bcm_usb_phy_type
{
99 #define NUM_BCM_SR_USB_COMBO_PHYS 2
101 static inline void bcm_usb_reg32_clrbits(void __iomem
*addr
, uint32_t clear
)
103 writel(readl(addr
) & ~clear
, addr
);
106 static inline void bcm_usb_reg32_setbits(void __iomem
*addr
, uint32_t set
)
108 writel(readl(addr
) | set
, addr
);
111 static int bcm_usb_pll_lock_check(void __iomem
*addr
, u32 bit
)
116 ret
= readl_poll_timeout_atomic(addr
, data
, (data
& bit
), 1,
117 PLL_LOCK_RETRY_COUNT
);
119 pr_err("%s: FAIL\n", __func__
);
124 static int bcm_usb_ss_phy_init(struct bcm_usb_phy_cfg
*phy_cfg
)
127 void __iomem
*regs
= phy_cfg
->regs
;
131 offset
= phy_cfg
->offset
;
133 /* Set pctl with mode and soft reset */
134 rd_data
= readl(regs
+ offset
[PHY_CTRL
]);
135 rd_data
&= ~(PHY_PCTL_MASK
<< u3phy_ctrl
[PHY_PCTL
]);
136 rd_data
|= (SSPHY_PCTL_VAL
<< u3phy_ctrl
[PHY_PCTL
]);
137 writel(rd_data
, regs
+ offset
[PHY_CTRL
]);
139 bcm_usb_reg32_clrbits(regs
+ offset
[PLL_CTRL
],
140 BIT(u3pll_ctrl
[SSPLL_SUSPEND_EN
]));
141 bcm_usb_reg32_setbits(regs
+ offset
[PLL_CTRL
],
142 BIT(u3pll_ctrl
[PLL_SEQ_START
]));
143 bcm_usb_reg32_setbits(regs
+ offset
[PLL_CTRL
],
144 BIT(u3pll_ctrl
[PLL_RESETB
]));
146 /* Maximum timeout for PLL reset done */
149 ret
= bcm_usb_pll_lock_check(regs
+ offset
[PLL_CTRL
],
150 BIT(u3pll_ctrl
[PLL_LOCK
]));
155 static int bcm_usb_hs_phy_init(struct bcm_usb_phy_cfg
*phy_cfg
)
158 void __iomem
*regs
= phy_cfg
->regs
;
161 offset
= phy_cfg
->offset
;
163 bcm_usb_reg32_clrbits(regs
+ offset
[PLL_CTRL
],
164 BIT(u2pll_ctrl
[PLL_RESETB
]));
165 bcm_usb_reg32_setbits(regs
+ offset
[PLL_CTRL
],
166 BIT(u2pll_ctrl
[PLL_RESETB
]));
168 ret
= bcm_usb_pll_lock_check(regs
+ offset
[PLL_CTRL
],
169 BIT(u2pll_ctrl
[PLL_LOCK
]));
174 static int bcm_usb_phy_reset(struct phy
*phy
)
176 struct bcm_usb_phy_cfg
*phy_cfg
= phy_get_drvdata(phy
);
177 void __iomem
*regs
= phy_cfg
->regs
;
180 offset
= phy_cfg
->offset
;
182 if (phy_cfg
->type
== USB_HS_PHY
) {
183 bcm_usb_reg32_clrbits(regs
+ offset
[PHY_CTRL
],
184 BIT(u2phy_ctrl
[CORERDY
]));
185 bcm_usb_reg32_setbits(regs
+ offset
[PHY_CTRL
],
186 BIT(u2phy_ctrl
[CORERDY
]));
192 static int bcm_usb_phy_init(struct phy
*phy
)
194 struct bcm_usb_phy_cfg
*phy_cfg
= phy_get_drvdata(phy
);
197 if (phy_cfg
->type
== USB_SS_PHY
)
198 ret
= bcm_usb_ss_phy_init(phy_cfg
);
199 else if (phy_cfg
->type
== USB_HS_PHY
)
200 ret
= bcm_usb_hs_phy_init(phy_cfg
);
205 static const struct phy_ops sr_phy_ops
= {
206 .init
= bcm_usb_phy_init
,
207 .reset
= bcm_usb_phy_reset
,
208 .owner
= THIS_MODULE
,
211 static struct phy
*bcm_usb_phy_xlate(struct device
*dev
,
212 struct of_phandle_args
*args
)
214 struct bcm_usb_phy_cfg
*phy_cfg
;
217 phy_cfg
= dev_get_drvdata(dev
);
219 return ERR_PTR(-EINVAL
);
221 if (phy_cfg
->version
== BCM_SR_USB_COMBO_PHY
) {
222 phy_idx
= args
->args
[0];
224 if (WARN_ON(phy_idx
> 1))
225 return ERR_PTR(-ENODEV
);
227 return phy_cfg
[phy_idx
].phy
;
232 static int bcm_usb_phy_create(struct device
*dev
, struct device_node
*node
,
233 void __iomem
*regs
, uint32_t version
)
235 struct bcm_usb_phy_cfg
*phy_cfg
;
238 if (version
== BCM_SR_USB_COMBO_PHY
) {
239 phy_cfg
= devm_kzalloc(dev
, NUM_BCM_SR_USB_COMBO_PHYS
*
240 sizeof(struct bcm_usb_phy_cfg
),
245 for (idx
= 0; idx
< NUM_BCM_SR_USB_COMBO_PHYS
; idx
++) {
246 phy_cfg
[idx
].regs
= regs
;
247 phy_cfg
[idx
].version
= version
;
249 phy_cfg
[idx
].offset
= bcm_usb_combo_phy_hs
;
250 phy_cfg
[idx
].type
= USB_HS_PHY
;
252 phy_cfg
[idx
].offset
= bcm_usb_combo_phy_ss
;
253 phy_cfg
[idx
].type
= USB_SS_PHY
;
255 phy_cfg
[idx
].phy
= devm_phy_create(dev
, node
,
257 if (IS_ERR(phy_cfg
[idx
].phy
))
258 return PTR_ERR(phy_cfg
[idx
].phy
);
260 phy_set_drvdata(phy_cfg
[idx
].phy
, &phy_cfg
[idx
]);
262 } else if (version
== BCM_SR_USB_HS_PHY
) {
263 phy_cfg
= devm_kzalloc(dev
, sizeof(struct bcm_usb_phy_cfg
),
268 phy_cfg
->regs
= regs
;
269 phy_cfg
->version
= version
;
270 phy_cfg
->offset
= bcm_usb_hs_phy
;
271 phy_cfg
->type
= USB_HS_PHY
;
272 phy_cfg
->phy
= devm_phy_create(dev
, node
, &sr_phy_ops
);
273 if (IS_ERR(phy_cfg
->phy
))
274 return PTR_ERR(phy_cfg
->phy
);
276 phy_set_drvdata(phy_cfg
->phy
, phy_cfg
);
280 dev_set_drvdata(dev
, phy_cfg
);
285 static const struct of_device_id bcm_usb_phy_of_match
[] = {
287 .compatible
= "brcm,sr-usb-combo-phy",
288 .data
= (void *)BCM_SR_USB_COMBO_PHY
,
291 .compatible
= "brcm,sr-usb-hs-phy",
292 .data
= (void *)BCM_SR_USB_HS_PHY
,
296 MODULE_DEVICE_TABLE(of
, bcm_usb_phy_of_match
);
298 static int bcm_usb_phy_probe(struct platform_device
*pdev
)
300 struct device
*dev
= &pdev
->dev
;
301 struct device_node
*dn
= dev
->of_node
;
302 const struct of_device_id
*of_id
;
305 enum bcm_usb_phy_version version
;
306 struct phy_provider
*phy_provider
;
308 regs
= devm_platform_ioremap_resource(pdev
, 0);
310 return PTR_ERR(regs
);
312 of_id
= of_match_node(bcm_usb_phy_of_match
, dn
);
314 version
= (enum bcm_usb_phy_version
)of_id
->data
;
318 ret
= bcm_usb_phy_create(dev
, dn
, regs
, version
);
322 phy_provider
= devm_of_phy_provider_register(dev
, bcm_usb_phy_xlate
);
324 return PTR_ERR_OR_ZERO(phy_provider
);
327 static struct platform_driver bcm_usb_phy_driver
= {
329 .name
= "phy-bcm-sr-usb",
330 .of_match_table
= bcm_usb_phy_of_match
,
332 .probe
= bcm_usb_phy_probe
,
334 module_platform_driver(bcm_usb_phy_driver
);
336 MODULE_AUTHOR("Broadcom");
337 MODULE_DESCRIPTION("Broadcom stingray USB Phy driver");
338 MODULE_LICENSE("GPL v2");