aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt15
-rw-r--r--Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt22
-rw-r--r--Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt19
-rw-r--r--Documentation/kernel-parameters.txt6
-rw-r--r--drivers/phy/Kconfig16
-rw-r--r--drivers/phy/Makefile2
-rw-r--r--drivers/phy/phy-rcar-gen3-usb2.c83
-rw-r--r--drivers/phy/phy-rockchip-dp.c151
-rw-r--r--drivers/phy/phy-rockchip-emmc.c229
-rw-r--r--drivers/phy/phy-rockchip-usb.c233
10 files changed, 656 insertions, 120 deletions
diff --git a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
index 2390e4e9c84c..eaf7e9b7ce6b 100644
--- a/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
+++ b/Documentation/devicetree/bindings/phy/rcar-gen3-phy-usb2.txt
@@ -7,33 +7,26 @@ Required properties:
7- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795 7- compatible: "renesas,usb2-phy-r8a7795" if the device is a part of an R8A7795
8 SoC. 8 SoC.
9- reg: offset and length of the partial USB 2.0 Host register block. 9- reg: offset and length of the partial USB 2.0 Host register block.
10- reg-names: must be "usb2_host".
11- clocks: clock phandle and specifier pair(s). 10- clocks: clock phandle and specifier pair(s).
12- #phy-cells: see phy-bindings.txt in the same directory, must be <0>. 11- #phy-cells: see phy-bindings.txt in the same directory, must be <0>.
13 12
14Optional properties: 13Optional properties:
15To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are 14To use a USB channel where USB 2.0 Host and HSUSB (USB 2.0 Peripheral) are
16combined, the device tree node should set HSUSB properties to reg and reg-names 15combined, the device tree node should set interrupt properties to use the
17properties. This is because HSUSB has registers to select USB 2.0 host or 16channel as USB OTG:
18peripheral at that channel:
19- reg: offset and length of the partial HSUSB register block.
20- reg-names: must be "hsusb".
21- interrupts: interrupt specifier for the PHY. 17- interrupts: interrupt specifier for the PHY.
22 18
23Example (R-Car H3): 19Example (R-Car H3):
24 20
25 usb-phy@ee080200 { 21 usb-phy@ee080200 {
26 compatible = "renesas,usb2-phy-r8a7795"; 22 compatible = "renesas,usb2-phy-r8a7795";
27 reg = <0 0xee080200 0 0x700>, <0 0xe6590100 0 0x100>; 23 reg = <0 0xee080200 0 0x700>;
28 reg-names = "usb2_host", "hsusb";
29 interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>; 24 interrupts = <GIC_SPI 108 IRQ_TYPE_LEVEL_HIGH>;
30 clocks = <&mstp7_clks R8A7795_CLK_EHCI0>, 25 clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
31 <&mstp7_clks R8A7795_CLK_HSUSB>;
32 }; 26 };
33 27
34 usb-phy@ee0a0200 { 28 usb-phy@ee0a0200 {
35 compatible = "renesas,usb2-phy-r8a7795"; 29 compatible = "renesas,usb2-phy-r8a7795";
36 reg = <0 0xee0a0200 0 0x700>; 30 reg = <0 0xee0a0200 0 0x700>;
37 reg-names = "usb2_host";
38 clocks = <&mstp7_clks R8A7795_CLK_EHCI0>; 31 clocks = <&mstp7_clks R8A7795_CLK_EHCI0>;
39 }; 32 };
diff --git a/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt
new file mode 100644
index 000000000000..50c4f9b00adf
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip-dp-phy.txt
@@ -0,0 +1,22 @@
1Rockchip specific extensions to the Analogix Display Port PHY
2------------------------------------
3
4Required properties:
5- compatible : should be one of the following supported values:
6 - "rockchip.rk3288-dp-phy"
7- clocks: from common clock binding: handle to dp clock.
8 of memory mapped region.
9- clock-names: from common clock binding:
10 Required elements: "24m"
11- rockchip,grf: phandle to the syscon managing the "general register files"
12- #phy-cells : from the generic PHY bindings, must be 0;
13
14Example:
15
16edp_phy: edp-phy {
17 compatible = "rockchip,rk3288-dp-phy";
18 rockchip,grf = <&grf>;
19 clocks = <&cru SCLK_EDP_24M>;
20 clock-names = "24m";
21 #phy-cells = <0>;
22};
diff --git a/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt
new file mode 100644
index 000000000000..61916f15a949
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/rockchip-emmc-phy.txt
@@ -0,0 +1,19 @@
1Rockchip EMMC PHY
2-----------------------
3
4Required properties:
5 - compatible: rockchip,rk3399-emmc-phy
6 - rockchip,grf : phandle to the syscon managing the "general
7 register files"
8 - #phy-cells: must be 0
9 - reg: PHY configure reg address offset in "general
10 register files"
11
12Example:
13
14emmcphy: phy {
15 compatible = "rockchip,rk3399-emmc-phy";
16 rockchip,grf = <&grf>;
17 reg = <0xf780>;
18 #phy-cells = <0>;
19};
diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt
index 9a53c929f017..8a6c6c02f916 100644
--- a/Documentation/kernel-parameters.txt
+++ b/Documentation/kernel-parameters.txt
@@ -3491,6 +3491,12 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
3491 3491
3492 ro [KNL] Mount root device read-only on boot 3492 ro [KNL] Mount root device read-only on boot
3493 3493
3494 rockchip.usb_uart
3495 Enable the uart passthrough on the designated usb port
3496 on Rockchip SoCs. When active, the signals of the
3497 debug-uart get routed to the D+ and D- pins of the usb
3498 port and the regular usb controller gets disabled.
3499
3494 root= [KNL] Root filesystem 3500 root= [KNL] Root filesystem
3495 See name_to_dev_t comment in init/do_mounts.c. 3501 See name_to_dev_t comment in init/do_mounts.c.
3496 3502
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 0124d17bd9fe..26566db09de0 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -32,7 +32,7 @@ config PHY_BERLIN_SATA
32config ARMADA375_USBCLUSTER_PHY 32config ARMADA375_USBCLUSTER_PHY
33 def_bool y 33 def_bool y
34 depends on MACH_ARMADA_375 || COMPILE_TEST 34 depends on MACH_ARMADA_375 || COMPILE_TEST
35 depends on OF 35 depends on OF && HAS_IOMEM
36 select GENERIC_PHY 36 select GENERIC_PHY
37 37
38config PHY_DM816X_USB 38config PHY_DM816X_USB
@@ -337,6 +337,20 @@ config PHY_ROCKCHIP_USB
337 help 337 help
338 Enable this to support the Rockchip USB 2.0 PHY. 338 Enable this to support the Rockchip USB 2.0 PHY.
339 339
340config PHY_ROCKCHIP_EMMC
341 tristate "Rockchip EMMC PHY Driver"
342 depends on ARCH_ROCKCHIP && OF
343 select GENERIC_PHY
344 help
345 Enable this to support the Rockchip EMMC PHY.
346
347config PHY_ROCKCHIP_DP
348 tristate "Rockchip Display Port PHY Driver"
349 depends on ARCH_ROCKCHIP && OF
350 select GENERIC_PHY
351 help
352 Enable this to support the Rockchip Display Port PHY.
353
340config PHY_ST_SPEAR1310_MIPHY 354config PHY_ST_SPEAR1310_MIPHY
341 tristate "ST SPEAR1310-MIPHY driver" 355 tristate "ST SPEAR1310-MIPHY driver"
342 select GENERIC_PHY 356 select GENERIC_PHY
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index c80f09df3bb8..24596a96a887 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -37,6 +37,8 @@ phy-exynos-usb2-$(CONFIG_PHY_S5PV210_USB2) += phy-s5pv210-usb2.o
37obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o 37obj-$(CONFIG_PHY_EXYNOS5_USBDRD) += phy-exynos5-usbdrd.o
38obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o 38obj-$(CONFIG_PHY_QCOM_APQ8064_SATA) += phy-qcom-apq8064-sata.o
39obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o 39obj-$(CONFIG_PHY_ROCKCHIP_USB) += phy-rockchip-usb.o
40obj-$(CONFIG_PHY_ROCKCHIP_EMMC) += phy-rockchip-emmc.o
41obj-$(CONFIG_PHY_ROCKCHIP_DP) += phy-rockchip-dp.o
40obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o 42obj-$(CONFIG_PHY_QCOM_IPQ806X_SATA) += phy-qcom-ipq806x-sata.o
41obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o 43obj-$(CONFIG_PHY_ST_SPEAR1310_MIPHY) += phy-spear1310-miphy.o
42obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o 44obj-$(CONFIG_PHY_ST_SPEAR1340_MIPHY) += phy-spear1340-miphy.o
diff --git a/drivers/phy/phy-rcar-gen3-usb2.c b/drivers/phy/phy-rcar-gen3-usb2.c
index ef332ef4abc7..bc4f7dd821aa 100644
--- a/drivers/phy/phy-rcar-gen3-usb2.c
+++ b/drivers/phy/phy-rcar-gen3-usb2.c
@@ -74,20 +74,6 @@
74#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ 74#define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */
75#define USB2_ADPCTRL_DRVVBUS BIT(4) 75#define USB2_ADPCTRL_DRVVBUS BIT(4)
76 76
77/******* HSUSB registers (original offset is +0x100) *******/
78#define HSUSB_LPSTS 0x02
79#define HSUSB_UGCTRL2 0x84
80
81/* Low Power Status register (LPSTS) */
82#define HSUSB_LPSTS_SUSPM 0x4000
83
84/* USB General control register 2 (UGCTRL2) */
85#define HSUSB_UGCTRL2_MASK 0x00000031 /* bit[31:6] should be 0 */
86#define HSUSB_UGCTRL2_USB0SEL 0x00000030
87#define HSUSB_UGCTRL2_USB0SEL_HOST 0x00000010
88#define HSUSB_UGCTRL2_USB0SEL_HS_USB 0x00000020
89#define HSUSB_UGCTRL2_USB0SEL_OTG 0x00000030
90
91struct rcar_gen3_data { 77struct rcar_gen3_data {
92 void __iomem *base; 78 void __iomem *base;
93 struct clk *clk; 79 struct clk *clk;
@@ -95,8 +81,8 @@ struct rcar_gen3_data {
95 81
96struct rcar_gen3_chan { 82struct rcar_gen3_chan {
97 struct rcar_gen3_data usb2; 83 struct rcar_gen3_data usb2;
98 struct rcar_gen3_data hsusb;
99 struct phy *phy; 84 struct phy *phy;
85 bool has_otg;
100}; 86};
101 87
102static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host) 88static void rcar_gen3_set_host_mode(struct rcar_gen3_chan *ch, int host)
@@ -202,24 +188,15 @@ static int rcar_gen3_phy_usb2_init(struct phy *p)
202{ 188{
203 struct rcar_gen3_chan *channel = phy_get_drvdata(p); 189 struct rcar_gen3_chan *channel = phy_get_drvdata(p);
204 void __iomem *usb2_base = channel->usb2.base; 190 void __iomem *usb2_base = channel->usb2.base;
205 void __iomem *hsusb_base = channel->hsusb.base;
206 u32 val;
207 191
208 /* Initialize USB2 part */ 192 /* Initialize USB2 part */
209 writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE); 193 writel(USB2_INT_ENABLE_INIT, usb2_base + USB2_INT_ENABLE);
210 writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET); 194 writel(USB2_SPD_RSM_TIMSET_INIT, usb2_base + USB2_SPD_RSM_TIMSET);
211 writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET); 195 writel(USB2_OC_TIMSET_INIT, usb2_base + USB2_OC_TIMSET);
212 196
213 /* Initialize HSUSB part */ 197 /* Initialize otg part */
214 if (hsusb_base) { 198 if (channel->has_otg)
215 val = readl(hsusb_base + HSUSB_UGCTRL2);
216 val = (val & ~HSUSB_UGCTRL2_USB0SEL) |
217 HSUSB_UGCTRL2_USB0SEL_OTG;
218 writel(val & HSUSB_UGCTRL2_MASK, hsusb_base + HSUSB_UGCTRL2);
219
220 /* Initialize otg part */
221 rcar_gen3_init_otg(channel); 199 rcar_gen3_init_otg(channel);
222 }
223 200
224 return 0; 201 return 0;
225} 202}
@@ -237,7 +214,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
237{ 214{
238 struct rcar_gen3_chan *channel = phy_get_drvdata(p); 215 struct rcar_gen3_chan *channel = phy_get_drvdata(p);
239 void __iomem *usb2_base = channel->usb2.base; 216 void __iomem *usb2_base = channel->usb2.base;
240 void __iomem *hsusb_base = channel->hsusb.base;
241 u32 val; 217 u32 val;
242 218
243 val = readl(usb2_base + USB2_USBCTR); 219 val = readl(usb2_base + USB2_USBCTR);
@@ -246,33 +222,6 @@ static int rcar_gen3_phy_usb2_power_on(struct phy *p)
246 val &= ~USB2_USBCTR_PLL_RST; 222 val &= ~USB2_USBCTR_PLL_RST;
247 writel(val, usb2_base + USB2_USBCTR); 223 writel(val, usb2_base + USB2_USBCTR);
248 224
249 /*
250 * TODO: To reduce power consuming, this driver should set the SUSPM
251 * after the PHY detects ID pin as peripheral.
252 */
253 if (hsusb_base) {
254 /* Power on HSUSB PHY */
255 val = readw(hsusb_base + HSUSB_LPSTS);
256 val |= HSUSB_LPSTS_SUSPM;
257 writew(val, hsusb_base + HSUSB_LPSTS);
258 }
259
260 return 0;
261}
262
263static int rcar_gen3_phy_usb2_power_off(struct phy *p)
264{
265 struct rcar_gen3_chan *channel = phy_get_drvdata(p);
266 void __iomem *hsusb_base = channel->hsusb.base;
267 u32 val;
268
269 if (hsusb_base) {
270 /* Power off HSUSB PHY */
271 val = readw(hsusb_base + HSUSB_LPSTS);
272 val &= ~HSUSB_LPSTS_SUSPM;
273 writew(val, hsusb_base + HSUSB_LPSTS);
274 }
275
276 return 0; 225 return 0;
277} 226}
278 227
@@ -280,7 +229,6 @@ static struct phy_ops rcar_gen3_phy_usb2_ops = {
280 .init = rcar_gen3_phy_usb2_init, 229 .init = rcar_gen3_phy_usb2_init,
281 .exit = rcar_gen3_phy_usb2_exit, 230 .exit = rcar_gen3_phy_usb2_exit,
282 .power_on = rcar_gen3_phy_usb2_power_on, 231 .power_on = rcar_gen3_phy_usb2_power_on,
283 .power_off = rcar_gen3_phy_usb2_power_off,
284 .owner = THIS_MODULE, 232 .owner = THIS_MODULE,
285}; 233};
286 234
@@ -313,6 +261,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
313 struct rcar_gen3_chan *channel; 261 struct rcar_gen3_chan *channel;
314 struct phy_provider *provider; 262 struct phy_provider *provider;
315 struct resource *res; 263 struct resource *res;
264 int irq;
316 265
317 if (!dev->of_node) { 266 if (!dev->of_node) {
318 dev_err(dev, "This driver needs device tree\n"); 267 dev_err(dev, "This driver needs device tree\n");
@@ -323,29 +272,19 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev)
323 if (!channel) 272 if (!channel)
324 return -ENOMEM; 273 return -ENOMEM;
325 274
326 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "usb2_host"); 275 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
327 channel->usb2.base = devm_ioremap_resource(dev, res); 276 channel->usb2.base = devm_ioremap_resource(dev, res);
328 if (IS_ERR(channel->usb2.base)) 277 if (IS_ERR(channel->usb2.base))
329 return PTR_ERR(channel->usb2.base); 278 return PTR_ERR(channel->usb2.base);
330 279
331 /* "hsusb" memory resource is optional */ 280 /* call request_irq for OTG */
332 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "hsusb"); 281 irq = platform_get_irq(pdev, 0);
333 282 if (irq >= 0) {
334 /* To avoid error message by devm_ioremap_resource() */ 283 irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
335 if (res) { 284 IRQF_SHARED, dev_name(dev), channel);
336 int irq;
337
338 channel->hsusb.base = devm_ioremap_resource(dev, res);
339 if (IS_ERR(channel->hsusb.base))
340 channel->hsusb.base = NULL;
341 /* call request_irq for OTG */
342 irq = platform_get_irq(pdev, 0);
343 if (irq >= 0)
344 irq = devm_request_irq(dev, irq, rcar_gen3_phy_usb2_irq,
345 IRQF_SHARED, dev_name(dev),
346 channel);
347 if (irq < 0) 285 if (irq < 0)
348 dev_err(dev, "No irq handler (%d)\n", irq); 286 dev_err(dev, "No irq handler (%d)\n", irq);
287 channel->has_otg = true;
349 } 288 }
350 289
351 /* devm_phy_create() will call pm_runtime_enable(dev); */ 290 /* devm_phy_create() will call pm_runtime_enable(dev); */
diff --git a/drivers/phy/phy-rockchip-dp.c b/drivers/phy/phy-rockchip-dp.c
new file mode 100644
index 000000000000..77e2d02e6bee
--- /dev/null
+++ b/drivers/phy/phy-rockchip-dp.c
@@ -0,0 +1,151 @@
1/*
2 * Rockchip DP PHY driver
3 *
4 * Copyright (C) 2016 FuZhou Rockchip Co., Ltd.
5 * Author: Yakir Yang <ykk@@rock-chips.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License.
10 */
11
12#include <linux/clk.h>
13#include <linux/mfd/syscon.h>
14#include <linux/module.h>
15#include <linux/of.h>
16#include <linux/phy/phy.h>
17#include <linux/platform_device.h>
18#include <linux/regmap.h>
19
20#define GRF_SOC_CON12 0x0274
21
22#define GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK BIT(20)
23#define GRF_EDP_REF_CLK_SEL_INTER BIT(4)
24
25#define GRF_EDP_PHY_SIDDQ_HIWORD_MASK BIT(21)
26#define GRF_EDP_PHY_SIDDQ_ON 0
27#define GRF_EDP_PHY_SIDDQ_OFF BIT(5)
28
29struct rockchip_dp_phy {
30 struct device *dev;
31 struct regmap *grf;
32 struct clk *phy_24m;
33};
34
35static int rockchip_set_phy_state(struct phy *phy, bool enable)
36{
37 struct rockchip_dp_phy *dp = phy_get_drvdata(phy);
38 int ret;
39
40 if (enable) {
41 ret = regmap_write(dp->grf, GRF_SOC_CON12,
42 GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
43 GRF_EDP_PHY_SIDDQ_ON);
44 if (ret < 0) {
45 dev_err(dp->dev, "Can't enable PHY power %d\n", ret);
46 return ret;
47 }
48
49 ret = clk_prepare_enable(dp->phy_24m);
50 } else {
51 clk_disable_unprepare(dp->phy_24m);
52
53 ret = regmap_write(dp->grf, GRF_SOC_CON12,
54 GRF_EDP_PHY_SIDDQ_HIWORD_MASK |
55 GRF_EDP_PHY_SIDDQ_OFF);
56 }
57
58 return ret;
59}
60
61static int rockchip_dp_phy_power_on(struct phy *phy)
62{
63 return rockchip_set_phy_state(phy, true);
64}
65
66static int rockchip_dp_phy_power_off(struct phy *phy)
67{
68 return rockchip_set_phy_state(phy, false);
69}
70
71static const struct phy_ops rockchip_dp_phy_ops = {
72 .power_on = rockchip_dp_phy_power_on,
73 .power_off = rockchip_dp_phy_power_off,
74 .owner = THIS_MODULE,
75};
76
77static int rockchip_dp_phy_probe(struct platform_device *pdev)
78{
79 struct device *dev = &pdev->dev;
80 struct device_node *np = dev->of_node;
81 struct phy_provider *phy_provider;
82 struct rockchip_dp_phy *dp;
83 struct phy *phy;
84 int ret;
85
86 if (!np)
87 return -ENODEV;
88
89 dp = devm_kzalloc(dev, sizeof(*dp), GFP_KERNEL);
90 if (IS_ERR(dp))
91 return -ENOMEM;
92
93 dp->dev = dev;
94
95 dp->phy_24m = devm_clk_get(dev, "24m");
96 if (IS_ERR(dp->phy_24m)) {
97 dev_err(dev, "cannot get clock 24m\n");
98 return PTR_ERR(dp->phy_24m);
99 }
100
101 ret = clk_set_rate(dp->phy_24m, 24000000);
102 if (ret < 0) {
103 dev_err(dp->dev, "cannot set clock phy_24m %d\n", ret);
104 return ret;
105 }
106
107 dp->grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
108 if (IS_ERR(dp->grf)) {
109 dev_err(dev, "rk3288-dp needs rockchip,grf property\n");
110 return PTR_ERR(dp->grf);
111 }
112
113 ret = regmap_write(dp->grf, GRF_SOC_CON12, GRF_EDP_REF_CLK_SEL_INTER |
114 GRF_EDP_REF_CLK_SEL_INTER_HIWORD_MASK);
115 if (ret != 0) {
116 dev_err(dp->dev, "Could not config GRF edp ref clk: %d\n", ret);
117 return ret;
118 }
119
120 phy = devm_phy_create(dev, np, &rockchip_dp_phy_ops);
121 if (IS_ERR(phy)) {
122 dev_err(dev, "failed to create phy\n");
123 return PTR_ERR(phy);
124 }
125 phy_set_drvdata(phy, dp);
126
127 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
128
129 return PTR_ERR_OR_ZERO(phy_provider);
130}
131
132static const struct of_device_id rockchip_dp_phy_dt_ids[] = {
133 { .compatible = "rockchip,rk3288-dp-phy" },
134 {}
135};
136
137MODULE_DEVICE_TABLE(of, rockchip_dp_phy_dt_ids);
138
139static struct platform_driver rockchip_dp_phy_driver = {
140 .probe = rockchip_dp_phy_probe,
141 .driver = {
142 .name = "rockchip-dp-phy",
143 .of_match_table = rockchip_dp_phy_dt_ids,
144 },
145};
146
147module_platform_driver(rockchip_dp_phy_driver);
148
149MODULE_AUTHOR("Yakir Yang <ykk@rock-chips.com>");
150MODULE_DESCRIPTION("Rockchip DP PHY driver");
151MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-emmc.c b/drivers/phy/phy-rockchip-emmc.c
new file mode 100644
index 000000000000..887b4c27195f
--- /dev/null
+++ b/drivers/phy/phy-rockchip-emmc.c
@@ -0,0 +1,229 @@
1/*
2 * Rockchip emmc PHY driver
3 *
4 * Copyright (C) 2016 Shawn Lin <shawn.lin@rock-chips.com>
5 * Copyright (C) 2016 ROCKCHIP, Inc.
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 */
16
17#include <linux/delay.h>
18#include <linux/mfd/syscon.h>
19#include <linux/module.h>
20#include <linux/of.h>
21#include <linux/of_address.h>
22#include <linux/phy/phy.h>
23#include <linux/platform_device.h>
24#include <linux/regmap.h>
25
26/*
27 * The higher 16-bit of this register is used for write protection
28 * only if BIT(x + 16) set to 1 the BIT(x) can be written.
29 */
30#define HIWORD_UPDATE(val, mask, shift) \
31 ((val) << (shift) | (mask) << ((shift) + 16))
32
33/* Register definition */
34#define GRF_EMMCPHY_CON0 0x0
35#define GRF_EMMCPHY_CON1 0x4
36#define GRF_EMMCPHY_CON2 0x8
37#define GRF_EMMCPHY_CON3 0xc
38#define GRF_EMMCPHY_CON4 0x10
39#define GRF_EMMCPHY_CON5 0x14
40#define GRF_EMMCPHY_CON6 0x18
41#define GRF_EMMCPHY_STATUS 0x20
42
43#define PHYCTRL_PDB_MASK 0x1
44#define PHYCTRL_PDB_SHIFT 0x0
45#define PHYCTRL_PDB_PWR_ON 0x1
46#define PHYCTRL_PDB_PWR_OFF 0x0
47#define PHYCTRL_ENDLL_MASK 0x1
48#define PHYCTRL_ENDLL_SHIFT 0x1
49#define PHYCTRL_ENDLL_ENABLE 0x1
50#define PHYCTRL_ENDLL_DISABLE 0x0
51#define PHYCTRL_CALDONE_MASK 0x1
52#define PHYCTRL_CALDONE_SHIFT 0x6
53#define PHYCTRL_CALDONE_DONE 0x1
54#define PHYCTRL_CALDONE_GOING 0x0
55#define PHYCTRL_DLLRDY_MASK 0x1
56#define PHYCTRL_DLLRDY_SHIFT 0x5
57#define PHYCTRL_DLLRDY_DONE 0x1
58#define PHYCTRL_DLLRDY_GOING 0x0
59
60struct rockchip_emmc_phy {
61 unsigned int reg_offset;
62 struct regmap *reg_base;
63};
64
65static int rockchip_emmc_phy_power(struct rockchip_emmc_phy *rk_phy,
66 bool on_off)
67{
68 unsigned int caldone;
69 unsigned int dllrdy;
70
71 /*
72 * Keep phyctrl_pdb and phyctrl_endll low to allow
73 * initialization of CALIO state M/C DFFs
74 */
75 regmap_write(rk_phy->reg_base,
76 rk_phy->reg_offset + GRF_EMMCPHY_CON6,
77 HIWORD_UPDATE(PHYCTRL_PDB_PWR_OFF,
78 PHYCTRL_PDB_MASK,
79 PHYCTRL_PDB_SHIFT));
80 regmap_write(rk_phy->reg_base,
81 rk_phy->reg_offset + GRF_EMMCPHY_CON6,
82 HIWORD_UPDATE(PHYCTRL_ENDLL_DISABLE,
83 PHYCTRL_ENDLL_MASK,
84 PHYCTRL_ENDLL_SHIFT));
85
86 /* Already finish power_off above */
87 if (on_off == PHYCTRL_PDB_PWR_OFF)
88 return 0;
89
90 /*
91 * According to the user manual, calpad calibration
92 * cycle takes more than 2us without the minimal recommended
93 * value, so we may need a little margin here
94 */
95 udelay(3);
96 regmap_write(rk_phy->reg_base,
97 rk_phy->reg_offset + GRF_EMMCPHY_CON6,
98 HIWORD_UPDATE(PHYCTRL_PDB_PWR_ON,
99 PHYCTRL_PDB_MASK,
100 PHYCTRL_PDB_SHIFT));
101
102 /*
103 * According to the user manual, it asks driver to
104 * wait 5us for calpad busy trimming
105 */
106 udelay(5);
107 regmap_read(rk_phy->reg_base,
108 rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
109 &caldone);
110 caldone = (caldone >> PHYCTRL_CALDONE_SHIFT) & PHYCTRL_CALDONE_MASK;
111 if (caldone != PHYCTRL_CALDONE_DONE) {
112 pr_err("rockchip_emmc_phy_power: caldone timeout.\n");
113 return -ETIMEDOUT;
114 }
115
116 regmap_write(rk_phy->reg_base,
117 rk_phy->reg_offset + GRF_EMMCPHY_CON6,
118 HIWORD_UPDATE(PHYCTRL_ENDLL_ENABLE,
119 PHYCTRL_ENDLL_MASK,
120 PHYCTRL_ENDLL_SHIFT));
121 /*
122 * After enable analog DLL circuits, we need extra 10.2us
123 * for dll to be ready for work.
124 */
125 udelay(11);
126 regmap_read(rk_phy->reg_base,
127 rk_phy->reg_offset + GRF_EMMCPHY_STATUS,
128 &dllrdy);
129 dllrdy = (dllrdy >> PHYCTRL_DLLRDY_SHIFT) & PHYCTRL_DLLRDY_MASK;
130 if (dllrdy != PHYCTRL_DLLRDY_DONE) {
131 pr_err("rockchip_emmc_phy_power: dllrdy timeout.\n");
132 return -ETIMEDOUT;
133 }
134
135 return 0;
136}
137
138static int rockchip_emmc_phy_power_off(struct phy *phy)
139{
140 struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
141 int ret = 0;
142
143 /* Power down emmc phy analog blocks */
144 ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_OFF);
145 if (ret)
146 return ret;
147
148 return 0;
149}
150
151static int rockchip_emmc_phy_power_on(struct phy *phy)
152{
153 struct rockchip_emmc_phy *rk_phy = phy_get_drvdata(phy);
154 int ret = 0;
155
156 /* Power up emmc phy analog blocks */
157 ret = rockchip_emmc_phy_power(rk_phy, PHYCTRL_PDB_PWR_ON);
158 if (ret)
159 return ret;
160
161 return 0;
162}
163
164static const struct phy_ops ops = {
165 .power_on = rockchip_emmc_phy_power_on,
166 .power_off = rockchip_emmc_phy_power_off,
167 .owner = THIS_MODULE,
168};
169
170static int rockchip_emmc_phy_probe(struct platform_device *pdev)
171{
172 struct device *dev = &pdev->dev;
173 struct rockchip_emmc_phy *rk_phy;
174 struct phy *generic_phy;
175 struct phy_provider *phy_provider;
176 struct regmap *grf;
177 unsigned int reg_offset;
178
179 grf = syscon_regmap_lookup_by_phandle(dev->of_node, "rockchip,grf");
180 if (IS_ERR(grf)) {
181 dev_err(dev, "Missing rockchip,grf property\n");
182 return PTR_ERR(grf);
183 }
184
185 rk_phy = devm_kzalloc(dev, sizeof(*rk_phy), GFP_KERNEL);
186 if (!rk_phy)
187 return -ENOMEM;
188
189 if (of_property_read_u32(dev->of_node, "reg", &reg_offset)) {
190 dev_err(dev, "missing reg property in node %s\n",
191 dev->of_node->name);
192 return -EINVAL;
193 }
194
195 rk_phy->reg_offset = reg_offset;
196 rk_phy->reg_base = grf;
197
198 generic_phy = devm_phy_create(dev, dev->of_node, &ops);
199 if (IS_ERR(generic_phy)) {
200 dev_err(dev, "failed to create PHY\n");
201 return PTR_ERR(generic_phy);
202 }
203
204 phy_set_drvdata(generic_phy, rk_phy);
205 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
206
207 return PTR_ERR_OR_ZERO(phy_provider);
208}
209
210static const struct of_device_id rockchip_emmc_phy_dt_ids[] = {
211 { .compatible = "rockchip,rk3399-emmc-phy" },
212 {}
213};
214
215MODULE_DEVICE_TABLE(of, rockchip_emmc_phy_dt_ids);
216
217static struct platform_driver rockchip_emmc_driver = {
218 .probe = rockchip_emmc_phy_probe,
219 .driver = {
220 .name = "rockchip-emmc-phy",
221 .of_match_table = rockchip_emmc_phy_dt_ids,
222 },
223};
224
225module_platform_driver(rockchip_emmc_driver);
226
227MODULE_AUTHOR("Shawn Lin <shawn.lin@rock-chips.com>");
228MODULE_DESCRIPTION("Rockchip EMMC PHY driver");
229MODULE_LICENSE("GPL v2");
diff --git a/drivers/phy/phy-rockchip-usb.c b/drivers/phy/phy-rockchip-usb.c
index 33a80eba1cb4..f62d899063a3 100644
--- a/drivers/phy/phy-rockchip-usb.c
+++ b/drivers/phy/phy-rockchip-usb.c
@@ -30,21 +30,23 @@
30#include <linux/regmap.h> 30#include <linux/regmap.h>
31#include <linux/mfd/syscon.h> 31#include <linux/mfd/syscon.h>
32 32
33/* 33static int enable_usb_uart;
34 * The higher 16-bit of this register is used for write protection 34
35 * only if BIT(13 + 16) set to 1 the BIT(13) can be written. 35#define HIWORD_UPDATE(val, mask) \
36 */ 36 ((val) | (mask) << 16)
37#define SIDDQ_WRITE_ENA BIT(29) 37
38#define SIDDQ_ON BIT(13) 38#define UOC_CON0_SIDDQ BIT(13)
39#define SIDDQ_OFF (0 << 13)
40 39
41struct rockchip_usb_phys { 40struct rockchip_usb_phys {
42 int reg; 41 int reg;
43 const char *pll_name; 42 const char *pll_name;
44}; 43};
45 44
45struct rockchip_usb_phy_base;
46struct rockchip_usb_phy_pdata { 46struct rockchip_usb_phy_pdata {
47 struct rockchip_usb_phys *phys; 47 struct rockchip_usb_phys *phys;
48 int (*init_usb_uart)(struct regmap *grf);
49 int usb_uart_phy;
48}; 50};
49 51
50struct rockchip_usb_phy_base { 52struct rockchip_usb_phy_base {
@@ -61,13 +63,15 @@ struct rockchip_usb_phy {
61 struct clk *clk480m; 63 struct clk *clk480m;
62 struct clk_hw clk480m_hw; 64 struct clk_hw clk480m_hw;
63 struct phy *phy; 65 struct phy *phy;
66 bool uart_enabled;
64}; 67};
65 68
66static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy, 69static int rockchip_usb_phy_power(struct rockchip_usb_phy *phy,
67 bool siddq) 70 bool siddq)
68{ 71{
69 return regmap_write(phy->base->reg_base, phy->reg_offset, 72 u32 val = HIWORD_UPDATE(siddq ? UOC_CON0_SIDDQ : 0, UOC_CON0_SIDDQ);
70 SIDDQ_WRITE_ENA | (siddq ? SIDDQ_ON : SIDDQ_OFF)); 73
74 return regmap_write(phy->base->reg_base, phy->reg_offset, val);
71} 75}
72 76
73static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw, 77static unsigned long rockchip_usb_phy480m_recalc_rate(struct clk_hw *hw,
@@ -108,7 +112,7 @@ static int rockchip_usb_phy480m_is_enabled(struct clk_hw *hw)
108 if (ret < 0) 112 if (ret < 0)
109 return ret; 113 return ret;
110 114
111 return (val & SIDDQ_ON) ? 0 : 1; 115 return (val & UOC_CON0_SIDDQ) ? 0 : 1;
112} 116}
113 117
114static const struct clk_ops rockchip_usb_phy480m_ops = { 118static const struct clk_ops rockchip_usb_phy480m_ops = {
@@ -122,6 +126,9 @@ static int rockchip_usb_phy_power_off(struct phy *_phy)
122{ 126{
123 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); 127 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
124 128
129 if (phy->uart_enabled)
130 return -EBUSY;
131
125 clk_disable_unprepare(phy->clk480m); 132 clk_disable_unprepare(phy->clk480m);
126 133
127 return 0; 134 return 0;
@@ -131,6 +138,9 @@ static int rockchip_usb_phy_power_on(struct phy *_phy)
131{ 138{
132 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy); 139 struct rockchip_usb_phy *phy = phy_get_drvdata(_phy);
133 140
141 if (phy->uart_enabled)
142 return -EBUSY;
143
134 return clk_prepare_enable(phy->clk480m); 144 return clk_prepare_enable(phy->clk480m);
135} 145}
136 146
@@ -144,8 +154,10 @@ static void rockchip_usb_phy_action(void *data)
144{ 154{
145 struct rockchip_usb_phy *rk_phy = data; 155 struct rockchip_usb_phy *rk_phy = data;
146 156
147 of_clk_del_provider(rk_phy->np); 157 if (!rk_phy->uart_enabled) {
148 clk_unregister(rk_phy->clk480m); 158 of_clk_del_provider(rk_phy->np);
159 clk_unregister(rk_phy->clk480m);
160 }
149 161
150 if (rk_phy->clk) 162 if (rk_phy->clk)
151 clk_put(rk_phy->clk); 163 clk_put(rk_phy->clk);
@@ -194,30 +206,35 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
194 return -EINVAL; 206 return -EINVAL;
195 } 207 }
196 208
197 if (rk_phy->clk) { 209 if (enable_usb_uart && base->pdata->usb_uart_phy == i) {
198 clk_name = __clk_get_name(rk_phy->clk); 210 dev_dbg(base->dev, "phy%d used as uart output\n", i);
199 init.flags = 0; 211 rk_phy->uart_enabled = true;
200 init.parent_names = &clk_name;
201 init.num_parents = 1;
202 } else { 212 } else {
203 init.flags = CLK_IS_ROOT; 213 if (rk_phy->clk) {
204 init.parent_names = NULL; 214 clk_name = __clk_get_name(rk_phy->clk);
205 init.num_parents = 0; 215 init.flags = 0;
206 } 216 init.parent_names = &clk_name;
217 init.num_parents = 1;
218 } else {
219 init.flags = CLK_IS_ROOT;
220 init.parent_names = NULL;
221 init.num_parents = 0;
222 }
207 223
208 init.ops = &rockchip_usb_phy480m_ops; 224 init.ops = &rockchip_usb_phy480m_ops;
209 rk_phy->clk480m_hw.init = &init; 225 rk_phy->clk480m_hw.init = &init;
210 226
211 rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw); 227 rk_phy->clk480m = clk_register(base->dev, &rk_phy->clk480m_hw);
212 if (IS_ERR(rk_phy->clk480m)) { 228 if (IS_ERR(rk_phy->clk480m)) {
213 err = PTR_ERR(rk_phy->clk480m); 229 err = PTR_ERR(rk_phy->clk480m);
214 goto err_clk; 230 goto err_clk;
215 } 231 }
216 232
217 err = of_clk_add_provider(child, of_clk_src_simple_get, 233 err = of_clk_add_provider(child, of_clk_src_simple_get,
218 rk_phy->clk480m); 234 rk_phy->clk480m);
219 if (err < 0) 235 if (err < 0)
220 goto err_clk_prov; 236 goto err_clk_prov;
237 }
221 238
222 err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy); 239 err = devm_add_action(base->dev, rockchip_usb_phy_action, rk_phy);
223 if (err) 240 if (err)
@@ -230,13 +247,21 @@ static int rockchip_usb_phy_init(struct rockchip_usb_phy_base *base,
230 } 247 }
231 phy_set_drvdata(rk_phy->phy, rk_phy); 248 phy_set_drvdata(rk_phy->phy, rk_phy);
232 249
233 /* only power up usb phy when it use, so disable it when init*/ 250 /*
234 return rockchip_usb_phy_power(rk_phy, 1); 251 * When acting as uart-pipe, just keep clock on otherwise
252 * only power up usb phy when it use, so disable it when init
253 */
254 if (rk_phy->uart_enabled)
255 return clk_prepare_enable(rk_phy->clk);
256 else
257 return rockchip_usb_phy_power(rk_phy, 1);
235 258
236err_devm_action: 259err_devm_action:
237 of_clk_del_provider(child); 260 if (!rk_phy->uart_enabled)
261 of_clk_del_provider(child);
238err_clk_prov: 262err_clk_prov:
239 clk_unregister(rk_phy->clk480m); 263 if (!rk_phy->uart_enabled)
264 clk_unregister(rk_phy->clk480m);
240err_clk: 265err_clk:
241 if (rk_phy->clk) 266 if (rk_phy->clk)
242 clk_put(rk_phy->clk); 267 clk_put(rk_phy->clk);
@@ -259,6 +284,86 @@ static const struct rockchip_usb_phy_pdata rk3188_pdata = {
259 }, 284 },
260}; 285};
261 286
287#define RK3288_UOC0_CON0 0x320
288#define RK3288_UOC0_CON0_COMMON_ON_N BIT(0)
289#define RK3288_UOC0_CON0_DISABLE BIT(4)
290
291#define RK3288_UOC0_CON2 0x328
292#define RK3288_UOC0_CON2_SOFT_CON_SEL BIT(2)
293
294#define RK3288_UOC0_CON3 0x32c
295#define RK3288_UOC0_CON3_UTMI_SUSPENDN BIT(0)
296#define RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING (1 << 1)
297#define RK3288_UOC0_CON3_UTMI_OPMODE_MASK (3 << 1)
298#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC (1 << 3)
299#define RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK (3 << 3)
300#define RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED BIT(5)
301#define RK3288_UOC0_CON3_BYPASSDMEN BIT(6)
302#define RK3288_UOC0_CON3_BYPASSSEL BIT(7)
303
304/*
305 * Enable the bypass of uart2 data through the otg usb phy.
306 * Original description in the TRM.
307 * 1. Disable the OTG block by setting OTGDISABLE0 to 1’b1.
308 * 2. Disable the pull-up resistance on the D+ line by setting
309 * OPMODE0[1:0] to 2’b01.
310 * 3. To ensure that the XO, Bias, and PLL blocks are powered down in Suspend
311 * mode, set COMMONONN to 1’b1.
312 * 4. Place the USB PHY in Suspend mode by setting SUSPENDM0 to 1’b0.
313 * 5. Set BYPASSSEL0 to 1’b1.
314 * 6. To transmit data, controls BYPASSDMEN0, and BYPASSDMDATA0.
315 * To receive data, monitor FSVPLUS0.
316 *
317 * The actual code in the vendor kernel does some things differently.
318 */
319static int __init rk3288_init_usb_uart(struct regmap *grf)
320{
321 u32 val;
322 int ret;
323
324 /*
325 * COMMON_ON and DISABLE settings are described in the TRM,
326 * but were not present in the original code.
327 * Also disable the analog phy components to save power.
328 */
329 val = HIWORD_UPDATE(RK3288_UOC0_CON0_COMMON_ON_N
330 | RK3288_UOC0_CON0_DISABLE
331 | UOC_CON0_SIDDQ,
332 RK3288_UOC0_CON0_COMMON_ON_N
333 | RK3288_UOC0_CON0_DISABLE
334 | UOC_CON0_SIDDQ);
335 ret = regmap_write(grf, RK3288_UOC0_CON0, val);
336 if (ret)
337 return ret;
338
339 val = HIWORD_UPDATE(RK3288_UOC0_CON2_SOFT_CON_SEL,
340 RK3288_UOC0_CON2_SOFT_CON_SEL);
341 ret = regmap_write(grf, RK3288_UOC0_CON2, val);
342 if (ret)
343 return ret;
344
345 val = HIWORD_UPDATE(RK3288_UOC0_CON3_UTMI_OPMODE_NODRIVING
346 | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_FSTRANSC
347 | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED,
348 RK3288_UOC0_CON3_UTMI_SUSPENDN
349 | RK3288_UOC0_CON3_UTMI_OPMODE_MASK
350 | RK3288_UOC0_CON3_UTMI_XCVRSEELCT_MASK
351 | RK3288_UOC0_CON3_UTMI_TERMSEL_FULLSPEED);
352 ret = regmap_write(grf, RK3288_UOC0_CON3, val);
353 if (ret)
354 return ret;
355
356 val = HIWORD_UPDATE(RK3288_UOC0_CON3_BYPASSSEL
357 | RK3288_UOC0_CON3_BYPASSDMEN,
358 RK3288_UOC0_CON3_BYPASSSEL
359 | RK3288_UOC0_CON3_BYPASSDMEN);
360 ret = regmap_write(grf, RK3288_UOC0_CON3, val);
361 if (ret)
362 return ret;
363
364 return 0;
365}
366
262static const struct rockchip_usb_phy_pdata rk3288_pdata = { 367static const struct rockchip_usb_phy_pdata rk3288_pdata = {
263 .phys = (struct rockchip_usb_phys[]){ 368 .phys = (struct rockchip_usb_phys[]){
264 { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" }, 369 { .reg = 0x320, .pll_name = "sclk_otgphy0_480m" },
@@ -266,6 +371,8 @@ static const struct rockchip_usb_phy_pdata rk3288_pdata = {
266 { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" }, 371 { .reg = 0x348, .pll_name = "sclk_otgphy2_480m" },
267 { /* sentinel */ } 372 { /* sentinel */ }
268 }, 373 },
374 .init_usb_uart = rk3288_init_usb_uart,
375 .usb_uart_phy = 0,
269}; 376};
270 377
271static int rockchip_usb_phy_probe(struct platform_device *pdev) 378static int rockchip_usb_phy_probe(struct platform_device *pdev)
@@ -328,6 +435,60 @@ static struct platform_driver rockchip_usb_driver = {
328 435
329module_platform_driver(rockchip_usb_driver); 436module_platform_driver(rockchip_usb_driver);
330 437
438#ifndef MODULE
439static int __init rockchip_init_usb_uart(void)
440{
441 const struct of_device_id *match;
442 const struct rockchip_usb_phy_pdata *data;
443 struct device_node *np;
444 struct regmap *grf;
445 int ret;
446
447 if (!enable_usb_uart)
448 return 0;
449
450 np = of_find_matching_node_and_match(NULL, rockchip_usb_phy_dt_ids,
451 &match);
452 if (!np) {
453 pr_err("%s: failed to find usbphy node\n", __func__);
454 return -ENOTSUPP;
455 }
456
457 pr_debug("%s: using settings for %s\n", __func__, match->compatible);
458 data = match->data;
459
460 if (!data->init_usb_uart) {
461 pr_err("%s: usb-uart not available on %s\n",
462 __func__, match->compatible);
463 return -ENOTSUPP;
464 }
465
466 grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
467 if (IS_ERR(grf)) {
468 pr_err("%s: Missing rockchip,grf property, %lu\n",
469 __func__, PTR_ERR(grf));
470 return PTR_ERR(grf);
471 }
472
473 ret = data->init_usb_uart(grf);
474 if (ret) {
475 pr_err("%s: could not init usb_uart, %d\n", __func__, ret);
476 enable_usb_uart = 0;
477 return ret;
478 }
479
480 return 0;
481}
482early_initcall(rockchip_init_usb_uart);
483
484static int __init rockchip_usb_uart(char *buf)
485{
486 enable_usb_uart = true;
487 return 0;
488}
489early_param("rockchip.usb_uart", rockchip_usb_uart);
490#endif
491
331MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>"); 492MODULE_AUTHOR("Yunzhi Li <lyz@rock-chips.com>");
332MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver"); 493MODULE_DESCRIPTION("Rockchip USB 2.0 PHY driver");
333MODULE_LICENSE("GPL v2"); 494MODULE_LICENSE("GPL v2");