-
Notifications
You must be signed in to change notification settings - Fork 1
/
irq-prober.c
141 lines (113 loc) · 2.59 KB
/
irq-prober.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/*
* irq-prober
*
* Copyright (C) 2015 Savoir-faire Linux, Inc.
*
* Authors:
* Sebastien Bourdelin <sebastien.bourdelin@gmail.com>
*
*/
#include <linux/delay.h>
#include <linux/gpio.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/module.h>
#define MODULE_NAME "irq-prober"
static int delay = 50;
module_param(delay, int, 0);
static int irq = -1;
module_param(irq, int, 0);
static int gpio = -1;
module_param(gpio, int, 0);
static int share = 0;
module_param(share, int, 0);
static int dev_id;
static struct work_struct irq_wq;
void irq_autoprobe(void)
{
unsigned long mask;
int irq_detect = -1;
mask = probe_irq_on();
udelay(delay);
irq_detect = probe_irq_off(mask);
if (irq_detect == 0) {
pr_info("%s: no irq detected using kernel autoprobe\n", MODULE_NAME);
}
/* if more than one line has been activated, the result is negative */
else if (irq_detect < 0) {
pr_info("%s: more than one irq detected using kernel autorpobe\n", MODULE_NAME);
}
}
static void irq_workqueue(struct work_struct *work)
{
pr_info("%s: IRQ %i handle successfully\n", MODULE_NAME, irq);
}
static irqreturn_t irq_handler(int irq, void *dev_id)
{
schedule_work(&irq_wq);
return IRQ_HANDLED;
}
static int __init irq_prober_init(void)
{
int err;
unsigned long mask;
INIT_WORK(&irq_wq, irq_workqueue);
/* if no irq has been chosen, we used the kernel irq
* autoprobe feature */
if (irq < 0 && gpio < 0) {
irq_autoprobe();
}
/* irq has been chosen */
else {
if (gpio >= 0) {
err = gpio_request(gpio, MODULE_NAME);
if (err) {
pr_err("%s: failed to request GPIO %i\n", MODULE_NAME,
gpio);
return err;
}
err = gpio_direction_input(gpio);
if (err) {
pr_err("%s: failed to set GPIO %i direction\n",
MODULE_NAME, gpio);
goto err_gpio_request;
}
irq = gpio_to_irq(gpio);
if (irq < 0) {
pr_err("%s: failed to set GPIO %i as an IRQ\n", MODULE_NAME,
gpio);
err = 1;
goto err_gpio_request;
}
}
mask = IRQF_TRIGGER_PROBE;
if (share) {
mask |= IRQF_SHARED;
}
err = request_irq(irq, irq_handler, mask, MODULE_NAME, &dev_id);
if (err) {
pr_err("%s: failed to assigned IRQ %i (%i)\n", MODULE_NAME, irq,
err);
irq = -1;
}
}
return 0;
err_gpio_request:
gpio_free(gpio);
return err;
}
static void __exit irq_prober_exit(void)
{
if (irq >= 0) {
free_irq(irq, &dev_id);
}
if (gpio >= 0) {
gpio_free(gpio);
}
flush_scheduled_work();
}
module_init(irq_prober_init);
module_exit(irq_prober_exit);
MODULE_AUTHOR("Sebastien Bourdelin");
MODULE_DESCRIPTION("IRQ prober");
MODULE_LICENSE("GPL v2");