diff options
Diffstat (limited to 'drivers/extcon/extcon-gpio.c')
| -rw-r--r-- | drivers/extcon/extcon-gpio.c | 78 | 
1 files changed, 54 insertions, 24 deletions
diff --git a/drivers/extcon/extcon-gpio.c b/drivers/extcon/extcon-gpio.c index f874c30ddbf..645b2835681 100644 --- a/drivers/extcon/extcon-gpio.c +++ b/drivers/extcon/extcon-gpio.c @@ -32,13 +32,15 @@  #include <linux/extcon/extcon-gpio.h>  struct gpio_extcon_data { -	struct extcon_dev edev; +	struct extcon_dev *edev;  	unsigned gpio; +	bool gpio_active_low;  	const char *state_on;  	const char *state_off;  	int irq;  	struct delayed_work work;  	unsigned long debounce_jiffies; +	bool check_on_resume;  };  static void gpio_extcon_work(struct work_struct *work) @@ -49,7 +51,9 @@ static void gpio_extcon_work(struct work_struct *work)  			     work);  	state = gpio_get_value(data->gpio); -	extcon_set_state(&data->edev, state); +	if (data->gpio_active_low) +		state = !state; +	extcon_set_state(data->edev, state);  }  static irqreturn_t gpio_irq_handler(int irq, void *dev_id) @@ -63,9 +67,10 @@ static irqreturn_t gpio_irq_handler(int irq, void *dev_id)  static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)  { -	struct gpio_extcon_data	*extcon_data = -		container_of(edev, struct gpio_extcon_data, edev); +	struct device *dev = edev->dev.parent; +	struct gpio_extcon_data *extcon_data = dev_get_drvdata(dev);  	const char *state; +  	if (extcon_get_state(edev))  		state = extcon_data->state_on;  	else @@ -78,9 +83,9 @@ static ssize_t extcon_gpio_print_state(struct extcon_dev *edev, char *buf)  static int gpio_extcon_probe(struct platform_device *pdev)  { -	struct gpio_extcon_platform_data *pdata = pdev->dev.platform_data; +	struct gpio_extcon_platform_data *pdata = dev_get_platdata(&pdev->dev);  	struct gpio_extcon_data *extcon_data; -	int ret = 0; +	int ret;  	if (!pdata)  		return -EBUSY; @@ -94,47 +99,56 @@ static int gpio_extcon_probe(struct platform_device *pdev)  	if (!extcon_data)  		return -ENOMEM; -	extcon_data->edev.name = pdata->name; +	extcon_data->edev = devm_extcon_dev_allocate(&pdev->dev, NULL); +	if (IS_ERR(extcon_data->edev)) { +		dev_err(&pdev->dev, "failed to allocate extcon device\n"); +		return -ENOMEM; +	} +	extcon_data->edev->name = pdata->name; +	extcon_data->edev->dev.parent = &pdev->dev; +  	extcon_data->gpio = pdata->gpio; +	extcon_data->gpio_active_low = pdata->gpio_active_low;  	extcon_data->state_on = pdata->state_on;  	extcon_data->state_off = pdata->state_off; +	extcon_data->check_on_resume = pdata->check_on_resume;  	if (pdata->state_on && pdata->state_off) -		extcon_data->edev.print_state = extcon_gpio_print_state; -	extcon_data->debounce_jiffies = msecs_to_jiffies(pdata->debounce); +		extcon_data->edev->print_state = extcon_gpio_print_state; -	ret = extcon_dev_register(&extcon_data->edev, &pdev->dev); +	ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, +				    pdev->name);  	if (ret < 0)  		return ret; -	ret = devm_gpio_request_one(&pdev->dev, extcon_data->gpio, GPIOF_DIR_IN, -				    pdev->name); +	if (pdata->debounce) { +		ret = gpio_set_debounce(extcon_data->gpio, +					pdata->debounce * 1000); +		if (ret < 0) +			extcon_data->debounce_jiffies = +				msecs_to_jiffies(pdata->debounce); +	} + +	ret = devm_extcon_dev_register(&pdev->dev, extcon_data->edev);  	if (ret < 0) -		goto err; +		return ret;  	INIT_DELAYED_WORK(&extcon_data->work, gpio_extcon_work);  	extcon_data->irq = gpio_to_irq(extcon_data->gpio); -	if (extcon_data->irq < 0) { -		ret = extcon_data->irq; -		goto err; -	} +	if (extcon_data->irq < 0) +		return extcon_data->irq;  	ret = request_any_context_irq(extcon_data->irq, gpio_irq_handler,  				      pdata->irq_flags, pdev->name,  				      extcon_data);  	if (ret < 0) -		goto err; +		return ret;  	platform_set_drvdata(pdev, extcon_data);  	/* Perform initial detection */  	gpio_extcon_work(&extcon_data->work.work);  	return 0; - -err: -	extcon_dev_unregister(&extcon_data->edev); - -	return ret;  }  static int gpio_extcon_remove(struct platform_device *pdev) @@ -143,17 +157,33 @@ static int gpio_extcon_remove(struct platform_device *pdev)  	cancel_delayed_work_sync(&extcon_data->work);  	free_irq(extcon_data->irq, extcon_data); -	extcon_dev_unregister(&extcon_data->edev);  	return 0;  } +#ifdef CONFIG_PM_SLEEP +static int gpio_extcon_resume(struct device *dev) +{ +	struct gpio_extcon_data *extcon_data; + +	extcon_data = dev_get_drvdata(dev); +	if (extcon_data->check_on_resume) +		queue_delayed_work(system_power_efficient_wq, +			&extcon_data->work, extcon_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",  		.owner	= THIS_MODULE, +		.pm	= &gpio_extcon_pm_ops,  	},  };  | 
