diff options
Diffstat (limited to 'drivers/misc/tegra-baseband')
-rw-r--r-- | drivers/misc/tegra-baseband/Kconfig | 32 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/Makefile | 6 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/bb-m7400.c | 340 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/bb-power.c | 337 | ||||
-rw-r--r-- | drivers/misc/tegra-baseband/bb-power.h | 99 |
5 files changed, 814 insertions, 0 deletions
diff --git a/drivers/misc/tegra-baseband/Kconfig b/drivers/misc/tegra-baseband/Kconfig new file mode 100644 index 00000000000..1f116918296 --- /dev/null +++ b/drivers/misc/tegra-baseband/Kconfig | |||
@@ -0,0 +1,32 @@ | |||
1 | menuconfig TEGRA_BB_SUPPORT | ||
2 | bool "Tegra baseband support" | ||
3 | depends on ARCH_TEGRA | ||
4 | ---help--- | ||
5 | Say Y here to get to see options for tegra baseband support. | ||
6 | This option alone does not add any kernel code. | ||
7 | |||
8 | If you say N, all options in this submenu will be skipped and disabled. | ||
9 | |||
10 | if TEGRA_BB_SUPPORT | ||
11 | |||
12 | config TEGRA_BB_POWER | ||
13 | bool "Enable tegra baseband power driver" | ||
14 | ---help--- | ||
15 | Adds power management driver for managing different baseband | ||
16 | modems with tegra processor. | ||
17 | |||
18 | This driver should work with at least the following devices: | ||
19 | |||
20 | * STE M7400 | ||
21 | * ... | ||
22 | |||
23 | Disabled by default. Choose Y here if you want to build the driver. | ||
24 | |||
25 | config TEGRA_BB_M7400 | ||
26 | bool "Enable driver for M7400 modem" | ||
27 | ---help--- | ||
28 | Enables driver for M7400 modem. | ||
29 | |||
30 | Disabled by default. Choose Y here if you want to build the driver. | ||
31 | |||
32 | endif # TEGRA_BB_SUPPORT | ||
diff --git a/drivers/misc/tegra-baseband/Makefile b/drivers/misc/tegra-baseband/Makefile new file mode 100644 index 00000000000..a95d84dbf11 --- /dev/null +++ b/drivers/misc/tegra-baseband/Makefile | |||
@@ -0,0 +1,6 @@ | |||
1 | # | ||
2 | # Makefile for tegra baseband support. | ||
3 | # | ||
4 | |||
5 | obj-$(CONFIG_TEGRA_BB_POWER) += bb-power.o | ||
6 | obj-$(CONFIG_TEGRA_BB_M7400) += bb-m7400.o | ||
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 | } | ||
diff --git a/drivers/misc/tegra-baseband/bb-power.c b/drivers/misc/tegra-baseband/bb-power.c new file mode 100644 index 00000000000..9210a8f3e84 --- /dev/null +++ b/drivers/misc/tegra-baseband/bb-power.c | |||
@@ -0,0 +1,337 @@ | |||
1 | /* | ||
2 | * drivers/misc/tegra-baseband/bb-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/delay.h> | ||
26 | #include <linux/fs.h> | ||
27 | #include <linux/usb.h> | ||
28 | #include <linux/uaccess.h> | ||
29 | #include <linux/platform_data/tegra_usb.h> | ||
30 | #include <mach/usb_phy.h> | ||
31 | #include <mach/tegra-bb-power.h> | ||
32 | #include "bb-power.h" | ||
33 | |||
34 | static struct tegra_bb_callback *callback; | ||
35 | static int attr_load_val; | ||
36 | static struct tegra_bb_power_mdata *mdata; | ||
37 | static bb_get_cblist get_cblist[] = { | ||
38 | NULL, | ||
39 | NULL, | ||
40 | NULL, | ||
41 | M7400_CB, | ||
42 | }; | ||
43 | |||
44 | static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata) | ||
45 | { | ||
46 | int ret; | ||
47 | int irq; | ||
48 | unsigned gpio_id; | ||
49 | const char *gpio_label; | ||
50 | unsigned long gpio_flags; | ||
51 | struct tegra_bb_gpio_data *gpiolist; | ||
52 | struct tegra_bb_gpio_irqdata *gpioirq; | ||
53 | |||
54 | gpiolist = gdata->gpio; | ||
55 | for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) { | ||
56 | gpio_id = (gpiolist->data.gpio); | ||
57 | gpio_label = (gpiolist->data.label); | ||
58 | gpio_flags = (gpiolist->data.flags); | ||
59 | |||
60 | /* Request the gpio */ | ||
61 | ret = gpio_request(gpio_id, gpio_label); | ||
62 | if (ret) { | ||
63 | pr_err("%s: Error: gpio_request for gpio %d failed.\n", | ||
64 | __func__, gpio_id); | ||
65 | return ret; | ||
66 | } | ||
67 | |||
68 | /* Set gpio direction, as requested */ | ||
69 | if (gpio_flags == GPIOF_IN) | ||
70 | gpio_direction_input(gpio_id); | ||
71 | else | ||
72 | gpio_direction_output(gpio_id, (!gpio_flags ? 0 : 1)); | ||
73 | |||
74 | /* Enable the gpio */ | ||
75 | tegra_gpio_enable(gpio_id); | ||
76 | |||
77 | /* Create a sysfs node, if requested */ | ||
78 | if (gpiolist->doexport) | ||
79 | gpio_export(gpio_id, false); | ||
80 | } | ||
81 | |||
82 | gpioirq = gdata->gpioirq; | ||
83 | for (; gpioirq->id != GPIO_INVALID; ++gpioirq) { | ||
84 | |||
85 | /* Create interrupt handler, if requested */ | ||
86 | if (gpioirq->handler != NULL) { | ||
87 | irq = gpio_to_irq(gpioirq->id); | ||
88 | ret = request_threaded_irq(irq, NULL, gpioirq->handler, | ||
89 | gpioirq->flags, gpioirq->name, gpioirq->cookie); | ||
90 | if (ret < 0) { | ||
91 | pr_err("%s: Error: threaded_irq req fail.\n" | ||
92 | , __func__); | ||
93 | return ret; | ||
94 | } | ||
95 | |||
96 | if (gpioirq->wake_capable) { | ||
97 | ret = enable_irq_wake(irq); | ||
98 | if (ret) { | ||
99 | pr_err("%s: Error: irqwake req fail.\n", | ||
100 | __func__); | ||
101 | return ret; | ||
102 | } | ||
103 | } | ||
104 | } | ||
105 | } | ||
106 | return 0; | ||
107 | } | ||
108 | |||
109 | static int tegra_bb_power_gpio_deinit(struct tegra_bb_power_gdata *gdata) | ||
110 | { | ||
111 | struct tegra_bb_gpio_data *gpiolist; | ||
112 | struct tegra_bb_gpio_irqdata *gpioirq; | ||
113 | |||
114 | gpiolist = gdata->gpio; | ||
115 | for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) { | ||
116 | |||
117 | /* Free the gpio */ | ||
118 | gpio_free(gpiolist->data.gpio); | ||
119 | } | ||
120 | |||
121 | gpioirq = gdata->gpioirq; | ||
122 | for (; gpioirq->id != GPIO_INVALID; ++gpioirq) { | ||
123 | |||
124 | /* Free the irq */ | ||
125 | free_irq(gpio_to_irq(gpioirq->id), gpioirq->cookie); | ||
126 | } | ||
127 | return 0; | ||
128 | } | ||
129 | |||
130 | static ssize_t tegra_bb_attr_write(struct device *dev, | ||
131 | struct device_attribute *attr, | ||
132 | const char *buf, size_t count) | ||
133 | { | ||
134 | int val; | ||
135 | |||
136 | if (sscanf(buf, "%d", &val) != 1) | ||
137 | return -EINVAL; | ||
138 | |||
139 | if (callback && callback->attrib) { | ||
140 | if (!callback->attrib(dev, val)) | ||
141 | attr_load_val = val; | ||
142 | } | ||
143 | return count; | ||
144 | } | ||
145 | |||
146 | static ssize_t tegra_bb_attr_read(struct device *dev, | ||
147 | struct device_attribute *attr, char *buf) | ||
148 | { | ||
149 | return sprintf(buf, "%d", attr_load_val); | ||
150 | } | ||
151 | |||
152 | static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP, | ||
153 | tegra_bb_attr_read, tegra_bb_attr_write); | ||
154 | |||
155 | static void tegra_usbdevice_added(struct usb_device *udev) | ||
156 | { | ||
157 | const struct usb_device_descriptor *desc = &udev->descriptor; | ||
158 | |||
159 | if (desc->idVendor == mdata->vid && | ||
160 | desc->idProduct == mdata->pid) { | ||
161 | pr_debug("%s: Device %s added.\n", udev->product, __func__); | ||
162 | |||
163 | if (mdata->wake_capable) | ||
164 | device_set_wakeup_enable(&udev->dev, true); | ||
165 | if (mdata->autosuspend_ready) | ||
166 | usb_enable_autosuspend(udev); | ||
167 | if (mdata->reg_cb) | ||
168 | mdata->reg_cb(udev); | ||
169 | } | ||
170 | } | ||
171 | |||
172 | static void tegra_usbdevice_removed(struct usb_device *udev) | ||
173 | { | ||
174 | const struct usb_device_descriptor *desc = &udev->descriptor; | ||
175 | |||
176 | if (desc->idVendor == mdata->vid && | ||
177 | desc->idProduct == mdata->pid) { | ||
178 | pr_debug("%s: Device %s removed.\n", udev->product, __func__); | ||
179 | } | ||
180 | } | ||
181 | |||
182 | static int tegra_usb_notify(struct notifier_block *self, unsigned long action, | ||
183 | void *dev) | ||
184 | { | ||
185 | switch (action) { | ||
186 | case USB_DEVICE_ADD: | ||
187 | tegra_usbdevice_added((struct usb_device *)dev); | ||
188 | break; | ||
189 | case USB_DEVICE_REMOVE: | ||
190 | tegra_usbdevice_removed((struct usb_device *)dev); | ||
191 | break; | ||
192 | } | ||
193 | return NOTIFY_OK; | ||
194 | } | ||
195 | |||
196 | static struct notifier_block tegra_usb_nb = { | ||
197 | .notifier_call = tegra_usb_notify, | ||
198 | }; | ||
199 | |||
200 | static int tegra_bb_power_probe(struct platform_device *device) | ||
201 | { | ||
202 | struct device *dev = &device->dev; | ||
203 | struct tegra_bb_pdata *pdata; | ||
204 | struct tegra_bb_power_data *data; | ||
205 | struct tegra_bb_power_gdata *gdata; | ||
206 | int err; | ||
207 | unsigned int bb_id; | ||
208 | |||
209 | pdata = (struct tegra_bb_pdata *) dev->platform_data; | ||
210 | if (!pdata) { | ||
211 | pr_err("%s - Error: platform data is empty.\n", __func__); | ||
212 | return -ENODEV; | ||
213 | } | ||
214 | |||
215 | /* Obtain BB specific callback list */ | ||
216 | bb_id = pdata->bb_id; | ||
217 | if (get_cblist[bb_id] != NULL) { | ||
218 | callback = (struct tegra_bb_callback *) get_cblist[bb_id](); | ||
219 | if (callback && callback->init) { | ||
220 | data = (struct tegra_bb_power_data *) | ||
221 | callback->init((void *)pdata); | ||
222 | |||
223 | gdata = data->gpio_data; | ||
224 | if (!gdata) { | ||
225 | pr_err("%s - Error: Gpio data is empty.\n", | ||
226 | __func__); | ||
227 | return -ENODEV; | ||
228 | } | ||
229 | |||
230 | /* Initialize gpio as required */ | ||
231 | tegra_bb_power_gpio_init(gdata); | ||
232 | |||
233 | mdata = data->modem_data; | ||
234 | if (mdata && mdata->vid && mdata->pid) | ||
235 | /* Register to notifications from usb core */ | ||
236 | usb_register_notify(&tegra_usb_nb); | ||
237 | } else { | ||
238 | pr_err("%s - Error: init callback is empty.\n", | ||
239 | __func__); | ||
240 | return -ENODEV; | ||
241 | } | ||
242 | } else { | ||
243 | pr_err("%s - Error: callback data is empty.\n", __func__); | ||
244 | return -ENODEV; | ||
245 | } | ||
246 | |||
247 | /* Create the control sysfs node */ | ||
248 | err = device_create_file(dev, &dev_attr_load); | ||
249 | if (err < 0) { | ||
250 | pr_err("%s - Error: device_create_file failed.\n", __func__); | ||
251 | return -ENODEV; | ||
252 | } | ||
253 | attr_load_val = 0; | ||
254 | |||
255 | return 0; | ||
256 | } | ||
257 | |||
258 | static int tegra_bb_power_remove(struct platform_device *device) | ||
259 | { | ||
260 | struct device *dev = &device->dev; | ||
261 | struct tegra_bb_power_data *data; | ||
262 | struct tegra_bb_power_gdata *gdata; | ||
263 | |||
264 | /* BB specific callback */ | ||
265 | if (callback && callback->deinit) { | ||
266 | data = (struct tegra_bb_power_data *) | ||
267 | callback->deinit(); | ||
268 | |||
269 | /* Deinitialize gpios */ | ||
270 | gdata = data->gpio_data; | ||
271 | if (gdata) | ||
272 | tegra_bb_power_gpio_deinit(gdata); | ||
273 | else { | ||
274 | pr_err("%s - Error: Gpio data is empty.\n", __func__); | ||
275 | return -ENODEV; | ||
276 | } | ||
277 | |||
278 | mdata = data->modem_data; | ||
279 | if (mdata && mdata->vid && mdata->pid) | ||
280 | /* Register to notifications from usb core */ | ||
281 | usb_unregister_notify(&tegra_usb_nb); | ||
282 | } | ||
283 | |||
284 | /* Remove the control sysfs node */ | ||
285 | device_remove_file(dev, &dev_attr_load); | ||
286 | |||
287 | return 0; | ||
288 | } | ||
289 | |||
290 | #ifdef CONFIG_PM | ||
291 | static int tegra_bb_power_suspend(struct platform_device *device, | ||
292 | pm_message_t state) | ||
293 | { | ||
294 | /* BB specific callback */ | ||
295 | if (callback && callback->power) | ||
296 | callback->power(PWRSTATE_L2L3); | ||
297 | return 0; | ||
298 | } | ||
299 | |||
300 | static int tegra_bb_power_resume(struct platform_device *device) | ||
301 | { | ||
302 | /* BB specific callback */ | ||
303 | if (callback && callback->power) | ||
304 | callback->power(PWRSTATE_L3L0); | ||
305 | return 0; | ||
306 | } | ||
307 | #endif | ||
308 | |||
309 | static struct platform_driver tegra_bb_power_driver = { | ||
310 | .probe = tegra_bb_power_probe, | ||
311 | .remove = tegra_bb_power_remove, | ||
312 | #ifdef CONFIG_PM | ||
313 | .suspend = tegra_bb_power_suspend, | ||
314 | .resume = tegra_bb_power_resume, | ||
315 | #endif | ||
316 | .driver = { | ||
317 | .name = "tegra_baseband_power", | ||
318 | }, | ||
319 | }; | ||
320 | |||
321 | static int __init tegra_baseband_power_init(void) | ||
322 | { | ||
323 | pr_debug("%s\n", __func__); | ||
324 | return platform_driver_register(&tegra_bb_power_driver); | ||
325 | } | ||
326 | |||
327 | static void __exit tegra_baseband_power_exit(void) | ||
328 | { | ||
329 | pr_debug("%s\n", __func__); | ||
330 | platform_driver_unregister(&tegra_bb_power_driver); | ||
331 | } | ||
332 | |||
333 | module_init(tegra_baseband_power_init) | ||
334 | module_exit(tegra_baseband_power_exit) | ||
335 | MODULE_AUTHOR("NVIDIA Corporation"); | ||
336 | MODULE_DESCRIPTION("Tegra modem power management driver"); | ||
337 | MODULE_LICENSE("GPL"); | ||
diff --git a/drivers/misc/tegra-baseband/bb-power.h b/drivers/misc/tegra-baseband/bb-power.h new file mode 100644 index 00000000000..cdd69380203 --- /dev/null +++ b/drivers/misc/tegra-baseband/bb-power.h | |||
@@ -0,0 +1,99 @@ | |||
1 | /* | ||
2 | * drivers/misc/tegra-baseband/bb-power.h | ||
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 | enum tegra_bb_state { | ||
18 | BBSTATE_UNKNOWN, | ||
19 | /* Baseband state L0 - Running */ | ||
20 | BBSTATE_L0, | ||
21 | /* Baseband state L2 - Suspended */ | ||
22 | BBSTATE_L2, | ||
23 | /* Baseband state L3 - Suspended and detached */ | ||
24 | BBSTATE_L3, | ||
25 | }; | ||
26 | |||
27 | enum tegra_bb_pwrstate { | ||
28 | /* System power state - Entering suspend */ | ||
29 | PWRSTATE_L2L3, | ||
30 | /* System power state - Resuming from suspend */ | ||
31 | PWRSTATE_L3L0, | ||
32 | PWRSTATE_INVALID, | ||
33 | }; | ||
34 | |||
35 | struct tegra_bb_gpio_data { | ||
36 | /* Baseband gpio data */ | ||
37 | struct gpio data; | ||
38 | /* Baseband gpio - Should it be exported to sysfs ? */ | ||
39 | bool doexport; | ||
40 | }; | ||
41 | |||
42 | struct tegra_bb_gpio_irqdata { | ||
43 | /* Baseband gpio IRQ - Id */ | ||
44 | int id; | ||
45 | /* Baseband gpio IRQ - Friendly name */ | ||
46 | const char *name; | ||
47 | /* Baseband gpio IRQ - IRQ handler */ | ||
48 | irq_handler_t handler; | ||
49 | /* Baseband gpio IRQ - IRQ trigger flags */ | ||
50 | int flags; | ||
51 | /* Baseband gpio IRQ - Can the gpio wake system from sleep ? */ | ||
52 | bool wake_capable; | ||
53 | void *cookie; | ||
54 | }; | ||
55 | |||
56 | typedef void* (*bb_get_cblist)(void); | ||
57 | typedef void* (*bb_init_cb)(void *pdata); | ||
58 | typedef void* (*bb_deinit_cb)(void); | ||
59 | typedef int (*bb_power_cb)(int code); | ||
60 | typedef int (*bb_attrib_cb)(struct device *dev, int value); | ||
61 | typedef int (*modem_register_cb)(struct usb_device *udev); | ||
62 | |||
63 | struct tegra_bb_power_gdata { | ||
64 | struct tegra_bb_gpio_data *gpio; | ||
65 | struct tegra_bb_gpio_irqdata *gpioirq; | ||
66 | }; | ||
67 | |||
68 | struct tegra_bb_power_mdata { | ||
69 | /* Baseband USB vendor ID */ | ||
70 | int vid; | ||
71 | /* Baseband USB product ID */ | ||
72 | int pid; | ||
73 | /* Baseband capability - Can it generate a wakeup ? */ | ||
74 | bool wake_capable; | ||
75 | /* Baseband capability - Can it be auto/runtime suspended ? */ | ||
76 | bool autosuspend_ready; | ||
77 | /* Baseband callback after a successful registration */ | ||
78 | modem_register_cb reg_cb; | ||
79 | }; | ||
80 | |||
81 | struct tegra_bb_power_data { | ||
82 | struct tegra_bb_power_gdata *gpio_data; | ||
83 | struct tegra_bb_power_mdata *modem_data; | ||
84 | }; | ||
85 | |||
86 | struct tegra_bb_callback { | ||
87 | bb_init_cb init; | ||
88 | bb_deinit_cb deinit; | ||
89 | bb_power_cb power; | ||
90 | bb_attrib_cb attrib; | ||
91 | bool valid; | ||
92 | }; | ||
93 | |||
94 | #ifdef CONFIG_TEGRA_BB_M7400 | ||
95 | extern void *m7400_get_cblist(void); | ||
96 | #define M7400_CB m7400_get_cblist | ||
97 | #else | ||
98 | #define M7400_CB NULL | ||
99 | #endif | ||