diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/misc/tegra-baseband/bb-m7400.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/misc/tegra-baseband/bb-m7400.c')
-rw-r--r-- | drivers/misc/tegra-baseband/bb-m7400.c | 340 |
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 | |||
37 | static 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 | }; | ||
47 | static bool ehci_registered; | ||
48 | static int modem_status; | ||
49 | static int gpio_awr; | ||
50 | static int gpio_cwr; | ||
51 | static int gpio_arr; | ||
52 | static struct usb_device *m7400_usb_device; | ||
53 | |||
54 | static 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 | |||
65 | static 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 | |||
83 | static 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 | |||
102 | static 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 | |||
109 | static 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 | |||
123 | static 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 | |||
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_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 | |||
212 | static 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 | |||
257 | static int m7400_registered(struct usb_device *udev) | ||
258 | { | ||
259 | m7400_usb_device = udev; | ||
260 | modem_status = BBSTATE_L0; | ||
261 | return 0; | ||
262 | } | ||
263 | |||
264 | static 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 | |||
270 | static struct tegra_bb_power_gdata m7400_gdata = { | ||
271 | .gpio = m7400_gpios, | ||
272 | .gpioirq = m7400_gpioirqs, | ||
273 | }; | ||
274 | |||
275 | static 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 | |||
283 | static struct tegra_bb_power_data m7400_data = { | ||
284 | .gpio_data = &m7400_gdata, | ||
285 | .modem_data = &m7400_mdata, | ||
286 | }; | ||
287 | |||
288 | static 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 | |||
323 | static void *m7400_deinit(void) | ||
324 | { | ||
325 | return (void *) &m7400_data; | ||
326 | } | ||
327 | |||
328 | static 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 | |||
337 | void *m7400_get_cblist(void) | ||
338 | { | ||
339 | return (void *) &m7400_callbacks; | ||
340 | } | ||