ce6f749605
The commit 2a9de9c0
("extcon: Use the unique id for external connector instead
of string") defines the unique id of each external connector to identify the
type of external connector instead of string name. So, devm_extcon_dev_allocate()
should include the second parameter (unsigned int *supported_cable). This patch
adds the supported_cable parameter which is passed by platform data.
Signed-off-by: Chanwoo Choi <cw00.choi@samsung.com>
163 lines
4.0 KiB
C
163 lines
4.0 KiB
C
/*
|
|
* drivers/extcon/extcon_gpio.c
|
|
*
|
|
* Single-state GPIO extcon driver based on extcon class
|
|
*
|
|
* Copyright (C) 2008 Google, Inc.
|
|
* Author: Mike Lockwood <lockwood@android.com>
|
|
*
|
|
* Modified by MyungJoo Ham <myungjoo.ham@samsung.com> to support extcon
|
|
* (originally switch class is supported)
|
|
*
|
|
* This software is licensed under the terms of the GNU General Public
|
|
* License version 2, as published by the Free Software Foundation, and
|
|
* may be copied, distributed, and modified under those terms.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
*/
|
|
|
|
#include <linux/extcon.h>
|
|
#include <linux/extcon/extcon-gpio.h>
|
|
#include <linux/gpio.h>
|
|
#include <linux/init.h>
|
|
#include <linux/interrupt.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
#include <linux/platform_device.h>
|
|
#include <linux/slab.h>
|
|
#include <linux/workqueue.h>
|
|
|
|
struct gpio_extcon_data {
|
|
struct extcon_dev *edev;
|
|
int irq;
|
|
struct delayed_work work;
|
|
unsigned long debounce_jiffies;
|
|
|
|
struct gpio_extcon_platform_data *pdata;
|
|
};
|
|
|
|
static void gpio_extcon_work(struct work_struct *work)
|
|
{
|
|
int state;
|
|
struct gpio_extcon_data *data =
|
|
container_of(to_delayed_work(work), struct gpio_extcon_data,
|
|
work);
|
|
|
|
state = gpio_get_value(data->pdata->gpio);
|
|
if (data->pdata->gpio_active_low)
|
|
state = !state;
|
|
extcon_set_state(data->edev, state);
|
|
}
|
|
|
|
static irqreturn_t gpio_irq_handler(int irq, void *dev_id)
|
|
{
|
|
struct gpio_extcon_data *data = dev_id;
|
|
|
|
queue_delayed_work(system_power_efficient_wq, &data->work,
|
|
data->debounce_jiffies);
|
|
return IRQ_HANDLED;
|
|
}
|
|
|
|
static int gpio_extcon_probe(struct platform_device *pdev)
|
|
{
|
|
struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);
|
|
struct gpio_extcon_data *data;
|
|
int ret;
|
|
|
|
if (!pdata)
|
|
return -EBUSY;
|
|
if (!pdata->irq_flags || pdata->extcon_id > EXTCON_NONE)
|
|
return -EINVAL;
|
|
|
|
data = devm_kzalloc(&pdev->dev, sizeof(struct gpio_extcon_data),
|
|
GFP_KERNEL);
|
|
if (!data)
|
|
return -ENOMEM;
|
|
|
|
data->edev = devm_extcon_dev_allocate(&pdev->dev, &pdata->extcon_id);
|
|
if (IS_ERR(data->edev)) {
|
|
dev_err(&pdev->dev, "failed to allocate extcon device\n");
|
|
return -ENOMEM;
|
|
}
|
|
data->pdata = pdata;
|
|
|
|
ret = devm_gpio_request_one(&pdev->dev, data->pdata->gpio, GPIOF_DIR_IN,
|
|
pdev->name);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
if (pdata->debounce) {
|
|
ret = gpio_set_debounce(data->pdata->gpio,
|
|
pdata->debounce * 1000);
|
|
if (ret < 0)
|
|
data->debounce_jiffies =
|
|
msecs_to_jiffies(pdata->debounce);
|
|
}
|
|
|
|
ret = devm_extcon_dev_register(&pdev->dev, data->edev);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
INIT_DELAYED_WORK(&data->work, gpio_extcon_work);
|
|
|
|
data->irq = gpio_to_irq(data->pdata->gpio);
|
|
if (data->irq < 0)
|
|
return data->irq;
|
|
|
|
ret = devm_request_any_context_irq(&pdev->dev, data->irq,
|
|
gpio_irq_handler, pdata->irq_flags,
|
|
pdev->name, data);
|
|
if (ret < 0)
|
|
return ret;
|
|
|
|
platform_set_drvdata(pdev, data);
|
|
/* Perform initial detection */
|
|
gpio_extcon_work(&data->work.work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int gpio_extcon_remove(struct platform_device *pdev)
|
|
{
|
|
struct gpio_extcon_data *data = platform_get_drvdata(pdev);
|
|
|
|
cancel_delayed_work_sync(&data->work);
|
|
|
|
return 0;
|
|
}
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int gpio_extcon_resume(struct device *dev)
|
|
{
|
|
struct gpio_extcon_data *data;
|
|
|
|
data = dev_get_drvdata(dev);
|
|
if (data->pdata->check_on_resume)
|
|
queue_delayed_work(system_power_efficient_wq,
|
|
&data->work, data->debounce_jiffies);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|
|
|
|
static SIMPLE_DEV_PM_OPS(gpio_extcon_pm_ops, NULL, gpio_extcon_resume);
|
|
|
|
static struct platform_driver gpio_extcon_driver = {
|
|
.probe = gpio_extcon_probe,
|
|
.remove = gpio_extcon_remove,
|
|
.driver = {
|
|
.name = "extcon-gpio",
|
|
.pm = &gpio_extcon_pm_ops,
|
|
},
|
|
};
|
|
|
|
module_platform_driver(gpio_extcon_driver);
|
|
|
|
MODULE_AUTHOR("Mike Lockwood <lockwood@android.com>");
|
|
MODULE_DESCRIPTION("GPIO extcon driver");
|
|
MODULE_LICENSE("GPL");
|