gpio-button-hotplug: support interrupt properties
Upstream Linux's input gpio-keys driver supports
specifying a external interrupt for a gpio via the
'interrupts' properties as well as having support
for software debounce.
This patch ports these features to OpenWrt's event
version. Only the "pure" interrupt-driven support is
left behind, since this goes a bit against the "gpio"
in the "gpio-keys" and I don't have a real device to
test this with.
This patch also silences the generated warnings showing
up since 4.14 due to the 'constification' of the
struct gpio_keys_button *buttons variable in the
upstream struct gpio_keys_platform_data declaration.
gpio-button-hotplug.c: In function 'gpio_keys_get_devtree_pdata':
gpio-button-hotplug.c:392:10: warning: assignment discards 'const'
	qualifier from pointer target type [-Wdiscarded-qualifiers]
   button = &pdata->buttons[i++];
          ^
gpio-button-hotplug.c: In function 'gpio_keys_button_probe':
gpio-button-hotplug.c:537:12: warning: assignment discards 'const'
	qualifier from pointer target type [-Wdiscarded-qualifiers]
   bdata->b = &pdata->buttons[i];
            ^
gpio-button-hotplug.c: In function 'gpio_keys_probe':
gpio-button-hotplug.c:563:37: warning: initialization discards 'const'
	qualifier from pointer target type [-Wdiscarded-qualifiers]
   struct gpio_keys_button *button = &pdata->buttons[i];
                                   ^
Acked-by: Petr Štetiar <ynezz@true.cz>
Signed-off-by: Christian Lamparter <chunkeey@gmail.com>
			
			
This commit is contained in:
		| @@ -26,6 +26,7 @@ | |||||||
| #include <linux/interrupt.h> | #include <linux/interrupt.h> | ||||||
| #include <linux/platform_device.h> | #include <linux/platform_device.h> | ||||||
| #include <linux/of_gpio.h> | #include <linux/of_gpio.h> | ||||||
|  | #include <linux/of_irq.h> | ||||||
| #include <linux/gpio_keys.h> | #include <linux/gpio_keys.h> | ||||||
|  |  | ||||||
| #define DRV_NAME	"gpio-keys" | #define DRV_NAME	"gpio-keys" | ||||||
| @@ -70,7 +71,10 @@ struct gpio_keys_button_data { | |||||||
| 	int count; | 	int count; | ||||||
| 	int threshold; | 	int threshold; | ||||||
| 	int can_sleep; | 	int can_sleep; | ||||||
| 	struct gpio_keys_button *b; | 	int irq; | ||||||
|  | 	unsigned int software_debounce; | ||||||
|  | 	struct gpio_desc *gpiod; | ||||||
|  | 	const struct gpio_keys_button *b; | ||||||
| }; | }; | ||||||
|  |  | ||||||
| extern u64 uevent_next_seqnum(void); | extern u64 uevent_next_seqnum(void); | ||||||
| @@ -298,7 +302,7 @@ static void gpio_keys_polled_check_state(struct gpio_keys_button_data *bdata) | |||||||
| 			return; | 			return; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		if ((bdata->last_state != -1) || (type == EV_SW)) | 		if (bdata->last_state != -1 || type == EV_SW) | ||||||
| 			button_hotplug_event(bdata, type, state); | 			button_hotplug_event(bdata, type, state); | ||||||
|  |  | ||||||
| 		bdata->last_state = state; | 		bdata->last_state = state; | ||||||
| @@ -340,11 +344,29 @@ static void gpio_keys_polled_close(struct gpio_keys_button_dev *bdev) | |||||||
| 		pdata->disable(bdev->dev); | 		pdata->disable(bdev->dev); | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void gpio_keys_irq_work_func(struct work_struct *work) | ||||||
|  | { | ||||||
|  | 	struct gpio_keys_button_data *bdata = container_of(work, | ||||||
|  | 		struct gpio_keys_button_data, work.work); | ||||||
|  | 	int state = gpio_button_get_value(bdata); | ||||||
|  |  | ||||||
|  | 	if (state != bdata->last_state) { | ||||||
|  | 		unsigned int type = bdata->b->type ?: EV_KEY; | ||||||
|  |  | ||||||
|  | 		if (bdata->last_state != -1 || type == EV_SW) | ||||||
|  | 			button_hotplug_event(bdata, type, state); | ||||||
|  |  | ||||||
|  | 		bdata->last_state = state; | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| static irqreturn_t button_handle_irq(int irq, void *_bdata) | static irqreturn_t button_handle_irq(int irq, void *_bdata) | ||||||
| { | { | ||||||
| 	struct gpio_keys_button_data *bdata = (struct gpio_keys_button_data *) _bdata; | 	struct gpio_keys_button_data *bdata = | ||||||
|  | 		(struct gpio_keys_button_data *) _bdata; | ||||||
|  |  | ||||||
| 	button_hotplug_event(bdata, bdata->b->type ?: EV_KEY, gpio_button_get_value(bdata)); | 	schedule_delayed_work(&bdata->work, | ||||||
|  | 			      msecs_to_jiffies(bdata->software_debounce)); | ||||||
|  |  | ||||||
| 	return IRQ_HANDLED; | 	return IRQ_HANDLED; | ||||||
| } | } | ||||||
| @@ -390,7 +412,9 @@ gpio_keys_get_devtree_pdata(struct device *dev) | |||||||
| 			continue; | 			continue; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		button = &pdata->buttons[i++]; | 		button = (struct gpio_keys_button *)(&pdata->buttons[i++]); | ||||||
|  |  | ||||||
|  | 		button->irq = irq_of_parse_and_map(pp, 0); | ||||||
|  |  | ||||||
| 		button->gpio = of_get_gpio_flags(pp, 0, &flags); | 		button->gpio = of_get_gpio_flags(pp, 0, &flags); | ||||||
| 		if (button->gpio < 0) { | 		if (button->gpio < 0) { | ||||||
| @@ -517,6 +541,9 @@ static int gpio_keys_button_probe(struct platform_device *pdev, | |||||||
| 				gpio, error); | 				gpio, error); | ||||||
| 			return error; | 			return error; | ||||||
| 		} | 		} | ||||||
|  | 		bdata->gpiod = gpio_to_desc(gpio); | ||||||
|  | 		if (!bdata->gpiod) | ||||||
|  | 			return -EINVAL; | ||||||
|  |  | ||||||
| 		error = gpio_direction_input(gpio); | 		error = gpio_direction_input(gpio); | ||||||
| 		if (error) { | 		if (error) { | ||||||
| @@ -529,12 +556,26 @@ static int gpio_keys_button_probe(struct platform_device *pdev, | |||||||
| 		bdata->can_sleep = gpio_cansleep(gpio); | 		bdata->can_sleep = gpio_cansleep(gpio); | ||||||
| 		bdata->last_state = -1; | 		bdata->last_state = -1; | ||||||
|  |  | ||||||
| 		if (bdev->polled) | 		if (bdev->polled) { | ||||||
| 			bdata->threshold = DIV_ROUND_UP(button->debounce_interval, | 			bdata->threshold = DIV_ROUND_UP(button->debounce_interval, | ||||||
| 						pdata->poll_interval); | 							pdata->poll_interval); | ||||||
| 		else | 		} else { | ||||||
| 			bdata->threshold = 1; | 			bdata->threshold = 1; | ||||||
|  |  | ||||||
|  | 			if (button->debounce_interval) { | ||||||
|  | 				error = gpiod_set_debounce(bdata->gpiod, | ||||||
|  | 					button->debounce_interval * 1000); | ||||||
|  | 				/* | ||||||
|  | 				 * use timer if gpiolib doesn't provide | ||||||
|  | 				 * debounce. | ||||||
|  | 				 */ | ||||||
|  | 				if (error < 0) { | ||||||
|  | 					bdata->software_debounce = | ||||||
|  | 						button->debounce_interval; | ||||||
|  | 				} | ||||||
|  | 			} | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		bdata->b = &pdata->buttons[i]; | 		bdata->b = &pdata->buttons[i]; | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
| @@ -561,23 +602,39 @@ static int gpio_keys_probe(struct platform_device *pdev) | |||||||
|  |  | ||||||
| 	pdata = bdev->pdata; | 	pdata = bdev->pdata; | ||||||
| 	for (i = 0; i < pdata->nbuttons; i++) { | 	for (i = 0; i < pdata->nbuttons; i++) { | ||||||
| 		struct gpio_keys_button *button = &pdata->buttons[i]; | 		const struct gpio_keys_button *button = &pdata->buttons[i]; | ||||||
| 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||||
|  | 		unsigned long irqflags = IRQF_ONESHOT; | ||||||
|  |  | ||||||
| 		if (!button->irq) | 		if (!button->irq) { | ||||||
| 			button->irq = gpio_to_irq(button->gpio); | 			bdata->irq = gpio_to_irq(button->gpio); | ||||||
| 		if (button->irq < 0) { |  | ||||||
| 			dev_err(&pdev->dev, "failed to get irq for gpio:%d\n", button->gpio); | 			if (bdata->irq < 0) { | ||||||
| 			continue; | 				dev_err(&pdev->dev, "failed to get irq for gpio:%d\n", | ||||||
|  | 					button->gpio); | ||||||
|  | 				continue; | ||||||
|  | 			} | ||||||
|  |  | ||||||
|  | 			irqflags |= IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | ||||||
|  | 		} else { | ||||||
|  | 			bdata->irq = button->irq; | ||||||
| 		} | 		} | ||||||
|  |  | ||||||
| 		ret = devm_request_threaded_irq(&pdev->dev, button->irq, NULL, button_handle_irq, | 		INIT_DELAYED_WORK(&bdata->work, gpio_keys_irq_work_func); | ||||||
| 						IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, |  | ||||||
| 						dev_name(&pdev->dev), bdata); | 		ret = devm_request_threaded_irq(&pdev->dev, | ||||||
| 		if (ret < 0) | 			bdata->irq, NULL, button_handle_irq, | ||||||
| 			dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n", button->irq, button->gpio); | 			irqflags, dev_name(&pdev->dev), bdata); | ||||||
| 		else |  | ||||||
| 			dev_dbg(&pdev->dev, "gpio:%d has irq:%d\n", button->gpio, button->irq); | 		if (ret < 0) { | ||||||
|  | 			bdata->irq = 0; | ||||||
|  | 			dev_err(&pdev->dev, "failed to request irq:%d for gpio:%d\n", | ||||||
|  | 				bdata->irq, button->gpio); | ||||||
|  | 			continue; | ||||||
|  | 		} else { | ||||||
|  | 			dev_dbg(&pdev->dev, "gpio:%d has irq:%d\n", | ||||||
|  | 				button->gpio, bdata->irq); | ||||||
|  | 		} | ||||||
|  |  | ||||||
| 		if (bdata->b->type == EV_SW) | 		if (bdata->b->type == EV_SW) | ||||||
| 			button_hotplug_event(bdata, EV_SW, gpio_button_get_value(bdata)); | 			button_hotplug_event(bdata, EV_SW, gpio_button_get_value(bdata)); | ||||||
| @@ -613,6 +670,19 @@ static int gpio_keys_polled_probe(struct platform_device *pdev) | |||||||
| 	return ret; | 	return ret; | ||||||
| } | } | ||||||
|  |  | ||||||
|  | static void gpio_keys_irq_close(struct gpio_keys_button_dev *bdev) | ||||||
|  | { | ||||||
|  | 	struct gpio_keys_platform_data *pdata = bdev->pdata; | ||||||
|  | 	size_t i; | ||||||
|  |  | ||||||
|  | 	for (i = 0; i < pdata->nbuttons; i++) { | ||||||
|  | 		struct gpio_keys_button_data *bdata = &bdev->data[i]; | ||||||
|  |  | ||||||
|  | 		disable_irq(bdata->irq); | ||||||
|  | 		cancel_delayed_work_sync(&bdata->work); | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  |  | ||||||
| static int gpio_keys_remove(struct platform_device *pdev) | static int gpio_keys_remove(struct platform_device *pdev) | ||||||
| { | { | ||||||
| 	struct gpio_keys_button_dev *bdev = platform_get_drvdata(pdev); | 	struct gpio_keys_button_dev *bdev = platform_get_drvdata(pdev); | ||||||
| @@ -621,6 +691,8 @@ static int gpio_keys_remove(struct platform_device *pdev) | |||||||
|  |  | ||||||
| 	if (bdev->polled) | 	if (bdev->polled) | ||||||
| 		gpio_keys_polled_close(bdev); | 		gpio_keys_polled_close(bdev); | ||||||
|  | 	else | ||||||
|  | 		gpio_keys_irq_close(bdev); | ||||||
|  |  | ||||||
| 	return 0; | 	return 0; | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user
	 Christian Lamparter
					Christian Lamparter