Hi Ken/Dmitry and all, This is the preliminary patch which I have got the TC35893 keypad working plugged in as a MFD client. The MFD functions which are tc3589x_* are a find-replace for the tc35892 functions in the existing MFD driver. Also added are a few definitions for the keypad platform data. I (realize now :() will also send out this patch too for reference. This is just a as-is driver migrated from our internal driver for the TC35893 standalone keypad driver; I may have missed removing redundant and legacy code yet; but this is for Ken and Co. as a TC35893(x) plugged into the MFD. Cheers! Sundar >-----Original Message----- >From: Sundar R IYER >Sent: Friday, November 26, 2010 8:35 PM >To: ken.lierman@xxxxxxxxxxxxx >Cc: dmitry.torokhov@xxxxxxxxx; alan@xxxxxxxxxxxxxxx; Linus WALLEIJ; Rabin >VINCENT; linux-input@xxxxxxxxxxxxxxx; Sundar R IYER >Subject: [PATCH] input/tc3589x: add support for tc3589x driver > >This adds support for the TC35893 keypad controller >as a MFD client to the TC3589x driver > >Signed-off-by: Sundar Iyer <sundar.iyer@xxxxxxxxxxxxxx> >--- > drivers/input/keyboard/Kconfig | 19 ++ > drivers/input/keyboard/Makefile | 1 + > drivers/input/keyboard/tc3589x-keypad.c | 498 >+++++++++++++++++++++++++++++++ > 3 files changed, 518 insertions(+), 0 deletions(-) > create mode 100755 drivers/input/keyboard/tc3589x-keypad.c > >diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig >index b8c51b9..01326fd 100644 >--- a/drivers/input/keyboard/Kconfig >+++ b/drivers/input/keyboard/Kconfig >@@ -443,6 +443,25 @@ config KEYBOARD_OMAP4 > To compile this driver as a module, choose M here: the > module will be called omap4-keypad. > > To compile this driver as a module, choose M here: the > module will be called spear-keboard. >+ >+config KEYBOARD_TC3589X >+ tristate "TC3589X Keypad support" >+ depends on MFD_TC3589X >+ help >+ Say Y here if you want to use the keypad controller on >+ TC35892/3 I/O expander >+ >+ To compile this driver as a module, choose M here: the >+ module will be called tc3589x-keypad >+ > config KEYBOARD_TNETV107X > tristate "TI TNETV107X keypad support" > depends on ARCH_DAVINCI_TNETV107X >diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile >index a34452e..4411c70 100644 >--- a/drivers/input/keyboard/Makefile >+++ b/drivers/input/keyboard/Makefile >@@ -40,6 +40,7 @@ obj-$(CONFIG_KEYBOARD_SH_KEYSC) += >sh_keysc.o > obj-$(CONFIG_KEYBOARD_STMPE) += stmpe-keypad.o > obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o > obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o >+obj-$(CONFIG_KEYBOARD_TC3589X) += tc3589x-keypad.o > obj-$(CONFIG_KEYBOARD_TNETV107X) += tnetv107x-keypad.o > obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o > obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o >diff --git a/drivers/input/keyboard/tc3589x-keypad.c >b/drivers/input/keyboard/tc3589x-keypad.c >new file mode 100755 >index 0000000..fb1a2f2 >--- /dev/null >+++ b/drivers/input/keyboard/tc3589x-keypad.c >@@ -0,0 +1,498 @@ >+/* >+ * Copyright (C) ST-Ericsson SA 2010 >+ * >+ * Author: Jayeeta Banerjee <jayeeta.banerjee@xxxxxxxxxxxxxx> >+ * Author: Sundar Iyer <sundar.iyer@xxxxxxxxxxxxxx> >+ * >+ * License Terms: GNU General Public License, version 2 >+ * >+ * TC35893 MFD Keypad Controller driver >+ */ >+ >+#include <linux/module.h> >+#include <linux/init.h> >+#include <linux/interrupt.h> >+#include <linux/spinlock.h> >+#include <linux/input.h> >+#include <linux/platform_device.h> >+#include <linux/input/matrix_keypad.h> >+#include <linux/i2c.h> >+#include <linux/slab.h> >+#include <linux/mfd/tc3589x.h> >+ >+/* Maximum supported keypad matrix row/columns size */ >+#define TC3589x_MAX_KPROW 8 >+#define TC3589x_MAX_KPCOL 12 >+ >+/* keypad related Constants */ >+#define TC3589x_MAX_DEBOUNCE_SETTLE 0xFF >+#define DEDICATED_KEY_VAL 0xFF >+ >+/* Pull up/down masks */ >+#define TC3589x_NO_PULL_MASK 0x0 >+#define TC3589x_PULL_DOWN_MASK 0x1 >+#define TC3589x_PULL_UP_MASK 0x2 >+#define TC3589x_PULLUP_ALL_MASK 0xAA >+#define TC3589x_IO_PULL_VAL(index, mask) ((mask)<<((index)%4)*2)) >+ >+/* Bit masks for IOCFG register */ >+#define IOCFG_BALLCFG 0x01 >+#define IOCFG_IG 0x08 >+ >+#define KP_EVCODE_COL_MASK 0x0F >+#define KP_EVCODE_ROW_MASK 0x70 >+#define KP_RELEASE_EVT_MASK 0x80 >+ >+#define KP_ROW_SHIFT 4 >+ >+#define KP_NO_VALID_KEY_MASK 0x7F >+ >+/* bit masks for RESTCTRL register */ >+#define TC3589x_KBDRST 0x2 >+#define TC3589x_IRQRST 0x10 >+#define TC3589x_RESET_ALL 0x1B >+ >+/* KBDMFS register bit mask */ >+#define TC3589x_KBDMFS_EN 0x1 >+ >+/* CLKEN register bitmask */ >+#define KPD_CLK_EN 0x1 >+ >+/* RSTINTCLR register bit mask */ >+#define IRQ_CLEAR 0x1 >+ >+/* bit masks for keyboard interrupts*/ >+#define TC3589x_EVT_LOSS_INT 0x8 >+#define TC3589x_EVT_INT 0x4 >+#define TC3589x_KBD_LOSS_INT 0x2 >+#define TC3589x_KBD_INT 0x1 >+ >+/* bit masks for keyboard interrupt clear*/ >+#define TC3589x_EVT_INT_CLR 0x2 >+#define TC3589x_KBD_INT_CLR 0x1 >+ >+#define TC3589x_KBD_KEYMAP_SIZE 64 >+ >+/** >+ * struct tc_keypad - data structure used by keypad driver >+ * @input: pointer to input device object >+ * @board: keypad platform device >+ * @krow: number of rows >+ * @kcol: number of coloumns >+ * @keymap: matrix scan code table for keycodes >+ * @enable: bool to enable/disable keypad operation >+ */ >+struct tc_keypad { >+ struct tc3589x *tc3589x; >+ struct input_dev *input; >+ const struct tc3589x_keypad_platform_data *board; >+ unsigned int krow; >+ unsigned int kcol; >+ unsigned short keymap[TC3589x_KBD_KEYMAP_SIZE]; >+ bool enable; >+}; >+ >+static int __devinit tc3589x_keypad_init_key_hardware(struct tc_keypad *keypad) >+{ >+ int ret; >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ u8 settle_time = keypad->board->settle_time; >+ u8 dbounce_period = keypad->board->debounce_period; >+ u8 rows = keypad->board->krow & 0xf; /* mask out the nibble */ >+ u8 column = keypad->board->kcol & 0xf; /* mask out the nibble */ >+ >+ /* validate platform configurations */ >+ if ((keypad->board->kcol > TC3589x_MAX_KPCOL) || >+ (keypad->board->krow > TC3589x_MAX_KPROW) || >+ (keypad->board->debounce_period > >TC3589x_MAX_DEBOUNCE_SETTLE) || >+ (keypad->board->settle_time > TC3589x_MAX_DEBOUNCE_SETTLE)) >+ return -EINVAL; >+ >+ /* configure KBDSIZE 4 LSbits for cols and 4 MSbits for rows */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDSIZE, 0x0, >+ (rows << KP_ROW_SHIFT) | column); >+ if (ret < 0) >+ return ret; >+ >+ /* configure dedicated key config, no dedicated key selected */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBCFG_LSB, >+ 0x0, DEDICATED_KEY_VAL); >+ if (ret < 0) >+ return ret; >+ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBCFG_MSB, >+ 0x0, DEDICATED_KEY_VAL); >+ if (ret < 0) >+ return ret; >+ >+ /* Configure settle time */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDSETTLE_REG, >+ 0x0, settle_time); >+ if (ret < 0) >+ return ret; >+ >+ /* Configure debounce time */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDBOUNCE, 0x0, >dbounce_period); >+ if (ret < 0) >+ return ret; >+ >+ /* Start of initialise keypad GPIOs */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOCFG, 0x0, IOCFG_IG); >+ if (ret < 0) >+ return ret; >+ >+ /* Configure pull-up resistors for all row GPIOs */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG0_LSB, 0x0, >+ TC3589x_PULLUP_ALL_MASK); >+ if (ret < 0) >+ return ret; >+ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG0_MSB, 0x0, >+ TC3589x_PULLUP_ALL_MASK); >+ if (ret < 0) >+ return ret; >+ >+ /* Configure pull-up resistors for all column GPIOs */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG1_LSB, 0x0, >+ TC3589x_PULLUP_ALL_MASK); >+ if (ret < 0) >+ return ret; >+ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG1_MSB, 0x0, >+ TC3589x_PULLUP_ALL_MASK); >+ if (ret < 0) >+ return ret; >+ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_IOPULLCFG2_LSB, 0x0, >+ TC3589x_PULLUP_ALL_MASK); >+ >+ return ret; >+} >+ >+#define TC35893_DATA_REGS 4 >+#define TC35893_KEYCODE_FIFO_EMPTY 0x7f >+#define TC35893_KEYCODE_FIFO_CLEAR 0xff >+ >+static irqreturn_t tc3589x_keypad_irq(int irq, void *dev) >+{ >+ struct tc_keypad *keypad = (struct tc_keypad *)dev; >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ u8 i, row_index, col_index, kbd_code, up; >+ u8 code; >+ >+ for (i = 0; i < (TC35893_DATA_REGS * 2); i++) { >+ kbd_code = tc3589x_reg_read(tc3589x, TC3589x_EVTCODE_FIFO); >+ >+ /* loop till fifo is empty and no more keys are pressed */ >+ if ((kbd_code == TC35893_KEYCODE_FIFO_EMPTY) || >+ (kbd_code == TC35893_KEYCODE_FIFO_CLEAR)) >+ continue; >+ >+ /* valid key is found */ >+ col_index = kbd_code & KP_EVCODE_COL_MASK; >+ row_index = (kbd_code & KP_EVCODE_ROW_MASK) >> >KP_ROW_SHIFT; >+ code = MATRIX_SCAN_CODE(row_index, col_index, 0x3); >+ up = kbd_code & KP_RELEASE_EVT_MASK; >+ >+ input_event(keypad->input, EV_MSC, MSC_SCAN, code); >+ input_report_key(keypad->input, keypad->keymap[code], !up); >+ input_sync(keypad->input); >+ } >+ >+ /* clear IRQ */ >+ tc3589x_set_bits(tc3589x, TC3589x_KBDIC, >+ 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); >+ /* enable IRQ */ >+ tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, >+ 0x0, TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT); >+ >+ return IRQ_HANDLED; >+} >+ >+static int __devinit tc3589x_keypad_enable(struct tc3589x *tc3589x) >+{ >+ int ret; >+ >+ /* pull the keypad module out of reset */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, >0x0); >+ if (ret < 0) >+ return ret; >+ >+ /* configure KBDMFS */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMFS, 0x0, >TC3589x_KBDMFS_EN); >+ if (ret < 0) >+ return ret; >+ >+ /* enable the keypad clock */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x0, KPD_CLK_EN); >+ if (ret < 0) >+ return ret; >+ >+ /* clear pending IRQs */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTINTCLR, 0x0, 0x1); >+ if (ret < 0) >+ return ret; >+ >+ /* enable the IRQs */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, 0x0, >+ TC3589x_EVT_LOSS_INT | >TC3589x_EVT_INT); >+ >+ return ret; >+} >+ >+static int __devexit tc3589x_keypad_disable(struct tc3589x *tc3589x) >+{ >+ int ret; >+ >+ /* clear IRQ */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDIC, >+ 0x0, TC3589x_EVT_INT_CLR | TC3589x_KBD_INT_CLR); >+ if (ret < 0) >+ return ret; >+ >+ /* disable all interrupts */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_KBDMSK, >+ ~(TC3589x_EVT_LOSS_INT | TC3589x_EVT_INT), 0x0); >+ if (ret < 0) >+ return ret; >+ >+ /* disable the keypad module */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_CLKEN, 0x1, 0x0); >+ if (ret < 0) >+ return ret; >+ >+ /* put the keypad module into reset */ >+ ret = tc3589x_set_bits(tc3589x, TC3589x_RSTCTRL, TC3589x_KBDRST, >0x1); >+ >+ return ret; >+} >+ >+static ssize_t keypad_show_attr_enable(struct device *dev, >+ struct device_attribute *attr, char *buf) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct tc_keypad *keypad = platform_get_drvdata(pdev); >+ >+ return sprintf(buf, "%u\n", keypad->enable); >+} >+ >+static ssize_t keypad_store_attr_enable(struct device *dev, >+ struct device_attribute *attr, const char *buf, size_t count) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct tc_keypad *keypad = platform_get_drvdata(pdev); >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ unsigned long state; >+ >+ if (sscanf(buf, "%lu", &state) != 1) >+ return -EINVAL; >+ >+ if ((state != 1) && (state != 0)) >+ return -EINVAL; >+ >+ if (state != keypad->enable) { >+ if (state) >+ tc3589x_keypad_enable(tc3589x); >+ else >+ tc3589x_keypad_disable(tc3589x); >+ keypad->enable = state; >+ } >+ >+ return strnlen(buf, count); >+} >+ >+static DEVICE_ATTR(enable, S_IWUSR | S_IRUGO, >+ keypad_show_attr_enable, keypad_store_attr_enable); >+ >+static struct attribute *tc3589x_keypad_attrs[] = { >+ &dev_attr_enable.attr, >+ NULL, >+}; >+ >+static struct attribute_group tc3589x_attr_group = { >+ .attrs = tc3589x_keypad_attrs, >+}; >+ >+static int __devinit tc3589x_keypad_probe(struct platform_device *pdev) >+{ >+ struct tc3589x *tc3589x = dev_get_drvdata(pdev->dev.parent); >+ struct tc_keypad *keypad; >+ struct input_dev *input; >+ const struct tc3589x_keypad_platform_data *plat; >+ int error, irq; >+ >+ plat = tc3589x->pdata->keypad; >+ if (!plat) { >+ dev_err(&pdev->dev, "invalid keypad platform data\n"); >+ return -EINVAL; >+ } >+ >+ irq = platform_get_irq(pdev, 0); >+ if (irq < 0) >+ return irq; >+ >+ keypad = kzalloc(sizeof(struct tc_keypad), GFP_KERNEL); >+ input = input_allocate_device(); >+ if (!keypad || !input) { >+ dev_err(&pdev->dev, "failed to allocate keypad memory\n"); >+ error = -ENOMEM; >+ goto err_free_mem; >+ } >+ >+ keypad->board = plat; >+ keypad->input = input; >+ keypad->tc3589x = tc3589x; >+ >+ /* enable the keypad module */ >+ error = tc3589x_keypad_enable(tc3589x); >+ if (error < 0) { >+ dev_err(&pdev->dev, "failed to enable keypad module\n"); >+ goto err_free_mem; >+ } >+ >+ error = tc3589x_keypad_init_key_hardware(keypad); >+ if (error < 0) { >+ dev_err(&pdev->dev, "failed to configure keypad module\n"); >+ goto err_free_mem; >+ } >+ >+ input->id.bustype = BUS_HOST; >+ input->name = pdev->name; >+ input->dev.parent = &pdev->dev; >+ >+ input->keycode = keypad->keymap; >+ input->keycodesize = sizeof(keypad->keymap[0]); >+ input->keycodemax = ARRAY_SIZE(keypad->keymap); >+ >+ input_set_capability(input, EV_MSC, MSC_SCAN); >+ >+ __set_bit(EV_KEY, input->evbit); >+ if (!plat->no_autorepeat) >+ __set_bit(EV_REP, input->evbit); >+ >+ matrix_keypad_build_keymap(plat->keymap_data, 0x3, >+ input->keycode, input->keybit); >+ >+ error = request_threaded_irq(irq, NULL, >+ tc3589x_keypad_irq, plat->irqtype, >+ "tc3589x-keypad", keypad); >+ if (error < 0) { >+ dev_err(&pdev->dev, >+ "Could not allocate irq %d,error %d\n", >+ plat->irq, error); >+ goto err_free_mem; >+ } >+ >+ error = input_register_device(input); >+ if (error) { >+ dev_err(&pdev->dev, "Could not register input device\n"); >+ goto err_free_irq; >+ } >+ >+ device_init_wakeup(&pdev->dev, plat->enable_wakeup); >+ device_set_wakeup_capable(&pdev->dev, plat->enable_wakeup); >+ >+ /* sysfs implementation for dynamic enable/disable the input event */ >+ error = sysfs_create_group(&pdev->dev.kobj, &tc3589x_attr_group); >+ if (error) { >+ dev_err(&pdev->dev, "Could not register sysfs entries\n"); >+ goto err_free_irq; >+ } >+ >+ keypad->enable = true; >+ platform_set_drvdata(pdev, keypad); >+ >+ return 0; >+ >+err_free_irq: >+ free_irq(plat->irq, keypad); >+err_free_mem: >+ input_free_device(input); >+ kfree(keypad); >+ return error; >+} >+ >+static int __devexit tc3589x_keypad_remove(struct platform_device *pdev) >+{ >+ struct tc_keypad *keypad = platform_get_drvdata(pdev); >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ >+ free_irq(keypad->board->irq, keypad); >+ >+ sysfs_remove_group(&pdev->dev.kobj, &tc3589x_attr_group); >+ >+ input_unregister_device(keypad->input); >+ >+ tc3589x_keypad_disable(tc3589x); >+ >+ kfree(keypad); >+ >+ return 0; >+} >+ >+#ifdef CONFIG_PM >+static int tc3589x_keypad_suspend(struct device *dev) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct tc_keypad *keypad = platform_get_drvdata(pdev); >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ int irq = keypad->board->irq; >+ >+ /* disable the IRQ */ >+ if (!device_may_wakeup(&pdev->dev)) >+ tc3589x_keypad_disable(tc3589x); >+ else >+ enable_irq_wake(irq); >+ >+ return 0; >+} >+ >+static int tc3589x_keypad_resume(struct device *dev) >+{ >+ struct platform_device *pdev = to_platform_device(dev); >+ struct tc_keypad *keypad = platform_get_drvdata(pdev); >+ struct tc3589x *tc3589x = keypad->tc3589x; >+ int irq = keypad->board->irq; >+ >+ /* enable the IRQ */ >+ if (!device_may_wakeup(&pdev->dev)) >+ tc3589x_keypad_enable(tc3589x); >+ else >+ disable_irq_wake(irq); >+ >+ return 0; >+} >+ >+static const struct dev_pm_ops tc3589x_keypad_dev_pm_ops = { >+ .suspend = tc3589x_keypad_suspend, >+ .resume = tc3589x_keypad_resume, >+}; >+#endif >+ >+static struct platform_driver tc3589x_keypad_driver = { >+ .driver.name = "tc3589x-keypad", >+ .driver.owner = THIS_MODULE, >+#ifdef CONFIG_PM >+ .driver.pm = &tc3589x_keypad_dev_pm_ops, >+#endif >+ .probe = tc3589x_keypad_probe, >+ .remove = __devexit_p(tc3589x_keypad_remove), >+}; >+ >+static int __init tc3589x_keypad_init(void) >+{ >+ return platform_driver_register(&tc3589x_keypad_driver); >+} >+ >+static void __exit tc3589x_keypad_exit(void) >+{ >+ return platform_driver_unregister(&tc3589x_keypad_driver); >+} >+ >+module_init(tc3589x_keypad_init); >+module_exit(tc3589x_keypad_exit); >+ >+MODULE_LICENSE("GPL v2"); >+MODULE_AUTHOR("Jayeeta Banerjee/Sundar Iyer"); >+MODULE_DESCRIPTION("TC35893 Keypad Driver"); >+ >-- >1.7.2.dirty -- To unsubscribe from this list: send the line "unsubscribe linux-input" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html