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");