Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

Input: add keypad driver for keys interfaced to TCA6416

This patch implements a simple Keypad driver that functions
as an I2C client. It handles key press events for keys
connected to TCA6416 I2C based IO expander.

Signed-off-by: Sriramakrishnan <srk@ti.com>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>

authored by

Sriramakrishnan Govindarajan and committed by
Dmitry Torokhov
30ba3ead 16b32e0c

+400
+16
drivers/input/keyboard/Kconfig
··· 179 179 To compile this driver as a module, choose M here: the 180 180 module will be called gpio_keys. 181 181 182 + config KEYBOARD_TCA6416 183 + tristate "TCA6416 Keypad Support" 184 + depends on I2C 185 + help 186 + This driver implements basic keypad functionality 187 + for keys connected through TCA6416 IO expander 188 + 189 + Say Y here if your device has keys connected to 190 + TCA6416 IO expander. Your board-specific setup logic 191 + must also provide pin-mask details(of which TCA6416 pins 192 + are used for keypad). 193 + 194 + If enabled the complete TCA6416 device will be managed through 195 + this driver. 196 + 197 + 182 198 config KEYBOARD_MATRIX 183 199 tristate "GPIO driven matrix keypad support" 184 200 depends on GENERIC_GPIO
+1
drivers/input/keyboard/Makefile
··· 14 14 obj-$(CONFIG_KEYBOARD_DAVINCI) += davinci_keyscan.o 15 15 obj-$(CONFIG_KEYBOARD_EP93XX) += ep93xx_keypad.o 16 16 obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o 17 + obj-$(CONFIG_KEYBOARD_TCA6416) += tca6416-keypad.o 17 18 obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o 18 19 obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o 19 20 obj-$(CONFIG_KEYBOARD_IMX) += imx_keypad.o
+349
drivers/input/keyboard/tca6416-keypad.c
··· 1 + /* 2 + * Driver for keys on TCA6416 I2C IO expander 3 + * 4 + * Copyright (C) 2010 Texas Instruments 5 + * 6 + * Author : Sriramakrishnan.A.G. <srk@ti.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #include <linux/types.h> 14 + #include <linux/module.h> 15 + #include <linux/init.h> 16 + #include <linux/delay.h> 17 + #include <linux/slab.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/workqueue.h> 20 + #include <linux/gpio.h> 21 + #include <linux/i2c.h> 22 + #include <linux/input.h> 23 + #include <linux/tca6416_keypad.h> 24 + 25 + #define TCA6416_INPUT 0 26 + #define TCA6416_OUTPUT 1 27 + #define TCA6416_INVERT 2 28 + #define TCA6416_DIRECTION 3 29 + 30 + static const struct i2c_device_id tca6416_id[] = { 31 + { "tca6416-keys", 16, }, 32 + { } 33 + }; 34 + MODULE_DEVICE_TABLE(i2c, tca6416_id); 35 + 36 + struct tca6416_drv_data { 37 + struct input_dev *input; 38 + struct tca6416_button data[0]; 39 + }; 40 + 41 + struct tca6416_keypad_chip { 42 + uint16_t reg_output; 43 + uint16_t reg_direction; 44 + uint16_t reg_input; 45 + 46 + struct i2c_client *client; 47 + struct input_dev *input; 48 + struct delayed_work dwork; 49 + u16 pinmask; 50 + int irqnum; 51 + bool use_polling; 52 + struct tca6416_button buttons[0]; 53 + }; 54 + 55 + static int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) 56 + { 57 + int error; 58 + 59 + error = i2c_smbus_write_word_data(chip->client, reg << 1, val); 60 + if (error < 0) { 61 + dev_err(&chip->client->dev, 62 + "%s failed, reg: %d, val: %d, error: %d\n", 63 + __func__, reg, val, error); 64 + return error; 65 + } 66 + 67 + return 0; 68 + } 69 + 70 + static int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) 71 + { 72 + int retval; 73 + 74 + retval = i2c_smbus_read_word_data(chip->client, reg << 1); 75 + if (retval < 0) { 76 + dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", 77 + __func__, reg, retval); 78 + return retval; 79 + } 80 + 81 + *val = (u16)retval; 82 + return 0; 83 + } 84 + 85 + static void tca6416_keys_scan(struct tca6416_keypad_chip *chip) 86 + { 87 + struct input_dev *input = chip->input; 88 + u16 reg_val, val; 89 + int error, i, pin_index; 90 + 91 + error = tca6416_read_reg(chip, TCA6416_INPUT, &reg_val); 92 + if (error) 93 + return; 94 + 95 + reg_val &= chip->pinmask; 96 + 97 + /* Figure out which lines have changed */ 98 + val = reg_val ^ chip->reg_input; 99 + chip->reg_input = reg_val; 100 + 101 + for (i = 0, pin_index = 0; i < 16; i++) { 102 + if (val & (1 << i)) { 103 + struct tca6416_button *button = &chip->buttons[pin_index]; 104 + unsigned int type = button->type ?: EV_KEY; 105 + int state = ((reg_val & (1 << i)) ? 1 : 0) 106 + ^ button->active_low; 107 + 108 + input_event(input, type, button->code, !!state); 109 + input_sync(input); 110 + } 111 + 112 + if (chip->pinmask & (1 << i)) 113 + pin_index++; 114 + } 115 + } 116 + 117 + /* 118 + * This is threaded IRQ handler and this can (and will) sleep. 119 + */ 120 + static irqreturn_t tca6416_keys_isr(int irq, void *dev_id) 121 + { 122 + struct tca6416_keypad_chip *chip = dev_id; 123 + 124 + tca6416_keys_scan(chip); 125 + 126 + return IRQ_HANDLED; 127 + } 128 + 129 + static void tca6416_keys_work_func(struct work_struct *work) 130 + { 131 + struct tca6416_keypad_chip *chip = 132 + container_of(work, struct tca6416_keypad_chip, dwork.work); 133 + 134 + tca6416_keys_scan(chip); 135 + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); 136 + } 137 + 138 + static int tca6416_keys_open(struct input_dev *dev) 139 + { 140 + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); 141 + 142 + /* Get initial device state in case it has switches */ 143 + tca6416_keys_scan(chip); 144 + 145 + if (chip->use_polling) 146 + schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); 147 + else 148 + enable_irq(chip->irqnum); 149 + 150 + return 0; 151 + } 152 + 153 + static void tca6416_keys_close(struct input_dev *dev) 154 + { 155 + struct tca6416_keypad_chip *chip = input_get_drvdata(dev); 156 + 157 + if (chip->use_polling) 158 + cancel_delayed_work_sync(&chip->dwork); 159 + else 160 + disable_irq(chip->irqnum); 161 + } 162 + 163 + static int __devinit tca6416_setup_registers(struct tca6416_keypad_chip *chip) 164 + { 165 + int error; 166 + 167 + error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); 168 + if (error) 169 + return error; 170 + 171 + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); 172 + if (error) 173 + return error; 174 + 175 + /* ensure that keypad pins are set to input */ 176 + error = tca6416_write_reg(chip, TCA6416_DIRECTION, 177 + chip->reg_direction | chip->pinmask); 178 + if (error) 179 + return error; 180 + 181 + error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); 182 + if (error) 183 + return error; 184 + 185 + error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); 186 + if (error) 187 + return error; 188 + 189 + chip->reg_input &= chip->pinmask; 190 + 191 + return 0; 192 + } 193 + 194 + static int __devinit tca6416_keypad_probe(struct i2c_client *client, 195 + const struct i2c_device_id *id) 196 + { 197 + struct tca6416_keys_platform_data *pdata; 198 + struct tca6416_keypad_chip *chip; 199 + struct input_dev *input; 200 + int error; 201 + int i; 202 + 203 + /* Check functionality */ 204 + if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { 205 + dev_err(&client->dev, "%s adapter not supported\n", 206 + dev_driver_string(&client->adapter->dev)); 207 + return -ENODEV; 208 + } 209 + 210 + pdata = client->dev.platform_data; 211 + if (!pdata) { 212 + dev_dbg(&client->dev, "no platform data\n"); 213 + return -EINVAL; 214 + } 215 + 216 + chip = kzalloc(sizeof(struct tca6416_keypad_chip) + 217 + pdata->nbuttons * sizeof(struct tca6416_button), 218 + GFP_KERNEL); 219 + input = input_allocate_device(); 220 + if (!chip || !input) { 221 + error = -ENOMEM; 222 + goto fail1; 223 + } 224 + 225 + chip->client = client; 226 + chip->input = input; 227 + chip->pinmask = pdata->pinmask; 228 + chip->use_polling = pdata->use_polling; 229 + 230 + INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); 231 + 232 + input->phys = "tca6416-keys/input0"; 233 + input->name = client->name; 234 + input->dev.parent = &client->dev; 235 + 236 + input->open = tca6416_keys_open; 237 + input->close = tca6416_keys_close; 238 + 239 + input->id.bustype = BUS_HOST; 240 + input->id.vendor = 0x0001; 241 + input->id.product = 0x0001; 242 + input->id.version = 0x0100; 243 + 244 + /* Enable auto repeat feature of Linux input subsystem */ 245 + if (pdata->rep) 246 + __set_bit(EV_REP, input->evbit); 247 + 248 + for (i = 0; i < pdata->nbuttons; i++) { 249 + unsigned int type; 250 + 251 + chip->buttons[i] = pdata->buttons[i]; 252 + type = (pdata->buttons[i].type) ?: EV_KEY; 253 + input_set_capability(input, type, pdata->buttons[i].code); 254 + } 255 + 256 + input_set_drvdata(input, chip); 257 + 258 + /* 259 + * Initialize cached registers from their original values. 260 + * we can't share this chip with another i2c master. 261 + */ 262 + error = tca6416_setup_registers(chip); 263 + if (error) 264 + goto fail1; 265 + 266 + if (!chip->use_polling) { 267 + if (pdata->irq_is_gpio) 268 + chip->irqnum = gpio_to_irq(client->irq); 269 + else 270 + chip->irqnum = client->irq; 271 + 272 + error = request_threaded_irq(chip->irqnum, NULL, 273 + tca6416_keys_isr, 274 + IRQF_TRIGGER_FALLING, 275 + "tca6416-keypad", chip); 276 + if (error) { 277 + dev_dbg(&client->dev, 278 + "Unable to claim irq %d; error %d\n", 279 + chip->irqnum, error); 280 + goto fail1; 281 + } 282 + disable_irq(chip->irqnum); 283 + } 284 + 285 + error = input_register_device(input); 286 + if (error) { 287 + dev_dbg(&client->dev, 288 + "Unable to register input device, error: %d\n", error); 289 + goto fail2; 290 + } 291 + 292 + i2c_set_clientdata(client, chip); 293 + 294 + return 0; 295 + 296 + fail2: 297 + if (!chip->use_polling) { 298 + free_irq(chip->irqnum, chip); 299 + enable_irq(chip->irqnum); 300 + } 301 + fail1: 302 + input_free_device(input); 303 + kfree(chip); 304 + return error; 305 + } 306 + 307 + static int __devexit tca6416_keypad_remove(struct i2c_client *client) 308 + { 309 + struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); 310 + 311 + if (!chip->use_polling) { 312 + free_irq(chip->irqnum, chip); 313 + enable_irq(chip->irqnum); 314 + } 315 + 316 + input_unregister_device(chip->input); 317 + kfree(chip); 318 + 319 + i2c_set_clientdata(client, NULL); 320 + 321 + return 0; 322 + } 323 + 324 + 325 + static struct i2c_driver tca6416_keypad_driver = { 326 + .driver = { 327 + .name = "tca6416-keypad", 328 + }, 329 + .probe = tca6416_keypad_probe, 330 + .remove = __devexit_p(tca6416_keypad_remove), 331 + .id_table = tca6416_id, 332 + }; 333 + 334 + static int __init tca6416_keypad_init(void) 335 + { 336 + return i2c_add_driver(&tca6416_keypad_driver); 337 + } 338 + 339 + subsys_initcall(tca6416_keypad_init); 340 + 341 + static void __exit tca6416_keypad_exit(void) 342 + { 343 + i2c_del_driver(&tca6416_keypad_driver); 344 + } 345 + module_exit(tca6416_keypad_exit); 346 + 347 + MODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); 348 + MODULE_DESCRIPTION("Keypad driver over tca6146 IO expander"); 349 + MODULE_LICENSE("GPL");
+34
include/linux/tca6416_keypad.h
··· 1 + /* 2 + * tca6416 keypad platform support 3 + * 4 + * Copyright (C) 2010 Texas Instruments 5 + * 6 + * Author: Sriramakrishnan <srk@ti.com> 7 + * 8 + * This program is free software; you can redistribute it and/or modify 9 + * it under the terms of the GNU General Public License version 2 as 10 + * published by the Free Software Foundation. 11 + */ 12 + 13 + #ifndef _TCA6416_KEYS_H 14 + #define _TCA6416_KEYS_H 15 + 16 + #include <linux/types.h> 17 + 18 + struct tca6416_button { 19 + /* Configuration parameters */ 20 + int code; /* input event code (KEY_*, SW_*) */ 21 + int active_low; 22 + int type; /* input event type (EV_KEY, EV_SW) */ 23 + }; 24 + 25 + struct tca6416_keys_platform_data { 26 + struct tca6416_button *buttons; 27 + int nbuttons; 28 + unsigned int rep:1; /* enable input subsystem auto repeat */ 29 + uint16_t pinmask; 30 + uint16_t invert; 31 + int irq_is_gpio; 32 + int use_polling; /* use polling if Interrupt is not connected*/ 33 + }; 34 + #endif