]> git.karo-electronics.de Git - karo-tx-linux.git/blob - drivers/usb/chipidea/usbmisc_imx.c
chipidea: usbmisc_imx: Allow USB OTG to work on mx51
[karo-tx-linux.git] / drivers / usb / chipidea / usbmisc_imx.c
1 /*
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>
17 #include <linux/delay.h>
18
19 #include "ci_hdrc_imx.h"
20
21 #define MX25_USB_PHY_CTRL_OFFSET        0x08
22 #define MX25_BM_EXTERNAL_VBUS_DIVIDER   BIT(23)
23
24 #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
44 #define MX27_H1_PM_BIT                  BIT(8)
45 #define MX27_H2_PM_BIT                  BIT(16)
46 #define MX27_OTG_PM_BIT                 BIT(24)
47
48 #define MX53_USB_OTG_PHY_CTRL_0_OFFSET  0x08
49 #define MX53_USB_OTG_PHY_CTRL_1_OFFSET  0x0c
50 #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)
55 #define MX53_USB_PHYCTRL1_PLLDIV_MASK   0x3
56 #define MX53_USB_PLL_DIV_24_MHZ         0x01
57
58 #define MX6_BM_OVER_CUR_DIS             BIT(7)
59
60 struct usbmisc_ops {
61         /* It's called once when probe a usb device */
62         int (*init)(struct imx_usbmisc_data *data);
63         /* It's called once after adding a usb device */
64         int (*post)(struct imx_usbmisc_data *data);
65 };
66
67 struct imx_usbmisc {
68         void __iomem *base;
69         spinlock_t lock;
70         struct clk *clk;
71         const struct usbmisc_ops *ops;
72 };
73
74 static struct imx_usbmisc *usbmisc;
75
76 static int usbmisc_imx25_init(struct imx_usbmisc_data *data)
77 {
78         unsigned long flags;
79         u32 val = 0;
80
81         if (data->index > 1)
82                 return -EINVAL;
83
84         spin_lock_irqsave(&usbmisc->lock, flags);
85         switch (data->index) {
86         case 0:
87                 val = readl(usbmisc->base);
88                 val &= ~(MX25_OTG_SIC_MASK | MX25_OTG_PP_BIT);
89                 val |= (MX25_EHCI_INTERFACE_DIFF_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_OTG_SIC_SHIFT;
90                 val |= (MX25_OTG_PM_BIT | MX25_OTG_OCPOL_BIT);
91                 writel(val, usbmisc->base);
92                 break;
93         case 1:
94                 val = readl(usbmisc->base);
95                 val &= ~(MX25_H1_SIC_MASK | MX25_H1_PP_BIT |  MX25_H1_IPPUE_UP_BIT);
96                 val |= (MX25_EHCI_INTERFACE_SINGLE_UNI & MX25_EHCI_INTERFACE_MASK) << MX25_H1_SIC_SHIFT;
97                 val |= (MX25_H1_PM_BIT | MX25_H1_OCPOL_BIT | MX25_H1_TLL_BIT |
98                         MX25_H1_USBTE_BIT | MX25_H1_IPPUE_DOWN_BIT);
99
100                 writel(val, usbmisc->base);
101
102                 break;
103         }
104         spin_unlock_irqrestore(&usbmisc->lock, flags);
105
106         return 0;
107 }
108
109 static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
110 {
111         void __iomem *reg;
112         unsigned long flags;
113         u32 val;
114
115         if (data->index > 2)
116                 return -EINVAL;
117
118         reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
119
120         if (data->evdo) {
121                 spin_lock_irqsave(&usbmisc->lock, flags);
122                 val = readl(reg);
123                 writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
124                 spin_unlock_irqrestore(&usbmisc->lock, flags);
125                 usleep_range(5000, 10000); /* needed to stabilize voltage */
126         }
127
128         return 0;
129 }
130
131 static int usbmisc_imx27_init(struct imx_usbmisc_data *data)
132 {
133         unsigned long flags;
134         u32 val;
135
136         switch (data->index) {
137         case 0:
138                 val = MX27_OTG_PM_BIT;
139                 break;
140         case 1:
141                 val = MX27_H1_PM_BIT;
142                 break;
143         case 2:
144                 val = MX27_H2_PM_BIT;
145                 break;
146         default:
147                 return -EINVAL;
148         };
149
150         spin_lock_irqsave(&usbmisc->lock, flags);
151         if (data->disable_oc)
152                 val = readl(usbmisc->base) | val;
153         else
154                 val = readl(usbmisc->base) & ~val;
155         writel(val, usbmisc->base);
156         spin_unlock_irqrestore(&usbmisc->lock, flags);
157
158         return 0;
159 }
160
161 static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
162 {
163         void __iomem *reg = NULL;
164         unsigned long flags;
165         u32 val = 0;
166
167         if (data->index > 3)
168                 return -EINVAL;
169
170         /* Select a 24 MHz reference clock for the PHY  */
171         reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET;
172         val = readl(reg);
173         val &= ~MX53_USB_PHYCTRL1_PLLDIV_MASK;
174         val |= MX53_USB_PLL_DIV_24_MHZ;
175         writel(val, usbmisc->base + MX53_USB_OTG_PHY_CTRL_1_OFFSET);
176
177         if (data->disable_oc) {
178                 spin_lock_irqsave(&usbmisc->lock, flags);
179                 switch (data->index) {
180                 case 0:
181                         reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
182                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
183                         break;
184                 case 1:
185                         reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
186                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
187                         break;
188                 case 2:
189                         reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
190                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
191                         break;
192                 case 3:
193                         reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
194                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
195                         break;
196                 }
197                 if (reg && val)
198                         writel(val, reg);
199                 spin_unlock_irqrestore(&usbmisc->lock, flags);
200         }
201
202         return 0;
203 }
204
205 static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
206 {
207         unsigned long flags;
208         u32 reg;
209
210         if (data->index > 3)
211                 return -EINVAL;
212
213         if (data->disable_oc) {
214                 spin_lock_irqsave(&usbmisc->lock, flags);
215                 reg = readl(usbmisc->base + data->index * 4);
216                 writel(reg | MX6_BM_OVER_CUR_DIS,
217                         usbmisc->base + data->index * 4);
218                 spin_unlock_irqrestore(&usbmisc->lock, flags);
219         }
220
221         return 0;
222 }
223
224 static const struct usbmisc_ops imx25_usbmisc_ops = {
225         .init = usbmisc_imx25_init,
226         .post = usbmisc_imx25_post,
227 };
228
229 static const struct usbmisc_ops imx27_usbmisc_ops = {
230         .init = usbmisc_imx27_init,
231 };
232
233 static const struct usbmisc_ops imx53_usbmisc_ops = {
234         .init = usbmisc_imx53_init,
235 };
236
237 static const struct usbmisc_ops imx6q_usbmisc_ops = {
238         .init = usbmisc_imx6q_init,
239 };
240
241 int imx_usbmisc_init(struct imx_usbmisc_data *data)
242 {
243         if (!usbmisc)
244                 return -EPROBE_DEFER;
245         if (!usbmisc->ops->init)
246                 return 0;
247         return usbmisc->ops->init(data);
248 }
249 EXPORT_SYMBOL_GPL(imx_usbmisc_init);
250
251 int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
252 {
253         if (!usbmisc)
254                 return -EPROBE_DEFER;
255         if (!usbmisc->ops->post)
256                 return 0;
257         return usbmisc->ops->post(data);
258 }
259 EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
260
261 static const struct of_device_id usbmisc_imx_dt_ids[] = {
262         {
263                 .compatible = "fsl,imx25-usbmisc",
264                 .data = &imx25_usbmisc_ops,
265         },
266         {
267                 .compatible = "fsl,imx35-usbmisc",
268                 .data = &imx25_usbmisc_ops,
269         },
270         {
271                 .compatible = "fsl,imx27-usbmisc",
272                 .data = &imx27_usbmisc_ops,
273         },
274         {
275                 .compatible = "fsl,imx51-usbmisc",
276                 .data = &imx53_usbmisc_ops,
277         },
278         {
279                 .compatible = "fsl,imx53-usbmisc",
280                 .data = &imx53_usbmisc_ops,
281         },
282         {
283                 .compatible = "fsl,imx6q-usbmisc",
284                 .data = &imx6q_usbmisc_ops,
285         },
286         { /* sentinel */ }
287 };
288 MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
289
290 static int usbmisc_imx_probe(struct platform_device *pdev)
291 {
292         struct resource *res;
293         struct imx_usbmisc *data;
294         int ret;
295         struct of_device_id *tmp_dev;
296
297         if (usbmisc)
298                 return -EBUSY;
299
300         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
301         if (!data)
302                 return -ENOMEM;
303
304         spin_lock_init(&data->lock);
305
306         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
307         data->base = devm_ioremap_resource(&pdev->dev, res);
308         if (IS_ERR(data->base))
309                 return PTR_ERR(data->base);
310
311         data->clk = devm_clk_get(&pdev->dev, NULL);
312         if (IS_ERR(data->clk)) {
313                 dev_err(&pdev->dev,
314                         "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
315                 return PTR_ERR(data->clk);
316         }
317
318         ret = clk_prepare_enable(data->clk);
319         if (ret) {
320                 dev_err(&pdev->dev,
321                         "clk_prepare_enable failed, err=%d\n", ret);
322                 return ret;
323         }
324
325         tmp_dev = (struct of_device_id *)
326                 of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
327         data->ops = (const struct usbmisc_ops *)tmp_dev->data;
328         usbmisc = data;
329
330         return 0;
331 }
332
333 static int usbmisc_imx_remove(struct platform_device *pdev)
334 {
335         clk_disable_unprepare(usbmisc->clk);
336         usbmisc = NULL;
337         return 0;
338 }
339
340 static struct platform_driver usbmisc_imx_driver = {
341         .probe = usbmisc_imx_probe,
342         .remove = usbmisc_imx_remove,
343         .driver = {
344                 .name = "usbmisc_imx",
345                 .owner = THIS_MODULE,
346                 .of_match_table = usbmisc_imx_dt_ids,
347          },
348 };
349
350 module_platform_driver(usbmisc_imx_driver);
351
352 MODULE_ALIAS("platform:usbmisc-imx");
353 MODULE_LICENSE("GPL v2");
354 MODULE_DESCRIPTION("driver for imx usb non-core registers");
355 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");