blob: 0d347a7058355c0d9ff7e90cbd0e73a0b535b6af [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
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138
139#include <linux/module.h>
140#include <linux/sched.h>
141#include <linux/slab.h>
142#include <linux/ioport.h>
143#include <linux/errno.h>
144#include <linux/kernel.h>
145#include <linux/fs.h>
146#include <linux/string.h>
147#include <linux/poll.h>
148#include <linux/init.h>
149#include <linux/interrupt.h>
150
151#include <asm/etraxgpio.h>
152#include <asm/arch/svinto.h>
153#include <asm/io.h>
154#include <asm/system.h>
155#include <asm/irq.h>
Mikael Starvik7e920422005-07-27 11:44:34 -0700156#include <asm/arch/io_interface_mux.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157
158#define GPIO_MAJOR 120 /* experimental MAJOR number */
159
160#define D(x)
161
162#if 0
163static int dp_cnt;
164#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
165#else
166#define DP(x)
167#endif
168
169static char gpio_name[] = "etrax gpio";
170
171#if 0
172static wait_queue_head_t *gpio_wq;
173#endif
174
175static int gpio_ioctl(struct inode *inode, struct file *file,
176 unsigned int cmd, unsigned long arg);
177static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
178 loff_t *off);
179static int gpio_open(struct inode *inode, struct file *filp);
180static int gpio_release(struct inode *inode, struct file *filp);
181static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
182
183/* private data per open() of this driver */
184
185struct gpio_private {
186 struct gpio_private *next;
187 /* These fields are for PA and PB only */
188 volatile unsigned char *port, *shadow;
189 volatile unsigned char *dir, *dir_shadow;
190 unsigned char changeable_dir;
191 unsigned char changeable_bits;
192 unsigned char clk_mask;
193 unsigned char data_mask;
194 unsigned char write_msb;
195 unsigned char pad1, pad2, pad3;
196 /* These fields are generic */
197 unsigned long highalarm, lowalarm;
198 wait_queue_head_t alarm_wq;
199 int minor;
200};
201
202/* linked list of alarms to check for */
203
204static struct gpio_private *alarmlist = 0;
205
206static int gpio_some_alarms = 0; /* Set if someone uses alarm */
207static unsigned long gpio_pa_irq_enabled_mask = 0;
208
Mikael Starvik7e920422005-07-27 11:44:34 -0700209static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
210
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211/* Port A and B use 8 bit access, but Port G is 32 bit */
212#define NUM_PORTS (GPIO_MINOR_B+1)
213
214static volatile unsigned char *ports[NUM_PORTS] = {
215 R_PORT_PA_DATA,
216 R_PORT_PB_DATA,
217};
218static volatile unsigned char *shads[NUM_PORTS] = {
219 &port_pa_data_shadow,
220 &port_pb_data_shadow
221};
222
223/* What direction bits that are user changeable 1=changeable*/
224#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
225#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
226#endif
227#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
228#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
229#endif
230
231#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
232#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
233#endif
234#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
235#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
236#endif
237
238
239static unsigned char changeable_dir[NUM_PORTS] = {
240 CONFIG_ETRAX_PA_CHANGEABLE_DIR,
241 CONFIG_ETRAX_PB_CHANGEABLE_DIR
242};
243static unsigned char changeable_bits[NUM_PORTS] = {
244 CONFIG_ETRAX_PA_CHANGEABLE_BITS,
245 CONFIG_ETRAX_PB_CHANGEABLE_BITS
246};
247
248static volatile unsigned char *dir[NUM_PORTS] = {
249 R_PORT_PA_DIR,
250 R_PORT_PB_DIR
251};
252
253static volatile unsigned char *dir_shadow[NUM_PORTS] = {
254 &port_pa_dir_shadow,
255 &port_pb_dir_shadow
256};
257
Mikael Starvik7e920422005-07-27 11:44:34 -0700258/* All bits in port g that can change dir. */
259static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
260
Linus Torvalds1da177e2005-04-16 15:20:36 -0700261/* Port G is 32 bit, handle it special, some bits are both inputs
262 and outputs at the same time, only some of the bits can change direction
263 and some of them in groups of 8 bit. */
264static unsigned long changeable_dir_g;
265static unsigned long dir_g_in_bits;
266static unsigned long dir_g_out_bits;
267static unsigned long dir_g_shadow; /* 1=output */
268
269#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
270
271
272
273static unsigned int
274gpio_poll(struct file *file,
275 poll_table *wait)
276{
277 unsigned int mask = 0;
278 struct gpio_private *priv = (struct gpio_private *)file->private_data;
279 unsigned long data;
Mikael Starvik7e920422005-07-27 11:44:34 -0700280 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700281 poll_wait(file, &priv->alarm_wq, wait);
282 if (priv->minor == GPIO_MINOR_A) {
283 unsigned long flags;
284 unsigned long tmp;
285 data = *R_PORT_PA_DATA;
286 /* PA has support for high level interrupt -
287 * lets activate for those low and with highalarm set
288 */
289 tmp = ~data & priv->highalarm & 0xFF;
290 tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
Mikael Starvik7e920422005-07-27 11:44:34 -0700291 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700292 gpio_pa_irq_enabled_mask |= tmp;
293 *R_IRQ_MASK1_SET = tmp;
Mikael Starvik7e920422005-07-27 11:44:34 -0700294 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295
296 } else if (priv->minor == GPIO_MINOR_B)
297 data = *R_PORT_PB_DATA;
298 else if (priv->minor == GPIO_MINOR_G)
299 data = *R_PORT_G_DATA;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800300 else {
301 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700302 return 0;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800303 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700304
305 if ((data & priv->highalarm) ||
306 (~data & priv->lowalarm)) {
307 mask = POLLIN|POLLRDNORM;
308 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700309
310 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700311
312 DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
Mikael Starvik7e920422005-07-27 11:44:34 -0700313
Linus Torvalds1da177e2005-04-16 15:20:36 -0700314 return mask;
315}
316
317int etrax_gpio_wake_up_check(void)
318{
319 struct gpio_private *priv = alarmlist;
320 unsigned long data = 0;
321 int ret = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700322 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323 while (priv) {
324 if (USE_PORTS(priv)) {
325 data = *priv->port;
326 } else if (priv->minor == GPIO_MINOR_G) {
327 data = *R_PORT_G_DATA;
328 }
329 if ((data & priv->highalarm) ||
330 (~data & priv->lowalarm)) {
331 DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
332 wake_up_interruptible(&priv->alarm_wq);
333 ret = 1;
334 }
335 priv = priv->next;
336 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700337 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700338 return ret;
339}
340
341static irqreturn_t
342gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
343{
344 if (gpio_some_alarms) {
345 etrax_gpio_wake_up_check();
346 return IRQ_HANDLED;
347 }
348 return IRQ_NONE;
349}
350
351static irqreturn_t
352gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
353{
354 unsigned long tmp;
Mikael Starvik7e920422005-07-27 11:44:34 -0700355 spin_lock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700356 /* Find what PA interrupts are active */
357 tmp = (*R_IRQ_READ1);
358
359 /* Find those that we have enabled */
360 tmp &= gpio_pa_irq_enabled_mask;
361
362 /* Clear them.. */
363 *R_IRQ_MASK1_CLR = tmp;
364 gpio_pa_irq_enabled_mask &= ~tmp;
365
Mikael Starvik7e920422005-07-27 11:44:34 -0700366 spin_unlock(&gpio_lock);
367
Linus Torvalds1da177e2005-04-16 15:20:36 -0700368 if (gpio_some_alarms) {
369 return IRQ_RETVAL(etrax_gpio_wake_up_check());
370 }
371 return IRQ_NONE;
372}
373
374
375static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
376 loff_t *off)
377{
378 struct gpio_private *priv = (struct gpio_private *)file->private_data;
379 unsigned char data, clk_mask, data_mask, write_msb;
380 unsigned long flags;
Mikael Starvik7e920422005-07-27 11:44:34 -0700381
382 spin_lock(&gpio_lock);
383
Linus Torvalds1da177e2005-04-16 15:20:36 -0700384 ssize_t retval = count;
385 if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
Roel Kluin5c6ff792007-11-14 17:00:06 -0800386 retval = -EFAULT;
387 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700388 }
389
390 if (!access_ok(VERIFY_READ, buf, count)) {
Roel Kluin5c6ff792007-11-14 17:00:06 -0800391 retval = -EFAULT;
392 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700393 }
394 clk_mask = priv->clk_mask;
395 data_mask = priv->data_mask;
396 /* It must have been configured using the IO_CFG_WRITE_MODE */
397 /* Perhaps a better error code? */
398 if (clk_mask == 0 || data_mask == 0) {
Roel Kluin5c6ff792007-11-14 17:00:06 -0800399 retval = -EPERM;
400 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 }
402 write_msb = priv->write_msb;
403 D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb));
404 while (count--) {
405 int i;
406 data = *buf++;
407 if (priv->write_msb) {
408 for (i = 7; i >= 0;i--) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700409 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 *priv->port = *priv->shadow &= ~clk_mask;
411 if (data & 1<<i)
412 *priv->port = *priv->shadow |= data_mask;
413 else
414 *priv->port = *priv->shadow &= ~data_mask;
415 /* For FPGA: min 5.0ns (DCC) before CCLK high */
416 *priv->port = *priv->shadow |= clk_mask;
417 local_irq_restore(flags);
418 }
419 } else {
420 for (i = 0; i <= 7;i++) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700421 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700422 *priv->port = *priv->shadow &= ~clk_mask;
423 if (data & 1<<i)
424 *priv->port = *priv->shadow |= data_mask;
425 else
426 *priv->port = *priv->shadow &= ~data_mask;
427 /* For FPGA: min 5.0ns (DCC) before CCLK high */
428 *priv->port = *priv->shadow |= clk_mask;
429 local_irq_restore(flags);
430 }
431 }
432 }
Roel Kluin5c6ff792007-11-14 17:00:06 -0800433out:
Mikael Starvik7e920422005-07-27 11:44:34 -0700434 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700435 return retval;
436}
437
438
439
440static int
441gpio_open(struct inode *inode, struct file *filp)
442{
443 struct gpio_private *priv;
Eric Sesterhenn32ea0862006-07-10 04:45:02 -0700444 int p = iminor(inode);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700445
446 if (p > GPIO_MINOR_LAST)
447 return -EINVAL;
448
Robert P. J. Day5cbded52006-12-13 00:35:56 -0800449 priv = kmalloc(sizeof(struct gpio_private),
Linus Torvalds1da177e2005-04-16 15:20:36 -0700450 GFP_KERNEL);
451
452 if (!priv)
453 return -ENOMEM;
454
455 priv->minor = p;
456
457 /* initialize the io/alarm struct and link it into our alarmlist */
458
459 priv->next = alarmlist;
460 alarmlist = priv;
461 if (USE_PORTS(priv)) { /* A and B */
462 priv->port = ports[p];
463 priv->shadow = shads[p];
464 priv->dir = dir[p];
465 priv->dir_shadow = dir_shadow[p];
466 priv->changeable_dir = changeable_dir[p];
467 priv->changeable_bits = changeable_bits[p];
468 } else {
469 priv->port = NULL;
470 priv->shadow = NULL;
471 priv->dir = NULL;
472 priv->dir_shadow = NULL;
473 priv->changeable_dir = 0;
474 priv->changeable_bits = 0;
475 }
476
477 priv->highalarm = 0;
478 priv->lowalarm = 0;
479 priv->clk_mask = 0;
480 priv->data_mask = 0;
481 init_waitqueue_head(&priv->alarm_wq);
482
483 filp->private_data = (void *)priv;
484
485 return 0;
486}
487
488static int
489gpio_release(struct inode *inode, struct file *filp)
490{
Mikael Starvik7e920422005-07-27 11:44:34 -0700491 struct gpio_private *p;
492 struct gpio_private *todel;
493
494 spin_lock(&gpio_lock);
495
496 p = alarmlist;
497 todel = (struct gpio_private *)filp->private_data;
498
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499 /* unlink from alarmlist and free the private structure */
500
501 if (p == todel) {
502 alarmlist = todel->next;
503 } else {
504 while (p->next != todel)
505 p = p->next;
506 p->next = todel->next;
507 }
508
509 kfree(todel);
510 /* Check if there are still any alarms set */
511 p = alarmlist;
512 while (p) {
513 if (p->highalarm | p->lowalarm) {
514 gpio_some_alarms = 1;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800515 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 return 0;
517 }
518 p = p->next;
519 }
520 gpio_some_alarms = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700521 spin_unlock(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700522 return 0;
523}
524
525/* Main device API. ioctl's to read/set/clear bits, as well as to
526 * set alarms to wait for using a subsequent select().
527 */
528
529unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
530{
531 /* Set direction 0=unchanged 1=input,
532 * return mask with 1=input
533 */
534 unsigned long flags;
535 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700536 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 *priv->dir = *priv->dir_shadow &=
538 ~((unsigned char)arg & priv->changeable_dir);
539 local_irq_restore(flags);
540 return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
541 } else if (priv->minor == GPIO_MINOR_G) {
542 /* We must fiddle with R_GEN_CONFIG to change dir */
Mikael Starvik7e920422005-07-27 11:44:34 -0700543 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 if (((arg & dir_g_in_bits) != arg) &&
545 (arg & changeable_dir_g)) {
546 arg &= changeable_dir_g;
547 /* Clear bits in genconfig to set to input */
548 if (arg & (1<<0)) {
549 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir);
550 dir_g_in_bits |= (1<<0);
551 dir_g_out_bits &= ~(1<<0);
552 }
553 if ((arg & 0x0000FF00) == 0x0000FF00) {
554 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir);
555 dir_g_in_bits |= 0x0000FF00;
556 dir_g_out_bits &= ~0x0000FF00;
557 }
558 if ((arg & 0x00FF0000) == 0x00FF0000) {
559 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir);
560 dir_g_in_bits |= 0x00FF0000;
561 dir_g_out_bits &= ~0x00FF0000;
562 }
563 if (arg & (1<<24)) {
564 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir);
565 dir_g_in_bits |= (1<<24);
566 dir_g_out_bits &= ~(1<<24);
567 }
568 D(printk(KERN_INFO "gpio: SETINPUT on port G set "
569 "genconfig to 0x%08lX "
570 "in_bits: 0x%08lX "
571 "out_bits: 0x%08lX\n",
572 (unsigned long)genconfig_shadow,
573 dir_g_in_bits, dir_g_out_bits));
574 *R_GEN_CONFIG = genconfig_shadow;
575 /* Must be a >120 ns delay before writing this again */
576
577 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700578 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700579 return dir_g_in_bits;
580 }
581 return 0;
582} /* setget_input */
583
584unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
585{
586 unsigned long flags;
587 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700588 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 *priv->dir = *priv->dir_shadow |=
590 ((unsigned char)arg & priv->changeable_dir);
591 local_irq_restore(flags);
592 return *priv->dir_shadow;
593 } else if (priv->minor == GPIO_MINOR_G) {
594 /* We must fiddle with R_GEN_CONFIG to change dir */
Mikael Starvik7e920422005-07-27 11:44:34 -0700595 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 if (((arg & dir_g_out_bits) != arg) &&
597 (arg & changeable_dir_g)) {
598 /* Set bits in genconfig to set to output */
599 if (arg & (1<<0)) {
600 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir);
601 dir_g_out_bits |= (1<<0);
602 dir_g_in_bits &= ~(1<<0);
603 }
604 if ((arg & 0x0000FF00) == 0x0000FF00) {
605 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir);
606 dir_g_out_bits |= 0x0000FF00;
607 dir_g_in_bits &= ~0x0000FF00;
608 }
609 if ((arg & 0x00FF0000) == 0x00FF0000) {
610 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir);
611 dir_g_out_bits |= 0x00FF0000;
612 dir_g_in_bits &= ~0x00FF0000;
613 }
614 if (arg & (1<<24)) {
615 genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir);
616 dir_g_out_bits |= (1<<24);
617 dir_g_in_bits &= ~(1<<24);
618 }
619 D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
620 "genconfig to 0x%08lX "
621 "in_bits: 0x%08lX "
622 "out_bits: 0x%08lX\n",
623 (unsigned long)genconfig_shadow,
624 dir_g_in_bits, dir_g_out_bits));
625 *R_GEN_CONFIG = genconfig_shadow;
626 /* Must be a >120 ns delay before writing this again */
627 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700628 local_irq_restore(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700629 return dir_g_out_bits & 0x7FFFFFFF;
630 }
631 return 0;
632} /* setget_output */
633
634static int
635gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
636
637static int
638gpio_ioctl(struct inode *inode, struct file *file,
639 unsigned int cmd, unsigned long arg)
640{
641 unsigned long flags;
642 unsigned long val;
Mikael Starvik7e920422005-07-27 11:44:34 -0700643 int ret = 0;
644
Linus Torvalds1da177e2005-04-16 15:20:36 -0700645 struct gpio_private *priv = (struct gpio_private *)file->private_data;
646 if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) {
647 return -EINVAL;
648 }
649
Mikael Starvik7e920422005-07-27 11:44:34 -0700650 spin_lock(&gpio_lock);
651
Linus Torvalds1da177e2005-04-16 15:20:36 -0700652 switch (_IOC_NR(cmd)) {
653 case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
654 // read the port
655 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700656 ret = *priv->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700657 } else if (priv->minor == GPIO_MINOR_G) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700658 ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 }
660 break;
661 case IO_SETBITS:
Mikael Starvik7e920422005-07-27 11:44:34 -0700662 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700663 // set changeable bits with a 1 in arg
664 if (USE_PORTS(priv)) {
665 *priv->port = *priv->shadow |=
666 ((unsigned char)arg & priv->changeable_bits);
667 } else if (priv->minor == GPIO_MINOR_G) {
668 *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
669 }
670 local_irq_restore(flags);
671 break;
672 case IO_CLRBITS:
Mikael Starvik7e920422005-07-27 11:44:34 -0700673 local_irq_save(flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 // clear changeable bits with a 1 in arg
675 if (USE_PORTS(priv)) {
676 *priv->port = *priv->shadow &=
677 ~((unsigned char)arg & priv->changeable_bits);
678 } else if (priv->minor == GPIO_MINOR_G) {
679 *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
680 }
681 local_irq_restore(flags);
682 break;
683 case IO_HIGHALARM:
684 // set alarm when bits with 1 in arg go high
685 priv->highalarm |= arg;
686 gpio_some_alarms = 1;
687 break;
688 case IO_LOWALARM:
689 // set alarm when bits with 1 in arg go low
690 priv->lowalarm |= arg;
691 gpio_some_alarms = 1;
692 break;
693 case IO_CLRALARM:
694 // clear alarm for bits with 1 in arg
695 priv->highalarm &= ~arg;
696 priv->lowalarm &= ~arg;
697 {
698 /* Must update gpio_some_alarms */
699 struct gpio_private *p = alarmlist;
700 int some_alarms;
701 some_alarms = 0;
702 while (p) {
703 if (p->highalarm | p->lowalarm) {
704 some_alarms = 1;
705 break;
706 }
707 p = p->next;
708 }
709 gpio_some_alarms = some_alarms;
710 }
711 break;
712 case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
713 /* Read direction 0=input 1=output */
714 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700715 ret = *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 } else if (priv->minor == GPIO_MINOR_G) {
717 /* Note: Some bits are both in and out,
718 * Those that are dual is set here as well.
719 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700720 ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700721 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700722 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700723 case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
724 /* Set direction 0=unchanged 1=input,
725 * return mask with 1=input
726 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700727 ret = setget_input(priv, arg) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728 break;
729 case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
730 /* Set direction 0=unchanged 1=output,
731 * return mask with 1=output
732 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700733 ret = setget_output(priv, arg) & 0x7FFFFFFF;
734 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735 case IO_SHUTDOWN:
736 SOFT_SHUTDOWN();
737 break;
738 case IO_GET_PWR_BT:
739#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
Mikael Starvik7e920422005-07-27 11:44:34 -0700740 ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741#else
Mikael Starvik7e920422005-07-27 11:44:34 -0700742 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700743#endif
744 break;
745 case IO_CFG_WRITE_MODE:
746 priv->clk_mask = arg & 0xFF;
747 priv->data_mask = (arg >> 8) & 0xFF;
748 priv->write_msb = (arg >> 16) & 0x01;
749 /* Check if we're allowed to change the bits and
750 * the direction is correct
751 */
752 if (!((priv->clk_mask & priv->changeable_bits) &&
753 (priv->data_mask & priv->changeable_bits) &&
754 (priv->clk_mask & *priv->dir_shadow) &&
755 (priv->data_mask & *priv->dir_shadow)))
756 {
757 priv->clk_mask = 0;
758 priv->data_mask = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700759 ret = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700760 }
761 break;
762 case IO_READ_INBITS:
763 /* *arg is result of reading the input pins */
764 if (USE_PORTS(priv)) {
765 val = *priv->port;
766 } else if (priv->minor == GPIO_MINOR_G) {
767 val = *R_PORT_G_DATA;
768 }
769 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700770 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700771 break;
772 case IO_READ_OUTBITS:
773 /* *arg is result of reading the output shadow */
774 if (USE_PORTS(priv)) {
775 val = *priv->shadow;
776 } else if (priv->minor == GPIO_MINOR_G) {
777 val = port_g_data_shadow;
778 }
779 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700780 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700781 break;
782 case IO_SETGET_INPUT:
783 /* bits set in *arg is set to input,
784 * *arg updated with current input pins.
785 */
786 if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700787 {
788 ret = -EFAULT;
789 break;
790 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700791 val = setget_input(priv, val);
792 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700793 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794 break;
795 case IO_SETGET_OUTPUT:
796 /* bits set in *arg is set to output,
797 * *arg updated with current output pins.
798 */
799 if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700800 {
801 ret = -EFAULT;
802 break;
803 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700804 val = setget_output(priv, val);
805 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700806 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700807 break;
808 default:
809 if (priv->minor == GPIO_MINOR_LEDS)
Mikael Starvik7e920422005-07-27 11:44:34 -0700810 ret = gpio_leds_ioctl(cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700811 else
Mikael Starvik7e920422005-07-27 11:44:34 -0700812 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700813 } /* switch */
Mikael Starvik7e920422005-07-27 11:44:34 -0700814
815 spin_unlock(&gpio_lock);
816 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817}
818
819static int
820gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
821{
822 unsigned char green;
823 unsigned char red;
824
825 switch (_IOC_NR(cmd)) {
826 case IO_LEDACTIVE_SET:
827 green = ((unsigned char) arg) & 1;
828 red = (((unsigned char) arg) >> 1) & 1;
829 LED_ACTIVE_SET_G(green);
830 LED_ACTIVE_SET_R(red);
831 break;
832
833 case IO_LED_SETBIT:
834 LED_BIT_SET(arg);
835 break;
836
837 case IO_LED_CLRBIT:
838 LED_BIT_CLR(arg);
839 break;
840
841 default:
842 return -EINVAL;
843 } /* switch */
844
845 return 0;
846}
847
Arjan van de Ven5dfe4c92007-02-12 00:55:31 -0800848const struct file_operations gpio_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700849 .owner = THIS_MODULE,
850 .poll = gpio_poll,
851 .ioctl = gpio_ioctl,
852 .write = gpio_write,
853 .open = gpio_open,
854 .release = gpio_release,
855};
856
857
Mikael Starvik7e920422005-07-27 11:44:34 -0700858void ioif_watcher(const unsigned int gpio_in_available,
859 const unsigned int gpio_out_available,
860 const unsigned char pa_available,
861 const unsigned char pb_available)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700862{
Mikael Starvik7e920422005-07-27 11:44:34 -0700863 unsigned long int flags;
864 D(printk("gpio.c: ioif_watcher called\n"));
865 D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n",
866 gpio_in_available, gpio_out_available, pa_available, pb_available));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700867
Mikael Starvik7e920422005-07-27 11:44:34 -0700868 spin_lock_irqsave(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700869
Mikael Starvik7e920422005-07-27 11:44:34 -0700870 dir_g_in_bits = gpio_in_available;
871 dir_g_out_bits = gpio_out_available;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700872
873 /* Initialise the dir_g_shadow etc. depending on genconfig */
874 /* 0=input 1=output */
875 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
876 dir_g_shadow |= (1 << 0);
877 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
878 dir_g_shadow |= 0x0000FF00;
879 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
880 dir_g_shadow |= 0x00FF0000;
881 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
882 dir_g_shadow |= (1 << 24);
883
Mikael Starvik7e920422005-07-27 11:44:34 -0700884 changeable_dir_g = changeable_dir_g_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700885 changeable_dir_g &= dir_g_out_bits;
886 changeable_dir_g &= dir_g_in_bits;
887 /* Correct the bits that can change direction */
888 dir_g_out_bits &= ~changeable_dir_g;
889 dir_g_out_bits |= dir_g_shadow;
890 dir_g_in_bits &= ~changeable_dir_g;
891 dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
892
Mikael Starvik7e920422005-07-27 11:44:34 -0700893 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700894
895 printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n",
896 dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
897 printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
898 dir_g_shadow, changeable_dir_g);
899}
900
901/* main driver initialization routine, called from mem.c */
902
903static __init int
904gpio_init(void)
905{
906 int res;
907#if defined (CONFIG_ETRAX_CSP0_LEDS)
908 int i;
909#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700910 printk("gpio init\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700911
912 /* do the formalities */
913
914 res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
915 if (res < 0) {
916 printk(KERN_ERR "gpio: couldn't get a major number.\n");
917 return res;
918 }
919
920 /* Clear all leds */
921#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
922 LED_NETWORK_SET(0);
923 LED_ACTIVE_SET(0);
924 LED_DISK_READ(0);
925 LED_DISK_WRITE(0);
926
927#if defined (CONFIG_ETRAX_CSP0_LEDS)
928 for (i = 0; i < 32; i++) {
929 LED_BIT_SET(i);
930 }
931#endif
932
933#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700934 /* The I/O interface allocation watcher will be called when
935 * registering it. */
936 if (cris_io_interface_register_watcher(ioif_watcher)){
937 printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n");
938 }
939
940 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 -0700941 /* We call etrax_gpio_wake_up_check() from timer interrupt and
942 * from cpu_idle() in kernel/process.c
943 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
944 * in some tests.
945 */
946 if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
Thomas Gleixneraa7135f2006-07-01 19:29:14 -0700947 IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700948 printk(KERN_CRIT "err: timer0 irq for gpio\n");
949 }
950 if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
Thomas Gleixneraa7135f2006-07-01 19:29:14 -0700951 IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700952 printk(KERN_CRIT "err: PA irq for gpio\n");
953 }
954
955
956 return res;
957}
958
959/* this makes sure that gpio_init is called during kernel boot */
960
961module_init(gpio_init);