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 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)
30 
31 #define MX6_BM_OVER_CUR_DIS		BIT(7)
32 
33 struct usbmisc_ops {
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);
38 };
39 
40 struct imx_usbmisc {
41 	void __iomem *base;
42 	spinlock_t lock;
43 	struct clk *clk;
44 	const struct usbmisc_ops *ops;
45 };
46 
47 static struct imx_usbmisc *usbmisc;
48 
49 static int usbmisc_imx25_post(struct imx_usbmisc_data *data)
50 {
51 	void __iomem *reg;
52 	unsigned long flags;
53 	u32 val;
54 
55 	if (data->index > 2)
56 		return -EINVAL;
57 
58 	reg = usbmisc->base + MX25_USB_PHY_CTRL_OFFSET;
59 
60 	if (data->evdo) {
61 		spin_lock_irqsave(&usbmisc->lock, flags);
62 		val = readl(reg);
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 */
66 	}
67 
68 	return 0;
69 }
70 
71 static int usbmisc_imx53_init(struct imx_usbmisc_data *data)
72 {
73 	void __iomem *reg = NULL;
74 	unsigned long flags;
75 	u32 val = 0;
76 
77 	if (data->index > 3)
78 		return -EINVAL;
79 
80 	if (data->disable_oc) {
81 		spin_lock_irqsave(&usbmisc->lock, flags);
82 		switch (data->index) {
83 		case 0:
84 			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
85 			val = readl(reg) | MX53_BM_OVER_CUR_DIS_OTG;
86 			break;
87 		case 1:
88 			reg = usbmisc->base + MX53_USB_OTG_PHY_CTRL_0_OFFSET;
89 			val = readl(reg) | MX53_BM_OVER_CUR_DIS_H1;
90 			break;
91 		case 2:
92 			reg = usbmisc->base + MX53_USB_UH2_CTRL_OFFSET;
93 			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
94 			break;
95 		case 3:
96 			reg = usbmisc->base + MX53_USB_UH3_CTRL_OFFSET;
97 			val = readl(reg) | MX53_BM_OVER_CUR_DIS_UHx;
98 			break;
99 		}
100 		if (reg && val)
101 			writel(val, reg);
102 		spin_unlock_irqrestore(&usbmisc->lock, flags);
103 	}
104 
105 	return 0;
106 }
107 
108 static int usbmisc_imx6q_init(struct imx_usbmisc_data *data)
109 {
110 	unsigned long flags;
111 	u32 reg;
112 
113 	if (data->index > 3)
114 		return -EINVAL;
115 
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);
122 	}
123 
124 	return 0;
125 }
126 
127 static const struct usbmisc_ops imx25_usbmisc_ops = {
128 	.post = usbmisc_imx25_post,
129 };
130 
131 static const struct usbmisc_ops imx53_usbmisc_ops = {
132 	.init = usbmisc_imx53_init,
133 };
134 
135 static const struct usbmisc_ops imx6q_usbmisc_ops = {
136 	.init = usbmisc_imx6q_init,
137 };
138 
139 int imx_usbmisc_init(struct imx_usbmisc_data *data)
140 {
141 	if (!usbmisc)
142 		return -EPROBE_DEFER;
143 	if (!usbmisc->ops->init)
144 		return 0;
145 	return usbmisc->ops->init(data);
146 }
147 EXPORT_SYMBOL_GPL(imx_usbmisc_init);
148 
149 int imx_usbmisc_init_post(struct imx_usbmisc_data *data)
150 {
151 	if (!usbmisc)
152 		return -EPROBE_DEFER;
153 	if (!usbmisc->ops->post)
154 		return 0;
155 	return usbmisc->ops->post(data);
156 }
157 EXPORT_SYMBOL_GPL(imx_usbmisc_init_post);
158 
159 static const struct of_device_id usbmisc_imx_dt_ids[] = {
160 	{
161 		.compatible = "fsl,imx25-usbmisc",
162 		.data = &imx25_usbmisc_ops,
163 	},
164 	{
165 		.compatible = "fsl,imx53-usbmisc",
166 		.data = &imx53_usbmisc_ops,
167 	},
168 	{
169 		.compatible = "fsl,imx6q-usbmisc",
170 		.data = &imx6q_usbmisc_ops,
171 	},
172 	{ /* sentinel */ }
173 };
174 MODULE_DEVICE_TABLE(of, usbmisc_imx_dt_ids);
175 
176 static int usbmisc_imx_probe(struct platform_device *pdev)
177 {
178 	struct resource	*res;
179 	struct imx_usbmisc *data;
180 	int ret;
181 	struct of_device_id *tmp_dev;
182 
183 	if (usbmisc)
184 		return -EBUSY;
185 
186 	data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
187 	if (!data)
188 		return -ENOMEM;
189 
190 	spin_lock_init(&data->lock);
191 
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);
196 
197 	data->clk = devm_clk_get(&pdev->dev, NULL);
198 	if (IS_ERR(data->clk)) {
199 		dev_err(&pdev->dev,
200 			"failed to get clock, err=%ld\n", PTR_ERR(data->clk));
201 		return PTR_ERR(data->clk);
202 	}
203 
204 	ret = clk_prepare_enable(data->clk);
205 	if (ret) {
206 		dev_err(&pdev->dev,
207 			"clk_prepare_enable failed, err=%d\n", ret);
208 		return ret;
209 	}
210 
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;
214 	usbmisc = data;
215 
216 	return 0;
217 }
218 
219 static int usbmisc_imx_remove(struct platform_device *pdev)
220 {
221 	clk_disable_unprepare(usbmisc->clk);
222 	usbmisc = NULL;
223 	return 0;
224 }
225 
226 static struct platform_driver usbmisc_imx_driver = {
227 	.probe = usbmisc_imx_probe,
228 	.remove = usbmisc_imx_remove,
229 	.driver = {
230 		.name = "usbmisc_imx",
231 		.owner = THIS_MODULE,
232 		.of_match_table = usbmisc_imx_dt_ids,
233 	 },
234 };
235 
236 module_platform_driver(usbmisc_imx_driver);
237 
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>");
242