Index: linux-2.6.26/drivers/mxc/baby/Kconfig =================================================================== --- linux-2.6.26.orig/drivers/mxc/baby/Kconfig 2009-07-14 01:45:03.000000000 +0800 +++ linux-2.6.26/drivers/mxc/baby/Kconfig 2009-07-17 01:45:51.000000000 +0800 @@ -11,4 +11,11 @@ ---help--- This driver allows debugging Baby GPIOs via the /sys interface. +config MXC_BABY_BUTTONS + bool "Baby Buttons" + depends on MACH_MX25_BABY + default n + ---help--- + This driver adds support the the GPIO buttons on baby. + endmenu Index: linux-2.6.26/drivers/mxc/baby/Makefile =================================================================== --- linux-2.6.26.orig/drivers/mxc/baby/Makefile 2009-07-14 01:45:03.000000000 +0800 +++ linux-2.6.26/drivers/mxc/baby/Makefile 2009-07-17 01:45:51.000000000 +0800 @@ -3,3 +3,4 @@ # obj-$(CONFIG_MXC_BABY_DEBUG_GPIO) += baby_gpio.o +obj-$(CONFIG_MXC_BABY_BUTTONS) += baby_buttons.o Index: linux-2.6.26/drivers/mxc/baby/baby_buttons.c =================================================================== --- /dev/null 1970-01-01 00:00:00.000000000 +0000 +++ linux-2.6.26/drivers/mxc/baby/baby_buttons.c 2009-07-17 01:22:37.000000000 +0800 @@ -0,0 +1,223 @@ +/* + * Copyright 2009 Logitech. All Rights Reserved. + */ + +/* + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include + +#include + + +#define DEBOUNCE_TIME (5 * HZ/1000) + +#define NUM_BUTTONS 3 + +unsigned int button_codes[] = { + KEY_MUTE, + KEY_RIGHT, + KEY_POWER, +}; + +enum button_state { + BUTTON_IDLE, + BUTTON_PRESSED, + BUTTON_HOLD, + BUTTON_RELEASED, +}; + +struct baby_buttons { + struct input_dev *inputdev; + struct timer_list timer; + + unsigned int gpio[NUM_BUTTONS]; + int value[NUM_BUTTONS]; + enum button_state bstate[NUM_BUTTONS]; + unsigned int *code; +}; + + +static void button_handler(struct baby_buttons *state, int bnum) +{ + unsigned int gpio = state->gpio[bnum]; + + switch (state->bstate[bnum]) { + case BUTTON_IDLE: + // H -> L just occurred (called by IRQ). + disable_irq(IOMUX_TO_IRQ(gpio)); + + state->bstate[bnum] = BUTTON_PRESSED; + mod_timer(&state->timer, jiffies + DEBOUNCE_TIME); + + input_report_key(state->inputdev, state->code[bnum], 1); + break; + + case BUTTON_PRESSED: + // H -> L timeout has passed. + // button could be high (un-pressed) or low still pressed. + if (mxc_get_gpio_datain(gpio)) { + // It was already released + // Since we could be in the release debounce time, start the debounce + // timer again. + mod_timer(&state->timer, jiffies + DEBOUNCE_TIME); + // report the button release. + input_report_key(state->inputdev, state->code[bnum], 0); + state->bstate[bnum] = BUTTON_RELEASED; + } + else { + // Button still pressed. Wait for it to be released with IRQ. + set_irq_type(IOMUX_TO_IRQ(gpio), IRQ_TYPE_EDGE_RISING); + enable_irq(IOMUX_TO_IRQ(gpio)); + state->bstate[bnum] = BUTTON_HOLD; + } + break; + + case BUTTON_HOLD: + disable_irq(IOMUX_TO_IRQ(gpio)); + + state->bstate[bnum] = BUTTON_RELEASED; + mod_timer(&state->timer, jiffies + DEBOUNCE_TIME); + + input_report_key(state->inputdev, state->code[bnum], 0); + break; + + case BUTTON_RELEASED: + state->bstate[bnum] = BUTTON_IDLE; + + set_irq_type(IOMUX_TO_IRQ(gpio), IRQ_TYPE_EDGE_FALLING); + enable_irq(IOMUX_TO_IRQ(gpio)); + break; + } +} + + +static void button_timer_callback(unsigned long data) +{ + struct baby_buttons *state = (struct baby_buttons *) data; + int i; + + for (i=0; ibstate[i] == BUTTON_PRESSED || + state->bstate[i] == BUTTON_RELEASED) { + button_handler(state, i); + } + } +} + + +static irqreturn_t button_irq(int irq, void *data) +{ + struct baby_buttons *state = (struct baby_buttons *) data; + int i; + + for (i=0; igpio[i]) == irq) { + button_handler(state, i); + } + } + + return IRQ_HANDLED; +} + + +static int baby_buttons_probe(struct platform_device *pdev) +{ + struct baby_buttons *state; + int i, ret; + + state = kmalloc(sizeof(struct baby_buttons), GFP_KERNEL); + if (!state) { + goto err0; + } + + state->code = button_codes; + + init_timer(&state->timer); + state->timer.data = (unsigned long)state; + state->timer.function = button_timer_callback; + + state->inputdev = input_allocate_device(); + if (!state->inputdev) { + goto err1; + } + + state->inputdev->evbit[0] = BIT(EV_KEY); + + for (i=0; istart; + + __set_bit(state->code[i], state->inputdev->keybit); + + state->gpio[i] = gpio; + state->value[i] = mxc_get_gpio_datain(gpio); + + if (mxc_get_gpio_datain(gpio)) { + state->bstate[i] = BUTTON_IDLE; + set_irq_type(IOMUX_TO_IRQ(gpio), IRQ_TYPE_EDGE_FALLING); + } + else { + state->bstate[i] = BUTTON_HOLD; + set_irq_type(IOMUX_TO_IRQ(gpio), IRQ_TYPE_EDGE_RISING); + } + + ret = request_irq(IOMUX_TO_IRQ(gpio), button_irq, IRQF_DISABLED | IRQF_SAMPLE_RANDOM, pdev->name, state); + if (ret) { + printk(KERN_ERR "Can't allocate irq\n"); + continue; + } + } + + ret = input_register_device(state->inputdev); + if (ret < 0) { + goto err2; + } + + return 0; + + err2: + input_free_device(state->inputdev); + err1: + kfree(state); + err0: + return 0; +} + + +static int baby_buttons_remove(struct platform_device *pdev) +{ + return 0; +} + + +static struct platform_driver baby_buttons_driver = { + .probe = baby_buttons_probe, + .remove = baby_buttons_remove, + .driver = { + .name = "baby_buttons", + }, +}; + +static int __init baby_buttons_init(void) +{ + return platform_driver_register(&baby_buttons_driver); +} + +static void __exit baby_buttons_exit(void) +{ + platform_driver_unregister(&baby_buttons_driver); +} + + +MODULE_AUTHOR ("Richard Titmuss "); +MODULE_DESCRIPTION("Baby button driver"); +MODULE_LICENSE("GPL"); + +module_init(baby_buttons_init) +module_exit(baby_buttons_exit)