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