aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorRafał Miłecki <rafal@milecki.pl>2016-08-11 18:28:03 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2016-09-10 07:18:28 -0400
commite5666281d9eadb98a802e5ec6e85f0b4640f30c4 (patch)
tree1a2cd3465dc17529d4f8819663009f4ec8f4a184
parentb3e0d141ca9f7355fca8a12feb451c31f6b2ee18 (diff)
phy: bcm-ns-usb3: new driver for USB 3.0 PHY on Northstar
Northstar is a family of SoCs used in home routers. They have USB 2.0 and 3.0 controllers with PHYs that need to be properly initialized. This driver provides PHY init support in a generic way and can be bound with XHCI controller driver. There aren't any public datasheets from Broadcom so we can't have nice defines for all used bits. It means we just follow Broadcom's initialization procedure using their magic values. We were quite lucky actually that Broadcom put some comments in their SDK reference code explaining what given writes are responsible for. Signed-off-by: Rafał Miłecki <rafal@milecki.pl> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt23
-rw-r--r--drivers/phy/Kconfig9
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-bcm-ns-usb3.c274
4 files changed, 307 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt b/Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt
new file mode 100644
index 000000000000..09aeba94538d
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/bcm-ns-usb3-phy.txt
@@ -0,0 +1,23 @@
1Driver for Broadcom Northstar USB 3.0 PHY
2
3Required properties:
4
5- compatible: one of: "brcm,ns-ax-usb3-phy", "brcm,ns-bx-usb3-phy".
6- reg: register mappings for DMP (Device Management Plugin) and ChipCommon B
7 MMI.
8- reg-names: "dmp" and "ccb-mii"
9
10Initialization of USB 3.0 PHY depends on Northstar version. There are currently
11three known series: Ax, Bx and Cx.
12Known A0: BCM4707 rev 0
13Known B0: BCM4707 rev 4, BCM53573 rev 2
14Known B1: BCM4707 rev 6
15Known C0: BCM47094 rev 0
16
17Example:
18 usb3-phy {
19 compatible = "brcm,ns-ax-usb3-phy";
20 reg = <0x18105000 0x1000>, <0x18003000 0x1000>;
21 reg-names = "dmp", "ccb-mii";
22 #phy-cells = <0>;
23 };
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 19bff3a10f69..afbdd6a9b964 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -24,6 +24,15 @@ config PHY_BCM_NS_USB2
24 Enable this to support Broadcom USB 2.0 PHY connected to the USB 24 Enable this to support Broadcom USB 2.0 PHY connected to the USB
25 controller on Northstar family. 25 controller on Northstar family.
26 26
27config PHY_BCM_NS_USB3
28 tristate "Broadcom Northstar USB 3.0 PHY Driver"
29 depends on ARCH_BCM_IPROC || COMPILE_TEST
30 depends on HAS_IOMEM && OF
31 select GENERIC_PHY
32 help
33 Enable this to support Broadcom USB 3.0 PHY connected to the USB
34 controller on Northstar family.
35
27config PHY_BERLIN_USB 36config PHY_BERLIN_USB
28 tristate "Marvell Berlin USB PHY Driver" 37 tristate "Marvell Berlin USB PHY Driver"
29 depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF 38 depends on ARCH_BERLIN && RESET_CONTROLLER && HAS_IOMEM && OF
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 90ae19879b0a..b99370a14ed9 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -4,6 +4,7 @@
4 4
5obj-$(CONFIG_GENERIC_PHY) += phy-core.o 5obj-$(CONFIG_GENERIC_PHY) += phy-core.o
6obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o 6obj-$(CONFIG_PHY_BCM_NS_USB2) += phy-bcm-ns-usb2.o
7obj-$(CONFIG_PHY_BCM_NS_USB3) += phy-bcm-ns-usb3.o
7obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o 8obj-$(CONFIG_PHY_BERLIN_USB) += phy-berlin-usb.o
8obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o 9obj-$(CONFIG_PHY_BERLIN_SATA) += phy-berlin-sata.o
9obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o 10obj-$(CONFIG_PHY_DA8XX_USB) += phy-da8xx-usb.o
diff --git a/drivers/phy/phy-bcm-ns-usb3.c b/drivers/phy/phy-bcm-ns-usb3.c
new file mode 100644
index 000000000000..f420fa4bebfc
--- /dev/null
+++ b/drivers/phy/phy-bcm-ns-usb3.c
@@ -0,0 +1,274 @@
1/*
2 * Broadcom Northstar USB 3.0 PHY Driver
3 *
4 * Copyright (C) 2016 Rafał Miłecki <rafal@milecki.pl>
5 *
6 * All magic values used for initialization (and related comments) were obtained
7 * from Broadcom's SDK:
8 * Copyright (c) Broadcom Corp, 2012
9 *
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.
13 */
14
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>
23
24#define BCM_NS_USB3_MII_MNG_TIMEOUT_US 1000 /* usecs */
25
26enum bcm_ns_family {
27 BCM_NS_UNKNOWN,
28 BCM_NS_AX,
29 BCM_NS_BX,
30};
31
32struct bcm_ns_usb3 {
33 struct device *dev;
34 enum bcm_ns_family family;
35 void __iomem *dmp;
36 void __iomem *ccb_mii;
37 struct phy *phy;
38};
39
40static const struct of_device_id bcm_ns_usb3_id_table[] = {
41 {
42 .compatible = "brcm,ns-ax-usb3-phy",
43 .data = (int *)BCM_NS_AX,
44 },
45 {
46 .compatible = "brcm,ns-bx-usb3-phy",
47 .data = (int *)BCM_NS_BX,
48 },
49 {},
50};
51MODULE_DEVICE_TABLE(of, bcm_ns_usb3_id_table);
52
53static int bcm_ns_usb3_wait_reg(struct bcm_ns_usb3 *usb3, void __iomem *addr,
54 u32 mask, u32 value, unsigned long timeout)
55{
56 unsigned long deadline = jiffies + timeout;
57 u32 val;
58
59 do {
60 val = readl(addr);
61 if ((val & mask) == value)
62 return 0;
63 cpu_relax();
64 udelay(10);
65 } while (!time_after_eq(jiffies, deadline));
66
67 dev_err(usb3->dev, "Timeout waiting for register %p\n", addr);
68
69 return -EBUSY;
70}
71
72static inline int bcm_ns_usb3_mii_mng_wait_idle(struct bcm_ns_usb3 *usb3)
73{
74 return bcm_ns_usb3_wait_reg(usb3, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL,
75 0x0100, 0x0000,
76 usecs_to_jiffies(BCM_NS_USB3_MII_MNG_TIMEOUT_US));
77}
78
79static int bcm_ns_usb3_mii_mng_write32(struct bcm_ns_usb3 *usb3, u32 value)
80{
81 int err;
82
83 err = bcm_ns_usb3_mii_mng_wait_idle(usb3);
84 if (err < 0) {
85 dev_err(usb3->dev, "Couldn't write 0x%08x value\n", value);
86 return err;
87 }
88
89 writel(value, usb3->ccb_mii + BCMA_CCB_MII_MNG_CMD_DATA);
90
91 return 0;
92}
93
94static int bcm_ns_usb3_phy_init_ns_bx(struct bcm_ns_usb3 *usb3)
95{
96 int err;
97
98 /* Enable MDIO. Setting MDCDIV as 26 */
99 writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
100
101 /* Wait for MDIO? */
102 udelay(2);
103
104 /* USB3 PLL Block */
105 err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
106 if (err < 0)
107 return err;
108
109 /* Assert Ana_Pllseq start */
110 bcm_ns_usb3_mii_mng_write32(usb3, 0x58061000);
111
112 /* Assert CML Divider ratio to 26 */
113 bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
114
115 /* Asserting PLL Reset */
116 bcm_ns_usb3_mii_mng_write32(usb3, 0x582ec000);
117
118 /* Deaaserting PLL Reset */
119 bcm_ns_usb3_mii_mng_write32(usb3, 0x582e8000);
120
121 /* Waiting MII Mgt interface idle */
122 bcm_ns_usb3_mii_mng_wait_idle(usb3);
123
124 /* Deasserting USB3 system reset */
125 writel(0, usb3->dmp + BCMA_RESET_CTL);
126
127 /* PLL frequency monitor enable */
128 bcm_ns_usb3_mii_mng_write32(usb3, 0x58069000);
129
130 /* PIPE Block */
131 bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8060);
132
133 /* CMPMAX & CMPMINTH setting */
134 bcm_ns_usb3_mii_mng_write32(usb3, 0x580af30d);
135
136 /* DEGLITCH MIN & MAX setting */
137 bcm_ns_usb3_mii_mng_write32(usb3, 0x580e6302);
138
139 /* TXPMD block */
140 bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
141
142 /* Enabling SSC */
143 bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
144
145 /* Waiting MII Mgt interface idle */
146 bcm_ns_usb3_mii_mng_wait_idle(usb3);
147
148 return 0;
149}
150
151static int bcm_ns_usb3_phy_init_ns_ax(struct bcm_ns_usb3 *usb3)
152{
153 int err;
154
155 /* Enable MDIO. Setting MDCDIV as 26 */
156 writel(0x0000009a, usb3->ccb_mii + BCMA_CCB_MII_MNG_CTL);
157
158 /* Wait for MDIO? */
159 udelay(2);
160
161 /* PLL30 block */
162 err = bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8000);
163 if (err < 0)
164 return err;
165
166 bcm_ns_usb3_mii_mng_write32(usb3, 0x582a6400);
167
168 bcm_ns_usb3_mii_mng_write32(usb3, 0x587e80e0);
169
170 bcm_ns_usb3_mii_mng_write32(usb3, 0x580a009c);
171
172 /* Enable SSC */
173 bcm_ns_usb3_mii_mng_write32(usb3, 0x587e8040);
174
175 bcm_ns_usb3_mii_mng_write32(usb3, 0x580a21d3);
176
177 bcm_ns_usb3_mii_mng_write32(usb3, 0x58061003);
178
179 /* Waiting MII Mgt interface idle */
180 bcm_ns_usb3_mii_mng_wait_idle(usb3);
181
182 /* Deasserting USB3 system reset */
183 writel(0, usb3->dmp + BCMA_RESET_CTL);
184
185 return 0;
186}
187
188static int bcm_ns_usb3_phy_init(struct phy *phy)
189{
190 struct bcm_ns_usb3 *usb3 = phy_get_drvdata(phy);
191 int err;
192
193 /* Perform USB3 system soft reset */
194 writel(BCMA_RESET_CTL_RESET, usb3->dmp + BCMA_RESET_CTL);
195
196 switch (usb3->family) {
197 case BCM_NS_AX:
198 err = bcm_ns_usb3_phy_init_ns_ax(usb3);
199 break;
200 case BCM_NS_BX:
201 err = bcm_ns_usb3_phy_init_ns_bx(usb3);
202 break;
203 default:
204 WARN_ON(1);
205 err = -ENOTSUPP;
206 }
207
208 return err;
209}
210
211static const struct phy_ops ops = {
212 .init = bcm_ns_usb3_phy_init,
213 .owner = THIS_MODULE,
214};
215
216static int bcm_ns_usb3_probe(struct platform_device *pdev)
217{
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;
223
224 usb3 = devm_kzalloc(dev, sizeof(*usb3), GFP_KERNEL);
225 if (!usb3)
226 return -ENOMEM;
227
228 usb3->dev = dev;
229
230 of_id = of_match_device(bcm_ns_usb3_id_table, dev);
231 if (!of_id)
232 return -EINVAL;
233 usb3->family = (enum bcm_ns_family)of_id->data;
234
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);
240 }
241
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);
247 }
248
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);
253 }
254
255 phy_set_drvdata(usb3->phy, usb3);
256 platform_set_drvdata(pdev, usb3);
257
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");
261
262 return PTR_ERR_OR_ZERO(phy_provider);
263}
264
265static struct platform_driver bcm_ns_usb3_driver = {
266 .probe = bcm_ns_usb3_probe,
267 .driver = {
268 .name = "bcm_ns_usb3",
269 .of_match_table = bcm_ns_usb3_id_table,
270 },
271};
272module_platform_driver(bcm_ns_usb3_driver);
273
274MODULE_LICENSE("GPL v2");