aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorSergei Shtylyov <sergei.shtylyov@cogentembedded.com>2014-07-22 15:27:14 -0400
committerKishon Vijay Abraham I <kishon@ti.com>2014-09-24 05:48:31 -0400
commit1233f59f745b237d85f12aa9cf12ffab469f322d (patch)
tree933473b42ec28e4ffcd9ea598b384aca6056d5a1 /drivers/phy
parent452b6361c4d9baf6940adb7b1316e0f386c39799 (diff)
phy: Renesas R-Car Gen2 PHY driver
This PHY, though formally being a part of Renesas USBHS controller, contains the UGCTRL2 register that controls multiplexing of the USB ports (Renesas calls them channels) to the different USB controllers: channel 0 can be connected to either PCI EHCI/OHCI or USBHS controllers, channel 2 can be connected to PCI EHCI/OHCI or xHCI controllers. This is a new driver for this USB PHY currently already supported under drivers/ usb/phy/. The reason for writing the new driver was the requirement that the multiplexing of USB channels to the controller be dynamic, depending on what USB drivers are loaded, rather than static as provided by the old driver. The infrastructure provided by drivers/phy/phy-core.c seems to fit that purpose ideally. The new driver only supports device tree probing for now. Signed-off-by: Sergei Shtylyov <sergei.shtylyov@cogentembedded.com> Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig7
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-rcar-gen2.c341
3 files changed, 349 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index f833aa271a2e..1802fa767aa5 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -48,6 +48,13 @@ config PHY_MIPHY365X
48 Enable this to support the miphy transceiver (for SATA/PCIE) 48 Enable this to support the miphy transceiver (for SATA/PCIE)
49 that is part of STMicroelectronics STiH41x SoC series. 49 that is part of STMicroelectronics STiH41x SoC series.
50 50
51config PHY_RCAR_GEN2
52 tristate "Renesas R-Car generation 2 USB PHY driver"
53 depends on ARCH_SHMOBILE
54 depends on GENERIC_PHY
55 help
56 Support for USB PHY found on Renesas R-Car generation 2 SoCs.
57
51config OMAP_CONTROL_PHY 58config OMAP_CONTROL_PHY
52 tristate "OMAP CONTROL PHY Driver" 59 tristate "OMAP CONTROL PHY Driver"
53 depends on ARCH_OMAP2PLUS || COMPILE_TEST 60 depends on ARCH_OMAP2PLUS || COMPILE_TEST
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 95c69ed5ed45..8ac9df5bd069 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -9,6 +9,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
9obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o 9obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
10obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o 10obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
11obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o 11obj-$(CONFIG_PHY_MIPHY365X) += phy-miphy365x.o
12obj-$(CONFIG_PHY_RCAR_GEN2) += phy-rcar-gen2.o
12obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o 13obj-$(CONFIG_OMAP_CONTROL_PHY) += phy-omap-control.o
13obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o 14obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
14obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o 15obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
diff --git a/drivers/phy/phy-rcar-gen2.c b/drivers/phy/phy-rcar-gen2.c
new file mode 100644
index 000000000000..2793af17799f
--- /dev/null
+++ b/drivers/phy/phy-rcar-gen2.c
@@ -0,0 +1,341 @@
1/*
2 * Renesas R-Car Gen2 PHY driver
3 *
4 * Copyright (C) 2014 Renesas Solutions Corp.
5 * Copyright (C) 2014 Cogent Embedded, 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 version 2 as
9 * published by the Free Software Foundation.
10 */
11
12#include <linux/clk.h>
13#include <linux/delay.h>
14#include <linux/io.h>
15#include <linux/module.h>
16#include <linux/of.h>
17#include <linux/phy/phy.h>
18#include <linux/platform_device.h>
19#include <linux/spinlock.h>
20
21#include <asm/cmpxchg.h>
22
23#define USBHS_LPSTS 0x02
24#define USBHS_UGCTRL 0x80
25#define USBHS_UGCTRL2 0x84
26#define USBHS_UGSTS 0x88 /* The manuals have 0x90 */
27
28/* Low Power Status register (LPSTS) */
29#define USBHS_LPSTS_SUSPM 0x4000
30
31/* USB General control register (UGCTRL) */
32#define USBHS_UGCTRL_CONNECT 0x00000004
33#define USBHS_UGCTRL_PLLRESET 0x00000001
34
35/* USB General control register 2 (UGCTRL2) */
36#define USBHS_UGCTRL2_USB2SEL 0x80000000
37#define USBHS_UGCTRL2_USB2SEL_PCI 0x00000000
38#define USBHS_UGCTRL2_USB2SEL_USB30 0x80000000
39#define USBHS_UGCTRL2_USB0SEL 0x00000030
40#define USBHS_UGCTRL2_USB0SEL_PCI 0x00000010
41#define USBHS_UGCTRL2_USB0SEL_HS_USB 0x00000030
42
43/* USB General status register (UGSTS) */
44#define USBHS_UGSTS_LOCK 0x00000300 /* The manuals have 0x3 */
45
46#define PHYS_PER_CHANNEL 2
47
48struct rcar_gen2_phy {
49 struct phy *phy;
50 struct rcar_gen2_channel *channel;
51 int number;
52 u32 select_value;
53};
54
55struct rcar_gen2_channel {
56 struct device_node *of_node;
57 struct rcar_gen2_phy_driver *drv;
58 struct rcar_gen2_phy phys[PHYS_PER_CHANNEL];
59 int selected_phy;
60 u32 select_mask;
61};
62
63struct rcar_gen2_phy_driver {
64 void __iomem *base;
65 struct clk *clk;
66 spinlock_t lock;
67 int num_channels;
68 struct rcar_gen2_channel *channels;
69};
70
71static int rcar_gen2_phy_init(struct phy *p)
72{
73 struct rcar_gen2_phy *phy = phy_get_drvdata(p);
74 struct rcar_gen2_channel *channel = phy->channel;
75 struct rcar_gen2_phy_driver *drv = channel->drv;
76 unsigned long flags;
77 u32 ugctrl2;
78
79 /*
80 * Try to acquire exclusive access to PHY. The first driver calling
81 * phy_init() on a given channel wins, and all attempts to use another
82 * PHY on this channel will fail until phy_exit() is called by the first
83 * driver. Achieving this with cmpxcgh() should be SMP-safe.
84 */
85 if (cmpxchg(&channel->selected_phy, -1, phy->number) != -1)
86 return -EBUSY;
87
88 clk_prepare_enable(drv->clk);
89
90 spin_lock_irqsave(&drv->lock, flags);
91 ugctrl2 = readl(drv->base + USBHS_UGCTRL2);
92 ugctrl2 &= ~channel->select_mask;
93 ugctrl2 |= phy->select_value;
94 writel(ugctrl2, drv->base + USBHS_UGCTRL2);
95 spin_unlock_irqrestore(&drv->lock, flags);
96 return 0;
97}
98
99static int rcar_gen2_phy_exit(struct phy *p)
100{
101 struct rcar_gen2_phy *phy = phy_get_drvdata(p);
102 struct rcar_gen2_channel *channel = phy->channel;
103
104 clk_disable_unprepare(channel->drv->clk);
105
106 channel->selected_phy = -1;
107
108 return 0;
109}
110
111static int rcar_gen2_phy_power_on(struct phy *p)
112{
113 struct rcar_gen2_phy *phy = phy_get_drvdata(p);
114 struct rcar_gen2_phy_driver *drv = phy->channel->drv;
115 void __iomem *base = drv->base;
116 unsigned long flags;
117 u32 value;
118 int err = 0, i;
119
120 /* Skip if it's not USBHS */
121 if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
122 return 0;
123
124 spin_lock_irqsave(&drv->lock, flags);
125
126 /* Power on USBHS PHY */
127 value = readl(base + USBHS_UGCTRL);
128 value &= ~USBHS_UGCTRL_PLLRESET;
129 writel(value, base + USBHS_UGCTRL);
130
131 value = readw(base + USBHS_LPSTS);
132 value |= USBHS_LPSTS_SUSPM;
133 writew(value, base + USBHS_LPSTS);
134
135 for (i = 0; i < 20; i++) {
136 value = readl(base + USBHS_UGSTS);
137 if ((value & USBHS_UGSTS_LOCK) == USBHS_UGSTS_LOCK) {
138 value = readl(base + USBHS_UGCTRL);
139 value |= USBHS_UGCTRL_CONNECT;
140 writel(value, base + USBHS_UGCTRL);
141 goto out;
142 }
143 udelay(1);
144 }
145
146 /* Timed out waiting for the PLL lock */
147 err = -ETIMEDOUT;
148
149out:
150 spin_unlock_irqrestore(&drv->lock, flags);
151
152 return err;
153}
154
155static int rcar_gen2_phy_power_off(struct phy *p)
156{
157 struct rcar_gen2_phy *phy = phy_get_drvdata(p);
158 struct rcar_gen2_phy_driver *drv = phy->channel->drv;
159 void __iomem *base = drv->base;
160 unsigned long flags;
161 u32 value;
162
163 /* Skip if it's not USBHS */
164 if (phy->select_value != USBHS_UGCTRL2_USB0SEL_HS_USB)
165 return 0;
166
167 spin_lock_irqsave(&drv->lock, flags);
168
169 /* Power off USBHS PHY */
170 value = readl(base + USBHS_UGCTRL);
171 value &= ~USBHS_UGCTRL_CONNECT;
172 writel(value, base + USBHS_UGCTRL);
173
174 value = readw(base + USBHS_LPSTS);
175 value &= ~USBHS_LPSTS_SUSPM;
176 writew(value, base + USBHS_LPSTS);
177
178 value = readl(base + USBHS_UGCTRL);
179 value |= USBHS_UGCTRL_PLLRESET;
180 writel(value, base + USBHS_UGCTRL);
181
182 spin_unlock_irqrestore(&drv->lock, flags);
183
184 return 0;
185}
186
187static struct phy_ops rcar_gen2_phy_ops = {
188 .init = rcar_gen2_phy_init,
189 .exit = rcar_gen2_phy_exit,
190 .power_on = rcar_gen2_phy_power_on,
191 .power_off = rcar_gen2_phy_power_off,
192 .owner = THIS_MODULE,
193};
194
195static const struct of_device_id rcar_gen2_phy_match_table[] = {
196 { .compatible = "renesas,usb-phy-r8a7790" },
197 { .compatible = "renesas,usb-phy-r8a7791" },
198 { }
199};
200MODULE_DEVICE_TABLE(of, rcar_gen2_phy_match_table);
201
202static struct phy *rcar_gen2_phy_xlate(struct device *dev,
203 struct of_phandle_args *args)
204{
205 struct rcar_gen2_phy_driver *drv;
206 struct device_node *np = args->np;
207 int i;
208
209 if (!of_device_is_available(np)) {
210 dev_warn(dev, "Requested PHY is disabled\n");
211 return ERR_PTR(-ENODEV);
212 }
213
214 drv = dev_get_drvdata(dev);
215 if (!drv)
216 return ERR_PTR(-EINVAL);
217
218 for (i = 0; i < drv->num_channels; i++) {
219 if (np == drv->channels[i].of_node)
220 break;
221 }
222
223 if (i >= drv->num_channels || args->args[0] >= 2)
224 return ERR_PTR(-ENODEV);
225
226 return drv->channels[i].phys[args->args[0]].phy;
227}
228
229static const u32 select_mask[] = {
230 [0] = USBHS_UGCTRL2_USB0SEL,
231 [2] = USBHS_UGCTRL2_USB2SEL,
232};
233
234static const u32 select_value[][PHYS_PER_CHANNEL] = {
235 [0] = { USBHS_UGCTRL2_USB0SEL_PCI, USBHS_UGCTRL2_USB0SEL_HS_USB },
236 [2] = { USBHS_UGCTRL2_USB2SEL_PCI, USBHS_UGCTRL2_USB2SEL_USB30 },
237};
238
239static int rcar_gen2_phy_probe(struct platform_device *pdev)
240{
241 struct device *dev = &pdev->dev;
242 struct rcar_gen2_phy_driver *drv;
243 struct phy_provider *provider;
244 struct device_node *np;
245 struct resource *res;
246 void __iomem *base;
247 struct clk *clk;
248 int i = 0;
249
250 if (!dev->of_node) {
251 dev_err(dev,
252 "This driver is required to be instantiated from device tree\n");
253 return -EINVAL;
254 }
255
256 clk = devm_clk_get(dev, "usbhs");
257 if (IS_ERR(clk)) {
258 dev_err(dev, "Can't get USBHS clock\n");
259 return PTR_ERR(clk);
260 }
261
262 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
263 base = devm_ioremap_resource(dev, res);
264 if (IS_ERR(base))
265 return PTR_ERR(base);
266
267 drv = devm_kzalloc(dev, sizeof(*drv), GFP_KERNEL);
268 if (!drv)
269 return -ENOMEM;
270
271 spin_lock_init(&drv->lock);
272
273 drv->clk = clk;
274 drv->base = base;
275
276 drv->num_channels = of_get_child_count(dev->of_node);
277 drv->channels = devm_kcalloc(dev, drv->num_channels,
278 sizeof(struct rcar_gen2_channel),
279 GFP_KERNEL);
280 if (!drv->channels)
281 return -ENOMEM;
282
283 for_each_child_of_node(dev->of_node, np) {
284 struct rcar_gen2_channel *channel = drv->channels + i;
285 u32 channel_num;
286 int error, n;
287
288 channel->of_node = np;
289 channel->drv = drv;
290 channel->selected_phy = -1;
291
292 error = of_property_read_u32(np, "reg", &channel_num);
293 if (error || channel_num > 2) {
294 dev_err(dev, "Invalid \"reg\" property\n");
295 return error;
296 }
297 channel->select_mask = select_mask[channel_num];
298
299 for (n = 0; n < PHYS_PER_CHANNEL; n++) {
300 struct rcar_gen2_phy *phy = &channel->phys[n];
301
302 phy->channel = channel;
303 phy->number = n;
304 phy->select_value = select_value[channel_num][n];
305
306 phy->phy = devm_phy_create(dev, NULL,
307 &rcar_gen2_phy_ops, NULL);
308 if (IS_ERR(phy->phy)) {
309 dev_err(dev, "Failed to create PHY\n");
310 return PTR_ERR(phy->phy);
311 }
312 phy_set_drvdata(phy->phy, phy);
313 }
314
315 i++;
316 }
317
318 provider = devm_of_phy_provider_register(dev, rcar_gen2_phy_xlate);
319 if (IS_ERR(provider)) {
320 dev_err(dev, "Failed to register PHY provider\n");
321 return PTR_ERR(provider);
322 }
323
324 dev_set_drvdata(dev, drv);
325
326 return 0;
327}
328
329static struct platform_driver rcar_gen2_phy_driver = {
330 .driver = {
331 .name = "phy_rcar_gen2",
332 .of_match_table = rcar_gen2_phy_match_table,
333 },
334 .probe = rcar_gen2_phy_probe,
335};
336
337module_platform_driver(rcar_gen2_phy_driver);
338
339MODULE_LICENSE("GPL v2");
340MODULE_DESCRIPTION("Renesas R-Car Gen2 PHY");
341MODULE_AUTHOR("Sergei Shtylyov <sergei.shtylyov@cogentembedded.com>");