xref: /openbmc/linux/drivers/input/keyboard/gpio_keys.c (revision c9efb0ba281e88e2faec6ad919be509b6ab8ead6)
1d2912cb1SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only
278a56aabSPhil Blundell /*
378a56aabSPhil Blundell  * Driver for keys on GPIO lines capable of generating interrupts.
478a56aabSPhil Blundell  *
578a56aabSPhil Blundell  * Copyright 2005 Phil Blundell
6fd05d089SDavid Jander  * Copyright 2010, 2011 David Jander <david@protonic.nl>
778a56aabSPhil Blundell  */
878a56aabSPhil Blundell 
978a56aabSPhil Blundell #include <linux/module.h>
1078a56aabSPhil Blundell 
11019002f2SPaul Cercueil #include <linux/hrtimer.h>
1278a56aabSPhil Blundell #include <linux/init.h>
1378a56aabSPhil Blundell #include <linux/fs.h>
1478a56aabSPhil Blundell #include <linux/interrupt.h>
1578a56aabSPhil Blundell #include <linux/irq.h>
1678a56aabSPhil Blundell #include <linux/sched.h>
1778a56aabSPhil Blundell #include <linux/pm.h>
185a0e3ad6STejun Heo #include <linux/slab.h>
1978a56aabSPhil Blundell #include <linux/sysctl.h>
2078a56aabSPhil Blundell #include <linux/proc_fs.h>
2178a56aabSPhil Blundell #include <linux/delay.h>
2278a56aabSPhil Blundell #include <linux/platform_device.h>
2378a56aabSPhil Blundell #include <linux/input.h>
2449015beeSDavid Brownell #include <linux/gpio_keys.h>
25da0d03feSJani Nikula #include <linux/workqueue.h>
26111bc59cSBen Dooks #include <linux/gpio.h>
275feeca3cSGeert Uytterhoeven #include <linux/gpio/consumer.h>
28415a4caaSSachin Kamat #include <linux/of.h>
29f2d347ffSAlexander Stein #include <linux/of_irq.h>
30d8ee4a1cSLaxman Dewangan #include <linux/spinlock.h>
3183fc580dSJeffy Chen #include <dt-bindings/input/gpio-keys.h>
3278a56aabSPhil Blundell 
33a33466e3SDmitry Baryshkov struct gpio_button_data {
34d9080921SDmitry Torokhov 	const struct gpio_keys_button *button;
35a33466e3SDmitry Baryshkov 	struct input_dev *input;
365feeca3cSGeert Uytterhoeven 	struct gpio_desc *gpiod;
378ed92556SDmitry Torokhov 
3883e4947aSHans de Goede 	unsigned short *code;
3983e4947aSHans de Goede 
40019002f2SPaul Cercueil 	struct hrtimer release_timer;
418ed92556SDmitry Torokhov 	unsigned int release_delay;	/* in msecs, for IRQ-only buttons */
428ed92556SDmitry Torokhov 
438ed92556SDmitry Torokhov 	struct delayed_work work;
44*c9efb0baSPaul Cercueil 	struct hrtimer debounce_timer;
458ed92556SDmitry Torokhov 	unsigned int software_debounce;	/* in msecs, for GPIO-driven buttons */
468ed92556SDmitry Torokhov 
47d8ee4a1cSLaxman Dewangan 	unsigned int irq;
4883fc580dSJeffy Chen 	unsigned int wakeup_trigger_type;
49d8ee4a1cSLaxman Dewangan 	spinlock_t lock;
509e3af04fSMika Westerberg 	bool disabled;
51d8ee4a1cSLaxman Dewangan 	bool key_pressed;
520f107573SJoseph Lo 	bool suspended;
53*c9efb0baSPaul Cercueil 	bool debounce_use_hrtimer;
54a33466e3SDmitry Baryshkov };
55a33466e3SDmitry Baryshkov 
56a33466e3SDmitry Baryshkov struct gpio_keys_drvdata {
57219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata;
58a33466e3SDmitry Baryshkov 	struct input_dev *input;
599e3af04fSMika Westerberg 	struct mutex disable_lock;
6083e4947aSHans de Goede 	unsigned short *keymap;
61a1b9b65eSGustavo A. R. Silva 	struct gpio_button_data data[];
62a33466e3SDmitry Baryshkov };
63a33466e3SDmitry Baryshkov 
649e3af04fSMika Westerberg /*
659e3af04fSMika Westerberg  * SYSFS interface for enabling/disabling keys and switches:
669e3af04fSMika Westerberg  *
679e3af04fSMika Westerberg  * There are 4 attributes under /sys/devices/platform/gpio-keys/
689e3af04fSMika Westerberg  *	keys [ro]              - bitmap of keys (EV_KEY) which can be
699e3af04fSMika Westerberg  *	                         disabled
709e3af04fSMika Westerberg  *	switches [ro]          - bitmap of switches (EV_SW) which can be
719e3af04fSMika Westerberg  *	                         disabled
729e3af04fSMika Westerberg  *	disabled_keys [rw]     - bitmap of keys currently disabled
739e3af04fSMika Westerberg  *	disabled_switches [rw] - bitmap of switches currently disabled
749e3af04fSMika Westerberg  *
759e3af04fSMika Westerberg  * Userland can change these values and hence disable event generation
769e3af04fSMika Westerberg  * for each key (or switch). Disabling a key means its interrupt line
779e3af04fSMika Westerberg  * is disabled.
789e3af04fSMika Westerberg  *
799e3af04fSMika Westerberg  * For example, if we have following switches set up as gpio-keys:
809e3af04fSMika Westerberg  *	SW_DOCK = 5
819e3af04fSMika Westerberg  *	SW_CAMERA_LENS_COVER = 9
829e3af04fSMika Westerberg  *	SW_KEYPAD_SLIDE = 10
839e3af04fSMika Westerberg  *	SW_FRONT_PROXIMITY = 11
849e3af04fSMika Westerberg  * This is read from switches:
859e3af04fSMika Westerberg  *	11-9,5
869e3af04fSMika Westerberg  * Next we want to disable proximity (11) and dock (5), we write:
879e3af04fSMika Westerberg  *	11,5
889e3af04fSMika Westerberg  * to file disabled_switches. Now proximity and dock IRQs are disabled.
899e3af04fSMika Westerberg  * This can be verified by reading the file disabled_switches:
909e3af04fSMika Westerberg  *	11,5
919e3af04fSMika Westerberg  * If we now want to enable proximity (11) switch we write:
929e3af04fSMika Westerberg  *	5
939e3af04fSMika Westerberg  * to disabled_switches.
949e3af04fSMika Westerberg  *
959e3af04fSMika Westerberg  * We can disable only those keys which don't allow sharing the irq.
969e3af04fSMika Westerberg  */
979e3af04fSMika Westerberg 
989e3af04fSMika Westerberg /**
999e3af04fSMika Westerberg  * get_n_events_by_type() - returns maximum number of events per @type
1009e3af04fSMika Westerberg  * @type: type of button (%EV_KEY, %EV_SW)
1019e3af04fSMika Westerberg  *
1029e3af04fSMika Westerberg  * Return value of this function can be used to allocate bitmap
1039e3af04fSMika Westerberg  * large enough to hold all bits for given type.
1049e3af04fSMika Westerberg  */
1058679ee42SDmitry Torokhov static int get_n_events_by_type(int type)
1069e3af04fSMika Westerberg {
1079e3af04fSMika Westerberg 	BUG_ON(type != EV_SW && type != EV_KEY);
1089e3af04fSMika Westerberg 
1099e3af04fSMika Westerberg 	return (type == EV_KEY) ? KEY_CNT : SW_CNT;
1109e3af04fSMika Westerberg }
1119e3af04fSMika Westerberg 
1129e3af04fSMika Westerberg /**
1138679ee42SDmitry Torokhov  * get_bm_events_by_type() - returns bitmap of supported events per @type
1142216c0e4SLee Jones  * @dev: input device from which bitmap is retrieved
1158679ee42SDmitry Torokhov  * @type: type of button (%EV_KEY, %EV_SW)
1168679ee42SDmitry Torokhov  *
1178679ee42SDmitry Torokhov  * Return value of this function can be used to allocate bitmap
1188679ee42SDmitry Torokhov  * large enough to hold all bits for given type.
1198679ee42SDmitry Torokhov  */
1208679ee42SDmitry Torokhov static const unsigned long *get_bm_events_by_type(struct input_dev *dev,
1218679ee42SDmitry Torokhov 						  int type)
1228679ee42SDmitry Torokhov {
1238679ee42SDmitry Torokhov 	BUG_ON(type != EV_SW && type != EV_KEY);
1248679ee42SDmitry Torokhov 
1258679ee42SDmitry Torokhov 	return (type == EV_KEY) ? dev->keybit : dev->swbit;
1268679ee42SDmitry Torokhov }
1278679ee42SDmitry Torokhov 
1288679ee42SDmitry Torokhov /**
1299e3af04fSMika Westerberg  * gpio_keys_disable_button() - disables given GPIO button
1309e3af04fSMika Westerberg  * @bdata: button data for button to be disabled
1319e3af04fSMika Westerberg  *
1329e3af04fSMika Westerberg  * Disables button pointed by @bdata. This is done by masking
1339e3af04fSMika Westerberg  * IRQ line. After this function is called, button won't generate
1349e3af04fSMika Westerberg  * input events anymore. Note that one can only disable buttons
1359e3af04fSMika Westerberg  * that don't share IRQs.
1369e3af04fSMika Westerberg  *
1379e3af04fSMika Westerberg  * Make sure that @bdata->disable_lock is locked when entering
1389e3af04fSMika Westerberg  * this function to avoid races when concurrent threads are
1399e3af04fSMika Westerberg  * disabling buttons at the same time.
1409e3af04fSMika Westerberg  */
1419e3af04fSMika Westerberg static void gpio_keys_disable_button(struct gpio_button_data *bdata)
1429e3af04fSMika Westerberg {
1439e3af04fSMika Westerberg 	if (!bdata->disabled) {
1449e3af04fSMika Westerberg 		/*
1458ed92556SDmitry Torokhov 		 * Disable IRQ and associated timer/work structure.
1469e3af04fSMika Westerberg 		 */
147d8ee4a1cSLaxman Dewangan 		disable_irq(bdata->irq);
1488ed92556SDmitry Torokhov 
149*c9efb0baSPaul Cercueil 		if (bdata->debounce_use_hrtimer)
150019002f2SPaul Cercueil 			hrtimer_cancel(&bdata->release_timer);
151*c9efb0baSPaul Cercueil 		else
152*c9efb0baSPaul Cercueil 			cancel_delayed_work_sync(&bdata->work);
1539e3af04fSMika Westerberg 
1549e3af04fSMika Westerberg 		bdata->disabled = true;
1559e3af04fSMika Westerberg 	}
1569e3af04fSMika Westerberg }
1579e3af04fSMika Westerberg 
1589e3af04fSMika Westerberg /**
1599e3af04fSMika Westerberg  * gpio_keys_enable_button() - enables given GPIO button
1609e3af04fSMika Westerberg  * @bdata: button data for button to be disabled
1619e3af04fSMika Westerberg  *
1629e3af04fSMika Westerberg  * Enables given button pointed by @bdata.
1639e3af04fSMika Westerberg  *
1649e3af04fSMika Westerberg  * Make sure that @bdata->disable_lock is locked when entering
1659e3af04fSMika Westerberg  * this function to avoid races with concurrent threads trying
1669e3af04fSMika Westerberg  * to enable the same button at the same time.
1679e3af04fSMika Westerberg  */
1689e3af04fSMika Westerberg static void gpio_keys_enable_button(struct gpio_button_data *bdata)
1699e3af04fSMika Westerberg {
1709e3af04fSMika Westerberg 	if (bdata->disabled) {
171d8ee4a1cSLaxman Dewangan 		enable_irq(bdata->irq);
1729e3af04fSMika Westerberg 		bdata->disabled = false;
1739e3af04fSMika Westerberg 	}
1749e3af04fSMika Westerberg }
1759e3af04fSMika Westerberg 
1769e3af04fSMika Westerberg /**
1779e3af04fSMika Westerberg  * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons
1789e3af04fSMika Westerberg  * @ddata: pointer to drvdata
1799e3af04fSMika Westerberg  * @buf: buffer where stringified bitmap is written
1809e3af04fSMika Westerberg  * @type: button type (%EV_KEY, %EV_SW)
1819e3af04fSMika Westerberg  * @only_disabled: does caller want only those buttons that are
1829e3af04fSMika Westerberg  *                 currently disabled or all buttons that can be
1839e3af04fSMika Westerberg  *                 disabled
1849e3af04fSMika Westerberg  *
1859e3af04fSMika Westerberg  * This function writes buttons that can be disabled to @buf. If
1869e3af04fSMika Westerberg  * @only_disabled is true, then @buf contains only those buttons
1879e3af04fSMika Westerberg  * that are currently disabled. Returns 0 on success or negative
1889e3af04fSMika Westerberg  * errno on failure.
1899e3af04fSMika Westerberg  */
1909e3af04fSMika Westerberg static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
1919e3af04fSMika Westerberg 					  char *buf, unsigned int type,
1929e3af04fSMika Westerberg 					  bool only_disabled)
1939e3af04fSMika Westerberg {
1949e3af04fSMika Westerberg 	int n_events = get_n_events_by_type(type);
1959e3af04fSMika Westerberg 	unsigned long *bits;
1969e3af04fSMika Westerberg 	ssize_t ret;
1979e3af04fSMika Westerberg 	int i;
1989e3af04fSMika Westerberg 
1994e2ec39dSAndy Shevchenko 	bits = bitmap_zalloc(n_events, GFP_KERNEL);
2009e3af04fSMika Westerberg 	if (!bits)
2019e3af04fSMika Westerberg 		return -ENOMEM;
2029e3af04fSMika Westerberg 
203219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2049e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2059e3af04fSMika Westerberg 
2069e3af04fSMika Westerberg 		if (bdata->button->type != type)
2079e3af04fSMika Westerberg 			continue;
2089e3af04fSMika Westerberg 
2099e3af04fSMika Westerberg 		if (only_disabled && !bdata->disabled)
2109e3af04fSMika Westerberg 			continue;
2119e3af04fSMika Westerberg 
21283e4947aSHans de Goede 		__set_bit(*bdata->code, bits);
2139e3af04fSMika Westerberg 	}
2149e3af04fSMika Westerberg 
2150b480037STejun Heo 	ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits);
2169e3af04fSMika Westerberg 	buf[ret++] = '\n';
2179e3af04fSMika Westerberg 	buf[ret] = '\0';
2189e3af04fSMika Westerberg 
2194e2ec39dSAndy Shevchenko 	bitmap_free(bits);
2209e3af04fSMika Westerberg 
2219e3af04fSMika Westerberg 	return ret;
2229e3af04fSMika Westerberg }
2239e3af04fSMika Westerberg 
2249e3af04fSMika Westerberg /**
2259e3af04fSMika Westerberg  * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap
2269e3af04fSMika Westerberg  * @ddata: pointer to drvdata
2279e3af04fSMika Westerberg  * @buf: buffer from userspace that contains stringified bitmap
2289e3af04fSMika Westerberg  * @type: button type (%EV_KEY, %EV_SW)
2299e3af04fSMika Westerberg  *
2309e3af04fSMika Westerberg  * This function parses stringified bitmap from @buf and disables/enables
231a16ca239SDmitry Torokhov  * GPIO buttons accordingly. Returns 0 on success and negative error
2329e3af04fSMika Westerberg  * on failure.
2339e3af04fSMika Westerberg  */
2349e3af04fSMika Westerberg static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
2359e3af04fSMika Westerberg 					   const char *buf, unsigned int type)
2369e3af04fSMika Westerberg {
2379e3af04fSMika Westerberg 	int n_events = get_n_events_by_type(type);
2388679ee42SDmitry Torokhov 	const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
2399e3af04fSMika Westerberg 	unsigned long *bits;
2409e3af04fSMika Westerberg 	ssize_t error;
2419e3af04fSMika Westerberg 	int i;
2429e3af04fSMika Westerberg 
2434e2ec39dSAndy Shevchenko 	bits = bitmap_zalloc(n_events, GFP_KERNEL);
2449e3af04fSMika Westerberg 	if (!bits)
2459e3af04fSMika Westerberg 		return -ENOMEM;
2469e3af04fSMika Westerberg 
2479e3af04fSMika Westerberg 	error = bitmap_parselist(buf, bits, n_events);
2489e3af04fSMika Westerberg 	if (error)
2499e3af04fSMika Westerberg 		goto out;
2509e3af04fSMika Westerberg 
2519e3af04fSMika Westerberg 	/* First validate */
2528679ee42SDmitry Torokhov 	if (!bitmap_subset(bits, bitmap, n_events)) {
2538679ee42SDmitry Torokhov 		error = -EINVAL;
2548679ee42SDmitry Torokhov 		goto out;
2558679ee42SDmitry Torokhov 	}
2568679ee42SDmitry Torokhov 
257219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2589e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2599e3af04fSMika Westerberg 
2609e3af04fSMika Westerberg 		if (bdata->button->type != type)
2619e3af04fSMika Westerberg 			continue;
2629e3af04fSMika Westerberg 
26383e4947aSHans de Goede 		if (test_bit(*bdata->code, bits) &&
2649e3af04fSMika Westerberg 		    !bdata->button->can_disable) {
2659e3af04fSMika Westerberg 			error = -EINVAL;
2669e3af04fSMika Westerberg 			goto out;
2679e3af04fSMika Westerberg 		}
2689e3af04fSMika Westerberg 	}
2699e3af04fSMika Westerberg 
2709e3af04fSMika Westerberg 	mutex_lock(&ddata->disable_lock);
2719e3af04fSMika Westerberg 
272219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2739e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2749e3af04fSMika Westerberg 
2759e3af04fSMika Westerberg 		if (bdata->button->type != type)
2769e3af04fSMika Westerberg 			continue;
2779e3af04fSMika Westerberg 
27883e4947aSHans de Goede 		if (test_bit(*bdata->code, bits))
2799e3af04fSMika Westerberg 			gpio_keys_disable_button(bdata);
2809e3af04fSMika Westerberg 		else
2819e3af04fSMika Westerberg 			gpio_keys_enable_button(bdata);
2829e3af04fSMika Westerberg 	}
2839e3af04fSMika Westerberg 
2849e3af04fSMika Westerberg 	mutex_unlock(&ddata->disable_lock);
2859e3af04fSMika Westerberg 
2869e3af04fSMika Westerberg out:
2874e2ec39dSAndy Shevchenko 	bitmap_free(bits);
2889e3af04fSMika Westerberg 	return error;
2899e3af04fSMika Westerberg }
2909e3af04fSMika Westerberg 
2919e3af04fSMika Westerberg #define ATTR_SHOW_FN(name, type, only_disabled)				\
2929e3af04fSMika Westerberg static ssize_t gpio_keys_show_##name(struct device *dev,		\
2939e3af04fSMika Westerberg 				     struct device_attribute *attr,	\
2949e3af04fSMika Westerberg 				     char *buf)				\
2959e3af04fSMika Westerberg {									\
2969e3af04fSMika Westerberg 	struct platform_device *pdev = to_platform_device(dev);		\
2979e3af04fSMika Westerberg 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
2989e3af04fSMika Westerberg 									\
2999e3af04fSMika Westerberg 	return gpio_keys_attr_show_helper(ddata, buf,			\
3009e3af04fSMika Westerberg 					  type, only_disabled);		\
3019e3af04fSMika Westerberg }
3029e3af04fSMika Westerberg 
3039e3af04fSMika Westerberg ATTR_SHOW_FN(keys, EV_KEY, false);
3049e3af04fSMika Westerberg ATTR_SHOW_FN(switches, EV_SW, false);
3059e3af04fSMika Westerberg ATTR_SHOW_FN(disabled_keys, EV_KEY, true);
3069e3af04fSMika Westerberg ATTR_SHOW_FN(disabled_switches, EV_SW, true);
3079e3af04fSMika Westerberg 
3089e3af04fSMika Westerberg /*
3099e3af04fSMika Westerberg  * ATTRIBUTES:
3109e3af04fSMika Westerberg  *
3119e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/keys [ro]
3129e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/switches [ro]
3139e3af04fSMika Westerberg  */
3149e3af04fSMika Westerberg static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL);
3159e3af04fSMika Westerberg static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL);
3169e3af04fSMika Westerberg 
3179e3af04fSMika Westerberg #define ATTR_STORE_FN(name, type)					\
3189e3af04fSMika Westerberg static ssize_t gpio_keys_store_##name(struct device *dev,		\
3199e3af04fSMika Westerberg 				      struct device_attribute *attr,	\
3209e3af04fSMika Westerberg 				      const char *buf,			\
3219e3af04fSMika Westerberg 				      size_t count)			\
3229e3af04fSMika Westerberg {									\
3239e3af04fSMika Westerberg 	struct platform_device *pdev = to_platform_device(dev);		\
3249e3af04fSMika Westerberg 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
3259e3af04fSMika Westerberg 	ssize_t error;							\
3269e3af04fSMika Westerberg 									\
3279e3af04fSMika Westerberg 	error = gpio_keys_attr_store_helper(ddata, buf, type);		\
3289e3af04fSMika Westerberg 	if (error)							\
3299e3af04fSMika Westerberg 		return error;						\
3309e3af04fSMika Westerberg 									\
3319e3af04fSMika Westerberg 	return count;							\
3329e3af04fSMika Westerberg }
3339e3af04fSMika Westerberg 
3349e3af04fSMika Westerberg ATTR_STORE_FN(disabled_keys, EV_KEY);
3359e3af04fSMika Westerberg ATTR_STORE_FN(disabled_switches, EV_SW);
3369e3af04fSMika Westerberg 
3379e3af04fSMika Westerberg /*
3389e3af04fSMika Westerberg  * ATTRIBUTES:
3399e3af04fSMika Westerberg  *
3409e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/disabled_keys [rw]
3419e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/disables_switches [rw]
3429e3af04fSMika Westerberg  */
3439e3af04fSMika Westerberg static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO,
3449e3af04fSMika Westerberg 		   gpio_keys_show_disabled_keys,
3459e3af04fSMika Westerberg 		   gpio_keys_store_disabled_keys);
3469e3af04fSMika Westerberg static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO,
3479e3af04fSMika Westerberg 		   gpio_keys_show_disabled_switches,
3489e3af04fSMika Westerberg 		   gpio_keys_store_disabled_switches);
3499e3af04fSMika Westerberg 
3509e3af04fSMika Westerberg static struct attribute *gpio_keys_attrs[] = {
3519e3af04fSMika Westerberg 	&dev_attr_keys.attr,
3529e3af04fSMika Westerberg 	&dev_attr_switches.attr,
3539e3af04fSMika Westerberg 	&dev_attr_disabled_keys.attr,
3549e3af04fSMika Westerberg 	&dev_attr_disabled_switches.attr,
3559e3af04fSMika Westerberg 	NULL,
3569e3af04fSMika Westerberg };
357cf1b2a20SGreg Kroah-Hartman ATTRIBUTE_GROUPS(gpio_keys);
3589e3af04fSMika Westerberg 
359d8ee4a1cSLaxman Dewangan static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
36078a56aabSPhil Blundell {
361d9080921SDmitry Torokhov 	const struct gpio_keys_button *button = bdata->button;
362ce25d7e9SUwe Kleine-König 	struct input_dev *input = bdata->input;
36384767d00SRoman Moravcik 	unsigned int type = button->type ?: EV_KEY;
3645feeca3cSGeert Uytterhoeven 	int state;
36584767d00SRoman Moravcik 
366*c9efb0baSPaul Cercueil 	state = bdata->debounce_use_hrtimer ?
367*c9efb0baSPaul Cercueil 			gpiod_get_value(bdata->gpiod) :
368*c9efb0baSPaul Cercueil 			gpiod_get_value_cansleep(bdata->gpiod);
36977fa0554SBjorn Andersson 	if (state < 0) {
3705feeca3cSGeert Uytterhoeven 		dev_err(input->dev.parent,
3715feeca3cSGeert Uytterhoeven 			"failed to get gpio state: %d\n", state);
37277fa0554SBjorn Andersson 		return;
37377fa0554SBjorn Andersson 	}
37477fa0554SBjorn Andersson 
37592a47674SAlexander Stein 	if (type == EV_ABS) {
37692a47674SAlexander Stein 		if (state)
37792a47674SAlexander Stein 			input_event(input, type, button->code, button->value);
37892a47674SAlexander Stein 	} else {
37983e4947aSHans de Goede 		input_event(input, type, *bdata->code, state);
38092a47674SAlexander Stein 	}
381a33466e3SDmitry Baryshkov }
382a33466e3SDmitry Baryshkov 
383*c9efb0baSPaul Cercueil static void gpio_keys_debounce_event(struct gpio_button_data *bdata)
3846ee88d71SDaniel Mack {
385d8ee4a1cSLaxman Dewangan 	gpio_keys_gpio_report_event(bdata);
38636a8fc6fSPaul Cercueil 	input_sync(bdata->input);
3872fba26c6SNeilBrown 
3882fba26c6SNeilBrown 	if (bdata->button->wakeup)
3892fba26c6SNeilBrown 		pm_relax(bdata->input->dev.parent);
3906ee88d71SDaniel Mack }
3916ee88d71SDaniel Mack 
392*c9efb0baSPaul Cercueil static void gpio_keys_gpio_work_func(struct work_struct *work)
393*c9efb0baSPaul Cercueil {
394*c9efb0baSPaul Cercueil 	struct gpio_button_data *bdata =
395*c9efb0baSPaul Cercueil 		container_of(work, struct gpio_button_data, work.work);
396*c9efb0baSPaul Cercueil 
397*c9efb0baSPaul Cercueil 	gpio_keys_debounce_event(bdata);
398*c9efb0baSPaul Cercueil }
399*c9efb0baSPaul Cercueil 
400*c9efb0baSPaul Cercueil static enum hrtimer_restart gpio_keys_debounce_timer(struct hrtimer *t)
401*c9efb0baSPaul Cercueil {
402*c9efb0baSPaul Cercueil 	struct gpio_button_data *bdata =
403*c9efb0baSPaul Cercueil 		container_of(t, struct gpio_button_data, debounce_timer);
404*c9efb0baSPaul Cercueil 
405*c9efb0baSPaul Cercueil 	gpio_keys_debounce_event(bdata);
406*c9efb0baSPaul Cercueil 
407*c9efb0baSPaul Cercueil 	return HRTIMER_NORESTART;
408*c9efb0baSPaul Cercueil }
409*c9efb0baSPaul Cercueil 
410d8ee4a1cSLaxman Dewangan static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
411a33466e3SDmitry Baryshkov {
41257ffe9d5SUwe Kleine-König 	struct gpio_button_data *bdata = dev_id;
413a33466e3SDmitry Baryshkov 
414d8ee4a1cSLaxman Dewangan 	BUG_ON(irq != bdata->irq);
415a33466e3SDmitry Baryshkov 
4160f107573SJoseph Lo 	if (bdata->button->wakeup) {
4170f107573SJoseph Lo 		const struct gpio_keys_button *button = bdata->button;
4180f107573SJoseph Lo 
4192fba26c6SNeilBrown 		pm_stay_awake(bdata->input->dev.parent);
4200f107573SJoseph Lo 		if (bdata->suspended  &&
4210f107573SJoseph Lo 		    (button->type == 0 || button->type == EV_KEY)) {
4220f107573SJoseph Lo 			/*
4230f107573SJoseph Lo 			 * Simulate wakeup key press in case the key has
4240f107573SJoseph Lo 			 * already released by the time we got interrupt
4250f107573SJoseph Lo 			 * handler to run.
4260f107573SJoseph Lo 			 */
4270f107573SJoseph Lo 			input_report_key(bdata->input, button->code, 1);
4280f107573SJoseph Lo 		}
4290f107573SJoseph Lo 	}
4308ed92556SDmitry Torokhov 
431*c9efb0baSPaul Cercueil 	if (bdata->debounce_use_hrtimer) {
432*c9efb0baSPaul Cercueil 		hrtimer_start(&bdata->debounce_timer,
433*c9efb0baSPaul Cercueil 			      ms_to_ktime(bdata->software_debounce),
434*c9efb0baSPaul Cercueil 			      HRTIMER_MODE_REL);
435*c9efb0baSPaul Cercueil 	} else {
4368ed92556SDmitry Torokhov 		mod_delayed_work(system_wq,
4378ed92556SDmitry Torokhov 				 &bdata->work,
4388ed92556SDmitry Torokhov 				 msecs_to_jiffies(bdata->software_debounce));
439*c9efb0baSPaul Cercueil 	}
440a33466e3SDmitry Baryshkov 
4411164ec1aSDavid Brownell 	return IRQ_HANDLED;
44278a56aabSPhil Blundell }
44378a56aabSPhil Blundell 
444019002f2SPaul Cercueil static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t)
445d8ee4a1cSLaxman Dewangan {
446019002f2SPaul Cercueil 	struct gpio_button_data *bdata = container_of(t,
447019002f2SPaul Cercueil 						      struct gpio_button_data,
448019002f2SPaul Cercueil 						      release_timer);
449d8ee4a1cSLaxman Dewangan 	struct input_dev *input = bdata->input;
450d8ee4a1cSLaxman Dewangan 
451d8ee4a1cSLaxman Dewangan 	if (bdata->key_pressed) {
45283e4947aSHans de Goede 		input_event(input, EV_KEY, *bdata->code, 0);
453d8ee4a1cSLaxman Dewangan 		input_sync(input);
454d8ee4a1cSLaxman Dewangan 		bdata->key_pressed = false;
455d8ee4a1cSLaxman Dewangan 	}
456019002f2SPaul Cercueil 
457019002f2SPaul Cercueil 	return HRTIMER_NORESTART;
458d8ee4a1cSLaxman Dewangan }
459d8ee4a1cSLaxman Dewangan 
460d8ee4a1cSLaxman Dewangan static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
461d8ee4a1cSLaxman Dewangan {
462d8ee4a1cSLaxman Dewangan 	struct gpio_button_data *bdata = dev_id;
463d8ee4a1cSLaxman Dewangan 	struct input_dev *input = bdata->input;
464d8ee4a1cSLaxman Dewangan 	unsigned long flags;
465d8ee4a1cSLaxman Dewangan 
466d8ee4a1cSLaxman Dewangan 	BUG_ON(irq != bdata->irq);
467d8ee4a1cSLaxman Dewangan 
468d8ee4a1cSLaxman Dewangan 	spin_lock_irqsave(&bdata->lock, flags);
469d8ee4a1cSLaxman Dewangan 
470d8ee4a1cSLaxman Dewangan 	if (!bdata->key_pressed) {
4712fba26c6SNeilBrown 		if (bdata->button->wakeup)
4722fba26c6SNeilBrown 			pm_wakeup_event(bdata->input->dev.parent, 0);
4732fba26c6SNeilBrown 
47483e4947aSHans de Goede 		input_event(input, EV_KEY, *bdata->code, 1);
475d8ee4a1cSLaxman Dewangan 		input_sync(input);
476d8ee4a1cSLaxman Dewangan 
4778ed92556SDmitry Torokhov 		if (!bdata->release_delay) {
47883e4947aSHans de Goede 			input_event(input, EV_KEY, *bdata->code, 0);
479d8ee4a1cSLaxman Dewangan 			input_sync(input);
480d8ee4a1cSLaxman Dewangan 			goto out;
481d8ee4a1cSLaxman Dewangan 		}
482d8ee4a1cSLaxman Dewangan 
483d8ee4a1cSLaxman Dewangan 		bdata->key_pressed = true;
484d8ee4a1cSLaxman Dewangan 	}
485d8ee4a1cSLaxman Dewangan 
4868ed92556SDmitry Torokhov 	if (bdata->release_delay)
487019002f2SPaul Cercueil 		hrtimer_start(&bdata->release_timer,
488019002f2SPaul Cercueil 			      ms_to_ktime(bdata->release_delay),
489019002f2SPaul Cercueil 			      HRTIMER_MODE_REL_HARD);
490d8ee4a1cSLaxman Dewangan out:
491d8ee4a1cSLaxman Dewangan 	spin_unlock_irqrestore(&bdata->lock, flags);
492d8ee4a1cSLaxman Dewangan 	return IRQ_HANDLED;
493d8ee4a1cSLaxman Dewangan }
494d8ee4a1cSLaxman Dewangan 
49527245519SAlexander Shiyan static void gpio_keys_quiesce_key(void *data)
49627245519SAlexander Shiyan {
49727245519SAlexander Shiyan 	struct gpio_button_data *bdata = data;
49827245519SAlexander Shiyan 
499*c9efb0baSPaul Cercueil 	if (bdata->debounce_use_hrtimer)
500*c9efb0baSPaul Cercueil 		hrtimer_cancel(&bdata->debounce_timer);
5018ed92556SDmitry Torokhov 	else
502*c9efb0baSPaul Cercueil 		cancel_delayed_work_sync(&bdata->work);
50327245519SAlexander Shiyan }
50427245519SAlexander Shiyan 
5055298cc4cSBill Pemberton static int gpio_keys_setup_key(struct platform_device *pdev,
506d9080921SDmitry Torokhov 				struct input_dev *input,
50783e4947aSHans de Goede 				struct gpio_keys_drvdata *ddata,
508700a38b2SDmitry Torokhov 				const struct gpio_keys_button *button,
50983e4947aSHans de Goede 				int idx,
510700a38b2SDmitry Torokhov 				struct fwnode_handle *child)
511bc8f1eafSBen Dooks {
51292a47674SAlexander Stein 	const char *desc = button->desc ? button->desc : "gpio_keys";
5139e3af04fSMika Westerberg 	struct device *dev = &pdev->dev;
51483e4947aSHans de Goede 	struct gpio_button_data *bdata = &ddata->data[idx];
515d8ee4a1cSLaxman Dewangan 	irq_handler_t isr;
5169e3af04fSMika Westerberg 	unsigned long irqflags;
51727245519SAlexander Shiyan 	int irq;
51827245519SAlexander Shiyan 	int error;
519bc8f1eafSBen Dooks 
520d9080921SDmitry Torokhov 	bdata->input = input;
521d9080921SDmitry Torokhov 	bdata->button = button;
522d8ee4a1cSLaxman Dewangan 	spin_lock_init(&bdata->lock);
523d8ee4a1cSLaxman Dewangan 
524700a38b2SDmitry Torokhov 	if (child) {
5252a60f598SDmitry Torokhov 		bdata->gpiod = devm_fwnode_gpiod_get(dev, child,
5262a60f598SDmitry Torokhov 						     NULL, GPIOD_IN, desc);
527700a38b2SDmitry Torokhov 		if (IS_ERR(bdata->gpiod)) {
528700a38b2SDmitry Torokhov 			error = PTR_ERR(bdata->gpiod);
529700a38b2SDmitry Torokhov 			if (error == -ENOENT) {
530700a38b2SDmitry Torokhov 				/*
531700a38b2SDmitry Torokhov 				 * GPIO is optional, we may be dealing with
532700a38b2SDmitry Torokhov 				 * purely interrupt-driven setup.
533700a38b2SDmitry Torokhov 				 */
534700a38b2SDmitry Torokhov 				bdata->gpiod = NULL;
535700a38b2SDmitry Torokhov 			} else {
536700a38b2SDmitry Torokhov 				if (error != -EPROBE_DEFER)
537700a38b2SDmitry Torokhov 					dev_err(dev, "failed to get gpio: %d\n",
538700a38b2SDmitry Torokhov 						error);
539700a38b2SDmitry Torokhov 				return error;
540700a38b2SDmitry Torokhov 			}
541700a38b2SDmitry Torokhov 		}
542700a38b2SDmitry Torokhov 	} else if (gpio_is_valid(button->gpio)) {
5435feeca3cSGeert Uytterhoeven 		/*
5445feeca3cSGeert Uytterhoeven 		 * Legacy GPIO number, so request the GPIO here and
5455feeca3cSGeert Uytterhoeven 		 * convert it to descriptor.
5465feeca3cSGeert Uytterhoeven 		 */
5475feeca3cSGeert Uytterhoeven 		unsigned flags = GPIOF_IN;
548bc8f1eafSBen Dooks 
5495feeca3cSGeert Uytterhoeven 		if (button->active_low)
5505feeca3cSGeert Uytterhoeven 			flags |= GPIOF_ACTIVE_LOW;
5515feeca3cSGeert Uytterhoeven 
552b4e66e7dSGuenter Roeck 		error = devm_gpio_request_one(dev, button->gpio, flags, desc);
553bc8f1eafSBen Dooks 		if (error < 0) {
554d8ee4a1cSLaxman Dewangan 			dev_err(dev, "Failed to request GPIO %d, error %d\n",
555bc8f1eafSBen Dooks 				button->gpio, error);
556d8ee4a1cSLaxman Dewangan 			return error;
557bc8f1eafSBen Dooks 		}
558bc8f1eafSBen Dooks 
5595feeca3cSGeert Uytterhoeven 		bdata->gpiod = gpio_to_desc(button->gpio);
5605feeca3cSGeert Uytterhoeven 		if (!bdata->gpiod)
5615feeca3cSGeert Uytterhoeven 			return -EINVAL;
562700a38b2SDmitry Torokhov 	}
5635feeca3cSGeert Uytterhoeven 
564700a38b2SDmitry Torokhov 	if (bdata->gpiod) {
56583fc580dSJeffy Chen 		bool active_low = gpiod_is_active_low(bdata->gpiod);
56683fc580dSJeffy Chen 
56728ed684fSGrazvydas Ignotas 		if (button->debounce_interval) {
5685feeca3cSGeert Uytterhoeven 			error = gpiod_set_debounce(bdata->gpiod,
56928ed684fSGrazvydas Ignotas 					button->debounce_interval * 1000);
57028ed684fSGrazvydas Ignotas 			/* use timer if gpiolib doesn't provide debounce */
57128ed684fSGrazvydas Ignotas 			if (error < 0)
5728ed92556SDmitry Torokhov 				bdata->software_debounce =
573d8ee4a1cSLaxman Dewangan 						button->debounce_interval;
574*c9efb0baSPaul Cercueil 
575*c9efb0baSPaul Cercueil 			/*
576*c9efb0baSPaul Cercueil 			 * If reading the GPIO won't sleep, we can use a
577*c9efb0baSPaul Cercueil 			 * hrtimer instead of a standard timer for the software
578*c9efb0baSPaul Cercueil 			 * debounce, to reduce the latency as much as possible.
579*c9efb0baSPaul Cercueil 			 */
580*c9efb0baSPaul Cercueil 			bdata->debounce_use_hrtimer =
581*c9efb0baSPaul Cercueil 					!gpiod_cansleep(bdata->gpiod);
58228ed684fSGrazvydas Ignotas 		}
58328ed684fSGrazvydas Ignotas 
58497d86e07SDmitry Torokhov 		if (button->irq) {
58597d86e07SDmitry Torokhov 			bdata->irq = button->irq;
58697d86e07SDmitry Torokhov 		} else {
5875feeca3cSGeert Uytterhoeven 			irq = gpiod_to_irq(bdata->gpiod);
588bc8f1eafSBen Dooks 			if (irq < 0) {
589bc8f1eafSBen Dooks 				error = irq;
590d8ee4a1cSLaxman Dewangan 				dev_err(dev,
591d8ee4a1cSLaxman Dewangan 					"Unable to get irq number for GPIO %d, error %d\n",
592bc8f1eafSBen Dooks 					button->gpio, error);
59327245519SAlexander Shiyan 				return error;
594d8ee4a1cSLaxman Dewangan 			}
595d8ee4a1cSLaxman Dewangan 			bdata->irq = irq;
59697d86e07SDmitry Torokhov 		}
597d8ee4a1cSLaxman Dewangan 
5988ed92556SDmitry Torokhov 		INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);
599d8ee4a1cSLaxman Dewangan 
600*c9efb0baSPaul Cercueil 		hrtimer_init(&bdata->debounce_timer,
601*c9efb0baSPaul Cercueil 			     CLOCK_REALTIME, HRTIMER_MODE_REL);
602*c9efb0baSPaul Cercueil 		bdata->debounce_timer.function = gpio_keys_debounce_timer;
603*c9efb0baSPaul Cercueil 
604d8ee4a1cSLaxman Dewangan 		isr = gpio_keys_gpio_isr;
605d8ee4a1cSLaxman Dewangan 		irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
606d8ee4a1cSLaxman Dewangan 
60783fc580dSJeffy Chen 		switch (button->wakeup_event_action) {
60883fc580dSJeffy Chen 		case EV_ACT_ASSERTED:
60983fc580dSJeffy Chen 			bdata->wakeup_trigger_type = active_low ?
61083fc580dSJeffy Chen 				IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
61183fc580dSJeffy Chen 			break;
61283fc580dSJeffy Chen 		case EV_ACT_DEASSERTED:
61383fc580dSJeffy Chen 			bdata->wakeup_trigger_type = active_low ?
61483fc580dSJeffy Chen 				IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
61583fc580dSJeffy Chen 			break;
61683fc580dSJeffy Chen 		case EV_ACT_ANY:
61783fc580dSJeffy Chen 		default:
61883fc580dSJeffy Chen 			/*
61983fc580dSJeffy Chen 			 * For other cases, we are OK letting suspend/resume
62083fc580dSJeffy Chen 			 * not reconfigure the trigger type.
62183fc580dSJeffy Chen 			 */
62283fc580dSJeffy Chen 			break;
62383fc580dSJeffy Chen 		}
624d8ee4a1cSLaxman Dewangan 	} else {
625d8ee4a1cSLaxman Dewangan 		if (!button->irq) {
626700a38b2SDmitry Torokhov 			dev_err(dev, "Found button without gpio or irq\n");
627d8ee4a1cSLaxman Dewangan 			return -EINVAL;
628d8ee4a1cSLaxman Dewangan 		}
629700a38b2SDmitry Torokhov 
630d8ee4a1cSLaxman Dewangan 		bdata->irq = button->irq;
631d8ee4a1cSLaxman Dewangan 
632d8ee4a1cSLaxman Dewangan 		if (button->type && button->type != EV_KEY) {
633d8ee4a1cSLaxman Dewangan 			dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
634d8ee4a1cSLaxman Dewangan 			return -EINVAL;
635bc8f1eafSBen Dooks 		}
636bc8f1eafSBen Dooks 
6378ed92556SDmitry Torokhov 		bdata->release_delay = button->debounce_interval;
638*c9efb0baSPaul Cercueil 		bdata->debounce_use_hrtimer = true;
639019002f2SPaul Cercueil 		hrtimer_init(&bdata->release_timer,
640019002f2SPaul Cercueil 			     CLOCK_REALTIME, HRTIMER_MODE_REL_HARD);
641019002f2SPaul Cercueil 		bdata->release_timer.function = gpio_keys_irq_timer;
642d8ee4a1cSLaxman Dewangan 
643d8ee4a1cSLaxman Dewangan 		isr = gpio_keys_irq_isr;
644d8ee4a1cSLaxman Dewangan 		irqflags = 0;
64583fc580dSJeffy Chen 
64683fc580dSJeffy Chen 		/*
64783fc580dSJeffy Chen 		 * For IRQ buttons, there is no interrupt for release.
64883fc580dSJeffy Chen 		 * So we don't need to reconfigure the trigger type for wakeup.
64983fc580dSJeffy Chen 		 */
650d8ee4a1cSLaxman Dewangan 	}
651d8ee4a1cSLaxman Dewangan 
65283e4947aSHans de Goede 	bdata->code = &ddata->keymap[idx];
65383e4947aSHans de Goede 	*bdata->code = button->code;
65483e4947aSHans de Goede 	input_set_capability(input, button->type ?: EV_KEY, *bdata->code);
655d8ee4a1cSLaxman Dewangan 
6569e3af04fSMika Westerberg 	/*
6578ed92556SDmitry Torokhov 	 * Install custom action to cancel release timer and
65827245519SAlexander Shiyan 	 * workqueue item.
65927245519SAlexander Shiyan 	 */
660b4e66e7dSGuenter Roeck 	error = devm_add_action(dev, gpio_keys_quiesce_key, bdata);
66127245519SAlexander Shiyan 	if (error) {
662b4e66e7dSGuenter Roeck 		dev_err(dev, "failed to register quiesce action, error: %d\n",
66327245519SAlexander Shiyan 			error);
66427245519SAlexander Shiyan 		return error;
66527245519SAlexander Shiyan 	}
66627245519SAlexander Shiyan 
66727245519SAlexander Shiyan 	/*
6689e3af04fSMika Westerberg 	 * If platform has specified that the button can be disabled,
6699e3af04fSMika Westerberg 	 * we don't want it to share the interrupt line.
6709e3af04fSMika Westerberg 	 */
6719e3af04fSMika Westerberg 	if (!button->can_disable)
6729e3af04fSMika Westerberg 		irqflags |= IRQF_SHARED;
6739e3af04fSMika Westerberg 
674b4e66e7dSGuenter Roeck 	error = devm_request_any_context_irq(dev, bdata->irq, isr, irqflags,
675b4e66e7dSGuenter Roeck 					     desc, bdata);
67694a8cab8SPhilippe Langlais 	if (error < 0) {
677bc8f1eafSBen Dooks 		dev_err(dev, "Unable to claim irq %d; error %d\n",
678d8ee4a1cSLaxman Dewangan 			bdata->irq, error);
67927245519SAlexander Shiyan 		return error;
680bc8f1eafSBen Dooks 	}
681bc8f1eafSBen Dooks 
682bc8f1eafSBen Dooks 	return 0;
683bc8f1eafSBen Dooks }
684bc8f1eafSBen Dooks 
6855b76d7b4SDmitry Torokhov static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
6865b76d7b4SDmitry Torokhov {
6875b76d7b4SDmitry Torokhov 	struct input_dev *input = ddata->input;
6885b76d7b4SDmitry Torokhov 	int i;
6895b76d7b4SDmitry Torokhov 
6905b76d7b4SDmitry Torokhov 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
6915b76d7b4SDmitry Torokhov 		struct gpio_button_data *bdata = &ddata->data[i];
6925feeca3cSGeert Uytterhoeven 		if (bdata->gpiod)
6935b76d7b4SDmitry Torokhov 			gpio_keys_gpio_report_event(bdata);
6945b76d7b4SDmitry Torokhov 	}
6955b76d7b4SDmitry Torokhov 	input_sync(input);
6965b76d7b4SDmitry Torokhov }
6975b76d7b4SDmitry Torokhov 
698173bdd74SShubhrajyoti D static int gpio_keys_open(struct input_dev *input)
699173bdd74SShubhrajyoti D {
700173bdd74SShubhrajyoti D 	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
701219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = ddata->pdata;
7025b76d7b4SDmitry Torokhov 	int error;
703173bdd74SShubhrajyoti D 
7045b76d7b4SDmitry Torokhov 	if (pdata->enable) {
7055b76d7b4SDmitry Torokhov 		error = pdata->enable(input->dev.parent);
7065b76d7b4SDmitry Torokhov 		if (error)
7075b76d7b4SDmitry Torokhov 			return error;
7085b76d7b4SDmitry Torokhov 	}
7095b76d7b4SDmitry Torokhov 
7105b76d7b4SDmitry Torokhov 	/* Report current state of buttons that are connected to GPIOs */
7115b76d7b4SDmitry Torokhov 	gpio_keys_report_state(ddata);
7125b76d7b4SDmitry Torokhov 
7135b76d7b4SDmitry Torokhov 	return 0;
714173bdd74SShubhrajyoti D }
715173bdd74SShubhrajyoti D 
716173bdd74SShubhrajyoti D static void gpio_keys_close(struct input_dev *input)
717173bdd74SShubhrajyoti D {
718173bdd74SShubhrajyoti D 	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
719219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = ddata->pdata;
720173bdd74SShubhrajyoti D 
721219edc71SAlexandre Pereira da Silva 	if (pdata->disable)
722219edc71SAlexandre Pereira da Silva 		pdata->disable(input->dev.parent);
723173bdd74SShubhrajyoti D }
724173bdd74SShubhrajyoti D 
725fd05d089SDavid Jander /*
726fd05d089SDavid Jander  * Handlers for alternative sources of platform_data
727fd05d089SDavid Jander  */
728219edc71SAlexandre Pereira da Silva 
729fd05d089SDavid Jander /*
730700a38b2SDmitry Torokhov  * Translate properties into platform_data
731fd05d089SDavid Jander  */
7325298cc4cSBill Pemberton static struct gpio_keys_platform_data *
733219edc71SAlexandre Pereira da Silva gpio_keys_get_devtree_pdata(struct device *dev)
734fd05d089SDavid Jander {
735219edc71SAlexandre Pereira da Silva 	struct gpio_keys_platform_data *pdata;
736219edc71SAlexandre Pereira da Silva 	struct gpio_keys_button *button;
737700a38b2SDmitry Torokhov 	struct fwnode_handle *child;
738219edc71SAlexandre Pereira da Silva 	int nbuttons;
739fd05d089SDavid Jander 
740700a38b2SDmitry Torokhov 	nbuttons = device_get_child_node_count(dev);
7415d422f2eSAndy Shevchenko 	if (nbuttons == 0)
7425d422f2eSAndy Shevchenko 		return ERR_PTR(-ENODEV);
743219edc71SAlexandre Pereira da Silva 
7445d422f2eSAndy Shevchenko 	pdata = devm_kzalloc(dev,
7455d422f2eSAndy Shevchenko 			     sizeof(*pdata) + nbuttons * sizeof(*button),
746219edc71SAlexandre Pereira da Silva 			     GFP_KERNEL);
7475d422f2eSAndy Shevchenko 	if (!pdata)
7485d422f2eSAndy Shevchenko 		return ERR_PTR(-ENOMEM);
749219edc71SAlexandre Pereira da Silva 
7500f78ba96SDmitry Torokhov 	button = (struct gpio_keys_button *)(pdata + 1);
7510f78ba96SDmitry Torokhov 
7520f78ba96SDmitry Torokhov 	pdata->buttons = button;
753219edc71SAlexandre Pereira da Silva 	pdata->nbuttons = nbuttons;
754fd05d089SDavid Jander 
755700a38b2SDmitry Torokhov 	pdata->rep = device_property_read_bool(dev, "autorepeat");
756fd05d089SDavid Jander 
757700a38b2SDmitry Torokhov 	device_property_read_string(dev, "label", &pdata->name);
758c4dc5f8cSLaxman Dewangan 
759700a38b2SDmitry Torokhov 	device_for_each_child_node(dev, child) {
760700a38b2SDmitry Torokhov 		if (is_of_node(child))
761700a38b2SDmitry Torokhov 			button->irq =
762700a38b2SDmitry Torokhov 				irq_of_parse_and_map(to_of_node(child), 0);
763fd05d089SDavid Jander 
764700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "linux,code",
765700a38b2SDmitry Torokhov 					     &button->code)) {
766700a38b2SDmitry Torokhov 			dev_err(dev, "Button without keycode\n");
767700a38b2SDmitry Torokhov 			fwnode_handle_put(child);
76897d86e07SDmitry Torokhov 			return ERR_PTR(-EINVAL);
76997d86e07SDmitry Torokhov 		}
770fd05d089SDavid Jander 
771700a38b2SDmitry Torokhov 		fwnode_property_read_string(child, "label", &button->desc);
772fd05d089SDavid Jander 
773700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "linux,input-type",
774700a38b2SDmitry Torokhov 					     &button->type))
775219edc71SAlexandre Pereira da Silva 			button->type = EV_KEY;
776fd05d089SDavid Jander 
777700a38b2SDmitry Torokhov 		button->wakeup =
778700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "wakeup-source") ||
77999b4ffbdSDmitry Torokhov 			/* legacy name */
780700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "gpio-key,wakeup");
781219edc71SAlexandre Pereira da Silva 
78283fc580dSJeffy Chen 		fwnode_property_read_u32(child, "wakeup-event-action",
78383fc580dSJeffy Chen 					 &button->wakeup_event_action);
78483fc580dSJeffy Chen 
785700a38b2SDmitry Torokhov 		button->can_disable =
786700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "linux,can-disable");
78797d86e07SDmitry Torokhov 
788700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "debounce-interval",
789219edc71SAlexandre Pereira da Silva 					 &button->debounce_interval))
790219edc71SAlexandre Pereira da Silva 			button->debounce_interval = 5;
7910f78ba96SDmitry Torokhov 
7920f78ba96SDmitry Torokhov 		button++;
793219edc71SAlexandre Pereira da Silva 	}
794219edc71SAlexandre Pereira da Silva 
795219edc71SAlexandre Pereira da Silva 	return pdata;
796fd05d089SDavid Jander }
797fd05d089SDavid Jander 
79822daae31SJingoo Han static const struct of_device_id gpio_keys_of_match[] = {
799fd05d089SDavid Jander 	{ .compatible = "gpio-keys", },
800fd05d089SDavid Jander 	{ },
801fd05d089SDavid Jander };
802fd05d089SDavid Jander MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
803fd05d089SDavid Jander 
8045298cc4cSBill Pemberton static int gpio_keys_probe(struct platform_device *pdev)
80578a56aabSPhil Blundell {
806db19fd8bSBen Dooks 	struct device *dev = &pdev->dev;
807219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
808700a38b2SDmitry Torokhov 	struct fwnode_handle *child = NULL;
809219edc71SAlexandre Pereira da Silva 	struct gpio_keys_drvdata *ddata;
81078a56aabSPhil Blundell 	struct input_dev *input;
81178a56aabSPhil Blundell 	int i, error;
812e15b0213SAnti Sullin 	int wakeup = 0;
81378a56aabSPhil Blundell 
814fd05d089SDavid Jander 	if (!pdata) {
815219edc71SAlexandre Pereira da Silva 		pdata = gpio_keys_get_devtree_pdata(dev);
816219edc71SAlexandre Pereira da Silva 		if (IS_ERR(pdata))
817219edc71SAlexandre Pereira da Silva 			return PTR_ERR(pdata);
818fd05d089SDavid Jander 	}
819fd05d089SDavid Jander 
820002cdb95SGustavo A. R. Silva 	ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
821002cdb95SGustavo A. R. Silva 			     GFP_KERNEL);
8225d422f2eSAndy Shevchenko 	if (!ddata) {
823db19fd8bSBen Dooks 		dev_err(dev, "failed to allocate state\n");
8245d422f2eSAndy Shevchenko 		return -ENOMEM;
8255d422f2eSAndy Shevchenko 	}
8265d422f2eSAndy Shevchenko 
82783e4947aSHans de Goede 	ddata->keymap = devm_kcalloc(dev,
82883e4947aSHans de Goede 				     pdata->nbuttons, sizeof(ddata->keymap[0]),
82983e4947aSHans de Goede 				     GFP_KERNEL);
83083e4947aSHans de Goede 	if (!ddata->keymap)
83183e4947aSHans de Goede 		return -ENOMEM;
83283e4947aSHans de Goede 
8335d422f2eSAndy Shevchenko 	input = devm_input_allocate_device(dev);
8345d422f2eSAndy Shevchenko 	if (!input) {
8355d422f2eSAndy Shevchenko 		dev_err(dev, "failed to allocate input device\n");
8365d422f2eSAndy Shevchenko 		return -ENOMEM;
837a33466e3SDmitry Baryshkov 	}
83878a56aabSPhil Blundell 
839219edc71SAlexandre Pereira da Silva 	ddata->pdata = pdata;
8409e3af04fSMika Westerberg 	ddata->input = input;
8419e3af04fSMika Westerberg 	mutex_init(&ddata->disable_lock);
8429e3af04fSMika Westerberg 
843a33466e3SDmitry Baryshkov 	platform_set_drvdata(pdev, ddata);
844173bdd74SShubhrajyoti D 	input_set_drvdata(input, ddata);
84578a56aabSPhil Blundell 
84646711277SAlexander Stein 	input->name = pdata->name ? : pdev->name;
84778a56aabSPhil Blundell 	input->phys = "gpio-keys/input0";
848b4e66e7dSGuenter Roeck 	input->dev.parent = dev;
849173bdd74SShubhrajyoti D 	input->open = gpio_keys_open;
850173bdd74SShubhrajyoti D 	input->close = gpio_keys_close;
85178a56aabSPhil Blundell 
85278a56aabSPhil Blundell 	input->id.bustype = BUS_HOST;
85378a56aabSPhil Blundell 	input->id.vendor = 0x0001;
85478a56aabSPhil Blundell 	input->id.product = 0x0001;
85578a56aabSPhil Blundell 	input->id.version = 0x0100;
85678a56aabSPhil Blundell 
85783e4947aSHans de Goede 	input->keycode = ddata->keymap;
85883e4947aSHans de Goede 	input->keycodesize = sizeof(ddata->keymap[0]);
85983e4947aSHans de Goede 	input->keycodemax = pdata->nbuttons;
86083e4947aSHans de Goede 
861b67b4b11SDominic Curran 	/* Enable auto repeat feature of Linux input subsystem */
862b67b4b11SDominic Curran 	if (pdata->rep)
863b67b4b11SDominic Curran 		__set_bit(EV_REP, input->evbit);
864b67b4b11SDominic Curran 
86578a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
866d9080921SDmitry Torokhov 		const struct gpio_keys_button *button = &pdata->buttons[i];
86778a56aabSPhil Blundell 
868700a38b2SDmitry Torokhov 		if (!dev_get_platdata(dev)) {
869b4e66e7dSGuenter Roeck 			child = device_get_next_child_node(dev, child);
870700a38b2SDmitry Torokhov 			if (!child) {
871b4e66e7dSGuenter Roeck 				dev_err(dev,
872700a38b2SDmitry Torokhov 					"missing child device node for entry %d\n",
873700a38b2SDmitry Torokhov 					i);
874700a38b2SDmitry Torokhov 				return -EINVAL;
875700a38b2SDmitry Torokhov 			}
876700a38b2SDmitry Torokhov 		}
877700a38b2SDmitry Torokhov 
87883e4947aSHans de Goede 		error = gpio_keys_setup_key(pdev, input, ddata,
87983e4947aSHans de Goede 					    button, i, child);
880700a38b2SDmitry Torokhov 		if (error) {
881700a38b2SDmitry Torokhov 			fwnode_handle_put(child);
88227245519SAlexander Shiyan 			return error;
883700a38b2SDmitry Torokhov 		}
88484767d00SRoman Moravcik 
885e15b0213SAnti Sullin 		if (button->wakeup)
886e15b0213SAnti Sullin 			wakeup = 1;
88778a56aabSPhil Blundell 	}
88878a56aabSPhil Blundell 
889700a38b2SDmitry Torokhov 	fwnode_handle_put(child);
890700a38b2SDmitry Torokhov 
89178a56aabSPhil Blundell 	error = input_register_device(input);
89278a56aabSPhil Blundell 	if (error) {
8939e3af04fSMika Westerberg 		dev_err(dev, "Unable to register input device, error: %d\n",
8949e3af04fSMika Westerberg 			error);
89578a56aabSPhil Blundell 		return error;
89678a56aabSPhil Blundell 	}
89778a56aabSPhil Blundell 
8983184125eSDmitry Torokhov 	device_init_wakeup(dev, wakeup);
8999e3af04fSMika Westerberg 
90078a56aabSPhil Blundell 	return 0;
90178a56aabSPhil Blundell }
90278a56aabSPhil Blundell 
90383fc580dSJeffy Chen static int __maybe_unused
90483fc580dSJeffy Chen gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata)
90583fc580dSJeffy Chen {
90683fc580dSJeffy Chen 	int error;
90783fc580dSJeffy Chen 
90883fc580dSJeffy Chen 	error = enable_irq_wake(bdata->irq);
90983fc580dSJeffy Chen 	if (error) {
91083fc580dSJeffy Chen 		dev_err(bdata->input->dev.parent,
91183fc580dSJeffy Chen 			"failed to configure IRQ %d as wakeup source: %d\n",
91283fc580dSJeffy Chen 			bdata->irq, error);
91383fc580dSJeffy Chen 		return error;
91483fc580dSJeffy Chen 	}
91583fc580dSJeffy Chen 
91683fc580dSJeffy Chen 	if (bdata->wakeup_trigger_type) {
91783fc580dSJeffy Chen 		error = irq_set_irq_type(bdata->irq,
91883fc580dSJeffy Chen 					 bdata->wakeup_trigger_type);
91983fc580dSJeffy Chen 		if (error) {
92083fc580dSJeffy Chen 			dev_err(bdata->input->dev.parent,
92183fc580dSJeffy Chen 				"failed to set wakeup trigger %08x for IRQ %d: %d\n",
92283fc580dSJeffy Chen 				bdata->wakeup_trigger_type, bdata->irq, error);
92383fc580dSJeffy Chen 			disable_irq_wake(bdata->irq);
92483fc580dSJeffy Chen 			return error;
92583fc580dSJeffy Chen 		}
92683fc580dSJeffy Chen 	}
92783fc580dSJeffy Chen 
92883fc580dSJeffy Chen 	return 0;
92983fc580dSJeffy Chen }
93083fc580dSJeffy Chen 
93183fc580dSJeffy Chen static void __maybe_unused
93283fc580dSJeffy Chen gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata)
93383fc580dSJeffy Chen {
93483fc580dSJeffy Chen 	int error;
93583fc580dSJeffy Chen 
93683fc580dSJeffy Chen 	/*
93783fc580dSJeffy Chen 	 * The trigger type is always both edges for gpio-based keys and we do
93883fc580dSJeffy Chen 	 * not support changing wakeup trigger for interrupt-based keys.
93983fc580dSJeffy Chen 	 */
94083fc580dSJeffy Chen 	if (bdata->wakeup_trigger_type) {
94183fc580dSJeffy Chen 		error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH);
94283fc580dSJeffy Chen 		if (error)
94383fc580dSJeffy Chen 			dev_warn(bdata->input->dev.parent,
94483fc580dSJeffy Chen 				 "failed to restore interrupt trigger for IRQ %d: %d\n",
94583fc580dSJeffy Chen 				 bdata->irq, error);
94683fc580dSJeffy Chen 	}
94783fc580dSJeffy Chen 
94883fc580dSJeffy Chen 	error = disable_irq_wake(bdata->irq);
94983fc580dSJeffy Chen 	if (error)
95083fc580dSJeffy Chen 		dev_warn(bdata->input->dev.parent,
95183fc580dSJeffy Chen 			 "failed to disable IRQ %d as wake source: %d\n",
95283fc580dSJeffy Chen 			 bdata->irq, error);
95383fc580dSJeffy Chen }
95483fc580dSJeffy Chen 
95583fc580dSJeffy Chen static int __maybe_unused
95683fc580dSJeffy Chen gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata)
95783fc580dSJeffy Chen {
95883fc580dSJeffy Chen 	struct gpio_button_data *bdata;
95983fc580dSJeffy Chen 	int error;
96083fc580dSJeffy Chen 	int i;
96183fc580dSJeffy Chen 
96283fc580dSJeffy Chen 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
96383fc580dSJeffy Chen 		bdata = &ddata->data[i];
96483fc580dSJeffy Chen 		if (bdata->button->wakeup) {
96583fc580dSJeffy Chen 			error = gpio_keys_button_enable_wakeup(bdata);
96683fc580dSJeffy Chen 			if (error)
96783fc580dSJeffy Chen 				goto err_out;
96883fc580dSJeffy Chen 		}
96983fc580dSJeffy Chen 		bdata->suspended = true;
97083fc580dSJeffy Chen 	}
97183fc580dSJeffy Chen 
97283fc580dSJeffy Chen 	return 0;
97383fc580dSJeffy Chen 
97483fc580dSJeffy Chen err_out:
97583fc580dSJeffy Chen 	while (i--) {
97683fc580dSJeffy Chen 		bdata = &ddata->data[i];
97783fc580dSJeffy Chen 		if (bdata->button->wakeup)
97883fc580dSJeffy Chen 			gpio_keys_button_disable_wakeup(bdata);
97983fc580dSJeffy Chen 		bdata->suspended = false;
98083fc580dSJeffy Chen 	}
98183fc580dSJeffy Chen 
98283fc580dSJeffy Chen 	return error;
98383fc580dSJeffy Chen }
98483fc580dSJeffy Chen 
98583fc580dSJeffy Chen static void __maybe_unused
98683fc580dSJeffy Chen gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata)
98783fc580dSJeffy Chen {
98883fc580dSJeffy Chen 	struct gpio_button_data *bdata;
98983fc580dSJeffy Chen 	int i;
99083fc580dSJeffy Chen 
99183fc580dSJeffy Chen 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
99283fc580dSJeffy Chen 		bdata = &ddata->data[i];
99383fc580dSJeffy Chen 		bdata->suspended = false;
99483fc580dSJeffy Chen 		if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq)))
99583fc580dSJeffy Chen 			gpio_keys_button_disable_wakeup(bdata);
99683fc580dSJeffy Chen 	}
99783fc580dSJeffy Chen }
99883fc580dSJeffy Chen 
999f9645f22SDmitry Torokhov static int __maybe_unused gpio_keys_suspend(struct device *dev)
1000e15b0213SAnti Sullin {
1001fd05d089SDavid Jander 	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
1002dda19a92SJonas Aaberg 	struct input_dev *input = ddata->input;
100383fc580dSJeffy Chen 	int error;
1004e15b0213SAnti Sullin 
1005fd05d089SDavid Jander 	if (device_may_wakeup(dev)) {
100683fc580dSJeffy Chen 		error = gpio_keys_enable_wakeup(ddata);
100783fc580dSJeffy Chen 		if (error)
100883fc580dSJeffy Chen 			return error;
1009dda19a92SJonas Aaberg 	} else {
1010dda19a92SJonas Aaberg 		mutex_lock(&input->mutex);
1011d69f0a43SAndrzej Pietrasiewicz 		if (input_device_enabled(input))
1012dda19a92SJonas Aaberg 			gpio_keys_close(input);
1013dda19a92SJonas Aaberg 		mutex_unlock(&input->mutex);
1014e15b0213SAnti Sullin 	}
1015e15b0213SAnti Sullin 
1016e15b0213SAnti Sullin 	return 0;
1017e15b0213SAnti Sullin }
1018e15b0213SAnti Sullin 
1019f9645f22SDmitry Torokhov static int __maybe_unused gpio_keys_resume(struct device *dev)
1020e15b0213SAnti Sullin {
1021fd05d089SDavid Jander 	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
1022dda19a92SJonas Aaberg 	struct input_dev *input = ddata->input;
1023dda19a92SJonas Aaberg 	int error = 0;
1024e15b0213SAnti Sullin 
1025dda19a92SJonas Aaberg 	if (device_may_wakeup(dev)) {
102683fc580dSJeffy Chen 		gpio_keys_disable_wakeup(ddata);
1027dda19a92SJonas Aaberg 	} else {
1028dda19a92SJonas Aaberg 		mutex_lock(&input->mutex);
1029d69f0a43SAndrzej Pietrasiewicz 		if (input_device_enabled(input))
1030dda19a92SJonas Aaberg 			error = gpio_keys_open(input);
1031dda19a92SJonas Aaberg 		mutex_unlock(&input->mutex);
1032dda19a92SJonas Aaberg 	}
1033dda19a92SJonas Aaberg 
1034dda19a92SJonas Aaberg 	if (error)
1035dda19a92SJonas Aaberg 		return error;
10365b76d7b4SDmitry Torokhov 
10375b76d7b4SDmitry Torokhov 	gpio_keys_report_state(ddata);
1038e15b0213SAnti Sullin 	return 0;
1039e15b0213SAnti Sullin }
1040e15b0213SAnti Sullin 
1041bdda8216SDmitry Torokhov static SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
1042bdda8216SDmitry Torokhov 
1043a5c5e50cSFlorian Fainelli static void gpio_keys_shutdown(struct platform_device *pdev)
1044a5c5e50cSFlorian Fainelli {
1045a5c5e50cSFlorian Fainelli 	int ret;
1046a5c5e50cSFlorian Fainelli 
1047a5c5e50cSFlorian Fainelli 	ret = gpio_keys_suspend(&pdev->dev);
1048a5c5e50cSFlorian Fainelli 	if (ret)
1049a5c5e50cSFlorian Fainelli 		dev_err(&pdev->dev, "failed to shutdown\n");
1050a5c5e50cSFlorian Fainelli }
1051a5c5e50cSFlorian Fainelli 
10529b07044cSUwe Kleine-König static struct platform_driver gpio_keys_device_driver = {
105378a56aabSPhil Blundell 	.probe		= gpio_keys_probe,
1054a5c5e50cSFlorian Fainelli 	.shutdown	= gpio_keys_shutdown,
105578a56aabSPhil Blundell 	.driver		= {
105678a56aabSPhil Blundell 		.name	= "gpio-keys",
1057ae78e0e0SMike Rapoport 		.pm	= &gpio_keys_pm_ops,
1058700a38b2SDmitry Torokhov 		.of_match_table = gpio_keys_of_match,
1059cf1b2a20SGreg Kroah-Hartman 		.dev_groups	= gpio_keys_groups,
106078a56aabSPhil Blundell 	}
106178a56aabSPhil Blundell };
106278a56aabSPhil Blundell 
106378a56aabSPhil Blundell static int __init gpio_keys_init(void)
106478a56aabSPhil Blundell {
106578a56aabSPhil Blundell 	return platform_driver_register(&gpio_keys_device_driver);
106678a56aabSPhil Blundell }
106778a56aabSPhil Blundell 
106878a56aabSPhil Blundell static void __exit gpio_keys_exit(void)
106978a56aabSPhil Blundell {
107078a56aabSPhil Blundell 	platform_driver_unregister(&gpio_keys_device_driver);
107178a56aabSPhil Blundell }
107278a56aabSPhil Blundell 
1073b2330205SDavid Jander late_initcall(gpio_keys_init);
107478a56aabSPhil Blundell module_exit(gpio_keys_exit);
107578a56aabSPhil Blundell 
107678a56aabSPhil Blundell MODULE_LICENSE("GPL");
107778a56aabSPhil Blundell MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
10787e2ecdf4SDavid Jander MODULE_DESCRIPTION("Keyboard driver for GPIOs");
1079d7b5247bSKay Sievers MODULE_ALIAS("platform:gpio-keys");
1080