Path: blob/master/drivers/input/keyboard/omap-keypad.c
15109 views
/*1* linux/drivers/input/keyboard/omap-keypad.c2*3* OMAP Keypad Driver4*5* Copyright (C) 2003 Nokia Corporation6* Written by Timo Teräs <[email protected]>7*8* Added support for H2 & H3 Keypad9* Copyright (C) 2004 Texas Instruments10*11* This program is free software; you can redistribute it and/or modify12* it under the terms of the GNU General Public License as published by13* the Free Software Foundation; either version 2 of the License, or14* (at your option) any later version.15*16* This program is distributed in the hope that it will be useful,17* but WITHOUT ANY WARRANTY; without even the implied warranty of18* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the19* GNU General Public License for more details.20*21* You should have received a copy of the GNU General Public License22* along with this program; if not, write to the Free Software23* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA24*/2526#include <linux/module.h>27#include <linux/init.h>28#include <linux/interrupt.h>29#include <linux/types.h>30#include <linux/input.h>31#include <linux/kernel.h>32#include <linux/delay.h>33#include <linux/platform_device.h>34#include <linux/mutex.h>35#include <linux/errno.h>36#include <linux/slab.h>37#include <mach/gpio.h>38#include <plat/keypad.h>39#include <plat/menelaus.h>40#include <asm/irq.h>41#include <mach/hardware.h>42#include <asm/io.h>43#include <plat/mux.h>4445#undef NEW_BOARD_LEARNING_MODE4647static void omap_kp_tasklet(unsigned long);48static void omap_kp_timer(unsigned long);4950static unsigned char keypad_state[8];51static DEFINE_MUTEX(kp_enable_mutex);52static int kp_enable = 1;53static int kp_cur_group = -1;5455struct omap_kp {56struct input_dev *input;57struct timer_list timer;58int irq;59unsigned int rows;60unsigned int cols;61unsigned long delay;62unsigned int debounce;63};6465static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);6667static unsigned int *row_gpios;68static unsigned int *col_gpios;6970#ifdef CONFIG_ARCH_OMAP271static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)72{73int col;7475for (col = 0; col < omap_kp->cols; col++)76gpio_set_value(col_gpios[col], value & (1 << col));77}7879static u8 get_row_gpio_val(struct omap_kp *omap_kp)80{81int row;82u8 value = 0;8384for (row = 0; row < omap_kp->rows; row++) {85if (gpio_get_value(row_gpios[row]))86value |= (1 << row);87}88return value;89}90#else91#define set_col_gpio_val(x, y) do {} while (0)92#define get_row_gpio_val(x) 093#endif9495static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)96{97struct omap_kp *omap_kp = dev_id;9899/* disable keyboard interrupt and schedule for handling */100if (cpu_is_omap24xx()) {101int i;102103for (i = 0; i < omap_kp->rows; i++) {104int gpio_irq = gpio_to_irq(row_gpios[i]);105/*106* The interrupt which we're currently handling should107* be disabled _nosync() to avoid deadlocks waiting108* for this handler to complete. All others should109* be disabled the regular way for SMP safety.110*/111if (gpio_irq == irq)112disable_irq_nosync(gpio_irq);113else114disable_irq(gpio_irq);115}116} else117/* disable keyboard interrupt and schedule for handling */118omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);119120tasklet_schedule(&kp_tasklet);121122return IRQ_HANDLED;123}124125static void omap_kp_timer(unsigned long data)126{127tasklet_schedule(&kp_tasklet);128}129130static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)131{132int col = 0;133134/* read the keypad status */135if (cpu_is_omap24xx()) {136/* read the keypad status */137for (col = 0; col < omap_kp->cols; col++) {138set_col_gpio_val(omap_kp, ~(1 << col));139state[col] = ~(get_row_gpio_val(omap_kp)) & 0xff;140}141set_col_gpio_val(omap_kp, 0);142143} else {144/* disable keyboard interrupt and schedule for handling */145omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);146147/* read the keypad status */148omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);149for (col = 0; col < omap_kp->cols; col++) {150omap_writew(~(1 << col) & 0xff,151OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);152153udelay(omap_kp->delay);154155state[col] = ~omap_readw(OMAP1_MPUIO_BASE +156OMAP_MPUIO_KBR_LATCH) & 0xff;157}158omap_writew(0x00, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBC);159udelay(2);160}161}162163static void omap_kp_tasklet(unsigned long data)164{165struct omap_kp *omap_kp_data = (struct omap_kp *) data;166unsigned short *keycodes = omap_kp_data->input->keycode;167unsigned int row_shift = get_count_order(omap_kp_data->cols);168unsigned char new_state[8], changed, key_down = 0;169int col, row;170int spurious = 0;171172/* check for any changes */173omap_kp_scan_keypad(omap_kp_data, new_state);174175/* check for changes and print those */176for (col = 0; col < omap_kp_data->cols; col++) {177changed = new_state[col] ^ keypad_state[col];178key_down |= new_state[col];179if (changed == 0)180continue;181182for (row = 0; row < omap_kp_data->rows; row++) {183int key;184if (!(changed & (1 << row)))185continue;186#ifdef NEW_BOARD_LEARNING_MODE187printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,188row, (new_state[col] & (1 << row)) ?189"pressed" : "released");190#else191key = keycodes[MATRIX_SCAN_CODE(row, col, row_shift)];192if (key < 0) {193printk(KERN_WARNING194"omap-keypad: Spurious key event %d-%d\n",195col, row);196/* We scan again after a couple of seconds */197spurious = 1;198continue;199}200201if (!(kp_cur_group == (key & GROUP_MASK) ||202kp_cur_group == -1))203continue;204205kp_cur_group = key & GROUP_MASK;206input_report_key(omap_kp_data->input, key & ~GROUP_MASK,207new_state[col] & (1 << row));208#endif209}210}211input_sync(omap_kp_data->input);212memcpy(keypad_state, new_state, sizeof(keypad_state));213214if (key_down) {215int delay = HZ / 20;216/* some key is pressed - keep irq disabled and use timer217* to poll the keypad */218if (spurious)219delay = 2 * HZ;220mod_timer(&omap_kp_data->timer, jiffies + delay);221} else {222/* enable interrupts */223if (cpu_is_omap24xx()) {224int i;225for (i = 0; i < omap_kp_data->rows; i++)226enable_irq(gpio_to_irq(row_gpios[i]));227} else {228omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);229kp_cur_group = -1;230}231}232}233234static ssize_t omap_kp_enable_show(struct device *dev,235struct device_attribute *attr, char *buf)236{237return sprintf(buf, "%u\n", kp_enable);238}239240static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,241const char *buf, size_t count)242{243int state;244245if (sscanf(buf, "%u", &state) != 1)246return -EINVAL;247248if ((state != 1) && (state != 0))249return -EINVAL;250251mutex_lock(&kp_enable_mutex);252if (state != kp_enable) {253if (state)254enable_irq(INT_KEYBOARD);255else256disable_irq(INT_KEYBOARD);257kp_enable = state;258}259mutex_unlock(&kp_enable_mutex);260261return strnlen(buf, count);262}263264static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);265266#ifdef CONFIG_PM267static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)268{269/* Nothing yet */270271return 0;272}273274static int omap_kp_resume(struct platform_device *dev)275{276/* Nothing yet */277278return 0;279}280#else281#define omap_kp_suspend NULL282#define omap_kp_resume NULL283#endif284285static int __devinit omap_kp_probe(struct platform_device *pdev)286{287struct omap_kp *omap_kp;288struct input_dev *input_dev;289struct omap_kp_platform_data *pdata = pdev->dev.platform_data;290int i, col_idx, row_idx, irq_idx, ret;291unsigned int row_shift, keycodemax;292293if (!pdata->rows || !pdata->cols || !pdata->keymap_data) {294printk(KERN_ERR "No rows, cols or keymap_data from pdata\n");295return -EINVAL;296}297298row_shift = get_count_order(pdata->cols);299keycodemax = pdata->rows << row_shift;300301omap_kp = kzalloc(sizeof(struct omap_kp) +302keycodemax * sizeof(unsigned short), GFP_KERNEL);303input_dev = input_allocate_device();304if (!omap_kp || !input_dev) {305kfree(omap_kp);306input_free_device(input_dev);307return -ENOMEM;308}309310platform_set_drvdata(pdev, omap_kp);311312omap_kp->input = input_dev;313314/* Disable the interrupt for the MPUIO keyboard */315if (!cpu_is_omap24xx())316omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);317318input_dev->keycode = &omap_kp[1];319input_dev->keycodesize = sizeof(unsigned short);320input_dev->keycodemax = keycodemax;321322if (pdata->rep)323__set_bit(EV_REP, input_dev->evbit);324325if (pdata->delay)326omap_kp->delay = pdata->delay;327328if (pdata->row_gpios && pdata->col_gpios) {329row_gpios = pdata->row_gpios;330col_gpios = pdata->col_gpios;331}332333omap_kp->rows = pdata->rows;334omap_kp->cols = pdata->cols;335336if (cpu_is_omap24xx()) {337/* Cols: outputs */338for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) {339if (gpio_request(col_gpios[col_idx], "omap_kp_col") < 0) {340printk(KERN_ERR "Failed to request"341"GPIO%d for keypad\n",342col_gpios[col_idx]);343goto err1;344}345gpio_direction_output(col_gpios[col_idx], 0);346}347/* Rows: inputs */348for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) {349if (gpio_request(row_gpios[row_idx], "omap_kp_row") < 0) {350printk(KERN_ERR "Failed to request"351"GPIO%d for keypad\n",352row_gpios[row_idx]);353goto err2;354}355gpio_direction_input(row_gpios[row_idx]);356}357} else {358col_idx = 0;359row_idx = 0;360}361362setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);363364/* get the irq and init timer*/365tasklet_enable(&kp_tasklet);366kp_tasklet.data = (unsigned long) omap_kp;367368ret = device_create_file(&pdev->dev, &dev_attr_enable);369if (ret < 0)370goto err2;371372/* setup input device */373__set_bit(EV_KEY, input_dev->evbit);374matrix_keypad_build_keymap(pdata->keymap_data, row_shift,375input_dev->keycode, input_dev->keybit);376input_dev->name = "omap-keypad";377input_dev->phys = "omap-keypad/input0";378input_dev->dev.parent = &pdev->dev;379380input_dev->id.bustype = BUS_HOST;381input_dev->id.vendor = 0x0001;382input_dev->id.product = 0x0001;383input_dev->id.version = 0x0100;384385ret = input_register_device(omap_kp->input);386if (ret < 0) {387printk(KERN_ERR "Unable to register omap-keypad input device\n");388goto err3;389}390391if (pdata->dbounce)392omap_writew(0xff, OMAP1_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);393394/* scan current status and enable interrupt */395omap_kp_scan_keypad(omap_kp, keypad_state);396if (!cpu_is_omap24xx()) {397omap_kp->irq = platform_get_irq(pdev, 0);398if (omap_kp->irq >= 0) {399if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,400"omap-keypad", omap_kp) < 0)401goto err4;402}403omap_writew(0, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);404} else {405for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {406if (request_irq(gpio_to_irq(row_gpios[irq_idx]),407omap_kp_interrupt,408IRQF_TRIGGER_FALLING,409"omap-keypad", omap_kp) < 0)410goto err5;411}412}413return 0;414err5:415for (i = irq_idx - 1; i >=0; i--)416free_irq(row_gpios[i], omap_kp);417err4:418input_unregister_device(omap_kp->input);419input_dev = NULL;420err3:421device_remove_file(&pdev->dev, &dev_attr_enable);422err2:423for (i = row_idx - 1; i >=0; i--)424gpio_free(row_gpios[i]);425err1:426for (i = col_idx - 1; i >=0; i--)427gpio_free(col_gpios[i]);428429kfree(omap_kp);430input_free_device(input_dev);431432return -EINVAL;433}434435static int __devexit omap_kp_remove(struct platform_device *pdev)436{437struct omap_kp *omap_kp = platform_get_drvdata(pdev);438439/* disable keypad interrupt handling */440tasklet_disable(&kp_tasklet);441if (cpu_is_omap24xx()) {442int i;443for (i = 0; i < omap_kp->cols; i++)444gpio_free(col_gpios[i]);445for (i = 0; i < omap_kp->rows; i++) {446gpio_free(row_gpios[i]);447free_irq(gpio_to_irq(row_gpios[i]), omap_kp);448}449} else {450omap_writew(1, OMAP1_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);451free_irq(omap_kp->irq, omap_kp);452}453454del_timer_sync(&omap_kp->timer);455tasklet_kill(&kp_tasklet);456457/* unregister everything */458input_unregister_device(omap_kp->input);459460kfree(omap_kp);461462return 0;463}464465static struct platform_driver omap_kp_driver = {466.probe = omap_kp_probe,467.remove = __devexit_p(omap_kp_remove),468.suspend = omap_kp_suspend,469.resume = omap_kp_resume,470.driver = {471.name = "omap-keypad",472.owner = THIS_MODULE,473},474};475476static int __init omap_kp_init(void)477{478printk(KERN_INFO "OMAP Keypad Driver\n");479return platform_driver_register(&omap_kp_driver);480}481482static void __exit omap_kp_exit(void)483{484platform_driver_unregister(&omap_kp_driver);485}486487module_init(omap_kp_init);488module_exit(omap_kp_exit);489490MODULE_AUTHOR("Timo Teräs");491MODULE_DESCRIPTION("OMAP Keypad Driver");492MODULE_LICENSE("GPL");493MODULE_ALIAS("platform:omap-keypad");494495496