aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/usb/otg/tegra-otg.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/usb/otg/tegra-otg.c')
-rw-r--r--drivers/usb/otg/tegra-otg.c540
1 files changed, 540 insertions, 0 deletions
diff --git a/drivers/usb/otg/tegra-otg.c b/drivers/usb/otg/tegra-otg.c
new file mode 100644
index 00000000000..4c04e6e183f
--- /dev/null
+++ b/drivers/usb/otg/tegra-otg.c
@@ -0,0 +1,540 @@
1/*
2 * drivers/usb/otg/tegra-otg.c
3 *
4 * OTG transceiver driver for Tegra UTMI phy
5 *
6 * Copyright (C) 2010 NVIDIA Corp.
7 * Copyright (C) 2010 Google, Inc.
8 *
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU General Public License as published by the
11 * Free Software Foundation; either version 2 of the License, or (at your
12 * option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful, but WITHOUT
15 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
16 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
17 * more details.
18 *
19 * You should have received a copy of the GNU General Public License along
20 * with this program; if not, write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 */
23
24#include <linux/usb.h>
25#include <linux/usb/otg.h>
26#include <linux/usb/gadget.h>
27#include <linux/usb/hcd.h>
28#include <linux/platform_device.h>
29#include <linux/platform_data/tegra_usb.h>
30#include <linux/clk.h>
31#include <linux/io.h>
32#include <linux/delay.h>
33#include <linux/err.h>
34
35#define USB_PHY_WAKEUP 0x408
36#define USB_ID_INT_EN (1 << 0)
37#define USB_ID_INT_STATUS (1 << 1)
38#define USB_ID_STATUS (1 << 2)
39#define USB_ID_PIN_WAKEUP_EN (1 << 6)
40#define USB_VBUS_WAKEUP_EN (1 << 30)
41#define USB_VBUS_INT_EN (1 << 8)
42#define USB_VBUS_INT_STATUS (1 << 9)
43#define USB_VBUS_STATUS (1 << 10)
44#define USB_INTS (USB_VBUS_INT_STATUS | USB_ID_INT_STATUS)
45
46typedef void (*callback_t)(enum usb_otg_state to,
47 enum usb_otg_state from, void *args);
48
49struct tegra_otg_data {
50 struct otg_transceiver otg;
51 unsigned long int_status;
52 spinlock_t lock;
53 void __iomem *regs;
54 struct clk *clk;
55 int irq;
56 struct platform_device *pdev;
57 struct work_struct work;
58 unsigned int intr_reg_data;
59 bool detect_vbus;
60 bool clk_enabled;
61 callback_t charger_cb;
62 void *charger_cb_data;
63
64};
65static struct tegra_otg_data *tegra_clone;
66
67static inline unsigned long otg_readl(struct tegra_otg_data *tegra,
68 unsigned int offset)
69{
70 return readl(tegra->regs + offset);
71}
72
73static inline void otg_writel(struct tegra_otg_data *tegra, unsigned long val,
74 unsigned int offset)
75{
76 writel(val, tegra->regs + offset);
77}
78
79static void tegra_otg_enable_clk(void)
80{
81 if (!tegra_clone->clk_enabled)
82 clk_enable(tegra_clone->clk);
83 tegra_clone->clk_enabled = true;
84}
85
86static void tegra_otg_disable_clk(void)
87{
88 if (tegra_clone->clk_enabled)
89 clk_disable(tegra_clone->clk);
90 tegra_clone->clk_enabled = false;
91}
92
93static const char *tegra_state_name(enum usb_otg_state state)
94{
95 switch (state) {
96 case OTG_STATE_A_HOST:
97 return "HOST";
98 case OTG_STATE_B_PERIPHERAL:
99 return "PERIPHERAL";
100 case OTG_STATE_A_SUSPEND:
101 return "SUSPEND";
102 case OTG_STATE_UNDEFINED:
103 return "UNDEFINED";
104 default:
105 return "INVALID";
106 }
107}
108
109static struct platform_device *
110tegra_usb_otg_host_register(struct platform_device *ehci_device,
111 struct tegra_ehci_platform_data *pdata)
112{
113 struct platform_device *pdev;
114 void *platform_data;
115 int val;
116
117 pdev = platform_device_alloc(ehci_device->name, ehci_device->id);
118 if (!pdev)
119 return NULL;
120
121 val = platform_device_add_resources(pdev, ehci_device->resource,
122 ehci_device->num_resources);
123 if (val)
124 goto error;
125
126 pdev->dev.dma_mask = ehci_device->dev.dma_mask;
127 pdev->dev.coherent_dma_mask = ehci_device->dev.coherent_dma_mask;
128
129 platform_data = kmalloc(sizeof(struct tegra_ehci_platform_data),
130 GFP_KERNEL);
131 if (!platform_data)
132 goto error;
133
134 memcpy(platform_data, pdata, sizeof(struct tegra_ehci_platform_data));
135 pdev->dev.platform_data = platform_data;
136
137 val = platform_device_add(pdev);
138 if (val)
139 goto error_add;
140
141 return pdev;
142
143error_add:
144 kfree(platform_data);
145error:
146 pr_err("%s: failed to add the host controller device\n", __func__);
147 platform_device_put(pdev);
148 return NULL;
149}
150
151static void tegra_usb_otg_host_unregister(struct platform_device *pdev)
152{
153 kfree(pdev->dev.platform_data);
154 pdev->dev.platform_data = NULL;
155 platform_device_unregister(pdev);
156}
157
158void tegra_start_host(struct tegra_otg_data *tegra)
159{
160 struct tegra_otg_platform_data *pdata = tegra->otg.dev->platform_data;
161 if (!tegra->pdev) {
162 tegra->pdev = tegra_usb_otg_host_register(pdata->ehci_device,
163 pdata->ehci_pdata);
164 }
165}
166
167void tegra_stop_host(struct tegra_otg_data *tegra)
168{
169 if (tegra->pdev) {
170 tegra_usb_otg_host_unregister(tegra->pdev);
171 tegra->pdev = NULL;
172 }
173}
174
175int register_otg_callback(callback_t cb, void *args)
176{
177 if (!tegra_clone)
178 return -ENODEV;
179 tegra_clone->charger_cb = cb;
180 tegra_clone->charger_cb_data = args;
181 return 0;
182}
183EXPORT_SYMBOL_GPL(register_otg_callback);
184
185static void irq_work(struct work_struct *work)
186{
187 struct tegra_otg_data *tegra =
188 container_of(work, struct tegra_otg_data, work);
189 struct otg_transceiver *otg = &tegra->otg;
190 enum usb_otg_state from = otg->state;
191 enum usb_otg_state to = OTG_STATE_UNDEFINED;
192 unsigned long flags;
193 unsigned long status;
194
195 if (tegra->detect_vbus) {
196 tegra->detect_vbus = false;
197 tegra_otg_enable_clk();
198 return;
199 }
200
201 clk_enable(tegra->clk);
202
203 spin_lock_irqsave(&tegra->lock, flags);
204
205 status = tegra->int_status;
206
207 if (tegra->int_status & USB_ID_INT_STATUS) {
208 if (status & USB_ID_STATUS) {
209 if ((status & USB_VBUS_STATUS) && (from != OTG_STATE_A_HOST))
210 to = OTG_STATE_B_PERIPHERAL;
211 else
212 to = OTG_STATE_A_SUSPEND;
213 }
214 else
215 to = OTG_STATE_A_HOST;
216 }
217 if (from != OTG_STATE_A_HOST) {
218 if (tegra->int_status & USB_VBUS_INT_STATUS) {
219 if (status & USB_VBUS_STATUS)
220 to = OTG_STATE_B_PERIPHERAL;
221 else
222 to = OTG_STATE_A_SUSPEND;
223 }
224 }
225 spin_unlock_irqrestore(&tegra->lock, flags);
226
227 if (to != OTG_STATE_UNDEFINED) {
228 otg->state = to;
229
230 dev_info(tegra->otg.dev, "%s --> %s\n", tegra_state_name(from),
231 tegra_state_name(to));
232
233 if (tegra->charger_cb)
234 tegra->charger_cb(to, from, tegra->charger_cb_data);
235
236 if (to == OTG_STATE_A_SUSPEND) {
237 if (from == OTG_STATE_A_HOST)
238 tegra_stop_host(tegra);
239 else if (from == OTG_STATE_B_PERIPHERAL && otg->gadget)
240 usb_gadget_vbus_disconnect(otg->gadget);
241 } else if (to == OTG_STATE_B_PERIPHERAL && otg->gadget) {
242 if (from == OTG_STATE_A_SUSPEND)
243 usb_gadget_vbus_connect(otg->gadget);
244 } else if (to == OTG_STATE_A_HOST) {
245 if (from == OTG_STATE_A_SUSPEND)
246 tegra_start_host(tegra);
247 }
248 }
249
250
251 clk_disable(tegra->clk);
252 tegra_otg_disable_clk();
253}
254
255static irqreturn_t tegra_otg_irq(int irq, void *data)
256{
257 struct tegra_otg_data *tegra = data;
258 unsigned long flags;
259 unsigned long val;
260
261 spin_lock_irqsave(&tegra->lock, flags);
262
263 val = otg_readl(tegra, USB_PHY_WAKEUP);
264 if (val & (USB_VBUS_INT_EN | USB_ID_INT_EN)) {
265 otg_writel(tegra, val, USB_PHY_WAKEUP);
266 if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
267 tegra->int_status = val;
268 tegra->detect_vbus = false;
269 schedule_work(&tegra->work);
270 }
271 }
272
273 spin_unlock_irqrestore(&tegra->lock, flags);
274
275 return IRQ_HANDLED;
276}
277
278void tegra_otg_check_vbus_detection(void)
279{
280 tegra_clone->detect_vbus = true;
281 schedule_work(&tegra_clone->work);
282}
283EXPORT_SYMBOL(tegra_otg_check_vbus_detection);
284
285static int tegra_otg_set_peripheral(struct otg_transceiver *otg,
286 struct usb_gadget *gadget)
287{
288 struct tegra_otg_data *tegra;
289 unsigned long val;
290
291 tegra = container_of(otg, struct tegra_otg_data, otg);
292 otg->gadget = gadget;
293
294 clk_enable(tegra->clk);
295 val = otg_readl(tegra, USB_PHY_WAKEUP);
296 val |= (USB_VBUS_INT_EN | USB_VBUS_WAKEUP_EN);
297 val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
298 otg_writel(tegra, val, USB_PHY_WAKEUP);
299 /* Add delay to make sure register is updated */
300 udelay(1);
301 clk_disable(tegra->clk);
302
303 if ((val & USB_ID_STATUS) && (val & USB_VBUS_STATUS)) {
304 val |= USB_VBUS_INT_STATUS;
305 } else if (!(val & USB_ID_STATUS)) {
306 val |= USB_ID_INT_STATUS;
307 } else {
308 val &= ~(USB_ID_INT_STATUS | USB_VBUS_INT_STATUS);
309 }
310
311 if ((val & USB_ID_INT_STATUS) || (val & USB_VBUS_INT_STATUS)) {
312 tegra->int_status = val;
313 tegra->detect_vbus = false;
314 schedule_work (&tegra->work);
315 }
316
317 return 0;
318}
319
320static int tegra_otg_set_host(struct otg_transceiver *otg,
321 struct usb_bus *host)
322{
323 struct tegra_otg_data *tegra;
324 unsigned long val;
325
326 tegra = container_of(otg, struct tegra_otg_data, otg);
327 otg->host = host;
328
329 clk_enable(tegra->clk);
330 val = otg_readl(tegra, USB_PHY_WAKEUP);
331 val &= ~(USB_VBUS_INT_STATUS | USB_ID_INT_STATUS);
332
333 val |= (USB_ID_INT_EN | USB_ID_PIN_WAKEUP_EN);
334 otg_writel(tegra, val, USB_PHY_WAKEUP);
335 clk_disable(tegra->clk);
336
337 return 0;
338}
339
340static int tegra_otg_set_power(struct otg_transceiver *otg, unsigned mA)
341{
342 return 0;
343}
344
345static int tegra_otg_set_suspend(struct otg_transceiver *otg, int suspend)
346{
347 return 0;
348}
349
350static int tegra_otg_probe(struct platform_device *pdev)
351{
352 struct tegra_otg_data *tegra;
353 struct tegra_otg_platform_data *otg_pdata;
354 struct tegra_ehci_platform_data *ehci_pdata;
355 struct resource *res;
356 int err;
357
358 tegra = kzalloc(sizeof(struct tegra_otg_data), GFP_KERNEL);
359 if (!tegra)
360 return -ENOMEM;
361
362 tegra->otg.dev = &pdev->dev;
363 otg_pdata = tegra->otg.dev->platform_data;
364 ehci_pdata = otg_pdata->ehci_pdata;
365 tegra->otg.label = "tegra-otg";
366 tegra->otg.state = OTG_STATE_UNDEFINED;
367 tegra->otg.set_host = tegra_otg_set_host;
368 tegra->otg.set_peripheral = tegra_otg_set_peripheral;
369 tegra->otg.set_suspend = tegra_otg_set_suspend;
370 tegra->otg.set_power = tegra_otg_set_power;
371 spin_lock_init(&tegra->lock);
372
373 platform_set_drvdata(pdev, tegra);
374 tegra_clone = tegra;
375 tegra->clk_enabled = false;
376
377 tegra->clk = clk_get(&pdev->dev, NULL);
378 if (IS_ERR(tegra->clk)) {
379 dev_err(&pdev->dev, "Can't get otg clock\n");
380 err = PTR_ERR(tegra->clk);
381 goto err_clk;
382 }
383
384 err = clk_enable(tegra->clk);
385 if (err)
386 goto err_clken;
387
388 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
389 if (!res) {
390 dev_err(&pdev->dev, "Failed to get I/O memory\n");
391 err = -ENXIO;
392 goto err_io;
393 }
394 tegra->regs = ioremap(res->start, resource_size(res));
395 if (!tegra->regs) {
396 err = -ENOMEM;
397 goto err_io;
398 }
399
400 tegra->otg.state = OTG_STATE_A_SUSPEND;
401
402 err = otg_set_transceiver(&tegra->otg);
403 if (err) {
404 dev_err(&pdev->dev, "can't register transceiver (%d)\n", err);
405 goto err_otg;
406 }
407
408 res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
409 if (!res) {
410 dev_err(&pdev->dev, "Failed to get IRQ\n");
411 err = -ENXIO;
412 goto err_irq;
413 }
414 tegra->irq = res->start;
415 err = request_threaded_irq(tegra->irq, tegra_otg_irq,
416 NULL,
417 IRQF_SHARED, "tegra-otg", tegra);
418 if (err) {
419 dev_err(&pdev->dev, "Failed to register IRQ\n");
420 goto err_irq;
421 }
422 INIT_WORK (&tegra->work, irq_work);
423
424 if (!ehci_pdata->default_enable)
425 clk_disable(tegra->clk);
426 dev_info(&pdev->dev, "otg transceiver registered\n");
427 return 0;
428
429err_irq:
430 otg_set_transceiver(NULL);
431err_otg:
432 iounmap(tegra->regs);
433err_io:
434 clk_disable(tegra->clk);
435err_clken:
436 clk_put(tegra->clk);
437err_clk:
438 platform_set_drvdata(pdev, NULL);
439 kfree(tegra);
440 return err;
441}
442
443static int __exit tegra_otg_remove(struct platform_device *pdev)
444{
445 struct tegra_otg_data *tegra = platform_get_drvdata(pdev);
446
447 free_irq(tegra->irq, tegra);
448 otg_set_transceiver(NULL);
449 iounmap(tegra->regs);
450 clk_disable(tegra->clk);
451 clk_put(tegra->clk);
452 platform_set_drvdata(pdev, NULL);
453 kfree(tegra);
454
455 return 0;
456}
457
458#ifdef CONFIG_PM
459static int tegra_otg_suspend(struct device *dev)
460{
461 struct platform_device *pdev = to_platform_device(dev);
462 struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
463 struct otg_transceiver *otg = &tegra_otg->otg;
464 enum usb_otg_state from = otg->state;
465 /* store the interupt enable for cable ID and VBUS */
466 clk_enable(tegra_otg->clk);
467 tegra_otg->intr_reg_data = readl(tegra_otg->regs + USB_PHY_WAKEUP);
468 writel(0, (tegra_otg->regs + USB_PHY_WAKEUP));
469 clk_disable(tegra_otg->clk);
470
471 if (from == OTG_STATE_B_PERIPHERAL && otg->gadget) {
472 usb_gadget_vbus_disconnect(otg->gadget);
473 otg->state = OTG_STATE_A_SUSPEND;
474 }
475 tegra_otg_disable_clk();
476 return 0;
477}
478
479static void tegra_otg_resume(struct device *dev)
480{
481 struct platform_device *pdev = to_platform_device(dev);
482 struct tegra_otg_data *tegra_otg = platform_get_drvdata(pdev);
483 int val;
484 unsigned long flags;
485
486 tegra_otg_enable_clk();
487
488 /* Following delay is intentional.
489 * It is placed here after observing system hang.
490 * Root cause is not confirmed.
491 */
492 msleep(1);
493 /* restore the interupt enable for cable ID and VBUS */
494 clk_enable(tegra_otg->clk);
495 writel(tegra_otg->intr_reg_data, (tegra_otg->regs + USB_PHY_WAKEUP));
496 val = readl(tegra_otg->regs + USB_PHY_WAKEUP);
497 clk_disable(tegra_otg->clk);
498
499 /* A device might be connected while CPU is in sleep mode. In this case no interrupt
500 * will be triggered
501 * force irq_work to recheck connected devices
502 */
503 if (!(val & USB_ID_STATUS)) {
504 spin_lock_irqsave(&tegra_otg->lock, flags);
505 tegra_otg->int_status = (val | USB_ID_INT_STATUS );
506 schedule_work(&tegra_otg->work);
507 spin_unlock_irqrestore(&tegra_otg->lock, flags);
508 }
509
510 return;
511}
512
513static const struct dev_pm_ops tegra_otg_pm_ops = {
514 .complete = tegra_otg_resume,
515 .suspend = tegra_otg_suspend,
516};
517#endif
518
519static struct platform_driver tegra_otg_driver = {
520 .driver = {
521 .name = "tegra-otg",
522#ifdef CONFIG_PM
523 .pm = &tegra_otg_pm_ops,
524#endif
525 },
526 .remove = __exit_p(tegra_otg_remove),
527 .probe = tegra_otg_probe,
528};
529
530static int __init tegra_otg_init(void)
531{
532 return platform_driver_register(&tegra_otg_driver);
533}
534subsys_initcall(tegra_otg_init);
535
536static void __exit tegra_otg_exit(void)
537{
538 platform_driver_unregister(&tegra_otg_driver);
539}
540module_exit(tegra_otg_exit);