18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Driver for keys on TCA6416 I2C IO expander 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (C) 2010 Texas Instruments 68c2ecf20Sopenharmony_ci * 78c2ecf20Sopenharmony_ci * Author : Sriramakrishnan.A.G. <srk@ti.com> 88c2ecf20Sopenharmony_ci */ 98c2ecf20Sopenharmony_ci 108c2ecf20Sopenharmony_ci#include <linux/types.h> 118c2ecf20Sopenharmony_ci#include <linux/module.h> 128c2ecf20Sopenharmony_ci#include <linux/init.h> 138c2ecf20Sopenharmony_ci#include <linux/delay.h> 148c2ecf20Sopenharmony_ci#include <linux/slab.h> 158c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 168c2ecf20Sopenharmony_ci#include <linux/workqueue.h> 178c2ecf20Sopenharmony_ci#include <linux/gpio.h> 188c2ecf20Sopenharmony_ci#include <linux/i2c.h> 198c2ecf20Sopenharmony_ci#include <linux/input.h> 208c2ecf20Sopenharmony_ci#include <linux/tca6416_keypad.h> 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_ci#define TCA6416_INPUT 0 238c2ecf20Sopenharmony_ci#define TCA6416_OUTPUT 1 248c2ecf20Sopenharmony_ci#define TCA6416_INVERT 2 258c2ecf20Sopenharmony_ci#define TCA6416_DIRECTION 3 268c2ecf20Sopenharmony_ci 278c2ecf20Sopenharmony_cistatic const struct i2c_device_id tca6416_id[] = { 288c2ecf20Sopenharmony_ci { "tca6416-keys", 16, }, 298c2ecf20Sopenharmony_ci { "tca6408-keys", 8, }, 308c2ecf20Sopenharmony_ci { } 318c2ecf20Sopenharmony_ci}; 328c2ecf20Sopenharmony_ciMODULE_DEVICE_TABLE(i2c, tca6416_id); 338c2ecf20Sopenharmony_ci 348c2ecf20Sopenharmony_cistruct tca6416_drv_data { 358c2ecf20Sopenharmony_ci struct input_dev *input; 368c2ecf20Sopenharmony_ci struct tca6416_button data[]; 378c2ecf20Sopenharmony_ci}; 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistruct tca6416_keypad_chip { 408c2ecf20Sopenharmony_ci uint16_t reg_output; 418c2ecf20Sopenharmony_ci uint16_t reg_direction; 428c2ecf20Sopenharmony_ci uint16_t reg_input; 438c2ecf20Sopenharmony_ci 448c2ecf20Sopenharmony_ci struct i2c_client *client; 458c2ecf20Sopenharmony_ci struct input_dev *input; 468c2ecf20Sopenharmony_ci struct delayed_work dwork; 478c2ecf20Sopenharmony_ci int io_size; 488c2ecf20Sopenharmony_ci int irqnum; 498c2ecf20Sopenharmony_ci u16 pinmask; 508c2ecf20Sopenharmony_ci bool use_polling; 518c2ecf20Sopenharmony_ci struct tca6416_button buttons[]; 528c2ecf20Sopenharmony_ci}; 538c2ecf20Sopenharmony_ci 548c2ecf20Sopenharmony_cistatic int tca6416_write_reg(struct tca6416_keypad_chip *chip, int reg, u16 val) 558c2ecf20Sopenharmony_ci{ 568c2ecf20Sopenharmony_ci int error; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci error = chip->io_size > 8 ? 598c2ecf20Sopenharmony_ci i2c_smbus_write_word_data(chip->client, reg << 1, val) : 608c2ecf20Sopenharmony_ci i2c_smbus_write_byte_data(chip->client, reg, val); 618c2ecf20Sopenharmony_ci if (error < 0) { 628c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, 638c2ecf20Sopenharmony_ci "%s failed, reg: %d, val: %d, error: %d\n", 648c2ecf20Sopenharmony_ci __func__, reg, val, error); 658c2ecf20Sopenharmony_ci return error; 668c2ecf20Sopenharmony_ci } 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_ci return 0; 698c2ecf20Sopenharmony_ci} 708c2ecf20Sopenharmony_ci 718c2ecf20Sopenharmony_cistatic int tca6416_read_reg(struct tca6416_keypad_chip *chip, int reg, u16 *val) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci int retval; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci retval = chip->io_size > 8 ? 768c2ecf20Sopenharmony_ci i2c_smbus_read_word_data(chip->client, reg << 1) : 778c2ecf20Sopenharmony_ci i2c_smbus_read_byte_data(chip->client, reg); 788c2ecf20Sopenharmony_ci if (retval < 0) { 798c2ecf20Sopenharmony_ci dev_err(&chip->client->dev, "%s failed, reg: %d, error: %d\n", 808c2ecf20Sopenharmony_ci __func__, reg, retval); 818c2ecf20Sopenharmony_ci return retval; 828c2ecf20Sopenharmony_ci } 838c2ecf20Sopenharmony_ci 848c2ecf20Sopenharmony_ci *val = (u16)retval; 858c2ecf20Sopenharmony_ci return 0; 868c2ecf20Sopenharmony_ci} 878c2ecf20Sopenharmony_ci 888c2ecf20Sopenharmony_cistatic void tca6416_keys_scan(struct tca6416_keypad_chip *chip) 898c2ecf20Sopenharmony_ci{ 908c2ecf20Sopenharmony_ci struct input_dev *input = chip->input; 918c2ecf20Sopenharmony_ci u16 reg_val, val; 928c2ecf20Sopenharmony_ci int error, i, pin_index; 938c2ecf20Sopenharmony_ci 948c2ecf20Sopenharmony_ci error = tca6416_read_reg(chip, TCA6416_INPUT, ®_val); 958c2ecf20Sopenharmony_ci if (error) 968c2ecf20Sopenharmony_ci return; 978c2ecf20Sopenharmony_ci 988c2ecf20Sopenharmony_ci reg_val &= chip->pinmask; 998c2ecf20Sopenharmony_ci 1008c2ecf20Sopenharmony_ci /* Figure out which lines have changed */ 1018c2ecf20Sopenharmony_ci val = reg_val ^ chip->reg_input; 1028c2ecf20Sopenharmony_ci chip->reg_input = reg_val; 1038c2ecf20Sopenharmony_ci 1048c2ecf20Sopenharmony_ci for (i = 0, pin_index = 0; i < 16; i++) { 1058c2ecf20Sopenharmony_ci if (val & (1 << i)) { 1068c2ecf20Sopenharmony_ci struct tca6416_button *button = &chip->buttons[pin_index]; 1078c2ecf20Sopenharmony_ci unsigned int type = button->type ?: EV_KEY; 1088c2ecf20Sopenharmony_ci int state = ((reg_val & (1 << i)) ? 1 : 0) 1098c2ecf20Sopenharmony_ci ^ button->active_low; 1108c2ecf20Sopenharmony_ci 1118c2ecf20Sopenharmony_ci input_event(input, type, button->code, !!state); 1128c2ecf20Sopenharmony_ci input_sync(input); 1138c2ecf20Sopenharmony_ci } 1148c2ecf20Sopenharmony_ci 1158c2ecf20Sopenharmony_ci if (chip->pinmask & (1 << i)) 1168c2ecf20Sopenharmony_ci pin_index++; 1178c2ecf20Sopenharmony_ci } 1188c2ecf20Sopenharmony_ci} 1198c2ecf20Sopenharmony_ci 1208c2ecf20Sopenharmony_ci/* 1218c2ecf20Sopenharmony_ci * This is threaded IRQ handler and this can (and will) sleep. 1228c2ecf20Sopenharmony_ci */ 1238c2ecf20Sopenharmony_cistatic irqreturn_t tca6416_keys_isr(int irq, void *dev_id) 1248c2ecf20Sopenharmony_ci{ 1258c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = dev_id; 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci tca6416_keys_scan(chip); 1288c2ecf20Sopenharmony_ci 1298c2ecf20Sopenharmony_ci return IRQ_HANDLED; 1308c2ecf20Sopenharmony_ci} 1318c2ecf20Sopenharmony_ci 1328c2ecf20Sopenharmony_cistatic void tca6416_keys_work_func(struct work_struct *work) 1338c2ecf20Sopenharmony_ci{ 1348c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = 1358c2ecf20Sopenharmony_ci container_of(work, struct tca6416_keypad_chip, dwork.work); 1368c2ecf20Sopenharmony_ci 1378c2ecf20Sopenharmony_ci tca6416_keys_scan(chip); 1388c2ecf20Sopenharmony_ci schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); 1398c2ecf20Sopenharmony_ci} 1408c2ecf20Sopenharmony_ci 1418c2ecf20Sopenharmony_cistatic int tca6416_keys_open(struct input_dev *dev) 1428c2ecf20Sopenharmony_ci{ 1438c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = input_get_drvdata(dev); 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci /* Get initial device state in case it has switches */ 1468c2ecf20Sopenharmony_ci tca6416_keys_scan(chip); 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ci if (chip->use_polling) 1498c2ecf20Sopenharmony_ci schedule_delayed_work(&chip->dwork, msecs_to_jiffies(100)); 1508c2ecf20Sopenharmony_ci else 1518c2ecf20Sopenharmony_ci enable_irq(chip->irqnum); 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci return 0; 1548c2ecf20Sopenharmony_ci} 1558c2ecf20Sopenharmony_ci 1568c2ecf20Sopenharmony_cistatic void tca6416_keys_close(struct input_dev *dev) 1578c2ecf20Sopenharmony_ci{ 1588c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = input_get_drvdata(dev); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci if (chip->use_polling) 1618c2ecf20Sopenharmony_ci cancel_delayed_work_sync(&chip->dwork); 1628c2ecf20Sopenharmony_ci else 1638c2ecf20Sopenharmony_ci disable_irq(chip->irqnum); 1648c2ecf20Sopenharmony_ci} 1658c2ecf20Sopenharmony_ci 1668c2ecf20Sopenharmony_cistatic int tca6416_setup_registers(struct tca6416_keypad_chip *chip) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci int error; 1698c2ecf20Sopenharmony_ci 1708c2ecf20Sopenharmony_ci error = tca6416_read_reg(chip, TCA6416_OUTPUT, &chip->reg_output); 1718c2ecf20Sopenharmony_ci if (error) 1728c2ecf20Sopenharmony_ci return error; 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); 1758c2ecf20Sopenharmony_ci if (error) 1768c2ecf20Sopenharmony_ci return error; 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci /* ensure that keypad pins are set to input */ 1798c2ecf20Sopenharmony_ci error = tca6416_write_reg(chip, TCA6416_DIRECTION, 1808c2ecf20Sopenharmony_ci chip->reg_direction | chip->pinmask); 1818c2ecf20Sopenharmony_ci if (error) 1828c2ecf20Sopenharmony_ci return error; 1838c2ecf20Sopenharmony_ci 1848c2ecf20Sopenharmony_ci error = tca6416_read_reg(chip, TCA6416_DIRECTION, &chip->reg_direction); 1858c2ecf20Sopenharmony_ci if (error) 1868c2ecf20Sopenharmony_ci return error; 1878c2ecf20Sopenharmony_ci 1888c2ecf20Sopenharmony_ci error = tca6416_read_reg(chip, TCA6416_INPUT, &chip->reg_input); 1898c2ecf20Sopenharmony_ci if (error) 1908c2ecf20Sopenharmony_ci return error; 1918c2ecf20Sopenharmony_ci 1928c2ecf20Sopenharmony_ci chip->reg_input &= chip->pinmask; 1938c2ecf20Sopenharmony_ci 1948c2ecf20Sopenharmony_ci return 0; 1958c2ecf20Sopenharmony_ci} 1968c2ecf20Sopenharmony_ci 1978c2ecf20Sopenharmony_cistatic int tca6416_keypad_probe(struct i2c_client *client, 1988c2ecf20Sopenharmony_ci const struct i2c_device_id *id) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci struct tca6416_keys_platform_data *pdata; 2018c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip; 2028c2ecf20Sopenharmony_ci struct input_dev *input; 2038c2ecf20Sopenharmony_ci int error; 2048c2ecf20Sopenharmony_ci int i; 2058c2ecf20Sopenharmony_ci 2068c2ecf20Sopenharmony_ci /* Check functionality */ 2078c2ecf20Sopenharmony_ci if (!i2c_check_functionality(client->adapter, I2C_FUNC_SMBUS_BYTE)) { 2088c2ecf20Sopenharmony_ci dev_err(&client->dev, "%s adapter not supported\n", 2098c2ecf20Sopenharmony_ci dev_driver_string(&client->adapter->dev)); 2108c2ecf20Sopenharmony_ci return -ENODEV; 2118c2ecf20Sopenharmony_ci } 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_ci pdata = dev_get_platdata(&client->dev); 2148c2ecf20Sopenharmony_ci if (!pdata) { 2158c2ecf20Sopenharmony_ci dev_dbg(&client->dev, "no platform data\n"); 2168c2ecf20Sopenharmony_ci return -EINVAL; 2178c2ecf20Sopenharmony_ci } 2188c2ecf20Sopenharmony_ci 2198c2ecf20Sopenharmony_ci chip = kzalloc(struct_size(chip, buttons, pdata->nbuttons), GFP_KERNEL); 2208c2ecf20Sopenharmony_ci input = input_allocate_device(); 2218c2ecf20Sopenharmony_ci if (!chip || !input) { 2228c2ecf20Sopenharmony_ci error = -ENOMEM; 2238c2ecf20Sopenharmony_ci goto fail1; 2248c2ecf20Sopenharmony_ci } 2258c2ecf20Sopenharmony_ci 2268c2ecf20Sopenharmony_ci chip->client = client; 2278c2ecf20Sopenharmony_ci chip->input = input; 2288c2ecf20Sopenharmony_ci chip->io_size = id->driver_data; 2298c2ecf20Sopenharmony_ci chip->pinmask = pdata->pinmask; 2308c2ecf20Sopenharmony_ci chip->use_polling = pdata->use_polling; 2318c2ecf20Sopenharmony_ci 2328c2ecf20Sopenharmony_ci INIT_DELAYED_WORK(&chip->dwork, tca6416_keys_work_func); 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci input->phys = "tca6416-keys/input0"; 2358c2ecf20Sopenharmony_ci input->name = client->name; 2368c2ecf20Sopenharmony_ci input->dev.parent = &client->dev; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci input->open = tca6416_keys_open; 2398c2ecf20Sopenharmony_ci input->close = tca6416_keys_close; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci input->id.bustype = BUS_HOST; 2428c2ecf20Sopenharmony_ci input->id.vendor = 0x0001; 2438c2ecf20Sopenharmony_ci input->id.product = 0x0001; 2448c2ecf20Sopenharmony_ci input->id.version = 0x0100; 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* Enable auto repeat feature of Linux input subsystem */ 2478c2ecf20Sopenharmony_ci if (pdata->rep) 2488c2ecf20Sopenharmony_ci __set_bit(EV_REP, input->evbit); 2498c2ecf20Sopenharmony_ci 2508c2ecf20Sopenharmony_ci for (i = 0; i < pdata->nbuttons; i++) { 2518c2ecf20Sopenharmony_ci unsigned int type; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci chip->buttons[i] = pdata->buttons[i]; 2548c2ecf20Sopenharmony_ci type = (pdata->buttons[i].type) ?: EV_KEY; 2558c2ecf20Sopenharmony_ci input_set_capability(input, type, pdata->buttons[i].code); 2568c2ecf20Sopenharmony_ci } 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci input_set_drvdata(input, chip); 2598c2ecf20Sopenharmony_ci 2608c2ecf20Sopenharmony_ci /* 2618c2ecf20Sopenharmony_ci * Initialize cached registers from their original values. 2628c2ecf20Sopenharmony_ci * we can't share this chip with another i2c master. 2638c2ecf20Sopenharmony_ci */ 2648c2ecf20Sopenharmony_ci error = tca6416_setup_registers(chip); 2658c2ecf20Sopenharmony_ci if (error) 2668c2ecf20Sopenharmony_ci goto fail1; 2678c2ecf20Sopenharmony_ci 2688c2ecf20Sopenharmony_ci if (!chip->use_polling) { 2698c2ecf20Sopenharmony_ci if (pdata->irq_is_gpio) 2708c2ecf20Sopenharmony_ci chip->irqnum = gpio_to_irq(client->irq); 2718c2ecf20Sopenharmony_ci else 2728c2ecf20Sopenharmony_ci chip->irqnum = client->irq; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci error = request_threaded_irq(chip->irqnum, NULL, 2758c2ecf20Sopenharmony_ci tca6416_keys_isr, 2768c2ecf20Sopenharmony_ci IRQF_TRIGGER_FALLING | 2778c2ecf20Sopenharmony_ci IRQF_ONESHOT, 2788c2ecf20Sopenharmony_ci "tca6416-keypad", chip); 2798c2ecf20Sopenharmony_ci if (error) { 2808c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 2818c2ecf20Sopenharmony_ci "Unable to claim irq %d; error %d\n", 2828c2ecf20Sopenharmony_ci chip->irqnum, error); 2838c2ecf20Sopenharmony_ci goto fail1; 2848c2ecf20Sopenharmony_ci } 2858c2ecf20Sopenharmony_ci disable_irq(chip->irqnum); 2868c2ecf20Sopenharmony_ci } 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci error = input_register_device(input); 2898c2ecf20Sopenharmony_ci if (error) { 2908c2ecf20Sopenharmony_ci dev_dbg(&client->dev, 2918c2ecf20Sopenharmony_ci "Unable to register input device, error: %d\n", error); 2928c2ecf20Sopenharmony_ci goto fail2; 2938c2ecf20Sopenharmony_ci } 2948c2ecf20Sopenharmony_ci 2958c2ecf20Sopenharmony_ci i2c_set_clientdata(client, chip); 2968c2ecf20Sopenharmony_ci device_init_wakeup(&client->dev, 1); 2978c2ecf20Sopenharmony_ci 2988c2ecf20Sopenharmony_ci return 0; 2998c2ecf20Sopenharmony_ci 3008c2ecf20Sopenharmony_cifail2: 3018c2ecf20Sopenharmony_ci if (!chip->use_polling) { 3028c2ecf20Sopenharmony_ci free_irq(chip->irqnum, chip); 3038c2ecf20Sopenharmony_ci enable_irq(chip->irqnum); 3048c2ecf20Sopenharmony_ci } 3058c2ecf20Sopenharmony_cifail1: 3068c2ecf20Sopenharmony_ci input_free_device(input); 3078c2ecf20Sopenharmony_ci kfree(chip); 3088c2ecf20Sopenharmony_ci return error; 3098c2ecf20Sopenharmony_ci} 3108c2ecf20Sopenharmony_ci 3118c2ecf20Sopenharmony_cistatic int tca6416_keypad_remove(struct i2c_client *client) 3128c2ecf20Sopenharmony_ci{ 3138c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); 3148c2ecf20Sopenharmony_ci 3158c2ecf20Sopenharmony_ci if (!chip->use_polling) { 3168c2ecf20Sopenharmony_ci free_irq(chip->irqnum, chip); 3178c2ecf20Sopenharmony_ci enable_irq(chip->irqnum); 3188c2ecf20Sopenharmony_ci } 3198c2ecf20Sopenharmony_ci 3208c2ecf20Sopenharmony_ci input_unregister_device(chip->input); 3218c2ecf20Sopenharmony_ci kfree(chip); 3228c2ecf20Sopenharmony_ci 3238c2ecf20Sopenharmony_ci return 0; 3248c2ecf20Sopenharmony_ci} 3258c2ecf20Sopenharmony_ci 3268c2ecf20Sopenharmony_ci#ifdef CONFIG_PM_SLEEP 3278c2ecf20Sopenharmony_cistatic int tca6416_keypad_suspend(struct device *dev) 3288c2ecf20Sopenharmony_ci{ 3298c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3308c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); 3318c2ecf20Sopenharmony_ci 3328c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3338c2ecf20Sopenharmony_ci enable_irq_wake(chip->irqnum); 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci return 0; 3368c2ecf20Sopenharmony_ci} 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_cistatic int tca6416_keypad_resume(struct device *dev) 3398c2ecf20Sopenharmony_ci{ 3408c2ecf20Sopenharmony_ci struct i2c_client *client = to_i2c_client(dev); 3418c2ecf20Sopenharmony_ci struct tca6416_keypad_chip *chip = i2c_get_clientdata(client); 3428c2ecf20Sopenharmony_ci 3438c2ecf20Sopenharmony_ci if (device_may_wakeup(dev)) 3448c2ecf20Sopenharmony_ci disable_irq_wake(chip->irqnum); 3458c2ecf20Sopenharmony_ci 3468c2ecf20Sopenharmony_ci return 0; 3478c2ecf20Sopenharmony_ci} 3488c2ecf20Sopenharmony_ci#endif 3498c2ecf20Sopenharmony_ci 3508c2ecf20Sopenharmony_cistatic SIMPLE_DEV_PM_OPS(tca6416_keypad_dev_pm_ops, 3518c2ecf20Sopenharmony_ci tca6416_keypad_suspend, tca6416_keypad_resume); 3528c2ecf20Sopenharmony_ci 3538c2ecf20Sopenharmony_cistatic struct i2c_driver tca6416_keypad_driver = { 3548c2ecf20Sopenharmony_ci .driver = { 3558c2ecf20Sopenharmony_ci .name = "tca6416-keypad", 3568c2ecf20Sopenharmony_ci .pm = &tca6416_keypad_dev_pm_ops, 3578c2ecf20Sopenharmony_ci }, 3588c2ecf20Sopenharmony_ci .probe = tca6416_keypad_probe, 3598c2ecf20Sopenharmony_ci .remove = tca6416_keypad_remove, 3608c2ecf20Sopenharmony_ci .id_table = tca6416_id, 3618c2ecf20Sopenharmony_ci}; 3628c2ecf20Sopenharmony_ci 3638c2ecf20Sopenharmony_cistatic int __init tca6416_keypad_init(void) 3648c2ecf20Sopenharmony_ci{ 3658c2ecf20Sopenharmony_ci return i2c_add_driver(&tca6416_keypad_driver); 3668c2ecf20Sopenharmony_ci} 3678c2ecf20Sopenharmony_ci 3688c2ecf20Sopenharmony_cisubsys_initcall(tca6416_keypad_init); 3698c2ecf20Sopenharmony_ci 3708c2ecf20Sopenharmony_cistatic void __exit tca6416_keypad_exit(void) 3718c2ecf20Sopenharmony_ci{ 3728c2ecf20Sopenharmony_ci i2c_del_driver(&tca6416_keypad_driver); 3738c2ecf20Sopenharmony_ci} 3748c2ecf20Sopenharmony_cimodule_exit(tca6416_keypad_exit); 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ciMODULE_AUTHOR("Sriramakrishnan <srk@ti.com>"); 3778c2ecf20Sopenharmony_ciMODULE_DESCRIPTION("Keypad driver over tca6416 IO expander"); 3788c2ecf20Sopenharmony_ciMODULE_LICENSE("GPL"); 379