2 * Broadcom Northstar USB 3.0 PHY Driver
4 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
6 * All magic values used for initialization (and related comments) were obtained
8 * Copyright (c) Broadcom Corp, 2012
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
15 #include <linux/bcma/bcma.h>
16 #include <linux/delay.h>
17 #include <linux/err.h>
18 #include <linux/module.h>
19 #include <linux/of_platform.h>
20 #include <linux/platform_device.h>
21 #include <linux/phy/phy.h>
22 #include <linux/slab.h>
24 #define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */
34 enum bcm_ns_family family
;
36 void __iomem
*ccb_mii
;
40 static const struct of_device_id bcm_ns_usb3_id_table
[] = {
42 .compatible
= "brcm,ns-ax-usb3-phy",
43 .data
= (int *)BCM_NS_AX
,
46 .compatible
= "brcm,ns-bx-usb3-phy",
47 .data
= (int *)BCM_NS_BX
,
51 MODULE_DEVICE_TABLE(of
, bcm_ns_usb3_id_table
);
53 static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3
*usb3
, void __iomem
*addr
,
54 u32 mask
, u32 value
, unsigned long timeout
)
56 unsigned long deadline
= jiffies
+ timeout
;
61 if ((val
& mask
) == value
)
65 } while (!time_after_eq(jiffies
, deadline
));
67 dev_err(usb3
->dev
, "Timeout waiting for register %p\n", addr
);
72 static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3
*usb3
)
74 return bcm_ns_usb3_wait_reg(usb3
, usb3
->ccb_mii
+ BCMA_CCB_MII_MNG_CTL
,
76 usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US
));
79 static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3
*usb3
, u32 value
)
83 err
= bcm_ns_usb3_mii_mng_wait_idle(usb3
);
85 dev_err(usb3
->dev
, "Couldn't write 0x%08x value\n", value
);
89 writel(value
, usb3
->ccb_mii
+ BCMA_CCB_MII_MNG_CMD_DATA
);
94 static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3
*usb3
)
98 /* Enable MDIO. Setting MDCDIV as 26 */
99 writel(0x0000009a, usb3
->ccb_mii
+ BCMA_CCB_MII_MNG_CTL
);
105 err
= bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e8000);
109 /* Assert Ana_Pllseq start */
110 bcm_ns_usb3_mii_mng_write32(usb3
, 0x58061000);
112 /* Assert CML Divider ratio to 26 */
113 bcm_ns_usb3_mii_mng_write32(usb3
, 0x582a6400);
115 /* Asserting PLL Reset */
116 bcm_ns_usb3_mii_mng_write32(usb3
, 0x582ec000);
118 /* Deaaserting PLL Reset */
119 bcm_ns_usb3_mii_mng_write32(usb3
, 0x582e8000);
121 /* Waiting MII Mgt interface idle */
122 bcm_ns_usb3_mii_mng_wait_idle(usb3
);
124 /* Deasserting USB3 system reset */
125 writel(0, usb3
->dmp
+ BCMA_RESET_CTL
);
127 /* PLL frequency monitor enable */
128 bcm_ns_usb3_mii_mng_write32(usb3
, 0x58069000);
131 bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e8060);
133 /* CMPMAX & CMPMINTH setting */
134 bcm_ns_usb3_mii_mng_write32(usb3
, 0x580af30d);
136 /* DEGLITCH MIN & MAX setting */
137 bcm_ns_usb3_mii_mng_write32(usb3
, 0x580e6302);
140 bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e8040);
143 bcm_ns_usb3_mii_mng_write32(usb3
, 0x58061003);
145 /* Waiting MII Mgt interface idle */
146 bcm_ns_usb3_mii_mng_wait_idle(usb3
);
151 static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3
*usb3
)
155 /* Enable MDIO. Setting MDCDIV as 26 */
156 writel(0x0000009a, usb3
->ccb_mii
+ BCMA_CCB_MII_MNG_CTL
);
162 err
= bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e8000);
166 bcm_ns_usb3_mii_mng_write32(usb3
, 0x582a6400);
168 bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e80e0);
170 bcm_ns_usb3_mii_mng_write32(usb3
, 0x580a009c);
173 bcm_ns_usb3_mii_mng_write32(usb3
, 0x587e8040);
175 bcm_ns_usb3_mii_mng_write32(usb3
, 0x580a21d3);
177 bcm_ns_usb3_mii_mng_write32(usb3
, 0x58061003);
179 /* Waiting MII Mgt interface idle */
180 bcm_ns_usb3_mii_mng_wait_idle(usb3
);
182 /* Deasserting USB3 system reset */
183 writel(0, usb3
->dmp
+ BCMA_RESET_CTL
);
188 static int bcm_ns_usb3_phy_init(struct phy
*phy
)
190 struct bcm_ns_usb3
*usb3
= phy_get_drvdata(phy
);
193 /* Perform USB3 system soft reset */
194 writel(BCMA_RESET_CTL_RESET
, usb3
->dmp
+ BCMA_RESET_CTL
);
196 switch (usb3
->family
) {
198 err
= bcm_ns_usb3_phy_init_ns_ax(usb3
);
201 err
= bcm_ns_usb3_phy_init_ns_bx(usb3
);
211 static const struct phy_ops ops
= {
212 .init
= bcm_ns_usb3_phy_init
,
213 .owner
= THIS_MODULE
,
216 static int bcm_ns_usb3_probe(struct platform_device
*pdev
)
218 struct device
*dev
= &pdev
->dev
;
219 const struct of_device_id
*of_id
;
220 struct bcm_ns_usb3
*usb3
;
221 struct resource
*res
;
222 struct phy_provider
*phy_provider
;
224 usb3
= devm_kzalloc(dev
, sizeof(*usb3
), GFP_KERNEL
);
230 of_id
= of_match_device(bcm_ns_usb3_id_table
, dev
);
233 usb3
->family
= (enum bcm_ns_family
)of_id
->data
;
235 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "dmp");
236 usb3
->dmp
= devm_ioremap_resource(dev
, res
);
237 if (IS_ERR(usb3
->dmp
)) {
238 dev_err(dev
, "Failed to map DMP regs\n");
239 return PTR_ERR(usb3
->dmp
);
242 res
= platform_get_resource_byname(pdev
, IORESOURCE_MEM
, "ccb-mii");
243 usb3
->ccb_mii
= devm_ioremap_resource(dev
, res
);
244 if (IS_ERR(usb3
->ccb_mii
)) {
245 dev_err(dev
, "Failed to map ChipCommon B MII regs\n");
246 return PTR_ERR(usb3
->ccb_mii
);
249 usb3
->phy
= devm_phy_create(dev
, NULL
, &ops
);
250 if (IS_ERR(usb3
->phy
)) {
251 dev_err(dev
, "Failed to create PHY\n");
252 return PTR_ERR(usb3
->phy
);
255 phy_set_drvdata(usb3
->phy
, usb3
);
256 platform_set_drvdata(pdev
, usb3
);
258 phy_provider
= devm_of_phy_provider_register(dev
, of_phy_simple_xlate
);
259 if (!IS_ERR(phy_provider
))
260 dev_info(dev
, "Registered Broadcom Northstar USB 3.0 PHY driver\n");
262 return PTR_ERR_OR_ZERO(phy_provider
);
265 static struct platform_driver bcm_ns_usb3_driver
= {
266 .probe
= bcm_ns_usb3_probe
,
268 .name
= "bcm_ns_usb3",
269 .of_match_table
= bcm_ns_usb3_id_table
,
272 module_platform_driver(bcm_ns_usb3_driver
);
274 MODULE_LICENSE("GPL v2");