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

pcf8574_keypad.c (4659B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/*
      3 * Driver for a keypad w/16 buttons connected to a PCF8574 I2C I/O expander
      4 *
      5 * Copyright 2005-2008 Analog Devices Inc.
      6 */
      7
      8#include <linux/module.h>
      9#include <linux/input.h>
     10#include <linux/interrupt.h>
     11#include <linux/i2c.h>
     12#include <linux/slab.h>
     13#include <linux/workqueue.h>
     14
     15#define DRV_NAME "pcf8574_keypad"
     16
     17static const unsigned char pcf8574_kp_btncode[] = {
     18	[0] = KEY_RESERVED,
     19	[1] = KEY_ENTER,
     20	[2] = KEY_BACKSLASH,
     21	[3] = KEY_0,
     22	[4] = KEY_RIGHTBRACE,
     23	[5] = KEY_C,
     24	[6] = KEY_9,
     25	[7] = KEY_8,
     26	[8] = KEY_7,
     27	[9] = KEY_B,
     28	[10] = KEY_6,
     29	[11] = KEY_5,
     30	[12] = KEY_4,
     31	[13] = KEY_A,
     32	[14] = KEY_3,
     33	[15] = KEY_2,
     34	[16] = KEY_1
     35};
     36
     37struct kp_data {
     38	unsigned short btncode[ARRAY_SIZE(pcf8574_kp_btncode)];
     39	struct input_dev *idev;
     40	struct i2c_client *client;
     41	char name[64];
     42	char phys[32];
     43	unsigned char laststate;
     44};
     45
     46static short read_state(struct kp_data *lp)
     47{
     48	unsigned char x, y, a, b;
     49
     50	i2c_smbus_write_byte(lp->client, 240);
     51	x = 0xF & (~(i2c_smbus_read_byte(lp->client) >> 4));
     52
     53	i2c_smbus_write_byte(lp->client, 15);
     54	y = 0xF & (~i2c_smbus_read_byte(lp->client));
     55
     56	for (a = 0; x > 0; a++)
     57		x = x >> 1;
     58	for (b = 0; y > 0; b++)
     59		y = y >> 1;
     60
     61	return ((a - 1) * 4) + b;
     62}
     63
     64static irqreturn_t pcf8574_kp_irq_handler(int irq, void *dev_id)
     65{
     66	struct kp_data *lp = dev_id;
     67	unsigned char nextstate = read_state(lp);
     68
     69	if (lp->laststate != nextstate) {
     70		int key_down = nextstate < ARRAY_SIZE(lp->btncode);
     71		unsigned short keycode = key_down ?
     72			lp->btncode[nextstate] : lp->btncode[lp->laststate];
     73
     74		input_report_key(lp->idev, keycode, key_down);
     75		input_sync(lp->idev);
     76
     77		lp->laststate = nextstate;
     78	}
     79
     80	return IRQ_HANDLED;
     81}
     82
     83static int pcf8574_kp_probe(struct i2c_client *client, const struct i2c_device_id *id)
     84{
     85	int i, ret;
     86	struct input_dev *idev;
     87	struct kp_data *lp;
     88
     89	if (i2c_smbus_write_byte(client, 240) < 0) {
     90		dev_err(&client->dev, "probe: write fail\n");
     91		return -ENODEV;
     92	}
     93
     94	lp = kzalloc(sizeof(*lp), GFP_KERNEL);
     95	if (!lp)
     96		return -ENOMEM;
     97
     98	idev = input_allocate_device();
     99	if (!idev) {
    100		dev_err(&client->dev, "Can't allocate input device\n");
    101		ret = -ENOMEM;
    102		goto fail_allocate;
    103	}
    104
    105	lp->idev = idev;
    106	lp->client = client;
    107
    108	idev->evbit[0] = BIT_MASK(EV_KEY);
    109	idev->keycode = lp->btncode;
    110	idev->keycodesize = sizeof(lp->btncode[0]);
    111	idev->keycodemax = ARRAY_SIZE(lp->btncode);
    112
    113	for (i = 0; i < ARRAY_SIZE(pcf8574_kp_btncode); i++) {
    114		if (lp->btncode[i] <= KEY_MAX) {
    115			lp->btncode[i] = pcf8574_kp_btncode[i];
    116			__set_bit(lp->btncode[i], idev->keybit);
    117		}
    118	}
    119	__clear_bit(KEY_RESERVED, idev->keybit);
    120
    121	sprintf(lp->name, DRV_NAME);
    122	sprintf(lp->phys, "kp_data/input0");
    123
    124	idev->name = lp->name;
    125	idev->phys = lp->phys;
    126	idev->id.bustype = BUS_I2C;
    127	idev->id.vendor = 0x0001;
    128	idev->id.product = 0x0001;
    129	idev->id.version = 0x0100;
    130
    131	lp->laststate = read_state(lp);
    132
    133	ret = request_threaded_irq(client->irq, NULL, pcf8574_kp_irq_handler,
    134				   IRQF_TRIGGER_LOW | IRQF_ONESHOT,
    135				   DRV_NAME, lp);
    136	if (ret) {
    137		dev_err(&client->dev, "IRQ %d is not free\n", client->irq);
    138		goto fail_free_device;
    139	}
    140
    141	ret = input_register_device(idev);
    142	if (ret) {
    143		dev_err(&client->dev, "input_register_device() failed\n");
    144		goto fail_free_irq;
    145	}
    146
    147	i2c_set_clientdata(client, lp);
    148	return 0;
    149
    150 fail_free_irq:
    151	free_irq(client->irq, lp);
    152 fail_free_device:
    153	input_free_device(idev);
    154 fail_allocate:
    155	kfree(lp);
    156
    157	return ret;
    158}
    159
    160static int pcf8574_kp_remove(struct i2c_client *client)
    161{
    162	struct kp_data *lp = i2c_get_clientdata(client);
    163
    164	free_irq(client->irq, lp);
    165
    166	input_unregister_device(lp->idev);
    167	kfree(lp);
    168
    169	return 0;
    170}
    171
    172#ifdef CONFIG_PM
    173static int pcf8574_kp_resume(struct device *dev)
    174{
    175	struct i2c_client *client = to_i2c_client(dev);
    176
    177	enable_irq(client->irq);
    178
    179	return 0;
    180}
    181
    182static int pcf8574_kp_suspend(struct device *dev)
    183{
    184	struct i2c_client *client = to_i2c_client(dev);
    185
    186	disable_irq(client->irq);
    187
    188	return 0;
    189}
    190
    191static const struct dev_pm_ops pcf8574_kp_pm_ops = {
    192	.suspend	= pcf8574_kp_suspend,
    193	.resume		= pcf8574_kp_resume,
    194};
    195
    196#else
    197# define pcf8574_kp_resume  NULL
    198# define pcf8574_kp_suspend NULL
    199#endif
    200
    201static const struct i2c_device_id pcf8574_kp_id[] = {
    202	{ DRV_NAME, 0 },
    203	{ }
    204};
    205MODULE_DEVICE_TABLE(i2c, pcf8574_kp_id);
    206
    207static struct i2c_driver pcf8574_kp_driver = {
    208	.driver = {
    209		.name  = DRV_NAME,
    210#ifdef CONFIG_PM
    211		.pm = &pcf8574_kp_pm_ops,
    212#endif
    213	},
    214	.probe    = pcf8574_kp_probe,
    215	.remove   = pcf8574_kp_remove,
    216	.id_table = pcf8574_kp_id,
    217};
    218
    219module_i2c_driver(pcf8574_kp_driver);
    220
    221MODULE_AUTHOR("Michael Hennerich");
    222MODULE_DESCRIPTION("Keypad input driver for 16 keys connected to PCF8574");
    223MODULE_LICENSE("GPL");