18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OMAP4 Keypad Driver
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments
68c2ecf20Sopenharmony_ci *
78c2ecf20Sopenharmony_ci * Author: Abraham Arce <x0066660@ti.com>
88c2ecf20Sopenharmony_ci * Initial Code: Syed Rafiuddin <rafiuddin.syed@ti.com>
98c2ecf20Sopenharmony_ci */
108c2ecf20Sopenharmony_ci
118c2ecf20Sopenharmony_ci#include <linux/module.h>
128c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
138c2ecf20Sopenharmony_ci#include <linux/platform_device.h>
148c2ecf20Sopenharmony_ci#include <linux/errno.h>
158c2ecf20Sopenharmony_ci#include <linux/io.h>
168c2ecf20Sopenharmony_ci#include <linux/of.h>
178c2ecf20Sopenharmony_ci#include <linux/input.h>
188c2ecf20Sopenharmony_ci#include <linux/input/matrix_keypad.h>
198c2ecf20Sopenharmony_ci#include <linux/slab.h>
208c2ecf20Sopenharmony_ci#include <linux/pm_runtime.h>
218c2ecf20Sopenharmony_ci
228c2ecf20Sopenharmony_ci/* OMAP4 registers */
238c2ecf20Sopenharmony_ci#define OMAP4_KBD_REVISION		0x00
248c2ecf20Sopenharmony_ci#define OMAP4_KBD_SYSCONFIG		0x10
258c2ecf20Sopenharmony_ci#define OMAP4_KBD_SYSSTATUS		0x14
268c2ecf20Sopenharmony_ci#define OMAP4_KBD_IRQSTATUS		0x18
278c2ecf20Sopenharmony_ci#define OMAP4_KBD_IRQENABLE		0x1C
288c2ecf20Sopenharmony_ci#define OMAP4_KBD_WAKEUPENABLE		0x20
298c2ecf20Sopenharmony_ci#define OMAP4_KBD_PENDING		0x24
308c2ecf20Sopenharmony_ci#define OMAP4_KBD_CTRL			0x28
318c2ecf20Sopenharmony_ci#define OMAP4_KBD_DEBOUNCINGTIME	0x2C
328c2ecf20Sopenharmony_ci#define OMAP4_KBD_LONGKEYTIME		0x30
338c2ecf20Sopenharmony_ci#define OMAP4_KBD_TIMEOUT		0x34
348c2ecf20Sopenharmony_ci#define OMAP4_KBD_STATEMACHINE		0x38
358c2ecf20Sopenharmony_ci#define OMAP4_KBD_ROWINPUTS		0x3C
368c2ecf20Sopenharmony_ci#define OMAP4_KBD_COLUMNOUTPUTS		0x40
378c2ecf20Sopenharmony_ci#define OMAP4_KBD_FULLCODE31_0		0x44
388c2ecf20Sopenharmony_ci#define OMAP4_KBD_FULLCODE63_32		0x48
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci/* OMAP4 bit definitions */
418c2ecf20Sopenharmony_ci#define OMAP4_DEF_IRQENABLE_EVENTEN	BIT(0)
428c2ecf20Sopenharmony_ci#define OMAP4_DEF_IRQENABLE_LONGKEY	BIT(1)
438c2ecf20Sopenharmony_ci#define OMAP4_DEF_WUP_EVENT_ENA		BIT(0)
448c2ecf20Sopenharmony_ci#define OMAP4_DEF_WUP_LONG_KEY_ENA	BIT(1)
458c2ecf20Sopenharmony_ci#define OMAP4_DEF_CTRL_NOSOFTMODE	BIT(1)
468c2ecf20Sopenharmony_ci#define OMAP4_DEF_CTRL_PTV_SHIFT	2
478c2ecf20Sopenharmony_ci
488c2ecf20Sopenharmony_ci/* OMAP4 values */
498c2ecf20Sopenharmony_ci#define OMAP4_VAL_IRQDISABLE		0x0
508c2ecf20Sopenharmony_ci
518c2ecf20Sopenharmony_ci/*
528c2ecf20Sopenharmony_ci * Errata i689: If a key is released for a time shorter than debounce time,
538c2ecf20Sopenharmony_ci * the keyboard will idle and never detect the key release. The workaround
548c2ecf20Sopenharmony_ci * is to use at least a 12ms debounce time. See omap5432 TRM chapter
558c2ecf20Sopenharmony_ci * "26.4.6.2 Keyboard Controller Timer" for more information.
568c2ecf20Sopenharmony_ci */
578c2ecf20Sopenharmony_ci#define OMAP4_KEYPAD_PTV_DIV_128        0x6
588c2ecf20Sopenharmony_ci#define OMAP4_KEYPAD_DEBOUNCINGTIME_MS(dbms, ptv)     \
598c2ecf20Sopenharmony_ci	((((dbms) * 1000) / ((1 << ((ptv) + 1)) * (1000000 / 32768))) - 1)
608c2ecf20Sopenharmony_ci#define OMAP4_VAL_DEBOUNCINGTIME_16MS					\
618c2ecf20Sopenharmony_ci	OMAP4_KEYPAD_DEBOUNCINGTIME_MS(16, OMAP4_KEYPAD_PTV_DIV_128)
628c2ecf20Sopenharmony_ci
638c2ecf20Sopenharmony_cienum {
648c2ecf20Sopenharmony_ci	KBD_REVISION_OMAP4 = 0,
658c2ecf20Sopenharmony_ci	KBD_REVISION_OMAP5,
668c2ecf20Sopenharmony_ci};
678c2ecf20Sopenharmony_ci
688c2ecf20Sopenharmony_cistruct omap4_keypad {
698c2ecf20Sopenharmony_ci	struct input_dev *input;
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci	void __iomem *base;
728c2ecf20Sopenharmony_ci	bool irq_wake_enabled;
738c2ecf20Sopenharmony_ci	unsigned int irq;
748c2ecf20Sopenharmony_ci
758c2ecf20Sopenharmony_ci	unsigned int rows;
768c2ecf20Sopenharmony_ci	unsigned int cols;
778c2ecf20Sopenharmony_ci	u32 reg_offset;
788c2ecf20Sopenharmony_ci	u32 irqreg_offset;
798c2ecf20Sopenharmony_ci	unsigned int row_shift;
808c2ecf20Sopenharmony_ci	bool no_autorepeat;
818c2ecf20Sopenharmony_ci	unsigned char key_state[8];
828c2ecf20Sopenharmony_ci	unsigned short *keymap;
838c2ecf20Sopenharmony_ci};
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_cistatic int kbd_readl(struct omap4_keypad *keypad_data, u32 offset)
868c2ecf20Sopenharmony_ci{
878c2ecf20Sopenharmony_ci	return __raw_readl(keypad_data->base +
888c2ecf20Sopenharmony_ci				keypad_data->reg_offset + offset);
898c2ecf20Sopenharmony_ci}
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_cistatic void kbd_writel(struct omap4_keypad *keypad_data, u32 offset, u32 value)
928c2ecf20Sopenharmony_ci{
938c2ecf20Sopenharmony_ci	__raw_writel(value,
948c2ecf20Sopenharmony_ci		     keypad_data->base + keypad_data->reg_offset + offset);
958c2ecf20Sopenharmony_ci}
968c2ecf20Sopenharmony_ci
978c2ecf20Sopenharmony_cistatic int kbd_read_irqreg(struct omap4_keypad *keypad_data, u32 offset)
988c2ecf20Sopenharmony_ci{
998c2ecf20Sopenharmony_ci	return __raw_readl(keypad_data->base +
1008c2ecf20Sopenharmony_ci				keypad_data->irqreg_offset + offset);
1018c2ecf20Sopenharmony_ci}
1028c2ecf20Sopenharmony_ci
1038c2ecf20Sopenharmony_cistatic void kbd_write_irqreg(struct omap4_keypad *keypad_data,
1048c2ecf20Sopenharmony_ci			     u32 offset, u32 value)
1058c2ecf20Sopenharmony_ci{
1068c2ecf20Sopenharmony_ci	__raw_writel(value,
1078c2ecf20Sopenharmony_ci		     keypad_data->base + keypad_data->irqreg_offset + offset);
1088c2ecf20Sopenharmony_ci}
1098c2ecf20Sopenharmony_ci
1108c2ecf20Sopenharmony_ci
1118c2ecf20Sopenharmony_ci/* Interrupt handlers */
1128c2ecf20Sopenharmony_cistatic irqreturn_t omap4_keypad_irq_handler(int irq, void *dev_id)
1138c2ecf20Sopenharmony_ci{
1148c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = dev_id;
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci	if (kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS))
1178c2ecf20Sopenharmony_ci		return IRQ_WAKE_THREAD;
1188c2ecf20Sopenharmony_ci
1198c2ecf20Sopenharmony_ci	return IRQ_NONE;
1208c2ecf20Sopenharmony_ci}
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_cistatic irqreturn_t omap4_keypad_irq_thread_fn(int irq, void *dev_id)
1238c2ecf20Sopenharmony_ci{
1248c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = dev_id;
1258c2ecf20Sopenharmony_ci	struct input_dev *input_dev = keypad_data->input;
1268c2ecf20Sopenharmony_ci	unsigned char key_state[ARRAY_SIZE(keypad_data->key_state)];
1278c2ecf20Sopenharmony_ci	unsigned int col, row, code, changed;
1288c2ecf20Sopenharmony_ci	u32 *new_state = (u32 *) key_state;
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci	*new_state = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE31_0);
1318c2ecf20Sopenharmony_ci	*(new_state + 1) = kbd_readl(keypad_data, OMAP4_KBD_FULLCODE63_32);
1328c2ecf20Sopenharmony_ci
1338c2ecf20Sopenharmony_ci	for (row = 0; row < keypad_data->rows; row++) {
1348c2ecf20Sopenharmony_ci		changed = key_state[row] ^ keypad_data->key_state[row];
1358c2ecf20Sopenharmony_ci		if (!changed)
1368c2ecf20Sopenharmony_ci			continue;
1378c2ecf20Sopenharmony_ci
1388c2ecf20Sopenharmony_ci		for (col = 0; col < keypad_data->cols; col++) {
1398c2ecf20Sopenharmony_ci			if (changed & (1 << col)) {
1408c2ecf20Sopenharmony_ci				code = MATRIX_SCAN_CODE(row, col,
1418c2ecf20Sopenharmony_ci						keypad_data->row_shift);
1428c2ecf20Sopenharmony_ci				input_event(input_dev, EV_MSC, MSC_SCAN, code);
1438c2ecf20Sopenharmony_ci				input_report_key(input_dev,
1448c2ecf20Sopenharmony_ci						 keypad_data->keymap[code],
1458c2ecf20Sopenharmony_ci						 key_state[row] & (1 << col));
1468c2ecf20Sopenharmony_ci			}
1478c2ecf20Sopenharmony_ci		}
1488c2ecf20Sopenharmony_ci	}
1498c2ecf20Sopenharmony_ci
1508c2ecf20Sopenharmony_ci	input_sync(input_dev);
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci	memcpy(keypad_data->key_state, key_state,
1538c2ecf20Sopenharmony_ci		sizeof(keypad_data->key_state));
1548c2ecf20Sopenharmony_ci
1558c2ecf20Sopenharmony_ci	/* clear pending interrupts */
1568c2ecf20Sopenharmony_ci	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
1578c2ecf20Sopenharmony_ci			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
1608c2ecf20Sopenharmony_ci}
1618c2ecf20Sopenharmony_ci
1628c2ecf20Sopenharmony_cistatic int omap4_keypad_open(struct input_dev *input)
1638c2ecf20Sopenharmony_ci{
1648c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = input_get_drvdata(input);
1658c2ecf20Sopenharmony_ci
1668c2ecf20Sopenharmony_ci	pm_runtime_get_sync(input->dev.parent);
1678c2ecf20Sopenharmony_ci
1688c2ecf20Sopenharmony_ci	disable_irq(keypad_data->irq);
1698c2ecf20Sopenharmony_ci
1708c2ecf20Sopenharmony_ci	kbd_writel(keypad_data, OMAP4_KBD_CTRL,
1718c2ecf20Sopenharmony_ci			OMAP4_DEF_CTRL_NOSOFTMODE |
1728c2ecf20Sopenharmony_ci			(OMAP4_KEYPAD_PTV_DIV_128 << OMAP4_DEF_CTRL_PTV_SHIFT));
1738c2ecf20Sopenharmony_ci	kbd_writel(keypad_data, OMAP4_KBD_DEBOUNCINGTIME,
1748c2ecf20Sopenharmony_ci			OMAP4_VAL_DEBOUNCINGTIME_16MS);
1758c2ecf20Sopenharmony_ci	/* clear pending interrupts */
1768c2ecf20Sopenharmony_ci	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
1778c2ecf20Sopenharmony_ci			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
1788c2ecf20Sopenharmony_ci	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
1798c2ecf20Sopenharmony_ci			OMAP4_DEF_IRQENABLE_EVENTEN |
1808c2ecf20Sopenharmony_ci				OMAP4_DEF_IRQENABLE_LONGKEY);
1818c2ecf20Sopenharmony_ci	kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE,
1828c2ecf20Sopenharmony_ci			OMAP4_DEF_WUP_EVENT_ENA | OMAP4_DEF_WUP_LONG_KEY_ENA);
1838c2ecf20Sopenharmony_ci
1848c2ecf20Sopenharmony_ci	enable_irq(keypad_data->irq);
1858c2ecf20Sopenharmony_ci
1868c2ecf20Sopenharmony_ci	return 0;
1878c2ecf20Sopenharmony_ci}
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_cistatic void omap4_keypad_stop(struct omap4_keypad *keypad_data)
1908c2ecf20Sopenharmony_ci{
1918c2ecf20Sopenharmony_ci	/* Disable interrupts and wake-up events */
1928c2ecf20Sopenharmony_ci	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQENABLE,
1938c2ecf20Sopenharmony_ci			 OMAP4_VAL_IRQDISABLE);
1948c2ecf20Sopenharmony_ci	kbd_writel(keypad_data, OMAP4_KBD_WAKEUPENABLE, 0);
1958c2ecf20Sopenharmony_ci
1968c2ecf20Sopenharmony_ci	/* clear pending interrupts */
1978c2ecf20Sopenharmony_ci	kbd_write_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS,
1988c2ecf20Sopenharmony_ci			 kbd_read_irqreg(keypad_data, OMAP4_KBD_IRQSTATUS));
1998c2ecf20Sopenharmony_ci}
2008c2ecf20Sopenharmony_ci
2018c2ecf20Sopenharmony_cistatic void omap4_keypad_close(struct input_dev *input)
2028c2ecf20Sopenharmony_ci{
2038c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci	keypad_data = input_get_drvdata(input);
2068c2ecf20Sopenharmony_ci	disable_irq(keypad_data->irq);
2078c2ecf20Sopenharmony_ci	omap4_keypad_stop(keypad_data);
2088c2ecf20Sopenharmony_ci	enable_irq(keypad_data->irq);
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci	pm_runtime_put_sync(input->dev.parent);
2118c2ecf20Sopenharmony_ci}
2128c2ecf20Sopenharmony_ci
2138c2ecf20Sopenharmony_cistatic int omap4_keypad_parse_dt(struct device *dev,
2148c2ecf20Sopenharmony_ci				 struct omap4_keypad *keypad_data)
2158c2ecf20Sopenharmony_ci{
2168c2ecf20Sopenharmony_ci	struct device_node *np = dev->of_node;
2178c2ecf20Sopenharmony_ci	int err;
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	err = matrix_keypad_parse_properties(dev, &keypad_data->rows,
2208c2ecf20Sopenharmony_ci					     &keypad_data->cols);
2218c2ecf20Sopenharmony_ci	if (err)
2228c2ecf20Sopenharmony_ci		return err;
2238c2ecf20Sopenharmony_ci
2248c2ecf20Sopenharmony_ci	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
2258c2ecf20Sopenharmony_ci		keypad_data->no_autorepeat = true;
2268c2ecf20Sopenharmony_ci
2278c2ecf20Sopenharmony_ci	return 0;
2288c2ecf20Sopenharmony_ci}
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_cistatic int omap4_keypad_check_revision(struct device *dev,
2318c2ecf20Sopenharmony_ci				       struct omap4_keypad *keypad_data)
2328c2ecf20Sopenharmony_ci{
2338c2ecf20Sopenharmony_ci	unsigned int rev;
2348c2ecf20Sopenharmony_ci
2358c2ecf20Sopenharmony_ci	rev = __raw_readl(keypad_data->base + OMAP4_KBD_REVISION);
2368c2ecf20Sopenharmony_ci	rev &= 0x03 << 30;
2378c2ecf20Sopenharmony_ci	rev >>= 30;
2388c2ecf20Sopenharmony_ci	switch (rev) {
2398c2ecf20Sopenharmony_ci	case KBD_REVISION_OMAP4:
2408c2ecf20Sopenharmony_ci		keypad_data->reg_offset = 0x00;
2418c2ecf20Sopenharmony_ci		keypad_data->irqreg_offset = 0x00;
2428c2ecf20Sopenharmony_ci		break;
2438c2ecf20Sopenharmony_ci	case KBD_REVISION_OMAP5:
2448c2ecf20Sopenharmony_ci		keypad_data->reg_offset = 0x10;
2458c2ecf20Sopenharmony_ci		keypad_data->irqreg_offset = 0x0c;
2468c2ecf20Sopenharmony_ci		break;
2478c2ecf20Sopenharmony_ci	default:
2488c2ecf20Sopenharmony_ci		dev_err(dev, "Keypad reports unsupported revision %d", rev);
2498c2ecf20Sopenharmony_ci		return -EINVAL;
2508c2ecf20Sopenharmony_ci	}
2518c2ecf20Sopenharmony_ci
2528c2ecf20Sopenharmony_ci	return 0;
2538c2ecf20Sopenharmony_ci}
2548c2ecf20Sopenharmony_ci
2558c2ecf20Sopenharmony_cistatic int omap4_keypad_probe(struct platform_device *pdev)
2568c2ecf20Sopenharmony_ci{
2578c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data;
2588c2ecf20Sopenharmony_ci	struct input_dev *input_dev;
2598c2ecf20Sopenharmony_ci	struct resource *res;
2608c2ecf20Sopenharmony_ci	unsigned int max_keys;
2618c2ecf20Sopenharmony_ci	int irq;
2628c2ecf20Sopenharmony_ci	int error;
2638c2ecf20Sopenharmony_ci
2648c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
2658c2ecf20Sopenharmony_ci	if (!res) {
2668c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "no base address specified\n");
2678c2ecf20Sopenharmony_ci		return -EINVAL;
2688c2ecf20Sopenharmony_ci	}
2698c2ecf20Sopenharmony_ci
2708c2ecf20Sopenharmony_ci	irq = platform_get_irq(pdev, 0);
2718c2ecf20Sopenharmony_ci	if (irq < 0)
2728c2ecf20Sopenharmony_ci		return irq;
2738c2ecf20Sopenharmony_ci
2748c2ecf20Sopenharmony_ci	keypad_data = kzalloc(sizeof(struct omap4_keypad), GFP_KERNEL);
2758c2ecf20Sopenharmony_ci	if (!keypad_data) {
2768c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "keypad_data memory allocation failed\n");
2778c2ecf20Sopenharmony_ci		return -ENOMEM;
2788c2ecf20Sopenharmony_ci	}
2798c2ecf20Sopenharmony_ci
2808c2ecf20Sopenharmony_ci	keypad_data->irq = irq;
2818c2ecf20Sopenharmony_ci
2828c2ecf20Sopenharmony_ci	error = omap4_keypad_parse_dt(&pdev->dev, keypad_data);
2838c2ecf20Sopenharmony_ci	if (error)
2848c2ecf20Sopenharmony_ci		goto err_free_keypad;
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci	res = request_mem_region(res->start, resource_size(res), pdev->name);
2878c2ecf20Sopenharmony_ci	if (!res) {
2888c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't request mem region\n");
2898c2ecf20Sopenharmony_ci		error = -EBUSY;
2908c2ecf20Sopenharmony_ci		goto err_free_keypad;
2918c2ecf20Sopenharmony_ci	}
2928c2ecf20Sopenharmony_ci
2938c2ecf20Sopenharmony_ci	keypad_data->base = ioremap(res->start, resource_size(res));
2948c2ecf20Sopenharmony_ci	if (!keypad_data->base) {
2958c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "can't ioremap mem resource\n");
2968c2ecf20Sopenharmony_ci		error = -ENOMEM;
2978c2ecf20Sopenharmony_ci		goto err_release_mem;
2988c2ecf20Sopenharmony_ci	}
2998c2ecf20Sopenharmony_ci
3008c2ecf20Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
3018c2ecf20Sopenharmony_ci
3028c2ecf20Sopenharmony_ci	/*
3038c2ecf20Sopenharmony_ci	 * Enable clocks for the keypad module so that we can read
3048c2ecf20Sopenharmony_ci	 * revision register.
3058c2ecf20Sopenharmony_ci	 */
3068c2ecf20Sopenharmony_ci	error = pm_runtime_get_sync(&pdev->dev);
3078c2ecf20Sopenharmony_ci	if (error) {
3088c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
3098c2ecf20Sopenharmony_ci		pm_runtime_put_noidle(&pdev->dev);
3108c2ecf20Sopenharmony_ci	} else {
3118c2ecf20Sopenharmony_ci		error = omap4_keypad_check_revision(&pdev->dev,
3128c2ecf20Sopenharmony_ci						    keypad_data);
3138c2ecf20Sopenharmony_ci		if (!error) {
3148c2ecf20Sopenharmony_ci			/* Ensure device does not raise interrupts */
3158c2ecf20Sopenharmony_ci			omap4_keypad_stop(keypad_data);
3168c2ecf20Sopenharmony_ci		}
3178c2ecf20Sopenharmony_ci		pm_runtime_put_sync(&pdev->dev);
3188c2ecf20Sopenharmony_ci	}
3198c2ecf20Sopenharmony_ci	if (error)
3208c2ecf20Sopenharmony_ci		goto err_pm_disable;
3218c2ecf20Sopenharmony_ci
3228c2ecf20Sopenharmony_ci	/* input device allocation */
3238c2ecf20Sopenharmony_ci	keypad_data->input = input_dev = input_allocate_device();
3248c2ecf20Sopenharmony_ci	if (!input_dev) {
3258c2ecf20Sopenharmony_ci		error = -ENOMEM;
3268c2ecf20Sopenharmony_ci		goto err_pm_disable;
3278c2ecf20Sopenharmony_ci	}
3288c2ecf20Sopenharmony_ci
3298c2ecf20Sopenharmony_ci	input_dev->name = pdev->name;
3308c2ecf20Sopenharmony_ci	input_dev->dev.parent = &pdev->dev;
3318c2ecf20Sopenharmony_ci	input_dev->id.bustype = BUS_HOST;
3328c2ecf20Sopenharmony_ci	input_dev->id.vendor = 0x0001;
3338c2ecf20Sopenharmony_ci	input_dev->id.product = 0x0001;
3348c2ecf20Sopenharmony_ci	input_dev->id.version = 0x0001;
3358c2ecf20Sopenharmony_ci
3368c2ecf20Sopenharmony_ci	input_dev->open = omap4_keypad_open;
3378c2ecf20Sopenharmony_ci	input_dev->close = omap4_keypad_close;
3388c2ecf20Sopenharmony_ci
3398c2ecf20Sopenharmony_ci	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
3408c2ecf20Sopenharmony_ci	if (!keypad_data->no_autorepeat)
3418c2ecf20Sopenharmony_ci		__set_bit(EV_REP, input_dev->evbit);
3428c2ecf20Sopenharmony_ci
3438c2ecf20Sopenharmony_ci	input_set_drvdata(input_dev, keypad_data);
3448c2ecf20Sopenharmony_ci
3458c2ecf20Sopenharmony_ci	keypad_data->row_shift = get_count_order(keypad_data->cols);
3468c2ecf20Sopenharmony_ci	max_keys = keypad_data->rows << keypad_data->row_shift;
3478c2ecf20Sopenharmony_ci	keypad_data->keymap = kcalloc(max_keys,
3488c2ecf20Sopenharmony_ci				      sizeof(keypad_data->keymap[0]),
3498c2ecf20Sopenharmony_ci				      GFP_KERNEL);
3508c2ecf20Sopenharmony_ci	if (!keypad_data->keymap) {
3518c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "Not enough memory for keymap\n");
3528c2ecf20Sopenharmony_ci		error = -ENOMEM;
3538c2ecf20Sopenharmony_ci		goto err_free_input;
3548c2ecf20Sopenharmony_ci	}
3558c2ecf20Sopenharmony_ci
3568c2ecf20Sopenharmony_ci	error = matrix_keypad_build_keymap(NULL, NULL,
3578c2ecf20Sopenharmony_ci					   keypad_data->rows, keypad_data->cols,
3588c2ecf20Sopenharmony_ci					   keypad_data->keymap, input_dev);
3598c2ecf20Sopenharmony_ci	if (error) {
3608c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to build keymap\n");
3618c2ecf20Sopenharmony_ci		goto err_free_keymap;
3628c2ecf20Sopenharmony_ci	}
3638c2ecf20Sopenharmony_ci
3648c2ecf20Sopenharmony_ci	error = request_threaded_irq(keypad_data->irq, omap4_keypad_irq_handler,
3658c2ecf20Sopenharmony_ci				     omap4_keypad_irq_thread_fn, IRQF_ONESHOT,
3668c2ecf20Sopenharmony_ci				     "omap4-keypad", keypad_data);
3678c2ecf20Sopenharmony_ci	if (error) {
3688c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register interrupt\n");
3698c2ecf20Sopenharmony_ci		goto err_free_keymap;
3708c2ecf20Sopenharmony_ci	}
3718c2ecf20Sopenharmony_ci
3728c2ecf20Sopenharmony_ci	error = input_register_device(keypad_data->input);
3738c2ecf20Sopenharmony_ci	if (error < 0) {
3748c2ecf20Sopenharmony_ci		dev_err(&pdev->dev, "failed to register input device\n");
3758c2ecf20Sopenharmony_ci		goto err_free_irq;
3768c2ecf20Sopenharmony_ci	}
3778c2ecf20Sopenharmony_ci
3788c2ecf20Sopenharmony_ci	device_init_wakeup(&pdev->dev, true);
3798c2ecf20Sopenharmony_ci	platform_set_drvdata(pdev, keypad_data);
3808c2ecf20Sopenharmony_ci
3818c2ecf20Sopenharmony_ci	return 0;
3828c2ecf20Sopenharmony_ci
3838c2ecf20Sopenharmony_cierr_free_irq:
3848c2ecf20Sopenharmony_ci	free_irq(keypad_data->irq, keypad_data);
3858c2ecf20Sopenharmony_cierr_free_keymap:
3868c2ecf20Sopenharmony_ci	kfree(keypad_data->keymap);
3878c2ecf20Sopenharmony_cierr_free_input:
3888c2ecf20Sopenharmony_ci	input_free_device(input_dev);
3898c2ecf20Sopenharmony_cierr_pm_disable:
3908c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
3918c2ecf20Sopenharmony_ci	iounmap(keypad_data->base);
3928c2ecf20Sopenharmony_cierr_release_mem:
3938c2ecf20Sopenharmony_ci	release_mem_region(res->start, resource_size(res));
3948c2ecf20Sopenharmony_cierr_free_keypad:
3958c2ecf20Sopenharmony_ci	kfree(keypad_data);
3968c2ecf20Sopenharmony_ci	return error;
3978c2ecf20Sopenharmony_ci}
3988c2ecf20Sopenharmony_ci
3998c2ecf20Sopenharmony_cistatic int omap4_keypad_remove(struct platform_device *pdev)
4008c2ecf20Sopenharmony_ci{
4018c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
4028c2ecf20Sopenharmony_ci	struct resource *res;
4038c2ecf20Sopenharmony_ci
4048c2ecf20Sopenharmony_ci	free_irq(keypad_data->irq, keypad_data);
4058c2ecf20Sopenharmony_ci
4068c2ecf20Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
4078c2ecf20Sopenharmony_ci
4088c2ecf20Sopenharmony_ci	input_unregister_device(keypad_data->input);
4098c2ecf20Sopenharmony_ci
4108c2ecf20Sopenharmony_ci	iounmap(keypad_data->base);
4118c2ecf20Sopenharmony_ci
4128c2ecf20Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
4138c2ecf20Sopenharmony_ci	release_mem_region(res->start, resource_size(res));
4148c2ecf20Sopenharmony_ci
4158c2ecf20Sopenharmony_ci	kfree(keypad_data->keymap);
4168c2ecf20Sopenharmony_ci	kfree(keypad_data);
4178c2ecf20Sopenharmony_ci
4188c2ecf20Sopenharmony_ci	return 0;
4198c2ecf20Sopenharmony_ci}
4208c2ecf20Sopenharmony_ci
4218c2ecf20Sopenharmony_cistatic const struct of_device_id omap_keypad_dt_match[] = {
4228c2ecf20Sopenharmony_ci	{ .compatible = "ti,omap4-keypad" },
4238c2ecf20Sopenharmony_ci	{},
4248c2ecf20Sopenharmony_ci};
4258c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(of, omap_keypad_dt_match);
4268c2ecf20Sopenharmony_ci
4278c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP
4288c2ecf20Sopenharmony_cistatic int omap4_keypad_suspend(struct device *dev)
4298c2ecf20Sopenharmony_ci{
4308c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
4318c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
4328c2ecf20Sopenharmony_ci	int error;
4338c2ecf20Sopenharmony_ci
4348c2ecf20Sopenharmony_ci	if (device_may_wakeup(&pdev->dev)) {
4358c2ecf20Sopenharmony_ci		error = enable_irq_wake(keypad_data->irq);
4368c2ecf20Sopenharmony_ci		if (!error)
4378c2ecf20Sopenharmony_ci			keypad_data->irq_wake_enabled = true;
4388c2ecf20Sopenharmony_ci	}
4398c2ecf20Sopenharmony_ci
4408c2ecf20Sopenharmony_ci	return 0;
4418c2ecf20Sopenharmony_ci}
4428c2ecf20Sopenharmony_ci
4438c2ecf20Sopenharmony_cistatic int omap4_keypad_resume(struct device *dev)
4448c2ecf20Sopenharmony_ci{
4458c2ecf20Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
4468c2ecf20Sopenharmony_ci	struct omap4_keypad *keypad_data = platform_get_drvdata(pdev);
4478c2ecf20Sopenharmony_ci
4488c2ecf20Sopenharmony_ci	if (device_may_wakeup(&pdev->dev) && keypad_data->irq_wake_enabled) {
4498c2ecf20Sopenharmony_ci		disable_irq_wake(keypad_data->irq);
4508c2ecf20Sopenharmony_ci		keypad_data->irq_wake_enabled = false;
4518c2ecf20Sopenharmony_ci	}
4528c2ecf20Sopenharmony_ci
4538c2ecf20Sopenharmony_ci	return 0;
4548c2ecf20Sopenharmony_ci}
4558c2ecf20Sopenharmony_ci#endif
4568c2ecf20Sopenharmony_ci
4578c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(omap4_keypad_pm_ops,
4588c2ecf20Sopenharmony_ci			 omap4_keypad_suspend, omap4_keypad_resume);
4598c2ecf20Sopenharmony_ci
4608c2ecf20Sopenharmony_cistatic struct platform_driver omap4_keypad_driver = {
4618c2ecf20Sopenharmony_ci	.probe		= omap4_keypad_probe,
4628c2ecf20Sopenharmony_ci	.remove		= omap4_keypad_remove,
4638c2ecf20Sopenharmony_ci	.driver		= {
4648c2ecf20Sopenharmony_ci		.name	= "omap4-keypad",
4658c2ecf20Sopenharmony_ci		.pm	= &omap4_keypad_pm_ops,
4668c2ecf20Sopenharmony_ci		.of_match_table = omap_keypad_dt_match,
4678c2ecf20Sopenharmony_ci	},
4688c2ecf20Sopenharmony_ci};
4698c2ecf20Sopenharmony_cimodule_platform_driver(omap4_keypad_driver);
4708c2ecf20Sopenharmony_ci
4718c2ecf20Sopenharmony_ciMODULE_AUTHOR("Texas Instruments");
4728c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("OMAP4 Keypad Driver");
4738c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL");
4748c2ecf20Sopenharmony_ciMODULE_ALIAS("platform:omap4-keypad");
475