1 /* Copyright (c) 2010, Code Aurora Forum. All rights reserved.
2  *
3  * This program is free software; you can redistribute it and/or modify
4  * it under the terms of the GNU General Public License version 2 and
5  * only version 2 as published by the Free Software Foundation.
6  */
7 
8 #include <linux/module.h>
9 #include <linux/platform_device.h>
10 #include <linux/pm_runtime.h>
11 #include <linux/usb/msm_hsusb_hw.h>
12 #include <linux/usb/ulpi.h>
13 
14 #include "ci13xxx_udc.c"
15 
16 #define MSM_USB_BASE	(udc->regs)
17 
msm_udc_irq(int irq,void * data)18 static irqreturn_t msm_udc_irq(int irq, void *data)
19 {
20 	return udc_irq();
21 }
22 
ci13xxx_msm_notify_event(struct ci13xxx * udc,unsigned event)23 static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
24 {
25 	struct device *dev = udc->gadget.dev.parent;
26 	int val;
27 
28 	switch (event) {
29 	case CI13XXX_CONTROLLER_RESET_EVENT:
30 		dev_dbg(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
31 		writel(0, USB_AHBBURST);
32 		writel(0, USB_AHBMODE);
33 		break;
34 	case CI13XXX_CONTROLLER_STOPPED_EVENT:
35 		dev_dbg(dev, "CI13XXX_CONTROLLER_STOPPED_EVENT received\n");
36 		/*
37 		 * Put the transceiver in non-driving mode. Otherwise host
38 		 * may not detect soft-disconnection.
39 		 */
40 		val = usb_phy_io_read(udc->transceiver, ULPI_FUNC_CTRL);
41 		val &= ~ULPI_FUNC_CTRL_OPMODE_MASK;
42 		val |= ULPI_FUNC_CTRL_OPMODE_NONDRIVING;
43 		usb_phy_io_write(udc->transceiver, val, ULPI_FUNC_CTRL);
44 		break;
45 	default:
46 		dev_dbg(dev, "unknown ci13xxx_udc event\n");
47 		break;
48 	}
49 }
50 
51 static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
52 	.name			= "ci13xxx_msm",
53 	.flags			= CI13XXX_REGS_SHARED |
54 				  CI13XXX_REQUIRE_TRANSCEIVER |
55 				  CI13XXX_PULLUP_ON_VBUS |
56 				  CI13XXX_DISABLE_STREAMING,
57 
58 	.notify_event		= ci13xxx_msm_notify_event,
59 };
60 
ci13xxx_msm_probe(struct platform_device * pdev)61 static int ci13xxx_msm_probe(struct platform_device *pdev)
62 {
63 	struct resource *res;
64 	void __iomem *regs;
65 	int irq;
66 	int ret;
67 
68 	dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
69 
70 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
71 	if (!res) {
72 		dev_err(&pdev->dev, "failed to get platform resource mem\n");
73 		return -ENXIO;
74 	}
75 
76 	regs = ioremap(res->start, resource_size(res));
77 	if (!regs) {
78 		dev_err(&pdev->dev, "ioremap failed\n");
79 		return -ENOMEM;
80 	}
81 
82 	ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, regs);
83 	if (ret < 0) {
84 		dev_err(&pdev->dev, "udc_probe failed\n");
85 		goto iounmap;
86 	}
87 
88 	irq = platform_get_irq(pdev, 0);
89 	if (irq < 0) {
90 		dev_err(&pdev->dev, "IRQ not found\n");
91 		ret = -ENXIO;
92 		goto udc_remove;
93 	}
94 
95 	ret = request_irq(irq, msm_udc_irq, IRQF_SHARED, pdev->name, pdev);
96 	if (ret < 0) {
97 		dev_err(&pdev->dev, "request_irq failed\n");
98 		goto udc_remove;
99 	}
100 
101 	pm_runtime_no_callbacks(&pdev->dev);
102 	pm_runtime_enable(&pdev->dev);
103 
104 	return 0;
105 
106 udc_remove:
107 	udc_remove();
108 iounmap:
109 	iounmap(regs);
110 
111 	return ret;
112 }
113 
114 static struct platform_driver ci13xxx_msm_driver = {
115 	.probe = ci13xxx_msm_probe,
116 	.driver = { .name = "msm_hsusb", },
117 };
118 MODULE_ALIAS("platform:msm_hsusb");
119 
ci13xxx_msm_init(void)120 static int __init ci13xxx_msm_init(void)
121 {
122 	return platform_driver_register(&ci13xxx_msm_driver);
123 }
124 module_init(ci13xxx_msm_init);
125 
126 MODULE_LICENSE("GPL v2");
127