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