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