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

lm8333.c (5287B)


      1// SPDX-License-Identifier: GPL-2.0-only
      2/*
      3 * LM8333 keypad driver
      4 * Copyright (C) 2012 Wolfram Sang, Pengutronix <kernel@pengutronix.de>
      5 */
      6
      7#include <linux/module.h>
      8#include <linux/slab.h>
      9#include <linux/irq.h>
     10#include <linux/i2c.h>
     11#include <linux/interrupt.h>
     12#include <linux/input/matrix_keypad.h>
     13#include <linux/input/lm8333.h>
     14
     15#define LM8333_FIFO_READ		0x20
     16#define LM8333_DEBOUNCE			0x22
     17#define LM8333_READ_INT			0xD0
     18#define LM8333_ACTIVE			0xE4
     19#define LM8333_READ_ERROR		0xF0
     20
     21#define LM8333_KEYPAD_IRQ		(1 << 0)
     22#define LM8333_ERROR_IRQ		(1 << 3)
     23
     24#define LM8333_ERROR_KEYOVR		0x04
     25#define LM8333_ERROR_FIFOOVR		0x40
     26
     27#define LM8333_FIFO_TRANSFER_SIZE	16
     28
     29#define LM8333_NUM_ROWS		8
     30#define LM8333_NUM_COLS		16
     31#define LM8333_ROW_SHIFT	4
     32
     33struct lm8333 {
     34	struct i2c_client *client;
     35	struct input_dev *input;
     36	unsigned short keycodes[LM8333_NUM_ROWS << LM8333_ROW_SHIFT];
     37};
     38
     39/* The accessors try twice because the first access may be needed for wakeup */
     40#define LM8333_READ_RETRIES 2
     41
     42int lm8333_read8(struct lm8333 *lm8333, u8 cmd)
     43{
     44	int retries = 0, ret;
     45
     46	do {
     47		ret = i2c_smbus_read_byte_data(lm8333->client, cmd);
     48	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
     49
     50	return ret;
     51}
     52
     53int lm8333_write8(struct lm8333 *lm8333, u8 cmd, u8 val)
     54{
     55	int retries = 0, ret;
     56
     57	do {
     58		ret = i2c_smbus_write_byte_data(lm8333->client, cmd, val);
     59	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
     60
     61	return ret;
     62}
     63
     64int lm8333_read_block(struct lm8333 *lm8333, u8 cmd, u8 len, u8 *buf)
     65{
     66	int retries = 0, ret;
     67
     68	do {
     69		ret = i2c_smbus_read_i2c_block_data(lm8333->client,
     70						    cmd, len, buf);
     71	} while (ret < 0 && retries++ < LM8333_READ_RETRIES);
     72
     73	return ret;
     74}
     75
     76static void lm8333_key_handler(struct lm8333 *lm8333)
     77{
     78	struct input_dev *input = lm8333->input;
     79	u8 keys[LM8333_FIFO_TRANSFER_SIZE];
     80	u8 code, pressed;
     81	int i, ret;
     82
     83	ret = lm8333_read_block(lm8333, LM8333_FIFO_READ,
     84				LM8333_FIFO_TRANSFER_SIZE, keys);
     85	if (ret != LM8333_FIFO_TRANSFER_SIZE) {
     86		dev_err(&lm8333->client->dev,
     87			"Error %d while reading FIFO\n", ret);
     88		return;
     89	}
     90
     91	for (i = 0; i < LM8333_FIFO_TRANSFER_SIZE && keys[i]; i++) {
     92		pressed = keys[i] & 0x80;
     93		code = keys[i] & 0x7f;
     94
     95		input_event(input, EV_MSC, MSC_SCAN, code);
     96		input_report_key(input, lm8333->keycodes[code], pressed);
     97	}
     98
     99	input_sync(input);
    100}
    101
    102static irqreturn_t lm8333_irq_thread(int irq, void *data)
    103{
    104	struct lm8333 *lm8333 = data;
    105	u8 status = lm8333_read8(lm8333, LM8333_READ_INT);
    106
    107	if (!status)
    108		return IRQ_NONE;
    109
    110	if (status & LM8333_ERROR_IRQ) {
    111		u8 err = lm8333_read8(lm8333, LM8333_READ_ERROR);
    112
    113		if (err & (LM8333_ERROR_KEYOVR | LM8333_ERROR_FIFOOVR)) {
    114			u8 dummy[LM8333_FIFO_TRANSFER_SIZE];
    115
    116			lm8333_read_block(lm8333, LM8333_FIFO_READ,
    117					LM8333_FIFO_TRANSFER_SIZE, dummy);
    118		}
    119		dev_err(&lm8333->client->dev, "Got error %02x\n", err);
    120	}
    121
    122	if (status & LM8333_KEYPAD_IRQ)
    123		lm8333_key_handler(lm8333);
    124
    125	return IRQ_HANDLED;
    126}
    127
    128static int lm8333_probe(struct i2c_client *client,
    129				  const struct i2c_device_id *id)
    130{
    131	const struct lm8333_platform_data *pdata =
    132			dev_get_platdata(&client->dev);
    133	struct lm8333 *lm8333;
    134	struct input_dev *input;
    135	int err, active_time;
    136
    137	if (!pdata)
    138		return -EINVAL;
    139
    140	active_time = pdata->active_time ?: 500;
    141	if (active_time / 3 <= pdata->debounce_time / 3) {
    142		dev_err(&client->dev, "Active time not big enough!\n");
    143		return -EINVAL;
    144	}
    145
    146	lm8333 = kzalloc(sizeof(*lm8333), GFP_KERNEL);
    147	input = input_allocate_device();
    148	if (!lm8333 || !input) {
    149		err = -ENOMEM;
    150		goto free_mem;
    151	}
    152
    153	lm8333->client = client;
    154	lm8333->input = input;
    155
    156	input->name = client->name;
    157	input->dev.parent = &client->dev;
    158	input->id.bustype = BUS_I2C;
    159
    160	input_set_capability(input, EV_MSC, MSC_SCAN);
    161
    162	err = matrix_keypad_build_keymap(pdata->matrix_data, NULL,
    163					 LM8333_NUM_ROWS, LM8333_NUM_COLS,
    164					 lm8333->keycodes, input);
    165	if (err)
    166		goto free_mem;
    167
    168	if (pdata->debounce_time) {
    169		err = lm8333_write8(lm8333, LM8333_DEBOUNCE,
    170				    pdata->debounce_time / 3);
    171		if (err)
    172			dev_warn(&client->dev, "Unable to set debounce time\n");
    173	}
    174
    175	if (pdata->active_time) {
    176		err = lm8333_write8(lm8333, LM8333_ACTIVE,
    177				    pdata->active_time / 3);
    178		if (err)
    179			dev_warn(&client->dev, "Unable to set active time\n");
    180	}
    181
    182	err = request_threaded_irq(client->irq, NULL, lm8333_irq_thread,
    183				   IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
    184				   "lm8333", lm8333);
    185	if (err)
    186		goto free_mem;
    187
    188	err = input_register_device(input);
    189	if (err)
    190		goto free_irq;
    191
    192	i2c_set_clientdata(client, lm8333);
    193	return 0;
    194
    195 free_irq:
    196	free_irq(client->irq, lm8333);
    197 free_mem:
    198	input_free_device(input);
    199	kfree(lm8333);
    200	return err;
    201}
    202
    203static int lm8333_remove(struct i2c_client *client)
    204{
    205	struct lm8333 *lm8333 = i2c_get_clientdata(client);
    206
    207	free_irq(client->irq, lm8333);
    208	input_unregister_device(lm8333->input);
    209	kfree(lm8333);
    210
    211	return 0;
    212}
    213
    214static const struct i2c_device_id lm8333_id[] = {
    215	{ "lm8333", 0 },
    216	{ }
    217};
    218MODULE_DEVICE_TABLE(i2c, lm8333_id);
    219
    220static struct i2c_driver lm8333_driver = {
    221	.driver = {
    222		.name		= "lm8333",
    223	},
    224	.probe		= lm8333_probe,
    225	.remove		= lm8333_remove,
    226	.id_table	= lm8333_id,
    227};
    228module_i2c_driver(lm8333_driver);
    229
    230MODULE_AUTHOR("Wolfram Sang <kernel@pengutronix.de>");
    231MODULE_DESCRIPTION("LM8333 keyboard driver");
    232MODULE_LICENSE("GPL v2");