Commit | Line | Data |
---|---|---|
ff4a7481 | 1 | // SPDX-License-Identifier: GPL-2.0 |
9f5e8eee PM |
2 | /* |
3 | * Generic push-switch framework | |
4 | * | |
5 | * Copyright (C) 2006 Paul Mundt | |
9f5e8eee PM |
6 | */ |
7 | #include <linux/init.h> | |
5a0e3ad6 | 8 | #include <linux/slab.h> |
9f5e8eee PM |
9 | #include <linux/module.h> |
10 | #include <linux/interrupt.h> | |
11 | #include <linux/platform_device.h> | |
12 | #include <asm/push-switch.h> | |
13 | ||
14 | #define DRV_NAME "push-switch" | |
fce3a24e | 15 | #define DRV_VERSION "0.1.1" |
9f5e8eee PM |
16 | |
17 | static ssize_t switch_show(struct device *dev, | |
18 | struct device_attribute *attr, | |
19 | char *buf) | |
20 | { | |
21 | struct push_switch_platform_info *psw_info = dev->platform_data; | |
22 | return sprintf(buf, "%s\n", psw_info->name); | |
23 | } | |
c828a892 | 24 | static DEVICE_ATTR_RO(switch); |
9f5e8eee | 25 | |
e99e88a9 | 26 | static void switch_timer(struct timer_list *t) |
9f5e8eee | 27 | { |
e99e88a9 | 28 | struct push_switch *psw = from_timer(psw, t, debounce); |
9f5e8eee PM |
29 | |
30 | schedule_work(&psw->work); | |
31 | } | |
32 | ||
fce3a24e | 33 | static void switch_work_handler(struct work_struct *work) |
9f5e8eee | 34 | { |
fce3a24e PM |
35 | struct push_switch *psw = container_of(work, struct push_switch, work); |
36 | struct platform_device *pdev = psw->pdev; | |
9f5e8eee PM |
37 | |
38 | psw->state = 0; | |
39 | ||
40 | kobject_uevent(&pdev->dev.kobj, KOBJ_CHANGE); | |
41 | } | |
42 | ||
43 | static int switch_drv_probe(struct platform_device *pdev) | |
44 | { | |
45 | struct push_switch_platform_info *psw_info; | |
46 | struct push_switch *psw; | |
47 | int ret, irq; | |
48 | ||
49 | psw = kzalloc(sizeof(struct push_switch), GFP_KERNEL); | |
50 | if (unlikely(!psw)) | |
51 | return -ENOMEM; | |
52 | ||
53 | irq = platform_get_irq(pdev, 0); | |
54 | if (unlikely(irq < 0)) { | |
55 | ret = -ENODEV; | |
56 | goto err; | |
57 | } | |
58 | ||
59 | psw_info = pdev->dev.platform_data; | |
60 | BUG_ON(!psw_info); | |
61 | ||
62 | ret = request_irq(irq, psw_info->irq_handler, | |
d11584a0 | 63 | psw_info->irq_flags, |
9f5e8eee PM |
64 | psw_info->name ? psw_info->name : DRV_NAME, pdev); |
65 | if (unlikely(ret < 0)) | |
66 | goto err; | |
67 | ||
68 | if (psw_info->name) { | |
69 | ret = device_create_file(&pdev->dev, &dev_attr_switch); | |
70 | if (unlikely(ret)) { | |
71 | dev_err(&pdev->dev, "Failed creating device attrs\n"); | |
72 | ret = -EINVAL; | |
73 | goto err_irq; | |
74 | } | |
75 | } | |
76 | ||
fce3a24e | 77 | INIT_WORK(&psw->work, switch_work_handler); |
e99e88a9 | 78 | timer_setup(&psw->debounce, switch_timer, 0); |
9f5e8eee | 79 | |
fce3a24e PM |
80 | /* Workqueue API brain-damage */ |
81 | psw->pdev = pdev; | |
82 | ||
9f5e8eee PM |
83 | platform_set_drvdata(pdev, psw); |
84 | ||
85 | return 0; | |
86 | ||
87 | err_irq: | |
88 | free_irq(irq, pdev); | |
89 | err: | |
90 | kfree(psw); | |
91 | return ret; | |
92 | } | |
93 | ||
94 | static int switch_drv_remove(struct platform_device *pdev) | |
95 | { | |
96 | struct push_switch *psw = platform_get_drvdata(pdev); | |
97 | struct push_switch_platform_info *psw_info = pdev->dev.platform_data; | |
98 | int irq = platform_get_irq(pdev, 0); | |
99 | ||
100 | if (psw_info->name) | |
101 | device_remove_file(&pdev->dev, &dev_attr_switch); | |
102 | ||
103 | platform_set_drvdata(pdev, NULL); | |
292a089d | 104 | timer_shutdown_sync(&psw->debounce); |
246f80a0 | 105 | flush_work(&psw->work); |
9f5e8eee PM |
106 | free_irq(irq, pdev); |
107 | ||
108 | kfree(psw); | |
109 | ||
110 | return 0; | |
111 | } | |
112 | ||
113 | static struct platform_driver switch_driver = { | |
114 | .probe = switch_drv_probe, | |
115 | .remove = switch_drv_remove, | |
116 | .driver = { | |
117 | .name = DRV_NAME, | |
118 | }, | |
119 | }; | |
120 | ||
121 | static int __init switch_init(void) | |
122 | { | |
123 | printk(KERN_NOTICE DRV_NAME ": version %s loaded\n", DRV_VERSION); | |
124 | return platform_driver_register(&switch_driver); | |
125 | } | |
126 | ||
127 | static void __exit switch_exit(void) | |
128 | { | |
129 | platform_driver_unregister(&switch_driver); | |
130 | } | |
131 | module_init(switch_init); | |
132 | module_exit(switch_exit); | |
133 | ||
134 | MODULE_VERSION(DRV_VERSION); | |
135 | MODULE_AUTHOR("Paul Mundt"); | |
e5e3dfc8 | 136 | MODULE_LICENSE("GPL v2"); |