aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorChen-Yu Tsai <wens@csie.org>2015-03-13 23:57:16 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2015-04-03 08:46:19 -0400
commit9c3b443026368583d2df3373a11b1c18c361d9a6 (patch)
tree46a8e11d23d07908b7aff3baf46b76e03bec987b
parent609adde838f4557f9d209b0432f4bac5c5eb5e86 (diff)
phy: Add driver to support individual USB PHYs on sun9i
Unlike previous Allwinner SoCs, there is no central PHY control block on the A80. Also, OTG support is completely split off into a different controller. This adds a new driver to support the regular USB PHYs. Signed-off-by: Chen-Yu Tsai <wens@csie.org> Acked-by: Maxime Ripard <maxime.ripard@free-electrons.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
-rw-r--r--Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt38
-rw-r--r--drivers/phy/Kconfig11
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-sun9i-usb.c202
4 files changed, 252 insertions, 0 deletions
diff --git a/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt b/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt
new file mode 100644
index 000000000000..1cca85c709d1
--- /dev/null
+++ b/Documentation/devicetree/bindings/phy/sun9i-usb-phy.txt
@@ -0,0 +1,38 @@
1Allwinner sun9i USB PHY
2-----------------------
3
4Required properties:
5- compatible : should be one of
6 * allwinner,sun9i-a80-usb-phy
7- reg : a list of offset + length pairs
8- #phy-cells : from the generic phy bindings, must be 0
9- phy_type : "hsic" for HSIC usage;
10 other values or absence of this property indicates normal USB
11- clocks : phandle + clock specifier for the phy clocks
12- clock-names : depending on the "phy_type" property,
13 * "phy" for normal USB
14 * "hsic_480M", "hsic_12M" for HSIC
15- resets : a list of phandle + reset specifier pairs
16- reset-names : depending on the "phy_type" property,
17 * "phy" for normal USB
18 * "hsic" for HSIC
19
20Optional Properties:
21- phy-supply : from the generic phy bindings, a phandle to a regulator that
22 provides power to VBUS.
23
24It is recommended to list all clocks and resets available.
25The driver will only use those matching the phy_type.
26
27Example:
28 usbphy1: phy@00a01800 {
29 compatible = "allwinner,sun9i-a80-usb-phy";
30 reg = <0x00a01800 0x4>;
31 clocks = <&usb_phy_clk 2>, <&usb_phy_clk 10>,
32 <&usb_phy_clk 3>;
33 clock-names = "hsic_480M", "hsic_12M", "phy";
34 resets = <&usb_phy_clk 18>, <&usb_phy_clk 19>;
35 reset-names = "hsic", "phy";
36 status = "disabled";
37 #phy-cells = <0>;
38 };
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index 885c39c28a8b..a53bd5b52df9 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -181,6 +181,17 @@ config PHY_SUN4I_USB
181 This driver controls the entire USB PHY block, both the USB OTG 181 This driver controls the entire USB PHY block, both the USB OTG
182 parts, as well as the 2 regular USB 2 host PHYs. 182 parts, as well as the 2 regular USB 2 host PHYs.
183 183
184config PHY_SUN9I_USB
185 tristate "Allwinner sun9i SoC USB PHY driver"
186 depends on ARCH_SUNXI && HAS_IOMEM && OF
187 depends on RESET_CONTROLLER
188 select GENERIC_PHY
189 help
190 Enable this to support the transceiver that is part of Allwinner
191 sun9i SoCs.
192
193 This driver controls each individual USB 2 host PHY.
194
184config PHY_SAMSUNG_USB2 195config PHY_SAMSUNG_USB2
185 tristate "Samsung USB 2.0 PHY driver" 196 tristate "Samsung USB 2.0 PHY driver"
186 depends on HAS_IOMEM 197 depends on HAS_IOMEM
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index dab6665ad9c1..f12625178780 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -21,6 +21,7 @@ obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
21obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o 21obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
22obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o 22obj-$(CONFIG_PHY_HIX5HD2_SATA) += phy-hix5hd2-sata.o
23obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o 23obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
24obj-$(CONFIG_PHY_SUN9I_USB) += phy-sun9i-usb.o
24obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o 25obj-$(CONFIG_PHY_SAMSUNG_USB2) += phy-exynos-usb2.o
25phy-exynos-usb2-y += phy-samsung-usb2.o 26phy-exynos-usb2-y += phy-samsung-usb2.o
26phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o 27phy-exynos-usb2-$(CONFIG_PHY_EXYNOS4210_USB2) += phy-exynos4210-usb2.o
diff --git a/drivers/phy/phy-sun9i-usb.c b/drivers/phy/phy-sun9i-usb.c
new file mode 100644
index 000000000000..0095914a662c
--- /dev/null
+++ b/drivers/phy/phy-sun9i-usb.c
@@ -0,0 +1,202 @@
1/*
2 * Allwinner sun9i USB phy driver
3 *
4 * Copyright (C) 2014-2015 Chen-Yu Tsai <wens@csie.org>
5 *
6 * Based on phy-sun4i-usb.c from
7 * Hans de Goede <hdegoede@redhat.com>
8 *
9 * and code from
10 * Allwinner Technology Co., Ltd. <www.allwinnertech.com>
11 *
12 * This program is free software; you can redistribute it and/or modify
13 * it under the terms of the GNU General Public License as published by
14 * the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * This program is distributed in the hope that it will be useful,
18 * but WITHOUT ANY WARRANTY; without even the implied warranty of
19 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
20 * GNU General Public License for more details.
21 */
22
23#include <linux/clk.h>
24#include <linux/err.h>
25#include <linux/io.h>
26#include <linux/module.h>
27#include <linux/phy/phy.h>
28#include <linux/usb/of.h>
29#include <linux/platform_device.h>
30#include <linux/reset.h>
31
32#define SUNXI_AHB_INCR16_BURST_EN BIT(11)
33#define SUNXI_AHB_INCR8_BURST_EN BIT(10)
34#define SUNXI_AHB_INCR4_BURST_EN BIT(9)
35#define SUNXI_AHB_INCRX_ALIGN_EN BIT(8)
36#define SUNXI_ULPI_BYPASS_EN BIT(0)
37
38/* usb1 HSIC specific bits */
39#define SUNXI_EHCI_HS_FORCE BIT(20)
40#define SUNXI_HSIC_CONNECT_DET BIT(17)
41#define SUNXI_HSIC_CONNECT_INT BIT(16)
42#define SUNXI_HSIC BIT(1)
43
44struct sun9i_usb_phy {
45 struct phy *phy;
46 void __iomem *pmu;
47 struct reset_control *reset;
48 struct clk *clk;
49 struct clk *hsic_clk;
50 enum usb_phy_interface type;
51};
52
53static void sun9i_usb_phy_passby(struct sun9i_usb_phy *phy, int enable)
54{
55 u32 bits, reg_value;
56
57 bits = SUNXI_AHB_INCR16_BURST_EN | SUNXI_AHB_INCR8_BURST_EN |
58 SUNXI_AHB_INCR4_BURST_EN | SUNXI_AHB_INCRX_ALIGN_EN |
59 SUNXI_ULPI_BYPASS_EN;
60
61 if (phy->type == USBPHY_INTERFACE_MODE_HSIC)
62 bits |= SUNXI_HSIC | SUNXI_EHCI_HS_FORCE |
63 SUNXI_HSIC_CONNECT_DET | SUNXI_HSIC_CONNECT_INT;
64
65 reg_value = readl(phy->pmu);
66
67 if (enable)
68 reg_value |= bits;
69 else
70 reg_value &= ~bits;
71
72 writel(reg_value, phy->pmu);
73}
74
75static int sun9i_usb_phy_init(struct phy *_phy)
76{
77 struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
78 int ret;
79
80 ret = clk_prepare_enable(phy->clk);
81 if (ret)
82 goto err_clk;
83
84 ret = clk_prepare_enable(phy->hsic_clk);
85 if (ret)
86 goto err_hsic_clk;
87
88 ret = reset_control_deassert(phy->reset);
89 if (ret)
90 goto err_reset;
91
92 sun9i_usb_phy_passby(phy, 1);
93 return 0;
94
95err_reset:
96 clk_disable_unprepare(phy->hsic_clk);
97
98err_hsic_clk:
99 clk_disable_unprepare(phy->clk);
100
101err_clk:
102 return ret;
103}
104
105static int sun9i_usb_phy_exit(struct phy *_phy)
106{
107 struct sun9i_usb_phy *phy = phy_get_drvdata(_phy);
108
109 sun9i_usb_phy_passby(phy, 0);
110 reset_control_assert(phy->reset);
111 clk_disable_unprepare(phy->hsic_clk);
112 clk_disable_unprepare(phy->clk);
113
114 return 0;
115}
116
117static struct phy_ops sun9i_usb_phy_ops = {
118 .init = sun9i_usb_phy_init,
119 .exit = sun9i_usb_phy_exit,
120 .owner = THIS_MODULE,
121};
122
123static int sun9i_usb_phy_probe(struct platform_device *pdev)
124{
125 struct sun9i_usb_phy *phy;
126 struct device *dev = &pdev->dev;
127 struct device_node *np = dev->of_node;
128 struct phy_provider *phy_provider;
129 struct resource *res;
130
131 phy = devm_kzalloc(dev, sizeof(*phy), GFP_KERNEL);
132 if (!phy)
133 return -ENOMEM;
134
135 phy->type = of_usb_get_phy_mode(np);
136 if (phy->type == USBPHY_INTERFACE_MODE_HSIC) {
137 phy->clk = devm_clk_get(dev, "hsic_480M");
138 if (IS_ERR(phy->clk)) {
139 dev_err(dev, "failed to get hsic_480M clock\n");
140 return PTR_ERR(phy->clk);
141 }
142
143 phy->hsic_clk = devm_clk_get(dev, "hsic_12M");
144 if (IS_ERR(phy->clk)) {
145 dev_err(dev, "failed to get hsic_12M clock\n");
146 return PTR_ERR(phy->clk);
147 }
148
149 phy->reset = devm_reset_control_get(dev, "hsic");
150 if (IS_ERR(phy->reset)) {
151 dev_err(dev, "failed to get reset control\n");
152 return PTR_ERR(phy->reset);
153 }
154 } else {
155 phy->clk = devm_clk_get(dev, "phy");
156 if (IS_ERR(phy->clk)) {
157 dev_err(dev, "failed to get phy clock\n");
158 return PTR_ERR(phy->clk);
159 }
160
161 phy->reset = devm_reset_control_get(dev, "phy");
162 if (IS_ERR(phy->reset)) {
163 dev_err(dev, "failed to get reset control\n");
164 return PTR_ERR(phy->reset);
165 }
166 }
167
168 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
169 phy->pmu = devm_ioremap_resource(dev, res);
170 if (IS_ERR(phy->pmu))
171 return PTR_ERR(phy->pmu);
172
173 phy->phy = devm_phy_create(dev, NULL, &sun9i_usb_phy_ops);
174 if (IS_ERR(phy->phy)) {
175 dev_err(dev, "failed to create PHY\n");
176 return PTR_ERR(phy->phy);
177 }
178
179 phy_set_drvdata(phy->phy, phy);
180 phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate);
181
182 return PTR_ERR_OR_ZERO(phy_provider);
183}
184
185static const struct of_device_id sun9i_usb_phy_of_match[] = {
186 { .compatible = "allwinner,sun9i-a80-usb-phy" },
187 { },
188};
189MODULE_DEVICE_TABLE(of, sun9i_usb_phy_of_match);
190
191static struct platform_driver sun9i_usb_phy_driver = {
192 .probe = sun9i_usb_phy_probe,
193 .driver = {
194 .of_match_table = sun9i_usb_phy_of_match,
195 .name = "sun9i-usb-phy",
196 }
197};
198module_platform_driver(sun9i_usb_phy_driver);
199
200MODULE_DESCRIPTION("Allwinner sun9i USB phy driver");
201MODULE_AUTHOR("Chen-Yu Tsai <wens@csie.org>");
202MODULE_LICENSE("GPL");