blob: 29200ebdd3570587186655ed34c47839ced7a775 [file] [log] [blame]
Richard Zhaod142d6b2012-09-12 14:58:05 +03001/*
2 * Copyright 2012 Freescale Semiconductor, Inc.
3 *
4 * The code contained herein is licensed under the GNU General Public
5 * License. You may obtain a copy of the GNU General Public License
6 * Version 2 or later at the following locations:
7 *
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
10 */
11
12#include <linux/module.h>
13#include <linux/of_platform.h>
14#include <linux/clk.h>
15#include <linux/err.h>
16#include <linux/io.h>
Michael Grzeschika0685332013-03-30 12:54:01 +020017#include <linux/delay.h>
Richard Zhaod142d6b2012-09-12 14:58:05 +030018
Alexander Shishkin8e229782013-06-24 14:46:36 +030019#include "ci_hdrc_imx.h"
Richard Zhaod142d6b2012-09-12 14:58:05 +030020
Michael Grzeschika0685332013-03-30 12:54:01 +020021#define MX25_USB_PHY_CTRL_OFFSET 0x08
22#define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
23
Denis Carikli72ee92d2014-04-23 15:56:36 +080024#define MX25_EHCI_INTERFACE_SINGLE_UNI (2 << 0)
25#define MX25_EHCI_INTERFACE_DIFF_UNI (0 << 0)
26#define MX25_EHCI_INTERFACE_MASK (0xf)
27
28#define MX25_OTG_SIC_SHIFT 29
29#define MX25_OTG_SIC_MASK (0x3 << MX25_OTG_SIC_SHIFT)
30#define MX25_OTG_PM_BIT BIT(24)
31#define MX25_OTG_PP_BIT BIT(11)
32#define MX25_OTG_OCPOL_BIT BIT(3)
33
34#define MX25_H1_SIC_SHIFT 21
35#define MX25_H1_SIC_MASK (0x3 << MX25_H1_SIC_SHIFT)
36#define MX25_H1_PP_BIT BIT(18)
37#define MX25_H1_PM_BIT BIT(16)
38#define MX25_H1_IPPUE_UP_BIT BIT(7)
39#define MX25_H1_IPPUE_DOWN_BIT BIT(6)
40#define MX25_H1_TLL_BIT BIT(5)
41#define MX25_H1_USBTE_BIT BIT(4)
42#define MX25_H1_OCPOL_BIT BIT(2)
43
Alexander Shiyan9f90e112013-12-06 16:35:14 +080044#define MX27_H1_PM_BIT BIT(8)
45#define MX27_H2_PM_BIT BIT(16)
46#define MX27_OTG_PM_BIT BIT(24)
47
Michael Grzeschikf0c910b2013-03-30 12:54:00 +020048#define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
Fabio Estevam33f92a82014-05-04 09:24:39 +080049#define MX53_USB_OTG_PHY_CTRL_1_OFFSET 0x0c
Michael Grzeschikf0c910b2013-03-30 12:54:00 +020050#define MX53_USB_UH2_CTRL_OFFSET 0x14
51#define MX53_USB_UH3_CTRL_OFFSET 0x18
52#define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
53#define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
54#define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
Fabio Estevam33f92a82014-05-04 09:24:39 +080055#define MX53_USB_PHYCTRL1_PLLDIV_MASK 0x3
56#define MX53_USB_PLL_DIV_24_MHZ 0x01
Michael Grzeschikf0c910b2013-03-30 12:54:00 +020057
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020058#define MX6_BM_OVER_CUR_DIS BIT(7)
Richard Zhaod142d6b2012-09-12 14:58:05 +030059
Stefan Agnerf40017e2014-09-22 08:14:15 +080060#define VF610_OVER_CUR_DIS BIT(7)
61
Sascha Hauer05986ba2013-08-14 12:44:16 +030062struct usbmisc_ops {
63 /* It's called once when probe a usb device */
64 int (*init)(struct imx_usbmisc_data *data);
65 /* It's called once after adding a usb device */
66 int (*post)(struct imx_usbmisc_data *data);
67};
68
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +020069struct imx_usbmisc {
Richard Zhaod142d6b2012-09-12 14:58:05 +030070 void __iomem *base;
71 spinlock_t lock;
72 struct clk *clk;
Marc Kleine-Buddee6091082013-03-30 12:53:59 +020073 const struct usbmisc_ops *ops;
Richard Zhaod142d6b2012-09-12 14:58:05 +030074};
75
Denis Carikli72ee92d2014-04-23 15:56:36 +080076static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
77{
Stefan Agnerf40017e2014-09-22 08:14:15 +080078 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
Denis Carikli72ee92d2014-04-23 15:56:36 +080079 unsigned long flags;
80 u32 val = 0;
81
82 if (data->index > 1)
83 return -EINVAL;
84
85 spin_lock_irqsave(&usbmisc->lock, flags);
86 switch (data->index) {
87 case 0:
88 val = readl(usbmisc->base);
89 val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
90 val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
91 val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
92 writel(val, usbmisc->base);
93 break;
94 case 1:
95 val = readl(usbmisc->base);
96 val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT | MX25_H1_IPPUE_UP_BIT);
97 val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
98 val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
99 MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
100
101 writel(val, usbmisc->base);
102
103 break;
104 }
105 spin_unlock_irqrestore(&usbmisc->lock, flags);
106
107 return 0;
108}
109
Sascha Hauer05986ba2013-08-14 12:44:16 +0300110static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300111{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800112 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
Michael Grzeschika0685332013-03-30 12:54:01 +0200113 void __iomem *reg;
114 unsigned long flags;
115 u32 val;
116
Sascha Hauer05986ba2013-08-14 12:44:16 +0300117 if (data->index > 2)
118 return -EINVAL;
Michael Grzeschika0685332013-03-30 12:54:01 +0200119
Sascha Hauer05986ba2013-08-14 12:44:16 +0300120 if (data->evdo) {
Michael Grzeschika0685332013-03-30 12:54:01 +0200121 spin_lock_irqsave(&usbmisc->lock, flags);
Fabio Estevam8d1dc4d2014-11-26 13:44:25 +0800122 reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
Michael Grzeschika0685332013-03-30 12:54:01 +0200123 val = readl(reg);
124 writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
125 spin_unlock_irqrestore(&usbmisc->lock, flags);
126 usleep_range(5000, 10000); /* needed to stabilize voltage */
127 }
128
129 return 0;
130}
131
Alexander Shiyan9f90e112013-12-06 16:35:14 +0800132static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
133{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800134 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
Alexander Shiyan9f90e112013-12-06 16:35:14 +0800135 unsigned long flags;
136 u32 val;
137
138 switch (data->index) {
139 case 0:
140 val = MX27_OTG_PM_BIT;
141 break;
142 case 1:
143 val = MX27_H1_PM_BIT;
144 break;
145 case 2:
146 val = MX27_H2_PM_BIT;
147 break;
148 default:
149 return -EINVAL;
150 };
151
152 spin_lock_irqsave(&usbmisc->lock, flags);
153 if (data->disable_oc)
154 val = readl(usbmisc->base) | val;
155 else
156 val = readl(usbmisc->base) & ~val;
157 writel(val, usbmisc->base);
158 spin_unlock_irqrestore(&usbmisc->lock, flags);
159
160 return 0;
161}
162
Sascha Hauer05986ba2013-08-14 12:44:16 +0300163static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200164{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800165 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200166 void __iomem *reg = NULL;
167 unsigned long flags;
168 u32 val = 0;
169
Sascha Hauer05986ba2013-08-14 12:44:16 +0300170 if (data->index > 3)
171 return -EINVAL;
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200172
Fabio Estevam33f92a82014-05-04 09:24:39 +0800173 /* Select a 24 MHz reference clock for the PHY */
174 reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET;
175 val = readl(reg);
176 val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
177 val |= MX53_USB_PLL_DIV_24_MHZ;
178 writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
179
Sascha Hauer05986ba2013-08-14 12:44:16 +0300180 if (data->disable_oc) {
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200181 spin_lock_irqsave(&usbmisc->lock, flags);
Sascha Hauer05986ba2013-08-14 12:44:16 +0300182 switch (data->index) {
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200183 case 0:
184 reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
185 val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
186 break;
187 case 1:
188 reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
189 val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
190 break;
191 case 2:
192 reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
193 val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
194 break;
195 case 3:
196 reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
197 val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
198 break;
199 }
200 if (reg && val)
201 writel(val, reg);
202 spin_unlock_irqrestore(&usbmisc->lock, flags);
203 }
204
205 return 0;
206}
207
Sascha Hauer05986ba2013-08-14 12:44:16 +0300208static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300209{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800210 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300211 unsigned long flags;
212 u32 reg;
213
Sascha Hauer05986ba2013-08-14 12:44:16 +0300214 if (data->index > 3)
215 return -EINVAL;
Richard Zhaod142d6b2012-09-12 14:58:05 +0300216
Sascha Hauer05986ba2013-08-14 12:44:16 +0300217 if (data->disable_oc) {
Richard Zhaod142d6b2012-09-12 14:58:05 +0300218 spin_lock_irqsave(&usbmisc->lock, flags);
Sascha Hauer05986ba2013-08-14 12:44:16 +0300219 reg = readl(usbmisc->base + data->index * 4);
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200220 writel(reg | MX6_BM_OVER_CUR_DIS,
Sascha Hauer05986ba2013-08-14 12:44:16 +0300221 usbmisc->base + data->index * 4);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300222 spin_unlock_irqrestore(&usbmisc->lock, flags);
223 }
224
225 return 0;
226}
227
Stefan Agnerf40017e2014-09-22 08:14:15 +0800228static int usbmisc_vf610_init(struct imx_usbmisc_data *data)
229{
230 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
231 u32 reg;
232
233 /*
234 * Vybrid only has one misc register set, but in two different
235 * areas. These is reflected in two instances of this driver.
236 */
237 if (data->index >= 1)
238 return -EINVAL;
239
240 if (data->disable_oc) {
241 reg = readl(usbmisc->base);
242 writel(reg | VF610_OVER_CUR_DIS, usbmisc->base);
243 }
244
245 return 0;
246}
247
Michael Grzeschika0685332013-03-30 12:54:01 +0200248static const struct usbmisc_ops imx25_usbmisc_ops = {
Denis Carikli72ee92d2014-04-23 15:56:36 +0800249 .init = usbmisc_imx25_init,
Michael Grzeschika0685332013-03-30 12:54:01 +0200250 .post = usbmisc_imx25_post,
251};
252
Alexander Shiyan9f90e112013-12-06 16:35:14 +0800253static const struct usbmisc_ops imx27_usbmisc_ops = {
254 .init = usbmisc_imx27_init,
255};
256
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200257static const struct usbmisc_ops imx53_usbmisc_ops = {
258 .init = usbmisc_imx53_init,
259};
260
Richard Zhaod142d6b2012-09-12 14:58:05 +0300261static const struct usbmisc_ops imx6q_usbmisc_ops = {
262 .init = usbmisc_imx6q_init,
263};
264
Stefan Agnerf40017e2014-09-22 08:14:15 +0800265static const struct usbmisc_ops vf610_usbmisc_ops = {
266 .init = usbmisc_vf610_init,
267};
268
Sascha Hauer05986ba2013-08-14 12:44:16 +0300269int imx_usbmisc_init(struct imx_usbmisc_data *data)
270{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800271 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
272
Sascha Hauer05986ba2013-08-14 12:44:16 +0300273 if (!usbmisc->ops->init)
274 return 0;
275 return usbmisc->ops->init(data);
276}
277EXPORT_SYMBOL_GPL(imx_usbmisc_init);
278
279int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
280{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800281 struct imx_usbmisc *usbmisc = dev_get_drvdata(data->dev);
282
Sascha Hauer05986ba2013-08-14 12:44:16 +0300283 if (!usbmisc->ops->post)
284 return 0;
285 return usbmisc->ops->post(data);
286}
287EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
288
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200289static const struct of_device_id usbmisc_imx_dt_ids[] = {
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200290 {
Michael Grzeschika0685332013-03-30 12:54:01 +0200291 .compatible = "fsl,imx25-usbmisc",
292 .data = &imx25_usbmisc_ops,
293 },
294 {
Denis Carikli72ee92d2014-04-23 15:56:36 +0800295 .compatible = "fsl,imx35-usbmisc",
296 .data = &imx25_usbmisc_ops,
297 },
298 {
Alexander Shiyan9f90e112013-12-06 16:35:14 +0800299 .compatible = "fsl,imx27-usbmisc",
300 .data = &imx27_usbmisc_ops,
301 },
302 {
Alexander Shiyanc4962e02013-12-06 16:35:15 +0800303 .compatible = "fsl,imx51-usbmisc",
304 .data = &imx53_usbmisc_ops,
305 },
306 {
Michael Grzeschikf0c910b2013-03-30 12:54:00 +0200307 .compatible = "fsl,imx53-usbmisc",
308 .data = &imx53_usbmisc_ops,
309 },
310 {
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200311 .compatible = "fsl,imx6q-usbmisc",
312 .data = &imx6q_usbmisc_ops,
313 },
Stefan Agnerf40017e2014-09-22 08:14:15 +0800314 {
315 .compatible = "fsl,vf610-usbmisc",
316 .data = &vf610_usbmisc_ops,
317 },
Richard Zhaod142d6b2012-09-12 14:58:05 +0300318 { /* sentinel */ }
319};
Arnaud Patard (Rtp)269b83d2013-06-20 23:33:25 +0200320MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300321
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200322static int usbmisc_imx_probe(struct platform_device *pdev)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300323{
324 struct resource *res;
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200325 struct imx_usbmisc *data;
Richard Zhaod142d6b2012-09-12 14:58:05 +0300326 int ret;
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200327 struct of_device_id *tmp_dev;
Richard Zhaod142d6b2012-09-12 14:58:05 +0300328
Richard Zhaod142d6b2012-09-12 14:58:05 +0300329 data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
330 if (!data)
331 return -ENOMEM;
332
333 spin_lock_init(&data->lock);
334
335 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
Thierry Reding148e1132013-01-21 11:09:22 +0100336 data->base = devm_ioremap_resource(&pdev->dev, res);
337 if (IS_ERR(data->base))
338 return PTR_ERR(data->base);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300339
340 data->clk = devm_clk_get(&pdev->dev, NULL);
341 if (IS_ERR(data->clk)) {
342 dev_err(&pdev->dev,
343 "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
344 return PTR_ERR(data->clk);
345 }
346
347 ret = clk_prepare_enable(data->clk);
348 if (ret) {
349 dev_err(&pdev->dev,
350 "clk_prepare_enable failed, err=%d\n", ret);
351 return ret;
352 }
353
Marc Kleine-Buddee6091082013-03-30 12:53:59 +0200354 tmp_dev = (struct of_device_id *)
355 of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
356 data->ops = (const struct usbmisc_ops *)tmp_dev->data;
Stefan Agnerf40017e2014-09-22 08:14:15 +0800357 platform_set_drvdata(pdev, data);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300358
Richard Zhaod142d6b2012-09-12 14:58:05 +0300359 return 0;
360}
361
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200362static int usbmisc_imx_remove(struct platform_device *pdev)
Richard Zhaod142d6b2012-09-12 14:58:05 +0300363{
Stefan Agnerf40017e2014-09-22 08:14:15 +0800364 struct imx_usbmisc *usbmisc = dev_get_drvdata(&pdev->dev);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300365 clk_disable_unprepare(usbmisc->clk);
366 return 0;
367}
368
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200369static struct platform_driver usbmisc_imx_driver = {
370 .probe = usbmisc_imx_probe,
371 .remove = usbmisc_imx_remove,
Richard Zhaod142d6b2012-09-12 14:58:05 +0300372 .driver = {
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200373 .name = "usbmisc_imx",
Richard Zhaod142d6b2012-09-12 14:58:05 +0300374 .owner = THIS_MODULE,
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200375 .of_match_table = usbmisc_imx_dt_ids,
Richard Zhaod142d6b2012-09-12 14:58:05 +0300376 },
377};
378
Philipp Zabel0404ae02013-06-13 17:59:59 +0300379module_platform_driver(usbmisc_imx_driver);
Richard Zhaod142d6b2012-09-12 14:58:05 +0300380
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200381MODULE_ALIAS("platform:usbmisc-imx");
Richard Zhaod142d6b2012-09-12 14:58:05 +0300382MODULE_LICENSE("GPL v2");
Michael Grzeschika7bc2fd2013-03-30 12:53:56 +0200383MODULE_DESCRIPTION("driver for imx usb non-core registers");
Richard Zhaod142d6b2012-09-12 14:58:05 +0300384MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");