summaryrefslogtreecommitdiff
path: root/packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch
diff options
context:
space:
mode:
Diffstat (limited to 'packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch')
-rw-r--r--packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch359
1 files changed, 359 insertions, 0 deletions
diff --git a/packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch b/packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch
new file mode 100644
index 0000000000..20cb56d11b
--- /dev/null
+++ b/packages/linux/simpad/linux-2.6.24-SIMpad-ucb1x00-switches.patch
@@ -0,0 +1,359 @@
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/Kconfig linux-2.6.24/drivers/mfd/Kconfig
+--- linux-2.6.24.vanilla/drivers/mfd/Kconfig 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/Kconfig 2008-02-20 21:17:07.000000000 +0100
+@@ -38,4 +38,7 @@
+ tristate "Touchscreen interface support"
+ depends on MCP_UCB1200 && INPUT
+
++config MCP_UCB1200_SWITCHES
++ tristate "SIMpad Switches support"
++ depends on MCP_UCB1200 && INPUT
+ endmenu
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/Makefile linux-2.6.24/drivers/mfd/Makefile
+--- linux-2.6.24.vanilla/drivers/mfd/Makefile 2008-01-24 23:58:37.000000000 +0100
++++ linux-2.6.24/drivers/mfd/Makefile 2008-02-20 21:17:07.000000000 +0100
+@@ -8,7 +8,7 @@
+ obj-$(CONFIG_MCP_SA11X0) += mcp-sa11x0.o
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-core.o
+ obj-$(CONFIG_MCP_UCB1200_TS) += ucb1x00-ts.o
+-
++obj-$(CONFIG_MCP_UCB1200_SWITCHES) += ucb1x00-switches.o
+ ifeq ($(CONFIG_SA1100_ASSABET),y)
+ obj-$(CONFIG_MCP_UCB1200) += ucb1x00-assabet.o
+ endif
+diff -Nur linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c linux-2.6.24/drivers/mfd/ucb1x00-switches.c
+--- linux-2.6.24.vanilla/drivers/mfd/ucb1x00-switches.c 1970-01-01 01:00:00.000000000 +0100
++++ linux-2.6.24/drivers/mfd/ucb1x00-switches.c 2008-02-20 21:17:07.000000000 +0100
+@@ -0,0 +1,332 @@
++/*
++ * linux/drivers/mfd/ucb1x00-switches.c
++ *
++ * Copyright (C) 2007 Bernhard Guillon.
++ *
++ * This program is free software; you can redistribute it and/or modify
++ * it under the terms of the GNU General Public License as published by
++ * the Free Software Foundation; either version 2 of the License.
++ *
++ * This driver is for the Switches of Siemens SIMpad (CL4,SL4,SLC), T-Sinus-Pad and
++ * Swisscom WP50 devices.
++ *
++ * Six switches are routed to GPIO pins on the UCB1300: S3 -- S8.
++ *
++ * This driver is based on the 2.4 ucb1x00-switches, the 2.6 ucb1x00-assabet
++ * and the ucb1x00-ts driver.
++ *
++ * 2007/06/21 mrdata:
++ * - create new thread kswd() to handle irq_events for ucb1300-gpio's
++ * - found out, that not every key-press or key-release
++ * generate a irq_event
++ * -> establish key_state handling
++ * key_state, key_state_last <-> KEY_PRESS, KEY_RELEASE
++ * -> after irq_event polling the ucb1300-gpio's till all keys
++ * in key_state = KEY_RELEASE
++ *
++ */
++#include <linux/module.h>
++#include <linux/init.h>
++#include <linux/input.h>
++#include <linux/device.h>
++#include <linux/sched.h>
++#include <linux/freezer.h>
++#include <linux/kthread.h>
++
++#include <asm/dma.h>
++
++#include "ucb1x00.h"
++
++#define KEY_PRESS 1
++#define KEY_RELEASE 0
++
++static int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT };
++
++static unsigned short int key_state [6] = { 0, 0, 0, 0, 0, 0};
++static unsigned short int key_state_last [6] = { 1, 1, 1, 1, 1, 1};
++
++struct ucb1x00_switches {
++ struct input_dev *idev;
++ struct ucb1x00 *ucb;
++
++ wait_queue_head_t irq_wait;
++ struct task_struct *rtask;
++
++ int idx;
++
++ unsigned int valid:1;
++};
++
++static int ucb1x00_thread(void *_switches_id)
++{
++ unsigned short int this;
++ int idx_tmp;
++ int i;
++ struct ucb1x00_switches *switches = _switches_id;
++ struct input_dev *idev = switches->idev;
++ struct task_struct *tsk = current;
++ DECLARE_WAITQUEUE(wait, tsk);
++
++ add_wait_queue(&switches->irq_wait, &wait);
++
++ while (!kthread_should_stop())
++ {
++ signed long timeout;
++
++ if ((switches->idx >= 0) && (switches->idx <= 5) && (switches->valid == 1))
++ {
++ switches->valid = 0;
++
++ idx_tmp = switches->idx;
++
++ ucb1x00_enable(switches->ucb);
++
++ this = ~ucb1x00_io_read(switches->ucb);
++
++ ucb1x00_disable(switches->ucb);
++
++ if (key_state[idx_tmp] == KEY_RELEASE)
++ {
++ key_state_last[idx_tmp] = KEY_RELEASE;
++ key_state[idx_tmp] = KEY_PRESS;
++
++ input_report_key(idev, key[idx_tmp], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++ }
++
++ for(;;)
++ {
++ ucb1x00_enable(switches->ucb);
++ this = ~ucb1x00_io_read(switches->ucb);
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; i++)
++ {
++ if ((key_state[i] == KEY_PRESS) && (((this & (1 << i)) ? 1 : 0) == KEY_RELEASE))
++ {
++ key_state_last[i] = KEY_PRESS;
++ key_state[i] = KEY_RELEASE;
++
++ input_report_key(idev, key[i], KEY_RELEASE);
++ input_sync(idev);
++ }
++
++ if ((key_state[i] == KEY_RELEASE) && (((this & (1 << i)) ? 1 : 0) == KEY_PRESS))
++ {
++ key_state_last[i] = KEY_RELEASE;
++ key_state[i] = KEY_PRESS;
++
++ input_report_key(idev, key[i], KEY_PRESS);
++ input_sync(idev);
++ }
++
++ }
++
++ // left loop, if no key press detect
++ if ((this | 0xff80) == 0xff80)
++ {
++ break;
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = HZ / 100;
++
++ schedule_timeout(timeout);
++ }
++ }
++
++ set_task_state(tsk, TASK_INTERRUPTIBLE);
++
++ try_to_freeze();
++
++ timeout = MAX_SCHEDULE_TIMEOUT;
++
++ schedule_timeout(timeout);
++ }
++
++ remove_wait_queue(&switches->irq_wait, &wait);
++
++ switches->rtask = NULL;
++
++ return 0;
++}
++
++
++static void ucb1x00_dev_irq(int idx, void *id)
++{
++ struct ucb1x00_switches *switches = id;
++
++ switches->idx = idx;
++ switches->valid = 1;
++
++ wake_up(&switches->irq_wait);
++}
++
++static int ucb1x00_switches_add(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches;
++ struct input_dev *idev;
++ int err,i;
++
++ switches = kzalloc(sizeof(struct ucb1x00_switches), GFP_KERNEL);
++ idev = input_allocate_device();
++
++ if (!switches || !idev) {
++ err = -ENOMEM;
++ goto fail;
++ }
++
++ switches->ucb = dev->ucb;
++
++ idev->private = switches;
++ idev->name = "SIMpad Switches";
++ idev->id.product = switches->ucb->id;
++
++ __set_bit(EV_KEY, idev->evbit);
++ __set_bit(EV_REP, idev->evbit);
++ __set_bit(KEY_PROG1, idev->keybit);
++ __set_bit(KEY_PROG2, idev->keybit);
++ __set_bit(KEY_UP, idev->keybit);
++ __set_bit(KEY_DOWN, idev->keybit);
++ __set_bit(KEY_LEFT, idev->keybit);
++ __set_bit(KEY_RIGHT, idev->keybit);
++
++ err = input_register_device(idev);
++ if (err)
++ goto fail;
++ switches->idev = idev;
++ dev->priv = switches;
++
++ BUG_ON(switches->rtask);
++
++ init_waitqueue_head(&switches->irq_wait);
++
++ ucb1x00_enable(switches->ucb);
++
++ ucb1x00_io_set_dir(switches->ucb,
++ UCB_IO_0 | UCB_IO_1 | UCB_IO_2 |
++ UCB_IO_3 | UCB_IO_4 | UCB_IO_5,
++ UCB_IO_8 | UCB_IO_9);
++
++ ucb1x00_disable(switches->ucb);
++
++ for (i = 0; i < 6; ++i) {
++ ucb1x00_enable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ if (ucb1x00_hook_irq(switches->ucb, i, ucb1x00_dev_irq, switches) < 0) {
++ printk(KERN_ERR "unable to hook IRQ for "
++ "UCB1300 SWITCH_%d\n", i);
++ return -EBUSY;
++ }
++ }
++
++ switches->rtask = kthread_run(ucb1x00_thread, switches, "kswd");
++ if (!IS_ERR(switches->rtask))
++ {
++ return 0;
++ }
++ else
++ {
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ switches->rtask = NULL;
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++
++ return -EFAULT;
++ }
++
++fail:
++ input_free_device(idev);
++ kfree(switches);
++ return err;
++
++}
++
++static void ucb1x00_switches_remove(struct ucb1x00_dev *dev)
++{
++ int i;
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask)
++ kthread_stop(switches->rtask);
++
++ switches->rtask = NULL;
++
++ input_unregister_device(switches->idev);
++
++ for (i = 5; i >= 0; --i) {
++ ucb1x00_disable_irq(switches->ucb, i, UCB_RISING | UCB_FALLING);
++
++ /* Only error conditions are ENOENT and EINVAL; silently
++ * ignore:
++ */
++ ucb1x00_free_irq(switches->ucb, i, NULL);
++ }
++ ucb1x00_disable(switches->ucb);
++ kfree(switches);
++}
++
++#ifdef CONFIG_PM
++static int ucb1x00_switches_resume(struct ucb1x00_dev *dev)
++{
++ struct ucb1x00_switches *switches = dev->priv;
++
++ if (switches->rtask != NULL)
++ {
++ switches->valid = 0;
++ wake_up(&switches->irq_wait);
++
++ printk(KERN_DEBUG "ucb1x00-switches.c -> _switches_resume() kswd - restart *DONE*\n");
++ }
++ return 0;
++}
++#else
++#define ucb1x00_switches_resume NULL
++#endif
++
++static struct ucb1x00_driver ucb1x00_switches_driver = {
++ .add = ucb1x00_switches_add,
++ .remove = ucb1x00_switches_remove,
++ .resume = ucb1x00_switches_resume,
++};
++
++static int __init ucb1x00_switches_init(void)
++{
++ return ucb1x00_register_driver(&ucb1x00_switches_driver);
++}
++
++static void __exit ucb1x00_switches_exit(void)
++{
++ ucb1x00_unregister_driver(&ucb1x00_switches_driver);
++}
++
++module_init(ucb1x00_switches_init);
++module_exit(ucb1x00_switches_exit);
++
++MODULE_AUTHOR("Bernhard Guillon <Bernhard.Guillon@opensimpad.org>");
++MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad");
++MODULE_LICENSE("GPL");