spi_parport with proper irq support
[pub/spi-gpio-pp.git] / gpio_test.c
1 /*
2 * gpio_test.c - GPIO test driver
3 *
4 * Copyright (C) 2008 Peter Henn
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
19 *
20 * It just triggers a specified GPIO line setup the output
21 */
22 #include <linux/kernel.h>
23 #include <linux/init.h>
24 #include <linux/device.h>
25 #include <linux/interrupt.h>
26 #include <linux/workqueue.h>
27 #include <linux/gpio.h>
28
29
30 //*****************************************************************************
31 // MODULE PARAMETERS
32 //*****************************************************************************
33 #define DRVNAME "gpio_test" /* name of the driver */
34 #define DRIVER_VERSION "0.3" /* helps identifing different versions of that driver */
35 #define LABEL DRVNAME
36
37 static int gpi = -1;
38 static int gpo = -1;
39 static unsigned int val = 0;
40 static int irq = -1;
41
42 struct gpio_test {
43 int out_cansleep;
44 int in_cansleep;
45 int irq;
46 int cnt;
47 struct tasklet_struct work_tasklet;
48 };
49
50 static struct gpio_test *gt;
51
52
53 static void gpio_tasklet(unsigned long data)
54 {
55 struct gpio_test *g = (struct gpio_test *)data;
56 pr_info("%s: Tasklet triggered: %d\n", DRVNAME, g->cnt);
57 }
58
59 static irqreturn_t gpio_test_irq_handler(int irq, void *dev_id)
60 {
61 struct gpio_test *g = (struct gpio_test *)dev_id;
62 // todo use dev_id to get pointer to own driver structure data;
63 // container can be memory init with GPK_KERNEL
64 if (irq == g->irq) {
65 g->cnt++;
66 tasklet_hi_schedule(&g->work_tasklet);
67 #if 0
68 val = !val;
69 if (!g->in_cansleep) {
70 pr_info("%s: GPIO #%d in value %d with counter %d\n", DRVNAME, gpi, gpio_get_value(gpi), g->cnt);
71 }
72 if ((gpo >= 0) && !g->out_cansleep) {
73 gpio_set_value(gpo, !val);
74 }
75 #endif
76 }
77 return IRQ_HANDLED;
78 }
79
80
81
82 /*
83 ** just set here the value
84 */
85 static int __init gpio_test_init(void)
86 {
87 int err;
88 gt = kzalloc(sizeof(*gt), GFP_KERNEL);
89 if (!gt)
90 return -ENOMEM;
91
92 /* ******* GPIO INPUT ******* */
93 if (gpi < 0) {
94 pr_err("%s: GPI input required\n", DRVNAME);
95 kfree(gt);
96 return -ENODEV;
97 }
98 err = gpio_is_valid(gpi);
99 if (err<0) {
100 pr_err("%s: GPIO #%d for inout is not usable\n", DRVNAME, gpi);
101 kfree(gt);
102 return err;
103 }
104 err = gpio_request(gpi, LABEL);
105 if (err<0) {
106 pr_err("%s: GPIO #%d for input is can not be requested\n", DRVNAME, gpi);
107 kfree(gt);
108 return err;
109 }
110 gt->in_cansleep = gpio_cansleep(gpi);
111 pr_info("%s: GPIO #%d for input %s sleep\n", DRVNAME, gpi, ((gt->in_cansleep>0) ? "can" : "will not"));
112 tasklet_init(&gt->work_tasklet,
113 (void (*)(unsigned long))gpio_tasklet,
114 (unsigned long)gt);
115
116 /* ******* GPIO INTERRUPT ******* */
117 gt->irq = gpio_to_irq(gpi);
118 if (gt->irq<0) {
119 pr_info("%s: GPIO #%d IRQ is not supported\n", DRVNAME, gpi);
120 gt->irq = irq;
121 if (gt->irq >= 0) {
122 pr_info("%s: we force interrupt to IRQ %d\n", DRVNAME, gt->irq);
123 }
124 } else {
125 pr_info("%s: GPIO #%d IRQ %d is supported\n", DRVNAME, gpi, gt->irq);
126 }
127 /* gpio_direction_input must be call from task context, we use insmod as task context here */
128 err = gpio_direction_input(gpi);
129 if (err<0) {
130 pr_err("%s: GPIO #%d input direction is not possible\n", DRVNAME, gpi);
131 tasklet_kill(&gt->work_tasklet);
132 gpio_free(gpi);
133 kfree(gt);
134 return err;
135 }
136 /* register irq handler */
137 gt->cnt=0;
138 // ToDo: Test with other flag types: IRQF_SHARED, IRQF_TRIGGER_*
139 if (gt->irq >= 0) {
140 err = request_irq(gt->irq, gpio_test_irq_handler, IRQF_TRIGGER_NONE, "gpio_test_handler", (void *)gt);
141 if (err<0) {
142 pr_err("%s: GPIO #%d IRQ %d can not be requested, got %d\n", DRVNAME, gpi, gt->irq, err);
143 gt->irq = -1;
144 }
145 }
146
147
148 /* ******* GPIO OUTPUT ******* */
149 if (gpo >= 0) {
150 err = gpio_is_valid(gpo);
151 if (err<0) {
152 pr_err("%s: GPIO #%d for output is not usable\n", DRVNAME, gpo);
153 if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
154 tasklet_kill(&gt->work_tasklet);
155 gpio_free(gpi);
156 kfree(gt);
157 return err;
158 }
159 err = gpio_request(gpo, LABEL);
160 if (err<0) {
161 pr_err("%s: GPIO #%d for output is can not be requested\n", DRVNAME, gpo);
162 if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
163 tasklet_kill(&gt->work_tasklet);
164 gpio_free(gpi);
165 kfree(gt);
166 return err;
167 }
168 gt->out_cansleep = gpio_cansleep(gpo);
169 pr_info("%s: GPIO #%d for output %s sleep\n", DRVNAME, gpo, ((gt->out_cansleep>0) ? "can" : "will not"));
170 err = gpio_direction_output(gpo, val);
171 if (err<0) {
172 pr_err("%s: GPIO #%d output direction is not possible\n", DRVNAME, gpo);
173 if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
174 tasklet_kill(&gt->work_tasklet);
175 gpio_free(gpi);
176 gpio_free(gpo);
177 kfree(gt);
178 return err;
179 }
180 }
181 return 0;
182 }
183
184 device_initcall(gpio_test_init);
185
186 /*
187 ** just restore here the old value
188 */
189 static void __exit gpio_test_exit(void)
190 {
191 if (gt->irq >= 0) free_irq(gt->irq, (void *)gt);
192 tasklet_kill(&gt->work_tasklet);
193 gpio_free(gpi);
194 if (gpo >= 0) {
195 if (gt->out_cansleep) {
196 gpio_set_value_cansleep(gpo, !val);
197 } else {
198 gpio_set_value(gpo, !val);
199 }
200 gpio_free(gpo);
201 }
202 kfree(gt);
203 }
204 module_exit(gpio_test_exit);
205
206 module_param(gpo, uint, S_IRUGO);
207 MODULE_PARM_DESC(gpo, "GPIO output address or number");
208
209 module_param(gpi, uint, S_IRUGO);
210 MODULE_PARM_DESC(gpi, "GPIO input address or number");
211
212 module_param(val, bool, S_IRUGO);
213 MODULE_PARM_DESC(val, "GPIO output value 0 or 1");
214
215 module_param(irq, uint, S_IRUGO);
216 MODULE_PARM_DESC(irq, "GPIO interrupt number");
217
218
219 MODULE_AUTHOR("Option Wireless");
220 MODULE_DESCRIPTION("GPIO output test");
221 MODULE_LICENSE("GPL");
222
223 MODULE_INFO(Version, DRIVER_VERSION);