blob: e3c1216fbf5ce9c7a13f99dbb1cf0528f2f21b1a [file] [log] [blame]
Lena Salman20984752012-03-12 17:26:39 +02001/* Copyright (c) 2010-2012, Code Aurora Forum. All rights reserved.
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +05302 *
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 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053012 */
13
14#include <linux/module.h>
15#include <linux/platform_device.h>
Pavankumar Kondeti2d0cdcc2010-12-07 17:54:05 +053016#include <linux/pm_runtime.h>
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053017#include <linux/usb/msm_hsusb_hw.h>
18#include <linux/usb/ulpi.h>
Steve Mucklef132c6c2012-06-06 18:30:57 -070019#include <linux/gpio.h>
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053020
21#include "ci13xxx_udc.c"
22
23#define MSM_USB_BASE (udc->regs)
24
Lena Salman20984752012-03-12 17:26:39 +020025struct ci13xxx_udc_context {
26 int irq;
27 void __iomem *regs;
Amit Blay9b033682012-05-24 16:59:23 +030028 int wake_gpio;
29 int wake_irq;
30 bool wake_irq_state;
Lena Salman20984752012-03-12 17:26:39 +020031};
Amit Blay9b033682012-05-24 16:59:23 +030032
Lena Salman20984752012-03-12 17:26:39 +020033static struct ci13xxx_udc_context _udc_ctxt;
34
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053035static irqreturn_t msm_udc_irq(int irq, void *data)
36{
37 return udc_irq();
38}
39
Amit Blay9b033682012-05-24 16:59:23 +030040static void ci13xxx_msm_suspend(void)
41{
42 struct device *dev = _udc->gadget.dev.parent;
43 dev_dbg(dev, "ci13xxx_msm_suspend\n");
44
45 if (_udc_ctxt.wake_irq && !_udc_ctxt.wake_irq_state) {
46 enable_irq_wake(_udc_ctxt.wake_irq);
47 enable_irq(_udc_ctxt.wake_irq);
48 _udc_ctxt.wake_irq_state = true;
49 }
50}
51
52static void ci13xxx_msm_resume(void)
53{
54 struct device *dev = _udc->gadget.dev.parent;
55 dev_dbg(dev, "ci13xxx_msm_resume\n");
56
57 if (_udc_ctxt.wake_irq && _udc_ctxt.wake_irq_state) {
58 disable_irq_wake(_udc_ctxt.wake_irq);
Amit Blay50256ee2012-06-14 14:48:01 +030059 disable_irq_nosync(_udc_ctxt.wake_irq);
Amit Blay9b033682012-05-24 16:59:23 +030060 _udc_ctxt.wake_irq_state = false;
61 }
62}
63
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053064static void ci13xxx_msm_notify_event(struct ci13xxx *udc, unsigned event)
65{
66 struct device *dev = udc->gadget.dev.parent;
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053067
68 switch (event) {
69 case CI13XXX_CONTROLLER_RESET_EVENT:
Amit Blay9b033682012-05-24 16:59:23 +030070 dev_info(dev, "CI13XXX_CONTROLLER_RESET_EVENT received\n");
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053071 writel(0, USB_AHBBURST);
Vijayavardhan Vennapusa5f32d7a2012-03-14 16:30:26 +053072 writel_relaxed(0x08, USB_AHBMODE);
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053073 break;
Amit Blay9b033682012-05-24 16:59:23 +030074 case CI13XXX_CONTROLLER_DISCONNECT_EVENT:
75 dev_info(dev, "CI13XXX_CONTROLLER_DISCONNECT_EVENT received\n");
76 ci13xxx_msm_resume();
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053077 break;
Amit Blay9b033682012-05-24 16:59:23 +030078 case CI13XXX_CONTROLLER_SUSPEND_EVENT:
79 dev_info(dev, "CI13XXX_CONTROLLER_SUSPEND_EVENT received\n");
80 ci13xxx_msm_suspend();
81 break;
82 case CI13XXX_CONTROLLER_RESUME_EVENT:
83 dev_info(dev, "CI13XXX_CONTROLLER_RESUME_EVENT received\n");
84 ci13xxx_msm_resume();
85 break;
86
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +053087 default:
88 dev_dbg(dev, "unknown ci13xxx_udc event\n");
89 break;
90 }
91}
92
Amit Blay9b033682012-05-24 16:59:23 +030093static irqreturn_t ci13xxx_msm_resume_irq(int irq, void *data)
94{
95 struct ci13xxx *udc = _udc;
96
97 if (udc->transceiver && udc->vbus_active && udc->suspended)
Steve Mucklef132c6c2012-06-06 18:30:57 -070098 usb_phy_set_suspend(udc->transceiver, 0);
Amit Blay9b033682012-05-24 16:59:23 +030099 else if (!udc->suspended)
100 ci13xxx_msm_resume();
101
102 return IRQ_HANDLED;
103}
104
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530105static struct ci13xxx_udc_driver ci13xxx_msm_udc_driver = {
106 .name = "ci13xxx_msm",
107 .flags = CI13XXX_REGS_SHARED |
108 CI13XXX_REQUIRE_TRANSCEIVER |
109 CI13XXX_PULLUP_ON_VBUS |
Vijayavardhan Vennapusad450cb02012-02-25 14:35:26 +0530110 CI13XXX_ZERO_ITC |
Chiranjeevi Velempatif1176012012-03-29 13:02:13 +0530111 CI13XXX_DISABLE_STREAMING |
Vijayavardhan Vennapusad450cb02012-02-25 14:35:26 +0530112 CI13XXX_IS_OTG,
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530113
114 .notify_event = ci13xxx_msm_notify_event,
115};
116
Amit Blay9b033682012-05-24 16:59:23 +0300117static int ci13xxx_msm_install_wake_gpio(struct platform_device *pdev,
118 struct resource *res)
119{
120 int wake_irq;
121 int ret;
122
123 dev_dbg(&pdev->dev, "ci13xxx_msm_install_wake_gpio\n");
124
125 _udc_ctxt.wake_gpio = res->start;
126 gpio_request(_udc_ctxt.wake_gpio, "USB_RESUME");
127 gpio_direction_input(_udc_ctxt.wake_gpio);
Steve Mucklef132c6c2012-06-06 18:30:57 -0700128 wake_irq = gpio_to_irq(_udc_ctxt.wake_gpio);
Amit Blay9b033682012-05-24 16:59:23 +0300129 if (wake_irq < 0) {
130 dev_err(&pdev->dev, "could not register USB_RESUME GPIO.\n");
131 return -ENXIO;
132 }
133
134 dev_dbg(&pdev->dev, "_udc_ctxt.gpio_irq = %d and irq = %d\n",
135 _udc_ctxt.wake_gpio, wake_irq);
136 ret = request_irq(wake_irq, ci13xxx_msm_resume_irq,
Amit Blay50256ee2012-06-14 14:48:01 +0300137 IRQF_TRIGGER_RISING | IRQF_ONESHOT, "usb resume", NULL);
Amit Blay9b033682012-05-24 16:59:23 +0300138 if (ret < 0) {
139 dev_err(&pdev->dev, "could not register USB_RESUME IRQ.\n");
140 goto gpio_free;
141 }
142 disable_irq(wake_irq);
143 _udc_ctxt.wake_irq = wake_irq;
144
145 return 0;
146
147gpio_free:
148 gpio_free(_udc_ctxt.wake_gpio);
149 _udc_ctxt.wake_gpio = 0;
150 return ret;
151}
152
153static void ci13xxx_msm_uninstall_wake_gpio(struct platform_device *pdev)
154{
155 dev_dbg(&pdev->dev, "ci13xxx_msm_uninstall_wake_gpio\n");
156
157 if (_udc_ctxt.wake_gpio) {
158 gpio_free(_udc_ctxt.wake_gpio);
159 _udc_ctxt.wake_gpio = 0;
160 }
161}
162
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530163static int ci13xxx_msm_probe(struct platform_device *pdev)
164{
165 struct resource *res;
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530166 int ret;
167
168 dev_dbg(&pdev->dev, "ci13xxx_msm_probe\n");
169
170 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
171 if (!res) {
172 dev_err(&pdev->dev, "failed to get platform resource mem\n");
173 return -ENXIO;
174 }
175
Lena Salman20984752012-03-12 17:26:39 +0200176 _udc_ctxt.regs = ioremap(res->start, resource_size(res));
177 if (!_udc_ctxt.regs) {
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530178 dev_err(&pdev->dev, "ioremap failed\n");
179 return -ENOMEM;
180 }
181
Lena Salman20984752012-03-12 17:26:39 +0200182 ret = udc_probe(&ci13xxx_msm_udc_driver, &pdev->dev, _udc_ctxt.regs);
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530183 if (ret < 0) {
184 dev_err(&pdev->dev, "udc_probe failed\n");
185 goto iounmap;
186 }
187
Lena Salman20984752012-03-12 17:26:39 +0200188 _udc_ctxt.irq = platform_get_irq(pdev, 0);
189 if (_udc_ctxt.irq < 0) {
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530190 dev_err(&pdev->dev, "IRQ not found\n");
191 ret = -ENXIO;
192 goto udc_remove;
193 }
194
Amit Blay9b033682012-05-24 16:59:23 +0300195 res = platform_get_resource_byname(pdev, IORESOURCE_IO, "USB_RESUME");
196 if (res) {
197 ret = ci13xxx_msm_install_wake_gpio(pdev, res);
198 if (ret < 0) {
199 dev_err(&pdev->dev, "gpio irq install failed\n");
200 goto udc_remove;
201 }
202 }
203
Lena Salman20984752012-03-12 17:26:39 +0200204 ret = request_irq(_udc_ctxt.irq, msm_udc_irq, IRQF_SHARED, pdev->name,
205 pdev);
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530206 if (ret < 0) {
207 dev_err(&pdev->dev, "request_irq failed\n");
Amit Blay9b033682012-05-24 16:59:23 +0300208 goto gpio_uninstall;
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530209 }
210
Pavankumar Kondeti2d0cdcc2010-12-07 17:54:05 +0530211 pm_runtime_no_callbacks(&pdev->dev);
212 pm_runtime_enable(&pdev->dev);
213
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530214 return 0;
215
Amit Blay9b033682012-05-24 16:59:23 +0300216gpio_uninstall:
217 ci13xxx_msm_uninstall_wake_gpio(pdev);
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530218udc_remove:
219 udc_remove();
220iounmap:
Lena Salman20984752012-03-12 17:26:39 +0200221 iounmap(_udc_ctxt.regs);
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530222
223 return ret;
224}
225
Lena Salman20984752012-03-12 17:26:39 +0200226int ci13xxx_msm_remove(struct platform_device *pdev)
227{
228 pm_runtime_disable(&pdev->dev);
229 free_irq(_udc_ctxt.irq, pdev);
Amit Blay9b033682012-05-24 16:59:23 +0300230 ci13xxx_msm_uninstall_wake_gpio(pdev);
Lena Salman20984752012-03-12 17:26:39 +0200231 udc_remove();
232 iounmap(_udc_ctxt.regs);
233 return 0;
234}
235
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530236static struct platform_driver ci13xxx_msm_driver = {
237 .probe = ci13xxx_msm_probe,
238 .driver = { .name = "msm_hsusb", },
Lena Salman20984752012-03-12 17:26:39 +0200239 .remove = ci13xxx_msm_remove,
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530240};
Sebastian Andrzej Siewior86081d72011-06-29 16:41:55 +0300241MODULE_ALIAS("platform:msm_hsusb");
Pavankumar Kondeti33f82f32010-12-07 17:54:03 +0530242
243static int __init ci13xxx_msm_init(void)
244{
245 return platform_driver_register(&ci13xxx_msm_driver);
246}
247module_init(ci13xxx_msm_init);
Marc Kleine-Budde4703d2e2011-10-10 18:38:11 +0200248
Lena Salman20984752012-03-12 17:26:39 +0200249static void __exit ci13xxx_msm_exit(void)
250{
251 platform_driver_unregister(&ci13xxx_msm_driver);
252}
253module_exit(ci13xxx_msm_exit);
254
Marc Kleine-Budde4703d2e2011-10-10 18:38:11 +0200255MODULE_LICENSE("GPL v2");