cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

samsung-keypad.c (15438B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Samsung keypad driver
      4 *
      5 * Copyright (C) 2010 Samsung Electronics Co.Ltd
      6 * Author: Joonyoung Shim <jy0922.shim@samsung.com>
      7 * Author: Donghwa Lee <dh09.lee@samsung.com>
      8 */
      9
     10#include <linux/clk.h>
     11#include <linux/delay.h>
     12#include <linux/err.h>
     13#include <linux/input.h>
     14#include <linux/interrupt.h>
     15#include <linux/io.h>
     16#include <linux/module.h>
     17#include <linux/platform_device.h>
     18#include <linux/pm.h>
     19#include <linux/pm_runtime.h>
     20#include <linux/slab.h>
     21#include <linux/of.h>
     22#include <linux/sched.h>
     23#include <linux/input/samsung-keypad.h>
     24
     25#define SAMSUNG_KEYIFCON			0x00
     26#define SAMSUNG_KEYIFSTSCLR			0x04
     27#define SAMSUNG_KEYIFCOL			0x08
     28#define SAMSUNG_KEYIFROW			0x0c
     29#define SAMSUNG_KEYIFFC				0x10
     30
     31/* SAMSUNG_KEYIFCON */
     32#define SAMSUNG_KEYIFCON_INT_F_EN		(1 << 0)
     33#define SAMSUNG_KEYIFCON_INT_R_EN		(1 << 1)
     34#define SAMSUNG_KEYIFCON_DF_EN			(1 << 2)
     35#define SAMSUNG_KEYIFCON_FC_EN			(1 << 3)
     36#define SAMSUNG_KEYIFCON_WAKEUPEN		(1 << 4)
     37
     38/* SAMSUNG_KEYIFSTSCLR */
     39#define SAMSUNG_KEYIFSTSCLR_P_INT_MASK		(0xff << 0)
     40#define SAMSUNG_KEYIFSTSCLR_R_INT_MASK		(0xff << 8)
     41#define SAMSUNG_KEYIFSTSCLR_R_INT_OFFSET	8
     42#define S5PV210_KEYIFSTSCLR_P_INT_MASK		(0x3fff << 0)
     43#define S5PV210_KEYIFSTSCLR_R_INT_MASK		(0x3fff << 16)
     44#define S5PV210_KEYIFSTSCLR_R_INT_OFFSET	16
     45
     46/* SAMSUNG_KEYIFCOL */
     47#define SAMSUNG_KEYIFCOL_MASK			(0xff << 0)
     48#define S5PV210_KEYIFCOLEN_MASK			(0xff << 8)
     49
     50/* SAMSUNG_KEYIFROW */
     51#define SAMSUNG_KEYIFROW_MASK			(0xff << 0)
     52#define S5PV210_KEYIFROW_MASK			(0x3fff << 0)
     53
     54/* SAMSUNG_KEYIFFC */
     55#define SAMSUNG_KEYIFFC_MASK			(0x3ff << 0)
     56
     57enum samsung_keypad_type {
     58	KEYPAD_TYPE_SAMSUNG,
     59	KEYPAD_TYPE_S5PV210,
     60};
     61
     62struct samsung_keypad {
     63	struct input_dev *input_dev;
     64	struct platform_device *pdev;
     65	struct clk *clk;
     66	void __iomem *base;
     67	wait_queue_head_t wait;
     68	bool stopped;
     69	bool wake_enabled;
     70	int irq;
     71	enum samsung_keypad_type type;
     72	unsigned int row_shift;
     73	unsigned int rows;
     74	unsigned int cols;
     75	unsigned int row_state[SAMSUNG_MAX_COLS];
     76	unsigned short keycodes[];
     77};
     78
     79static void samsung_keypad_scan(struct samsung_keypad *keypad,
     80				unsigned int *row_state)
     81{
     82	unsigned int col;
     83	unsigned int val;
     84
     85	for (col = 0; col < keypad->cols; col++) {
     86		if (keypad->type == KEYPAD_TYPE_S5PV210) {
     87			val = S5PV210_KEYIFCOLEN_MASK;
     88			val &= ~(1 << col) << 8;
     89		} else {
     90			val = SAMSUNG_KEYIFCOL_MASK;
     91			val &= ~(1 << col);
     92		}
     93
     94		writel(val, keypad->base + SAMSUNG_KEYIFCOL);
     95		mdelay(1);
     96
     97		val = readl(keypad->base + SAMSUNG_KEYIFROW);
     98		row_state[col] = ~val & ((1 << keypad->rows) - 1);
     99	}
    100
    101	/* KEYIFCOL reg clear */
    102	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
    103}
    104
    105static bool samsung_keypad_report(struct samsung_keypad *keypad,
    106				  unsigned int *row_state)
    107{
    108	struct input_dev *input_dev = keypad->input_dev;
    109	unsigned int changed;
    110	unsigned int pressed;
    111	unsigned int key_down = 0;
    112	unsigned int val;
    113	unsigned int col, row;
    114
    115	for (col = 0; col < keypad->cols; col++) {
    116		changed = row_state[col] ^ keypad->row_state[col];
    117		key_down |= row_state[col];
    118		if (!changed)
    119			continue;
    120
    121		for (row = 0; row < keypad->rows; row++) {
    122			if (!(changed & (1 << row)))
    123				continue;
    124
    125			pressed = row_state[col] & (1 << row);
    126
    127			dev_dbg(&keypad->input_dev->dev,
    128				"key %s, row: %d, col: %d\n",
    129				pressed ? "pressed" : "released", row, col);
    130
    131			val = MATRIX_SCAN_CODE(row, col, keypad->row_shift);
    132
    133			input_event(input_dev, EV_MSC, MSC_SCAN, val);
    134			input_report_key(input_dev,
    135					keypad->keycodes[val], pressed);
    136		}
    137		input_sync(keypad->input_dev);
    138	}
    139
    140	memcpy(keypad->row_state, row_state, sizeof(keypad->row_state));
    141
    142	return key_down;
    143}
    144
    145static irqreturn_t samsung_keypad_irq(int irq, void *dev_id)
    146{
    147	struct samsung_keypad *keypad = dev_id;
    148	unsigned int row_state[SAMSUNG_MAX_COLS];
    149	bool key_down;
    150
    151	pm_runtime_get_sync(&keypad->pdev->dev);
    152
    153	do {
    154		readl(keypad->base + SAMSUNG_KEYIFSTSCLR);
    155		/* Clear interrupt. */
    156		writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
    157
    158		samsung_keypad_scan(keypad, row_state);
    159
    160		key_down = samsung_keypad_report(keypad, row_state);
    161		if (key_down)
    162			wait_event_timeout(keypad->wait, keypad->stopped,
    163					   msecs_to_jiffies(50));
    164
    165	} while (key_down && !keypad->stopped);
    166
    167	pm_runtime_put(&keypad->pdev->dev);
    168
    169	return IRQ_HANDLED;
    170}
    171
    172static void samsung_keypad_start(struct samsung_keypad *keypad)
    173{
    174	unsigned int val;
    175
    176	pm_runtime_get_sync(&keypad->pdev->dev);
    177
    178	/* Tell IRQ thread that it may poll the device. */
    179	keypad->stopped = false;
    180
    181	clk_enable(keypad->clk);
    182
    183	/* Enable interrupt bits. */
    184	val = readl(keypad->base + SAMSUNG_KEYIFCON);
    185	val |= SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN;
    186	writel(val, keypad->base + SAMSUNG_KEYIFCON);
    187
    188	/* KEYIFCOL reg clear. */
    189	writel(0, keypad->base + SAMSUNG_KEYIFCOL);
    190
    191	pm_runtime_put(&keypad->pdev->dev);
    192}
    193
    194static void samsung_keypad_stop(struct samsung_keypad *keypad)
    195{
    196	unsigned int val;
    197
    198	pm_runtime_get_sync(&keypad->pdev->dev);
    199
    200	/* Signal IRQ thread to stop polling and disable the handler. */
    201	keypad->stopped = true;
    202	wake_up(&keypad->wait);
    203	disable_irq(keypad->irq);
    204
    205	/* Clear interrupt. */
    206	writel(~0x0, keypad->base + SAMSUNG_KEYIFSTSCLR);
    207
    208	/* Disable interrupt bits. */
    209	val = readl(keypad->base + SAMSUNG_KEYIFCON);
    210	val &= ~(SAMSUNG_KEYIFCON_INT_F_EN | SAMSUNG_KEYIFCON_INT_R_EN);
    211	writel(val, keypad->base + SAMSUNG_KEYIFCON);
    212
    213	clk_disable(keypad->clk);
    214
    215	/*
    216	 * Now that chip should not generate interrupts we can safely
    217	 * re-enable the handler.
    218	 */
    219	enable_irq(keypad->irq);
    220
    221	pm_runtime_put(&keypad->pdev->dev);
    222}
    223
    224static int samsung_keypad_open(struct input_dev *input_dev)
    225{
    226	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
    227
    228	samsung_keypad_start(keypad);
    229
    230	return 0;
    231}
    232
    233static void samsung_keypad_close(struct input_dev *input_dev)
    234{
    235	struct samsung_keypad *keypad = input_get_drvdata(input_dev);
    236
    237	samsung_keypad_stop(keypad);
    238}
    239
    240#ifdef CONFIG_OF
    241static struct samsung_keypad_platdata *
    242samsung_keypad_parse_dt(struct device *dev)
    243{
    244	struct samsung_keypad_platdata *pdata;
    245	struct matrix_keymap_data *keymap_data;
    246	uint32_t *keymap, num_rows = 0, num_cols = 0;
    247	struct device_node *np = dev->of_node, *key_np;
    248	unsigned int key_count;
    249
    250	if (!np) {
    251		dev_err(dev, "missing device tree data\n");
    252		return ERR_PTR(-EINVAL);
    253	}
    254
    255	pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
    256	if (!pdata) {
    257		dev_err(dev, "could not allocate memory for platform data\n");
    258		return ERR_PTR(-ENOMEM);
    259	}
    260
    261	of_property_read_u32(np, "samsung,keypad-num-rows", &num_rows);
    262	of_property_read_u32(np, "samsung,keypad-num-columns", &num_cols);
    263	if (!num_rows || !num_cols) {
    264		dev_err(dev, "number of keypad rows/columns not specified\n");
    265		return ERR_PTR(-EINVAL);
    266	}
    267	pdata->rows = num_rows;
    268	pdata->cols = num_cols;
    269
    270	keymap_data = devm_kzalloc(dev, sizeof(*keymap_data), GFP_KERNEL);
    271	if (!keymap_data) {
    272		dev_err(dev, "could not allocate memory for keymap data\n");
    273		return ERR_PTR(-ENOMEM);
    274	}
    275	pdata->keymap_data = keymap_data;
    276
    277	key_count = of_get_child_count(np);
    278	keymap_data->keymap_size = key_count;
    279	keymap = devm_kcalloc(dev, key_count, sizeof(uint32_t), GFP_KERNEL);
    280	if (!keymap) {
    281		dev_err(dev, "could not allocate memory for keymap\n");
    282		return ERR_PTR(-ENOMEM);
    283	}
    284	keymap_data->keymap = keymap;
    285
    286	for_each_child_of_node(np, key_np) {
    287		u32 row, col, key_code;
    288		of_property_read_u32(key_np, "keypad,row", &row);
    289		of_property_read_u32(key_np, "keypad,column", &col);
    290		of_property_read_u32(key_np, "linux,code", &key_code);
    291		*keymap++ = KEY(row, col, key_code);
    292	}
    293
    294	if (of_get_property(np, "linux,input-no-autorepeat", NULL))
    295		pdata->no_autorepeat = true;
    296
    297	pdata->wakeup = of_property_read_bool(np, "wakeup-source") ||
    298			/* legacy name */
    299			of_property_read_bool(np, "linux,input-wakeup");
    300
    301
    302	return pdata;
    303}
    304#else
    305static struct samsung_keypad_platdata *
    306samsung_keypad_parse_dt(struct device *dev)
    307{
    308	dev_err(dev, "no platform data defined\n");
    309
    310	return ERR_PTR(-EINVAL);
    311}
    312#endif
    313
    314static int samsung_keypad_probe(struct platform_device *pdev)
    315{
    316	const struct samsung_keypad_platdata *pdata;
    317	const struct matrix_keymap_data *keymap_data;
    318	struct samsung_keypad *keypad;
    319	struct resource *res;
    320	struct input_dev *input_dev;
    321	unsigned int row_shift;
    322	unsigned int keymap_size;
    323	int error;
    324
    325	pdata = dev_get_platdata(&pdev->dev);
    326	if (!pdata) {
    327		pdata = samsung_keypad_parse_dt(&pdev->dev);
    328		if (IS_ERR(pdata))
    329			return PTR_ERR(pdata);
    330	}
    331
    332	keymap_data = pdata->keymap_data;
    333	if (!keymap_data) {
    334		dev_err(&pdev->dev, "no keymap data defined\n");
    335		return -EINVAL;
    336	}
    337
    338	if (!pdata->rows || pdata->rows > SAMSUNG_MAX_ROWS)
    339		return -EINVAL;
    340
    341	if (!pdata->cols || pdata->cols > SAMSUNG_MAX_COLS)
    342		return -EINVAL;
    343
    344	/* initialize the gpio */
    345	if (pdata->cfg_gpio)
    346		pdata->cfg_gpio(pdata->rows, pdata->cols);
    347
    348	row_shift = get_count_order(pdata->cols);
    349	keymap_size = (pdata->rows << row_shift) * sizeof(keypad->keycodes[0]);
    350
    351	keypad = devm_kzalloc(&pdev->dev, sizeof(*keypad) + keymap_size,
    352			      GFP_KERNEL);
    353	input_dev = devm_input_allocate_device(&pdev->dev);
    354	if (!keypad || !input_dev)
    355		return -ENOMEM;
    356
    357	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    358	if (!res)
    359		return -ENODEV;
    360
    361	keypad->base = devm_ioremap(&pdev->dev, res->start, resource_size(res));
    362	if (!keypad->base)
    363		return -EBUSY;
    364
    365	keypad->clk = devm_clk_get(&pdev->dev, "keypad");
    366	if (IS_ERR(keypad->clk)) {
    367		dev_err(&pdev->dev, "failed to get keypad clk\n");
    368		return PTR_ERR(keypad->clk);
    369	}
    370
    371	error = clk_prepare(keypad->clk);
    372	if (error) {
    373		dev_err(&pdev->dev, "keypad clock prepare failed\n");
    374		return error;
    375	}
    376
    377	keypad->input_dev = input_dev;
    378	keypad->pdev = pdev;
    379	keypad->row_shift = row_shift;
    380	keypad->rows = pdata->rows;
    381	keypad->cols = pdata->cols;
    382	keypad->stopped = true;
    383	init_waitqueue_head(&keypad->wait);
    384
    385	if (pdev->dev.of_node)
    386		keypad->type = of_device_is_compatible(pdev->dev.of_node,
    387					"samsung,s5pv210-keypad");
    388	else
    389		keypad->type = platform_get_device_id(pdev)->driver_data;
    390
    391	input_dev->name = pdev->name;
    392	input_dev->id.bustype = BUS_HOST;
    393	input_dev->dev.parent = &pdev->dev;
    394
    395	input_dev->open = samsung_keypad_open;
    396	input_dev->close = samsung_keypad_close;
    397
    398	error = matrix_keypad_build_keymap(keymap_data, NULL,
    399					   pdata->rows, pdata->cols,
    400					   keypad->keycodes, input_dev);
    401	if (error) {
    402		dev_err(&pdev->dev, "failed to build keymap\n");
    403		goto err_unprepare_clk;
    404	}
    405
    406	input_set_capability(input_dev, EV_MSC, MSC_SCAN);
    407	if (!pdata->no_autorepeat)
    408		__set_bit(EV_REP, input_dev->evbit);
    409
    410	input_set_drvdata(input_dev, keypad);
    411
    412	keypad->irq = platform_get_irq(pdev, 0);
    413	if (keypad->irq < 0) {
    414		error = keypad->irq;
    415		goto err_unprepare_clk;
    416	}
    417
    418	error = devm_request_threaded_irq(&pdev->dev, keypad->irq, NULL,
    419					  samsung_keypad_irq, IRQF_ONESHOT,
    420					  dev_name(&pdev->dev), keypad);
    421	if (error) {
    422		dev_err(&pdev->dev, "failed to register keypad interrupt\n");
    423		goto err_unprepare_clk;
    424	}
    425
    426	device_init_wakeup(&pdev->dev, pdata->wakeup);
    427	platform_set_drvdata(pdev, keypad);
    428	pm_runtime_enable(&pdev->dev);
    429
    430	error = input_register_device(keypad->input_dev);
    431	if (error)
    432		goto err_disable_runtime_pm;
    433
    434	if (pdev->dev.of_node) {
    435		devm_kfree(&pdev->dev, (void *)pdata->keymap_data->keymap);
    436		devm_kfree(&pdev->dev, (void *)pdata->keymap_data);
    437		devm_kfree(&pdev->dev, (void *)pdata);
    438	}
    439	return 0;
    440
    441err_disable_runtime_pm:
    442	pm_runtime_disable(&pdev->dev);
    443err_unprepare_clk:
    444	clk_unprepare(keypad->clk);
    445	return error;
    446}
    447
    448static int samsung_keypad_remove(struct platform_device *pdev)
    449{
    450	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
    451
    452	pm_runtime_disable(&pdev->dev);
    453
    454	input_unregister_device(keypad->input_dev);
    455
    456	clk_unprepare(keypad->clk);
    457
    458	return 0;
    459}
    460
    461#ifdef CONFIG_PM
    462static int samsung_keypad_runtime_suspend(struct device *dev)
    463{
    464	struct platform_device *pdev = to_platform_device(dev);
    465	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
    466	unsigned int val;
    467	int error;
    468
    469	if (keypad->stopped)
    470		return 0;
    471
    472	/* This may fail on some SoCs due to lack of controller support */
    473	error = enable_irq_wake(keypad->irq);
    474	if (!error)
    475		keypad->wake_enabled = true;
    476
    477	val = readl(keypad->base + SAMSUNG_KEYIFCON);
    478	val |= SAMSUNG_KEYIFCON_WAKEUPEN;
    479	writel(val, keypad->base + SAMSUNG_KEYIFCON);
    480
    481	clk_disable(keypad->clk);
    482
    483	return 0;
    484}
    485
    486static int samsung_keypad_runtime_resume(struct device *dev)
    487{
    488	struct platform_device *pdev = to_platform_device(dev);
    489	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
    490	unsigned int val;
    491
    492	if (keypad->stopped)
    493		return 0;
    494
    495	clk_enable(keypad->clk);
    496
    497	val = readl(keypad->base + SAMSUNG_KEYIFCON);
    498	val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
    499	writel(val, keypad->base + SAMSUNG_KEYIFCON);
    500
    501	if (keypad->wake_enabled)
    502		disable_irq_wake(keypad->irq);
    503
    504	return 0;
    505}
    506#endif
    507
    508#ifdef CONFIG_PM_SLEEP
    509static void samsung_keypad_toggle_wakeup(struct samsung_keypad *keypad,
    510					 bool enable)
    511{
    512	unsigned int val;
    513
    514	clk_enable(keypad->clk);
    515
    516	val = readl(keypad->base + SAMSUNG_KEYIFCON);
    517	if (enable) {
    518		val |= SAMSUNG_KEYIFCON_WAKEUPEN;
    519		if (device_may_wakeup(&keypad->pdev->dev))
    520			enable_irq_wake(keypad->irq);
    521	} else {
    522		val &= ~SAMSUNG_KEYIFCON_WAKEUPEN;
    523		if (device_may_wakeup(&keypad->pdev->dev))
    524			disable_irq_wake(keypad->irq);
    525	}
    526	writel(val, keypad->base + SAMSUNG_KEYIFCON);
    527
    528	clk_disable(keypad->clk);
    529}
    530
    531static int samsung_keypad_suspend(struct device *dev)
    532{
    533	struct platform_device *pdev = to_platform_device(dev);
    534	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
    535	struct input_dev *input_dev = keypad->input_dev;
    536
    537	mutex_lock(&input_dev->mutex);
    538
    539	if (input_device_enabled(input_dev))
    540		samsung_keypad_stop(keypad);
    541
    542	samsung_keypad_toggle_wakeup(keypad, true);
    543
    544	mutex_unlock(&input_dev->mutex);
    545
    546	return 0;
    547}
    548
    549static int samsung_keypad_resume(struct device *dev)
    550{
    551	struct platform_device *pdev = to_platform_device(dev);
    552	struct samsung_keypad *keypad = platform_get_drvdata(pdev);
    553	struct input_dev *input_dev = keypad->input_dev;
    554
    555	mutex_lock(&input_dev->mutex);
    556
    557	samsung_keypad_toggle_wakeup(keypad, false);
    558
    559	if (input_device_enabled(input_dev))
    560		samsung_keypad_start(keypad);
    561
    562	mutex_unlock(&input_dev->mutex);
    563
    564	return 0;
    565}
    566#endif
    567
    568static const struct dev_pm_ops samsung_keypad_pm_ops = {
    569	SET_SYSTEM_SLEEP_PM_OPS(samsung_keypad_suspend, samsung_keypad_resume)
    570	SET_RUNTIME_PM_OPS(samsung_keypad_runtime_suspend,
    571			   samsung_keypad_runtime_resume, NULL)
    572};
    573
    574#ifdef CONFIG_OF
    575static const struct of_device_id samsung_keypad_dt_match[] = {
    576	{ .compatible = "samsung,s3c6410-keypad" },
    577	{ .compatible = "samsung,s5pv210-keypad" },
    578	{},
    579};
    580MODULE_DEVICE_TABLE(of, samsung_keypad_dt_match);
    581#endif
    582
    583static const struct platform_device_id samsung_keypad_driver_ids[] = {
    584	{
    585		.name		= "samsung-keypad",
    586		.driver_data	= KEYPAD_TYPE_SAMSUNG,
    587	}, {
    588		.name		= "s5pv210-keypad",
    589		.driver_data	= KEYPAD_TYPE_S5PV210,
    590	},
    591	{ },
    592};
    593MODULE_DEVICE_TABLE(platform, samsung_keypad_driver_ids);
    594
    595static struct platform_driver samsung_keypad_driver = {
    596	.probe		= samsung_keypad_probe,
    597	.remove		= samsung_keypad_remove,
    598	.driver		= {
    599		.name	= "samsung-keypad",
    600		.of_match_table = of_match_ptr(samsung_keypad_dt_match),
    601		.pm	= &samsung_keypad_pm_ops,
    602	},
    603	.id_table	= samsung_keypad_driver_ids,
    604};
    605module_platform_driver(samsung_keypad_driver);
    606
    607MODULE_DESCRIPTION("Samsung keypad driver");
    608MODULE_AUTHOR("Joonyoung Shim <jy0922.shim@samsung.com>");
    609MODULE_AUTHOR("Donghwa Lee <dh09.lee@samsung.com>");
    610MODULE_LICENSE("GPL");