]> Pileus Git - ~andy/linux/blob - drivers/usb/chipidea/usbmisc_imx.c
Merge branch 'timers/clockevents' of git://git.linaro.org/people/dlezcano/clockevents...
[~andy/linux] / 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 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 MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
179
180 static int usbmisc_imx_probe(struct platform_device *pdev)
181 {
182         struct resource *res;
183         struct imx_usbmisc *data;
184         int ret;
185         struct of_device_id *tmp_dev;
186
187         if (usbmisc)
188                 return -EBUSY;
189
190         data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
191         if (!data)
192                 return -ENOMEM;
193
194         spin_lock_init(&data->lock);
195
196         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
197         data->base = devm_ioremap_resource(&pdev->dev, res);
198         if (IS_ERR(data->base))
199                 return PTR_ERR(data->base);
200
201         data->clk = devm_clk_get(&pdev->dev, NULL);
202         if (IS_ERR(data->clk)) {
203                 dev_err(&pdev->dev,
204                         "failed to get clock, err=%ld\n", PTR_ERR(data->clk));
205                 return PTR_ERR(data->clk);
206         }
207
208         ret = clk_prepare_enable(data->clk);
209         if (ret) {
210                 dev_err(&pdev->dev,
211                         "clk_prepare_enable failed, err=%d\n", ret);
212                 return ret;
213         }
214
215         tmp_dev = (struct of_device_id *)
216                 of_match_device(usbmisc_imx_dt_ids, &pdev->dev);
217         data->ops = (const struct usbmisc_ops *)tmp_dev->data;
218         usbmisc = data;
219         ret = usbmisc_set_ops(data->ops);
220         if (ret) {
221                 usbmisc = NULL;
222                 clk_disable_unprepare(data->clk);
223                 return ret;
224         }
225
226         return 0;
227 }
228
229 static int usbmisc_imx_remove(struct platform_device *pdev)
230 {
231         usbmisc_unset_ops(usbmisc->ops);
232         clk_disable_unprepare(usbmisc->clk);
233         usbmisc = NULL;
234         return 0;
235 }
236
237 static struct platform_driver usbmisc_imx_driver = {
238         .probe = usbmisc_imx_probe,
239         .remove = usbmisc_imx_remove,
240         .driver = {
241                 .name = "usbmisc_imx",
242                 .owner = THIS_MODULE,
243                 .of_match_table = usbmisc_imx_dt_ids,
244          },
245 };
246
247 module_platform_driver(usbmisc_imx_driver);
248
249 MODULE_ALIAS("platform:usbmisc-imx");
250 MODULE_LICENSE("GPL v2");
251 MODULE_DESCRIPTION("driver for imx usb non-core registers");
252 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");