blob: 09963fe299a7d4ea364c757144c72b5a1db01c2c [file] [log] [blame]
Mikael Starvik7e920422005-07-27 11:44:34 -07001/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 *
3 * Etrax general port I/O device
4 *
5 * Copyright (c) 1999, 2000, 2001, 2002 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 * $Log: gpio.c,v $
Mikael Starvik7e920422005-07-27 11:44:34 -070012 * Revision 1.17 2005/06/19 17:06:46 starvik
13 * Merge of Linux 2.6.12.
14 *
15 * Revision 1.16 2005/03/07 13:02:29 starvik
16 * Protect driver global states with spinlock
17 *
18 * Revision 1.15 2005/01/05 06:08:55 starvik
19 * No need to do local_irq_disable after local_irq_save.
20 *
21 * Revision 1.14 2004/12/13 12:21:52 starvik
22 * Added I/O and DMA allocators from Linux 2.4
23 *
Linus Torvalds1da177e2005-04-16 15:20:36 -070024 * Revision 1.12 2004/08/24 07:19:59 starvik
25 * Whitespace cleanup
26 *
27 * Revision 1.11 2004/05/14 07:58:03 starvik
28 * Merge of changes from 2.4
29 *
30 * Revision 1.9 2003/09/11 07:29:48 starvik
31 * Merge of Linux 2.6.0-test5
32 *
33 * Revision 1.8 2003/07/04 08:27:37 starvik
34 * Merge of Linux 2.5.74
35 *
36 * Revision 1.7 2003/01/10 07:44:07 starvik
37 * init_ioremap is now called by kernel before drivers are initialized
38 *
39 * Revision 1.6 2002/12/11 13:13:57 starvik
40 * Added arch/ to v10 specific includes
41 * Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
42 *
43 * Revision 1.5 2002/11/20 11:56:11 starvik
44 * Merge of Linux 2.5.48
45 *
46 * Revision 1.4 2002/11/18 10:10:05 starvik
47 * Linux 2.5 port of latest gpio.c from Linux 2.4
48 *
49 * Revision 1.20 2002/10/16 21:16:24 johana
50 * Added support for PA high level interrupt.
51 * That gives 2ms response time with iodtest for high levels and 2-12 ms
52 * response time on low levels if the check is not made in
53 * process.c:cpu_idle() as well.
54 *
55 * Revision 1.19 2002/10/14 18:27:33 johana
56 * Implemented alarm handling so select() now works.
57 * Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in
58 * cpu_idle().
59 * Otherwise I get 15-18 ms (same as doing the poll in userspace -
60 * but less overhead).
61 * TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it
62 * is in 2.4) as well?
63 * TODO? Perhaps call request_irq()/free_irq() only when needed?
64 * Increased version to 2.5
65 *
66 * Revision 1.18 2002/10/11 15:02:00 johana
67 * Mask inverted 8 bit value in setget_input().
68 *
69 * Revision 1.17 2002/06/17 15:53:01 johana
70 * Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT
71 * that take a pointer as argument and thus can handle 32 bit ports (G)
72 * correctly.
73 * These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT.
74 * (especially if Port G bit 31 is used)
75 *
76 * Revision 1.16 2002/06/17 09:59:51 johana
77 * Returning 32 bit values in the ioctl return value doesn't work if bit
78 * 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF.
79 * A new set of ioctl's will be added.
80 *
81 * Revision 1.15 2002/05/06 13:19:13 johana
82 * IO_SETINPUT returns mask with bit set = inputs for PA and PB as well.
83 *
84 * Revision 1.14 2002/04/12 12:01:53 johana
85 * Use global r_port_g_data_shadow.
86 * Moved gpio_init_port_g() closer to gpio_init() and marked it __init.
87 *
88 * Revision 1.13 2002/04/10 12:03:55 johana
89 * Added support for port G /dev/gpiog (minor 3).
90 * Changed indentation on switch cases.
91 * Fixed other spaces to tabs.
92 *
93 * Revision 1.12 2001/11/12 19:42:15 pkj
94 * * Corrected return values from gpio_leds_ioctl().
95 * * Fixed compiler warnings.
96 *
97 * Revision 1.11 2001/10/30 14:39:12 johana
98 * Added D() around gpio_write printk.
99 *
100 * Revision 1.10 2001/10/25 10:24:42 johana
101 * Added IO_CFG_WRITE_MODE ioctl and write method that can do fast
102 * bittoggling in the kernel. (This speeds up programming an FPGA with 450kB
103 * from ~60 seconds to 4 seconds).
104 * Added save_flags/cli/restore_flags in ioctl.
105 *
106 * Revision 1.9 2001/05/04 14:16:07 matsfg
107 * Corrected spelling error
108 *
109 * Revision 1.8 2001/04/27 13:55:26 matsfg
110 * Moved initioremap.
111 * Turns off all LEDS on init.
112 * Added support for shutdown and powerbutton.
113 *
114 * Revision 1.7 2001/04/04 13:30:08 matsfg
115 * Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping
116 *
117 * Revision 1.6 2001/03/26 16:03:06 bjornw
118 * Needs linux/config.h
119 *
120 * Revision 1.5 2001/03/26 14:22:03 bjornw
121 * Namechange of some config options
122 *
123 * Revision 1.4 2001/02/27 13:52:48 bjornw
124 * malloc.h -> slab.h
125 *
126 * Revision 1.3 2001/01/24 15:06:48 bjornw
127 * gpio_wq correct type
128 *
129 * Revision 1.2 2001/01/18 16:07:30 bjornw
130 * 2.4 port
131 *
132 * Revision 1.1 2001/01/18 15:55:16 bjornw
133 * Verbatim copy of etraxgpio.c from elinux 2.0 added
134 *
135 *
136 */
137
138#include <linux/config.h>
139
140#include <linux/module.h>
141#include <linux/sched.h>
142#include <linux/slab.h>
143#include <linux/ioport.h>
144#include <linux/errno.h>
145#include <linux/kernel.h>
146#include <linux/fs.h>
147#include <linux/string.h>
148#include <linux/poll.h>
149#include <linux/init.h>
150#include <linux/interrupt.h>
151
152#include <asm/etraxgpio.h>
153#include <asm/arch/svinto.h>
154#include <asm/io.h>
155#include <asm/system.h>
156#include <asm/irq.h>
Mikael Starvik7e920422005-07-27 11:44:34 -0700157#include <asm/arch/io_interface_mux.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700158
159#define GPIO_MAJOR 120 /* experimental MAJOR number */
160
161#define D(x)
162
163#if 0
164static int dp_cnt;
165#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
166#else
167#define DP(x)
168#endif
169
170static char gpio_name[] = "etrax gpio";
171
172#if 0
173static wait_queue_head_t *gpio_wq;
174#endif
175
176static int gpio_ioctl(struct inode *inode, struct file *file,
177 unsigned int cmd, unsigned long arg);
178static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
179 loff_t *off);
180static int gpio_open(struct inode *inode, struct file *filp);
181static int gpio_release(struct inode *inode, struct file *filp);
182static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
183
184/* private data per open() of this driver */
185
186struct gpio_private {
187 struct gpio_private *next;
188 /* These fields are for PA and PB only */
189 volatile unsigned char *port, *shadow;
190 volatile unsigned char *dir, *dir_shadow;
191 unsigned char changeable_dir;
192 unsigned char changeable_bits;
193 unsigned char clk_mask;
194 unsigned char data_mask;
195 unsigned char write_msb;
196 unsigned char pad1, pad2, pad3;
197 /* These fields are generic */
198 unsigned long highalarm, lowalarm;
199 wait_queue_head_t alarm_wq;
200 int minor;
201};
202
203/* linked list of alarms to check for */
204
205static struct gpio_private *alarmlist = 0;
206
207static int gpio_some_alarms = 0; /* Set if someone uses alarm */
208static unsigned long gpio_pa_irq_enabled_mask = 0;
209
Mikael Starvik7e920422005-07-27 11:44:34 -0700210static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
211
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212/* Port A and B use 8 bit access, but Port G is 32 bit */
213#define NUM_PORTS (GPIO_MINOR_B+1)
214
215static volatile unsigned char *ports[NUM_PORTS] = {
216 R_PORT_PA_DATA,
217 R_PORT_PB_DATA,
218};
219static volatile unsigned char *shads[NUM_PORTS] = {
220 &port_pa_data_shadow,
221 &port_pb_data_shadow
222};
223
224/* What direction bits that are user changeable 1=changeable*/
225#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
226#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
227#endif
228#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
229#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
230#endif
231
232#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
233#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
234#endif
235#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
236#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
237#endif
238
239
240static unsigned char changeable_dir[NUM_PORTS] = {
241 CONFIG_ETRAX_PA_CHANGEABLE_DIR,
242 CONFIG_ETRAX_PB_CHANGEABLE_DIR
243};
244static unsigned char changeable_bits[NUM_PORTS] = {
245 CONFIG_ETRAX_PA_CHANGEABLE_BITS,
246 CONFIG_ETRAX_PB_CHANGEABLE_BITS
247};
248
249static volatile unsigned char *dir[NUM_PORTS] = {
250 R_PORT_PA_DIR,
251 R_PORT_PB_DIR
252};
253
254static volatile unsigned char *dir_shadow[NUM_PORTS] = {
255 &port_pa_dir_shadow,
256 &port_pb_dir_shadow
257};
258
Mikael Starvik7e920422005-07-27 11:44:34 -0700259/* All bits in port g that can change dir. */
260static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
261
Linus Torvalds1da177e2005-04-16 15:20:36 -0700262/* Port G is 32 bit, handle it special, some bits are both inputs
263 and outputs at the same time, only some of the bits can change direction
264 and some of them in groups of 8 bit. */
265static unsigned long changeable_dir_g;
266static unsigned long dir_g_in_bits;
267static unsigned long dir_g_out_bits;
268static unsigned long dir_g_shadow; /* 1=output */
269
270#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
271
272
273
274static unsigned int
275gpio_poll(struct file *file,
276 poll_table *wait)
277{
278 unsigned int mask = 0;
279 struct gpio_private *priv = (struct gpio_private *)file->private_data;
280 unsigned long data;
Mikael Starvik7e920422005-07-27 11:44:34 -0700281 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700282 poll_wait(file, &priv->alarm_wq, wait);
283 if (priv->minor == GPIO_MINOR_A) {
284 unsigned long flags;
285 unsigned long tmp;
286 data = *R_PORT_PA_DATA;
287 /* PA has support for high level interrupt -
288 * lets activate for those low and with highalarm set
289 */
290 tmp = ~data & priv->highalarm & 0xFF;
291 tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
Mikael Starvik7e920422005-07-27 11:44:34 -0700292 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700293 gpio_pa_irq_enabled_mask |= tmp;
294 *R_IRQ_MASK1_SET = tmp;
Mikael Starvik7e920422005-07-27 11:44:34 -0700295 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296
297 } else if (priv->minor == GPIO_MINOR_B)
298 data = *R_PORT_PB_DATA;
299 else if (priv->minor == GPIO_MINOR_G)
300 data = *R_PORT_G_DATA;
301 else
302 return 0;
303
304 if ((data & priv->highalarm) ||
305 (~data & priv->lowalarm)) {
306 mask = POLLIN|POLLRDNORM;
307 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700308
309 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700310
311 DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
Mikael Starvik7e920422005-07-27 11:44:34 -0700312
Linus Torvalds1da177e2005-04-16 15:20:36 -0700313 return mask;
314}
315
316int etrax_gpio_wake_up_check(void)
317{
318 struct gpio_private *priv = alarmlist;
319 unsigned long data = 0;
320 int ret = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700321 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322 while (priv) {
323 if (USE_PORTS(priv)) {
324 data = *priv->port;
325 } else if (priv->minor == GPIO_MINOR_G) {
326 data = *R_PORT_G_DATA;
327 }
328 if ((data & priv->highalarm) ||
329 (~data & priv->lowalarm)) {
330 DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
331 wake_up_interruptible(&priv->alarm_wq);
332 ret = 1;
333 }
334 priv = priv->next;
335 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700336 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700337 return ret;
338}
339
340static irqreturn_t
341gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
342{
343 if (gpio_some_alarms) {
344 etrax_gpio_wake_up_check();
345 return IRQ_HANDLED;
346 }
347 return IRQ_NONE;
348}
349
350static irqreturn_t
351gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
352{
353 unsigned long tmp;
Mikael Starvik7e920422005-07-27 11:44:34 -0700354 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355 /* Find what PA interrupts are active */
356 tmp = (*R_IRQ_READ1);
357
358 /* Find those that we have enabled */
359 tmp &= gpio_pa_irq_enabled_mask;
360
361 /* Clear them.. */
362 *R_IRQ_MASK1_CLR = tmp;
363 gpio_pa_irq_enabled_mask &= ~tmp;
364
Mikael Starvik7e920422005-07-27 11:44:34 -0700365 spin_unlock(&gpio_lock);
366
Linus Torvalds1da177e2005-04-16 15:20:36 -0700367 if (gpio_some_alarms) {
368 return IRQ_RETVAL(etrax_gpio_wake_up_check());
369 }
370 return IRQ_NONE;
371}
372
373
374static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
375 loff_t *off)
376{
377 struct gpio_private *priv = (struct gpio_private *)file->private_data;
378 unsigned char data, clk_mask, data_mask, write_msb;
379 unsigned long flags;
Mikael Starvik7e920422005-07-27 11:44:34 -0700380
381 spin_lock(&gpio_lock);
382
Linus Torvalds1da177e2005-04-16 15:20:36 -0700383 ssize_t retval = count;
384 if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
385 return -EFAULT;
386 }
387
388 if (!access_ok(VERIFY_READ, buf, count)) {
389 return -EFAULT;
390 }
391 clk_mask = priv->clk_mask;
392 data_mask = priv->data_mask;
393 /* It must have been configured using the IO_CFG_WRITE_MODE */
394 /* Perhaps a better error code? */
395 if (clk_mask == 0 || data_mask == 0) {
396 return -EPERM;
397 }
398 write_msb = priv->write_msb;
399 D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
400 while (count--) {
401 int i;
402 data = *buf++;
403 if (priv->write_msb) {
404 for (i = 7; i >= 0;i--) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700405 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406 *priv->port = *priv->shadow &= ~clk_mask;
407 if (data & 1<<i)
408 *priv->port = *priv->shadow |= data_mask;
409 else
410 *priv->port = *priv->shadow &= ~data_mask;
411 /* For FPGA: min 5.0ns (DCC) before CCLK high */
412 *priv->port = *priv->shadow |= clk_mask;
413 local_irq_restore(flags);
414 }
415 } else {
416 for (i = 0; i <= 7;i++) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700417 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700418 *priv->port = *priv->shadow &= ~clk_mask;
419 if (data & 1<<i)
420 *priv->port = *priv->shadow |= data_mask;
421 else
422 *priv->port = *priv->shadow &= ~data_mask;
423 /* For FPGA: min 5.0ns (DCC) before CCLK high */
424 *priv->port = *priv->shadow |= clk_mask;
425 local_irq_restore(flags);
426 }
427 }
428 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700429 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700430 return retval;
431}
432
433
434
435static int
436gpio_open(struct inode *inode, struct file *filp)
437{
438 struct gpio_private *priv;
439 int p = MINOR(inode->i_rdev);
440
441 if (p > GPIO_MINOR_LAST)
442 return -EINVAL;
443
444 priv = (struct gpio_private *)kmalloc(sizeof(struct gpio_private),
445 GFP_KERNEL);
446
447 if (!priv)
448 return -ENOMEM;
449
450 priv->minor = p;
451
452 /* initialize the io/alarm struct and link it into our alarmlist */
453
454 priv->next = alarmlist;
455 alarmlist = priv;
456 if (USE_PORTS(priv)) { /* A and B */
457 priv->port = ports[p];
458 priv->shadow = shads[p];
459 priv->dir = dir[p];
460 priv->dir_shadow = dir_shadow[p];
461 priv->changeable_dir = changeable_dir[p];
462 priv->changeable_bits = changeable_bits[p];
463 } else {
464 priv->port = NULL;
465 priv->shadow = NULL;
466 priv->dir = NULL;
467 priv->dir_shadow = NULL;
468 priv->changeable_dir = 0;
469 priv->changeable_bits = 0;
470 }
471
472 priv->highalarm = 0;
473 priv->lowalarm = 0;
474 priv->clk_mask = 0;
475 priv->data_mask = 0;
476 init_waitqueue_head(&priv->alarm_wq);
477
478 filp->private_data = (void *)priv;
479
480 return 0;
481}
482
483static int
484gpio_release(struct inode *inode, struct file *filp)
485{
Mikael Starvik7e920422005-07-27 11:44:34 -0700486 struct gpio_private *p;
487 struct gpio_private *todel;
488
489 spin_lock(&gpio_lock);
490
491 p = alarmlist;
492 todel = (struct gpio_private *)filp->private_data;
493
Linus Torvalds1da177e2005-04-16 15:20:36 -0700494 /* unlink from alarmlist and free the private structure */
495
496 if (p == todel) {
497 alarmlist = todel->next;
498 } else {
499 while (p->next != todel)
500 p = p->next;
501 p->next = todel->next;
502 }
503
504 kfree(todel);
505 /* Check if there are still any alarms set */
506 p = alarmlist;
507 while (p) {
508 if (p->highalarm | p->lowalarm) {
509 gpio_some_alarms = 1;
510 return 0;
511 }
512 p = p->next;
513 }
514 gpio_some_alarms = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700515 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 return 0;
517}
518
519/* Main device API. ioctl's to read/set/clear bits, as well as to
520 * set alarms to wait for using a subsequent select().
521 */
522
523unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
524{
525 /* Set direction 0=unchanged 1=input,
526 * return mask with 1=input
527 */
528 unsigned long flags;
529 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700530 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 *priv->dir = *priv->dir_shadow &=
532 ~((unsigned char)arg & priv->changeable_dir);
533 local_irq_restore(flags);
534 return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
535 } else if (priv->minor == GPIO_MINOR_G) {
536 /* We must fiddle with R_GEN_CONFIG to change dir */
Mikael Starvik7e920422005-07-27 11:44:34 -0700537 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 if (((arg & dir_g_in_bits) != arg) &&
539 (arg & changeable_dir_g)) {
540 arg &= changeable_dir_g;
541 /* Clear bits in genconfig to set to input */
542 if (arg & (1<<0)) {
543 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir);
544 dir_g_in_bits |= (1<<0);
545 dir_g_out_bits &= ~(1<<0);
546 }
547 if ((arg & 0x0000FF00) == 0x0000FF00) {
548 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir);
549 dir_g_in_bits |= 0x0000FF00;
550 dir_g_out_bits &= ~0x0000FF00;
551 }
552 if ((arg & 0x00FF0000) == 0x00FF0000) {
553 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir);
554 dir_g_in_bits |= 0x00FF0000;
555 dir_g_out_bits &= ~0x00FF0000;
556 }
557 if (arg & (1<<24)) {
558 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir);
559 dir_g_in_bits |= (1<<24);
560 dir_g_out_bits &= ~(1<<24);
561 }
562 D(printk(KERN_INFO "gpio: SETINPUT on port G set "
563 "genconfig to 0x%08lX "
564 "in_bits: 0x%08lX "
565 "out_bits: 0x%08lX\n",
566 (unsigned long)genconfig_shadow,
567 dir_g_in_bits, dir_g_out_bits));
568 *R_GEN_CONFIG = genconfig_shadow;
569 /* Must be a >120 ns delay before writing this again */
570
571 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700572 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700573 return dir_g_in_bits;
574 }
575 return 0;
576} /* setget_input */
577
578unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
579{
580 unsigned long flags;
581 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700582 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 *priv->dir = *priv->dir_shadow |=
584 ((unsigned char)arg & priv->changeable_dir);
585 local_irq_restore(flags);
586 return *priv->dir_shadow;
587 } else if (priv->minor == GPIO_MINOR_G) {
588 /* We must fiddle with R_GEN_CONFIG to change dir */
Mikael Starvik7e920422005-07-27 11:44:34 -0700589 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 if (((arg & dir_g_out_bits) != arg) &&
591 (arg & changeable_dir_g)) {
592 /* Set bits in genconfig to set to output */
593 if (arg & (1<<0)) {
594 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir);
595 dir_g_out_bits |= (1<<0);
596 dir_g_in_bits &= ~(1<<0);
597 }
598 if ((arg & 0x0000FF00) == 0x0000FF00) {
599 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir);
600 dir_g_out_bits |= 0x0000FF00;
601 dir_g_in_bits &= ~0x0000FF00;
602 }
603 if ((arg & 0x00FF0000) == 0x00FF0000) {
604 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir);
605 dir_g_out_bits |= 0x00FF0000;
606 dir_g_in_bits &= ~0x00FF0000;
607 }
608 if (arg & (1<<24)) {
609 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir);
610 dir_g_out_bits |= (1<<24);
611 dir_g_in_bits &= ~(1<<24);
612 }
613 D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
614 "genconfig to 0x%08lX "
615 "in_bits: 0x%08lX "
616 "out_bits: 0x%08lX\n",
617 (unsigned long)genconfig_shadow,
618 dir_g_in_bits, dir_g_out_bits));
619 *R_GEN_CONFIG = genconfig_shadow;
620 /* Must be a >120 ns delay before writing this again */
621 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700622 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700623 return dir_g_out_bits & 0x7FFFFFFF;
624 }
625 return 0;
626} /* setget_output */
627
628static int
629gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
630
631static int
632gpio_ioctl(struct inode *inode, struct file *file,
633 unsigned int cmd, unsigned long arg)
634{
635 unsigned long flags;
636 unsigned long val;
Mikael Starvik7e920422005-07-27 11:44:34 -0700637 int ret = 0;
638
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 struct gpio_private *priv = (struct gpio_private *)file->private_data;
640 if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
641 return -EINVAL;
642 }
643
Mikael Starvik7e920422005-07-27 11:44:34 -0700644 spin_lock(&gpio_lock);
645
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 switch (_IOC_NR(cmd)) {
647 case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
648 // read the port
649 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700650 ret = *priv->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700651 } else if (priv->minor == GPIO_MINOR_G) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700652 ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700653 }
654 break;
655 case IO_SETBITS:
Mikael Starvik7e920422005-07-27 11:44:34 -0700656 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 // set changeable bits with a 1 in arg
658 if (USE_PORTS(priv)) {
659 *priv->port = *priv->shadow |=
660 ((unsigned char)arg & priv->changeable_bits);
661 } else if (priv->minor == GPIO_MINOR_G) {
662 *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
663 }
664 local_irq_restore(flags);
665 break;
666 case IO_CLRBITS:
Mikael Starvik7e920422005-07-27 11:44:34 -0700667 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 // clear changeable bits with a 1 in arg
669 if (USE_PORTS(priv)) {
670 *priv->port = *priv->shadow &=
671 ~((unsigned char)arg & priv->changeable_bits);
672 } else if (priv->minor == GPIO_MINOR_G) {
673 *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
674 }
675 local_irq_restore(flags);
676 break;
677 case IO_HIGHALARM:
678 // set alarm when bits with 1 in arg go high
679 priv->highalarm |= arg;
680 gpio_some_alarms = 1;
681 break;
682 case IO_LOWALARM:
683 // set alarm when bits with 1 in arg go low
684 priv->lowalarm |= arg;
685 gpio_some_alarms = 1;
686 break;
687 case IO_CLRALARM:
688 // clear alarm for bits with 1 in arg
689 priv->highalarm &= ~arg;
690 priv->lowalarm &= ~arg;
691 {
692 /* Must update gpio_some_alarms */
693 struct gpio_private *p = alarmlist;
694 int some_alarms;
695 some_alarms = 0;
696 while (p) {
697 if (p->highalarm | p->lowalarm) {
698 some_alarms = 1;
699 break;
700 }
701 p = p->next;
702 }
703 gpio_some_alarms = some_alarms;
704 }
705 break;
706 case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
707 /* Read direction 0=input 1=output */
708 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700709 ret = *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700710 } else if (priv->minor == GPIO_MINOR_G) {
711 /* Note: Some bits are both in and out,
712 * Those that are dual is set here as well.
713 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700714 ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700716 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700717 case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
718 /* Set direction 0=unchanged 1=input,
719 * return mask with 1=input
720 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700721 ret = setget_input(priv, arg) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700722 break;
723 case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
724 /* Set direction 0=unchanged 1=output,
725 * return mask with 1=output
726 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700727 ret = setget_output(priv, arg) & 0x7FFFFFFF;
728 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700729 case IO_SHUTDOWN:
730 SOFT_SHUTDOWN();
731 break;
732 case IO_GET_PWR_BT:
733#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
Mikael Starvik7e920422005-07-27 11:44:34 -0700734 ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735#else
Mikael Starvik7e920422005-07-27 11:44:34 -0700736 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737#endif
738 break;
739 case IO_CFG_WRITE_MODE:
740 priv->clk_mask = arg & 0xFF;
741 priv->data_mask = (arg >> 8) & 0xFF;
742 priv->write_msb = (arg >> 16) & 0x01;
743 /* Check if we're allowed to change the bits and
744 * the direction is correct
745 */
746 if (!((priv->clk_mask & priv->changeable_bits) &&
747 (priv->data_mask & priv->changeable_bits) &&
748 (priv->clk_mask & *priv->dir_shadow) &&
749 (priv->data_mask & *priv->dir_shadow)))
750 {
751 priv->clk_mask = 0;
752 priv->data_mask = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700753 ret = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 }
755 break;
756 case IO_READ_INBITS:
757 /* *arg is result of reading the input pins */
758 if (USE_PORTS(priv)) {
759 val = *priv->port;
760 } else if (priv->minor == GPIO_MINOR_G) {
761 val = *R_PORT_G_DATA;
762 }
763 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700764 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700765 break;
766 case IO_READ_OUTBITS:
767 /* *arg is result of reading the output shadow */
768 if (USE_PORTS(priv)) {
769 val = *priv->shadow;
770 } else if (priv->minor == GPIO_MINOR_G) {
771 val = port_g_data_shadow;
772 }
773 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700774 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775 break;
776 case IO_SETGET_INPUT:
777 /* bits set in *arg is set to input,
778 * *arg updated with current input pins.
779 */
780 if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700781 {
782 ret = -EFAULT;
783 break;
784 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700785 val = setget_input(priv, val);
786 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700787 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700788 break;
789 case IO_SETGET_OUTPUT:
790 /* bits set in *arg is set to output,
791 * *arg updated with current output pins.
792 */
793 if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700794 {
795 ret = -EFAULT;
796 break;
797 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700798 val = setget_output(priv, val);
799 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700800 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700801 break;
802 default:
803 if (priv->minor == GPIO_MINOR_LEDS)
Mikael Starvik7e920422005-07-27 11:44:34 -0700804 ret = gpio_leds_ioctl(cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700805 else
Mikael Starvik7e920422005-07-27 11:44:34 -0700806 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 } /* switch */
Mikael Starvik7e920422005-07-27 11:44:34 -0700808
809 spin_unlock(&gpio_lock);
810 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811}
812
813static int
814gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
815{
816 unsigned char green;
817 unsigned char red;
818
819 switch (_IOC_NR(cmd)) {
820 case IO_LEDACTIVE_SET:
821 green = ((unsigned char) arg) & 1;
822 red = (((unsigned char) arg) >> 1) & 1;
823 LED_ACTIVE_SET_G(green);
824 LED_ACTIVE_SET_R(red);
825 break;
826
827 case IO_LED_SETBIT:
828 LED_BIT_SET(arg);
829 break;
830
831 case IO_LED_CLRBIT:
832 LED_BIT_CLR(arg);
833 break;
834
835 default:
836 return -EINVAL;
837 } /* switch */
838
839 return 0;
840}
841
842struct file_operations gpio_fops = {
843 .owner = THIS_MODULE,
844 .poll = gpio_poll,
845 .ioctl = gpio_ioctl,
846 .write = gpio_write,
847 .open = gpio_open,
848 .release = gpio_release,
849};
850
851
Mikael Starvik7e920422005-07-27 11:44:34 -0700852void ioif_watcher(const unsigned int gpio_in_available,
853 const unsigned int gpio_out_available,
854 const unsigned char pa_available,
855 const unsigned char pb_available)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700856{
Mikael Starvik7e920422005-07-27 11:44:34 -0700857 unsigned long int flags;
858 D(printk("gpio.c: ioif_watcher called\n"));
859 D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n",
860 gpio_in_available, gpio_out_available, pa_available, pb_available));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700861
Mikael Starvik7e920422005-07-27 11:44:34 -0700862 spin_lock_irqsave(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700863
Mikael Starvik7e920422005-07-27 11:44:34 -0700864 dir_g_in_bits = gpio_in_available;
865 dir_g_out_bits = gpio_out_available;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700866
867 /* Initialise the dir_g_shadow etc. depending on genconfig */
868 /* 0=input 1=output */
869 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
870 dir_g_shadow |= (1 << 0);
871 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
872 dir_g_shadow |= 0x0000FF00;
873 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
874 dir_g_shadow |= 0x00FF0000;
875 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
876 dir_g_shadow |= (1 << 24);
877
Mikael Starvik7e920422005-07-27 11:44:34 -0700878 changeable_dir_g = changeable_dir_g_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700879 changeable_dir_g &= dir_g_out_bits;
880 changeable_dir_g &= dir_g_in_bits;
881 /* Correct the bits that can change direction */
882 dir_g_out_bits &= ~changeable_dir_g;
883 dir_g_out_bits |= dir_g_shadow;
884 dir_g_in_bits &= ~changeable_dir_g;
885 dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
886
Mikael Starvik7e920422005-07-27 11:44:34 -0700887 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700888
889 printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
890 dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
891 printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
892 dir_g_shadow, changeable_dir_g);
893}
894
895/* main driver initialization routine, called from mem.c */
896
897static __init int
898gpio_init(void)
899{
900 int res;
901#if defined (CONFIG_ETRAX_CSP0_LEDS)
902 int i;
903#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700904 printk("gpio init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700905
906 /* do the formalities */
907
908 res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
909 if (res < 0) {
910 printk(KERN_ERR "gpio: couldn't get a major number.\n");
911 return res;
912 }
913
914 /* Clear all leds */
915#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
916 LED_NETWORK_SET(0);
917 LED_ACTIVE_SET(0);
918 LED_DISK_READ(0);
919 LED_DISK_WRITE(0);
920
921#if defined (CONFIG_ETRAX_CSP0_LEDS)
922 for (i = 0; i < 32; i++) {
923 LED_BIT_SET(i);
924 }
925#endif
926
927#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700928 /* The I/O interface allocation watcher will be called when
929 * registering it. */
930 if (cris_io_interface_register_watcher(ioif_watcher)){
931 printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n");
932 }
933
934 printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700935 /* We call etrax_gpio_wake_up_check() from timer interrupt and
936 * from cpu_idle() in kernel/process.c
937 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
938 * in some tests.
939 */
940 if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
941 SA_SHIRQ | SA_INTERRUPT,"gpio poll", NULL)) {
942 printk(KERN_CRIT "err: timer0 irq for gpio\n");
943 }
944 if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
945 SA_SHIRQ | SA_INTERRUPT,"gpio PA", NULL)) {
946 printk(KERN_CRIT "err: PA irq for gpio\n");
947 }
948
949
950 return res;
951}
952
953/* this makes sure that gpio_init is called during kernel boot */
954
955module_init(gpio_init);