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

lpc32xx-keys.c (8654B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * NXP LPC32xx SoC Key Scan Interface
      4 *
      5 * Authors:
      6 *    Kevin Wells <kevin.wells@nxp.com>
      7 *    Roland Stigge <stigge@antcom.de>
      8 *
      9 * Copyright (C) 2010 NXP Semiconductors
     10 * Copyright (C) 2012 Roland Stigge
     11 *
     12 * This controller supports square key matrices from 1x1 up to 8x8
     13 */
     14
     15#include <linux/module.h>
     16#include <linux/interrupt.h>
     17#include <linux/slab.h>
     18#include <linux/irq.h>
     19#include <linux/pm.h>
     20#include <linux/platform_device.h>
     21#include <linux/input.h>
     22#include <linux/clk.h>
     23#include <linux/io.h>
     24#include <linux/of.h>
     25#include <linux/input/matrix_keypad.h>
     26
     27#define DRV_NAME				"lpc32xx_keys"
     28
     29/*
     30 * Key scanner register offsets
     31 */
     32#define LPC32XX_KS_DEB(x)			((x) + 0x00)
     33#define LPC32XX_KS_STATE_COND(x)		((x) + 0x04)
     34#define LPC32XX_KS_IRQ(x)			((x) + 0x08)
     35#define LPC32XX_KS_SCAN_CTL(x)			((x) + 0x0C)
     36#define LPC32XX_KS_FAST_TST(x)			((x) + 0x10)
     37#define LPC32XX_KS_MATRIX_DIM(x)		((x) + 0x14) /* 1..8 */
     38#define LPC32XX_KS_DATA(x, y)			((x) + 0x40 + ((y) << 2))
     39
     40#define LPC32XX_KSCAN_DEB_NUM_DEB_PASS(n)	((n) & 0xFF)
     41
     42#define LPC32XX_KSCAN_SCOND_IN_IDLE		0x0
     43#define LPC32XX_KSCAN_SCOND_IN_SCANONCE		0x1
     44#define LPC32XX_KSCAN_SCOND_IN_IRQGEN		0x2
     45#define LPC32XX_KSCAN_SCOND_IN_SCAN_MATRIX	0x3
     46
     47#define LPC32XX_KSCAN_IRQ_PENDING_CLR		0x1
     48
     49#define LPC32XX_KSCAN_SCTRL_SCAN_DELAY(n)	((n) & 0xFF)
     50
     51#define LPC32XX_KSCAN_FTST_FORCESCANONCE	0x1
     52#define LPC32XX_KSCAN_FTST_USE32K_CLK		0x2
     53
     54#define LPC32XX_KSCAN_MSEL_SELECT(n)		((n) & 0xF)
     55
     56struct lpc32xx_kscan_drv {
     57	struct input_dev *input;
     58	struct clk *clk;
     59	void __iomem *kscan_base;
     60	unsigned int irq;
     61
     62	u32 matrix_sz;		/* Size of matrix in XxY, ie. 3 = 3x3 */
     63	u32 deb_clks;		/* Debounce clocks (based on 32KHz clock) */
     64	u32 scan_delay;		/* Scan delay (based on 32KHz clock) */
     65
     66	unsigned short *keymap;	/* Pointer to key map for the scan matrix */
     67	unsigned int row_shift;
     68
     69	u8 lastkeystates[8];
     70};
     71
     72static void lpc32xx_mod_states(struct lpc32xx_kscan_drv *kscandat, int col)
     73{
     74	struct input_dev *input = kscandat->input;
     75	unsigned row, changed, scancode, keycode;
     76	u8 key;
     77
     78	key = readl(LPC32XX_KS_DATA(kscandat->kscan_base, col));
     79	changed = key ^ kscandat->lastkeystates[col];
     80	kscandat->lastkeystates[col] = key;
     81
     82	for (row = 0; changed; row++, changed >>= 1) {
     83		if (changed & 1) {
     84			/* Key state changed, signal an event */
     85			scancode = MATRIX_SCAN_CODE(row, col,
     86						    kscandat->row_shift);
     87			keycode = kscandat->keymap[scancode];
     88			input_event(input, EV_MSC, MSC_SCAN, scancode);
     89			input_report_key(input, keycode, key & (1 << row));
     90		}
     91	}
     92}
     93
     94static irqreturn_t lpc32xx_kscan_irq(int irq, void *dev_id)
     95{
     96	struct lpc32xx_kscan_drv *kscandat = dev_id;
     97	int i;
     98
     99	for (i = 0; i < kscandat->matrix_sz; i++)
    100		lpc32xx_mod_states(kscandat, i);
    101
    102	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    103
    104	input_sync(kscandat->input);
    105
    106	return IRQ_HANDLED;
    107}
    108
    109static int lpc32xx_kscan_open(struct input_dev *dev)
    110{
    111	struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
    112	int error;
    113
    114	error = clk_prepare_enable(kscandat->clk);
    115	if (error)
    116		return error;
    117
    118	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    119
    120	return 0;
    121}
    122
    123static void lpc32xx_kscan_close(struct input_dev *dev)
    124{
    125	struct lpc32xx_kscan_drv *kscandat = input_get_drvdata(dev);
    126
    127	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    128	clk_disable_unprepare(kscandat->clk);
    129}
    130
    131static int lpc32xx_parse_dt(struct device *dev,
    132				      struct lpc32xx_kscan_drv *kscandat)
    133{
    134	struct device_node *np = dev->of_node;
    135	u32 rows = 0, columns = 0;
    136	int err;
    137
    138	err = matrix_keypad_parse_properties(dev, &rows, &columns);
    139	if (err)
    140		return err;
    141	if (rows != columns) {
    142		dev_err(dev, "rows and columns must be equal!\n");
    143		return -EINVAL;
    144	}
    145
    146	kscandat->matrix_sz = rows;
    147	kscandat->row_shift = get_count_order(columns);
    148
    149	of_property_read_u32(np, "nxp,debounce-delay-ms", &kscandat->deb_clks);
    150	of_property_read_u32(np, "nxp,scan-delay-ms", &kscandat->scan_delay);
    151	if (!kscandat->deb_clks || !kscandat->scan_delay) {
    152		dev_err(dev, "debounce or scan delay not specified\n");
    153		return -EINVAL;
    154	}
    155
    156	return 0;
    157}
    158
    159static int lpc32xx_kscan_probe(struct platform_device *pdev)
    160{
    161	struct lpc32xx_kscan_drv *kscandat;
    162	struct input_dev *input;
    163	struct resource *res;
    164	size_t keymap_size;
    165	int error;
    166	int irq;
    167
    168	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
    169	if (!res) {
    170		dev_err(&pdev->dev, "failed to get platform I/O memory\n");
    171		return -EINVAL;
    172	}
    173
    174	irq = platform_get_irq(pdev, 0);
    175	if (irq < 0)
    176		return -EINVAL;
    177
    178	kscandat = devm_kzalloc(&pdev->dev, sizeof(*kscandat),
    179				GFP_KERNEL);
    180	if (!kscandat)
    181		return -ENOMEM;
    182
    183	error = lpc32xx_parse_dt(&pdev->dev, kscandat);
    184	if (error) {
    185		dev_err(&pdev->dev, "failed to parse device tree\n");
    186		return error;
    187	}
    188
    189	keymap_size = sizeof(kscandat->keymap[0]) *
    190				(kscandat->matrix_sz << kscandat->row_shift);
    191	kscandat->keymap = devm_kzalloc(&pdev->dev, keymap_size, GFP_KERNEL);
    192	if (!kscandat->keymap)
    193		return -ENOMEM;
    194
    195	kscandat->input = input = devm_input_allocate_device(&pdev->dev);
    196	if (!input) {
    197		dev_err(&pdev->dev, "failed to allocate input device\n");
    198		return -ENOMEM;
    199	}
    200
    201	/* Setup key input */
    202	input->name		= pdev->name;
    203	input->phys		= "lpc32xx/input0";
    204	input->id.vendor	= 0x0001;
    205	input->id.product	= 0x0001;
    206	input->id.version	= 0x0100;
    207	input->open		= lpc32xx_kscan_open;
    208	input->close		= lpc32xx_kscan_close;
    209	input->dev.parent	= &pdev->dev;
    210
    211	input_set_capability(input, EV_MSC, MSC_SCAN);
    212
    213	error = matrix_keypad_build_keymap(NULL, NULL,
    214					   kscandat->matrix_sz,
    215					   kscandat->matrix_sz,
    216					   kscandat->keymap, kscandat->input);
    217	if (error) {
    218		dev_err(&pdev->dev, "failed to build keymap\n");
    219		return error;
    220	}
    221
    222	input_set_drvdata(kscandat->input, kscandat);
    223
    224	kscandat->kscan_base = devm_ioremap_resource(&pdev->dev, res);
    225	if (IS_ERR(kscandat->kscan_base))
    226		return PTR_ERR(kscandat->kscan_base);
    227
    228	/* Get the key scanner clock */
    229	kscandat->clk = devm_clk_get(&pdev->dev, NULL);
    230	if (IS_ERR(kscandat->clk)) {
    231		dev_err(&pdev->dev, "failed to get clock\n");
    232		return PTR_ERR(kscandat->clk);
    233	}
    234
    235	/* Configure the key scanner */
    236	error = clk_prepare_enable(kscandat->clk);
    237	if (error)
    238		return error;
    239
    240	writel(kscandat->deb_clks, LPC32XX_KS_DEB(kscandat->kscan_base));
    241	writel(kscandat->scan_delay, LPC32XX_KS_SCAN_CTL(kscandat->kscan_base));
    242	writel(LPC32XX_KSCAN_FTST_USE32K_CLK,
    243	       LPC32XX_KS_FAST_TST(kscandat->kscan_base));
    244	writel(kscandat->matrix_sz,
    245	       LPC32XX_KS_MATRIX_DIM(kscandat->kscan_base));
    246	writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    247	clk_disable_unprepare(kscandat->clk);
    248
    249	error = devm_request_irq(&pdev->dev, irq, lpc32xx_kscan_irq, 0,
    250				 pdev->name, kscandat);
    251	if (error) {
    252		dev_err(&pdev->dev, "failed to request irq\n");
    253		return error;
    254	}
    255
    256	error = input_register_device(kscandat->input);
    257	if (error) {
    258		dev_err(&pdev->dev, "failed to register input device\n");
    259		return error;
    260	}
    261
    262	platform_set_drvdata(pdev, kscandat);
    263
    264	return 0;
    265}
    266
    267#ifdef CONFIG_PM_SLEEP
    268static int lpc32xx_kscan_suspend(struct device *dev)
    269{
    270	struct platform_device *pdev = to_platform_device(dev);
    271	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
    272	struct input_dev *input = kscandat->input;
    273
    274	mutex_lock(&input->mutex);
    275
    276	if (input_device_enabled(input)) {
    277		/* Clear IRQ and disable clock */
    278		writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    279		clk_disable_unprepare(kscandat->clk);
    280	}
    281
    282	mutex_unlock(&input->mutex);
    283	return 0;
    284}
    285
    286static int lpc32xx_kscan_resume(struct device *dev)
    287{
    288	struct platform_device *pdev = to_platform_device(dev);
    289	struct lpc32xx_kscan_drv *kscandat = platform_get_drvdata(pdev);
    290	struct input_dev *input = kscandat->input;
    291	int retval = 0;
    292
    293	mutex_lock(&input->mutex);
    294
    295	if (input_device_enabled(input)) {
    296		/* Enable clock and clear IRQ */
    297		retval = clk_prepare_enable(kscandat->clk);
    298		if (retval == 0)
    299			writel(1, LPC32XX_KS_IRQ(kscandat->kscan_base));
    300	}
    301
    302	mutex_unlock(&input->mutex);
    303	return retval;
    304}
    305#endif
    306
    307static SIMPLE_DEV_PM_OPS(lpc32xx_kscan_pm_ops, lpc32xx_kscan_suspend,
    308			 lpc32xx_kscan_resume);
    309
    310static const struct of_device_id lpc32xx_kscan_match[] = {
    311	{ .compatible = "nxp,lpc3220-key" },
    312	{},
    313};
    314MODULE_DEVICE_TABLE(of, lpc32xx_kscan_match);
    315
    316static struct platform_driver lpc32xx_kscan_driver = {
    317	.probe		= lpc32xx_kscan_probe,
    318	.driver		= {
    319		.name	= DRV_NAME,
    320		.pm	= &lpc32xx_kscan_pm_ops,
    321		.of_match_table = lpc32xx_kscan_match,
    322	}
    323};
    324
    325module_platform_driver(lpc32xx_kscan_driver);
    326
    327MODULE_LICENSE("GPL");
    328MODULE_AUTHOR("Kevin Wells <kevin.wells@nxp.com>");
    329MODULE_AUTHOR("Roland Stigge <stigge@antcom.de>");
    330MODULE_DESCRIPTION("Key scanner driver for LPC32XX devices");