Linux-libre 3.10.48-gnu
[librecmc/linux-libre.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 "ci13xxx_imx.h"
20
21 #define USB_DEV_MAX 4
22
23 #define MX25_USB_PHY_CTRL_OFFSET        0x08
24 #define MX25_BM_EXTERNAL_VBUS_DIVIDER   BIT(23)
25
26 #define MX53_USB_OTG_PHY_CTRL_0_OFFSET  0x08
27 #define MX53_USB_UH2_CTRL_OFFSET        0x14
28 #define MX53_USB_UH3_CTRL_OFFSET        0x18
29 #define MX53_BM_OVER_CUR_DIS_H1         BIT(5)
30 #define MX53_BM_OVER_CUR_DIS_OTG        BIT(8)
31 #define MX53_BM_OVER_CUR_DIS_UHx        BIT(30)
32
33 #define MX6_BM_OVER_CUR_DIS             BIT(7)
34
35 struct imx_usbmisc {
36         void __iomem *base;
37         spinlock_t lock;
38         struct clk *clk;
39         struct usbmisc_usb_device usbdev[USB_DEV_MAX];
40         const struct usbmisc_ops *ops;
41 };
42
43 static struct imx_usbmisc *usbmisc;
44
45 static struct usbmisc_usb_device *get_usbdev(struct device *dev)
46 {
47         int i, ret;
48
49         for (i = 0; i < USB_DEV_MAX; i++) {
50                 if (usbmisc->usbdev[i].dev == dev)
51                         return &usbmisc->usbdev[i];
52                 else if (!usbmisc->usbdev[i].dev)
53                         break;
54         }
55
56         if (i >= USB_DEV_MAX)
57                 return ERR_PTR(-EBUSY);
58
59         ret = usbmisc_get_init_data(dev, &usbmisc->usbdev[i]);
60         if (ret)
61                 return ERR_PTR(ret);
62
63         return &usbmisc->usbdev[i];
64 }
65
66 static int usbmisc_imx25_post(struct device *dev)
67 {
68         struct usbmisc_usb_device *usbdev;
69         void __iomem *reg;
70         unsigned long flags;
71         u32 val;
72
73         usbdev = get_usbdev(dev);
74         if (IS_ERR(usbdev))
75                 return PTR_ERR(usbdev);
76
77         reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
78
79         if (usbdev->evdo) {
80                 spin_lock_irqsave(&usbmisc->lock, flags);
81                 val = readl(reg);
82                 writel(val | MX25_BM_EXTERNAL_VBUS_DIVIDER, reg);
83                 spin_unlock_irqrestore(&usbmisc->lock, flags);
84                 usleep_range(5000, 10000); /* needed to stabilize voltage */
85         }
86
87         return 0;
88 }
89
90 static int usbmisc_imx53_init(struct device *dev)
91 {
92         struct usbmisc_usb_device *usbdev;
93         void __iomem *reg = NULL;
94         unsigned long flags;
95         u32 val = 0;
96
97         usbdev = get_usbdev(dev);
98         if (IS_ERR(usbdev))
99                 return PTR_ERR(usbdev);
100
101         if (usbdev->disable_oc) {
102                 spin_lock_irqsave(&usbmisc->lock, flags);
103                 switch (usbdev->index) {
104                 case 0:
105                         reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
106                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
107                         break;
108                 case 1:
109                         reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
110                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
111                         break;
112                 case 2:
113                         reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
114                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
115                         break;
116                 case 3:
117                         reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
118                         val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
119                         break;
120                 }
121                 if (reg && val)
122                         writel(val, reg);
123                 spin_unlock_irqrestore(&usbmisc->lock, flags);
124         }
125
126         return 0;
127 }
128
129 static int usbmisc_imx6q_init(struct device *dev)
130 {
131
132         struct usbmisc_usb_device *usbdev;
133         unsigned long flags;
134         u32 reg;
135
136         usbdev = get_usbdev(dev);
137         if (IS_ERR(usbdev))
138                 return PTR_ERR(usbdev);
139
140         if (usbdev->disable_oc) {
141                 spin_lock_irqsave(&usbmisc->lock, flags);
142                 reg = readl(usbmisc->base + usbdev->index * 4);
143                 writel(reg | MX6_BM_OVER_CUR_DIS,
144                         usbmisc->base + usbdev->index * 4);
145                 spin_unlock_irqrestore(&usbmisc->lock, flags);
146         }
147
148         return 0;
149 }
150
151 static const struct usbmisc_ops imx25_usbmisc_ops = {
152         .post = usbmisc_imx25_post,
153 };
154
155 static const struct usbmisc_ops imx53_usbmisc_ops = {
156         .init = usbmisc_imx53_init,
157 };
158
159 static const struct usbmisc_ops imx6q_usbmisc_ops = {
160         .init = usbmisc_imx6q_init,
161 };
162
163 static const struct of_device_id usbmisc_imx_dt_ids[] = {
164         {
165                 .compatible = "fsl,imx25-usbmisc",
166                 .data = &imx25_usbmisc_ops,
167         },
168         {
169                 .compatible = "fsl,imx53-usbmisc",
170                 .data = &imx53_usbmisc_ops,
171         },
172         {
173                 .compatible = "fsl,imx6q-usbmisc",
174                 .data = &imx6q_usbmisc_ops,
175         },
176         { /* sentinel */ }
177 };
178
179 static int usbmisc_imx_probe(struct platform_device *pdev)
180 {
181         struct resource *res;
182         struct imx_usbmisc *data;
183         int ret;
184         struct of_device_id *tmp_dev;
185
186         if (usbmisc)
187                 return -EBUSY;
188
189         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
190         if (!data)
191                 return -ENOMEM;
192
193         spin_lock_init(&data->lock);
194
195         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
196         data->base = devm_ioremap_resource(&pdev->dev, res);
197         if (IS_ERR(data->base))
198                 return PTR_ERR(data->base);
199
200         data->clk = devm_clk_get(&pdev->dev, NULL);
201         if (IS_ERR(data->clk)) {
202                 dev_err(&pdev->dev,
203                         "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
204                 return PTR_ERR(data->clk);
205         }
206
207         ret = clk_prepare_enable(data->clk);
208         if (ret) {
209                 dev_err(&pdev->dev,
210                         "clk_prepare_enable failed, err=%d\n", ret);
211                 return ret;
212         }
213
214         tmp_dev = (struct of_device_id *)
215                 of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
216         data->ops = (const struct usbmisc_ops *)tmp_dev->data;
217         usbmisc = data;
218         ret = usbmisc_set_ops(data->ops);
219         if (ret) {
220                 usbmisc = NULL;
221                 clk_disable_unprepare(data->clk);
222                 return ret;
223         }
224
225         return 0;
226 }
227
228 static int usbmisc_imx_remove(struct platform_device *pdev)
229 {
230         usbmisc_unset_ops(usbmisc->ops);
231         clk_disable_unprepare(usbmisc->clk);
232         usbmisc = NULL;
233         return 0;
234 }
235
236 static struct platform_driver usbmisc_imx_driver = {
237         .probe = usbmisc_imx_probe,
238         .remove = usbmisc_imx_remove,
239         .driver = {
240                 .name = "usbmisc_imx",
241                 .owner = THIS_MODULE,
242                 .of_match_table = usbmisc_imx_dt_ids,
243          },
244 };
245
246 int usbmisc_imx_drv_init(void)
247 {
248         return platform_driver_register(&usbmisc_imx_driver);
249 }
250 subsys_initcall(usbmisc_imx_drv_init);
251
252 void usbmisc_imx_drv_exit(void)
253 {
254         platform_driver_unregister(&usbmisc_imx_driver);
255 }
256 module_exit(usbmisc_imx_drv_exit);
257
258 MODULE_ALIAS("platform:usbmisc-imx");
259 MODULE_LICENSE("GPL v2");
260 MODULE_DESCRIPTION("driver for imx usb non-core registers");
261 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");