aboutsummaryrefslogtreecommitdiffstats
path: root/arch/sh/drivers
diff options
context:
space:
mode:
authorPaul Mundt <lethal@linux-sh.org>2006-11-23 21:22:57 -0500
committerPaul Mundt <lethal@linux-sh.org>2006-12-05 20:45:38 -0500
commit9f5e8eee5cfe1328660c71812d87c2a67bda389f (patch)
treeb016c812709e4cc33496caac213a1315ca0d9e39 /arch/sh/drivers
parent99a596f93be10001c50894bcab69e458a49a3b8c (diff)
sh: generic push-switch framework.
This adds support for a generic push switch framework. Adaptable for various switches, including GPIO switches and the push switches commonly found on Renesas debug boards. This allows switch states to be trivially reported through sysfs. Signed-off-by: Paul Mundt <lethal@linux-sh.org>
Diffstat (limited to 'arch/sh/drivers')
-rw-r--r--arch/sh/drivers/Kconfig9
-rw-r--r--arch/sh/drivers/Makefile2
-rw-r--r--arch/sh/drivers/push-switch.c138
3 files changed, 148 insertions, 1 deletions
diff --git a/arch/sh/drivers/Kconfig b/arch/sh/drivers/Kconfig
new file mode 100644
index 000000000000..c54c758e6243
--- /dev/null
+++ b/arch/sh/drivers/Kconfig
@@ -0,0 +1,9 @@
1menu "Additional SuperH Device Drivers"
2
3config PUSH_SWITCH
4 tristate "Push switch support"
5 help
6 This enables support for the push switch framework, a simple
7 framework that allows for sysfs driven switch status reporting.
8
9endmenu
diff --git a/arch/sh/drivers/Makefile b/arch/sh/drivers/Makefile
index 338c3729d270..bf18dbfb6787 100644
--- a/arch/sh/drivers/Makefile
+++ b/arch/sh/drivers/Makefile
@@ -5,4 +5,4 @@
5obj-$(CONFIG_PCI) += pci/ 5obj-$(CONFIG_PCI) += pci/
6obj-$(CONFIG_SH_DMA) += dma/ 6obj-$(CONFIG_SH_DMA) += dma/
7obj-$(CONFIG_SUPERHYWAY) += superhyway/ 7obj-$(CONFIG_SUPERHYWAY) += superhyway/
8 8obj-$(CONFIG_PUSH_SWITCH) += push-switch.o
diff --git a/arch/sh/drivers/push-switch.c b/arch/sh/drivers/push-switch.c
new file mode 100644
index 000000000000..f2b9157c314f
--- /dev/null
+++ b/arch/sh/drivers/push-switch.c
@@ -0,0 +1,138 @@
1/*
2 * Generic push-switch framework
3 *
4 * Copyright (C) 2006 Paul Mundt
5 *
6 * This file is subject to the terms and conditions of the GNU General Public
7 * License. See the file "COPYING" in the main directory of this archive
8 * for more details.
9 */
10#include <linux/init.h>
11#include <linux/module.h>
12#include <linux/interrupt.h>
13#include <linux/platform_device.h>
14#include <asm/push-switch.h>
15
16#define DRV_NAME "push-switch"
17#define DRV_VERSION "0.1.0"
18
19static ssize_t switch_show(struct device *dev,
20 struct device_attribute *attr,
21 char *buf)
22{
23 struct push_switch_platform_info *psw_info = dev->platform_data;
24 return sprintf(buf, "%s\n", psw_info->name);
25}
26static DEVICE_ATTR(switch, S_IRUGO, switch_show, NULL);
27
28static void switch_timer(unsigned long data)
29{
30 struct push_switch *psw = (struct push_switch *)data;
31
32 schedule_work(&psw->work);
33}
34
35static void switch_work_handler(void *data)
36{
37 struct platform_device *pdev = data;
38 struct push_switch *psw = platform_get_drvdata(pdev);
39
40 psw->state = 0;
41
42 kobject_uevent(&pdev->dev.kobj, KOBJ_CHANGE);
43}
44
45static int switch_drv_probe(struct platform_device *pdev)
46{
47 struct push_switch_platform_info *psw_info;
48 struct push_switch *psw;
49 int ret, irq;
50
51 psw = kzalloc(sizeof(struct push_switch), GFP_KERNEL);
52 if (unlikely(!psw))
53 return -ENOMEM;
54
55 irq = platform_get_irq(pdev, 0);
56 if (unlikely(irq < 0)) {
57 ret = -ENODEV;
58 goto err;
59 }
60
61 psw_info = pdev->dev.platform_data;
62 BUG_ON(!psw_info);
63
64 ret = request_irq(irq, psw_info->irq_handler,
65 IRQF_DISABLED | psw_info->irq_flags,
66 psw_info->name ? psw_info->name : DRV_NAME, pdev);
67 if (unlikely(ret < 0))
68 goto err;
69
70 if (psw_info->name) {
71 ret = device_create_file(&pdev->dev, &dev_attr_switch);
72 if (unlikely(ret)) {
73 dev_err(&pdev->dev, "Failed creating device attrs\n");
74 ret = -EINVAL;
75 goto err_irq;
76 }
77 }
78
79 INIT_WORK(&psw->work, switch_work_handler, pdev);
80 init_timer(&psw->debounce);
81
82 psw->debounce.function = switch_timer;
83 psw->debounce.data = (unsigned long)psw;
84
85 platform_set_drvdata(pdev, psw);
86
87 return 0;
88
89err_irq:
90 free_irq(irq, pdev);
91err:
92 kfree(psw);
93 return ret;
94}
95
96static int switch_drv_remove(struct platform_device *pdev)
97{
98 struct push_switch *psw = platform_get_drvdata(pdev);
99 struct push_switch_platform_info *psw_info = pdev->dev.platform_data;
100 int irq = platform_get_irq(pdev, 0);
101
102 if (psw_info->name)
103 device_remove_file(&pdev->dev, &dev_attr_switch);
104
105 platform_set_drvdata(pdev, NULL);
106 flush_scheduled_work();
107 del_timer_sync(&psw->debounce);
108 free_irq(irq, pdev);
109
110 kfree(psw);
111
112 return 0;
113}
114
115static struct platform_driver switch_driver = {
116 .probe = switch_drv_probe,
117 .remove = switch_drv_remove,
118 .driver = {
119 .name = DRV_NAME,
120 },
121};
122
123static int __init switch_init(void)
124{
125 printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION);
126 return platform_driver_register(&switch_driver);
127}
128
129static void __exit switch_exit(void)
130{
131 platform_driver_unregister(&switch_driver);
132}
133module_init(switch_init);
134module_exit(switch_exit);
135
136MODULE_VERSION(DRV_VERSION);
137MODULE_AUTHOR("Paul Mundt");
138MODULE_LICENSE("GPLv2");