]> rtime.felk.cvut.cz Git - sojka/nv-tegra/linux-3.10.git/blob - drivers/extcon/extcon-gpio-states.c
extcon: gpio-states: cable detection check in resume
[sojka/nv-tegra/linux-3.10.git] / drivers / extcon / extcon-gpio-states.c
1 /*
2  * drivers/extcon/extcon-gpio-states.c
3  *
4  * Multiple GPIO state based based on extcon class driver.
5  *
6  * Author: Laxman Dewangan <ldewangan@nvidia.com>
7  *
8  * Based on extcon-gpio driver by
9  *      Copyright (C) 2008 Google, Inc.
10  *      Author: Mike Lockwood <lockwood@android.com>
11  *
12  * This software is licensed under the terms of the GNU General Public
13  * License version 2, as published by the Free Software Foundation, and
14  * may be copied, distributed, and modified under those terms.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  */
21
22 #include <linux/module.h>
23 #include <linux/kernel.h>
24 #include <linux/init.h>
25 #include <linux/interrupt.h>
26 #include <linux/platform_device.h>
27 #include <linux/slab.h>
28 #include <linux/workqueue.h>
29 #include <linux/gpio.h>
30 #include <linux/extcon.h>
31 #include <linux/of.h>
32 #include <linux/of_gpio.h>
33 #include <linux/spinlock.h>
34 #include <linux/wakelock.h>
35
36 #define EXTCON_GPIO_STATE_WAKEUP_TIME           5000
37
38 struct gpio_extcon_cables {
39         int gstate;
40         int cstate;
41 };
42
43 struct gpio_info {
44         int gpio;
45         int irq;
46 };
47
48 struct gpio_extcon_platform_data {
49         const char *name;
50         unsigned long debounce;
51         unsigned long irq_flags;
52         struct gpio_info *gpios;
53         int n_gpio;
54         const char **out_cable_name;
55         int n_out_cables;
56         struct gpio_extcon_cables *cable_states;
57         int n_cable_states;
58         int cable_detect_delay;
59 };
60
61 struct gpio_extcon_info {
62         struct device *dev;
63         struct extcon_dev edev;
64         struct delayed_work work;
65         unsigned long debounce_jiffies;
66         struct timer_list timer;
67         int timer_to_work_jiffies;
68         spinlock_t lock;
69         int *gpio_curr_state;
70         struct gpio_extcon_platform_data *pdata;
71         struct wake_lock wake_lock;
72         int cable_detect_jiffies;
73 };
74
75 static void gpio_extcon_work(struct work_struct *work)
76 {
77         int state = 0;
78         int cstate = -1;
79         struct gpio_extcon_info *gpex = container_of(to_delayed_work(work),
80                                         struct gpio_extcon_info, work);
81         int gstate = 0;
82         int i;
83
84         for (i = 0; i < gpex->pdata->n_gpio; ++i) {
85                 state = gpio_get_value_cansleep(gpex->pdata->gpios[i].gpio);
86                 if (state)
87                         gstate |= BIT(i);
88         }
89
90         for (i = 0; i < gpex->pdata->n_cable_states; ++i) {
91                 if (gpex->pdata->cable_states[i].gstate == gstate) {
92                         cstate = gpex->pdata->cable_states[i].cstate;
93                         break;
94                 }
95         }
96
97         if (cstate == -1) {
98                 dev_info(gpex->dev, "Cable state not found 0x%02x\n", gstate);
99                 cstate = 0;
100         }
101
102         dev_info(gpex->dev, "Cable state %d\n", cstate);
103         extcon_set_state(&gpex->edev, cstate);
104 }
105
106 static void gpio_extcon_notifier_timer(unsigned long _data)
107 {
108         struct gpio_extcon_info *gpex = (struct gpio_extcon_info *)_data;
109
110         /*take wakelock to complete cable detection */
111         if (!wake_lock_active(&gpex->wake_lock))
112                 wake_lock_timeout(&gpex->wake_lock, gpex->cable_detect_jiffies);
113
114         schedule_delayed_work(&gpex->work, gpex->timer_to_work_jiffies);
115 }
116
117 static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
118 {
119         struct gpio_extcon_info *gpex = dev_id;
120         unsigned long flags;
121
122         spin_lock_irqsave(&gpex->lock, flags);
123         mod_timer(&gpex->timer, jiffies + gpex->debounce_jiffies);
124         spin_unlock_irqrestore(&gpex->lock, flags);
125
126         return IRQ_HANDLED;
127 }
128
129 static struct gpio_extcon_platform_data *of_get_platform_data(
130                 struct platform_device *pdev)
131 {
132         struct gpio_extcon_platform_data *pdata;
133         struct device_node *np = pdev->dev.of_node;
134         int gpio;
135         int n_gpio;
136         u32 pval;
137         int ret;
138         const char *names;
139         struct property *prop;
140         int count;
141
142         pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
143         if (!pdata)
144                 return ERR_PTR(-ENOMEM);
145
146         of_property_read_string(np, "extcon-gpio,name", &pdata->name);
147         if (!pdata->name)
148                 pdata->name = np->name;
149
150         n_gpio = of_gpio_named_count(np, "gpios");
151         if (n_gpio < 1) {
152                 dev_err(&pdev->dev, "Not sufficient gpios\n");
153                 return ERR_PTR(-EINVAL);
154         }
155
156         pdata->n_gpio = n_gpio;
157         pdata->gpios = devm_kzalloc(&pdev->dev,
158                         sizeof(*pdata->gpios) * n_gpio, GFP_KERNEL);
159         if (!pdata->gpios)
160                 return ERR_PTR(-ENOMEM);
161         for (count = 0; count < n_gpio; ++count) {
162                 gpio = of_get_named_gpio(np, "gpios", count);
163                 if ((gpio < 0) && (gpio != -ENOENT))
164                         return ERR_PTR(gpio);
165                 pdata->gpios[count].gpio = gpio;
166         }
167
168         ret = of_property_read_u32(np, "extcon-gpio,irq-flags", &pval);
169         if (!ret)
170                 pdata->irq_flags = pval;
171         else
172                 pdata->irq_flags = IRQF_TRIGGER_RISING |
173                                                 IRQF_TRIGGER_FALLING;
174
175         ret = of_property_read_u32(np, "extcon-gpio,debounce", &pval);
176         if (!ret)
177                 pdata->debounce = pval;
178         else
179                 pdata->debounce = 10;
180
181         ret = of_property_read_u32(np, "cable-detect-delay", &pval);
182         if (!ret)
183                 pdata->cable_detect_delay = pval;
184         else
185                 pdata->cable_detect_delay = EXTCON_GPIO_STATE_WAKEUP_TIME;
186
187         pdata->n_out_cables = of_property_count_strings(np,
188                                         "extcon-gpio,out-cable-names");
189         if (pdata->n_out_cables <= 0) {
190                 dev_err(&pdev->dev, "not found out cable names\n");
191                 return ERR_PTR(-EINVAL);
192         }
193
194         pdata->out_cable_name = devm_kzalloc(&pdev->dev,
195                                 (pdata->n_out_cables + 1) *
196                                 sizeof(*pdata->out_cable_name), GFP_KERNEL);
197         if (!pdata->out_cable_name)
198                 return ERR_PTR(-ENOMEM);
199         count = 0;
200         of_property_for_each_string(np, "extcon-gpio,out-cable-names",
201                                                 prop, names)
202                 pdata->out_cable_name[count++] = names;
203         pdata->out_cable_name[count] = NULL;
204
205         pdata->n_cable_states = of_property_count_u32(np,
206                                                 "extcon-gpio,cable-states");
207         if (pdata->n_cable_states < 2) {
208                 dev_err(&pdev->dev, "not found proper cable state\n");
209                 return ERR_PTR(-EINVAL);
210         }
211         pdata->n_cable_states /= 2;
212         pdata->cable_states = devm_kzalloc(&pdev->dev,
213                                 (pdata->n_cable_states) *
214                                 sizeof(*pdata->cable_states), GFP_KERNEL);
215         if (!pdata->cable_states)
216                 return ERR_PTR(-ENOMEM);
217         for (count = 0;  count < pdata->n_cable_states; ++count) {
218                 ret = of_property_read_u32_index(np, "extcon-gpio,cable-states",
219                                 count * 2, &pval);
220                 if (!ret)
221                         pdata->cable_states[count].gstate = pval;
222
223                 ret = of_property_read_u32_index(np, "extcon-gpio,cable-states",
224                                 count * 2 + 1, &pval);
225                 if (!ret)
226                         pdata->cable_states[count].cstate = pval;
227         }
228
229         return pdata;
230 }
231
232 static int gpio_extcon_probe(struct platform_device *pdev)
233 {
234         struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data;
235         struct gpio_extcon_info *gpex;
236         int ret = 0;
237         int i;
238
239         if (!pdata && pdev->dev.of_node) {
240                 pdata = of_get_platform_data(pdev);
241                 if (IS_ERR(pdata))
242                         return PTR_ERR(pdata);
243         }
244         if (!pdata)
245                 return -EINVAL;
246
247         if (!pdata->irq_flags) {
248                 dev_err(&pdev->dev, "IRQ flag is not specified.\n");
249                 return -EINVAL;
250         }
251
252         gpex = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_info),
253                                    GFP_KERNEL);
254         if (!gpex)
255                 return -ENOMEM;
256
257         gpex->dev = &pdev->dev;
258         gpex->edev.name = pdata->name;
259         gpex->edev.dev.parent = &pdev->dev;
260         gpex->debounce_jiffies = msecs_to_jiffies(pdata->debounce);
261         gpex->timer_to_work_jiffies = msecs_to_jiffies(100);
262         gpex->edev.supported_cable = pdata->out_cable_name;
263         gpex->cable_detect_jiffies =
264                         msecs_to_jiffies(pdata->cable_detect_delay);
265         gpex->pdata = pdata;
266         spin_lock_init(&gpex->lock);
267
268         for (i = 0; i < gpex->pdata->n_gpio; ++i) {
269                 int irq;
270
271                 irq =  gpio_to_irq(gpex->pdata->gpios[i].gpio);
272                 if (irq < 0) {
273                         dev_err(&pdev->dev, "gpio %d to irq failed: %d\n",
274                                         gpex->pdata->gpios[i].gpio, irq);
275                         return irq;
276                 }
277                 gpex->pdata->gpios[i].irq = irq;
278         }
279
280         wake_lock_init(&gpex->wake_lock, WAKE_LOCK_SUSPEND,
281                                                 "extcon-suspend-lock");
282
283         ret = extcon_dev_register(&gpex->edev);
284         if (ret < 0)
285                 return ret;
286
287         INIT_DELAYED_WORK(&gpex->work, gpio_extcon_work);
288         setup_timer(&gpex->timer, gpio_extcon_notifier_timer,
289                         (unsigned long)gpex);
290
291         for (i = 0; i < gpex->pdata->n_gpio; ++i) {
292                 int gpio = gpex->pdata->gpios[i].gpio;
293                 int irq = gpex->pdata->gpios[i].irq;
294
295                 ret = devm_gpio_request_one(&pdev->dev, gpio, GPIOF_DIR_IN,
296                                     pdev->name);
297                 if (ret < 0)
298                         goto err;
299
300                 ret = devm_request_any_context_irq(&pdev->dev, irq,
301                                 gpio_irq_handler, pdata->irq_flags,
302                                 pdev->name, gpex);
303                 if (ret < 0)
304                         goto err;
305         }
306
307         platform_set_drvdata(pdev, gpex);
308         device_set_wakeup_capable(gpex->dev, true);
309
310         /* Enable wake default */
311         device_wakeup_enable(gpex->dev);
312
313         /* Perform initial detection */
314         gpio_extcon_work(&gpex->work.work);
315         return 0;
316
317 err:
318         extcon_dev_unregister(&gpex->edev);
319         return ret;
320 }
321
322 static int gpio_extcon_remove(struct platform_device *pdev)
323 {
324         struct gpio_extcon_info *gpex = platform_get_drvdata(pdev);
325
326         del_timer_sync(&gpex->timer);
327         cancel_delayed_work_sync(&gpex->work);
328         extcon_dev_unregister(&gpex->edev);
329         return 0;
330 }
331
332 #ifdef CONFIG_PM_SLEEP
333 static int gpio_extcon_suspend(struct device *dev)
334 {
335         struct gpio_extcon_info *gpex = dev_get_drvdata(dev);
336         int i;
337
338         cancel_delayed_work_sync(&gpex->work);
339         if (device_may_wakeup(gpex->dev)) {
340                 for (i = 0; i < gpex->pdata->n_gpio; ++i)
341                         enable_irq_wake(gpex->pdata->gpios[i].irq);
342         }
343
344         return 0;
345 }
346
347 static int gpio_extcon_resume(struct device *dev)
348 {
349         struct gpio_extcon_info *gpex = dev_get_drvdata(dev);
350         int i;
351
352         if (device_may_wakeup(gpex->dev)) {
353                 for (i = 0; i < gpex->pdata->n_gpio; ++i)
354                         disable_irq_wake(gpex->pdata->gpios[i].irq);
355         }
356         gpio_extcon_work(&gpex->work.work);
357
358         return 0;
359 }
360 #endif /* CONFIG_PM_SLEEP */
361
362 static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, gpio_extcon_suspend,
363                                                 gpio_extcon_resume);
364
365 static struct of_device_id of_extcon_gpio_tbl[] = {
366         { .compatible = "extcon-gpio-states", },
367         { /* end */ }
368 };
369 MODULE_DEVICE_TABLE(of, of_extcon_gpio_tbl);
370
371 static struct platform_driver gpio_extcon_driver = {
372         .probe          = gpio_extcon_probe,
373         .remove         = gpio_extcon_remove,
374         .driver         = {
375                 .name   = "extcon-gpio-states",
376                 .owner  = THIS_MODULE,
377                 .of_match_table = of_extcon_gpio_tbl,
378                 .pm = &gpio_extcon_pm_ops,
379         },
380 };
381
382 static int __init gpio_extcon_driver_init(void)
383 {
384         return platform_driver_register(&gpio_extcon_driver);
385 }
386 subsys_initcall_sync(gpio_extcon_driver_init);
387
388 static void __exit gpio_extcon_driver_exit(void)
389 {
390         platform_driver_unregister(&gpio_extcon_driver);
391 }
392 module_exit(gpio_extcon_driver_exit);
393
394 MODULE_AUTHOR("Laxman Dewangan <ldewangan@nvidia.com>");
395 MODULE_DESCRIPTION("GPIO state based extcon driver");
396 MODULE_LICENSE("GPL v2");