aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/misc/tegra-baseband/bb-m7400.c
diff options
context:
space:
mode:
authorJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
committerJonathan Herman <hermanjl@cs.unc.edu>2013-01-22 10:38:37 -0500
commitfcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch)
treea57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/tegra-baseband/bb-m7400.c
parent8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff)
Added missing tegra files.HEADmaster
Diffstat (limited to 'drivers/misc/tegra-baseband/bb-m7400.c')
-rw-r--r--drivers/misc/tegra-baseband/bb-m7400.c340
1 files changed, 340 insertions, 0 deletions
diff --git a/drivers/misc/tegra-baseband/bb-m7400.c b/drivers/misc/tegra-baseband/bb-m7400.c
new file mode 100644
index 00000000000..5808a6e321c
--- /dev/null
+++ b/drivers/misc/tegra-baseband/bb-m7400.c
@@ -0,0 +1,340 @@
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 <asm/mach-types.h>
32#include <asm/mach/arch.h>
33#include <mach/tegra-bb-power.h>
34#include <mach/usb_phy.h>
35#include "bb-power.h"
36
37static struct tegra_bb_gpio_data m7400_gpios[] = {
38 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_PWR_ON" }, true },
39 { { GPIO_INVALID, GPIOF_IN, "MDM_PWRSTATUS" }, true },
40 { { GPIO_INVALID, GPIOF_OUT_INIT_HIGH, "MDM_SERVICE" }, true },
41 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_AWR" }, false },
42 { { GPIO_INVALID, GPIOF_IN, "MDM_USB_CWR" }, false },
43 { { GPIO_INVALID, GPIOF_IN, "MDM_RESOUT2" }, true },
44 { { GPIO_INVALID, GPIOF_OUT_INIT_LOW, "MDM_USB_ARR" }, false },
45 { { GPIO_INVALID, 0, NULL }, false }, /* End of table */
46};
47static bool ehci_registered;
48static int modem_status;
49static int gpio_awr;
50static int gpio_cwr;
51static int gpio_arr;
52static struct usb_device *m7400_usb_device;
53
54static int gpio_wait_timeout(int gpio, int value, int timeout_msec)
55{
56 int count;
57 for (count = 0; count < timeout_msec; ++count) {
58 if (gpio_get_value(gpio) == value)
59 return 0;
60 mdelay(1);
61 }
62 return -1;
63}
64
65static int m7400_enum_handshake(void)
66{
67 int retval = 0;
68
69 /* Wait for CP to indicate ready - by driving CWR high. */
70 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
71 pr_info("%s: Error: timeout waiting for modem resume.\n",
72 __func__);
73 retval = -1;
74 }
75
76 /* Signal AP ready - Drive AWR and ARR high. */
77 gpio_set_value(gpio_awr, 1);
78 gpio_set_value(gpio_arr, 1);
79
80 return retval;
81}
82
83static int m7400_apup_handshake(bool checkresponse)
84{
85 int retval = 0;
86
87 /* Signal AP ready - Drive AWR and ARR high. */
88 gpio_set_value(gpio_awr, 1);
89 gpio_set_value(gpio_arr, 1);
90
91 if (checkresponse) {
92 /* Wait for CP ack - by driving CWR high. */
93 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
94 pr_info("%s: Error: timeout waiting for modem ack.\n",
95 __func__);
96 retval = -1;
97 }
98 }
99 return retval;
100}
101
102static void m7400_apdown_handshake(void)
103{
104 /* Signal AP going down to modem - Drive AWR low. */
105 /* No need to wait for a CP response */
106 gpio_set_value(gpio_awr, 0);
107}
108
109static int m7400_l2_suspend(void)
110{
111 /* Gets called for two cases :
112 a) Port suspend.
113 b) Bus suspend. */
114 if (modem_status == BBSTATE_L2)
115 return 0;
116
117 /* Post bus suspend: Drive ARR low. */
118 gpio_set_value(gpio_arr, 0);
119 modem_status = BBSTATE_L2;
120 return 0;
121}
122
123static int m7400_l2_resume(void)
124{
125 /* Gets called for two cases :
126 a) L2 resume.
127 b) bus resume phase of L3 resume. */
128 if (modem_status == BBSTATE_L0)
129 return 0;
130
131 /* Pre bus resume: Drive ARR high. */
132 gpio_set_value(gpio_arr, 1);
133
134 /* If host initiated resume - Wait for CP ack (CWR goes high). */
135 /* If device initiated resume - CWR will be already high. */
136 if (gpio_wait_timeout(gpio_cwr, 1, 10) != 0) {
137 pr_info("%s: Error: timeout waiting for modem ack.\n",
138 __func__);
139 return -1;
140 }
141 modem_status = BBSTATE_L0;
142 return 0;
143}
144
145static void m7400_l3_suspend(void)
146{
147 m7400_apdown_handshake();
148 modem_status = BBSTATE_L3;
149}
150
151static void m7400_l3_resume(void)
152{
153 m7400_apup_handshake(true);
154 modem_status = BBSTATE_L0;
155}
156
157static 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
179static 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
194static void m7400_ehci_customize(struct platform_device *pdev)
195{
196 struct tegra_ehci_platform_data *ehci_pdata;
197 struct tegra_uhsic_config *hsic_config;
198
199 ehci_pdata = (struct tegra_ehci_platform_data *)
200 pdev->dev.platform_data;
201 hsic_config = (struct tegra_uhsic_config *)
202 ehci_pdata->phy_config;
203
204 /* Register PHY callbacks */
205 hsic_config->postsuspend = m7400_l2_suspend;
206 hsic_config->preresume = m7400_l2_resume;
207
208 /* Override required settings */
209 ehci_pdata->power_down_on_bus_suspend = 0;
210}
211
212static int m7400_attrib_write(struct device *dev, int value)
213{
214 struct tegra_bb_pdata *pdata;
215 static struct platform_device *ehci_device;
216 static bool first_enum = true;
217
218 if (value > 1 || (!ehci_registered && !value)) {
219 /* Supported values are 0/1. */
220 return -1;
221 }
222
223 pdata = (struct tegra_bb_pdata *) dev->platform_data;
224 if (value) {
225
226 /* Check readiness for enumeration */
227 if (first_enum)
228 first_enum = false;
229 else
230 m7400_enum_handshake();
231
232 /* Register ehci controller */
233 ehci_device = pdata->ehci_register();
234 if (ehci_device == NULL) {
235 pr_info("%s - Error: ehci register failed.\n",
236 __func__);
237 return -1;
238 }
239
240 /* Customize PHY setup/callbacks */
241 m7400_ehci_customize(ehci_device);
242
243 ehci_registered = true;
244 } else {
245 /* Unregister ehci controller */
246 if (ehci_device != NULL)
247 pdata->ehci_unregister(ehci_device);
248
249 /* Signal AP going down */
250 m7400_apdown_handshake();
251 ehci_registered = false;
252 }
253
254 return 0;
255}
256
257static int m7400_registered(struct usb_device *udev)
258{
259 m7400_usb_device = udev;
260 modem_status = BBSTATE_L0;
261 return 0;
262}
263
264static struct tegra_bb_gpio_irqdata m7400_gpioirqs[] = {
265 { GPIO_INVALID, "tegra_bb_wake", m7400_wake_irq,
266 IRQF_TRIGGER_RISING, true, NULL },
267 { GPIO_INVALID, NULL, NULL, 0, NULL }, /* End of table */
268};
269
270static struct tegra_bb_power_gdata m7400_gdata = {
271 .gpio = m7400_gpios,
272 .gpioirq = m7400_gpioirqs,
273};
274
275static struct tegra_bb_power_mdata m7400_mdata = {
276 .vid = 0x04cc,
277 .pid = 0x230f,
278 .wake_capable = true,
279 .autosuspend_ready = true,
280 .reg_cb = m7400_registered,
281};
282
283static struct tegra_bb_power_data m7400_data = {
284 .gpio_data = &m7400_gdata,
285 .modem_data = &m7400_mdata,
286};
287
288static void *m7400_init(void *pdata)
289{
290 struct tegra_bb_pdata *platdata = (struct tegra_bb_pdata *) pdata;
291 union tegra_bb_gpio_id *id = platdata->id;
292
293 /* Fill the gpio ids allocated by hardware */
294 m7400_gpios[0].data.gpio = id->m7400.pwr_on;
295 m7400_gpios[1].data.gpio = id->m7400.pwr_status;
296 m7400_gpios[2].data.gpio = id->m7400.service;
297 m7400_gpios[3].data.gpio = id->m7400.usb_awr;
298 m7400_gpios[4].data.gpio = id->m7400.usb_cwr;
299 m7400_gpios[5].data.gpio = id->m7400.resout2;
300 m7400_gpios[6].data.gpio = id->m7400.uart_awr;
301 m7400_gpioirqs[0].id = id->m7400.usb_cwr;
302
303 if (!platdata->ehci_register || !platdata->ehci_unregister) {
304 pr_info("%s - Error: ehci reg/unreg functions missing.\n"
305 , __func__);
306 return 0;
307 }
308
309 gpio_awr = m7400_gpios[3].data.gpio;
310 gpio_cwr = m7400_gpios[4].data.gpio;
311 gpio_arr = m7400_gpios[6].data.gpio;
312 if (gpio_awr == GPIO_INVALID || gpio_cwr == GPIO_INVALID
313 || gpio_arr == GPIO_INVALID) {
314 pr_info("%s - Error: Invalid gpio data.\n", __func__);
315 return 0;
316 }
317
318 ehci_registered = false;
319 modem_status = BBSTATE_UNKNOWN;
320 return (void *) &m7400_data;
321}
322
323static void *m7400_deinit(void)
324{
325 return (void *) &m7400_data;
326}
327
328static struct tegra_bb_callback m7400_callbacks = {
329 .init = m7400_init,
330 .deinit = m7400_deinit,
331 .attrib = m7400_attrib_write,
332#ifdef CONFIG_PM
333 .power = m7400_power,
334#endif
335};
336
337void *m7400_get_cblist(void)
338{
339 return (void *) &m7400_callbacks;
340}