arm: tegra: xmm: device set to null in unregister
[linux-2.6.git] / drivers / misc / tegra-baseband / bb-m7400.c
1 /*
2  * drivers/misc/tegra-baseband/bb-m7400.c
3  *
4  * Copyright (c) 2011, NVIDIA Corporation.
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but WITHOUT
12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14  * more details.
15  *
16  * You should have received a copy of the GNU General Public License along
17  * with this program; if not, write to the Free Software Foundation, Inc.,
18  * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20
21 #include <linux/resource.h>
22 #include <linux/platform_device.h>
23 #include <linux/delay.h>
24 #include <linux/gpio.h>
25 #include <linux/interrupt.h>
26 #include <linux/irq.h>
27 #include <linux/err.h>
28 #include <linux/device.h>
29 #include <linux/usb.h>
30 #include <linux/wakelock.h>
31 #include <linux/platform_data/tegra_usb.h>
32 #include <asm/mach-types.h>
33 #include <asm/mach/arch.h>
34 #include <mach/tegra-bb-power.h>
35 #include <mach/usb_phy.h>
36
37 #include "bb-power.h"
38
39 static struct tegra_bb_gpio_data m7400_gpios[] = {
40         { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_PWR_ON" }, true },
41         { { GPIO_INVALID, GPIOF_IN, "MDM_PWRSTATUS" }, true },
42         { { GPIO_INVALID, GPIOF_OUT_INIT_HIGH, "MDM_SERVICE" }, true },
43         { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_AWR" }, false },
44         { { GPIO_INVALID, GPIOF_IN, "MDM_USB_CWR" }, false },
45         { { GPIO_INVALID, GPIOF_IN, "MDM_RESOUT2" }, true },
46         { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_ARR" }, false },
47         { { GPIO_INVALID, 0, NULL }, false },   /* End of table */
48 };
49 static bool ehci_registered;
50 static int modem_status;
51 static int gpio_awr;
52 static int gpio_cwr;
53 static int gpio_arr;
54 static struct usb_device *m7400_usb_device;
55
56 static int gpio_wait_timeout(int gpio, int value, int timeout_msec)
57 {
58         int count;
59         for (count = 0; count < timeout_msec; ++count) {
60                 if (gpio_get_value(gpio) == value)
61                         return 0;
62                 mdelay(1);
63         }
64         return -1;
65 }
66
67 static int m7400_enum_handshake(void)
68 {
69         int retval = 0;
70
71         /* Wait for CP to indicate ready - by driving CWR high. */
72         if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
73                         pr_info("%s: Error: timeout waiting for modem resume.\n",
74                                                         __func__);
75                         retval = -1;
76         }
77
78         /* Signal AP ready - Drive AWR and ARR high. */
79         gpio_set_value(gpio_awr, 1);
80         gpio_set_value(gpio_arr, 1);
81
82         return retval;
83 }
84
85 static int m7400_apup_handshake(bool checkresponse)
86 {
87         int retval = 0;
88
89         /* Signal AP ready - Drive AWR and ARR high. */
90         gpio_set_value(gpio_awr, 1);
91         gpio_set_value(gpio_arr, 1);
92
93         if (checkresponse) {
94                 /* Wait for CP ack - by driving CWR high. */
95                 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
96                         pr_info("%s: Error: timeout waiting for modem ack.\n",
97                                                         __func__);
98                         retval = -1;
99                 }
100         }
101         return retval;
102 }
103
104 static void m7400_apdown_handshake(void)
105 {
106         /* Signal AP going down to modem - Drive AWR low. */
107         /* No need to wait for a CP response */
108         gpio_set_value(gpio_awr, 0);
109 }
110
111 static void m7400_l2_suspend(void)
112 {
113         /* Gets called for two cases :
114                 a) Port suspend.
115                 b) Bus suspend. */
116         if (modem_status == BBSTATE_L2)
117                 return;
118
119         /* Post bus suspend: Drive ARR low. */
120         gpio_set_value(gpio_arr, 0);
121         modem_status = BBSTATE_L2;
122 }
123
124 static void m7400_l2_resume(void)
125 {
126         /* Gets called for two cases :
127                 a) L2 resume.
128                 b) bus resume phase of L3 resume. */
129         if (modem_status == BBSTATE_L0)
130                 return;
131
132         /* Pre bus resume: Drive ARR high. */
133         gpio_set_value(gpio_arr, 1);
134
135         /* If host initiated resume - Wait for CP ack (CWR goes high). */
136         /* If device initiated resume - CWR will be already high. */
137         if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
138                 pr_info("%s: Error: timeout waiting for modem ack.\n",
139                                                 __func__);
140                 return;
141         }
142         modem_status = BBSTATE_L0;
143 }
144
145 static void m7400_l3_suspend(void)
146 {
147         m7400_apdown_handshake();
148         modem_status = BBSTATE_L3;
149 }
150
151 static void m7400_l3_resume(void)
152 {
153         m7400_apup_handshake(true);
154         modem_status = BBSTATE_L0;
155 }
156
157 static irqreturn_t m7400_wake_irq(int irq, void *dev_id)
158 {
159         struct usb_interface *intf;
160
161         switch (modem_status) {
162         case BBSTATE_L2:
163                 /* Resume usb host activity. */
164                 if (m7400_usb_device) {
165                         usb_lock_device(m7400_usb_device);
166                         intf = usb_ifnum_to_if(m7400_usb_device, 0);
167                         usb_autopm_get_interface(intf);
168                         usb_autopm_put_interface(intf);
169                         usb_unlock_device(m7400_usb_device);
170                 }
171                 break;
172         default:
173                 break;
174         }
175
176         return IRQ_HANDLED;
177 }
178
179 static int m7400_power(int code)
180 {
181         switch (code) {
182         case PWRSTATE_L2L3:
183         m7400_l3_suspend();
184         break;
185         case PWRSTATE_L3L0:
186         m7400_l3_resume();
187         break;
188         default:
189         break;
190         }
191         return 0;
192 }
193
194 static void m7400_ehci_customize(struct platform_device *pdev)
195 {
196         struct tegra_usb_platform_data *ehci_pdata;
197
198         ehci_pdata = (struct tegra_usb_platform_data *)
199                         pdev->dev.platform_data;
200
201         /* Register PHY callbacks */
202         ehci_pdata->ops->post_suspend = m7400_l2_suspend;
203         ehci_pdata->ops->pre_resume = m7400_l2_resume;
204
205         /* Override required settings */
206         ehci_pdata->u_data.host.power_off_on_suspend = false;
207 }
208
209 static int m7400_attrib_write(struct device *dev, int value)
210 {
211         struct tegra_bb_pdata *pdata;
212         static struct platform_device *ehci_device;
213         static bool first_enum = true;
214
215         if (value > 1 || (!ehci_registered && !value)) {
216                 /* Supported values are 0/1. */
217                 return -1;
218         }
219
220         pdata = (struct tegra_bb_pdata *) dev->platform_data;
221         if (value) {
222
223                 /* Check readiness for enumeration */
224                 if (first_enum)
225                         first_enum = false;
226                 else
227                         m7400_enum_handshake();
228
229                 /* Register ehci controller */
230                 ehci_device = pdata->ehci_register();
231                 if (ehci_device == NULL) {
232                         pr_info("%s - Error: ehci register failed.\n",
233                                                          __func__);
234                         return -1;
235                 }
236
237                 /* Customize PHY setup/callbacks */
238                 m7400_ehci_customize(ehci_device);
239
240                 ehci_registered = true;
241         } else {
242                 /* Unregister ehci controller */
243                 if (ehci_device != NULL)
244                         pdata->ehci_unregister(&ehci_device);
245
246                 /* Signal AP going down */
247                 m7400_apdown_handshake();
248                 ehci_registered = false;
249         }
250
251         return 0;
252 }
253
254 static int m7400_registered(struct usb_device *udev)
255 {
256         m7400_usb_device = udev;
257         modem_status = BBSTATE_L0;
258         return 0;
259 }
260
261 static struct tegra_bb_gpio_irqdata m7400_gpioirqs[] = {
262         { GPIO_INVALID, "tegra_bb_wake", m7400_wake_irq,
263                                 IRQF_TRIGGER_RISING, true, NULL },
264         { GPIO_INVALID, NULL, NULL, 0, NULL },  /* End of table */
265 };
266
267 static struct tegra_bb_power_gdata m7400_gdata = {
268         .gpio = m7400_gpios,
269         .gpioirq = m7400_gpioirqs,
270 };
271
272 static struct tegra_bb_power_mdata m7400_mdata = {
273         .vid = 0x04cc,
274         .pid = 0x230f,
275         .wake_capable = true,
276         .autosuspend_ready = true,
277         .reg_cb = m7400_registered,
278 };
279
280 static struct tegra_bb_power_data m7400_data = {
281         .gpio_data = &m7400_gdata,
282         .modem_data = &m7400_mdata,
283 };
284
285 static void *m7400_init(void *pdata)
286 {
287         struct tegra_bb_pdata *platdata = (struct tegra_bb_pdata *) pdata;
288         union tegra_bb_gpio_id *id = platdata->id;
289
290         /* Fill the gpio ids allocated by hardware */
291         m7400_gpios[0].data.gpio = id->m7400.pwr_on;
292         m7400_gpios[1].data.gpio = id->m7400.pwr_status;
293         m7400_gpios[2].data.gpio = id->m7400.service;
294         m7400_gpios[3].data.gpio = id->m7400.usb_awr;
295         m7400_gpios[4].data.gpio = id->m7400.usb_cwr;
296         m7400_gpios[5].data.gpio = id->m7400.resout2;
297         m7400_gpios[6].data.gpio = id->m7400.uart_awr;
298         m7400_gpioirqs[0].id = id->m7400.usb_cwr;
299
300         if (!platdata->ehci_register || !platdata->ehci_unregister) {
301                 pr_info("%s - Error: ehci reg/unreg functions missing.\n"
302                                                         , __func__);
303                 return 0;
304         }
305
306         gpio_awr = m7400_gpios[3].data.gpio;
307         gpio_cwr = m7400_gpios[4].data.gpio;
308         gpio_arr = m7400_gpios[6].data.gpio;
309         if (gpio_awr == GPIO_INVALID || gpio_cwr == GPIO_INVALID
310                         || gpio_arr == GPIO_INVALID) {
311                 pr_info("%s - Error: Invalid gpio data.\n", __func__);
312                 return 0;
313         }
314
315         ehci_registered = false;
316         modem_status = BBSTATE_UNKNOWN;
317         return (void *) &m7400_data;
318 }
319
320 static void *m7400_deinit(void)
321 {
322         return (void *) &m7400_data;
323 }
324
325 static struct tegra_bb_callback m7400_callbacks = {
326         .init = m7400_init,
327         .deinit = m7400_deinit,
328         .attrib = m7400_attrib_write,
329 #ifdef CONFIG_PM
330         .power = m7400_power,
331 #endif
332 };
333
334 void *m7400_get_cblist(void)
335 {
336         return (void *) &m7400_callbacks;
337 }