-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathgpio2440.c
203 lines (177 loc) · 5.51 KB
/
gpio2440.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
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/miscdevice.h>
#include <linux/fs.h>
#include <linux/io.h>
#include <linux/gpio.h>
#include <linux/interrupt.h>
#include <linux/sched.h>
#include "regs-gpio.h"
#include "gpio2440.h"
#include <mach/hardware.h>
#include <mach/gpio-fns.h>
#include <asm/irq.h>
#include <mach/regs-gpio.h>
#include <plat/gpio-core.h>
MODULE_DESCRIPTION ( "gpio2440 driver" );
MODULE_AUTHOR ( "Sistemas Embebidos" );
MODULE_LICENSE ( "GPL" );
static pid_t process[GPIOD_MAXIRQ];
static void gpiod_rstirq(unsigned int irq){
if (irq >= 0) {
if (process[irq] >= 0) {
free_irq(irq, NULL);
process[irq] = -1;
}
}
}
static void gpiod_reset(void) {
int i = 0;
for (i = 0; i < GPIOD_MAXEINT; i++) {
gpiod_rstirq(IRQ_EINT(i));
}
}
static int gpiod_irq_sig(int irq) {
if (irq < IRQ_EINT4) {
return GPIOD_SIGBASE + irq - IRQ_EINT0;
} else {
return GPIOD_SIGBASE + irq - IRQ_EINT4 + 4;
}
}
static struct task_struct *gpiod_find_task(pid_t procid) {
struct task_struct *taskp;
rcu_read_lock();
taskp = find_task_by_vpid(procid);
rcu_read_unlock();
return taskp;
}
static irqreturn_t gpiod_interrupt(int irq, void *dev_id) {
int signum = gpiod_irq_sig(irq);
// printk(KERN_INFO GPIOD_DEVNAME ": handled irq %i.\n", irq );
if (process[irq] > 0) {
int ret;
struct siginfo info;
struct task_struct *taskp;
/* send the signal */
memset(&info, 0, sizeof(struct siginfo));
info.si_signo = signum;
info.si_code = SI_USER;
taskp = gpiod_find_task(process[irq]);
if (!taskp) {
printk(KERN_WARNING GPIOD_DEVNAME ": No such process %i.\n",
process[irq]);
return IRQ_NONE;
}
ret = send_sig_info(signum, &info, taskp);
if (ret < 0) {
printk(KERN_WARNING GPIOD_DEVNAME ": Error sending signal %i.\n",
signum);
return IRQ_NONE;
}
}
return IRQ_HANDLED;
}
static long gpiod_ioctl (struct file *filp, unsigned int cmd, unsigned long arg)
{
int procid = ((struct gpiod_ioc_args*)arg)->pid;
unsigned int pin = ((struct gpiod_ioc_args*)arg)->pin;
unsigned int value = ((struct gpiod_ioc_args*)arg)->value;
int irq = gpio_to_irq(pin);
int retval = 0;
if ((process[pin] > -1) && (procid != process[pin])) {
if ((gpiod_find_task(process[pin]) > 0)) {
printk(KERN_WARNING GPIOD_DEVNAME
": Pin %i in use by process %i.\n", pin, process[pin]);
return -EBUSY;
}
}
switch (cmd) {
case GPIOD_IOC_RESET:
gpiod_reset();
break;
case GPIOD_IOC_GETDAT:
retval = s3c2410_gpio_getpin(pin);
((struct gpiod_ioc_args*)arg)->value = retval;
break;
case GPIOD_IOC_IRQSIG:
if (irq < 0) {
printk(KERN_WARNING GPIOD_DEVNAME
": No interrupt for pin %x.\n", pin);
return -EPERM;
}
return gpiod_irq_sig(irq);
break;
case GPIOD_IOC_CONFIG:
s3c2410_gpio_cfgpin(pin, value);
process[pin] = procid;
break;
case GPIOD_IOC_PULLUP:
s3c2410_gpio_pullup(pin, value);
break;
case GPIOD_IOC_SETDAT:
s3c2410_gpio_setpin(pin, value);
break;
case GPIOD_IOC_SETIRQ:
if (irq < 0) {
printk(KERN_WARNING GPIOD_DEVNAME
": No interrupt for pin %x.\n", pin);
return -EPERM;
}
retval = request_irq(irq, gpiod_interrupt, IRQF_DISABLED | value,
GPIOD_DEVNAME, NULL);
if (retval) {
free_irq(irq, NULL);
request_irq(irq, gpiod_interrupt, IRQF_DISABLED | value,
GPIOD_DEVNAME, NULL);
}
return 0;
break;
case GPIOD_IOC_CLRIRQ:
if (irq < 0) {
printk(KERN_WARNING GPIOD_DEVNAME
": No interrupt for pin %x.\n", pin);
return -EPERM;
}
free_irq(irq, NULL);
return 0;
break;
case GPIOD_IOC_IRQFLT:
s3c2410_gpio_irqfilter(pin, GPIOD_IRQFLT_ON, S3C2410_EINTFLT_PCLK |
S3C2410_EINTFLT_WIDTHMSK(GPIOD_IRQFLT_WMAX));
break;
case GPIOD_IOC_FREEPIN:
gpiod_rstirq(irq);
break;
default:
printk(KERN_WARNING GPIOD_DEVNAME ": Invalid command.\n");
return -EINVAL;
}
return retval;
}
static const struct file_operations gpiod_fops = {
.owner = THIS_MODULE,
.unlocked_ioctl = gpiod_ioctl,
};
static struct miscdevice gpiod_button_misc_device = {
MISC_DYNAMIC_MINOR,
GPIOD_DEVNAME,
&gpiod_fops
};
static int gpiod_init_module(void) {
printk(KERN_INFO GPIOD_DEVNAME ": Start module\n");
if (misc_register(&gpiod_button_misc_device)) {
printk(KERN_WARNING GPIOD_DEVNAME ": Couldn't register device.\n");
return -EBUSY;
}
gpiod_reset();
return 0;
}
static void gpiod_exit_module(void) {
printk(KERN_INFO GPIOD_DEVNAME ": Exit module\n");
gpiod_reset();
misc_deregister ( &gpiod_button_misc_device );
}
module_init ( gpiod_init_module );
module_exit ( gpiod_exit_module );