/* * 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. * */ #include #include #include #include #include #include "ucb1x00.h" struct ucb1x00_switches { struct input_dev *idev; struct ucb1x00 *ucb; }; static void ucb1x00_dev_irq(int idx, void *id) { static unsigned short int last; unsigned short int this; struct ucb1x00_switches *switches = id; struct input_dev *idev = switches->idev; int key [6] = { KEY_PROG1,KEY_PROG2,KEY_UP,KEY_DOWN,KEY_LEFT,KEY_RIGHT }; ucb1x00_enable(switches->ucb); this=~ucb1x00_io_read(switches->ucb); if (this==last) { return; } last=this; if ((idx >=0) && (idx <=5)) { if ((this & (1<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; 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; } } return 0; 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; 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); } static struct ucb1x00_driver ucb1x00_switches_driver = { .add = ucb1x00_switches_add, .remove = ucb1x00_switches_remove, }; 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 "); MODULE_DESCRIPTION("UCB1x00 Switches driver for Siemens SIMpad"); MODULE_LICENSE("GPL");