summaryrefslogtreecommitdiffstats
path: root/drivers/phy
diff options
context:
space:
mode:
authorKishon Vijay Abraham I <kishon@ti.com>2014-03-03 06:38:12 -0500
committerKishon Vijay Abraham I <kishon@ti.com>2014-03-06 04:19:33 -0500
commita70143bbef6bf06050c32a26d99e917b3e82deb7 (patch)
tree7d84ccb6eddc7768cb688600d7069193a54b9908 /drivers/phy
parent06c886a95cbb533fb3c4178ec0362460b17926ed (diff)
drivers: phy: usb3/pipe3: Adapt pipe3 driver to Generic PHY Framework
Adapted omap-usb3 PHY driver to Generic PHY Framework and moved phy-omap-usb3 driver in drivers/usb/phy to drivers/phy and also renamed the file to phy-ti-pipe3 since this same driver will be used for SATA PHY and PCIE PHY. Signed-off-by: Kishon Vijay Abraham I <kishon@ti.com>
Diffstat (limited to 'drivers/phy')
-rw-r--r--drivers/phy/Kconfig11
-rw-r--r--drivers/phy/Makefile1
-rw-r--r--drivers/phy/phy-ti-pipe3.c413
3 files changed, 425 insertions, 0 deletions
diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig
index dc1756cf5d75..fde4192cc322 100644
--- a/drivers/phy/Kconfig
+++ b/drivers/phy/Kconfig
@@ -43,6 +43,17 @@ config OMAP_USB2
43 The USB OTG controller communicates with the comparator using this 43 The USB OTG controller communicates with the comparator using this
44 driver. 44 driver.
45 45
46config TI_PIPE3
47 tristate "TI PIPE3 PHY Driver"
48 depends on ARCH_OMAP2PLUS || COMPILE_TEST
49 select GENERIC_PHY
50 select OMAP_CONTROL_USB
51 help
52 Enable this to support the PIPE3 PHY that is part of TI SOCs. This
53 driver takes care of all the PHY functionality apart from comparator.
54 This driver interacts with the "OMAP Control PHY Driver" to power
55 on/off the PHY.
56
46config TWL4030_USB 57config TWL4030_USB
47 tristate "TWL4030 USB Transceiver Driver" 58 tristate "TWL4030 USB Transceiver Driver"
48 depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS 59 depends on TWL4030_CORE && REGULATOR_TWL4030 && USB_MUSB_OMAP2PLUS
diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile
index 5d0b59edaf88..977a50ead5a4 100644
--- a/drivers/phy/Makefile
+++ b/drivers/phy/Makefile
@@ -8,6 +8,7 @@ obj-$(CONFIG_PHY_EXYNOS_DP_VIDEO) += phy-exynos-dp-video.o
8obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o 8obj-$(CONFIG_PHY_EXYNOS_MIPI_VIDEO) += phy-exynos-mipi-video.o
9obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o 9obj-$(CONFIG_PHY_MVEBU_SATA) += phy-mvebu-sata.o
10obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o 10obj-$(CONFIG_OMAP_USB2) += phy-omap-usb2.o
11obj-$(CONFIG_TI_PIPE3) += phy-ti-pipe3.o
11obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o 12obj-$(CONFIG_TWL4030_USB) += phy-twl4030-usb.o
12obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o 13obj-$(CONFIG_PHY_EXYNOS5250_SATA) += phy-exynos5250-sata.o
13obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o 14obj-$(CONFIG_PHY_SUN4I_USB) += phy-sun4i-usb.o
diff --git a/drivers/phy/phy-ti-pipe3.c b/drivers/phy/phy-ti-pipe3.c
new file mode 100644
index 000000000000..c8d16746a5e2
--- /dev/null
+++ b/drivers/phy/phy-ti-pipe3.c
@@ -0,0 +1,413 @@
1/*
2 * phy-ti-pipe3 - PIPE3 PHY driver.
3 *
4 * Copyright (C) 2013 Texas Instruments Incorporated - http://www.ti.com
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * Author: Kishon Vijay Abraham I <kishon@ti.com>
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 */
18
19#include <linux/module.h>
20#include <linux/platform_device.h>
21#include <linux/slab.h>
22#include <linux/phy/phy.h>
23#include <linux/of.h>
24#include <linux/clk.h>
25#include <linux/err.h>
26#include <linux/io.h>
27#include <linux/pm_runtime.h>
28#include <linux/delay.h>
29#include <linux/usb/omap_control_usb.h>
30#include <linux/of_platform.h>
31
32#define PLL_STATUS 0x00000004
33#define PLL_GO 0x00000008
34#define PLL_CONFIGURATION1 0x0000000C
35#define PLL_CONFIGURATION2 0x00000010
36#define PLL_CONFIGURATION3 0x00000014
37#define PLL_CONFIGURATION4 0x00000020
38
39#define PLL_REGM_MASK 0x001FFE00
40#define PLL_REGM_SHIFT 0x9
41#define PLL_REGM_F_MASK 0x0003FFFF
42#define PLL_REGM_F_SHIFT 0x0
43#define PLL_REGN_MASK 0x000001FE
44#define PLL_REGN_SHIFT 0x1
45#define PLL_SELFREQDCO_MASK 0x0000000E
46#define PLL_SELFREQDCO_SHIFT 0x1
47#define PLL_SD_MASK 0x0003FC00
48#define PLL_SD_SHIFT 0x9
49#define SET_PLL_GO 0x1
50#define PLL_TICOPWDN 0x10000
51#define PLL_LOCK 0x2
52#define PLL_IDLE 0x1
53
54/*
55 * This is an Empirical value that works, need to confirm the actual
56 * value required for the PIPE3PHY_PLL_CONFIGURATION2.PLL_IDLE status
57 * to be correctly reflected in the PIPE3PHY_PLL_STATUS register.
58 */
59# define PLL_IDLE_TIME 100;
60
61struct pipe3_dpll_params {
62 u16 m;
63 u8 n;
64 u8 freq:3;
65 u8 sd;
66 u32 mf;
67};
68
69struct ti_pipe3 {
70 void __iomem *pll_ctrl_base;
71 struct device *dev;
72 struct device *control_dev;
73 struct clk *wkupclk;
74 struct clk *sys_clk;
75 struct clk *optclk;
76};
77
78struct pipe3_dpll_map {
79 unsigned long rate;
80 struct pipe3_dpll_params params;
81};
82
83static struct pipe3_dpll_map dpll_map[] = {
84 {12000000, {1250, 5, 4, 20, 0} }, /* 12 MHz */
85 {16800000, {3125, 20, 4, 20, 0} }, /* 16.8 MHz */
86 {19200000, {1172, 8, 4, 20, 65537} }, /* 19.2 MHz */
87 {20000000, {1000, 7, 4, 10, 0} }, /* 20 MHz */
88 {26000000, {1250, 12, 4, 20, 0} }, /* 26 MHz */
89 {38400000, {3125, 47, 4, 20, 92843} }, /* 38.4 MHz */
90};
91
92static inline u32 ti_pipe3_readl(void __iomem *addr, unsigned offset)
93{
94 return __raw_readl(addr + offset);
95}
96
97static inline void ti_pipe3_writel(void __iomem *addr, unsigned offset,
98 u32 data)
99{
100 __raw_writel(data, addr + offset);
101}
102
103static struct pipe3_dpll_params *ti_pipe3_get_dpll_params(unsigned long rate)
104{
105 int i;
106
107 for (i = 0; i < ARRAY_SIZE(dpll_map); i++) {
108 if (rate == dpll_map[i].rate)
109 return &dpll_map[i].params;
110 }
111
112 return NULL;
113}
114
115static int ti_pipe3_power_off(struct phy *x)
116{
117 struct ti_pipe3 *phy = phy_get_drvdata(x);
118 int val;
119 int timeout = PLL_IDLE_TIME;
120
121 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
122 val |= PLL_IDLE;
123 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
124
125 do {
126 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
127 if (val & PLL_TICOPWDN)
128 break;
129 udelay(5);
130 } while (--timeout);
131
132 if (!timeout) {
133 dev_err(phy->dev, "power off failed\n");
134 return -EBUSY;
135 }
136
137 omap_control_usb_phy_power(phy->control_dev, 0);
138
139 return 0;
140}
141
142static int ti_pipe3_power_on(struct phy *x)
143{
144 struct ti_pipe3 *phy = phy_get_drvdata(x);
145 int val;
146 int timeout = PLL_IDLE_TIME;
147
148 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
149 val &= ~PLL_IDLE;
150 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
151
152 do {
153 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
154 if (!(val & PLL_TICOPWDN))
155 break;
156 udelay(5);
157 } while (--timeout);
158
159 if (!timeout) {
160 dev_err(phy->dev, "power on failed\n");
161 return -EBUSY;
162 }
163
164 return 0;
165}
166
167static void ti_pipe3_dpll_relock(struct ti_pipe3 *phy)
168{
169 u32 val;
170 unsigned long timeout;
171
172 ti_pipe3_writel(phy->pll_ctrl_base, PLL_GO, SET_PLL_GO);
173
174 timeout = jiffies + msecs_to_jiffies(20);
175 do {
176 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_STATUS);
177 if (val & PLL_LOCK)
178 break;
179 } while (!WARN_ON(time_after(jiffies, timeout)));
180}
181
182static int ti_pipe3_dpll_lock(struct ti_pipe3 *phy)
183{
184 u32 val;
185 unsigned long rate;
186 struct pipe3_dpll_params *dpll_params;
187
188 rate = clk_get_rate(phy->sys_clk);
189 dpll_params = ti_pipe3_get_dpll_params(rate);
190 if (!dpll_params) {
191 dev_err(phy->dev,
192 "No DPLL configuration for %lu Hz SYS CLK\n", rate);
193 return -EINVAL;
194 }
195
196 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
197 val &= ~PLL_REGN_MASK;
198 val |= dpll_params->n << PLL_REGN_SHIFT;
199 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
200
201 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION2);
202 val &= ~PLL_SELFREQDCO_MASK;
203 val |= dpll_params->freq << PLL_SELFREQDCO_SHIFT;
204 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION2, val);
205
206 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION1);
207 val &= ~PLL_REGM_MASK;
208 val |= dpll_params->m << PLL_REGM_SHIFT;
209 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION1, val);
210
211 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION4);
212 val &= ~PLL_REGM_F_MASK;
213 val |= dpll_params->mf << PLL_REGM_F_SHIFT;
214 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION4, val);
215
216 val = ti_pipe3_readl(phy->pll_ctrl_base, PLL_CONFIGURATION3);
217 val &= ~PLL_SD_MASK;
218 val |= dpll_params->sd << PLL_SD_SHIFT;
219 ti_pipe3_writel(phy->pll_ctrl_base, PLL_CONFIGURATION3, val);
220
221 ti_pipe3_dpll_relock(phy);
222
223 return 0;
224}
225
226static int ti_pipe3_init(struct phy *x)
227{
228 struct ti_pipe3 *phy = phy_get_drvdata(x);
229 int ret;
230
231 ret = ti_pipe3_dpll_lock(phy);
232 if (ret)
233 return ret;
234
235 omap_control_usb_phy_power(phy->control_dev, 1);
236
237 return 0;
238}
239
240static struct phy_ops ops = {
241 .init = ti_pipe3_init,
242 .power_on = ti_pipe3_power_on,
243 .power_off = ti_pipe3_power_off,
244 .owner = THIS_MODULE,
245};
246
247static int ti_pipe3_probe(struct platform_device *pdev)
248{
249 struct ti_pipe3 *phy;
250 struct phy *generic_phy;
251 struct phy_provider *phy_provider;
252 struct resource *res;
253 struct device_node *node = pdev->dev.of_node;
254 struct device_node *control_node;
255 struct platform_device *control_pdev;
256
257 if (!node)
258 return -EINVAL;
259
260 phy = devm_kzalloc(&pdev->dev, sizeof(*phy), GFP_KERNEL);
261 if (!phy) {
262 dev_err(&pdev->dev, "unable to alloc mem for TI PIPE3 PHY\n");
263 return -ENOMEM;
264 }
265
266 res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "pll_ctrl");
267 phy->pll_ctrl_base = devm_ioremap_resource(&pdev->dev, res);
268 if (IS_ERR(phy->pll_ctrl_base))
269 return PTR_ERR(phy->pll_ctrl_base);
270
271 phy->dev = &pdev->dev;
272
273 phy->wkupclk = devm_clk_get(phy->dev, "usb_phy_cm_clk32k");
274 if (IS_ERR(phy->wkupclk)) {
275 dev_err(&pdev->dev, "unable to get usb_phy_cm_clk32k\n");
276 return PTR_ERR(phy->wkupclk);
277 }
278 clk_prepare(phy->wkupclk);
279
280 phy->optclk = devm_clk_get(phy->dev, "usb_otg_ss_refclk960m");
281 if (IS_ERR(phy->optclk)) {
282 dev_err(&pdev->dev, "unable to get usb_otg_ss_refclk960m\n");
283 return PTR_ERR(phy->optclk);
284 }
285 clk_prepare(phy->optclk);
286
287 phy->sys_clk = devm_clk_get(phy->dev, "sys_clkin");
288 if (IS_ERR(phy->sys_clk)) {
289 pr_err("%s: unable to get sys_clkin\n", __func__);
290 return -EINVAL;
291 }
292
293 control_node = of_parse_phandle(node, "ctrl-module", 0);
294 if (!control_node) {
295 dev_err(&pdev->dev, "Failed to get control device phandle\n");
296 return -EINVAL;
297 }
298
299 control_pdev = of_find_device_by_node(control_node);
300 if (!control_pdev) {
301 dev_err(&pdev->dev, "Failed to get control device\n");
302 return -EINVAL;
303 }
304
305 phy->control_dev = &control_pdev->dev;
306
307 omap_control_usb_phy_power(phy->control_dev, 0);
308
309 platform_set_drvdata(pdev, phy);
310 pm_runtime_enable(phy->dev);
311
312 generic_phy = devm_phy_create(phy->dev, &ops, NULL);
313 if (IS_ERR(generic_phy))
314 return PTR_ERR(generic_phy);
315
316 phy_set_drvdata(generic_phy, phy);
317 phy_provider = devm_of_phy_provider_register(phy->dev,
318 of_phy_simple_xlate);
319 if (IS_ERR(phy_provider))
320 return PTR_ERR(phy_provider);
321
322 pm_runtime_get(&pdev->dev);
323
324 return 0;
325}
326
327static int ti_pipe3_remove(struct platform_device *pdev)
328{
329 struct ti_pipe3 *phy = platform_get_drvdata(pdev);
330
331 clk_unprepare(phy->wkupclk);
332 clk_unprepare(phy->optclk);
333 if (!pm_runtime_suspended(&pdev->dev))
334 pm_runtime_put(&pdev->dev);
335 pm_runtime_disable(&pdev->dev);
336
337 return 0;
338}
339
340#ifdef CONFIG_PM_RUNTIME
341
342static int ti_pipe3_runtime_suspend(struct device *dev)
343{
344 struct ti_pipe3 *phy = dev_get_drvdata(dev);
345
346 clk_disable(phy->wkupclk);
347 clk_disable(phy->optclk);
348
349 return 0;
350}
351
352static int ti_pipe3_runtime_resume(struct device *dev)
353{
354 u32 ret = 0;
355 struct ti_pipe3 *phy = dev_get_drvdata(dev);
356
357 ret = clk_enable(phy->optclk);
358 if (ret) {
359 dev_err(phy->dev, "Failed to enable optclk %d\n", ret);
360 goto err1;
361 }
362
363 ret = clk_enable(phy->wkupclk);
364 if (ret) {
365 dev_err(phy->dev, "Failed to enable wkupclk %d\n", ret);
366 goto err2;
367 }
368
369 return 0;
370
371err2:
372 clk_disable(phy->optclk);
373
374err1:
375 return ret;
376}
377
378static const struct dev_pm_ops ti_pipe3_pm_ops = {
379 SET_RUNTIME_PM_OPS(ti_pipe3_runtime_suspend,
380 ti_pipe3_runtime_resume, NULL)
381};
382
383#define DEV_PM_OPS (&ti_pipe3_pm_ops)
384#else
385#define DEV_PM_OPS NULL
386#endif
387
388#ifdef CONFIG_OF
389static const struct of_device_id ti_pipe3_id_table[] = {
390 { .compatible = "ti,phy-usb3" },
391 { .compatible = "ti,omap-usb3" },
392 {}
393};
394MODULE_DEVICE_TABLE(of, ti_pipe3_id_table);
395#endif
396
397static struct platform_driver ti_pipe3_driver = {
398 .probe = ti_pipe3_probe,
399 .remove = ti_pipe3_remove,
400 .driver = {
401 .name = "ti-pipe3",
402 .owner = THIS_MODULE,
403 .pm = DEV_PM_OPS,
404 .of_match_table = of_match_ptr(ti_pipe3_id_table),
405 },
406};
407
408module_platform_driver(ti_pipe3_driver);
409
410MODULE_ALIAS("platform: ti_pipe3");
411MODULE_AUTHOR("Texas Instruments Inc.");
412MODULE_DESCRIPTION("TI PIPE3 phy driver");
413MODULE_LICENSE("GPL v2");