diff options
Diffstat (limited to 'arch/arm/mach-tegra/baseband-xmm-power.c')
-rw-r--r-- | arch/arm/mach-tegra/baseband-xmm-power.c | 1063 |
1 files changed, 1063 insertions, 0 deletions
diff --git a/arch/arm/mach-tegra/baseband-xmm-power.c b/arch/arm/mach-tegra/baseband-xmm-power.c new file mode 100644 index 00000000000..7d5c527cab8 --- /dev/null +++ b/arch/arm/mach-tegra/baseband-xmm-power.c | |||
@@ -0,0 +1,1063 @@ | |||
1 | /* | ||
2 | * arch/arm/mach-tegra/baseband-xmm-power.c | ||
3 | * | ||
4 | * Copyright (C) 2011 NVIDIA Corporation | ||
5 | * | ||
6 | * This software is licensed under the terms of the GNU General Public | ||
7 | * License version 2, as published by the Free Software Foundation, and | ||
8 | * may be copied, distributed, and modified under those terms. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | */ | ||
16 | |||
17 | #include <linux/kernel.h> | ||
18 | #include <linux/init.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/platform_device.h> | ||
22 | #include <linux/gpio.h> | ||
23 | #include <linux/interrupt.h> | ||
24 | #include <linux/workqueue.h> | ||
25 | #include <linux/slab.h> | ||
26 | #include <linux/delay.h> | ||
27 | #include <linux/fs.h> | ||
28 | #include <linux/uaccess.h> | ||
29 | #include <linux/wakelock.h> | ||
30 | #include <linux/spinlock.h> | ||
31 | #include <linux/usb.h> | ||
32 | #include <linux/pm_runtime.h> | ||
33 | #include <mach/usb_phy.h> | ||
34 | #include "board.h" | ||
35 | #include "devices.h" | ||
36 | #include "gpio-names.h" | ||
37 | #include "baseband-xmm-power.h" | ||
38 | |||
39 | MODULE_LICENSE("GPL"); | ||
40 | |||
41 | unsigned long modem_ver = XMM_MODEM_VER_1130; | ||
42 | EXPORT_SYMBOL(modem_ver); | ||
43 | |||
44 | unsigned long modem_flash; | ||
45 | EXPORT_SYMBOL(modem_flash); | ||
46 | |||
47 | unsigned long modem_pm = 1; | ||
48 | EXPORT_SYMBOL(modem_pm); | ||
49 | |||
50 | unsigned long enum_delay_ms = 1000; /* ignored if !modem_flash */ | ||
51 | |||
52 | module_param(modem_ver, ulong, 0644); | ||
53 | MODULE_PARM_DESC(modem_ver, | ||
54 | "baseband xmm power - modem software version"); | ||
55 | module_param(modem_flash, ulong, 0644); | ||
56 | MODULE_PARM_DESC(modem_flash, | ||
57 | "baseband xmm power - modem flash (1 = flash, 0 = flashless)"); | ||
58 | module_param(modem_pm, ulong, 0644); | ||
59 | MODULE_PARM_DESC(modem_pm, | ||
60 | "baseband xmm power - modem power management (1 = pm, 0 = no pm)"); | ||
61 | module_param(enum_delay_ms, ulong, 0644); | ||
62 | MODULE_PARM_DESC(enum_delay_ms, | ||
63 | "baseband xmm power - delay in ms between modem on and enumeration"); | ||
64 | |||
65 | static struct usb_device_id xmm_pm_ids[] = { | ||
66 | { USB_DEVICE(VENDOR_ID, PRODUCT_ID), | ||
67 | .driver_info = 0 }, | ||
68 | {} | ||
69 | }; | ||
70 | |||
71 | |||
72 | static struct gpio tegra_baseband_gpios[] = { | ||
73 | { -1, GPIOF_OUT_INIT_LOW, "BB_RSTn" }, | ||
74 | { -1, GPIOF_OUT_INIT_LOW, "BB_ON" }, | ||
75 | { -1, GPIOF_OUT_INIT_LOW, "IPC_BB_WAKE" }, | ||
76 | { -1, GPIOF_IN, "IPC_AP_WAKE" }, | ||
77 | { -1, GPIOF_OUT_INIT_HIGH, "IPC_HSIC_ACTIVE" }, | ||
78 | { -1, GPIOF_IN, "IPC_HSIC_SUS_REQ" }, | ||
79 | }; | ||
80 | |||
81 | static enum { | ||
82 | IPC_AP_WAKE_UNINIT, | ||
83 | IPC_AP_WAKE_IRQ_READY, | ||
84 | IPC_AP_WAKE_INIT1, | ||
85 | IPC_AP_WAKE_INIT2, | ||
86 | IPC_AP_WAKE_L, | ||
87 | IPC_AP_WAKE_H, | ||
88 | } ipc_ap_wake_state; | ||
89 | |||
90 | enum baseband_xmm_powerstate_t baseband_xmm_powerstate; | ||
91 | static struct workqueue_struct *workqueue; | ||
92 | static struct work_struct init1_work; | ||
93 | static struct work_struct init2_work; | ||
94 | static struct work_struct L2_resume_work; | ||
95 | static struct baseband_power_platform_data *baseband_power_driver_data; | ||
96 | static bool register_hsic_device; | ||
97 | static struct wake_lock wakelock; | ||
98 | static struct usb_device *usbdev; | ||
99 | static bool CP_initiated_L2toL0; | ||
100 | static bool modem_power_on; | ||
101 | static int power_onoff; | ||
102 | static int reenable_autosuspend; | ||
103 | static struct work_struct autopm_resume_work; | ||
104 | static bool wakeup_pending; | ||
105 | static bool modem_sleep_flag; | ||
106 | static spinlock_t xmm_lock; | ||
107 | static DEFINE_MUTEX(xmm_onoff_mutex); | ||
108 | |||
109 | static void baseband_xmm_power_L2_resume(void); | ||
110 | static int baseband_xmm_power_driver_handle_resume( | ||
111 | struct baseband_power_platform_data *data); | ||
112 | |||
113 | static int baseband_modem_power_on(struct baseband_power_platform_data *data) | ||
114 | { | ||
115 | /* set IPC_HSIC_ACTIVE active */ | ||
116 | gpio_set_value(baseband_power_driver_data-> | ||
117 | modem.xmm.ipc_hsic_active, 1); | ||
118 | |||
119 | /* wait 20 ms */ | ||
120 | mdelay(20); | ||
121 | |||
122 | /* reset / power on sequence */ | ||
123 | mdelay(40); | ||
124 | gpio_set_value(data->modem.xmm.bb_rst, 1); | ||
125 | mdelay(1); | ||
126 | gpio_set_value(data->modem.xmm.bb_on, 1); | ||
127 | udelay(40); | ||
128 | gpio_set_value(data->modem.xmm.bb_on, 0); | ||
129 | |||
130 | return 0; | ||
131 | } | ||
132 | |||
133 | static int baseband_xmm_power_on(struct platform_device *device) | ||
134 | { | ||
135 | struct baseband_power_platform_data *data | ||
136 | = (struct baseband_power_platform_data *) | ||
137 | device->dev.platform_data; | ||
138 | int ret; | ||
139 | |||
140 | pr_debug("%s {\n", __func__); | ||
141 | |||
142 | /* check for platform data */ | ||
143 | if (!data) { | ||
144 | pr_err("%s: !data\n", __func__); | ||
145 | return -EINVAL; | ||
146 | } | ||
147 | if (baseband_xmm_powerstate != BBXMM_PS_UNINIT) | ||
148 | return -EINVAL; | ||
149 | |||
150 | /* reset the state machine */ | ||
151 | baseband_xmm_powerstate = BBXMM_PS_INIT; | ||
152 | modem_sleep_flag = false; | ||
153 | |||
154 | if (modem_ver < XMM_MODEM_VER_1130) | ||
155 | ipc_ap_wake_state = IPC_AP_WAKE_INIT1; | ||
156 | else | ||
157 | ipc_ap_wake_state = IPC_AP_WAKE_INIT2; | ||
158 | |||
159 | pr_debug("%s wake_st(%d) modem version %d\n", __func__, | ||
160 | ipc_ap_wake_state, modem_ver); | ||
161 | |||
162 | /* register usb host controller */ | ||
163 | if (!modem_flash) { | ||
164 | pr_debug("%s - %d\n", __func__, __LINE__); | ||
165 | /* register usb host controller only once */ | ||
166 | if (register_hsic_device) { | ||
167 | pr_debug("%s: register usb host controller\n", | ||
168 | __func__); | ||
169 | modem_power_on = true; | ||
170 | if (data->hsic_register) | ||
171 | data->modem.xmm.hsic_device = | ||
172 | data->hsic_register(); | ||
173 | else | ||
174 | pr_err("%s: hsic_register is missing\n", | ||
175 | __func__); | ||
176 | register_hsic_device = false; | ||
177 | } else { | ||
178 | /* register usb host controller */ | ||
179 | if (data->hsic_register) | ||
180 | data->modem.xmm.hsic_device = | ||
181 | data->hsic_register(); | ||
182 | /* turn on modem */ | ||
183 | pr_debug("%s call baseband_modem_power_on\n", __func__); | ||
184 | baseband_modem_power_on(data); | ||
185 | } | ||
186 | } | ||
187 | ret = enable_irq_wake(gpio_to_irq(data->modem.xmm.ipc_ap_wake)); | ||
188 | if (ret < 0) | ||
189 | pr_err("%s: enable_irq_wake error\n", __func__); | ||
190 | |||
191 | pr_debug("%s }\n", __func__); | ||
192 | |||
193 | return 0; | ||
194 | } | ||
195 | |||
196 | static int baseband_xmm_power_off(struct platform_device *device) | ||
197 | { | ||
198 | struct baseband_power_platform_data *data; | ||
199 | int ret; | ||
200 | unsigned long flags; | ||
201 | |||
202 | pr_debug("%s {\n", __func__); | ||
203 | |||
204 | if (baseband_xmm_powerstate == BBXMM_PS_UNINIT) | ||
205 | return -EINVAL; | ||
206 | /* check for device / platform data */ | ||
207 | if (!device) { | ||
208 | pr_err("%s: !device\n", __func__); | ||
209 | return -EINVAL; | ||
210 | } | ||
211 | data = (struct baseband_power_platform_data *) | ||
212 | device->dev.platform_data; | ||
213 | if (!data) { | ||
214 | pr_err("%s: !data\n", __func__); | ||
215 | return -EINVAL; | ||
216 | } | ||
217 | |||
218 | ipc_ap_wake_state = IPC_AP_WAKE_UNINIT; | ||
219 | ret = disable_irq_wake(gpio_to_irq(data->modem.xmm.ipc_ap_wake)); | ||
220 | if (ret < 0) | ||
221 | pr_err("%s: disable_irq_wake error\n", __func__); | ||
222 | |||
223 | /* unregister usb host controller */ | ||
224 | if (data->hsic_unregister) | ||
225 | data->hsic_unregister(data->modem.xmm.hsic_device); | ||
226 | else | ||
227 | pr_err("%s: hsic_unregister is missing\n", __func__); | ||
228 | |||
229 | |||
230 | /* set IPC_HSIC_ACTIVE low */ | ||
231 | gpio_set_value(baseband_power_driver_data-> | ||
232 | modem.xmm.ipc_hsic_active, 0); | ||
233 | |||
234 | /* wait 20 ms */ | ||
235 | mdelay(20); | ||
236 | |||
237 | /* drive bb_rst low */ | ||
238 | gpio_set_value(data->modem.xmm.bb_rst, 0); | ||
239 | mdelay(1); | ||
240 | |||
241 | spin_lock_irqsave(&xmm_lock, flags); | ||
242 | baseband_xmm_powerstate = BBXMM_PS_UNINIT; | ||
243 | wakeup_pending = false; | ||
244 | modem_sleep_flag = false; | ||
245 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
246 | /* start registration process once again on xmm on */ | ||
247 | register_hsic_device = true; | ||
248 | pr_debug("%s }\n", __func__); | ||
249 | |||
250 | return 0; | ||
251 | } | ||
252 | |||
253 | static ssize_t baseband_xmm_onoff(struct device *dev, | ||
254 | struct device_attribute *attr, | ||
255 | const char *buf, size_t count) | ||
256 | { | ||
257 | int pwr; | ||
258 | int size; | ||
259 | struct platform_device *device = to_platform_device(dev); | ||
260 | |||
261 | mutex_lock(&xmm_onoff_mutex); | ||
262 | |||
263 | pr_debug("%s\n", __func__); | ||
264 | |||
265 | /* check input */ | ||
266 | if (buf == NULL) { | ||
267 | pr_err("%s: buf NULL\n", __func__); | ||
268 | mutex_unlock(&xmm_onoff_mutex); | ||
269 | return -EINVAL; | ||
270 | } | ||
271 | pr_debug("%s: count=%d\n", __func__, count); | ||
272 | |||
273 | /* parse input */ | ||
274 | size = sscanf(buf, "%d", &pwr); | ||
275 | if (size != 1) { | ||
276 | pr_err("%s: size=%d -EINVAL\n", __func__, size); | ||
277 | mutex_unlock(&xmm_onoff_mutex); | ||
278 | return -EINVAL; | ||
279 | } | ||
280 | |||
281 | if (power_onoff == pwr) { | ||
282 | pr_err("%s: Ignored, due to same CP power state(%d)\n", | ||
283 | __func__, power_onoff); | ||
284 | mutex_unlock(&xmm_onoff_mutex); | ||
285 | return -EINVAL; | ||
286 | } | ||
287 | power_onoff = pwr; | ||
288 | pr_debug("%s power_onoff=%d\n", __func__, power_onoff); | ||
289 | |||
290 | if (power_onoff == 0) | ||
291 | baseband_xmm_power_off(device); | ||
292 | else if (power_onoff == 1) | ||
293 | baseband_xmm_power_on(device); | ||
294 | |||
295 | mutex_unlock(&xmm_onoff_mutex); | ||
296 | |||
297 | return count; | ||
298 | } | ||
299 | |||
300 | static DEVICE_ATTR(xmm_onoff, S_IRUSR | S_IWUSR | S_IRGRP, | ||
301 | NULL, baseband_xmm_onoff); | ||
302 | |||
303 | |||
304 | void baseband_xmm_set_power_status(unsigned int status) | ||
305 | { | ||
306 | struct baseband_power_platform_data *data = baseband_power_driver_data; | ||
307 | int value = 0; | ||
308 | unsigned long flags; | ||
309 | |||
310 | pr_debug("%s\n", __func__); | ||
311 | |||
312 | if (baseband_xmm_powerstate == status) | ||
313 | return; | ||
314 | |||
315 | switch (status) { | ||
316 | case BBXMM_PS_L0: | ||
317 | if (modem_sleep_flag) { | ||
318 | pr_info("%s Resume from L3 without calling resume" | ||
319 | "function\n", __func__); | ||
320 | baseband_xmm_power_driver_handle_resume(data); | ||
321 | } | ||
322 | pr_info("L0\n"); | ||
323 | value = gpio_get_value(data->modem.xmm.ipc_hsic_active); | ||
324 | pr_debug("before L0 ipc_hsic_active=%d\n", value); | ||
325 | if (!value) { | ||
326 | pr_debug("before L0 gpio set ipc_hsic_active=1 ->\n"); | ||
327 | gpio_set_value(data->modem.xmm.ipc_hsic_active, 1); | ||
328 | } | ||
329 | if (modem_power_on) { | ||
330 | modem_power_on = false; | ||
331 | baseband_modem_power_on(data); | ||
332 | } | ||
333 | |||
334 | if (!wake_lock_active(&wakelock)) | ||
335 | wake_lock(&wakelock); | ||
336 | |||
337 | pr_debug("gpio host active high->\n"); | ||
338 | break; | ||
339 | case BBXMM_PS_L2: | ||
340 | pr_info("L2\n"); | ||
341 | wake_unlock(&wakelock); | ||
342 | modem_sleep_flag = true; | ||
343 | break; | ||
344 | case BBXMM_PS_L3: | ||
345 | if (baseband_xmm_powerstate == BBXMM_PS_L2TOL0) { | ||
346 | if (!data->modem.xmm.ipc_ap_wake) { | ||
347 | spin_lock_irqsave(&xmm_lock, flags); | ||
348 | wakeup_pending = true; | ||
349 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
350 | pr_info("%s: L2 race condition-CP wakeup" | ||
351 | " pending\n", __func__); | ||
352 | } | ||
353 | } | ||
354 | pr_info("L3\n"); | ||
355 | if (wake_lock_active(&wakelock)) { | ||
356 | pr_info("%s: releasing wakelock before L3\n", | ||
357 | __func__); | ||
358 | wake_unlock(&wakelock); | ||
359 | } | ||
360 | gpio_set_value(data->modem.xmm.ipc_hsic_active, 0); | ||
361 | pr_debug("gpio host active low->\n"); | ||
362 | break; | ||
363 | case BBXMM_PS_L2TOL0: | ||
364 | /* do this only from L2 state */ | ||
365 | if (baseband_xmm_powerstate == BBXMM_PS_L2) { | ||
366 | baseband_xmm_powerstate = status; | ||
367 | pr_debug("BB XMM POWER STATE = %d\n", status); | ||
368 | baseband_xmm_power_L2_resume(); | ||
369 | } | ||
370 | default: | ||
371 | break; | ||
372 | } | ||
373 | baseband_xmm_powerstate = status; | ||
374 | pr_debug("BB XMM POWER STATE = %d\n", status); | ||
375 | } | ||
376 | EXPORT_SYMBOL_GPL(baseband_xmm_set_power_status); | ||
377 | |||
378 | irqreturn_t baseband_xmm_power_ipc_ap_wake_irq(int irq, void *dev_id) | ||
379 | { | ||
380 | int value; | ||
381 | |||
382 | value = gpio_get_value(baseband_power_driver_data-> | ||
383 | modem.xmm.ipc_ap_wake); | ||
384 | |||
385 | pr_debug("%s g(%d), wake_st(%d)\n", __func__, value, ipc_ap_wake_state); | ||
386 | |||
387 | if (ipc_ap_wake_state < IPC_AP_WAKE_IRQ_READY) { | ||
388 | pr_err("%s - spurious irq\n", __func__); | ||
389 | } else if (ipc_ap_wake_state == IPC_AP_WAKE_IRQ_READY) { | ||
390 | if (!value) { | ||
391 | pr_debug("%s - IPC_AP_WAKE_INIT1" | ||
392 | " - got falling edge\n", | ||
393 | __func__); | ||
394 | /* go to IPC_AP_WAKE_INIT1 state */ | ||
395 | ipc_ap_wake_state = IPC_AP_WAKE_INIT1; | ||
396 | /* queue work */ | ||
397 | queue_work(workqueue, &init1_work); | ||
398 | } else { | ||
399 | pr_debug("%s - IPC_AP_WAKE_INIT1" | ||
400 | " - wait for falling edge\n", | ||
401 | __func__); | ||
402 | } | ||
403 | } else if (ipc_ap_wake_state == IPC_AP_WAKE_INIT1) { | ||
404 | if (!value) { | ||
405 | pr_debug("%s - IPC_AP_WAKE_INIT2" | ||
406 | " - wait for rising edge\n", | ||
407 | __func__); | ||
408 | } else { | ||
409 | pr_debug("%s - IPC_AP_WAKE_INIT2" | ||
410 | " - got rising edge\n", | ||
411 | __func__); | ||
412 | /* go to IPC_AP_WAKE_INIT2 state */ | ||
413 | ipc_ap_wake_state = IPC_AP_WAKE_INIT2; | ||
414 | /* queue work */ | ||
415 | queue_work(workqueue, &init2_work); | ||
416 | } | ||
417 | } else { | ||
418 | if (!value) { | ||
419 | pr_debug("%s - falling\n", __func__); | ||
420 | /* [ver < 1130] gpio protocol falling edge */ | ||
421 | if (modem_ver < XMM_MODEM_VER_1130) { | ||
422 | pr_debug("gpio host wakeup done <-\n"); | ||
423 | value = gpio_get_value | ||
424 | (baseband_power_driver_data-> | ||
425 | modem.xmm.ipc_bb_wake); | ||
426 | if (value) { | ||
427 | /* Clear the slave wakeup request */ | ||
428 | gpio_set_value | ||
429 | (baseband_power_driver_data-> | ||
430 | modem.xmm.ipc_bb_wake, 0); | ||
431 | pr_debug("gpio slave wakeup done ->\n"); | ||
432 | } | ||
433 | } | ||
434 | /* [ver >= 1130] gpio protocol falling edge */ | ||
435 | if (modem_ver >= XMM_MODEM_VER_1130) { | ||
436 | if (baseband_xmm_powerstate == BBXMM_PS_L2) { | ||
437 | CP_initiated_L2toL0 = true; | ||
438 | baseband_xmm_set_power_status | ||
439 | (BBXMM_PS_L2TOL0); | ||
440 | } else if (baseband_xmm_powerstate == | ||
441 | BBXMM_PS_L3) { | ||
442 | spin_lock(&xmm_lock); | ||
443 | wakeup_pending = true; | ||
444 | spin_unlock(&xmm_lock); | ||
445 | pr_info("CP L3 -> L0\n"); | ||
446 | } | ||
447 | } | ||
448 | /* save gpio state */ | ||
449 | ipc_ap_wake_state = IPC_AP_WAKE_L; | ||
450 | } else { | ||
451 | pr_debug("%s - rising\n", __func__); | ||
452 | /* [ver >= 1130] gpio protocol rising edge */ | ||
453 | if (modem_ver >= XMM_MODEM_VER_1130) { | ||
454 | pr_debug("gpio host wakeup done <-\n"); | ||
455 | value = gpio_get_value | ||
456 | (baseband_power_driver_data-> | ||
457 | modem.xmm.ipc_bb_wake); | ||
458 | if (value) { | ||
459 | /* Clear the slave wakeup request */ | ||
460 | gpio_set_value | ||
461 | (baseband_power_driver_data-> | ||
462 | modem.xmm.ipc_bb_wake, 0); | ||
463 | pr_debug("gpio slave wakeup done ->\n"); | ||
464 | if (reenable_autosuspend && usbdev) { | ||
465 | reenable_autosuspend = false; | ||
466 | queue_work(workqueue, | ||
467 | &autopm_resume_work); | ||
468 | } | ||
469 | } | ||
470 | if ((baseband_xmm_powerstate == | ||
471 | BBXMM_PS_L2TOL0) || | ||
472 | (baseband_xmm_powerstate == | ||
473 | BBXMM_PS_L3TOL0)) | ||
474 | baseband_xmm_set_power_status( | ||
475 | BBXMM_PS_L0); | ||
476 | else | ||
477 | pr_info("%s:no state" | ||
478 | "change required\n", __func__); | ||
479 | } | ||
480 | /* save gpio state */ | ||
481 | ipc_ap_wake_state = IPC_AP_WAKE_H; | ||
482 | } | ||
483 | } | ||
484 | |||
485 | return IRQ_HANDLED; | ||
486 | } | ||
487 | EXPORT_SYMBOL(baseband_xmm_power_ipc_ap_wake_irq); | ||
488 | |||
489 | static void baseband_xmm_power_init1_work(struct work_struct *work) | ||
490 | { | ||
491 | int value; | ||
492 | |||
493 | pr_debug("%s {\n", __func__); | ||
494 | |||
495 | /* check if IPC_HSIC_ACTIVE high */ | ||
496 | value = gpio_get_value(baseband_power_driver_data-> | ||
497 | modem.xmm.ipc_hsic_active); | ||
498 | if (value != 1) { | ||
499 | pr_err("%s - expected IPC_HSIC_ACTIVE high!\n", __func__); | ||
500 | return; | ||
501 | } | ||
502 | |||
503 | /* wait 100 ms */ | ||
504 | mdelay(100); | ||
505 | |||
506 | /* set IPC_HSIC_ACTIVE low */ | ||
507 | gpio_set_value(baseband_power_driver_data-> | ||
508 | modem.xmm.ipc_hsic_active, 0); | ||
509 | |||
510 | /* wait 10 ms */ | ||
511 | mdelay(10); | ||
512 | |||
513 | /* set IPC_HSIC_ACTIVE high */ | ||
514 | gpio_set_value(baseband_power_driver_data-> | ||
515 | modem.xmm.ipc_hsic_active, 1); | ||
516 | |||
517 | /* wait 20 ms */ | ||
518 | mdelay(20); | ||
519 | |||
520 | pr_debug("%s }\n", __func__); | ||
521 | } | ||
522 | |||
523 | static void baseband_xmm_power_init2_work(struct work_struct *work) | ||
524 | { | ||
525 | struct baseband_power_platform_data *data = baseband_power_driver_data; | ||
526 | |||
527 | pr_debug("%s\n", __func__); | ||
528 | |||
529 | /* check input */ | ||
530 | if (!data) | ||
531 | return; | ||
532 | |||
533 | /* register usb host controller only once */ | ||
534 | if (register_hsic_device) { | ||
535 | if (data->hsic_register) | ||
536 | data->modem.xmm.hsic_device = data->hsic_register(); | ||
537 | else | ||
538 | pr_err("%s: hsic_register is missing\n", __func__); | ||
539 | register_hsic_device = false; | ||
540 | } | ||
541 | |||
542 | } | ||
543 | |||
544 | static void baseband_xmm_power_autopm_resume(struct work_struct *work) | ||
545 | { | ||
546 | struct usb_interface *intf; | ||
547 | |||
548 | pr_debug("%s\n", __func__); | ||
549 | if (usbdev) { | ||
550 | usb_lock_device(usbdev); | ||
551 | intf = usb_ifnum_to_if(usbdev, 0); | ||
552 | usb_autopm_get_interface(intf); | ||
553 | usb_autopm_put_interface(intf); | ||
554 | usb_unlock_device(usbdev); | ||
555 | } | ||
556 | } | ||
557 | |||
558 | |||
559 | /* Do the work for AP/CP initiated L2->L0 */ | ||
560 | static void baseband_xmm_power_L2_resume(void) | ||
561 | { | ||
562 | struct baseband_power_platform_data *data = baseband_power_driver_data; | ||
563 | int value; | ||
564 | int delay = 1000; /* maxmum delay in msec */ | ||
565 | |||
566 | pr_debug("%s\n", __func__); | ||
567 | |||
568 | if (!baseband_power_driver_data) | ||
569 | return; | ||
570 | |||
571 | modem_sleep_flag = false; | ||
572 | |||
573 | if (CP_initiated_L2toL0) { | ||
574 | pr_info("CP L2->L0\n"); | ||
575 | CP_initiated_L2toL0 = false; | ||
576 | queue_work(workqueue, &L2_resume_work); | ||
577 | } else { | ||
578 | /* set the slave wakeup request */ | ||
579 | pr_info("AP L2->L0\n"); | ||
580 | gpio_set_value(data->modem.xmm.ipc_bb_wake, 1); | ||
581 | pr_debug("waiting for host wakeup from CP...\n"); | ||
582 | do { | ||
583 | mdelay(1); | ||
584 | value = gpio_get_value(data->modem.xmm.ipc_ap_wake); | ||
585 | delay--; | ||
586 | } while ((value) && (delay)); | ||
587 | if (delay) | ||
588 | pr_debug("gpio host wakeup low <-\n"); | ||
589 | else | ||
590 | pr_info("!!AP L2->L0 Failed\n"); | ||
591 | } | ||
592 | } | ||
593 | |||
594 | /* Do the work for CP initiated L2->L0 */ | ||
595 | static void baseband_xmm_power_L2_resume_work(struct work_struct *work) | ||
596 | { | ||
597 | struct usb_interface *intf; | ||
598 | |||
599 | pr_debug("%s {\n", __func__); | ||
600 | |||
601 | if (!usbdev) | ||
602 | return; | ||
603 | usb_lock_device(usbdev); | ||
604 | intf = usb_ifnum_to_if(usbdev, 0); | ||
605 | if (usb_autopm_get_interface(intf) == 0) | ||
606 | usb_autopm_put_interface(intf); | ||
607 | usb_unlock_device(usbdev); | ||
608 | |||
609 | pr_debug("} %s\n", __func__); | ||
610 | } | ||
611 | |||
612 | static void baseband_xmm_power_reset_on(void) | ||
613 | { | ||
614 | /* reset / power on sequence */ | ||
615 | mdelay(40); | ||
616 | gpio_set_value(baseband_power_driver_data->modem.xmm.bb_rst, 1); | ||
617 | mdelay(1); | ||
618 | gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 1); | ||
619 | udelay(40); | ||
620 | gpio_set_value(baseband_power_driver_data->modem.xmm.bb_on, 0); | ||
621 | } | ||
622 | |||
623 | static struct baseband_xmm_power_work_t *baseband_xmm_power_work; | ||
624 | |||
625 | static void baseband_xmm_power_work_func(struct work_struct *work) | ||
626 | { | ||
627 | struct baseband_xmm_power_work_t *bbxmm_work | ||
628 | = (struct baseband_xmm_power_work_t *) work; | ||
629 | |||
630 | pr_debug("%s\n", __func__); | ||
631 | |||
632 | switch (bbxmm_work->state) { | ||
633 | case BBXMM_WORK_UNINIT: | ||
634 | pr_debug("BBXMM_WORK_UNINIT\n"); | ||
635 | break; | ||
636 | case BBXMM_WORK_INIT: | ||
637 | pr_debug("BBXMM_WORK_INIT\n"); | ||
638 | /* go to next state */ | ||
639 | bbxmm_work->state = (modem_flash && !modem_pm) | ||
640 | ? BBXMM_WORK_INIT_FLASH_STEP1 | ||
641 | : (modem_flash && modem_pm) | ||
642 | ? BBXMM_WORK_INIT_FLASH_PM_STEP1 | ||
643 | : (!modem_flash && modem_pm) | ||
644 | ? BBXMM_WORK_INIT_FLASHLESS_PM_STEP1 | ||
645 | : BBXMM_WORK_UNINIT; | ||
646 | pr_debug("Go to next state %d\n", bbxmm_work->state); | ||
647 | queue_work(workqueue, work); | ||
648 | break; | ||
649 | case BBXMM_WORK_INIT_FLASH_STEP1: | ||
650 | pr_debug("BBXMM_WORK_INIT_FLASH_STEP1\n"); | ||
651 | /* register usb host controller */ | ||
652 | pr_debug("%s: register usb host controller\n", __func__); | ||
653 | if (baseband_power_driver_data->hsic_register) | ||
654 | baseband_power_driver_data->modem.xmm.hsic_device = | ||
655 | baseband_power_driver_data->hsic_register(); | ||
656 | else | ||
657 | pr_err("%s: hsic_register is missing\n", __func__); | ||
658 | break; | ||
659 | case BBXMM_WORK_INIT_FLASH_PM_STEP1: | ||
660 | pr_debug("BBXMM_WORK_INIT_FLASH_PM_STEP1\n"); | ||
661 | /* [modem ver >= 1130] start with IPC_HSIC_ACTIVE low */ | ||
662 | if (modem_ver >= XMM_MODEM_VER_1130) { | ||
663 | pr_debug("%s: ver > 1130:" | ||
664 | " ipc_hsic_active -> 0\n", __func__); | ||
665 | gpio_set_value(baseband_power_driver_data-> | ||
666 | modem.xmm.ipc_hsic_active, 0); | ||
667 | } | ||
668 | /* reset / power on sequence */ | ||
669 | baseband_xmm_power_reset_on(); | ||
670 | /* set power status as on */ | ||
671 | power_onoff = 1; | ||
672 | /* optional delay | ||
673 | * 0 = flashless | ||
674 | * ==> causes next step to enumerate modem boot rom | ||
675 | * (058b / 0041) | ||
676 | * some delay > boot rom timeout | ||
677 | * ==> causes next step to enumerate modem software | ||
678 | * (1519 / 0020) | ||
679 | * (requires modem to be flash version, not flashless | ||
680 | * version) | ||
681 | */ | ||
682 | if (enum_delay_ms) | ||
683 | mdelay(enum_delay_ms); | ||
684 | /* register usb host controller */ | ||
685 | pr_debug("%s: register usb host controller\n", __func__); | ||
686 | if (baseband_power_driver_data->hsic_register) | ||
687 | baseband_power_driver_data->modem.xmm.hsic_device = | ||
688 | baseband_power_driver_data->hsic_register(); | ||
689 | else | ||
690 | pr_err("%s: hsic_register is missing\n", __func__); | ||
691 | /* go to next state */ | ||
692 | bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) | ||
693 | ? BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1 | ||
694 | : BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1; | ||
695 | queue_work(workqueue, work); | ||
696 | pr_debug("Go to next state %d\n", bbxmm_work->state); | ||
697 | break; | ||
698 | case BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1: | ||
699 | pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_LT_1130_STEP1\n"); | ||
700 | break; | ||
701 | case BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1: | ||
702 | pr_debug("BBXMM_WORK_INIT_FLASH_PM_VER_GE_1130_STEP1\n"); | ||
703 | break; | ||
704 | case BBXMM_WORK_INIT_FLASHLESS_PM_STEP1: | ||
705 | pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_STEP1\n"); | ||
706 | /* go to next state */ | ||
707 | bbxmm_work->state = (modem_ver < XMM_MODEM_VER_1130) | ||
708 | ? BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_WAIT_IRQ | ||
709 | : BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1; | ||
710 | queue_work(workqueue, work); | ||
711 | break; | ||
712 | case BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1: | ||
713 | pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_LT_1130_STEP1\n"); | ||
714 | break; | ||
715 | case BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1: | ||
716 | pr_debug("BBXMM_WORK_INIT_FLASHLESS_PM_VER_GE_1130_STEP1\n"); | ||
717 | break; | ||
718 | default: | ||
719 | break; | ||
720 | } | ||
721 | |||
722 | } | ||
723 | |||
724 | static void baseband_xmm_device_add_handler(struct usb_device *udev) | ||
725 | { | ||
726 | struct usb_interface *intf = usb_ifnum_to_if(udev, 0); | ||
727 | const struct usb_device_id *id; | ||
728 | |||
729 | if (intf == NULL) | ||
730 | return; | ||
731 | |||
732 | id = usb_match_id(intf, xmm_pm_ids); | ||
733 | |||
734 | if (id) { | ||
735 | pr_debug("persist_enabled: %u\n", udev->persist_enabled); | ||
736 | pr_info("Add device %d <%s %s>\n", udev->devnum, | ||
737 | udev->manufacturer, udev->product); | ||
738 | usbdev = udev; | ||
739 | usb_enable_autosuspend(udev); | ||
740 | pr_info("enable autosuspend\n"); | ||
741 | } | ||
742 | } | ||
743 | |||
744 | static void baseband_xmm_device_remove_handler(struct usb_device *udev) | ||
745 | { | ||
746 | if (usbdev == udev) { | ||
747 | pr_info("Remove device %d <%s %s>\n", udev->devnum, | ||
748 | udev->manufacturer, udev->product); | ||
749 | usbdev = 0; | ||
750 | } | ||
751 | |||
752 | } | ||
753 | |||
754 | static int usb_xmm_notify(struct notifier_block *self, unsigned long action, | ||
755 | void *blob) | ||
756 | { | ||
757 | switch (action) { | ||
758 | case USB_DEVICE_ADD: | ||
759 | baseband_xmm_device_add_handler(blob); | ||
760 | break; | ||
761 | case USB_DEVICE_REMOVE: | ||
762 | baseband_xmm_device_remove_handler(blob); | ||
763 | break; | ||
764 | } | ||
765 | |||
766 | return NOTIFY_OK; | ||
767 | } | ||
768 | |||
769 | |||
770 | static struct notifier_block usb_xmm_nb = { | ||
771 | .notifier_call = usb_xmm_notify, | ||
772 | }; | ||
773 | |||
774 | static int baseband_xmm_power_driver_probe(struct platform_device *device) | ||
775 | { | ||
776 | struct baseband_power_platform_data *data | ||
777 | = (struct baseband_power_platform_data *) | ||
778 | device->dev.platform_data; | ||
779 | struct device *dev = &device->dev; | ||
780 | unsigned long flags; | ||
781 | int err; | ||
782 | |||
783 | pr_debug("%s\n", __func__); | ||
784 | pr_debug("[XMM] enum_delay_ms=%ld\n", enum_delay_ms); | ||
785 | |||
786 | /* check for platform data */ | ||
787 | if (!data) | ||
788 | return -ENODEV; | ||
789 | |||
790 | /* check if supported modem */ | ||
791 | if (data->baseband_type != BASEBAND_XMM) { | ||
792 | pr_err("unsuppported modem\n"); | ||
793 | return -ENODEV; | ||
794 | } | ||
795 | |||
796 | /* save platform data */ | ||
797 | baseband_power_driver_data = data; | ||
798 | |||
799 | /* create device file */ | ||
800 | err = device_create_file(dev, &dev_attr_xmm_onoff); | ||
801 | if (err < 0) { | ||
802 | pr_err("%s - device_create_file failed\n", __func__); | ||
803 | return -ENODEV; | ||
804 | } | ||
805 | |||
806 | /* init wake lock */ | ||
807 | wake_lock_init(&wakelock, WAKE_LOCK_SUSPEND, "baseband_xmm_power"); | ||
808 | |||
809 | /* init spin lock */ | ||
810 | spin_lock_init(&xmm_lock); | ||
811 | /* request baseband gpio(s) */ | ||
812 | tegra_baseband_gpios[0].gpio = baseband_power_driver_data | ||
813 | ->modem.xmm.bb_rst; | ||
814 | tegra_baseband_gpios[1].gpio = baseband_power_driver_data | ||
815 | ->modem.xmm.bb_on; | ||
816 | tegra_baseband_gpios[2].gpio = baseband_power_driver_data | ||
817 | ->modem.xmm.ipc_bb_wake; | ||
818 | tegra_baseband_gpios[3].gpio = baseband_power_driver_data | ||
819 | ->modem.xmm.ipc_ap_wake; | ||
820 | tegra_baseband_gpios[4].gpio = baseband_power_driver_data | ||
821 | ->modem.xmm.ipc_hsic_active; | ||
822 | tegra_baseband_gpios[5].gpio = baseband_power_driver_data | ||
823 | ->modem.xmm.ipc_hsic_sus_req; | ||
824 | err = gpio_request_array(tegra_baseband_gpios, | ||
825 | ARRAY_SIZE(tegra_baseband_gpios)); | ||
826 | if (err < 0) { | ||
827 | pr_err("%s - request gpio(s) failed\n", __func__); | ||
828 | return -ENODEV; | ||
829 | } | ||
830 | |||
831 | /* request baseband irq(s) */ | ||
832 | if (modem_flash && modem_pm) { | ||
833 | pr_debug("%s: request_irq IPC_AP_WAKE_IRQ\n", __func__); | ||
834 | ipc_ap_wake_state = IPC_AP_WAKE_UNINIT; | ||
835 | err = request_threaded_irq( | ||
836 | gpio_to_irq(data->modem.xmm.ipc_ap_wake), | ||
837 | NULL, | ||
838 | baseband_xmm_power_ipc_ap_wake_irq, | ||
839 | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING, | ||
840 | "IPC_AP_WAKE_IRQ", | ||
841 | NULL); | ||
842 | if (err < 0) { | ||
843 | pr_err("%s - request irq IPC_AP_WAKE_IRQ failed\n", | ||
844 | __func__); | ||
845 | return err; | ||
846 | } | ||
847 | err = enable_irq_wake(gpio_to_irq(data->modem.xmm.ipc_ap_wake)); | ||
848 | if (err < 0) | ||
849 | pr_err("%s: enable_irq_wake error\n", __func__); | ||
850 | ipc_ap_wake_state = IPC_AP_WAKE_IRQ_READY; | ||
851 | if (modem_ver >= XMM_MODEM_VER_1130) { | ||
852 | pr_debug("%s: ver > 1130: AP_WAKE_INIT1\n", __func__); | ||
853 | /* ver 1130 or later starts in INIT1 state */ | ||
854 | ipc_ap_wake_state = IPC_AP_WAKE_INIT1; | ||
855 | } | ||
856 | } | ||
857 | |||
858 | /* init work queue */ | ||
859 | workqueue = create_singlethread_workqueue | ||
860 | ("baseband_xmm_power_workqueue"); | ||
861 | if (!workqueue) { | ||
862 | pr_err("cannot create workqueue\n"); | ||
863 | return -1; | ||
864 | } | ||
865 | baseband_xmm_power_work = (struct baseband_xmm_power_work_t *) | ||
866 | kmalloc(sizeof(struct baseband_xmm_power_work_t), GFP_KERNEL); | ||
867 | if (!baseband_xmm_power_work) { | ||
868 | pr_err("cannot allocate baseband_xmm_power_work\n"); | ||
869 | return -1; | ||
870 | } | ||
871 | INIT_WORK((struct work_struct *) baseband_xmm_power_work, | ||
872 | baseband_xmm_power_work_func); | ||
873 | baseband_xmm_power_work->state = BBXMM_WORK_INIT; | ||
874 | queue_work(workqueue, | ||
875 | (struct work_struct *) baseband_xmm_power_work); | ||
876 | |||
877 | /* init work objects */ | ||
878 | INIT_WORK(&init1_work, baseband_xmm_power_init1_work); | ||
879 | INIT_WORK(&init2_work, baseband_xmm_power_init2_work); | ||
880 | INIT_WORK(&L2_resume_work, baseband_xmm_power_L2_resume_work); | ||
881 | INIT_WORK(&autopm_resume_work, baseband_xmm_power_autopm_resume); | ||
882 | |||
883 | /* init state variables */ | ||
884 | register_hsic_device = true; | ||
885 | CP_initiated_L2toL0 = false; | ||
886 | spin_lock_irqsave(&xmm_lock, flags); | ||
887 | baseband_xmm_powerstate = BBXMM_PS_UNINIT; | ||
888 | wakeup_pending = false; | ||
889 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
890 | |||
891 | usb_register_notify(&usb_xmm_nb); | ||
892 | |||
893 | pr_debug("%s }\n", __func__); | ||
894 | return 0; | ||
895 | } | ||
896 | |||
897 | static int baseband_xmm_power_driver_remove(struct platform_device *device) | ||
898 | { | ||
899 | struct baseband_power_platform_data *data | ||
900 | = (struct baseband_power_platform_data *) | ||
901 | device->dev.platform_data; | ||
902 | struct device *dev = &device->dev; | ||
903 | |||
904 | pr_debug("%s\n", __func__); | ||
905 | |||
906 | /* check for platform data */ | ||
907 | if (!data) | ||
908 | return 0; | ||
909 | |||
910 | usb_unregister_notify(&usb_xmm_nb); | ||
911 | |||
912 | /* free work structure */ | ||
913 | kfree(baseband_xmm_power_work); | ||
914 | baseband_xmm_power_work = (struct baseband_xmm_power_work_t *) 0; | ||
915 | |||
916 | /* free baseband irq(s) */ | ||
917 | if (modem_flash && modem_pm) { | ||
918 | free_irq(gpio_to_irq(baseband_power_driver_data | ||
919 | ->modem.xmm.ipc_ap_wake), NULL); | ||
920 | } | ||
921 | |||
922 | /* free baseband gpio(s) */ | ||
923 | gpio_free_array(tegra_baseband_gpios, | ||
924 | ARRAY_SIZE(tegra_baseband_gpios)); | ||
925 | |||
926 | /* destroy wake lock */ | ||
927 | wake_lock_destroy(&wakelock); | ||
928 | |||
929 | /* delete device file */ | ||
930 | device_remove_file(dev, &dev_attr_xmm_onoff); | ||
931 | |||
932 | /* unregister usb host controller */ | ||
933 | if (data->hsic_unregister) | ||
934 | data->hsic_unregister(data->modem.xmm.hsic_device); | ||
935 | else | ||
936 | pr_err("%s: hsic_unregister is missing\n", __func__); | ||
937 | |||
938 | return 0; | ||
939 | } | ||
940 | |||
941 | static int baseband_xmm_power_driver_handle_resume( | ||
942 | struct baseband_power_platform_data *data) | ||
943 | { | ||
944 | int value; | ||
945 | int delay = 1000; /* maxmum delay in msec */ | ||
946 | unsigned long flags; | ||
947 | |||
948 | pr_debug("%s\n", __func__); | ||
949 | if (!data) | ||
950 | return 0; | ||
951 | |||
952 | /* check if modem is on */ | ||
953 | if (power_onoff == 0) { | ||
954 | pr_debug("%s - flight mode - nop\n", __func__); | ||
955 | return 0; | ||
956 | } | ||
957 | |||
958 | modem_sleep_flag = false; | ||
959 | |||
960 | /* L3->L0 */ | ||
961 | baseband_xmm_set_power_status(BBXMM_PS_L3TOL0); | ||
962 | value = gpio_get_value(data->modem.xmm.ipc_ap_wake); | ||
963 | if (value) { | ||
964 | pr_info("AP L3 -> L0\n"); | ||
965 | /* wake bb */ | ||
966 | gpio_set_value(data->modem.xmm.ipc_bb_wake, 1); | ||
967 | |||
968 | pr_debug("waiting for host wakeup...\n"); | ||
969 | do { | ||
970 | mdelay(1); | ||
971 | value = gpio_get_value(data->modem.xmm.ipc_ap_wake); | ||
972 | delay--; | ||
973 | } while ((value) && (delay)); | ||
974 | if (delay) | ||
975 | pr_debug("gpio host wakeup low <-\n"); | ||
976 | } else { | ||
977 | pr_info("CP L3 -> L0\n"); | ||
978 | spin_lock_irqsave(&xmm_lock, flags); | ||
979 | /* Clear wakeup pending flag */ | ||
980 | wakeup_pending = false; | ||
981 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
982 | } | ||
983 | reenable_autosuspend = true; | ||
984 | |||
985 | return 0; | ||
986 | |||
987 | } | ||
988 | |||
989 | #ifdef CONFIG_PM | ||
990 | static int baseband_xmm_power_driver_suspend(struct device *dev) | ||
991 | { | ||
992 | pr_debug("%s\n", __func__); | ||
993 | return 0; | ||
994 | } | ||
995 | |||
996 | static int baseband_xmm_power_driver_resume(struct device *dev) | ||
997 | { | ||
998 | struct platform_device *pdev = to_platform_device(dev); | ||
999 | struct baseband_power_platform_data *data | ||
1000 | = (struct baseband_power_platform_data *) | ||
1001 | pdev->dev.platform_data; | ||
1002 | |||
1003 | pr_debug("%s\n", __func__); | ||
1004 | baseband_xmm_power_driver_handle_resume(data); | ||
1005 | |||
1006 | return 0; | ||
1007 | } | ||
1008 | |||
1009 | static int baseband_xmm_power_suspend_noirq(struct device *dev) | ||
1010 | { | ||
1011 | unsigned long flags; | ||
1012 | |||
1013 | pr_debug("%s\n", __func__); | ||
1014 | spin_lock_irqsave(&xmm_lock, flags); | ||
1015 | if (wakeup_pending) { | ||
1016 | wakeup_pending = false; | ||
1017 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
1018 | pr_info("%s:**Abort Suspend: reason CP WAKEUP**\n", __func__); | ||
1019 | return -EBUSY; | ||
1020 | } | ||
1021 | spin_unlock_irqrestore(&xmm_lock, flags); | ||
1022 | return 0; | ||
1023 | } | ||
1024 | |||
1025 | static int baseband_xmm_power_resume_noirq(struct device *dev) | ||
1026 | { | ||
1027 | pr_debug("%s\n", __func__); | ||
1028 | return 0; | ||
1029 | } | ||
1030 | |||
1031 | static const struct dev_pm_ops baseband_xmm_power_dev_pm_ops = { | ||
1032 | .suspend_noirq = baseband_xmm_power_suspend_noirq, | ||
1033 | .resume_noirq = baseband_xmm_power_resume_noirq, | ||
1034 | .suspend = baseband_xmm_power_driver_suspend, | ||
1035 | .resume = baseband_xmm_power_driver_resume, | ||
1036 | }; | ||
1037 | #endif | ||
1038 | |||
1039 | static struct platform_driver baseband_power_driver = { | ||
1040 | .probe = baseband_xmm_power_driver_probe, | ||
1041 | .remove = baseband_xmm_power_driver_remove, | ||
1042 | .driver = { | ||
1043 | .name = "baseband_xmm_power", | ||
1044 | #ifdef CONFIG_PM | ||
1045 | .pm = &baseband_xmm_power_dev_pm_ops, | ||
1046 | #endif | ||
1047 | }, | ||
1048 | }; | ||
1049 | |||
1050 | static int __init baseband_xmm_power_init(void) | ||
1051 | { | ||
1052 | pr_debug("%s\n", __func__); | ||
1053 | return platform_driver_register(&baseband_power_driver); | ||
1054 | } | ||
1055 | |||
1056 | static void __exit baseband_xmm_power_exit(void) | ||
1057 | { | ||
1058 | pr_debug("%s\n", __func__); | ||
1059 | platform_driver_unregister(&baseband_power_driver); | ||
1060 | } | ||
1061 | |||
1062 | module_init(baseband_xmm_power_init) | ||
1063 | module_exit(baseband_xmm_power_exit) | ||