linux/drivers/extcon/extcon-gpio.c
<<
>>
Prefs
   1/*
   2 * extcon_gpio.c - Single-state GPIO extcon driver based on extcon class
   3 *
   4 * Copyright (C) 2008 Google, Inc.
   5 * Author: Mike Lockwood <lockwood@android.com>
   6 *
   7 * Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
   8 * (originally switch class is supported)
   9 *
  10 * This software is licensed under the terms of the GNU General Public
  11 * License version 2, as published by the Free Software Foundation, and
  12 * may be copied, distributed, and modified under those terms.
  13 *
  14 * This program is distributed in the hope that it will be useful,
  15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17 * GNU General Public License for more details.
  18 */
  19
  20#include <linux/extcon.h>
  21#include <linux/extcon/extcon-gpio.h>
  22#include <linux/gpio.h>
  23#include <linux/gpio/consumer.h>
  24#include <linux/init.h>
  25#include <linux/interrupt.h>
  26#include <linux/kernel.h>
  27#include <linux/module.h>
  28#include <linux/platform_device.h>
  29#include <linux/slab.h>
  30#include <linux/workqueue.h>
  31
  32struct gpio_extcon_data {
  33        struct extcon_dev *edev;
  34        int irq;
  35        struct delayed_work work;
  36        unsigned long debounce_jiffies;
  37
  38        struct gpio_desc *id_gpiod;
  39        struct gpio_extcon_pdata *pdata;
  40};
  41
  42static void gpio_extcon_work(struct work_struct *work)
  43{
  44        int state;
  45        struct gpio_extcon_data *data =
  46                container_of(to_delayed_work(work), struct gpio_extcon_data,
  47                             work);
  48
  49        state = gpiod_get_value_cansleep(data->id_gpiod);
  50        if (data->pdata->gpio_active_low)
  51                state = !state;
  52
  53        extcon_set_state_sync(data->edev, data->pdata->extcon_id, state);
  54}
  55
  56static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
  57{
  58        struct gpio_extcon_data *data = dev_id;
  59
  60        queue_delayed_work(system_power_efficient_wq, &data->work,
  61                              data->debounce_jiffies);
  62        return IRQ_HANDLED;
  63}
  64
  65static int gpio_extcon_init(struct device *dev, struct gpio_extcon_data *data)
  66{
  67        struct gpio_extcon_pdata *pdata = data->pdata;
  68        int ret;
  69
  70        ret = devm_gpio_request_one(dev, pdata->gpio, GPIOF_DIR_IN,
  71                                dev_name(dev));
  72        if (ret < 0)
  73                return ret;
  74
  75        data->id_gpiod = gpio_to_desc(pdata->gpio);
  76        if (!data->id_gpiod)
  77                return -EINVAL;
  78
  79        if (pdata->debounce) {
  80                ret = gpiod_set_debounce(data->id_gpiod,
  81                                        pdata->debounce * 1000);
  82                if (ret < 0)
  83                        data->debounce_jiffies =
  84                                msecs_to_jiffies(pdata->debounce);
  85        }
  86
  87        data->irq = gpiod_to_irq(data->id_gpiod);
  88        if (data->irq < 0)
  89                return data->irq;
  90
  91        return 0;
  92}
  93
  94static int gpio_extcon_probe(struct platform_device *pdev)
  95{
  96        struct gpio_extcon_pdata *pdata = dev_get_platdata(&pdev->dev);
  97        struct gpio_extcon_data *data;
  98        int ret;
  99
 100        if (!pdata)
 101                return -EBUSY;
 102        if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
 103                return -EINVAL;
 104
 105        data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
 106                                   GFP_KERNEL);
 107        if (!data)
 108                return -ENOMEM;
 109        data->pdata = pdata;
 110
 111        /* Initialize the gpio */
 112        ret = gpio_extcon_init(&pdev->dev, data);
 113        if (ret < 0)
 114                return ret;
 115
 116        /* Allocate the memory of extcon devie and register extcon device */
 117        data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
 118        if (IS_ERR(data->edev)) {
 119                dev_err(&pdev->dev, "failed to allocate extcon device\n");
 120                return -ENOMEM;
 121        }
 122
 123        ret = devm_extcon_dev_register(&pdev->dev, data->edev);
 124        if (ret < 0)
 125                return ret;
 126
 127        INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
 128
 129        /*
 130         * Request the interrupt of gpio to detect whether external connector
 131         * is attached or detached.
 132         */
 133        ret = devm_request_any_context_irq(&pdev->dev, data->irq,
 134                                        gpio_irq_handler, pdata->irq_flags,
 135                                        pdev->name, data);
 136        if (ret < 0)
 137                return ret;
 138
 139        platform_set_drvdata(pdev, data);
 140        /* Perform initial detection */
 141        gpio_extcon_work(&data->work.work);
 142
 143        return 0;
 144}
 145
 146static int gpio_extcon_remove(struct platform_device *pdev)
 147{
 148        struct gpio_extcon_data *data = platform_get_drvdata(pdev);
 149
 150        cancel_delayed_work_sync(&data->work);
 151
 152        return 0;
 153}
 154
 155#ifdef CONFIG_PM_SLEEP
 156static int gpio_extcon_resume(struct device *dev)
 157{
 158        struct gpio_extcon_data *data;
 159
 160        data = dev_get_drvdata(dev);
 161        if (data->pdata->check_on_resume)
 162                queue_delayed_work(system_power_efficient_wq,
 163                        &data->work, data->debounce_jiffies);
 164
 165        return 0;
 166}
 167#endif
 168
 169static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
 170
 171static struct platform_driver gpio_extcon_driver = {
 172        .probe          = gpio_extcon_probe,
 173        .remove         = gpio_extcon_remove,
 174        .driver         = {
 175                .name   = "extcon-gpio",
 176                .pm     = &gpio_extcon_pm_ops,
 177        },
 178};
 179
 180module_platform_driver(gpio_extcon_driver);
 181
 182MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
 183MODULE_DESCRIPTION("GPIO extcon driver");
 184MODULE_LICENSE("GPL");
 185
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.