xref: /openbmc/linux/drivers/input/keyboard/gpio_keys.c (revision c900529f3d9161bfde5cca0754f83b4d3c3e0220)
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;
44c9efb0baSPaul 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;
53c9efb0baSPaul 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  */
get_n_events_by_type(int type)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  */
get_bm_events_by_type(struct input_dev * dev,int type)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 
gpio_keys_quiesce_key(void * data)1282531fdbfSDmitry Torokhov static void gpio_keys_quiesce_key(void *data)
1292531fdbfSDmitry Torokhov {
1302531fdbfSDmitry Torokhov 	struct gpio_button_data *bdata = data;
1312531fdbfSDmitry Torokhov 
1322531fdbfSDmitry Torokhov 	if (!bdata->gpiod)
1332531fdbfSDmitry Torokhov 		hrtimer_cancel(&bdata->release_timer);
134cee409bbSLad Prabhakar 	else if (bdata->debounce_use_hrtimer)
1352531fdbfSDmitry Torokhov 		hrtimer_cancel(&bdata->debounce_timer);
1362531fdbfSDmitry Torokhov 	else
1372531fdbfSDmitry Torokhov 		cancel_delayed_work_sync(&bdata->work);
1382531fdbfSDmitry Torokhov }
1392531fdbfSDmitry Torokhov 
1408679ee42SDmitry Torokhov /**
1419e3af04fSMika Westerberg  * gpio_keys_disable_button() - disables given GPIO button
1429e3af04fSMika Westerberg  * @bdata: button data for button to be disabled
1439e3af04fSMika Westerberg  *
1449e3af04fSMika Westerberg  * Disables button pointed by @bdata. This is done by masking
1459e3af04fSMika Westerberg  * IRQ line. After this function is called, button won't generate
1469e3af04fSMika Westerberg  * input events anymore. Note that one can only disable buttons
1479e3af04fSMika Westerberg  * that don't share IRQs.
1489e3af04fSMika Westerberg  *
1499e3af04fSMika Westerberg  * Make sure that @bdata->disable_lock is locked when entering
1509e3af04fSMika Westerberg  * this function to avoid races when concurrent threads are
1519e3af04fSMika Westerberg  * disabling buttons at the same time.
1529e3af04fSMika Westerberg  */
gpio_keys_disable_button(struct gpio_button_data * bdata)1539e3af04fSMika Westerberg static void gpio_keys_disable_button(struct gpio_button_data *bdata)
1549e3af04fSMika Westerberg {
1559e3af04fSMika Westerberg 	if (!bdata->disabled) {
1569e3af04fSMika Westerberg 		/*
1578ed92556SDmitry Torokhov 		 * Disable IRQ and associated timer/work structure.
1589e3af04fSMika Westerberg 		 */
159d8ee4a1cSLaxman Dewangan 		disable_irq(bdata->irq);
1602531fdbfSDmitry Torokhov 		gpio_keys_quiesce_key(bdata);
1619e3af04fSMika Westerberg 		bdata->disabled = true;
1629e3af04fSMika Westerberg 	}
1639e3af04fSMika Westerberg }
1649e3af04fSMika Westerberg 
1659e3af04fSMika Westerberg /**
1669e3af04fSMika Westerberg  * gpio_keys_enable_button() - enables given GPIO button
1679e3af04fSMika Westerberg  * @bdata: button data for button to be disabled
1689e3af04fSMika Westerberg  *
1699e3af04fSMika Westerberg  * Enables given button pointed by @bdata.
1709e3af04fSMika Westerberg  *
1719e3af04fSMika Westerberg  * Make sure that @bdata->disable_lock is locked when entering
1729e3af04fSMika Westerberg  * this function to avoid races with concurrent threads trying
1739e3af04fSMika Westerberg  * to enable the same button at the same time.
1749e3af04fSMika Westerberg  */
gpio_keys_enable_button(struct gpio_button_data * bdata)1759e3af04fSMika Westerberg static void gpio_keys_enable_button(struct gpio_button_data *bdata)
1769e3af04fSMika Westerberg {
1779e3af04fSMika Westerberg 	if (bdata->disabled) {
178d8ee4a1cSLaxman Dewangan 		enable_irq(bdata->irq);
1799e3af04fSMika Westerberg 		bdata->disabled = false;
1809e3af04fSMika Westerberg 	}
1819e3af04fSMika Westerberg }
1829e3af04fSMika Westerberg 
1839e3af04fSMika Westerberg /**
1849e3af04fSMika Westerberg  * gpio_keys_attr_show_helper() - fill in stringified bitmap of buttons
1859e3af04fSMika Westerberg  * @ddata: pointer to drvdata
1869e3af04fSMika Westerberg  * @buf: buffer where stringified bitmap is written
1879e3af04fSMika Westerberg  * @type: button type (%EV_KEY, %EV_SW)
1889e3af04fSMika Westerberg  * @only_disabled: does caller want only those buttons that are
1899e3af04fSMika Westerberg  *                 currently disabled or all buttons that can be
1909e3af04fSMika Westerberg  *                 disabled
1919e3af04fSMika Westerberg  *
1929e3af04fSMika Westerberg  * This function writes buttons that can be disabled to @buf. If
1939e3af04fSMika Westerberg  * @only_disabled is true, then @buf contains only those buttons
1949e3af04fSMika Westerberg  * that are currently disabled. Returns 0 on success or negative
1959e3af04fSMika Westerberg  * errno on failure.
1969e3af04fSMika Westerberg  */
gpio_keys_attr_show_helper(struct gpio_keys_drvdata * ddata,char * buf,unsigned int type,bool only_disabled)1979e3af04fSMika Westerberg static ssize_t gpio_keys_attr_show_helper(struct gpio_keys_drvdata *ddata,
1989e3af04fSMika Westerberg 					  char *buf, unsigned int type,
1999e3af04fSMika Westerberg 					  bool only_disabled)
2009e3af04fSMika Westerberg {
2019e3af04fSMika Westerberg 	int n_events = get_n_events_by_type(type);
2029e3af04fSMika Westerberg 	unsigned long *bits;
2039e3af04fSMika Westerberg 	ssize_t ret;
2049e3af04fSMika Westerberg 	int i;
2059e3af04fSMika Westerberg 
2064e2ec39dSAndy Shevchenko 	bits = bitmap_zalloc(n_events, GFP_KERNEL);
2079e3af04fSMika Westerberg 	if (!bits)
2089e3af04fSMika Westerberg 		return -ENOMEM;
2099e3af04fSMika Westerberg 
210219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2119e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2129e3af04fSMika Westerberg 
2139e3af04fSMika Westerberg 		if (bdata->button->type != type)
2149e3af04fSMika Westerberg 			continue;
2159e3af04fSMika Westerberg 
2169e3af04fSMika Westerberg 		if (only_disabled && !bdata->disabled)
2179e3af04fSMika Westerberg 			continue;
2189e3af04fSMika Westerberg 
21983e4947aSHans de Goede 		__set_bit(*bdata->code, bits);
2209e3af04fSMika Westerberg 	}
2219e3af04fSMika Westerberg 
2220b480037STejun Heo 	ret = scnprintf(buf, PAGE_SIZE - 1, "%*pbl", n_events, bits);
2239e3af04fSMika Westerberg 	buf[ret++] = '\n';
2249e3af04fSMika Westerberg 	buf[ret] = '\0';
2259e3af04fSMika Westerberg 
2264e2ec39dSAndy Shevchenko 	bitmap_free(bits);
2279e3af04fSMika Westerberg 
2289e3af04fSMika Westerberg 	return ret;
2299e3af04fSMika Westerberg }
2309e3af04fSMika Westerberg 
2319e3af04fSMika Westerberg /**
2329e3af04fSMika Westerberg  * gpio_keys_attr_store_helper() - enable/disable buttons based on given bitmap
2339e3af04fSMika Westerberg  * @ddata: pointer to drvdata
2349e3af04fSMika Westerberg  * @buf: buffer from userspace that contains stringified bitmap
2359e3af04fSMika Westerberg  * @type: button type (%EV_KEY, %EV_SW)
2369e3af04fSMika Westerberg  *
2379e3af04fSMika Westerberg  * This function parses stringified bitmap from @buf and disables/enables
238a16ca239SDmitry Torokhov  * GPIO buttons accordingly. Returns 0 on success and negative error
2399e3af04fSMika Westerberg  * on failure.
2409e3af04fSMika Westerberg  */
gpio_keys_attr_store_helper(struct gpio_keys_drvdata * ddata,const char * buf,unsigned int type)2419e3af04fSMika Westerberg static ssize_t gpio_keys_attr_store_helper(struct gpio_keys_drvdata *ddata,
2429e3af04fSMika Westerberg 					   const char *buf, unsigned int type)
2439e3af04fSMika Westerberg {
2449e3af04fSMika Westerberg 	int n_events = get_n_events_by_type(type);
2458679ee42SDmitry Torokhov 	const unsigned long *bitmap = get_bm_events_by_type(ddata->input, type);
2469e3af04fSMika Westerberg 	unsigned long *bits;
2479e3af04fSMika Westerberg 	ssize_t error;
2489e3af04fSMika Westerberg 	int i;
2499e3af04fSMika Westerberg 
25008a6df09SChristophe JAILLET 	bits = bitmap_alloc(n_events, GFP_KERNEL);
2519e3af04fSMika Westerberg 	if (!bits)
2529e3af04fSMika Westerberg 		return -ENOMEM;
2539e3af04fSMika Westerberg 
2549e3af04fSMika Westerberg 	error = bitmap_parselist(buf, bits, n_events);
2559e3af04fSMika Westerberg 	if (error)
2569e3af04fSMika Westerberg 		goto out;
2579e3af04fSMika Westerberg 
2589e3af04fSMika Westerberg 	/* First validate */
2598679ee42SDmitry Torokhov 	if (!bitmap_subset(bits, bitmap, n_events)) {
2608679ee42SDmitry Torokhov 		error = -EINVAL;
2618679ee42SDmitry Torokhov 		goto out;
2628679ee42SDmitry Torokhov 	}
2638679ee42SDmitry Torokhov 
264219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2659e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2669e3af04fSMika Westerberg 
2679e3af04fSMika Westerberg 		if (bdata->button->type != type)
2689e3af04fSMika Westerberg 			continue;
2699e3af04fSMika Westerberg 
27083e4947aSHans de Goede 		if (test_bit(*bdata->code, bits) &&
2719e3af04fSMika Westerberg 		    !bdata->button->can_disable) {
2729e3af04fSMika Westerberg 			error = -EINVAL;
2739e3af04fSMika Westerberg 			goto out;
2749e3af04fSMika Westerberg 		}
2759e3af04fSMika Westerberg 	}
2769e3af04fSMika Westerberg 
2779e3af04fSMika Westerberg 	mutex_lock(&ddata->disable_lock);
2789e3af04fSMika Westerberg 
279219edc71SAlexandre Pereira da Silva 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
2809e3af04fSMika Westerberg 		struct gpio_button_data *bdata = &ddata->data[i];
2819e3af04fSMika Westerberg 
2829e3af04fSMika Westerberg 		if (bdata->button->type != type)
2839e3af04fSMika Westerberg 			continue;
2849e3af04fSMika Westerberg 
28583e4947aSHans de Goede 		if (test_bit(*bdata->code, bits))
2869e3af04fSMika Westerberg 			gpio_keys_disable_button(bdata);
2879e3af04fSMika Westerberg 		else
2889e3af04fSMika Westerberg 			gpio_keys_enable_button(bdata);
2899e3af04fSMika Westerberg 	}
2909e3af04fSMika Westerberg 
2919e3af04fSMika Westerberg 	mutex_unlock(&ddata->disable_lock);
2929e3af04fSMika Westerberg 
2939e3af04fSMika Westerberg out:
2944e2ec39dSAndy Shevchenko 	bitmap_free(bits);
2959e3af04fSMika Westerberg 	return error;
2969e3af04fSMika Westerberg }
2979e3af04fSMika Westerberg 
2989e3af04fSMika Westerberg #define ATTR_SHOW_FN(name, type, only_disabled)				\
2999e3af04fSMika Westerberg static ssize_t gpio_keys_show_##name(struct device *dev,		\
3009e3af04fSMika Westerberg 				     struct device_attribute *attr,	\
3019e3af04fSMika Westerberg 				     char *buf)				\
3029e3af04fSMika Westerberg {									\
3039e3af04fSMika Westerberg 	struct platform_device *pdev = to_platform_device(dev);		\
3049e3af04fSMika Westerberg 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
3059e3af04fSMika Westerberg 									\
3069e3af04fSMika Westerberg 	return gpio_keys_attr_show_helper(ddata, buf,			\
3079e3af04fSMika Westerberg 					  type, only_disabled);		\
3089e3af04fSMika Westerberg }
3099e3af04fSMika Westerberg 
3109e3af04fSMika Westerberg ATTR_SHOW_FN(keys, EV_KEY, false);
3119e3af04fSMika Westerberg ATTR_SHOW_FN(switches, EV_SW, false);
3129e3af04fSMika Westerberg ATTR_SHOW_FN(disabled_keys, EV_KEY, true);
3139e3af04fSMika Westerberg ATTR_SHOW_FN(disabled_switches, EV_SW, true);
3149e3af04fSMika Westerberg 
3159e3af04fSMika Westerberg /*
3169e3af04fSMika Westerberg  * ATTRIBUTES:
3179e3af04fSMika Westerberg  *
3189e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/keys [ro]
3199e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/switches [ro]
3209e3af04fSMika Westerberg  */
3219e3af04fSMika Westerberg static DEVICE_ATTR(keys, S_IRUGO, gpio_keys_show_keys, NULL);
3229e3af04fSMika Westerberg static DEVICE_ATTR(switches, S_IRUGO, gpio_keys_show_switches, NULL);
3239e3af04fSMika Westerberg 
3249e3af04fSMika Westerberg #define ATTR_STORE_FN(name, type)					\
3259e3af04fSMika Westerberg static ssize_t gpio_keys_store_##name(struct device *dev,		\
3269e3af04fSMika Westerberg 				      struct device_attribute *attr,	\
3279e3af04fSMika Westerberg 				      const char *buf,			\
3289e3af04fSMika Westerberg 				      size_t count)			\
3299e3af04fSMika Westerberg {									\
3309e3af04fSMika Westerberg 	struct platform_device *pdev = to_platform_device(dev);		\
3319e3af04fSMika Westerberg 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);	\
3329e3af04fSMika Westerberg 	ssize_t error;							\
3339e3af04fSMika Westerberg 									\
3349e3af04fSMika Westerberg 	error = gpio_keys_attr_store_helper(ddata, buf, type);		\
3359e3af04fSMika Westerberg 	if (error)							\
3369e3af04fSMika Westerberg 		return error;						\
3379e3af04fSMika Westerberg 									\
3389e3af04fSMika Westerberg 	return count;							\
3399e3af04fSMika Westerberg }
3409e3af04fSMika Westerberg 
3419e3af04fSMika Westerberg ATTR_STORE_FN(disabled_keys, EV_KEY);
3429e3af04fSMika Westerberg ATTR_STORE_FN(disabled_switches, EV_SW);
3439e3af04fSMika Westerberg 
3449e3af04fSMika Westerberg /*
3459e3af04fSMika Westerberg  * ATTRIBUTES:
3469e3af04fSMika Westerberg  *
3479e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/disabled_keys [rw]
3489e3af04fSMika Westerberg  * /sys/devices/platform/gpio-keys/disables_switches [rw]
3499e3af04fSMika Westerberg  */
3509e3af04fSMika Westerberg static DEVICE_ATTR(disabled_keys, S_IWUSR | S_IRUGO,
3519e3af04fSMika Westerberg 		   gpio_keys_show_disabled_keys,
3529e3af04fSMika Westerberg 		   gpio_keys_store_disabled_keys);
3539e3af04fSMika Westerberg static DEVICE_ATTR(disabled_switches, S_IWUSR | S_IRUGO,
3549e3af04fSMika Westerberg 		   gpio_keys_show_disabled_switches,
3559e3af04fSMika Westerberg 		   gpio_keys_store_disabled_switches);
3569e3af04fSMika Westerberg 
3579e3af04fSMika Westerberg static struct attribute *gpio_keys_attrs[] = {
3589e3af04fSMika Westerberg 	&dev_attr_keys.attr,
3599e3af04fSMika Westerberg 	&dev_attr_switches.attr,
3609e3af04fSMika Westerberg 	&dev_attr_disabled_keys.attr,
3619e3af04fSMika Westerberg 	&dev_attr_disabled_switches.attr,
3629e3af04fSMika Westerberg 	NULL,
3639e3af04fSMika Westerberg };
364cf1b2a20SGreg Kroah-Hartman ATTRIBUTE_GROUPS(gpio_keys);
3659e3af04fSMika Westerberg 
gpio_keys_gpio_report_event(struct gpio_button_data * bdata)366d8ee4a1cSLaxman Dewangan static void gpio_keys_gpio_report_event(struct gpio_button_data *bdata)
36778a56aabSPhil Blundell {
368d9080921SDmitry Torokhov 	const struct gpio_keys_button *button = bdata->button;
369ce25d7e9SUwe Kleine-König 	struct input_dev *input = bdata->input;
37084767d00SRoman Moravcik 	unsigned int type = button->type ?: EV_KEY;
3715feeca3cSGeert Uytterhoeven 	int state;
37284767d00SRoman Moravcik 
373c9efb0baSPaul Cercueil 	state = bdata->debounce_use_hrtimer ?
374c9efb0baSPaul Cercueil 			gpiod_get_value(bdata->gpiod) :
375c9efb0baSPaul Cercueil 			gpiod_get_value_cansleep(bdata->gpiod);
37677fa0554SBjorn Andersson 	if (state < 0) {
3775feeca3cSGeert Uytterhoeven 		dev_err(input->dev.parent,
3785feeca3cSGeert Uytterhoeven 			"failed to get gpio state: %d\n", state);
37977fa0554SBjorn Andersson 		return;
38077fa0554SBjorn Andersson 	}
38177fa0554SBjorn Andersson 
38292a47674SAlexander Stein 	if (type == EV_ABS) {
38392a47674SAlexander Stein 		if (state)
38492a47674SAlexander Stein 			input_event(input, type, button->code, button->value);
38592a47674SAlexander Stein 	} else {
38683e4947aSHans de Goede 		input_event(input, type, *bdata->code, state);
38792a47674SAlexander Stein 	}
388a33466e3SDmitry Baryshkov }
389a33466e3SDmitry Baryshkov 
gpio_keys_debounce_event(struct gpio_button_data * bdata)390c9efb0baSPaul Cercueil static void gpio_keys_debounce_event(struct gpio_button_data *bdata)
3916ee88d71SDaniel Mack {
392d8ee4a1cSLaxman Dewangan 	gpio_keys_gpio_report_event(bdata);
39336a8fc6fSPaul Cercueil 	input_sync(bdata->input);
3942fba26c6SNeilBrown 
3952fba26c6SNeilBrown 	if (bdata->button->wakeup)
3962fba26c6SNeilBrown 		pm_relax(bdata->input->dev.parent);
3976ee88d71SDaniel Mack }
3986ee88d71SDaniel Mack 
gpio_keys_gpio_work_func(struct work_struct * work)399c9efb0baSPaul Cercueil static void gpio_keys_gpio_work_func(struct work_struct *work)
400c9efb0baSPaul Cercueil {
401c9efb0baSPaul Cercueil 	struct gpio_button_data *bdata =
402c9efb0baSPaul Cercueil 		container_of(work, struct gpio_button_data, work.work);
403c9efb0baSPaul Cercueil 
404c9efb0baSPaul Cercueil 	gpio_keys_debounce_event(bdata);
405c9efb0baSPaul Cercueil }
406c9efb0baSPaul Cercueil 
gpio_keys_debounce_timer(struct hrtimer * t)407c9efb0baSPaul Cercueil static enum hrtimer_restart gpio_keys_debounce_timer(struct hrtimer *t)
408c9efb0baSPaul Cercueil {
409c9efb0baSPaul Cercueil 	struct gpio_button_data *bdata =
410c9efb0baSPaul Cercueil 		container_of(t, struct gpio_button_data, debounce_timer);
411c9efb0baSPaul Cercueil 
412c9efb0baSPaul Cercueil 	gpio_keys_debounce_event(bdata);
413c9efb0baSPaul Cercueil 
414c9efb0baSPaul Cercueil 	return HRTIMER_NORESTART;
415c9efb0baSPaul Cercueil }
416c9efb0baSPaul Cercueil 
gpio_keys_gpio_isr(int irq,void * dev_id)417d8ee4a1cSLaxman Dewangan static irqreturn_t gpio_keys_gpio_isr(int irq, void *dev_id)
418a33466e3SDmitry Baryshkov {
41957ffe9d5SUwe Kleine-König 	struct gpio_button_data *bdata = dev_id;
420a33466e3SDmitry Baryshkov 
421d8ee4a1cSLaxman Dewangan 	BUG_ON(irq != bdata->irq);
422a33466e3SDmitry Baryshkov 
4230f107573SJoseph Lo 	if (bdata->button->wakeup) {
4240f107573SJoseph Lo 		const struct gpio_keys_button *button = bdata->button;
4250f107573SJoseph Lo 
4262fba26c6SNeilBrown 		pm_stay_awake(bdata->input->dev.parent);
4270f107573SJoseph Lo 		if (bdata->suspended  &&
4280f107573SJoseph Lo 		    (button->type == 0 || button->type == EV_KEY)) {
4290f107573SJoseph Lo 			/*
4300f107573SJoseph Lo 			 * Simulate wakeup key press in case the key has
4310f107573SJoseph Lo 			 * already released by the time we got interrupt
4320f107573SJoseph Lo 			 * handler to run.
4330f107573SJoseph Lo 			 */
4340f107573SJoseph Lo 			input_report_key(bdata->input, button->code, 1);
4350f107573SJoseph Lo 		}
4360f107573SJoseph Lo 	}
4378ed92556SDmitry Torokhov 
438c9efb0baSPaul Cercueil 	if (bdata->debounce_use_hrtimer) {
439c9efb0baSPaul Cercueil 		hrtimer_start(&bdata->debounce_timer,
440c9efb0baSPaul Cercueil 			      ms_to_ktime(bdata->software_debounce),
441c9efb0baSPaul Cercueil 			      HRTIMER_MODE_REL);
442c9efb0baSPaul Cercueil 	} else {
4438ed92556SDmitry Torokhov 		mod_delayed_work(system_wq,
4448ed92556SDmitry Torokhov 				 &bdata->work,
4458ed92556SDmitry Torokhov 				 msecs_to_jiffies(bdata->software_debounce));
446c9efb0baSPaul Cercueil 	}
447a33466e3SDmitry Baryshkov 
4481164ec1aSDavid Brownell 	return IRQ_HANDLED;
44978a56aabSPhil Blundell }
45078a56aabSPhil Blundell 
gpio_keys_irq_timer(struct hrtimer * t)451019002f2SPaul Cercueil static enum hrtimer_restart gpio_keys_irq_timer(struct hrtimer *t)
452d8ee4a1cSLaxman Dewangan {
453019002f2SPaul Cercueil 	struct gpio_button_data *bdata = container_of(t,
454019002f2SPaul Cercueil 						      struct gpio_button_data,
455019002f2SPaul Cercueil 						      release_timer);
456d8ee4a1cSLaxman Dewangan 	struct input_dev *input = bdata->input;
457d8ee4a1cSLaxman Dewangan 
458d8ee4a1cSLaxman Dewangan 	if (bdata->key_pressed) {
4593615536cSGeert Uytterhoeven 		input_report_key(input, *bdata->code, 0);
460d8ee4a1cSLaxman Dewangan 		input_sync(input);
461d8ee4a1cSLaxman Dewangan 		bdata->key_pressed = false;
462d8ee4a1cSLaxman Dewangan 	}
463019002f2SPaul Cercueil 
464019002f2SPaul Cercueil 	return HRTIMER_NORESTART;
465d8ee4a1cSLaxman Dewangan }
466d8ee4a1cSLaxman Dewangan 
gpio_keys_irq_isr(int irq,void * dev_id)467d8ee4a1cSLaxman Dewangan static irqreturn_t gpio_keys_irq_isr(int irq, void *dev_id)
468d8ee4a1cSLaxman Dewangan {
469d8ee4a1cSLaxman Dewangan 	struct gpio_button_data *bdata = dev_id;
470d8ee4a1cSLaxman Dewangan 	struct input_dev *input = bdata->input;
471d8ee4a1cSLaxman Dewangan 	unsigned long flags;
472d8ee4a1cSLaxman Dewangan 
473d8ee4a1cSLaxman Dewangan 	BUG_ON(irq != bdata->irq);
474d8ee4a1cSLaxman Dewangan 
475d8ee4a1cSLaxman Dewangan 	spin_lock_irqsave(&bdata->lock, flags);
476d8ee4a1cSLaxman Dewangan 
477d8ee4a1cSLaxman Dewangan 	if (!bdata->key_pressed) {
4782fba26c6SNeilBrown 		if (bdata->button->wakeup)
4792fba26c6SNeilBrown 			pm_wakeup_event(bdata->input->dev.parent, 0);
4802fba26c6SNeilBrown 
4813615536cSGeert Uytterhoeven 		input_report_key(input, *bdata->code, 1);
482d8ee4a1cSLaxman Dewangan 		input_sync(input);
483d8ee4a1cSLaxman Dewangan 
4848ed92556SDmitry Torokhov 		if (!bdata->release_delay) {
4853615536cSGeert Uytterhoeven 			input_report_key(input, *bdata->code, 0);
486d8ee4a1cSLaxman Dewangan 			input_sync(input);
487d8ee4a1cSLaxman Dewangan 			goto out;
488d8ee4a1cSLaxman Dewangan 		}
489d8ee4a1cSLaxman Dewangan 
490d8ee4a1cSLaxman Dewangan 		bdata->key_pressed = true;
491d8ee4a1cSLaxman Dewangan 	}
492d8ee4a1cSLaxman Dewangan 
4938ed92556SDmitry Torokhov 	if (bdata->release_delay)
494019002f2SPaul Cercueil 		hrtimer_start(&bdata->release_timer,
495019002f2SPaul Cercueil 			      ms_to_ktime(bdata->release_delay),
496019002f2SPaul Cercueil 			      HRTIMER_MODE_REL_HARD);
497d8ee4a1cSLaxman Dewangan out:
498d8ee4a1cSLaxman Dewangan 	spin_unlock_irqrestore(&bdata->lock, flags);
499d8ee4a1cSLaxman Dewangan 	return IRQ_HANDLED;
500d8ee4a1cSLaxman Dewangan }
501d8ee4a1cSLaxman Dewangan 
gpio_keys_setup_key(struct platform_device * pdev,struct input_dev * input,struct gpio_keys_drvdata * ddata,const struct gpio_keys_button * button,int idx,struct fwnode_handle * child)5025298cc4cSBill Pemberton static int gpio_keys_setup_key(struct platform_device *pdev,
503d9080921SDmitry Torokhov 				struct input_dev *input,
50483e4947aSHans de Goede 				struct gpio_keys_drvdata *ddata,
505700a38b2SDmitry Torokhov 				const struct gpio_keys_button *button,
50683e4947aSHans de Goede 				int idx,
507700a38b2SDmitry Torokhov 				struct fwnode_handle *child)
508bc8f1eafSBen Dooks {
50992a47674SAlexander Stein 	const char *desc = button->desc ? button->desc : "gpio_keys";
5109e3af04fSMika Westerberg 	struct device *dev = &pdev->dev;
51183e4947aSHans de Goede 	struct gpio_button_data *bdata = &ddata->data[idx];
512d8ee4a1cSLaxman Dewangan 	irq_handler_t isr;
5139e3af04fSMika Westerberg 	unsigned long irqflags;
51427245519SAlexander Shiyan 	int irq;
51527245519SAlexander Shiyan 	int error;
516bc8f1eafSBen Dooks 
517d9080921SDmitry Torokhov 	bdata->input = input;
518d9080921SDmitry Torokhov 	bdata->button = button;
519d8ee4a1cSLaxman Dewangan 	spin_lock_init(&bdata->lock);
520d8ee4a1cSLaxman Dewangan 
521700a38b2SDmitry Torokhov 	if (child) {
5222a60f598SDmitry Torokhov 		bdata->gpiod = devm_fwnode_gpiod_get(dev, child,
5232a60f598SDmitry Torokhov 						     NULL, GPIOD_IN, desc);
524700a38b2SDmitry Torokhov 		if (IS_ERR(bdata->gpiod)) {
525700a38b2SDmitry Torokhov 			error = PTR_ERR(bdata->gpiod);
526*904ef2c4SGeert Uytterhoeven 			if (error != -ENOENT)
527*904ef2c4SGeert Uytterhoeven 				return dev_err_probe(dev, error,
528*904ef2c4SGeert Uytterhoeven 						     "failed to get gpio\n");
529*904ef2c4SGeert Uytterhoeven 
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 		}
536700a38b2SDmitry Torokhov 	} else if (gpio_is_valid(button->gpio)) {
5375feeca3cSGeert Uytterhoeven 		/*
5385feeca3cSGeert Uytterhoeven 		 * Legacy GPIO number, so request the GPIO here and
5395feeca3cSGeert Uytterhoeven 		 * convert it to descriptor.
5405feeca3cSGeert Uytterhoeven 		 */
5415feeca3cSGeert Uytterhoeven 		unsigned flags = GPIOF_IN;
542bc8f1eafSBen Dooks 
5435feeca3cSGeert Uytterhoeven 		if (button->active_low)
5445feeca3cSGeert Uytterhoeven 			flags |= GPIOF_ACTIVE_LOW;
5455feeca3cSGeert Uytterhoeven 
546b4e66e7dSGuenter Roeck 		error = devm_gpio_request_one(dev, button->gpio, flags, desc);
547bc8f1eafSBen Dooks 		if (error < 0) {
548d8ee4a1cSLaxman Dewangan 			dev_err(dev, "Failed to request GPIO %d, error %d\n",
549bc8f1eafSBen Dooks 				button->gpio, error);
550d8ee4a1cSLaxman Dewangan 			return error;
551bc8f1eafSBen Dooks 		}
552bc8f1eafSBen Dooks 
5535feeca3cSGeert Uytterhoeven 		bdata->gpiod = gpio_to_desc(button->gpio);
5545feeca3cSGeert Uytterhoeven 		if (!bdata->gpiod)
5555feeca3cSGeert Uytterhoeven 			return -EINVAL;
556700a38b2SDmitry Torokhov 	}
5575feeca3cSGeert Uytterhoeven 
558700a38b2SDmitry Torokhov 	if (bdata->gpiod) {
55983fc580dSJeffy Chen 		bool active_low = gpiod_is_active_low(bdata->gpiod);
56083fc580dSJeffy Chen 
56128ed684fSGrazvydas Ignotas 		if (button->debounce_interval) {
5625feeca3cSGeert Uytterhoeven 			error = gpiod_set_debounce(bdata->gpiod,
56328ed684fSGrazvydas Ignotas 					button->debounce_interval * 1000);
56428ed684fSGrazvydas Ignotas 			/* use timer if gpiolib doesn't provide debounce */
56528ed684fSGrazvydas Ignotas 			if (error < 0)
5668ed92556SDmitry Torokhov 				bdata->software_debounce =
567d8ee4a1cSLaxman Dewangan 						button->debounce_interval;
568c9efb0baSPaul Cercueil 
569c9efb0baSPaul Cercueil 			/*
570c9efb0baSPaul Cercueil 			 * If reading the GPIO won't sleep, we can use a
571c9efb0baSPaul Cercueil 			 * hrtimer instead of a standard timer for the software
572c9efb0baSPaul Cercueil 			 * debounce, to reduce the latency as much as possible.
573c9efb0baSPaul Cercueil 			 */
574c9efb0baSPaul Cercueil 			bdata->debounce_use_hrtimer =
575c9efb0baSPaul Cercueil 					!gpiod_cansleep(bdata->gpiod);
57628ed684fSGrazvydas Ignotas 		}
57728ed684fSGrazvydas Ignotas 
57897d86e07SDmitry Torokhov 		if (button->irq) {
57997d86e07SDmitry Torokhov 			bdata->irq = button->irq;
58097d86e07SDmitry Torokhov 		} else {
5815feeca3cSGeert Uytterhoeven 			irq = gpiod_to_irq(bdata->gpiod);
582bc8f1eafSBen Dooks 			if (irq < 0) {
583bc8f1eafSBen Dooks 				error = irq;
584d8ee4a1cSLaxman Dewangan 				dev_err(dev,
585d8ee4a1cSLaxman Dewangan 					"Unable to get irq number for GPIO %d, error %d\n",
586bc8f1eafSBen Dooks 					button->gpio, error);
58727245519SAlexander Shiyan 				return error;
588d8ee4a1cSLaxman Dewangan 			}
589d8ee4a1cSLaxman Dewangan 			bdata->irq = irq;
59097d86e07SDmitry Torokhov 		}
591d8ee4a1cSLaxman Dewangan 
5928ed92556SDmitry Torokhov 		INIT_DELAYED_WORK(&bdata->work, gpio_keys_gpio_work_func);
593d8ee4a1cSLaxman Dewangan 
594c9efb0baSPaul Cercueil 		hrtimer_init(&bdata->debounce_timer,
595c9efb0baSPaul Cercueil 			     CLOCK_REALTIME, HRTIMER_MODE_REL);
596c9efb0baSPaul Cercueil 		bdata->debounce_timer.function = gpio_keys_debounce_timer;
597c9efb0baSPaul Cercueil 
598d8ee4a1cSLaxman Dewangan 		isr = gpio_keys_gpio_isr;
599d8ee4a1cSLaxman Dewangan 		irqflags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING;
600d8ee4a1cSLaxman Dewangan 
60183fc580dSJeffy Chen 		switch (button->wakeup_event_action) {
60283fc580dSJeffy Chen 		case EV_ACT_ASSERTED:
60383fc580dSJeffy Chen 			bdata->wakeup_trigger_type = active_low ?
60483fc580dSJeffy Chen 				IRQ_TYPE_EDGE_FALLING : IRQ_TYPE_EDGE_RISING;
60583fc580dSJeffy Chen 			break;
60683fc580dSJeffy Chen 		case EV_ACT_DEASSERTED:
60783fc580dSJeffy Chen 			bdata->wakeup_trigger_type = active_low ?
60883fc580dSJeffy Chen 				IRQ_TYPE_EDGE_RISING : IRQ_TYPE_EDGE_FALLING;
60983fc580dSJeffy Chen 			break;
61083fc580dSJeffy Chen 		case EV_ACT_ANY:
61183fc580dSJeffy Chen 		default:
61283fc580dSJeffy Chen 			/*
61383fc580dSJeffy Chen 			 * For other cases, we are OK letting suspend/resume
61483fc580dSJeffy Chen 			 * not reconfigure the trigger type.
61583fc580dSJeffy Chen 			 */
61683fc580dSJeffy Chen 			break;
61783fc580dSJeffy Chen 		}
618d8ee4a1cSLaxman Dewangan 	} else {
619d8ee4a1cSLaxman Dewangan 		if (!button->irq) {
620700a38b2SDmitry Torokhov 			dev_err(dev, "Found button without gpio or irq\n");
621d8ee4a1cSLaxman Dewangan 			return -EINVAL;
622d8ee4a1cSLaxman Dewangan 		}
623700a38b2SDmitry Torokhov 
624d8ee4a1cSLaxman Dewangan 		bdata->irq = button->irq;
625d8ee4a1cSLaxman Dewangan 
626d8ee4a1cSLaxman Dewangan 		if (button->type && button->type != EV_KEY) {
627d8ee4a1cSLaxman Dewangan 			dev_err(dev, "Only EV_KEY allowed for IRQ buttons.\n");
628d8ee4a1cSLaxman Dewangan 			return -EINVAL;
629bc8f1eafSBen Dooks 		}
630bc8f1eafSBen Dooks 
6318ed92556SDmitry Torokhov 		bdata->release_delay = button->debounce_interval;
632019002f2SPaul Cercueil 		hrtimer_init(&bdata->release_timer,
633019002f2SPaul Cercueil 			     CLOCK_REALTIME, HRTIMER_MODE_REL_HARD);
634019002f2SPaul Cercueil 		bdata->release_timer.function = gpio_keys_irq_timer;
635d8ee4a1cSLaxman Dewangan 
636d8ee4a1cSLaxman Dewangan 		isr = gpio_keys_irq_isr;
637d8ee4a1cSLaxman Dewangan 		irqflags = 0;
63883fc580dSJeffy Chen 
63983fc580dSJeffy Chen 		/*
64083fc580dSJeffy Chen 		 * For IRQ buttons, there is no interrupt for release.
64183fc580dSJeffy Chen 		 * So we don't need to reconfigure the trigger type for wakeup.
64283fc580dSJeffy Chen 		 */
643d8ee4a1cSLaxman Dewangan 	}
644d8ee4a1cSLaxman Dewangan 
64583e4947aSHans de Goede 	bdata->code = &ddata->keymap[idx];
64683e4947aSHans de Goede 	*bdata->code = button->code;
64783e4947aSHans de Goede 	input_set_capability(input, button->type ?: EV_KEY, *bdata->code);
648d8ee4a1cSLaxman Dewangan 
6499e3af04fSMika Westerberg 	/*
6508ed92556SDmitry Torokhov 	 * Install custom action to cancel release timer and
65127245519SAlexander Shiyan 	 * workqueue item.
65227245519SAlexander Shiyan 	 */
653b4e66e7dSGuenter Roeck 	error = devm_add_action(dev, gpio_keys_quiesce_key, bdata);
65427245519SAlexander Shiyan 	if (error) {
655b4e66e7dSGuenter Roeck 		dev_err(dev, "failed to register quiesce action, error: %d\n",
65627245519SAlexander Shiyan 			error);
65727245519SAlexander Shiyan 		return error;
65827245519SAlexander Shiyan 	}
65927245519SAlexander Shiyan 
66027245519SAlexander Shiyan 	/*
6619e3af04fSMika Westerberg 	 * If platform has specified that the button can be disabled,
6629e3af04fSMika Westerberg 	 * we don't want it to share the interrupt line.
6639e3af04fSMika Westerberg 	 */
6649e3af04fSMika Westerberg 	if (!button->can_disable)
6659e3af04fSMika Westerberg 		irqflags |= IRQF_SHARED;
6669e3af04fSMika Westerberg 
667b4e66e7dSGuenter Roeck 	error = devm_request_any_context_irq(dev, bdata->irq, isr, irqflags,
668b4e66e7dSGuenter Roeck 					     desc, bdata);
66994a8cab8SPhilippe Langlais 	if (error < 0) {
670bc8f1eafSBen Dooks 		dev_err(dev, "Unable to claim irq %d; error %d\n",
671d8ee4a1cSLaxman Dewangan 			bdata->irq, error);
67227245519SAlexander Shiyan 		return error;
673bc8f1eafSBen Dooks 	}
674bc8f1eafSBen Dooks 
675bc8f1eafSBen Dooks 	return 0;
676bc8f1eafSBen Dooks }
677bc8f1eafSBen Dooks 
gpio_keys_report_state(struct gpio_keys_drvdata * ddata)6785b76d7b4SDmitry Torokhov static void gpio_keys_report_state(struct gpio_keys_drvdata *ddata)
6795b76d7b4SDmitry Torokhov {
6805b76d7b4SDmitry Torokhov 	struct input_dev *input = ddata->input;
6815b76d7b4SDmitry Torokhov 	int i;
6825b76d7b4SDmitry Torokhov 
6835b76d7b4SDmitry Torokhov 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
6845b76d7b4SDmitry Torokhov 		struct gpio_button_data *bdata = &ddata->data[i];
6855feeca3cSGeert Uytterhoeven 		if (bdata->gpiod)
6865b76d7b4SDmitry Torokhov 			gpio_keys_gpio_report_event(bdata);
6875b76d7b4SDmitry Torokhov 	}
6885b76d7b4SDmitry Torokhov 	input_sync(input);
6895b76d7b4SDmitry Torokhov }
6905b76d7b4SDmitry Torokhov 
gpio_keys_open(struct input_dev * input)691173bdd74SShubhrajyoti D static int gpio_keys_open(struct input_dev *input)
692173bdd74SShubhrajyoti D {
693173bdd74SShubhrajyoti D 	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
694219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = ddata->pdata;
6955b76d7b4SDmitry Torokhov 	int error;
696173bdd74SShubhrajyoti D 
6975b76d7b4SDmitry Torokhov 	if (pdata->enable) {
6985b76d7b4SDmitry Torokhov 		error = pdata->enable(input->dev.parent);
6995b76d7b4SDmitry Torokhov 		if (error)
7005b76d7b4SDmitry Torokhov 			return error;
7015b76d7b4SDmitry Torokhov 	}
7025b76d7b4SDmitry Torokhov 
7035b76d7b4SDmitry Torokhov 	/* Report current state of buttons that are connected to GPIOs */
7045b76d7b4SDmitry Torokhov 	gpio_keys_report_state(ddata);
7055b76d7b4SDmitry Torokhov 
7065b76d7b4SDmitry Torokhov 	return 0;
707173bdd74SShubhrajyoti D }
708173bdd74SShubhrajyoti D 
gpio_keys_close(struct input_dev * input)709173bdd74SShubhrajyoti D static void gpio_keys_close(struct input_dev *input)
710173bdd74SShubhrajyoti D {
711173bdd74SShubhrajyoti D 	struct gpio_keys_drvdata *ddata = input_get_drvdata(input);
712219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = ddata->pdata;
713173bdd74SShubhrajyoti D 
714219edc71SAlexandre Pereira da Silva 	if (pdata->disable)
715219edc71SAlexandre Pereira da Silva 		pdata->disable(input->dev.parent);
716173bdd74SShubhrajyoti D }
717173bdd74SShubhrajyoti D 
718fd05d089SDavid Jander /*
719fd05d089SDavid Jander  * Handlers for alternative sources of platform_data
720fd05d089SDavid Jander  */
721219edc71SAlexandre Pereira da Silva 
722fd05d089SDavid Jander /*
723700a38b2SDmitry Torokhov  * Translate properties into platform_data
724fd05d089SDavid Jander  */
7255298cc4cSBill Pemberton static struct gpio_keys_platform_data *
gpio_keys_get_devtree_pdata(struct device * dev)726219edc71SAlexandre Pereira da Silva gpio_keys_get_devtree_pdata(struct device *dev)
727fd05d089SDavid Jander {
728219edc71SAlexandre Pereira da Silva 	struct gpio_keys_platform_data *pdata;
729219edc71SAlexandre Pereira da Silva 	struct gpio_keys_button *button;
730700a38b2SDmitry Torokhov 	struct fwnode_handle *child;
731219edc71SAlexandre Pereira da Silva 	int nbuttons;
732fd05d089SDavid Jander 
733700a38b2SDmitry Torokhov 	nbuttons = device_get_child_node_count(dev);
7345d422f2eSAndy Shevchenko 	if (nbuttons == 0)
7355d422f2eSAndy Shevchenko 		return ERR_PTR(-ENODEV);
736219edc71SAlexandre Pereira da Silva 
7375d422f2eSAndy Shevchenko 	pdata = devm_kzalloc(dev,
7385d422f2eSAndy Shevchenko 			     sizeof(*pdata) + nbuttons * sizeof(*button),
739219edc71SAlexandre Pereira da Silva 			     GFP_KERNEL);
7405d422f2eSAndy Shevchenko 	if (!pdata)
7415d422f2eSAndy Shevchenko 		return ERR_PTR(-ENOMEM);
742219edc71SAlexandre Pereira da Silva 
7430f78ba96SDmitry Torokhov 	button = (struct gpio_keys_button *)(pdata + 1);
7440f78ba96SDmitry Torokhov 
7450f78ba96SDmitry Torokhov 	pdata->buttons = button;
746219edc71SAlexandre Pereira da Silva 	pdata->nbuttons = nbuttons;
747fd05d089SDavid Jander 
748700a38b2SDmitry Torokhov 	pdata->rep = device_property_read_bool(dev, "autorepeat");
749fd05d089SDavid Jander 
750700a38b2SDmitry Torokhov 	device_property_read_string(dev, "label", &pdata->name);
751c4dc5f8cSLaxman Dewangan 
752700a38b2SDmitry Torokhov 	device_for_each_child_node(dev, child) {
753700a38b2SDmitry Torokhov 		if (is_of_node(child))
754700a38b2SDmitry Torokhov 			button->irq =
755700a38b2SDmitry Torokhov 				irq_of_parse_and_map(to_of_node(child), 0);
756fd05d089SDavid Jander 
757700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "linux,code",
758700a38b2SDmitry Torokhov 					     &button->code)) {
759700a38b2SDmitry Torokhov 			dev_err(dev, "Button without keycode\n");
760700a38b2SDmitry Torokhov 			fwnode_handle_put(child);
76197d86e07SDmitry Torokhov 			return ERR_PTR(-EINVAL);
76297d86e07SDmitry Torokhov 		}
763fd05d089SDavid Jander 
764700a38b2SDmitry Torokhov 		fwnode_property_read_string(child, "label", &button->desc);
765fd05d089SDavid Jander 
766700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "linux,input-type",
767700a38b2SDmitry Torokhov 					     &button->type))
768219edc71SAlexandre Pereira da Silva 			button->type = EV_KEY;
769fd05d089SDavid Jander 
7704b665e17SGergo Koteles 		fwnode_property_read_u32(child, "linux,input-value",
7714b665e17SGergo Koteles 					 (u32 *)&button->value);
7724b665e17SGergo Koteles 
773700a38b2SDmitry Torokhov 		button->wakeup =
774700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "wakeup-source") ||
77599b4ffbdSDmitry Torokhov 			/* legacy name */
776700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "gpio-key,wakeup");
777219edc71SAlexandre Pereira da Silva 
77883fc580dSJeffy Chen 		fwnode_property_read_u32(child, "wakeup-event-action",
77983fc580dSJeffy Chen 					 &button->wakeup_event_action);
78083fc580dSJeffy Chen 
781700a38b2SDmitry Torokhov 		button->can_disable =
782700a38b2SDmitry Torokhov 			fwnode_property_read_bool(child, "linux,can-disable");
78397d86e07SDmitry Torokhov 
784700a38b2SDmitry Torokhov 		if (fwnode_property_read_u32(child, "debounce-interval",
785219edc71SAlexandre Pereira da Silva 					 &button->debounce_interval))
786219edc71SAlexandre Pereira da Silva 			button->debounce_interval = 5;
7870f78ba96SDmitry Torokhov 
7880f78ba96SDmitry Torokhov 		button++;
789219edc71SAlexandre Pereira da Silva 	}
790219edc71SAlexandre Pereira da Silva 
791219edc71SAlexandre Pereira da Silva 	return pdata;
792fd05d089SDavid Jander }
793fd05d089SDavid Jander 
79422daae31SJingoo Han static const struct of_device_id gpio_keys_of_match[] = {
795fd05d089SDavid Jander 	{ .compatible = "gpio-keys", },
796fd05d089SDavid Jander 	{ },
797fd05d089SDavid Jander };
798fd05d089SDavid Jander MODULE_DEVICE_TABLE(of, gpio_keys_of_match);
799fd05d089SDavid Jander 
gpio_keys_probe(struct platform_device * pdev)8005298cc4cSBill Pemberton static int gpio_keys_probe(struct platform_device *pdev)
80178a56aabSPhil Blundell {
802db19fd8bSBen Dooks 	struct device *dev = &pdev->dev;
803219edc71SAlexandre Pereira da Silva 	const struct gpio_keys_platform_data *pdata = dev_get_platdata(dev);
804700a38b2SDmitry Torokhov 	struct fwnode_handle *child = NULL;
805219edc71SAlexandre Pereira da Silva 	struct gpio_keys_drvdata *ddata;
80678a56aabSPhil Blundell 	struct input_dev *input;
80778a56aabSPhil Blundell 	int i, error;
808e15b0213SAnti Sullin 	int wakeup = 0;
80978a56aabSPhil Blundell 
810fd05d089SDavid Jander 	if (!pdata) {
811219edc71SAlexandre Pereira da Silva 		pdata = gpio_keys_get_devtree_pdata(dev);
812219edc71SAlexandre Pereira da Silva 		if (IS_ERR(pdata))
813219edc71SAlexandre Pereira da Silva 			return PTR_ERR(pdata);
814fd05d089SDavid Jander 	}
815fd05d089SDavid Jander 
816002cdb95SGustavo A. R. Silva 	ddata = devm_kzalloc(dev, struct_size(ddata, data, pdata->nbuttons),
817002cdb95SGustavo A. R. Silva 			     GFP_KERNEL);
8185d422f2eSAndy Shevchenko 	if (!ddata) {
819db19fd8bSBen Dooks 		dev_err(dev, "failed to allocate state\n");
8205d422f2eSAndy Shevchenko 		return -ENOMEM;
8215d422f2eSAndy Shevchenko 	}
8225d422f2eSAndy Shevchenko 
82383e4947aSHans de Goede 	ddata->keymap = devm_kcalloc(dev,
82483e4947aSHans de Goede 				     pdata->nbuttons, sizeof(ddata->keymap[0]),
82583e4947aSHans de Goede 				     GFP_KERNEL);
82683e4947aSHans de Goede 	if (!ddata->keymap)
82783e4947aSHans de Goede 		return -ENOMEM;
82883e4947aSHans de Goede 
8295d422f2eSAndy Shevchenko 	input = devm_input_allocate_device(dev);
8305d422f2eSAndy Shevchenko 	if (!input) {
8315d422f2eSAndy Shevchenko 		dev_err(dev, "failed to allocate input device\n");
8325d422f2eSAndy Shevchenko 		return -ENOMEM;
833a33466e3SDmitry Baryshkov 	}
83478a56aabSPhil Blundell 
835219edc71SAlexandre Pereira da Silva 	ddata->pdata = pdata;
8369e3af04fSMika Westerberg 	ddata->input = input;
8379e3af04fSMika Westerberg 	mutex_init(&ddata->disable_lock);
8389e3af04fSMika Westerberg 
839a33466e3SDmitry Baryshkov 	platform_set_drvdata(pdev, ddata);
840173bdd74SShubhrajyoti D 	input_set_drvdata(input, ddata);
84178a56aabSPhil Blundell 
84246711277SAlexander Stein 	input->name = pdata->name ? : pdev->name;
84378a56aabSPhil Blundell 	input->phys = "gpio-keys/input0";
844b4e66e7dSGuenter Roeck 	input->dev.parent = dev;
845173bdd74SShubhrajyoti D 	input->open = gpio_keys_open;
846173bdd74SShubhrajyoti D 	input->close = gpio_keys_close;
84778a56aabSPhil Blundell 
84878a56aabSPhil Blundell 	input->id.bustype = BUS_HOST;
84978a56aabSPhil Blundell 	input->id.vendor = 0x0001;
85078a56aabSPhil Blundell 	input->id.product = 0x0001;
85178a56aabSPhil Blundell 	input->id.version = 0x0100;
85278a56aabSPhil Blundell 
85383e4947aSHans de Goede 	input->keycode = ddata->keymap;
85483e4947aSHans de Goede 	input->keycodesize = sizeof(ddata->keymap[0]);
85583e4947aSHans de Goede 	input->keycodemax = pdata->nbuttons;
85683e4947aSHans de Goede 
857b67b4b11SDominic Curran 	/* Enable auto repeat feature of Linux input subsystem */
858b67b4b11SDominic Curran 	if (pdata->rep)
859b67b4b11SDominic Curran 		__set_bit(EV_REP, input->evbit);
860b67b4b11SDominic Curran 
86178a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
862d9080921SDmitry Torokhov 		const struct gpio_keys_button *button = &pdata->buttons[i];
86378a56aabSPhil Blundell 
864700a38b2SDmitry Torokhov 		if (!dev_get_platdata(dev)) {
865b4e66e7dSGuenter Roeck 			child = device_get_next_child_node(dev, child);
866700a38b2SDmitry Torokhov 			if (!child) {
867b4e66e7dSGuenter Roeck 				dev_err(dev,
868700a38b2SDmitry Torokhov 					"missing child device node for entry %d\n",
869700a38b2SDmitry Torokhov 					i);
870700a38b2SDmitry Torokhov 				return -EINVAL;
871700a38b2SDmitry Torokhov 			}
872700a38b2SDmitry Torokhov 		}
873700a38b2SDmitry Torokhov 
87483e4947aSHans de Goede 		error = gpio_keys_setup_key(pdev, input, ddata,
87583e4947aSHans de Goede 					    button, i, child);
876700a38b2SDmitry Torokhov 		if (error) {
877700a38b2SDmitry Torokhov 			fwnode_handle_put(child);
87827245519SAlexander Shiyan 			return error;
879700a38b2SDmitry Torokhov 		}
88084767d00SRoman Moravcik 
881e15b0213SAnti Sullin 		if (button->wakeup)
882e15b0213SAnti Sullin 			wakeup = 1;
88378a56aabSPhil Blundell 	}
88478a56aabSPhil Blundell 
885700a38b2SDmitry Torokhov 	fwnode_handle_put(child);
886700a38b2SDmitry Torokhov 
88778a56aabSPhil Blundell 	error = input_register_device(input);
88878a56aabSPhil Blundell 	if (error) {
8899e3af04fSMika Westerberg 		dev_err(dev, "Unable to register input device, error: %d\n",
8909e3af04fSMika Westerberg 			error);
89178a56aabSPhil Blundell 		return error;
89278a56aabSPhil Blundell 	}
89378a56aabSPhil Blundell 
8943184125eSDmitry Torokhov 	device_init_wakeup(dev, wakeup);
8959e3af04fSMika Westerberg 
89678a56aabSPhil Blundell 	return 0;
89778a56aabSPhil Blundell }
89878a56aabSPhil Blundell 
89983fc580dSJeffy Chen static int __maybe_unused
gpio_keys_button_enable_wakeup(struct gpio_button_data * bdata)90083fc580dSJeffy Chen gpio_keys_button_enable_wakeup(struct gpio_button_data *bdata)
90183fc580dSJeffy Chen {
90283fc580dSJeffy Chen 	int error;
90383fc580dSJeffy Chen 
90483fc580dSJeffy Chen 	error = enable_irq_wake(bdata->irq);
90583fc580dSJeffy Chen 	if (error) {
90683fc580dSJeffy Chen 		dev_err(bdata->input->dev.parent,
90783fc580dSJeffy Chen 			"failed to configure IRQ %d as wakeup source: %d\n",
90883fc580dSJeffy Chen 			bdata->irq, error);
90983fc580dSJeffy Chen 		return error;
91083fc580dSJeffy Chen 	}
91183fc580dSJeffy Chen 
91283fc580dSJeffy Chen 	if (bdata->wakeup_trigger_type) {
91383fc580dSJeffy Chen 		error = irq_set_irq_type(bdata->irq,
91483fc580dSJeffy Chen 					 bdata->wakeup_trigger_type);
91583fc580dSJeffy Chen 		if (error) {
91683fc580dSJeffy Chen 			dev_err(bdata->input->dev.parent,
91783fc580dSJeffy Chen 				"failed to set wakeup trigger %08x for IRQ %d: %d\n",
91883fc580dSJeffy Chen 				bdata->wakeup_trigger_type, bdata->irq, error);
91983fc580dSJeffy Chen 			disable_irq_wake(bdata->irq);
92083fc580dSJeffy Chen 			return error;
92183fc580dSJeffy Chen 		}
92283fc580dSJeffy Chen 	}
92383fc580dSJeffy Chen 
92483fc580dSJeffy Chen 	return 0;
92583fc580dSJeffy Chen }
92683fc580dSJeffy Chen 
92783fc580dSJeffy Chen static void __maybe_unused
gpio_keys_button_disable_wakeup(struct gpio_button_data * bdata)92883fc580dSJeffy Chen gpio_keys_button_disable_wakeup(struct gpio_button_data *bdata)
92983fc580dSJeffy Chen {
93083fc580dSJeffy Chen 	int error;
93183fc580dSJeffy Chen 
93283fc580dSJeffy Chen 	/*
93383fc580dSJeffy Chen 	 * The trigger type is always both edges for gpio-based keys and we do
93483fc580dSJeffy Chen 	 * not support changing wakeup trigger for interrupt-based keys.
93583fc580dSJeffy Chen 	 */
93683fc580dSJeffy Chen 	if (bdata->wakeup_trigger_type) {
93783fc580dSJeffy Chen 		error = irq_set_irq_type(bdata->irq, IRQ_TYPE_EDGE_BOTH);
93883fc580dSJeffy Chen 		if (error)
93983fc580dSJeffy Chen 			dev_warn(bdata->input->dev.parent,
94083fc580dSJeffy Chen 				 "failed to restore interrupt trigger for IRQ %d: %d\n",
94183fc580dSJeffy Chen 				 bdata->irq, error);
94283fc580dSJeffy Chen 	}
94383fc580dSJeffy Chen 
94483fc580dSJeffy Chen 	error = disable_irq_wake(bdata->irq);
94583fc580dSJeffy Chen 	if (error)
94683fc580dSJeffy Chen 		dev_warn(bdata->input->dev.parent,
94783fc580dSJeffy Chen 			 "failed to disable IRQ %d as wake source: %d\n",
94883fc580dSJeffy Chen 			 bdata->irq, error);
94983fc580dSJeffy Chen }
95083fc580dSJeffy Chen 
95183fc580dSJeffy Chen static int __maybe_unused
gpio_keys_enable_wakeup(struct gpio_keys_drvdata * ddata)95283fc580dSJeffy Chen gpio_keys_enable_wakeup(struct gpio_keys_drvdata *ddata)
95383fc580dSJeffy Chen {
95483fc580dSJeffy Chen 	struct gpio_button_data *bdata;
95583fc580dSJeffy Chen 	int error;
95683fc580dSJeffy Chen 	int i;
95783fc580dSJeffy Chen 
95883fc580dSJeffy Chen 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
95983fc580dSJeffy Chen 		bdata = &ddata->data[i];
96083fc580dSJeffy Chen 		if (bdata->button->wakeup) {
96183fc580dSJeffy Chen 			error = gpio_keys_button_enable_wakeup(bdata);
96283fc580dSJeffy Chen 			if (error)
96383fc580dSJeffy Chen 				goto err_out;
96483fc580dSJeffy Chen 		}
96583fc580dSJeffy Chen 		bdata->suspended = true;
96683fc580dSJeffy Chen 	}
96783fc580dSJeffy Chen 
96883fc580dSJeffy Chen 	return 0;
96983fc580dSJeffy Chen 
97083fc580dSJeffy Chen err_out:
97183fc580dSJeffy Chen 	while (i--) {
97283fc580dSJeffy Chen 		bdata = &ddata->data[i];
97383fc580dSJeffy Chen 		if (bdata->button->wakeup)
97483fc580dSJeffy Chen 			gpio_keys_button_disable_wakeup(bdata);
97583fc580dSJeffy Chen 		bdata->suspended = false;
97683fc580dSJeffy Chen 	}
97783fc580dSJeffy Chen 
97883fc580dSJeffy Chen 	return error;
97983fc580dSJeffy Chen }
98083fc580dSJeffy Chen 
98183fc580dSJeffy Chen static void __maybe_unused
gpio_keys_disable_wakeup(struct gpio_keys_drvdata * ddata)98283fc580dSJeffy Chen gpio_keys_disable_wakeup(struct gpio_keys_drvdata *ddata)
98383fc580dSJeffy Chen {
98483fc580dSJeffy Chen 	struct gpio_button_data *bdata;
98583fc580dSJeffy Chen 	int i;
98683fc580dSJeffy Chen 
98783fc580dSJeffy Chen 	for (i = 0; i < ddata->pdata->nbuttons; i++) {
98883fc580dSJeffy Chen 		bdata = &ddata->data[i];
98983fc580dSJeffy Chen 		bdata->suspended = false;
99083fc580dSJeffy Chen 		if (irqd_is_wakeup_set(irq_get_irq_data(bdata->irq)))
99183fc580dSJeffy Chen 			gpio_keys_button_disable_wakeup(bdata);
99283fc580dSJeffy Chen 	}
99383fc580dSJeffy Chen }
99483fc580dSJeffy Chen 
gpio_keys_suspend(struct device * dev)995ba4de5d9SJonathan Cameron static int gpio_keys_suspend(struct device *dev)
996e15b0213SAnti Sullin {
997fd05d089SDavid Jander 	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
998dda19a92SJonas Aaberg 	struct input_dev *input = ddata->input;
99983fc580dSJeffy Chen 	int error;
1000e15b0213SAnti Sullin 
1001fd05d089SDavid Jander 	if (device_may_wakeup(dev)) {
100283fc580dSJeffy Chen 		error = gpio_keys_enable_wakeup(ddata);
100383fc580dSJeffy Chen 		if (error)
100483fc580dSJeffy Chen 			return error;
1005dda19a92SJonas Aaberg 	} else {
1006dda19a92SJonas Aaberg 		mutex_lock(&input->mutex);
1007d69f0a43SAndrzej Pietrasiewicz 		if (input_device_enabled(input))
1008dda19a92SJonas Aaberg 			gpio_keys_close(input);
1009dda19a92SJonas Aaberg 		mutex_unlock(&input->mutex);
1010e15b0213SAnti Sullin 	}
1011e15b0213SAnti Sullin 
1012e15b0213SAnti Sullin 	return 0;
1013e15b0213SAnti Sullin }
1014e15b0213SAnti Sullin 
gpio_keys_resume(struct device * dev)1015ba4de5d9SJonathan Cameron static int gpio_keys_resume(struct device *dev)
1016e15b0213SAnti Sullin {
1017fd05d089SDavid Jander 	struct gpio_keys_drvdata *ddata = dev_get_drvdata(dev);
1018dda19a92SJonas Aaberg 	struct input_dev *input = ddata->input;
1019dda19a92SJonas Aaberg 	int error = 0;
1020e15b0213SAnti Sullin 
1021dda19a92SJonas Aaberg 	if (device_may_wakeup(dev)) {
102283fc580dSJeffy Chen 		gpio_keys_disable_wakeup(ddata);
1023dda19a92SJonas Aaberg 	} else {
1024dda19a92SJonas Aaberg 		mutex_lock(&input->mutex);
1025d69f0a43SAndrzej Pietrasiewicz 		if (input_device_enabled(input))
1026dda19a92SJonas Aaberg 			error = gpio_keys_open(input);
1027dda19a92SJonas Aaberg 		mutex_unlock(&input->mutex);
1028dda19a92SJonas Aaberg 	}
1029dda19a92SJonas Aaberg 
1030dda19a92SJonas Aaberg 	if (error)
1031dda19a92SJonas Aaberg 		return error;
10325b76d7b4SDmitry Torokhov 
10335b76d7b4SDmitry Torokhov 	gpio_keys_report_state(ddata);
1034e15b0213SAnti Sullin 	return 0;
1035e15b0213SAnti Sullin }
1036e15b0213SAnti Sullin 
1037ba4de5d9SJonathan Cameron static DEFINE_SIMPLE_DEV_PM_OPS(gpio_keys_pm_ops, gpio_keys_suspend, gpio_keys_resume);
1038bdda8216SDmitry Torokhov 
gpio_keys_shutdown(struct platform_device * pdev)1039a5c5e50cSFlorian Fainelli static void gpio_keys_shutdown(struct platform_device *pdev)
1040a5c5e50cSFlorian Fainelli {
1041a5c5e50cSFlorian Fainelli 	int ret;
1042a5c5e50cSFlorian Fainelli 
1043a5c5e50cSFlorian Fainelli 	ret = gpio_keys_suspend(&pdev->dev);
1044a5c5e50cSFlorian Fainelli 	if (ret)
1045a5c5e50cSFlorian Fainelli 		dev_err(&pdev->dev, "failed to shutdown\n");
1046a5c5e50cSFlorian Fainelli }
1047a5c5e50cSFlorian Fainelli 
10489b07044cSUwe Kleine-König static struct platform_driver gpio_keys_device_driver = {
104978a56aabSPhil Blundell 	.probe		= gpio_keys_probe,
1050a5c5e50cSFlorian Fainelli 	.shutdown	= gpio_keys_shutdown,
105178a56aabSPhil Blundell 	.driver		= {
105278a56aabSPhil Blundell 		.name	= "gpio-keys",
1053ba4de5d9SJonathan Cameron 		.pm	= pm_sleep_ptr(&gpio_keys_pm_ops),
1054700a38b2SDmitry Torokhov 		.of_match_table = gpio_keys_of_match,
1055cf1b2a20SGreg Kroah-Hartman 		.dev_groups	= gpio_keys_groups,
105678a56aabSPhil Blundell 	}
105778a56aabSPhil Blundell };
105878a56aabSPhil Blundell 
gpio_keys_init(void)105978a56aabSPhil Blundell static int __init gpio_keys_init(void)
106078a56aabSPhil Blundell {
106178a56aabSPhil Blundell 	return platform_driver_register(&gpio_keys_device_driver);
106278a56aabSPhil Blundell }
106378a56aabSPhil Blundell 
gpio_keys_exit(void)106478a56aabSPhil Blundell static void __exit gpio_keys_exit(void)
106578a56aabSPhil Blundell {
106678a56aabSPhil Blundell 	platform_driver_unregister(&gpio_keys_device_driver);
106778a56aabSPhil Blundell }
106878a56aabSPhil Blundell 
1069b2330205SDavid Jander late_initcall(gpio_keys_init);
107078a56aabSPhil Blundell module_exit(gpio_keys_exit);
107178a56aabSPhil Blundell 
107278a56aabSPhil Blundell MODULE_LICENSE("GPL");
107378a56aabSPhil Blundell MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
10747e2ecdf4SDavid Jander MODULE_DESCRIPTION("Keyboard driver for GPIOs");
1075d7b5247bSKay Sievers MODULE_ALIAS("platform:gpio-keys");
1076