]>
Commit | Line | Data |
---|---|---|
1 | // SPDX-License-Identifier: GPL-2.0 | |
2 | /* | |
3 | * Etrax general port I/O device | |
4 | * | |
5 | * Copyright (c) 1999-2007 Axis Communications AB | |
6 | * | |
7 | * Authors: Bjorn Wesen (initial version) | |
8 | * Ola Knutsson (LED handling) | |
9 | * Johan Adolfsson (read/set directions, write, port G) | |
10 | */ | |
11 | ||
12 | ||
13 | #include <linux/module.h> | |
14 | #include <linux/sched.h> | |
15 | #include <linux/slab.h> | |
16 | #include <linux/ioport.h> | |
17 | #include <linux/errno.h> | |
18 | #include <linux/kernel.h> | |
19 | #include <linux/fs.h> | |
20 | #include <linux/string.h> | |
21 | #include <linux/poll.h> | |
22 | #include <linux/init.h> | |
23 | #include <linux/interrupt.h> | |
24 | ||
25 | #include <asm/etraxgpio.h> | |
26 | #include <arch/svinto.h> | |
27 | #include <asm/io.h> | |
28 | #include <asm/irq.h> | |
29 | #include <arch/io_interface_mux.h> | |
30 | ||
31 | #define GPIO_MAJOR 120 /* experimental MAJOR number */ | |
32 | ||
33 | #define D(x) | |
34 | ||
35 | #if 0 | |
36 | static int dp_cnt; | |
37 | #define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0) | |
38 | #else | |
39 | #define DP(x) | |
40 | #endif | |
41 | ||
42 | static char gpio_name[] = "etrax gpio"; | |
43 | ||
44 | #if 0 | |
45 | static wait_queue_head_t *gpio_wq; | |
46 | #endif | |
47 | ||
48 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg); | |
49 | static ssize_t gpio_write(struct file *file, const char __user *buf, | |
50 | size_t count, loff_t *off); | |
51 | static int gpio_open(struct inode *inode, struct file *filp); | |
52 | static int gpio_release(struct inode *inode, struct file *filp); | |
53 | static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait); | |
54 | ||
55 | /* private data per open() of this driver */ | |
56 | ||
57 | struct gpio_private { | |
58 | struct gpio_private *next; | |
59 | /* These fields are for PA and PB only */ | |
60 | volatile unsigned char *port, *shadow; | |
61 | volatile unsigned char *dir, *dir_shadow; | |
62 | unsigned char changeable_dir; | |
63 | unsigned char changeable_bits; | |
64 | unsigned char clk_mask; | |
65 | unsigned char data_mask; | |
66 | unsigned char write_msb; | |
67 | unsigned char pad1, pad2, pad3; | |
68 | /* These fields are generic */ | |
69 | unsigned long highalarm, lowalarm; | |
70 | wait_queue_head_t alarm_wq; | |
71 | int minor; | |
72 | }; | |
73 | ||
74 | /* linked list of alarms to check for */ | |
75 | ||
76 | static struct gpio_private *alarmlist; | |
77 | ||
78 | static int gpio_some_alarms; /* Set if someone uses alarm */ | |
79 | static unsigned long gpio_pa_irq_enabled_mask; | |
80 | ||
81 | static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ | |
82 | ||
83 | /* Port A and B use 8 bit access, but Port G is 32 bit */ | |
84 | #define NUM_PORTS (GPIO_MINOR_B+1) | |
85 | ||
86 | static volatile unsigned char *ports[NUM_PORTS] = { | |
87 | R_PORT_PA_DATA, | |
88 | R_PORT_PB_DATA, | |
89 | }; | |
90 | static volatile unsigned char *shads[NUM_PORTS] = { | |
91 | &port_pa_data_shadow, | |
92 | &port_pb_data_shadow | |
93 | }; | |
94 | ||
95 | /* What direction bits that are user changeable 1=changeable*/ | |
96 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR | |
97 | #define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00 | |
98 | #endif | |
99 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR | |
100 | #define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00 | |
101 | #endif | |
102 | ||
103 | #ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS | |
104 | #define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF | |
105 | #endif | |
106 | #ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS | |
107 | #define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF | |
108 | #endif | |
109 | ||
110 | ||
111 | static unsigned char changeable_dir[NUM_PORTS] = { | |
112 | CONFIG_ETRAX_PA_CHANGEABLE_DIR, | |
113 | CONFIG_ETRAX_PB_CHANGEABLE_DIR | |
114 | }; | |
115 | static unsigned char changeable_bits[NUM_PORTS] = { | |
116 | CONFIG_ETRAX_PA_CHANGEABLE_BITS, | |
117 | CONFIG_ETRAX_PB_CHANGEABLE_BITS | |
118 | }; | |
119 | ||
120 | static volatile unsigned char *dir[NUM_PORTS] = { | |
121 | R_PORT_PA_DIR, | |
122 | R_PORT_PB_DIR | |
123 | }; | |
124 | ||
125 | static volatile unsigned char *dir_shadow[NUM_PORTS] = { | |
126 | &port_pa_dir_shadow, | |
127 | &port_pb_dir_shadow | |
128 | }; | |
129 | ||
130 | /* All bits in port g that can change dir. */ | |
131 | static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; | |
132 | ||
133 | /* Port G is 32 bit, handle it special, some bits are both inputs | |
134 | and outputs at the same time, only some of the bits can change direction | |
135 | and some of them in groups of 8 bit. */ | |
136 | static unsigned long changeable_dir_g; | |
137 | static unsigned long dir_g_in_bits; | |
138 | static unsigned long dir_g_out_bits; | |
139 | static unsigned long dir_g_shadow; /* 1=output */ | |
140 | ||
141 | #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) | |
142 | ||
143 | ||
144 | static unsigned int gpio_poll(struct file *file, poll_table *wait) | |
145 | { | |
146 | unsigned int mask = 0; | |
147 | struct gpio_private *priv = file->private_data; | |
148 | unsigned long data; | |
149 | unsigned long flags; | |
150 | ||
151 | spin_lock_irqsave(&gpio_lock, flags); | |
152 | ||
153 | poll_wait(file, &priv->alarm_wq, wait); | |
154 | if (priv->minor == GPIO_MINOR_A) { | |
155 | unsigned long tmp; | |
156 | data = *R_PORT_PA_DATA; | |
157 | /* PA has support for high level interrupt - | |
158 | * lets activate for those low and with highalarm set | |
159 | */ | |
160 | tmp = ~data & priv->highalarm & 0xFF; | |
161 | tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); | |
162 | ||
163 | gpio_pa_irq_enabled_mask |= tmp; | |
164 | *R_IRQ_MASK1_SET = tmp; | |
165 | } else if (priv->minor == GPIO_MINOR_B) | |
166 | data = *R_PORT_PB_DATA; | |
167 | else if (priv->minor == GPIO_MINOR_G) | |
168 | data = *R_PORT_G_DATA; | |
169 | else { | |
170 | mask = 0; | |
171 | goto out; | |
172 | } | |
173 | ||
174 | if ((data & priv->highalarm) || | |
175 | (~data & priv->lowalarm)) { | |
176 | mask = POLLIN|POLLRDNORM; | |
177 | } | |
178 | ||
179 | out: | |
180 | spin_unlock_irqrestore(&gpio_lock, flags); | |
181 | DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); | |
182 | ||
183 | return mask; | |
184 | } | |
185 | ||
186 | int etrax_gpio_wake_up_check(void) | |
187 | { | |
188 | struct gpio_private *priv; | |
189 | unsigned long data = 0; | |
190 | int ret = 0; | |
191 | unsigned long flags; | |
192 | ||
193 | spin_lock_irqsave(&gpio_lock, flags); | |
194 | priv = alarmlist; | |
195 | while (priv) { | |
196 | if (USE_PORTS(priv)) | |
197 | data = *priv->port; | |
198 | else if (priv->minor == GPIO_MINOR_G) | |
199 | data = *R_PORT_G_DATA; | |
200 | ||
201 | if ((data & priv->highalarm) || | |
202 | (~data & priv->lowalarm)) { | |
203 | DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); | |
204 | wake_up_interruptible(&priv->alarm_wq); | |
205 | ret = 1; | |
206 | } | |
207 | priv = priv->next; | |
208 | } | |
209 | spin_unlock_irqrestore(&gpio_lock, flags); | |
210 | return ret; | |
211 | } | |
212 | ||
213 | static irqreturn_t | |
214 | gpio_poll_timer_interrupt(int irq, void *dev_id) | |
215 | { | |
216 | if (gpio_some_alarms) { | |
217 | etrax_gpio_wake_up_check(); | |
218 | return IRQ_HANDLED; | |
219 | } | |
220 | return IRQ_NONE; | |
221 | } | |
222 | ||
223 | static irqreturn_t | |
224 | gpio_interrupt(int irq, void *dev_id) | |
225 | { | |
226 | unsigned long tmp; | |
227 | unsigned long flags; | |
228 | ||
229 | spin_lock_irqsave(&gpio_lock, flags); | |
230 | ||
231 | /* Find what PA interrupts are active */ | |
232 | tmp = (*R_IRQ_READ1); | |
233 | ||
234 | /* Find those that we have enabled */ | |
235 | tmp &= gpio_pa_irq_enabled_mask; | |
236 | ||
237 | /* Clear them.. */ | |
238 | *R_IRQ_MASK1_CLR = tmp; | |
239 | gpio_pa_irq_enabled_mask &= ~tmp; | |
240 | ||
241 | spin_unlock_irqrestore(&gpio_lock, flags); | |
242 | ||
243 | if (gpio_some_alarms) | |
244 | return IRQ_RETVAL(etrax_gpio_wake_up_check()); | |
245 | ||
246 | return IRQ_NONE; | |
247 | } | |
248 | ||
249 | static void gpio_write_bit(struct gpio_private *priv, | |
250 | unsigned char data, int bit) | |
251 | { | |
252 | *priv->port = *priv->shadow &= ~(priv->clk_mask); | |
253 | if (data & 1 << bit) | |
254 | *priv->port = *priv->shadow |= priv->data_mask; | |
255 | else | |
256 | *priv->port = *priv->shadow &= ~(priv->data_mask); | |
257 | ||
258 | /* For FPGA: min 5.0ns (DCC) before CCLK high */ | |
259 | *priv->port = *priv->shadow |= priv->clk_mask; | |
260 | } | |
261 | ||
262 | static void gpio_write_byte(struct gpio_private *priv, unsigned char data) | |
263 | { | |
264 | int i; | |
265 | ||
266 | if (priv->write_msb) | |
267 | for (i = 7; i >= 0; i--) | |
268 | gpio_write_bit(priv, data, i); | |
269 | else | |
270 | for (i = 0; i <= 7; i++) | |
271 | gpio_write_bit(priv, data, i); | |
272 | } | |
273 | ||
274 | static ssize_t gpio_write(struct file *file, const char __user *buf, | |
275 | size_t count, loff_t *off) | |
276 | { | |
277 | struct gpio_private *priv = file->private_data; | |
278 | unsigned long flags; | |
279 | ssize_t retval = count; | |
280 | ||
281 | if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) | |
282 | return -EFAULT; | |
283 | ||
284 | if (!access_ok(VERIFY_READ, buf, count)) | |
285 | return -EFAULT; | |
286 | ||
287 | spin_lock_irqsave(&gpio_lock, flags); | |
288 | ||
289 | /* It must have been configured using the IO_CFG_WRITE_MODE */ | |
290 | /* Perhaps a better error code? */ | |
291 | if (priv->clk_mask == 0 || priv->data_mask == 0) { | |
292 | retval = -EPERM; | |
293 | goto out; | |
294 | } | |
295 | ||
296 | D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X " | |
297 | "clk 0x%02X msb: %i\n", | |
298 | count, priv->data_mask, priv->clk_mask, priv->write_msb)); | |
299 | ||
300 | while (count--) | |
301 | gpio_write_byte(priv, *buf++); | |
302 | ||
303 | out: | |
304 | spin_unlock_irqrestore(&gpio_lock, flags); | |
305 | return retval; | |
306 | } | |
307 | ||
308 | ||
309 | ||
310 | static int | |
311 | gpio_open(struct inode *inode, struct file *filp) | |
312 | { | |
313 | struct gpio_private *priv; | |
314 | int p = iminor(inode); | |
315 | unsigned long flags; | |
316 | ||
317 | if (p > GPIO_MINOR_LAST) | |
318 | return -EINVAL; | |
319 | ||
320 | priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL); | |
321 | ||
322 | if (!priv) | |
323 | return -ENOMEM; | |
324 | ||
325 | priv->minor = p; | |
326 | ||
327 | /* initialize the io/alarm struct */ | |
328 | ||
329 | if (USE_PORTS(priv)) { /* A and B */ | |
330 | priv->port = ports[p]; | |
331 | priv->shadow = shads[p]; | |
332 | priv->dir = dir[p]; | |
333 | priv->dir_shadow = dir_shadow[p]; | |
334 | priv->changeable_dir = changeable_dir[p]; | |
335 | priv->changeable_bits = changeable_bits[p]; | |
336 | } else { | |
337 | priv->port = NULL; | |
338 | priv->shadow = NULL; | |
339 | priv->dir = NULL; | |
340 | priv->dir_shadow = NULL; | |
341 | priv->changeable_dir = 0; | |
342 | priv->changeable_bits = 0; | |
343 | } | |
344 | ||
345 | priv->highalarm = 0; | |
346 | priv->lowalarm = 0; | |
347 | priv->clk_mask = 0; | |
348 | priv->data_mask = 0; | |
349 | init_waitqueue_head(&priv->alarm_wq); | |
350 | ||
351 | filp->private_data = priv; | |
352 | ||
353 | /* link it into our alarmlist */ | |
354 | spin_lock_irqsave(&gpio_lock, flags); | |
355 | priv->next = alarmlist; | |
356 | alarmlist = priv; | |
357 | spin_unlock_irqrestore(&gpio_lock, flags); | |
358 | ||
359 | return 0; | |
360 | } | |
361 | ||
362 | static int | |
363 | gpio_release(struct inode *inode, struct file *filp) | |
364 | { | |
365 | struct gpio_private *p; | |
366 | struct gpio_private *todel; | |
367 | unsigned long flags; | |
368 | ||
369 | spin_lock_irqsave(&gpio_lock, flags); | |
370 | ||
371 | p = alarmlist; | |
372 | todel = filp->private_data; | |
373 | ||
374 | /* unlink from alarmlist and free the private structure */ | |
375 | ||
376 | if (p == todel) { | |
377 | alarmlist = todel->next; | |
378 | } else { | |
379 | while (p->next != todel) | |
380 | p = p->next; | |
381 | p->next = todel->next; | |
382 | } | |
383 | ||
384 | kfree(todel); | |
385 | /* Check if there are still any alarms set */ | |
386 | p = alarmlist; | |
387 | while (p) { | |
388 | if (p->highalarm | p->lowalarm) { | |
389 | gpio_some_alarms = 1; | |
390 | goto out; | |
391 | } | |
392 | p = p->next; | |
393 | } | |
394 | gpio_some_alarms = 0; | |
395 | out: | |
396 | spin_unlock_irqrestore(&gpio_lock, flags); | |
397 | return 0; | |
398 | } | |
399 | ||
400 | /* Main device API. ioctl's to read/set/clear bits, as well as to | |
401 | * set alarms to wait for using a subsequent select(). | |
402 | */ | |
403 | inline unsigned long setget_input(struct gpio_private *priv, unsigned long arg) | |
404 | { | |
405 | /* Set direction 0=unchanged 1=input, | |
406 | * return mask with 1=input */ | |
407 | if (USE_PORTS(priv)) { | |
408 | *priv->dir = *priv->dir_shadow &= | |
409 | ~((unsigned char)arg & priv->changeable_dir); | |
410 | return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ | |
411 | } | |
412 | ||
413 | if (priv->minor != GPIO_MINOR_G) | |
414 | return 0; | |
415 | ||
416 | /* We must fiddle with R_GEN_CONFIG to change dir */ | |
417 | if (((arg & dir_g_in_bits) != arg) && | |
418 | (arg & changeable_dir_g)) { | |
419 | arg &= changeable_dir_g; | |
420 | /* Clear bits in genconfig to set to input */ | |
421 | if (arg & (1<<0)) { | |
422 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir); | |
423 | dir_g_in_bits |= (1<<0); | |
424 | dir_g_out_bits &= ~(1<<0); | |
425 | } | |
426 | if ((arg & 0x0000FF00) == 0x0000FF00) { | |
427 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir); | |
428 | dir_g_in_bits |= 0x0000FF00; | |
429 | dir_g_out_bits &= ~0x0000FF00; | |
430 | } | |
431 | if ((arg & 0x00FF0000) == 0x00FF0000) { | |
432 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir); | |
433 | dir_g_in_bits |= 0x00FF0000; | |
434 | dir_g_out_bits &= ~0x00FF0000; | |
435 | } | |
436 | if (arg & (1<<24)) { | |
437 | genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir); | |
438 | dir_g_in_bits |= (1<<24); | |
439 | dir_g_out_bits &= ~(1<<24); | |
440 | } | |
441 | D(printk(KERN_DEBUG "gpio: SETINPUT on port G set " | |
442 | "genconfig to 0x%08lX " | |
443 | "in_bits: 0x%08lX " | |
444 | "out_bits: 0x%08lX\n", | |
445 | (unsigned long)genconfig_shadow, | |
446 | dir_g_in_bits, dir_g_out_bits)); | |
447 | *R_GEN_CONFIG = genconfig_shadow; | |
448 | /* Must be a >120 ns delay before writing this again */ | |
449 | ||
450 | } | |
451 | return dir_g_in_bits; | |
452 | } /* setget_input */ | |
453 | ||
454 | inline unsigned long setget_output(struct gpio_private *priv, unsigned long arg) | |
455 | { | |
456 | if (USE_PORTS(priv)) { | |
457 | *priv->dir = *priv->dir_shadow |= | |
458 | ((unsigned char)arg & priv->changeable_dir); | |
459 | return *priv->dir_shadow; | |
460 | } | |
461 | if (priv->minor != GPIO_MINOR_G) | |
462 | return 0; | |
463 | ||
464 | /* We must fiddle with R_GEN_CONFIG to change dir */ | |
465 | if (((arg & dir_g_out_bits) != arg) && | |
466 | (arg & changeable_dir_g)) { | |
467 | /* Set bits in genconfig to set to output */ | |
468 | if (arg & (1<<0)) { | |
469 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir); | |
470 | dir_g_out_bits |= (1<<0); | |
471 | dir_g_in_bits &= ~(1<<0); | |
472 | } | |
473 | if ((arg & 0x0000FF00) == 0x0000FF00) { | |
474 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir); | |
475 | dir_g_out_bits |= 0x0000FF00; | |
476 | dir_g_in_bits &= ~0x0000FF00; | |
477 | } | |
478 | if ((arg & 0x00FF0000) == 0x00FF0000) { | |
479 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir); | |
480 | dir_g_out_bits |= 0x00FF0000; | |
481 | dir_g_in_bits &= ~0x00FF0000; | |
482 | } | |
483 | if (arg & (1<<24)) { | |
484 | genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir); | |
485 | dir_g_out_bits |= (1<<24); | |
486 | dir_g_in_bits &= ~(1<<24); | |
487 | } | |
488 | D(printk(KERN_INFO "gpio: SETOUTPUT on port G set " | |
489 | "genconfig to 0x%08lX " | |
490 | "in_bits: 0x%08lX " | |
491 | "out_bits: 0x%08lX\n", | |
492 | (unsigned long)genconfig_shadow, | |
493 | dir_g_in_bits, dir_g_out_bits)); | |
494 | *R_GEN_CONFIG = genconfig_shadow; | |
495 | /* Must be a >120 ns delay before writing this again */ | |
496 | } | |
497 | return dir_g_out_bits & 0x7FFFFFFF; | |
498 | } /* setget_output */ | |
499 | ||
500 | static int | |
501 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg); | |
502 | ||
503 | static long gpio_ioctl(struct file *file, unsigned int cmd, unsigned long arg) | |
504 | { | |
505 | unsigned long flags; | |
506 | unsigned long val; | |
507 | int ret = 0; | |
508 | ||
509 | struct gpio_private *priv = file->private_data; | |
510 | if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) | |
511 | return -EINVAL; | |
512 | ||
513 | switch (_IOC_NR(cmd)) { | |
514 | case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ | |
515 | // read the port | |
516 | spin_lock_irqsave(&gpio_lock, flags); | |
517 | if (USE_PORTS(priv)) { | |
518 | ret = *priv->port; | |
519 | } else if (priv->minor == GPIO_MINOR_G) { | |
520 | ret = (*R_PORT_G_DATA) & 0x7FFFFFFF; | |
521 | } | |
522 | spin_unlock_irqrestore(&gpio_lock, flags); | |
523 | ||
524 | break; | |
525 | case IO_SETBITS: | |
526 | // set changeable bits with a 1 in arg | |
527 | spin_lock_irqsave(&gpio_lock, flags); | |
528 | ||
529 | if (USE_PORTS(priv)) { | |
530 | *priv->port = *priv->shadow |= | |
531 | ((unsigned char)arg & priv->changeable_bits); | |
532 | } else if (priv->minor == GPIO_MINOR_G) { | |
533 | *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); | |
534 | } | |
535 | spin_unlock_irqrestore(&gpio_lock, flags); | |
536 | ||
537 | break; | |
538 | case IO_CLRBITS: | |
539 | // clear changeable bits with a 1 in arg | |
540 | spin_lock_irqsave(&gpio_lock, flags); | |
541 | if (USE_PORTS(priv)) { | |
542 | *priv->port = *priv->shadow &= | |
543 | ~((unsigned char)arg & priv->changeable_bits); | |
544 | } else if (priv->minor == GPIO_MINOR_G) { | |
545 | *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); | |
546 | } | |
547 | spin_unlock_irqrestore(&gpio_lock, flags); | |
548 | break; | |
549 | case IO_HIGHALARM: | |
550 | // set alarm when bits with 1 in arg go high | |
551 | spin_lock_irqsave(&gpio_lock, flags); | |
552 | priv->highalarm |= arg; | |
553 | gpio_some_alarms = 1; | |
554 | spin_unlock_irqrestore(&gpio_lock, flags); | |
555 | break; | |
556 | case IO_LOWALARM: | |
557 | // set alarm when bits with 1 in arg go low | |
558 | spin_lock_irqsave(&gpio_lock, flags); | |
559 | priv->lowalarm |= arg; | |
560 | gpio_some_alarms = 1; | |
561 | spin_unlock_irqrestore(&gpio_lock, flags); | |
562 | break; | |
563 | case IO_CLRALARM: | |
564 | /* clear alarm for bits with 1 in arg */ | |
565 | spin_lock_irqsave(&gpio_lock, flags); | |
566 | priv->highalarm &= ~arg; | |
567 | priv->lowalarm &= ~arg; | |
568 | { | |
569 | /* Must update gpio_some_alarms */ | |
570 | struct gpio_private *p = alarmlist; | |
571 | int some_alarms; | |
572 | p = alarmlist; | |
573 | some_alarms = 0; | |
574 | while (p) { | |
575 | if (p->highalarm | p->lowalarm) { | |
576 | some_alarms = 1; | |
577 | break; | |
578 | } | |
579 | p = p->next; | |
580 | } | |
581 | gpio_some_alarms = some_alarms; | |
582 | } | |
583 | spin_unlock_irqrestore(&gpio_lock, flags); | |
584 | break; | |
585 | case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ | |
586 | /* Read direction 0=input 1=output */ | |
587 | spin_lock_irqsave(&gpio_lock, flags); | |
588 | if (USE_PORTS(priv)) { | |
589 | ret = *priv->dir_shadow; | |
590 | } else if (priv->minor == GPIO_MINOR_G) { | |
591 | /* Note: Some bits are both in and out, | |
592 | * Those that are dual is set here as well. | |
593 | */ | |
594 | ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF; | |
595 | } | |
596 | spin_unlock_irqrestore(&gpio_lock, flags); | |
597 | break; | |
598 | case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */ | |
599 | /* Set direction 0=unchanged 1=input, | |
600 | * return mask with 1=input | |
601 | */ | |
602 | spin_lock_irqsave(&gpio_lock, flags); | |
603 | ret = setget_input(priv, arg) & 0x7FFFFFFF; | |
604 | spin_unlock_irqrestore(&gpio_lock, flags); | |
605 | break; | |
606 | case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */ | |
607 | /* Set direction 0=unchanged 1=output, | |
608 | * return mask with 1=output | |
609 | */ | |
610 | spin_lock_irqsave(&gpio_lock, flags); | |
611 | ret = setget_output(priv, arg) & 0x7FFFFFFF; | |
612 | spin_unlock_irqrestore(&gpio_lock, flags); | |
613 | break; | |
614 | case IO_SHUTDOWN: | |
615 | spin_lock_irqsave(&gpio_lock, flags); | |
616 | SOFT_SHUTDOWN(); | |
617 | spin_unlock_irqrestore(&gpio_lock, flags); | |
618 | break; | |
619 | case IO_GET_PWR_BT: | |
620 | spin_lock_irqsave(&gpio_lock, flags); | |
621 | #if defined (CONFIG_ETRAX_SOFT_SHUTDOWN) | |
622 | ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT)); | |
623 | #else | |
624 | ret = 0; | |
625 | #endif | |
626 | spin_unlock_irqrestore(&gpio_lock, flags); | |
627 | break; | |
628 | case IO_CFG_WRITE_MODE: | |
629 | spin_lock_irqsave(&gpio_lock, flags); | |
630 | priv->clk_mask = arg & 0xFF; | |
631 | priv->data_mask = (arg >> 8) & 0xFF; | |
632 | priv->write_msb = (arg >> 16) & 0x01; | |
633 | /* Check if we're allowed to change the bits and | |
634 | * the direction is correct | |
635 | */ | |
636 | if (!((priv->clk_mask & priv->changeable_bits) && | |
637 | (priv->data_mask & priv->changeable_bits) && | |
638 | (priv->clk_mask & *priv->dir_shadow) && | |
639 | (priv->data_mask & *priv->dir_shadow))) | |
640 | { | |
641 | priv->clk_mask = 0; | |
642 | priv->data_mask = 0; | |
643 | ret = -EPERM; | |
644 | } | |
645 | spin_unlock_irqrestore(&gpio_lock, flags); | |
646 | break; | |
647 | case IO_READ_INBITS: | |
648 | /* *arg is result of reading the input pins */ | |
649 | spin_lock_irqsave(&gpio_lock, flags); | |
650 | if (USE_PORTS(priv)) { | |
651 | val = *priv->port; | |
652 | } else if (priv->minor == GPIO_MINOR_G) { | |
653 | val = *R_PORT_G_DATA; | |
654 | } | |
655 | spin_unlock_irqrestore(&gpio_lock, flags); | |
656 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
657 | ret = -EFAULT; | |
658 | break; | |
659 | case IO_READ_OUTBITS: | |
660 | /* *arg is result of reading the output shadow */ | |
661 | spin_lock_irqsave(&gpio_lock, flags); | |
662 | if (USE_PORTS(priv)) { | |
663 | val = *priv->shadow; | |
664 | } else if (priv->minor == GPIO_MINOR_G) { | |
665 | val = port_g_data_shadow; | |
666 | } | |
667 | spin_unlock_irqrestore(&gpio_lock, flags); | |
668 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
669 | ret = -EFAULT; | |
670 | break; | |
671 | case IO_SETGET_INPUT: | |
672 | /* bits set in *arg is set to input, | |
673 | * *arg updated with current input pins. | |
674 | */ | |
675 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) | |
676 | { | |
677 | ret = -EFAULT; | |
678 | break; | |
679 | } | |
680 | spin_lock_irqsave(&gpio_lock, flags); | |
681 | val = setget_input(priv, val); | |
682 | spin_unlock_irqrestore(&gpio_lock, flags); | |
683 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
684 | ret = -EFAULT; | |
685 | break; | |
686 | case IO_SETGET_OUTPUT: | |
687 | /* bits set in *arg is set to output, | |
688 | * *arg updated with current output pins. | |
689 | */ | |
690 | if (copy_from_user(&val, (void __user *)arg, sizeof(val))) { | |
691 | ret = -EFAULT; | |
692 | break; | |
693 | } | |
694 | spin_lock_irqsave(&gpio_lock, flags); | |
695 | val = setget_output(priv, val); | |
696 | spin_unlock_irqrestore(&gpio_lock, flags); | |
697 | if (copy_to_user((void __user *)arg, &val, sizeof(val))) | |
698 | ret = -EFAULT; | |
699 | break; | |
700 | default: | |
701 | spin_lock_irqsave(&gpio_lock, flags); | |
702 | if (priv->minor == GPIO_MINOR_LEDS) | |
703 | ret = gpio_leds_ioctl(cmd, arg); | |
704 | else | |
705 | ret = -EINVAL; | |
706 | spin_unlock_irqrestore(&gpio_lock, flags); | |
707 | } /* switch */ | |
708 | ||
709 | return ret; | |
710 | } | |
711 | ||
712 | static int | |
713 | gpio_leds_ioctl(unsigned int cmd, unsigned long arg) | |
714 | { | |
715 | unsigned char green; | |
716 | unsigned char red; | |
717 | ||
718 | switch (_IOC_NR(cmd)) { | |
719 | case IO_LEDACTIVE_SET: | |
720 | green = ((unsigned char)arg) & 1; | |
721 | red = (((unsigned char)arg) >> 1) & 1; | |
722 | CRIS_LED_ACTIVE_SET_G(green); | |
723 | CRIS_LED_ACTIVE_SET_R(red); | |
724 | break; | |
725 | ||
726 | case IO_LED_SETBIT: | |
727 | CRIS_LED_BIT_SET(arg); | |
728 | break; | |
729 | ||
730 | case IO_LED_CLRBIT: | |
731 | CRIS_LED_BIT_CLR(arg); | |
732 | break; | |
733 | ||
734 | default: | |
735 | return -EINVAL; | |
736 | } /* switch */ | |
737 | ||
738 | return 0; | |
739 | } | |
740 | ||
741 | static const struct file_operations gpio_fops = { | |
742 | .owner = THIS_MODULE, | |
743 | .poll = gpio_poll, | |
744 | .unlocked_ioctl = gpio_ioctl, | |
745 | .write = gpio_write, | |
746 | .open = gpio_open, | |
747 | .release = gpio_release, | |
748 | .llseek = noop_llseek, | |
749 | }; | |
750 | ||
751 | static void ioif_watcher(const unsigned int gpio_in_available, | |
752 | const unsigned int gpio_out_available, | |
753 | const unsigned char pa_available, | |
754 | const unsigned char pb_available) | |
755 | { | |
756 | unsigned long int flags; | |
757 | ||
758 | D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n")); | |
759 | D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x " | |
760 | "PA: 0x%02x PB: 0x%02x\n", | |
761 | gpio_in_available, gpio_out_available, | |
762 | pa_available, pb_available)); | |
763 | ||
764 | spin_lock_irqsave(&gpio_lock, flags); | |
765 | ||
766 | dir_g_in_bits = gpio_in_available; | |
767 | dir_g_out_bits = gpio_out_available; | |
768 | ||
769 | /* Initialise the dir_g_shadow etc. depending on genconfig */ | |
770 | /* 0=input 1=output */ | |
771 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) | |
772 | dir_g_shadow |= (1 << 0); | |
773 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) | |
774 | dir_g_shadow |= 0x0000FF00; | |
775 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out)) | |
776 | dir_g_shadow |= 0x00FF0000; | |
777 | if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out)) | |
778 | dir_g_shadow |= (1 << 24); | |
779 | ||
780 | changeable_dir_g = changeable_dir_g_mask; | |
781 | changeable_dir_g &= dir_g_out_bits; | |
782 | changeable_dir_g &= dir_g_in_bits; | |
783 | ||
784 | /* Correct the bits that can change direction */ | |
785 | dir_g_out_bits &= ~changeable_dir_g; | |
786 | dir_g_out_bits |= dir_g_shadow; | |
787 | dir_g_in_bits &= ~changeable_dir_g; | |
788 | dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g); | |
789 | ||
790 | spin_unlock_irqrestore(&gpio_lock, flags); | |
791 | ||
792 | printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX " | |
793 | "val: %08lX\n", | |
794 | dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); | |
795 | printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", | |
796 | dir_g_shadow, changeable_dir_g); | |
797 | } | |
798 | ||
799 | /* main driver initialization routine, called from mem.c */ | |
800 | ||
801 | static int __init gpio_init(void) | |
802 | { | |
803 | int res; | |
804 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | |
805 | int i; | |
806 | #endif | |
807 | ||
808 | res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); | |
809 | if (res < 0) { | |
810 | printk(KERN_ERR "gpio: couldn't get a major number.\n"); | |
811 | return res; | |
812 | } | |
813 | ||
814 | /* Clear all leds */ | |
815 | #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) | |
816 | CRIS_LED_NETWORK_SET(0); | |
817 | CRIS_LED_ACTIVE_SET(0); | |
818 | CRIS_LED_DISK_READ(0); | |
819 | CRIS_LED_DISK_WRITE(0); | |
820 | ||
821 | #if defined (CONFIG_ETRAX_CSP0_LEDS) | |
822 | for (i = 0; i < 32; i++) | |
823 | CRIS_LED_BIT_SET(i); | |
824 | #endif | |
825 | ||
826 | #endif | |
827 | /* The I/O interface allocation watcher will be called when | |
828 | * registering it. */ | |
829 | if (cris_io_interface_register_watcher(ioif_watcher)){ | |
830 | printk(KERN_WARNING "gpio_init: Failed to install IO " | |
831 | "if allocator watcher\n"); | |
832 | } | |
833 | ||
834 | printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 " | |
835 | "Axis Communications AB\n"); | |
836 | /* We call etrax_gpio_wake_up_check() from timer interrupt and | |
837 | * from default_idle() in kernel/process.c | |
838 | * The check in default_idle() reduces latency from ~15 ms to ~6 ms | |
839 | * in some tests. | |
840 | */ | |
841 | res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, | |
842 | IRQF_SHARED, "gpio poll", gpio_name); | |
843 | if (res) { | |
844 | printk(KERN_CRIT "err: timer0 irq for gpio\n"); | |
845 | return res; | |
846 | } | |
847 | res = request_irq(PA_IRQ_NBR, gpio_interrupt, | |
848 | IRQF_SHARED, "gpio PA", gpio_name); | |
849 | if (res) | |
850 | printk(KERN_CRIT "err: PA irq for gpio\n"); | |
851 | ||
852 | return res; | |
853 | } | |
854 | ||
855 | /* this makes sure that gpio_init is called during kernel boot */ | |
856 | module_init(gpio_init); | |
857 |