Path: blob/master/drivers/input/keyboard/tc3589x-keypad.c
15111 views
/*1* Copyright (C) ST-Ericsson SA 20102*3* Author: Jayeeta Banerjee <[email protected]>4* Author: Sundar Iyer <[email protected]>5*6* License Terms: GNU General Public License, version 27*8* TC35893 MFD Keypad Controller driver9*/1011#include <linux/module.h>12#include <linux/init.h>13#include <linux/interrupt.h>14#include <linux/input.h>15#include <linux/platform_device.h>16#include <linux/input/matrix_keypad.h>17#include <linux/i2c.h>18#include <linux/slab.h>19#include <linux/mfd/tc3589x.h>2021/* Maximum supported keypad matrix row/columns size */22#define TC3589x_MAX_KPROW 823#define TC3589x_MAX_KPCOL 122425/* keypad related Constants */26#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF27#define DEDICATED_KEY_VAL 0xFF2829/* Pull up/down masks */30#define TC3589x_NO_PULL_MASK 0x031#define TC3589x_PULL_DOWN_MASK 0x132#define TC3589x_PULL_UP_MASK 0x233#define TC3589x_PULLUP_ALL_MASK 0xAA34#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2))3536/* Bit masks for IOCFG register */37#define IOCFG_BALLCFG 0x0138#define IOCFG_IG 0x083940#define KP_EVCODE_COL_MASK 0x0F41#define KP_EVCODE_ROW_MASK 0x7042#define KP_RELEASE_EVT_MASK 0x804344#define KP_ROW_SHIFT 44546#define KP_NO_VALID_KEY_MASK 0x7F4748/* bit masks for RESTCTRL register */49#define TC3589x_KBDRST 0x250#define TC3589x_IRQRST 0x1051#define TC3589x_RESET_ALL 0x1B5253/* KBDMFS register bit mask */54#define TC3589x_KBDMFS_EN 0x15556/* CLKEN register bitmask */57#define KPD_CLK_EN 0x15859/* RSTINTCLR register bit mask */60#define IRQ_CLEAR 0x16162/* bit masks for keyboard interrupts*/63#define TC3589x_EVT_LOSS_INT 0x864#define TC3589x_EVT_INT 0x465#define TC3589x_KBD_LOSS_INT 0x266#define TC3589x_KBD_INT 0x16768/* bit masks for keyboard interrupt clear*/69#define TC3589x_EVT_INT_CLR 0x270#define TC3589x_KBD_INT_CLR 0x17172#define TC3589x_KBD_KEYMAP_SIZE 647374/**75* struct tc_keypad - data structure used by keypad driver76* @input: pointer to input device object77* @board: keypad platform device78* @krow: number of rows79* @kcol: number of coloumns80* @keymap: matrix scan code table for keycodes81*/82struct tc_keypad {83struct tc3589x *tc3589x;84struct input_dev *input;85const struct tc3589x_keypad_platform_data *board;86unsigned int krow;87unsigned int kcol;88unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE];89bool keypad_stopped;90};9192static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad)93{94int ret;95struct tc3589x *tc3589x = keypad->tc3589x;96u8 settle_time = keypad->board->settle_time;97u8 dbounce_period = keypad->board->debounce_period;98u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */99u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */100101/* validate platform configurations */102if (keypad->board->kcol > TC3589x_MAX_KPCOL ||103keypad->board->krow > TC3589x_MAX_KPROW ||104keypad->board->debounce_period > TC3589x_MAX_DEBOUNCE_SETTLE ||105keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)106return -EINVAL;107108/* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */109ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSIZE,110(rows << KP_ROW_SHIFT) | column);111if (ret < 0)112return ret;113114/* configure dedicated key config, no dedicated key selected */115ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_LSB, DEDICATED_KEY_VAL);116if (ret < 0)117return ret;118119ret = tc3589x_reg_write(tc3589x, TC3589x_KBCFG_MSB, DEDICATED_KEY_VAL);120if (ret < 0)121return ret;122123/* Configure settle time */124ret = tc3589x_reg_write(tc3589x, TC3589x_KBDSETTLE_REG, settle_time);125if (ret < 0)126return ret;127128/* Configure debounce time */129ret = tc3589x_reg_write(tc3589x, TC3589x_KBDBOUNCE, dbounce_period);130if (ret < 0)131return ret;132133/* Start of initialise keypad GPIOs */134ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG);135if (ret < 0)136return ret;137138/* Configure pull-up resistors for all row GPIOs */139ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_LSB,140TC3589x_PULLUP_ALL_MASK);141if (ret < 0)142return ret;143144ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG0_MSB,145TC3589x_PULLUP_ALL_MASK);146if (ret < 0)147return ret;148149/* Configure pull-up resistors for all column GPIOs */150ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_LSB,151TC3589x_PULLUP_ALL_MASK);152if (ret < 0)153return ret;154155ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG1_MSB,156TC3589x_PULLUP_ALL_MASK);157if (ret < 0)158return ret;159160ret = tc3589x_reg_write(tc3589x, TC3589x_IOPULLCFG2_LSB,161TC3589x_PULLUP_ALL_MASK);162163return ret;164}165166#define TC35893_DATA_REGS 4167#define TC35893_KEYCODE_FIFO_EMPTY 0x7f168#define TC35893_KEYCODE_FIFO_CLEAR 0xff169#define TC35893_KEYPAD_ROW_SHIFT 0x3170171static irqreturn_t tc3589x_keypad_irq(int irq, void *dev)172{173struct tc_keypad *keypad = dev;174struct tc3589x *tc3589x = keypad->tc3589x;175u8 i, row_index, col_index, kbd_code, up;176u8 code;177178for (i = 0; i < TC35893_DATA_REGS * 2; i++) {179kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO);180181/* loop till fifo is empty and no more keys are pressed */182if (kbd_code == TC35893_KEYCODE_FIFO_EMPTY ||183kbd_code == TC35893_KEYCODE_FIFO_CLEAR)184continue;185186/* valid key is found */187col_index = kbd_code & KP_EVCODE_COL_MASK;188row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> KP_ROW_SHIFT;189code = MATRIX_SCAN_CODE(row_index, col_index,190TC35893_KEYPAD_ROW_SHIFT);191up = kbd_code & KP_RELEASE_EVT_MASK;192193input_event(keypad->input, EV_MSC, MSC_SCAN, code);194input_report_key(keypad->input, keypad->keymap[code], !up);195input_sync(keypad->input);196}197198/* clear IRQ */199tc3589x_set_bits(tc3589x, TC3589x_KBDIC,2000x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);201/* enable IRQ */202tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,2030x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);204205return IRQ_HANDLED;206}207208static int tc3589x_keypad_enable(struct tc_keypad *keypad)209{210struct tc3589x *tc3589x = keypad->tc3589x;211int ret;212213/* pull the keypad module out of reset */214ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x0);215if (ret < 0)216return ret;217218/* configure KBDMFS */219ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, TC3589x_KBDMFS_EN);220if (ret < 0)221return ret;222223/* enable the keypad clock */224ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN);225if (ret < 0)226return ret;227228/* clear pending IRQs */229ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1);230if (ret < 0)231return ret;232233/* enable the IRQs */234ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0,235TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT);236if (ret < 0)237return ret;238239keypad->keypad_stopped = false;240241return ret;242}243244static int tc3589x_keypad_disable(struct tc_keypad *keypad)245{246struct tc3589x *tc3589x = keypad->tc3589x;247int ret;248249/* clear IRQ */250ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC,2510x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR);252if (ret < 0)253return ret;254255/* disable all interrupts */256ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK,257~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0);258if (ret < 0)259return ret;260261/* disable the keypad module */262ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0);263if (ret < 0)264return ret;265266/* put the keypad module into reset */267ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, 0x1);268269keypad->keypad_stopped = true;270271return ret;272}273274static int tc3589x_keypad_open(struct input_dev *input)275{276int error;277struct tc_keypad *keypad = input_get_drvdata(input);278279/* enable the keypad module */280error = tc3589x_keypad_enable(keypad);281if (error < 0) {282dev_err(&input->dev, "failed to enable keypad module\n");283return error;284}285286error = tc3589x_keypad_init_key_hardware(keypad);287if (error < 0) {288dev_err(&input->dev, "failed to configure keypad module\n");289return error;290}291292return 0;293}294295static void tc3589x_keypad_close(struct input_dev *input)296{297struct tc_keypad *keypad = input_get_drvdata(input);298299/* disable the keypad module */300tc3589x_keypad_disable(keypad);301}302303static int __devinit tc3589x_keypad_probe(struct platform_device *pdev)304{305struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent);306struct tc_keypad *keypad;307struct input_dev *input;308const struct tc3589x_keypad_platform_data *plat;309int error, irq;310311plat = tc3589x->pdata->keypad;312if (!plat) {313dev_err(&pdev->dev, "invalid keypad platform data\n");314return -EINVAL;315}316317irq = platform_get_irq(pdev, 0);318if (irq < 0)319return irq;320321keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL);322input = input_allocate_device();323if (!keypad || !input) {324dev_err(&pdev->dev, "failed to allocate keypad memory\n");325error = -ENOMEM;326goto err_free_mem;327}328329keypad->board = plat;330keypad->input = input;331keypad->tc3589x = tc3589x;332333input->id.bustype = BUS_I2C;334input->name = pdev->name;335input->dev.parent = &pdev->dev;336337input->keycode = keypad->keymap;338input->keycodesize = sizeof(keypad->keymap[0]);339input->keycodemax = ARRAY_SIZE(keypad->keymap);340341input->open = tc3589x_keypad_open;342input->close = tc3589x_keypad_close;343344input_set_drvdata(input, keypad);345346input_set_capability(input, EV_MSC, MSC_SCAN);347348__set_bit(EV_KEY, input->evbit);349if (!plat->no_autorepeat)350__set_bit(EV_REP, input->evbit);351352matrix_keypad_build_keymap(plat->keymap_data, 0x3,353input->keycode, input->keybit);354355error = request_threaded_irq(irq, NULL,356tc3589x_keypad_irq, plat->irqtype,357"tc3589x-keypad", keypad);358if (error < 0) {359dev_err(&pdev->dev,360"Could not allocate irq %d,error %d\n",361irq, error);362goto err_free_mem;363}364365error = input_register_device(input);366if (error) {367dev_err(&pdev->dev, "Could not register input device\n");368goto err_free_irq;369}370371/* let platform decide if keypad is a wakeup source or not */372device_init_wakeup(&pdev->dev, plat->enable_wakeup);373device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup);374375platform_set_drvdata(pdev, keypad);376377return 0;378379err_free_irq:380free_irq(irq, keypad);381err_free_mem:382input_free_device(input);383kfree(keypad);384return error;385}386387static int __devexit tc3589x_keypad_remove(struct platform_device *pdev)388{389struct tc_keypad *keypad = platform_get_drvdata(pdev);390int irq = platform_get_irq(pdev, 0);391392if (!keypad->keypad_stopped)393tc3589x_keypad_disable(keypad);394395free_irq(irq, keypad);396397input_unregister_device(keypad->input);398399kfree(keypad);400401return 0;402}403404#ifdef CONFIG_PM_SLEEP405static int tc3589x_keypad_suspend(struct device *dev)406{407struct platform_device *pdev = to_platform_device(dev);408struct tc_keypad *keypad = platform_get_drvdata(pdev);409int irq = platform_get_irq(pdev, 0);410411/* keypad is already off; we do nothing */412if (keypad->keypad_stopped)413return 0;414415/* if device is not a wakeup source, disable it for powersave */416if (!device_may_wakeup(&pdev->dev))417tc3589x_keypad_disable(keypad);418else419enable_irq_wake(irq);420421return 0;422}423424static int tc3589x_keypad_resume(struct device *dev)425{426struct platform_device *pdev = to_platform_device(dev);427struct tc_keypad *keypad = platform_get_drvdata(pdev);428int irq = platform_get_irq(pdev, 0);429430if (!keypad->keypad_stopped)431return 0;432433/* enable the device to resume normal operations */434if (!device_may_wakeup(&pdev->dev))435tc3589x_keypad_enable(keypad);436else437disable_irq_wake(irq);438439return 0;440}441#endif442443static SIMPLE_DEV_PM_OPS(tc3589x_keypad_dev_pm_ops,444tc3589x_keypad_suspend, tc3589x_keypad_resume);445446static struct platform_driver tc3589x_keypad_driver = {447.driver = {448.name = "tc3589x-keypad",449.owner = THIS_MODULE,450.pm = &tc3589x_keypad_dev_pm_ops,451},452.probe = tc3589x_keypad_probe,453.remove = __devexit_p(tc3589x_keypad_remove),454};455456static int __init tc3589x_keypad_init(void)457{458return platform_driver_register(&tc3589x_keypad_driver);459}460module_init(tc3589x_keypad_init);461462static void __exit tc3589x_keypad_exit(void)463{464return platform_driver_unregister(&tc3589x_keypad_driver);465}466module_exit(tc3589x_keypad_exit);467468MODULE_LICENSE("GPL v2");469MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer");470MODULE_DESCRIPTION("TC35893 Keypad Driver");471MODULE_ALIAS("platform:tc3589x-keypad");472473474