162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * STMicroelectronics Key Scanning driver
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (c) 2014 STMicroelectonics Ltd.
662306a36Sopenharmony_ci * Author: Stuart Menefy <stuart.menefy@st.com>
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Based on sh_keysc.c, copyright 2008 Magnus Damm
962306a36Sopenharmony_ci */
1062306a36Sopenharmony_ci
1162306a36Sopenharmony_ci#include <linux/clk.h>
1262306a36Sopenharmony_ci#include <linux/input.h>
1362306a36Sopenharmony_ci#include <linux/input/matrix_keypad.h>
1462306a36Sopenharmony_ci#include <linux/io.h>
1562306a36Sopenharmony_ci#include <linux/interrupt.h>
1662306a36Sopenharmony_ci#include <linux/module.h>
1762306a36Sopenharmony_ci#include <linux/of.h>
1862306a36Sopenharmony_ci#include <linux/platform_device.h>
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci#define ST_KEYSCAN_MAXKEYS 16
2162306a36Sopenharmony_ci
2262306a36Sopenharmony_ci#define KEYSCAN_CONFIG_OFF		0x0
2362306a36Sopenharmony_ci#define KEYSCAN_CONFIG_ENABLE		0x1
2462306a36Sopenharmony_ci#define KEYSCAN_DEBOUNCE_TIME_OFF	0x4
2562306a36Sopenharmony_ci#define KEYSCAN_MATRIX_STATE_OFF	0x8
2662306a36Sopenharmony_ci#define KEYSCAN_MATRIX_DIM_OFF		0xc
2762306a36Sopenharmony_ci#define KEYSCAN_MATRIX_DIM_X_SHIFT	0x0
2862306a36Sopenharmony_ci#define KEYSCAN_MATRIX_DIM_Y_SHIFT	0x2
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_cistruct st_keyscan {
3162306a36Sopenharmony_ci	void __iomem *base;
3262306a36Sopenharmony_ci	int irq;
3362306a36Sopenharmony_ci	struct clk *clk;
3462306a36Sopenharmony_ci	struct input_dev *input_dev;
3562306a36Sopenharmony_ci	unsigned long last_state;
3662306a36Sopenharmony_ci	unsigned int n_rows;
3762306a36Sopenharmony_ci	unsigned int n_cols;
3862306a36Sopenharmony_ci	unsigned int debounce_us;
3962306a36Sopenharmony_ci};
4062306a36Sopenharmony_ci
4162306a36Sopenharmony_cistatic irqreturn_t keyscan_isr(int irq, void *dev_id)
4262306a36Sopenharmony_ci{
4362306a36Sopenharmony_ci	struct st_keyscan *keypad = dev_id;
4462306a36Sopenharmony_ci	unsigned short *keycode = keypad->input_dev->keycode;
4562306a36Sopenharmony_ci	unsigned long state, change;
4662306a36Sopenharmony_ci	int bit_nr;
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci	state = readl(keypad->base + KEYSCAN_MATRIX_STATE_OFF) & 0xffff;
4962306a36Sopenharmony_ci	change = keypad->last_state ^ state;
5062306a36Sopenharmony_ci	keypad->last_state = state;
5162306a36Sopenharmony_ci
5262306a36Sopenharmony_ci	for_each_set_bit(bit_nr, &change, BITS_PER_LONG)
5362306a36Sopenharmony_ci		input_report_key(keypad->input_dev,
5462306a36Sopenharmony_ci				 keycode[bit_nr], state & BIT(bit_nr));
5562306a36Sopenharmony_ci
5662306a36Sopenharmony_ci	input_sync(keypad->input_dev);
5762306a36Sopenharmony_ci
5862306a36Sopenharmony_ci	return IRQ_HANDLED;
5962306a36Sopenharmony_ci}
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_cistatic int keyscan_start(struct st_keyscan *keypad)
6262306a36Sopenharmony_ci{
6362306a36Sopenharmony_ci	int error;
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	error = clk_enable(keypad->clk);
6662306a36Sopenharmony_ci	if (error)
6762306a36Sopenharmony_ci		return error;
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ci	writel(keypad->debounce_us * (clk_get_rate(keypad->clk) / 1000000),
7062306a36Sopenharmony_ci	       keypad->base + KEYSCAN_DEBOUNCE_TIME_OFF);
7162306a36Sopenharmony_ci
7262306a36Sopenharmony_ci	writel(((keypad->n_cols - 1) << KEYSCAN_MATRIX_DIM_X_SHIFT) |
7362306a36Sopenharmony_ci	       ((keypad->n_rows - 1) << KEYSCAN_MATRIX_DIM_Y_SHIFT),
7462306a36Sopenharmony_ci	       keypad->base + KEYSCAN_MATRIX_DIM_OFF);
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	writel(KEYSCAN_CONFIG_ENABLE, keypad->base + KEYSCAN_CONFIG_OFF);
7762306a36Sopenharmony_ci
7862306a36Sopenharmony_ci	return 0;
7962306a36Sopenharmony_ci}
8062306a36Sopenharmony_ci
8162306a36Sopenharmony_cistatic void keyscan_stop(struct st_keyscan *keypad)
8262306a36Sopenharmony_ci{
8362306a36Sopenharmony_ci	writel(0, keypad->base + KEYSCAN_CONFIG_OFF);
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	clk_disable(keypad->clk);
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int keyscan_open(struct input_dev *dev)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	struct st_keyscan *keypad = input_get_drvdata(dev);
9162306a36Sopenharmony_ci
9262306a36Sopenharmony_ci	return keyscan_start(keypad);
9362306a36Sopenharmony_ci}
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_cistatic void keyscan_close(struct input_dev *dev)
9662306a36Sopenharmony_ci{
9762306a36Sopenharmony_ci	struct st_keyscan *keypad = input_get_drvdata(dev);
9862306a36Sopenharmony_ci
9962306a36Sopenharmony_ci	keyscan_stop(keypad);
10062306a36Sopenharmony_ci}
10162306a36Sopenharmony_ci
10262306a36Sopenharmony_cistatic int keypad_matrix_key_parse_dt(struct st_keyscan *keypad_data)
10362306a36Sopenharmony_ci{
10462306a36Sopenharmony_ci	struct device *dev = keypad_data->input_dev->dev.parent;
10562306a36Sopenharmony_ci	struct device_node *np = dev->of_node;
10662306a36Sopenharmony_ci	int error;
10762306a36Sopenharmony_ci
10862306a36Sopenharmony_ci	error = matrix_keypad_parse_properties(dev, &keypad_data->n_rows,
10962306a36Sopenharmony_ci					       &keypad_data->n_cols);
11062306a36Sopenharmony_ci	if (error) {
11162306a36Sopenharmony_ci		dev_err(dev, "failed to parse keypad params\n");
11262306a36Sopenharmony_ci		return error;
11362306a36Sopenharmony_ci	}
11462306a36Sopenharmony_ci
11562306a36Sopenharmony_ci	of_property_read_u32(np, "st,debounce-us", &keypad_data->debounce_us);
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	dev_dbg(dev, "n_rows=%d n_col=%d debounce=%d\n",
11862306a36Sopenharmony_ci		keypad_data->n_rows, keypad_data->n_cols,
11962306a36Sopenharmony_ci		keypad_data->debounce_us);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci	return 0;
12262306a36Sopenharmony_ci}
12362306a36Sopenharmony_ci
12462306a36Sopenharmony_cistatic int keyscan_probe(struct platform_device *pdev)
12562306a36Sopenharmony_ci{
12662306a36Sopenharmony_ci	struct st_keyscan *keypad_data;
12762306a36Sopenharmony_ci	struct input_dev *input_dev;
12862306a36Sopenharmony_ci	int error;
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_ci	if (!pdev->dev.of_node) {
13162306a36Sopenharmony_ci		dev_err(&pdev->dev, "no DT data present\n");
13262306a36Sopenharmony_ci		return -EINVAL;
13362306a36Sopenharmony_ci	}
13462306a36Sopenharmony_ci
13562306a36Sopenharmony_ci	keypad_data = devm_kzalloc(&pdev->dev, sizeof(*keypad_data),
13662306a36Sopenharmony_ci				   GFP_KERNEL);
13762306a36Sopenharmony_ci	if (!keypad_data)
13862306a36Sopenharmony_ci		return -ENOMEM;
13962306a36Sopenharmony_ci
14062306a36Sopenharmony_ci	input_dev = devm_input_allocate_device(&pdev->dev);
14162306a36Sopenharmony_ci	if (!input_dev) {
14262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to allocate the input device\n");
14362306a36Sopenharmony_ci		return -ENOMEM;
14462306a36Sopenharmony_ci	}
14562306a36Sopenharmony_ci
14662306a36Sopenharmony_ci	input_dev->name = pdev->name;
14762306a36Sopenharmony_ci	input_dev->phys = "keyscan-keys/input0";
14862306a36Sopenharmony_ci	input_dev->dev.parent = &pdev->dev;
14962306a36Sopenharmony_ci	input_dev->open = keyscan_open;
15062306a36Sopenharmony_ci	input_dev->close = keyscan_close;
15162306a36Sopenharmony_ci
15262306a36Sopenharmony_ci	input_dev->id.bustype = BUS_HOST;
15362306a36Sopenharmony_ci
15462306a36Sopenharmony_ci	keypad_data->input_dev = input_dev;
15562306a36Sopenharmony_ci
15662306a36Sopenharmony_ci	error = keypad_matrix_key_parse_dt(keypad_data);
15762306a36Sopenharmony_ci	if (error)
15862306a36Sopenharmony_ci		return error;
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	error = matrix_keypad_build_keymap(NULL, NULL,
16162306a36Sopenharmony_ci					   keypad_data->n_rows,
16262306a36Sopenharmony_ci					   keypad_data->n_cols,
16362306a36Sopenharmony_ci					   NULL, input_dev);
16462306a36Sopenharmony_ci	if (error) {
16562306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to build keymap\n");
16662306a36Sopenharmony_ci		return error;
16762306a36Sopenharmony_ci	}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_ci	input_set_drvdata(input_dev, keypad_data);
17062306a36Sopenharmony_ci
17162306a36Sopenharmony_ci	keypad_data->base = devm_platform_get_and_ioremap_resource(pdev, 0, NULL);
17262306a36Sopenharmony_ci	if (IS_ERR(keypad_data->base))
17362306a36Sopenharmony_ci		return PTR_ERR(keypad_data->base);
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_ci	keypad_data->clk = devm_clk_get(&pdev->dev, NULL);
17662306a36Sopenharmony_ci	if (IS_ERR(keypad_data->clk)) {
17762306a36Sopenharmony_ci		dev_err(&pdev->dev, "cannot get clock\n");
17862306a36Sopenharmony_ci		return PTR_ERR(keypad_data->clk);
17962306a36Sopenharmony_ci	}
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci	error = clk_enable(keypad_data->clk);
18262306a36Sopenharmony_ci	if (error) {
18362306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to enable clock\n");
18462306a36Sopenharmony_ci		return error;
18562306a36Sopenharmony_ci	}
18662306a36Sopenharmony_ci
18762306a36Sopenharmony_ci	keyscan_stop(keypad_data);
18862306a36Sopenharmony_ci
18962306a36Sopenharmony_ci	keypad_data->irq = platform_get_irq(pdev, 0);
19062306a36Sopenharmony_ci	if (keypad_data->irq < 0)
19162306a36Sopenharmony_ci		return -EINVAL;
19262306a36Sopenharmony_ci
19362306a36Sopenharmony_ci	error = devm_request_irq(&pdev->dev, keypad_data->irq, keyscan_isr, 0,
19462306a36Sopenharmony_ci				 pdev->name, keypad_data);
19562306a36Sopenharmony_ci	if (error) {
19662306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to request IRQ\n");
19762306a36Sopenharmony_ci		return error;
19862306a36Sopenharmony_ci	}
19962306a36Sopenharmony_ci
20062306a36Sopenharmony_ci	error = input_register_device(input_dev);
20162306a36Sopenharmony_ci	if (error) {
20262306a36Sopenharmony_ci		dev_err(&pdev->dev, "failed to register input device\n");
20362306a36Sopenharmony_ci		return error;
20462306a36Sopenharmony_ci	}
20562306a36Sopenharmony_ci
20662306a36Sopenharmony_ci	platform_set_drvdata(pdev, keypad_data);
20762306a36Sopenharmony_ci
20862306a36Sopenharmony_ci	device_set_wakeup_capable(&pdev->dev, 1);
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_ci	return 0;
21162306a36Sopenharmony_ci}
21262306a36Sopenharmony_ci
21362306a36Sopenharmony_cistatic int keyscan_suspend(struct device *dev)
21462306a36Sopenharmony_ci{
21562306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
21662306a36Sopenharmony_ci	struct st_keyscan *keypad = platform_get_drvdata(pdev);
21762306a36Sopenharmony_ci	struct input_dev *input = keypad->input_dev;
21862306a36Sopenharmony_ci
21962306a36Sopenharmony_ci	mutex_lock(&input->mutex);
22062306a36Sopenharmony_ci
22162306a36Sopenharmony_ci	if (device_may_wakeup(dev))
22262306a36Sopenharmony_ci		enable_irq_wake(keypad->irq);
22362306a36Sopenharmony_ci	else if (input_device_enabled(input))
22462306a36Sopenharmony_ci		keyscan_stop(keypad);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci	mutex_unlock(&input->mutex);
22762306a36Sopenharmony_ci	return 0;
22862306a36Sopenharmony_ci}
22962306a36Sopenharmony_ci
23062306a36Sopenharmony_cistatic int keyscan_resume(struct device *dev)
23162306a36Sopenharmony_ci{
23262306a36Sopenharmony_ci	struct platform_device *pdev = to_platform_device(dev);
23362306a36Sopenharmony_ci	struct st_keyscan *keypad = platform_get_drvdata(pdev);
23462306a36Sopenharmony_ci	struct input_dev *input = keypad->input_dev;
23562306a36Sopenharmony_ci	int retval = 0;
23662306a36Sopenharmony_ci
23762306a36Sopenharmony_ci	mutex_lock(&input->mutex);
23862306a36Sopenharmony_ci
23962306a36Sopenharmony_ci	if (device_may_wakeup(dev))
24062306a36Sopenharmony_ci		disable_irq_wake(keypad->irq);
24162306a36Sopenharmony_ci	else if (input_device_enabled(input))
24262306a36Sopenharmony_ci		retval = keyscan_start(keypad);
24362306a36Sopenharmony_ci
24462306a36Sopenharmony_ci	mutex_unlock(&input->mutex);
24562306a36Sopenharmony_ci	return retval;
24662306a36Sopenharmony_ci}
24762306a36Sopenharmony_ci
24862306a36Sopenharmony_cistatic DEFINE_SIMPLE_DEV_PM_OPS(keyscan_dev_pm_ops,
24962306a36Sopenharmony_ci				keyscan_suspend, keyscan_resume);
25062306a36Sopenharmony_ci
25162306a36Sopenharmony_cistatic const struct of_device_id keyscan_of_match[] = {
25262306a36Sopenharmony_ci	{ .compatible = "st,sti-keyscan" },
25362306a36Sopenharmony_ci	{ },
25462306a36Sopenharmony_ci};
25562306a36Sopenharmony_ciMODULE_DEVICE_TABLE(of, keyscan_of_match);
25662306a36Sopenharmony_ci
25762306a36Sopenharmony_cistatic struct platform_driver keyscan_device_driver = {
25862306a36Sopenharmony_ci	.probe		= keyscan_probe,
25962306a36Sopenharmony_ci	.driver		= {
26062306a36Sopenharmony_ci		.name	= "st-keyscan",
26162306a36Sopenharmony_ci		.pm	= pm_sleep_ptr(&keyscan_dev_pm_ops),
26262306a36Sopenharmony_ci		.of_match_table = keyscan_of_match,
26362306a36Sopenharmony_ci	}
26462306a36Sopenharmony_ci};
26562306a36Sopenharmony_ci
26662306a36Sopenharmony_cimodule_platform_driver(keyscan_device_driver);
26762306a36Sopenharmony_ci
26862306a36Sopenharmony_ciMODULE_AUTHOR("Stuart Menefy <stuart.menefy@st.com>");
26962306a36Sopenharmony_ciMODULE_DESCRIPTION("STMicroelectronics keyscan device driver");
27062306a36Sopenharmony_ciMODULE_LICENSE("GPL");
271