2 * Copyright 2012 Freescale Semiconductor, Inc.
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:
8 * http://www.opensource.org/licenses/gpl-license.html
9 * http://www.gnu.org/copyleft/gpl.html
12 #include <linux/module.h>
13 #include <linux/of_platform.h>
14 #include <linux/clk.h>
15 #include <linux/err.h>
17 #include <linux/delay.h>
19 #include "ci_hdrc_imx.h"
21 #define MX25_USB_PHY_CTRL_OFFSET 0x08
22 #define MX25_BM_EXTERNAL_VBUS_DIVIDER BIT(23)
24 #define MX53_USB_OTG_PHY_CTRL_0_OFFSET 0x08
25 #define MX53_USB_UH2_CTRL_OFFSET 0x14
26 #define MX53_USB_UH3_CTRL_OFFSET 0x18
27 #define MX53_BM_OVER_CUR_DIS_H1 BIT(5)
28 #define MX53_BM_OVER_CUR_DIS_OTG BIT(8)
29 #define MX53_BM_OVER_CUR_DIS_UHx BIT(30)
31 #define MX6_BM_OVER_CUR_DIS BIT(7)
34 /* It's called once when probe a usb device */
35 int (*init
)(struct imx_usbmisc_data
*data
);
36 /* It's called once after adding a usb device */
37 int (*post
)(struct imx_usbmisc_data
*data
);
44 const struct usbmisc_ops
*ops
;
47 static struct imx_usbmisc
*usbmisc
;
49 static int usbmisc_imx25_post(struct imx_usbmisc_data
*data
)
58 reg
= usbmisc
->base
+ MX25_USB_PHY_CTRL_OFFSET
;
61 spin_lock_irqsave(&usbmisc
->lock
, flags
);
63 writel(val
| MX25_BM_EXTERNAL_VBUS_DIVIDER
, reg
);
64 spin_unlock_irqrestore(&usbmisc
->lock
, flags
);
65 usleep_range(5000, 10000); /* needed to stabilize voltage */
71 static int usbmisc_imx53_init(struct imx_usbmisc_data
*data
)
73 void __iomem
*reg
= NULL
;
80 if (data
->disable_oc
) {
81 spin_lock_irqsave(&usbmisc
->lock
, flags
);
82 switch (data
->index
) {
84 reg
= usbmisc
->base
+ MX53_USB_OTG_PHY_CTRL_0_OFFSET
;
85 val
= readl(reg
) | MX53_BM_OVER_CUR_DIS_OTG
;
88 reg
= usbmisc
->base
+ MX53_USB_OTG_PHY_CTRL_0_OFFSET
;
89 val
= readl(reg
) | MX53_BM_OVER_CUR_DIS_H1
;
92 reg
= usbmisc
->base
+ MX53_USB_UH2_CTRL_OFFSET
;
93 val
= readl(reg
) | MX53_BM_OVER_CUR_DIS_UHx
;
96 reg
= usbmisc
->base
+ MX53_USB_UH3_CTRL_OFFSET
;
97 val
= readl(reg
) | MX53_BM_OVER_CUR_DIS_UHx
;
102 spin_unlock_irqrestore(&usbmisc
->lock
, flags
);
108 static int usbmisc_imx6q_init(struct imx_usbmisc_data
*data
)
116 if (data
->disable_oc
) {
117 spin_lock_irqsave(&usbmisc
->lock
, flags
);
118 reg
= readl(usbmisc
->base
+ data
->index
* 4);
119 writel(reg
| MX6_BM_OVER_CUR_DIS
,
120 usbmisc
->base
+ data
->index
* 4);
121 spin_unlock_irqrestore(&usbmisc
->lock
, flags
);
127 static const struct usbmisc_ops imx25_usbmisc_ops
= {
128 .post
= usbmisc_imx25_post
,
131 static const struct usbmisc_ops imx53_usbmisc_ops
= {
132 .init
= usbmisc_imx53_init
,
135 static const struct usbmisc_ops imx6q_usbmisc_ops
= {
136 .init
= usbmisc_imx6q_init
,
139 int imx_usbmisc_init(struct imx_usbmisc_data
*data
)
142 return -EPROBE_DEFER
;
143 if (!usbmisc
->ops
->init
)
145 return usbmisc
->ops
->init(data
);
147 EXPORT_SYMBOL_GPL(imx_usbmisc_init
);
149 int imx_usbmisc_init_post(struct imx_usbmisc_data
*data
)
152 return -EPROBE_DEFER
;
153 if (!usbmisc
->ops
->post
)
155 return usbmisc
->ops
->post(data
);
157 EXPORT_SYMBOL_GPL(imx_usbmisc_init_post
);
159 static const struct of_device_id usbmisc_imx_dt_ids
[] = {
161 .compatible
= "fsl,imx25-usbmisc",
162 .data
= &imx25_usbmisc_ops
,
165 .compatible
= "fsl,imx53-usbmisc",
166 .data
= &imx53_usbmisc_ops
,
169 .compatible
= "fsl,imx6q-usbmisc",
170 .data
= &imx6q_usbmisc_ops
,
174 MODULE_DEVICE_TABLE(of
, usbmisc_imx_dt_ids
);
176 static int usbmisc_imx_probe(struct platform_device
*pdev
)
178 struct resource
*res
;
179 struct imx_usbmisc
*data
;
181 struct of_device_id
*tmp_dev
;
186 data
= devm_kzalloc(&pdev
->dev
, sizeof(*data
), GFP_KERNEL
);
190 spin_lock_init(&data
->lock
);
192 res
= platform_get_resource(pdev
, IORESOURCE_MEM
, 0);
193 data
->base
= devm_ioremap_resource(&pdev
->dev
, res
);
194 if (IS_ERR(data
->base
))
195 return PTR_ERR(data
->base
);
197 data
->clk
= devm_clk_get(&pdev
->dev
, NULL
);
198 if (IS_ERR(data
->clk
)) {
200 "failed to get clock, err=%ld\n", PTR_ERR(data
->clk
));
201 return PTR_ERR(data
->clk
);
204 ret
= clk_prepare_enable(data
->clk
);
207 "clk_prepare_enable failed, err=%d\n", ret
);
211 tmp_dev
= (struct of_device_id
*)
212 of_match_device(usbmisc_imx_dt_ids
, &pdev
->dev
);
213 data
->ops
= (const struct usbmisc_ops
*)tmp_dev
->data
;
219 static int usbmisc_imx_remove(struct platform_device
*pdev
)
221 clk_disable_unprepare(usbmisc
->clk
);
226 static struct platform_driver usbmisc_imx_driver
= {
227 .probe
= usbmisc_imx_probe
,
228 .remove
= usbmisc_imx_remove
,
230 .name
= "usbmisc_imx",
231 .owner
= THIS_MODULE
,
232 .of_match_table
= usbmisc_imx_dt_ids
,
236 module_platform_driver(usbmisc_imx_driver
);
238 MODULE_ALIAS("platform:usbmisc-imx");
239 MODULE_LICENSE("GPL v2");
240 MODULE_DESCRIPTION("driver for imx usb non-core registers");
241 MODULE_AUTHOR("Richard Zhao <richard.zhao@freescale.com>");