162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Samsung keypad driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2010 Samsung Electronics Co.Ltd
662306a36Sopenharmony_ci * Author: Joonyoung Shim <jy0922.shim@samsung.com>
762306a36Sopenharmony_ci * Author: Donghwa Lee <dh09.lee@samsung.com>
862306a36Sopenharmony_ci */
962306a36Sopenharmony_ci
1062306a36Sopenharmony_ci#include <linux/clk.h>
1162306a36Sopenharmony_ci#include <linux/delay.h>
1262306a36Sopenharmony_ci#include <linux/err.h>
1362306a36Sopenharmony_ci#include <linux/input.h>
1462306a36Sopenharmony_ci#include <linux/interrupt.h>
1562306a36Sopenharmony_ci#include <linux/io.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/platform_device.h>
1862306a36Sopenharmony_ci#include <linux/pm.h>
1962306a36Sopenharmony_ci#include <linux/pm_runtime.h>
2062306a36Sopenharmony_ci#include <linux/slab.h>
2162306a36Sopenharmony_ci#include <linux/of.h>
2262306a36Sopenharmony_ci#include <linux/sched.h>
2362306a36Sopenharmony_ci#include <linux/input/samsung-keypad.h>
2462306a36Sopenharmony_ci
2562306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON			0x00
2662306a36Sopenharmony_ci#define SAMSUNG_KEYIFSTSCLR			0x04
2762306a36Sopenharmony_ci#define SAMSUNG_KEYIFCOL			0x08
2862306a36Sopenharmony_ci#define SAMSUNG_KEYIFROW			0x0c
2962306a36Sopenharmony_ci#define SAMSUNG_KEYIFFC				0x10
3062306a36Sopenharmony_ci
3162306a36Sopenharmony_ci/* SAMSUNG_KEYIFCON */
3262306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON_INT_F_EN		(1 << 0)
3362306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON_INT_R_EN		(1 << 1)
3462306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON_DF_EN			(1 << 2)
3562306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON_FC_EN			(1 << 3)
3662306a36Sopenharmony_ci#define SAMSUNG_KEYIFCON_WAKEUPEN		(1 << 4)
3762306a36Sopenharmony_ci
3862306a36Sopenharmony_ci/* SAMSUNG_KEYIFSTSCLR */
3962306a36Sopenharmony_ci#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK		(0xff << 0)
4062306a36Sopenharmony_ci#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK		(0xff << 8)
4162306a36Sopenharmony_ci#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET	8
4262306a36Sopenharmony_ci#define S5PV210_KEYIFSTSCLR_P_INT_MASK		(0x3fff << 0)
4362306a36Sopenharmony_ci#define S5PV210_KEYIFSTSCLR_R_INT_MASK		(0x3fff << 16)
4462306a36Sopenharmony_ci#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET	16
4562306a36Sopenharmony_ci
4662306a36Sopenharmony_ci/* SAMSUNG_KEYIFCOL */
4762306a36Sopenharmony_ci#define SAMSUNG_KEYIFCOL_MASK			(0xff << 0)
4862306a36Sopenharmony_ci#define S5PV210_KEYIFCOLEN_MASK			(0xff << 8)
4962306a36Sopenharmony_ci
5062306a36Sopenharmony_ci/* SAMSUNG_KEYIFROW */
5162306a36Sopenharmony_ci#define SAMSUNG_KEYIFROW_MASK			(0xff << 0)
5262306a36Sopenharmony_ci#define S5PV210_KEYIFROW_MASK			(0x3fff << 0)
5362306a36Sopenharmony_ci
5462306a36Sopenharmony_ci/* SAMSUNG_KEYIFFC */
5562306a36Sopenharmony_ci#define SAMSUNG_KEYIFFC_MASK			(0x3ff << 0)
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_cienum samsung_keypad_type {
5862306a36Sopenharmony_ci	KEYPAD_TYPE_SAMSUNG,
5962306a36Sopenharmony_ci	KEYPAD_TYPE_S5PV210,
6062306a36Sopenharmony_ci};
6162306a36Sopenharmony_ci
6262306a36Sopenharmony_cistruct samsung_keypad {
6362306a36Sopenharmony_ci	struct input_dev *input_dev;
6462306a36Sopenharmony_ci	struct platform_device *pdev;
6562306a36Sopenharmony_ci	struct clk *clk;
6662306a36Sopenharmony_ci	void __iomem *base;
6762306a36Sopenharmony_ci	wait_queue_head_t wait;
6862306a36Sopenharmony_ci	bool stopped;
6962306a36Sopenharmony_ci	bool wake_enabled;
7062306a36Sopenharmony_ci	int irq;
7162306a36Sopenharmony_ci	enum samsung_keypad_type type;
7262306a36Sopenharmony_ci	unsigned int row_shift;
7362306a36Sopenharmony_ci	unsigned int rows;
7462306a36Sopenharmony_ci	unsigned int cols;
7562306a36Sopenharmony_ci	unsigned int row_state[SAMSUNG_MAX_COLS];
7662306a36Sopenharmony_ci	unsigned short keycodes[];
7762306a36Sopenharmony_ci};
7862306a36Sopenharmony_ci
7962306a36Sopenharmony_cistatic void samsung_keypad_scan(struct samsung_keypad *keypad,
8062306a36Sopenharmony_ci				unsigned int *row_state)
8162306a36Sopenharmony_ci{
8262306a36Sopenharmony_ci	unsigned int col;
8362306a36Sopenharmony_ci	unsigned int val;
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	for (col = 0; col < keypad->cols; col++) {
8662306a36Sopenharmony_ci		if (keypad->type == KEYPAD_TYPE_S5PV210) {
8762306a36Sopenharmony_ci			val = S5PV210_KEYIFCOLEN_MASK;
8862306a36Sopenharmony_ci			val &= ~(1 << col) << 8;
8962306a36Sopenharmony_ci		} else {
9062306a36Sopenharmony_ci			val = SAMSUNG_KEYIFCOL_MASK;
9162306a36Sopenharmony_ci			val &= ~(1 << col);
9262306a36Sopenharmony_ci		}
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci		writel(val, keypad->base + SAMSUNG_KEYIFCOL);
9562306a36Sopenharmony_ci		mdelay(1);
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci		val = readl(keypad->base + SAMSUNG_KEYIFROW);
9862306a36Sopenharmony_ci		row_state[col] = ~val & ((1 << keypad->rows) - 1);
9962306a36Sopenharmony_ci	}
10062306a36Sopenharmony_ci
10162306a36Sopenharmony_ci	/* KEYIFCOL reg clear */
10262306a36Sopenharmony_ci	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
10362306a36Sopenharmony_ci}
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_cistatic bool samsung_keypad_report(struct samsung_keypad *keypad,
10662306a36Sopenharmony_ci				  unsigned int *row_state)
10762306a36Sopenharmony_ci{
10862306a36Sopenharmony_ci	struct input_dev *input_dev = keypad->input_dev;
10962306a36Sopenharmony_ci	unsigned int changed;
11062306a36Sopenharmony_ci	unsigned int pressed;
11162306a36Sopenharmony_ci	unsigned int key_down = 0;
11262306a36Sopenharmony_ci	unsigned int val;
11362306a36Sopenharmony_ci	unsigned int col, row;
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	for (col = 0; col < keypad->cols; col++) {
11662306a36Sopenharmony_ci		changed = row_state[col] ^ keypad->row_state[col];
11762306a36Sopenharmony_ci		key_down |= row_state[col];
11862306a36Sopenharmony_ci		if (!changed)
11962306a36Sopenharmony_ci			continue;
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci		for (row = 0; row < keypad->rows; row++) {
12262306a36Sopenharmony_ci			if (!(changed & (1 << row)))
12362306a36Sopenharmony_ci				continue;
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci			pressed = row_state[col] & (1 << row);
12662306a36Sopenharmony_ci
12762306a36Sopenharmony_ci			dev_dbg(&keypad->input_dev->dev,
12862306a36Sopenharmony_ci				"key %s, row: %d, col: %d\n",
12962306a36Sopenharmony_ci				pressed ? "pressed" : "released", row, col);
13062306a36Sopenharmony_ci
13162306a36Sopenharmony_ci			val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
13262306a36Sopenharmony_ci
13362306a36Sopenharmony_ci			input_event(input_dev, EV_MSC, MSC_SCAN, val);
13462306a36Sopenharmony_ci			input_report_key(input_dev,
13562306a36Sopenharmony_ci					keypad->keycodes[val], pressed);
13662306a36Sopenharmony_ci		}
13762306a36Sopenharmony_ci		input_sync(keypad->input_dev);
13862306a36Sopenharmony_ci	}
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
14162306a36Sopenharmony_ci
14262306a36Sopenharmony_ci	return key_down;
14362306a36Sopenharmony_ci}
14462306a36Sopenharmony_ci
14562306a36Sopenharmony_cistatic irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
14662306a36Sopenharmony_ci{
14762306a36Sopenharmony_ci	struct samsung_keypad *keypad = dev_id;
14862306a36Sopenharmony_ci	unsigned int row_state[SAMSUNG_MAX_COLS];
14962306a36Sopenharmony_ci	bool key_down;
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_ci	pm_runtime_get_sync(&keypad->pdev->dev);
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	do {
15462306a36Sopenharmony_ci		readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
15562306a36Sopenharmony_ci		/* Clear interrupt. */
15662306a36Sopenharmony_ci		writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
15762306a36Sopenharmony_ci
15862306a36Sopenharmony_ci		samsung_keypad_scan(keypad, row_state);
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci		key_down = samsung_keypad_report(keypad, row_state);
16162306a36Sopenharmony_ci		if (key_down)
16262306a36Sopenharmony_ci			wait_event_timeout(keypad->wait, keypad->stopped,
16362306a36Sopenharmony_ci					   msecs_to_jiffies(50));
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	} while (key_down && !keypad->stopped);
16662306a36Sopenharmony_ci
16762306a36Sopenharmony_ci	pm_runtime_put(&keypad->pdev->dev);
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	return IRQ_HANDLED;
17062306a36Sopenharmony_ci}
17162306a36Sopenharmony_ci
17262306a36Sopenharmony_cistatic void samsung_keypad_start(struct samsung_keypad *keypad)
17362306a36Sopenharmony_ci{
17462306a36Sopenharmony_ci	unsigned int val;
17562306a36Sopenharmony_ci
17662306a36Sopenharmony_ci	pm_runtime_get_sync(&keypad->pdev->dev);
17762306a36Sopenharmony_ci
17862306a36Sopenharmony_ci	/* Tell IRQ thread that it may poll the device. */
17962306a36Sopenharmony_ci	keypad->stopped = false;
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	clk_enable(keypad->clk);
18262306a36Sopenharmony_ci
18362306a36Sopenharmony_ci	/* Enable interrupt bits. */
18462306a36Sopenharmony_ci	val = readl(keypad->base + SAMSUNG_KEYIFCON);
18562306a36Sopenharmony_ci	val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
18662306a36Sopenharmony_ci	writel(val, keypad->base + SAMSUNG_KEYIFCON);
18762306a36Sopenharmony_ci
18862306a36Sopenharmony_ci	/* KEYIFCOL reg clear. */
18962306a36Sopenharmony_ci	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
19062306a36Sopenharmony_ci
19162306a36Sopenharmony_ci	pm_runtime_put(&keypad->pdev->dev);
19262306a36Sopenharmony_ci}
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_cistatic void samsung_keypad_stop(struct samsung_keypad *keypad)
19562306a36Sopenharmony_ci{
19662306a36Sopenharmony_ci	unsigned int val;
19762306a36Sopenharmony_ci
19862306a36Sopenharmony_ci	pm_runtime_get_sync(&keypad->pdev->dev);
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	/* Signal IRQ thread to stop polling and disable the handler. */
20162306a36Sopenharmony_ci	keypad->stopped = true;
20262306a36Sopenharmony_ci	wake_up(&keypad->wait);
20362306a36Sopenharmony_ci	disable_irq(keypad->irq);
20462306a36Sopenharmony_ci
20562306a36Sopenharmony_ci	/* Clear interrupt. */
20662306a36Sopenharmony_ci	writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	/* Disable interrupt bits. */
20962306a36Sopenharmony_ci	val = readl(keypad->base + SAMSUNG_KEYIFCON);
21062306a36Sopenharmony_ci	val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
21162306a36Sopenharmony_ci	writel(val, keypad->base + SAMSUNG_KEYIFCON);
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_ci	clk_disable(keypad->clk);
21462306a36Sopenharmony_ci
21562306a36Sopenharmony_ci	/*
21662306a36Sopenharmony_ci	 * Now that chip should not generate interrupts we can safely
21762306a36Sopenharmony_ci	 * re-enable the handler.
21862306a36Sopenharmony_ci	 */
21962306a36Sopenharmony_ci	enable_irq(keypad->irq);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	pm_runtime_put(&keypad->pdev->dev);
22262306a36Sopenharmony_ci}
22362306a36Sopenharmony_ci
22462306a36Sopenharmony_cistatic int samsung_keypad_open(struct input_dev *input_dev)
22562306a36Sopenharmony_ci{
22662306a36Sopenharmony_ci	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
22762306a36Sopenharmony_ci
22862306a36Sopenharmony_ci	samsung_keypad_start(keypad);
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_ci	return 0;
23162306a36Sopenharmony_ci}
23262306a36Sopenharmony_ci
23362306a36Sopenharmony_cistatic void samsung_keypad_close(struct input_dev *input_dev)
23462306a36Sopenharmony_ci{
23562306a36Sopenharmony_ci	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	samsung_keypad_stop(keypad);
23862306a36Sopenharmony_ci}
23962306a36Sopenharmony_ci
24062306a36Sopenharmony_ci#ifdef CONFIG_OF
24162306a36Sopenharmony_cistatic struct samsung_keypad_platdata *
24262306a36Sopenharmony_cisamsung_keypad_parse_dt(struct device *dev)
24362306a36Sopenharmony_ci{
24462306a36Sopenharmony_ci	struct samsung_keypad_platdata *pdata;
24562306a36Sopenharmony_ci	struct matrix_keymap_data *keymap_data;
24662306a36Sopenharmony_ci	uint32_t *keymap, num_rows = 0, num_cols = 0;
24762306a36Sopenharmony_ci	struct device_node *np = dev->of_node, *key_np;
24862306a36Sopenharmony_ci	unsigned int key_count;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	if (!np) {
25162306a36Sopenharmony_ci		dev_err(dev, "missing device tree data\n");
25262306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
25362306a36Sopenharmony_ci	}
25462306a36Sopenharmony_ci
25562306a36Sopenharmony_ci	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
25662306a36Sopenharmony_ci	if (!pdata) {
25762306a36Sopenharmony_ci		dev_err(dev, "could not allocate memory for platform data\n");
25862306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
25962306a36Sopenharmony_ci	}
26062306a36Sopenharmony_ci
26162306a36Sopenharmony_ci	of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows);
26262306a36Sopenharmony_ci	of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols);
26362306a36Sopenharmony_ci	if (!num_rows || !num_cols) {
26462306a36Sopenharmony_ci		dev_err(dev, "number of keypad rows/columns not specified\n");
26562306a36Sopenharmony_ci		return ERR_PTR(-EINVAL);
26662306a36Sopenharmony_ci	}
26762306a36Sopenharmony_ci	pdata->rows = num_rows;
26862306a36Sopenharmony_ci	pdata->cols = num_cols;
26962306a36Sopenharmony_ci
27062306a36Sopenharmony_ci	keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL);
27162306a36Sopenharmony_ci	if (!keymap_data) {
27262306a36Sopenharmony_ci		dev_err(dev, "could not allocate memory for keymap data\n");
27362306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
27462306a36Sopenharmony_ci	}
27562306a36Sopenharmony_ci	pdata->keymap_data = keymap_data;
27662306a36Sopenharmony_ci
27762306a36Sopenharmony_ci	key_count = of_get_child_count(np);
27862306a36Sopenharmony_ci	keymap_data->keymap_size = key_count;
27962306a36Sopenharmony_ci	keymap = devm_kcalloc(dev, key_count, sizeof(uint32_t), GFP_KERNEL);
28062306a36Sopenharmony_ci	if (!keymap) {
28162306a36Sopenharmony_ci		dev_err(dev, "could not allocate memory for keymap\n");
28262306a36Sopenharmony_ci		return ERR_PTR(-ENOMEM);
28362306a36Sopenharmony_ci	}
28462306a36Sopenharmony_ci	keymap_data->keymap = keymap;
28562306a36Sopenharmony_ci
28662306a36Sopenharmony_ci	for_each_child_of_node(np, key_np) {
28762306a36Sopenharmony_ci		u32 row, col, key_code;
28862306a36Sopenharmony_ci		of_property_read_u32(key_np, "keypad,row", &row);
28962306a36Sopenharmony_ci		of_property_read_u32(key_np, "keypad,column", &col);
29062306a36Sopenharmony_ci		of_property_read_u32(key_np, "linux,code", &key_code);
29162306a36Sopenharmony_ci		*keymap++ = KEY(row, col, key_code);
29262306a36Sopenharmony_ci	}
29362306a36Sopenharmony_ci
29462306a36Sopenharmony_ci	pdata->no_autorepeat = of_property_read_bool(np, "linux,input-no-autorepeat");
29562306a36Sopenharmony_ci
29662306a36Sopenharmony_ci	pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
29762306a36Sopenharmony_ci			/* legacy name */
29862306a36Sopenharmony_ci			of_property_read_bool(np, "linux,input-wakeup");
29962306a36Sopenharmony_ci
30062306a36Sopenharmony_ci
30162306a36Sopenharmony_ci	return pdata;
30262306a36Sopenharmony_ci}
30362306a36Sopenharmony_ci#else
30462306a36Sopenharmony_cistatic struct samsung_keypad_platdata *
30562306a36Sopenharmony_cisamsung_keypad_parse_dt(struct device *dev)
30662306a36Sopenharmony_ci{
30762306a36Sopenharmony_ci	dev_err(dev, "no platform data defined\n");
30862306a36Sopenharmony_ci
30962306a36Sopenharmony_ci	return ERR_PTR(-EINVAL);
31062306a36Sopenharmony_ci}
31162306a36Sopenharmony_ci#endif
31262306a36Sopenharmony_ci
31362306a36Sopenharmony_cistatic int samsung_keypad_probe(struct platform_device *pdev)
31462306a36Sopenharmony_ci{
31562306a36Sopenharmony_ci	const struct samsung_keypad_platdata *pdata;
31662306a36Sopenharmony_ci	const struct matrix_keymap_data *keymap_data;
31762306a36Sopenharmony_ci	struct samsung_keypad *keypad;
31862306a36Sopenharmony_ci	struct resource *res;
31962306a36Sopenharmony_ci	struct input_dev *input_dev;
32062306a36Sopenharmony_ci	unsigned int row_shift;
32162306a36Sopenharmony_ci	unsigned int keymap_size;
32262306a36Sopenharmony_ci	int error;
32362306a36Sopenharmony_ci
32462306a36Sopenharmony_ci	pdata = dev_get_platdata(&pdev->dev);
32562306a36Sopenharmony_ci	if (!pdata) {
32662306a36Sopenharmony_ci		pdata = samsung_keypad_parse_dt(&pdev->dev);
32762306a36Sopenharmony_ci		if (IS_ERR(pdata))
32862306a36Sopenharmony_ci			return PTR_ERR(pdata);
32962306a36Sopenharmony_ci	}
33062306a36Sopenharmony_ci
33162306a36Sopenharmony_ci	keymap_data = pdata->keymap_data;
33262306a36Sopenharmony_ci	if (!keymap_data) {
33362306a36Sopenharmony_ci		dev_err(&pdev->dev, "no keymap data defined\n");
33462306a36Sopenharmony_ci		return -EINVAL;
33562306a36Sopenharmony_ci	}
33662306a36Sopenharmony_ci
33762306a36Sopenharmony_ci	if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
33862306a36Sopenharmony_ci		return -EINVAL;
33962306a36Sopenharmony_ci
34062306a36Sopenharmony_ci	if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
34162306a36Sopenharmony_ci		return -EINVAL;
34262306a36Sopenharmony_ci
34362306a36Sopenharmony_ci	/* initialize the gpio */
34462306a36Sopenharmony_ci	if (pdata->cfg_gpio)
34562306a36Sopenharmony_ci		pdata->cfg_gpio(pdata->rows, pdata->cols);
34662306a36Sopenharmony_ci
34762306a36Sopenharmony_ci	row_shift = get_count_order(pdata->cols);
34862306a36Sopenharmony_ci	keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
34962306a36Sopenharmony_ci
35062306a36Sopenharmony_ci	keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
35162306a36Sopenharmony_ci			      GFP_KERNEL);
35262306a36Sopenharmony_ci	input_dev = devm_input_allocate_device(&pdev->dev);
35362306a36Sopenharmony_ci	if (!keypad || !input_dev)
35462306a36Sopenharmony_ci		return -ENOMEM;
35562306a36Sopenharmony_ci
35662306a36Sopenharmony_ci	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
35762306a36Sopenharmony_ci	if (!res)
35862306a36Sopenharmony_ci		return -ENODEV;
35962306a36Sopenharmony_ci
36062306a36Sopenharmony_ci	keypad->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
36162306a36Sopenharmony_ci	if (!keypad->base)
36262306a36Sopenharmony_ci		return -EBUSY;
36362306a36Sopenharmony_ci
36462306a36Sopenharmony_ci	keypad->clk = devm_clk_get(&pdev->dev, "keypad");
36562306a36Sopenharmony_ci	if (IS_ERR(keypad->clk)) {
36662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to get keypad clk\n");
36762306a36Sopenharmony_ci		return PTR_ERR(keypad->clk);
36862306a36Sopenharmony_ci	}
36962306a36Sopenharmony_ci
37062306a36Sopenharmony_ci	error = clk_prepare(keypad->clk);
37162306a36Sopenharmony_ci	if (error) {
37262306a36Sopenharmony_ci		dev_err(&pdev->dev, "keypad clock prepare failed\n");
37362306a36Sopenharmony_ci		return error;
37462306a36Sopenharmony_ci	}
37562306a36Sopenharmony_ci
37662306a36Sopenharmony_ci	keypad->input_dev = input_dev;
37762306a36Sopenharmony_ci	keypad->pdev = pdev;
37862306a36Sopenharmony_ci	keypad->row_shift = row_shift;
37962306a36Sopenharmony_ci	keypad->rows = pdata->rows;
38062306a36Sopenharmony_ci	keypad->cols = pdata->cols;
38162306a36Sopenharmony_ci	keypad->stopped = true;
38262306a36Sopenharmony_ci	init_waitqueue_head(&keypad->wait);
38362306a36Sopenharmony_ci
38462306a36Sopenharmony_ci	if (pdev->dev.of_node)
38562306a36Sopenharmony_ci		keypad->type = of_device_is_compatible(pdev->dev.of_node,
38662306a36Sopenharmony_ci					"samsung,s5pv210-keypad");
38762306a36Sopenharmony_ci	else
38862306a36Sopenharmony_ci		keypad->type = platform_get_device_id(pdev)->driver_data;
38962306a36Sopenharmony_ci
39062306a36Sopenharmony_ci	input_dev->name = pdev->name;
39162306a36Sopenharmony_ci	input_dev->id.bustype = BUS_HOST;
39262306a36Sopenharmony_ci	input_dev->dev.parent = &pdev->dev;
39362306a36Sopenharmony_ci
39462306a36Sopenharmony_ci	input_dev->open = samsung_keypad_open;
39562306a36Sopenharmony_ci	input_dev->close = samsung_keypad_close;
39662306a36Sopenharmony_ci
39762306a36Sopenharmony_ci	error = matrix_keypad_build_keymap(keymap_data, NULL,
39862306a36Sopenharmony_ci					   pdata->rows, pdata->cols,
39962306a36Sopenharmony_ci					   keypad->keycodes, input_dev);
40062306a36Sopenharmony_ci	if (error) {
40162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to build keymap\n");
40262306a36Sopenharmony_ci		goto err_unprepare_clk;
40362306a36Sopenharmony_ci	}
40462306a36Sopenharmony_ci
40562306a36Sopenharmony_ci	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
40662306a36Sopenharmony_ci	if (!pdata->no_autorepeat)
40762306a36Sopenharmony_ci		__set_bit(EV_REP, input_dev->evbit);
40862306a36Sopenharmony_ci
40962306a36Sopenharmony_ci	input_set_drvdata(input_dev, keypad);
41062306a36Sopenharmony_ci
41162306a36Sopenharmony_ci	keypad->irq = platform_get_irq(pdev, 0);
41262306a36Sopenharmony_ci	if (keypad->irq < 0) {
41362306a36Sopenharmony_ci		error = keypad->irq;
41462306a36Sopenharmony_ci		goto err_unprepare_clk;
41562306a36Sopenharmony_ci	}
41662306a36Sopenharmony_ci
41762306a36Sopenharmony_ci	error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
41862306a36Sopenharmony_ci					  samsung_keypad_irq, IRQF_ONESHOT,
41962306a36Sopenharmony_ci					  dev_name(&pdev->dev), keypad);
42062306a36Sopenharmony_ci	if (error) {
42162306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register keypad interrupt\n");
42262306a36Sopenharmony_ci		goto err_unprepare_clk;
42362306a36Sopenharmony_ci	}
42462306a36Sopenharmony_ci
42562306a36Sopenharmony_ci	device_init_wakeup(&pdev->dev, pdata->wakeup);
42662306a36Sopenharmony_ci	platform_set_drvdata(pdev, keypad);
42762306a36Sopenharmony_ci	pm_runtime_enable(&pdev->dev);
42862306a36Sopenharmony_ci
42962306a36Sopenharmony_ci	error = input_register_device(keypad->input_dev);
43062306a36Sopenharmony_ci	if (error)
43162306a36Sopenharmony_ci		goto err_disable_runtime_pm;
43262306a36Sopenharmony_ci
43362306a36Sopenharmony_ci	if (pdev->dev.of_node) {
43462306a36Sopenharmony_ci		devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
43562306a36Sopenharmony_ci		devm_kfree(&pdev->dev, (void *)pdata->keymap_data);
43662306a36Sopenharmony_ci		devm_kfree(&pdev->dev, (void *)pdata);
43762306a36Sopenharmony_ci	}
43862306a36Sopenharmony_ci	return 0;
43962306a36Sopenharmony_ci
44062306a36Sopenharmony_cierr_disable_runtime_pm:
44162306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
44262306a36Sopenharmony_cierr_unprepare_clk:
44362306a36Sopenharmony_ci	clk_unprepare(keypad->clk);
44462306a36Sopenharmony_ci	return error;
44562306a36Sopenharmony_ci}
44662306a36Sopenharmony_ci
44762306a36Sopenharmony_cistatic int samsung_keypad_remove(struct platform_device *pdev)
44862306a36Sopenharmony_ci{
44962306a36Sopenharmony_ci	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
45062306a36Sopenharmony_ci
45162306a36Sopenharmony_ci	pm_runtime_disable(&pdev->dev);
45262306a36Sopenharmony_ci
45362306a36Sopenharmony_ci	input_unregister_device(keypad->input_dev);
45462306a36Sopenharmony_ci
45562306a36Sopenharmony_ci	clk_unprepare(keypad->clk);
45662306a36Sopenharmony_ci
45762306a36Sopenharmony_ci	return 0;
45862306a36Sopenharmony_ci}
45962306a36Sopenharmony_ci
46062306a36Sopenharmony_cistatic int samsung_keypad_runtime_suspend(struct device *dev)
46162306a36Sopenharmony_ci{
46262306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
46362306a36Sopenharmony_ci	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
46462306a36Sopenharmony_ci	unsigned int val;
46562306a36Sopenharmony_ci	int error;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	if (keypad->stopped)
46862306a36Sopenharmony_ci		return 0;
46962306a36Sopenharmony_ci
47062306a36Sopenharmony_ci	/* This may fail on some SoCs due to lack of controller support */
47162306a36Sopenharmony_ci	error = enable_irq_wake(keypad->irq);
47262306a36Sopenharmony_ci	if (!error)
47362306a36Sopenharmony_ci		keypad->wake_enabled = true;
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_ci	val = readl(keypad->base + SAMSUNG_KEYIFCON);
47662306a36Sopenharmony_ci	val |= SAMSUNG_KEYIFCON_WAKEUPEN;
47762306a36Sopenharmony_ci	writel(val, keypad->base + SAMSUNG_KEYIFCON);
47862306a36Sopenharmony_ci
47962306a36Sopenharmony_ci	clk_disable(keypad->clk);
48062306a36Sopenharmony_ci
48162306a36Sopenharmony_ci	return 0;
48262306a36Sopenharmony_ci}
48362306a36Sopenharmony_ci
48462306a36Sopenharmony_cistatic int samsung_keypad_runtime_resume(struct device *dev)
48562306a36Sopenharmony_ci{
48662306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
48762306a36Sopenharmony_ci	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
48862306a36Sopenharmony_ci	unsigned int val;
48962306a36Sopenharmony_ci
49062306a36Sopenharmony_ci	if (keypad->stopped)
49162306a36Sopenharmony_ci		return 0;
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci	clk_enable(keypad->clk);
49462306a36Sopenharmony_ci
49562306a36Sopenharmony_ci	val = readl(keypad->base + SAMSUNG_KEYIFCON);
49662306a36Sopenharmony_ci	val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
49762306a36Sopenharmony_ci	writel(val, keypad->base + SAMSUNG_KEYIFCON);
49862306a36Sopenharmony_ci
49962306a36Sopenharmony_ci	if (keypad->wake_enabled)
50062306a36Sopenharmony_ci		disable_irq_wake(keypad->irq);
50162306a36Sopenharmony_ci
50262306a36Sopenharmony_ci	return 0;
50362306a36Sopenharmony_ci}
50462306a36Sopenharmony_ci
50562306a36Sopenharmony_cistatic void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
50662306a36Sopenharmony_ci					 bool enable)
50762306a36Sopenharmony_ci{
50862306a36Sopenharmony_ci	unsigned int val;
50962306a36Sopenharmony_ci
51062306a36Sopenharmony_ci	clk_enable(keypad->clk);
51162306a36Sopenharmony_ci
51262306a36Sopenharmony_ci	val = readl(keypad->base + SAMSUNG_KEYIFCON);
51362306a36Sopenharmony_ci	if (enable) {
51462306a36Sopenharmony_ci		val |= SAMSUNG_KEYIFCON_WAKEUPEN;
51562306a36Sopenharmony_ci		if (device_may_wakeup(&keypad->pdev->dev))
51662306a36Sopenharmony_ci			enable_irq_wake(keypad->irq);
51762306a36Sopenharmony_ci	} else {
51862306a36Sopenharmony_ci		val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
51962306a36Sopenharmony_ci		if (device_may_wakeup(&keypad->pdev->dev))
52062306a36Sopenharmony_ci			disable_irq_wake(keypad->irq);
52162306a36Sopenharmony_ci	}
52262306a36Sopenharmony_ci	writel(val, keypad->base + SAMSUNG_KEYIFCON);
52362306a36Sopenharmony_ci
52462306a36Sopenharmony_ci	clk_disable(keypad->clk);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_cistatic int samsung_keypad_suspend(struct device *dev)
52862306a36Sopenharmony_ci{
52962306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
53062306a36Sopenharmony_ci	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
53162306a36Sopenharmony_ci	struct input_dev *input_dev = keypad->input_dev;
53262306a36Sopenharmony_ci
53362306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
53462306a36Sopenharmony_ci
53562306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
53662306a36Sopenharmony_ci		samsung_keypad_stop(keypad);
53762306a36Sopenharmony_ci
53862306a36Sopenharmony_ci	samsung_keypad_toggle_wakeup(keypad, true);
53962306a36Sopenharmony_ci
54062306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
54162306a36Sopenharmony_ci
54262306a36Sopenharmony_ci	return 0;
54362306a36Sopenharmony_ci}
54462306a36Sopenharmony_ci
54562306a36Sopenharmony_cistatic int samsung_keypad_resume(struct device *dev)
54662306a36Sopenharmony_ci{
54762306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
54862306a36Sopenharmony_ci	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
54962306a36Sopenharmony_ci	struct input_dev *input_dev = keypad->input_dev;
55062306a36Sopenharmony_ci
55162306a36Sopenharmony_ci	mutex_lock(&input_dev->mutex);
55262306a36Sopenharmony_ci
55362306a36Sopenharmony_ci	samsung_keypad_toggle_wakeup(keypad, false);
55462306a36Sopenharmony_ci
55562306a36Sopenharmony_ci	if (input_device_enabled(input_dev))
55662306a36Sopenharmony_ci		samsung_keypad_start(keypad);
55762306a36Sopenharmony_ci
55862306a36Sopenharmony_ci	mutex_unlock(&input_dev->mutex);
55962306a36Sopenharmony_ci
56062306a36Sopenharmony_ci	return 0;
56162306a36Sopenharmony_ci}
56262306a36Sopenharmony_ci
56362306a36Sopenharmony_cistatic const struct dev_pm_ops samsung_keypad_pm_ops = {
56462306a36Sopenharmony_ci	SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
56562306a36Sopenharmony_ci	RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
56662306a36Sopenharmony_ci		       samsung_keypad_runtime_resume, NULL)
56762306a36Sopenharmony_ci};
56862306a36Sopenharmony_ci
56962306a36Sopenharmony_ci#ifdef CONFIG_OF
57062306a36Sopenharmony_cistatic const struct of_device_id samsung_keypad_dt_match[] = {
57162306a36Sopenharmony_ci	{ .compatible = "samsung,s3c6410-keypad" },
57262306a36Sopenharmony_ci	{ .compatible = "samsung,s5pv210-keypad" },
57362306a36Sopenharmony_ci	{},
57462306a36Sopenharmony_ci};
57562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
57662306a36Sopenharmony_ci#endif
57762306a36Sopenharmony_ci
57862306a36Sopenharmony_cistatic const struct platform_device_id samsung_keypad_driver_ids[] = {
57962306a36Sopenharmony_ci	{
58062306a36Sopenharmony_ci		.name		= "samsung-keypad",
58162306a36Sopenharmony_ci		.driver_data	= KEYPAD_TYPE_SAMSUNG,
58262306a36Sopenharmony_ci	}, {
58362306a36Sopenharmony_ci		.name		= "s5pv210-keypad",
58462306a36Sopenharmony_ci		.driver_data	= KEYPAD_TYPE_S5PV210,
58562306a36Sopenharmony_ci	},
58662306a36Sopenharmony_ci	{ },
58762306a36Sopenharmony_ci};
58862306a36Sopenharmony_ciMODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
58962306a36Sopenharmony_ci
59062306a36Sopenharmony_cistatic struct platform_driver samsung_keypad_driver = {
59162306a36Sopenharmony_ci	.probe		= samsung_keypad_probe,
59262306a36Sopenharmony_ci	.remove		= samsung_keypad_remove,
59362306a36Sopenharmony_ci	.driver		= {
59462306a36Sopenharmony_ci		.name	= "samsung-keypad",
59562306a36Sopenharmony_ci		.of_match_table = of_match_ptr(samsung_keypad_dt_match),
59662306a36Sopenharmony_ci		.pm	= pm_ptr(&samsung_keypad_pm_ops),
59762306a36Sopenharmony_ci	},
59862306a36Sopenharmony_ci	.id_table	= samsung_keypad_driver_ids,
59962306a36Sopenharmony_ci};
60062306a36Sopenharmony_cimodule_platform_driver(samsung_keypad_driver);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ciMODULE_DESCRIPTION("Samsung keypad driver");
60362306a36Sopenharmony_ciMODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
60462306a36Sopenharmony_ciMODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
60562306a36Sopenharmony_ciMODULE_LICENSE("GPL");
606