Revert "Merge commit 'main-jb-2012.08.03-B4' into t114-0806"
[linux-2.6.git] / drivers / misc / tegra-baseband / bb-power.c
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 <mach/gpio-tegra.h>
33 #include "bb-power.h"
34
35 static struct tegra_bb_callback *callback;
36 static int attr_load_val;
37 static struct tegra_bb_power_mdata *mdata;
38 static bb_get_cblist get_cblist[] = {
39         NULL,
40         NULL,
41         NULL,
42         M7400_CB,
43 };
44
45 static int tegra_bb_power_gpio_init(struct tegra_bb_power_gdata *gdata)
46 {
47         int ret;
48         int irq;
49         unsigned gpio_id;
50         const char *gpio_label;
51         unsigned long gpio_flags;
52         struct tegra_bb_gpio_data *gpiolist;
53         struct tegra_bb_gpio_irqdata *gpioirq;
54
55         gpiolist = gdata->gpio;
56         for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) {
57                 gpio_id = (gpiolist->data.gpio);
58                 gpio_label = (gpiolist->data.label);
59                 gpio_flags = (gpiolist->data.flags);
60
61                 /* Request the gpio */
62                 ret = gpio_request(gpio_id, gpio_label);
63                 if (ret) {
64                         pr_err("%s: Error: gpio_request for gpio %d failed.\n",
65                                                          __func__, gpio_id);
66                         return ret;
67                 }
68
69                 /* Set gpio direction, as requested */
70                 if (gpio_flags == GPIOF_IN)
71                         gpio_direction_input(gpio_id);
72                 else
73                         gpio_direction_output(gpio_id, (!gpio_flags ? 0 : 1));
74
75                 /* Enable the gpio */
76                 tegra_gpio_enable(gpio_id);
77
78                 /* Create a sysfs node, if requested */
79                 if (gpiolist->doexport)
80                         gpio_export(gpio_id, false);
81         }
82
83         gpioirq = gdata->gpioirq;
84         for (; gpioirq->id != GPIO_INVALID; ++gpioirq) {
85
86                 /* Create interrupt handler, if requested */
87                 if (gpioirq->handler != NULL) {
88                         irq = gpio_to_irq(gpioirq->id);
89                         ret = request_threaded_irq(irq, NULL, gpioirq->handler,
90                                 gpioirq->flags, gpioirq->name, gpioirq->cookie);
91                         if (ret < 0) {
92                                 pr_err("%s: Error: threaded_irq req fail.\n"
93                                                                 , __func__);
94                                 return ret;
95                         }
96
97                         if (gpioirq->wake_capable) {
98                                 ret = enable_irq_wake(irq);
99                                 if (ret) {
100                                         pr_err("%s: Error: irqwake req fail.\n",
101                                                                 __func__);
102                                         return ret;
103                                 }
104                         }
105                 }
106         }
107         return 0;
108 }
109
110 static int tegra_bb_power_gpio_deinit(struct tegra_bb_power_gdata *gdata)
111 {
112         struct tegra_bb_gpio_data *gpiolist;
113         struct tegra_bb_gpio_irqdata *gpioirq;
114
115         gpiolist = gdata->gpio;
116         for (; gpiolist->data.gpio != GPIO_INVALID; ++gpiolist) {
117
118                 /* Free the gpio */
119                 gpio_free(gpiolist->data.gpio);
120         }
121
122         gpioirq = gdata->gpioirq;
123         for (; gpioirq->id != GPIO_INVALID; ++gpioirq) {
124
125                 /* Free the irq */
126                 free_irq(gpio_to_irq(gpioirq->id), gpioirq->cookie);
127         }
128         return 0;
129 }
130
131 static ssize_t tegra_bb_attr_write(struct device *dev,
132                         struct device_attribute *attr,
133                         const char *buf, size_t count)
134 {
135         int val;
136
137         if (sscanf(buf, "%d", &val) != 1)
138                 return -EINVAL;
139
140         if (callback && callback->attrib) {
141                 if (!callback->attrib(dev, val))
142                         attr_load_val = val;
143         }
144         return count;
145 }
146
147 static ssize_t tegra_bb_attr_read(struct device *dev,
148                         struct device_attribute *attr, char *buf)
149 {
150         return sprintf(buf, "%d", attr_load_val);
151 }
152
153 static DEVICE_ATTR(load, S_IRUSR | S_IWUSR | S_IRGRP,
154                 tegra_bb_attr_read, tegra_bb_attr_write);
155
156 static void tegra_usbdevice_added(struct usb_device *udev)
157 {
158         const struct usb_device_descriptor *desc = &udev->descriptor;
159
160         if (desc->idVendor == mdata->vid &&
161             desc->idProduct == mdata->pid) {
162                 pr_debug("%s: Device %s added.\n", udev->product, __func__);
163
164                 if (mdata->wake_capable)
165                         device_set_wakeup_enable(&udev->dev, true);
166                 if (mdata->autosuspend_ready)
167                         usb_enable_autosuspend(udev);
168                 if (mdata->reg_cb)
169                         mdata->reg_cb(udev);
170         }
171 }
172
173 static void tegra_usbdevice_removed(struct usb_device *udev)
174 {
175         const struct usb_device_descriptor *desc = &udev->descriptor;
176
177         if (desc->idVendor == mdata->vid &&
178             desc->idProduct == mdata->pid) {
179                 pr_debug("%s: Device %s removed.\n", udev->product, __func__);
180         }
181 }
182
183 static int tegra_usb_notify(struct notifier_block *self, unsigned long action,
184                       void *dev)
185 {
186         switch (action) {
187         case USB_DEVICE_ADD:
188                 tegra_usbdevice_added((struct usb_device *)dev);
189                 break;
190         case USB_DEVICE_REMOVE:
191                 tegra_usbdevice_removed((struct usb_device *)dev);
192                 break;
193         }
194         return NOTIFY_OK;
195 }
196
197 static struct notifier_block tegra_usb_nb = {
198         .notifier_call = tegra_usb_notify,
199 };
200
201 static int tegra_bb_power_probe(struct platform_device *device)
202 {
203         struct device *dev = &device->dev;
204         struct tegra_bb_pdata *pdata;
205         struct tegra_bb_power_data *data;
206         struct tegra_bb_power_gdata *gdata;
207         int err;
208         unsigned int bb_id;
209
210         pdata = (struct tegra_bb_pdata *) dev->platform_data;
211         if (!pdata) {
212                 pr_err("%s - Error: platform data is empty.\n", __func__);
213                 return -ENODEV;
214         }
215
216         /* Obtain BB specific callback list */
217         bb_id = pdata->bb_id;
218         if (get_cblist[bb_id] != NULL) {
219                 callback = (struct tegra_bb_callback *) get_cblist[bb_id]();
220                 if (callback && callback->init) {
221                         data = (struct tegra_bb_power_data *)
222                         callback->init((void *)pdata);
223
224                         gdata = data->gpio_data;
225                         if (!gdata) {
226                                 pr_err("%s - Error: Gpio data is empty.\n",
227                                                                 __func__);
228                                 return -ENODEV;
229                         }
230
231                         /* Initialize gpio as required */
232                         tegra_bb_power_gpio_init(gdata);
233
234                         mdata = data->modem_data;
235                         if (mdata && mdata->vid && mdata->pid)
236                                 /* Register to notifications from usb core */
237                                 usb_register_notify(&tegra_usb_nb);
238                 } else {
239                         pr_err("%s - Error: init callback is empty.\n",
240                                                                 __func__);
241                         return -ENODEV;
242                 }
243         } else {
244                 pr_err("%s - Error: callback data is empty.\n", __func__);
245                 return -ENODEV;
246         }
247
248         /* Create the control sysfs node */
249         err = device_create_file(dev, &dev_attr_load);
250         if (err < 0) {
251                 pr_err("%s - Error: device_create_file failed.\n", __func__);
252                 return -ENODEV;
253         }
254         attr_load_val = 0;
255
256         return 0;
257 }
258
259 static int tegra_bb_power_remove(struct platform_device *device)
260 {
261         struct device *dev = &device->dev;
262         struct tegra_bb_power_data *data;
263         struct tegra_bb_power_gdata *gdata;
264
265         /* BB specific callback */
266         if (callback && callback->deinit) {
267                 data = (struct tegra_bb_power_data *)
268                 callback->deinit();
269
270                 /* Deinitialize gpios */
271                 gdata = data->gpio_data;
272                 if (gdata)
273                         tegra_bb_power_gpio_deinit(gdata);
274                 else {
275                         pr_err("%s - Error: Gpio data is empty.\n", __func__);
276                         return -ENODEV;
277                 }
278
279                 mdata = data->modem_data;
280                 if (mdata && mdata->vid && mdata->pid)
281                         /* Register to notifications from usb core */
282                         usb_unregister_notify(&tegra_usb_nb);
283         }
284
285         /* Remove the control sysfs node */
286         device_remove_file(dev, &dev_attr_load);
287
288         return 0;
289 }
290
291 #ifdef CONFIG_PM
292 static int tegra_bb_power_suspend(struct platform_device *device,
293         pm_message_t state)
294 {
295         /* BB specific callback */
296         if (callback && callback->power)
297                 callback->power(PWRSTATE_L2L3);
298         return 0;
299 }
300
301 static int tegra_bb_power_resume(struct platform_device *device)
302 {
303         /* BB specific callback */
304         if (callback && callback->power)
305                 callback->power(PWRSTATE_L3L0);
306         return 0;
307 }
308 #endif
309
310 static struct platform_driver tegra_bb_power_driver = {
311         .probe = tegra_bb_power_probe,
312         .remove = tegra_bb_power_remove,
313 #ifdef CONFIG_PM
314         .suspend = tegra_bb_power_suspend,
315         .resume = tegra_bb_power_resume,
316 #endif
317         .driver = {
318                 .name = "tegra_baseband_power",
319         },
320 };
321
322 static int __init tegra_baseband_power_init(void)
323 {
324         pr_debug("%s\n", __func__);
325         return platform_driver_register(&tegra_bb_power_driver);
326 }
327
328 static void __exit tegra_baseband_power_exit(void)
329 {
330         pr_debug("%s\n", __func__);
331         platform_driver_unregister(&tegra_bb_power_driver);
332 }
333
334 module_init(tegra_baseband_power_init)
335 module_exit(tegra_baseband_power_exit)
336 MODULE_AUTHOR("NVIDIA Corporation");
337 MODULE_DESCRIPTION("Tegra modem power management driver");
338 MODULE_LICENSE("GPL");