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