blob: ffce4116c1b2540e0e97a4c67dd8a8b4e21a26a6 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * GPIO edge detection driver for ASR eCall transaction
3 *
4 * Copyright 2021 ASR Microelectronics (Shanghai) Co., Ltd.
5 *
6 * This file is subject to the terms and conditions of the GNU General
7 * Public License. See the file "COPYING" in the main directory of this
8 * archive for more details.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20#include <linux/version.h>
21#include <linux/module.h>
22#include <linux/kernel.h>
23#include <linux/fs.h>
24#include <linux/gpio.h>
25#include <linux/uaccess.h>
26#include <linux/delay.h>
27#include <linux/edge_wakeup_mmp.h>
28#include <linux/workqueue.h>
29#include <linux/interrupt.h>
30#include <linux/device.h>
31#include <linux/platform_device.h>
32#include <linux/slab.h>
33#include <stddef.h>
34
35#define ECALL_DEVICE_NUM 2
36struct ecall_event_device {
37 char name[16];
38 struct device *dev;
39 int state;
40 int gpio;
41 int irq;
42 struct delayed_work work;
43 struct workqueue_struct *wq;
44 spinlock_t lock;
45};
46
47static struct class *ecall_event_class;
48static struct ecall_event_device *guedevice[ECALL_DEVICE_NUM];
49static const char* gpio_name[ECALL_DEVICE_NUM] = {
50 "gpio-auto-ecall",
51 "gpio-manual-ecall",
52};
53
54static void report_ecall_event(struct ecall_event_device *uedev, int state)
55{
56 char name_buf[50];
57 char *env[3];
58
59 snprintf(name_buf, sizeof(name_buf), "%s", uedev->name);
60 env[0] = name_buf;
61 env[1] = state ? "GPIO_UP" : "GPIO_DOWN";
62 env[2] = NULL;
63
64 if (state)
65 kobject_uevent_env(&uedev->dev->kobj, KOBJ_ONLINE, env);
66 else
67 kobject_uevent_env(&uedev->dev->kobj, KOBJ_OFFLINE, env);
68 printk(KERN_INFO"%s: uevent from %s [%s] is sent\n",
69 __func__, env[0], env[1]);
70}
71
72static void ecall_event_work(struct work_struct *work)
73{
74 struct ecall_event_device *uedev = container_of(to_delayed_work(work), struct ecall_event_device, work);
75 int state = gpio_get_value(uedev->gpio);
76
77 if (state != uedev->state) {
78 uedev->state = state;
79 report_ecall_event(uedev, state);
80 }
81}
82
83irqreturn_t ecall_event_handler(int irq, void *dev_id)
84{
85 struct ecall_event_device *uedev = (struct ecall_event_device *)dev_id;
86 unsigned long flags = 0;
87
88 spin_lock_irqsave(&uedev->lock, flags);
89 if (work_pending(&uedev->work.work))
90 cancel_delayed_work(&uedev->work);
91 queue_delayed_work(uedev->wq, &uedev->work, HZ / 4);
92 spin_unlock_irqrestore(&uedev->lock, flags);
93
94 printk(KERN_INFO"%s: irq[%d]\n", __func__, irq);
95 return IRQ_HANDLED;
96}
97
98static ssize_t ecall_send_event(struct device *dev,
99 struct device_attribute *attr, const char *buf,
100 size_t count)
101{
102 struct ecall_event_device *uedev = (struct ecall_event_device *)dev_get_drvdata(dev);
103 unsigned long state;
104 int ret = 0;
105
106 ret = kstrtoul(buf, 10, &state);
107 report_ecall_event(uedev, (int)state);
108 if (ret)
109 return ret;
110 else
111 return count;
112}
113
114static DEVICE_ATTR(send_event, 0220, NULL, ecall_send_event);
115static struct device_attribute *ecall_event_attr[] = {
116 &dev_attr_send_event,
117 NULL
118};
119
120static int ecall_event_create_sys_device(struct device *dev)
121{
122 int ret = 0;
123 struct device_attribute **attr = ecall_event_attr;
124
125 for (; *attr; ++attr) {
126 ret = device_create_file(dev, *attr);
127 if (ret)
128 break;
129 }
130
131 if (ret) {
132 for (--attr; attr >= ecall_event_attr; --attr)
133 device_remove_file(dev, *attr);
134 }
135 return 0;
136}
137
138static int ecall_event_remove_sys_device(struct device *dev)
139{
140 struct device_attribute **attr = ecall_event_attr;
141
142 for (; *attr; ++attr)
143 device_remove_file(dev, *attr);
144 return 0;
145}
146
147static int init_device(struct platform_device *pdev, int dev_num)
148{
149 int ret = -1;
150 unsigned long tmp;
151 struct ecall_event_device *uedevice = guedevice[dev_num];
152
153 snprintf(uedevice->name, sizeof(uedevice->name) - 1,
154 "ecall%d", dev_num);
155 spin_lock_init(&uedevice->lock);
156 uedevice->dev = device_create(ecall_event_class, NULL,
157 MKDEV(0, dev_num), NULL, uedevice->name);
158
159 if (IS_ERR(uedevice->dev)) {
160 printk(KERN_INFO"%s: create ecalldev failed!\n", __func__);
161 ret = PTR_ERR(uedevice->dev);
162 goto out;
163 }
164
165 dev_set_drvdata(uedevice->dev, uedevice);
166 of_property_read_u32(pdev->dev.of_node, gpio_name[dev_num], &uedevice->gpio);
167 if (uedevice->gpio >= 0) {
168 tmp = dev_num;
169 ret = request_mfp_edge_wakeup(uedevice->gpio,
170 NULL,
171 (void *)tmp, &pdev->dev);
172 if (ret) {
173 dev_err(uedevice->dev, "failed to request edge wakeup.\n");
174 goto edge_wakeup;
175 }
176 uedevice->state = gpio_get_value(uedevice->gpio);
177 }
178
179 ret = ecall_event_create_sys_device(uedevice->dev);
180 if (ret < 0) {
181 printk(KERN_INFO"%s: create sys device failed!\n", __func__);
182 goto destroy_device;
183 }
184
185 gpio_request(uedevice->gpio, uedevice->name);
186 gpio_direction_input(uedevice->gpio);
187 INIT_DELAYED_WORK(&uedevice->work, ecall_event_work);
188 uedevice->wq = create_workqueue(uedevice->name);
189 if (uedevice->wq == NULL) {
190 printk(KERN_INFO"%s: can't create work queue!\n", __func__);
191 ret = -ENOMEM;
192 goto free_gpio;
193 }
194
195 uedevice->irq = gpio_to_irq(uedevice->gpio);
196 ret = request_irq(uedevice->irq, ecall_event_handler,
197 IRQF_SHARED | IRQF_TRIGGER_RISING |
198 IRQF_TRIGGER_FALLING | IRQF_ONESHOT, uedevice->name,
199 uedevice);
200 if (ret < 0) {
201 printk(KERN_INFO"%s: request irq failed!\n", __func__);
202 goto destroy_wq;
203 }
204
205 device_init_wakeup(uedevice->dev, 1);
206 ret = 0;
207 goto out;
208
209destroy_wq:
210 destroy_workqueue(uedevice->wq);
211free_gpio:
212 gpio_free(uedevice->gpio);
213destroy_device:
214 device_destroy(ecall_event_class, MKDEV(0, dev_num));
215edge_wakeup:
216 if (uedevice->gpio >= 0)
217 remove_mfp_edge_wakeup(uedevice->gpio);
218out:
219 return ret;
220}
221
222static void deinit_device(int dev_num)
223{
224 struct ecall_event_device *uedevice = guedevice[dev_num];
225
226 free_irq(uedevice->irq, uedevice);
227 if (uedevice->wq != NULL)
228 destroy_workqueue(uedevice->wq);
229 if (uedevice->gpio >= 0)
230 remove_mfp_edge_wakeup(uedevice->gpio);
231 gpio_free(uedevice->gpio);
232 ecall_event_remove_sys_device(uedevice->dev);
233 device_destroy(ecall_event_class, MKDEV(0, dev_num));
234}
235
236static int ecall_event_probe(struct platform_device *pdev)
237{
238 int ret;
239 int dev_num;
240
241 if (!ecall_event_class)
242 ecall_event_class = class_create(THIS_MODULE, "ecall_event");
243 if (IS_ERR(ecall_event_class))
244 return PTR_ERR(ecall_event_class);
245
246 for (dev_num = 0; dev_num < ECALL_DEVICE_NUM; dev_num++) {
247 if (of_device_is_compatible(pdev->dev.of_node, "asr,ecall-event")) {
248 printk(KERN_INFO"%s: ecall-event\n", __func__);
249 } else {
250 printk(KERN_INFO"%s: unknown device\n", __func__);
251 goto deinit;
252 }
253
254 guedevice[dev_num] = kzalloc(sizeof(struct ecall_event_device), GFP_KERNEL);
255 ret = init_device(pdev, dev_num);
256 if (ret < 0)
257 goto deinit;
258 }
259
260 return 0;
261
262deinit:
263 deinit_device(dev_num);
264 class_destroy(ecall_event_class);
265
266 return -1;
267}
268
269static int ecall_event_remove(struct platform_device *pdev)
270{
271 int i;
272 for (i = 0; i < ECALL_DEVICE_NUM; i++)
273 deinit_device(i);
274 class_destroy(ecall_event_class);
275 for (i = 0; i < ECALL_DEVICE_NUM; i++) {
276 if (guedevice[i])
277 kfree(guedevice[i]);
278 }
279 return 0;
280}
281
282static const struct of_device_id ecall_of_match[] = {
283 { .compatible = "asr,ecall-event",},
284 {},
285};
286MODULE_DEVICE_TABLE(of, ecall_of_match);
287
288static struct platform_driver ecall_driver = {
289 .probe = ecall_event_probe,
290 .remove = ecall_event_remove,
291 .driver = {
292 .name = "ecall",
293 .owner = THIS_MODULE,
294 .of_match_table = ecall_of_match,
295 },
296};
297
298module_platform_driver(ecall_driver);
299
300MODULE_LICENSE("GPL");
301MODULE_AUTHOR("Shuo Dai");
302MODULE_DESCRIPTION("ASR GPIO event for eCall");