blob: 26ae11be208d036d637937c4e94c521b56ef4bb9 [file] [log] [blame]
Jesper Nilsson45a41272008-01-25 15:42:41 +01001/*
Linus Torvalds1da177e2005-04-16 15:20:36 -07002 * Etrax general port I/O device
3 *
Jesper Nilsson45a41272008-01-25 15:42:41 +01004 * Copyright (c) 1999-2007 Axis Communications AB
Linus Torvalds1da177e2005-04-16 15:20:36 -07005 *
6 * Authors: Bjorn Wesen (initial version)
7 * Ola Knutsson (LED handling)
8 * Johan Adolfsson (read/set directions, write, port G)
Linus Torvalds1da177e2005-04-16 15:20:36 -07009 */
10
Linus Torvalds1da177e2005-04-16 15:20:36 -070011
12#include <linux/module.h>
13#include <linux/sched.h>
14#include <linux/slab.h>
15#include <linux/ioport.h>
16#include <linux/errno.h>
17#include <linux/kernel.h>
18#include <linux/fs.h>
19#include <linux/string.h>
20#include <linux/poll.h>
21#include <linux/init.h>
22#include <linux/interrupt.h>
23
24#include <asm/etraxgpio.h>
25#include <asm/arch/svinto.h>
26#include <asm/io.h>
27#include <asm/system.h>
28#include <asm/irq.h>
Mikael Starvik7e920422005-07-27 11:44:34 -070029#include <asm/arch/io_interface_mux.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070030
31#define GPIO_MAJOR 120 /* experimental MAJOR number */
32
33#define D(x)
34
35#if 0
36static 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
Jesper Nilsson45a41272008-01-25 15:42:41 +010041
Linus Torvalds1da177e2005-04-16 15:20:36 -070042static char gpio_name[] = "etrax gpio";
43
44#if 0
45static wait_queue_head_t *gpio_wq;
46#endif
47
48static int gpio_ioctl(struct inode *inode, struct file *file,
49 unsigned int cmd, unsigned long arg);
50static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
51 loff_t *off);
52static int gpio_open(struct inode *inode, struct file *filp);
53static int gpio_release(struct inode *inode, struct file *filp);
54static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
55
56/* private data per open() of this driver */
57
58struct gpio_private {
59 struct gpio_private *next;
60 /* These fields are for PA and PB only */
61 volatile unsigned char *port, *shadow;
62 volatile unsigned char *dir, *dir_shadow;
63 unsigned char changeable_dir;
64 unsigned char changeable_bits;
65 unsigned char clk_mask;
66 unsigned char data_mask;
67 unsigned char write_msb;
68 unsigned char pad1, pad2, pad3;
69 /* These fields are generic */
70 unsigned long highalarm, lowalarm;
71 wait_queue_head_t alarm_wq;
72 int minor;
73};
74
75/* linked list of alarms to check for */
76
77static struct gpio_private *alarmlist = 0;
78
79static int gpio_some_alarms = 0; /* Set if someone uses alarm */
80static unsigned long gpio_pa_irq_enabled_mask = 0;
81
Mikael Starvik7e920422005-07-27 11:44:34 -070082static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
83
Linus Torvalds1da177e2005-04-16 15:20:36 -070084/* Port A and B use 8 bit access, but Port G is 32 bit */
85#define NUM_PORTS (GPIO_MINOR_B+1)
86
Jesper Nilsson45a41272008-01-25 15:42:41 +010087static volatile unsigned char *ports[NUM_PORTS] = {
88 R_PORT_PA_DATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -070089 R_PORT_PB_DATA,
90};
91static volatile unsigned char *shads[NUM_PORTS] = {
Jesper Nilsson45a41272008-01-25 15:42:41 +010092 &port_pa_data_shadow,
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 &port_pb_data_shadow
94};
95
96/* What direction bits that are user changeable 1=changeable*/
97#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
98#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
99#endif
100#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
101#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
102#endif
103
104#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
105#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
106#endif
107#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
108#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
109#endif
110
111
Jesper Nilsson45a41272008-01-25 15:42:41 +0100112static unsigned char changeable_dir[NUM_PORTS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113 CONFIG_ETRAX_PA_CHANGEABLE_DIR,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100114 CONFIG_ETRAX_PB_CHANGEABLE_DIR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700115};
Jesper Nilsson45a41272008-01-25 15:42:41 +0100116static unsigned char changeable_bits[NUM_PORTS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700117 CONFIG_ETRAX_PA_CHANGEABLE_BITS,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100118 CONFIG_ETRAX_PB_CHANGEABLE_BITS
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119};
120
Jesper Nilsson45a41272008-01-25 15:42:41 +0100121static volatile unsigned char *dir[NUM_PORTS] = {
122 R_PORT_PA_DIR,
123 R_PORT_PB_DIR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124};
125
126static volatile unsigned char *dir_shadow[NUM_PORTS] = {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100127 &port_pa_dir_shadow,
128 &port_pb_dir_shadow
Linus Torvalds1da177e2005-04-16 15:20:36 -0700129};
130
Mikael Starvik7e920422005-07-27 11:44:34 -0700131/* All bits in port g that can change dir. */
132static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
133
Jesper Nilsson45a41272008-01-25 15:42:41 +0100134/* Port G is 32 bit, handle it special, some bits are both inputs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700135 and outputs at the same time, only some of the bits can change direction
136 and some of them in groups of 8 bit. */
137static unsigned long changeable_dir_g;
138static unsigned long dir_g_in_bits;
139static unsigned long dir_g_out_bits;
140static unsigned long dir_g_shadow; /* 1=output */
141
142#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
143
144
Jesper Nilsson45a41272008-01-25 15:42:41 +0100145static unsigned int gpio_poll(struct file *file, poll_table *wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700146{
147 unsigned int mask = 0;
148 struct gpio_private *priv = (struct gpio_private *)file->private_data;
149 unsigned long data;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100150 unsigned long flags;
151
152 spin_lock_irqsave(&gpio_lock, flags);
153
Linus Torvalds1da177e2005-04-16 15:20:36 -0700154 poll_wait(file, &priv->alarm_wq, wait);
155 if (priv->minor == GPIO_MINOR_A) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700156 unsigned long tmp;
157 data = *R_PORT_PA_DATA;
158 /* PA has support for high level interrupt -
159 * lets activate for those low and with highalarm set
160 */
161 tmp = ~data & priv->highalarm & 0xFF;
162 tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
Jesper Nilsson45a41272008-01-25 15:42:41 +0100163
Linus Torvalds1da177e2005-04-16 15:20:36 -0700164 gpio_pa_irq_enabled_mask |= tmp;
165 *R_IRQ_MASK1_SET = tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700166 } else if (priv->minor == GPIO_MINOR_B)
167 data = *R_PORT_PB_DATA;
168 else if (priv->minor == GPIO_MINOR_G)
169 data = *R_PORT_G_DATA;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800170 else {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100171 mask = 0;
172 goto out;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800173 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100174
Linus Torvalds1da177e2005-04-16 15:20:36 -0700175 if ((data & priv->highalarm) ||
176 (~data & priv->lowalarm)) {
177 mask = POLLIN|POLLRDNORM;
178 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700179
Jesper Nilsson45a41272008-01-25 15:42:41 +0100180out:
181 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700182 DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
Mikael Starvik7e920422005-07-27 11:44:34 -0700183
Linus Torvalds1da177e2005-04-16 15:20:36 -0700184 return mask;
185}
186
187int etrax_gpio_wake_up_check(void)
188{
Jesper Nilsson45a41272008-01-25 15:42:41 +0100189 struct gpio_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700190 unsigned long data = 0;
191 int ret = 0;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100192 unsigned long flags;
193
194 spin_lock_irqsave(&gpio_lock, flags);
195 priv = alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700196 while (priv) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100197 if (USE_PORTS(priv))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700198 data = *priv->port;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100199 else if (priv->minor == GPIO_MINOR_G)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700200 data = *R_PORT_G_DATA;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100201
Linus Torvalds1da177e2005-04-16 15:20:36 -0700202 if ((data & priv->highalarm) ||
203 (~data & priv->lowalarm)) {
204 DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
205 wake_up_interruptible(&priv->alarm_wq);
206 ret = 1;
207 }
208 priv = priv->next;
209 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100210 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700211 return ret;
212}
213
214static irqreturn_t
Jesper Nilsson45a41272008-01-25 15:42:41 +0100215gpio_poll_timer_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700216{
217 if (gpio_some_alarms) {
218 etrax_gpio_wake_up_check();
219 return IRQ_HANDLED;
220 }
221 return IRQ_NONE;
222}
223
224static irqreturn_t
Jesper Nilsson45a41272008-01-25 15:42:41 +0100225gpio_pa_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700226{
227 unsigned long tmp;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100228 unsigned long flags;
229
230 spin_lock_irqsave(&gpio_lock, flags);
231
Linus Torvalds1da177e2005-04-16 15:20:36 -0700232 /* Find what PA interrupts are active */
233 tmp = (*R_IRQ_READ1);
234
235 /* Find those that we have enabled */
236 tmp &= gpio_pa_irq_enabled_mask;
237
238 /* Clear them.. */
239 *R_IRQ_MASK1_CLR = tmp;
240 gpio_pa_irq_enabled_mask &= ~tmp;
241
Jesper Nilsson45a41272008-01-25 15:42:41 +0100242 spin_unlock_irqrestore(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700243
Jesper Nilsson45a41272008-01-25 15:42:41 +0100244 if (gpio_some_alarms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700245 return IRQ_RETVAL(etrax_gpio_wake_up_check());
Jesper Nilsson45a41272008-01-25 15:42:41 +0100246
Linus Torvalds1da177e2005-04-16 15:20:36 -0700247 return IRQ_NONE;
248}
249
Jesper Nilsson45a41272008-01-25 15:42:41 +0100250static void gpio_write_bit(struct gpio_private *priv,
251 unsigned char data, int bit)
252{
253 *priv->port = *priv->shadow &= ~(priv->clk_mask);
254 if (data & 1 << bit)
255 *priv->port = *priv->shadow |= priv->data_mask;
256 else
257 *priv->port = *priv->shadow &= ~(priv->data_mask);
258
259 /* For FPGA: min 5.0ns (DCC) before CCLK high */
260 *priv->port = *priv->shadow |= priv->clk_mask;
261}
262
263static void gpio_write_byte(struct gpio_private *priv, unsigned char data)
264{
265 int i;
266
267 if (priv->write_msb)
268 for (i = 7; i >= 0; i--)
269 gpio_write_bit(priv, data, i);
270 else
271 for (i = 0; i <= 7; i++)
272 gpio_write_bit(priv, data, i);
273}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700274
275static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
276 loff_t *off)
277{
278 struct gpio_private *priv = (struct gpio_private *)file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700279 unsigned long flags;
280 ssize_t retval = count;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100281
282 if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B)
283 return -EFAULT;
284
285 if (!access_ok(VERIFY_READ, buf, count))
286 return -EFAULT;
287
288 spin_lock_irqsave(&gpio_lock, flags);
289
Linus Torvalds1da177e2005-04-16 15:20:36 -0700290 /* It must have been configured using the IO_CFG_WRITE_MODE */
291 /* Perhaps a better error code? */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100292 if (priv->clk_mask == 0 || priv->data_mask == 0) {
Roel Kluin5c6ff792007-11-14 17:00:06 -0800293 retval = -EPERM;
294 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700295 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100296
297 D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X "
298 "clk 0x%02X msb: %i\n",
299 count, priv->data_mask, priv->clk_mask, priv->write_msb));
300
301 while (count--)
302 gpio_write_byte(priv, *buf++);
303
Roel Kluin5c6ff792007-11-14 17:00:06 -0800304out:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100305 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700306 return retval;
307}
308
309
310
311static int
312gpio_open(struct inode *inode, struct file *filp)
313{
314 struct gpio_private *priv;
Eric Sesterhenn32ea0862006-07-10 04:45:02 -0700315 int p = iminor(inode);
Jesper Nilsson45a41272008-01-25 15:42:41 +0100316 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700317
318 if (p > GPIO_MINOR_LAST)
319 return -EINVAL;
320
Jesper Nilsson45a41272008-01-25 15:42:41 +0100321 priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 if (!priv)
324 return -ENOMEM;
325
Jesper Nilsson45a41272008-01-25 15:42:41 +0100326 memset(priv, 0, sizeof(*priv));
327
Linus Torvalds1da177e2005-04-16 15:20:36 -0700328 priv->minor = p;
329
Jesper Nilsson45a41272008-01-25 15:42:41 +0100330 /* initialize the io/alarm struct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700331
Linus Torvalds1da177e2005-04-16 15:20:36 -0700332 if (USE_PORTS(priv)) { /* A and B */
333 priv->port = ports[p];
334 priv->shadow = shads[p];
335 priv->dir = dir[p];
336 priv->dir_shadow = dir_shadow[p];
337 priv->changeable_dir = changeable_dir[p];
338 priv->changeable_bits = changeable_bits[p];
339 } else {
340 priv->port = NULL;
341 priv->shadow = NULL;
342 priv->dir = NULL;
343 priv->dir_shadow = NULL;
344 priv->changeable_dir = 0;
345 priv->changeable_bits = 0;
346 }
347
348 priv->highalarm = 0;
349 priv->lowalarm = 0;
350 priv->clk_mask = 0;
351 priv->data_mask = 0;
352 init_waitqueue_head(&priv->alarm_wq);
353
354 filp->private_data = (void *)priv;
355
Jesper Nilsson45a41272008-01-25 15:42:41 +0100356 /* link it into our alarmlist */
357 spin_lock_irqsave(&gpio_lock, flags);
358 priv->next = alarmlist;
359 alarmlist = priv;
360 spin_unlock_irqrestore(&gpio_lock, flags);
361
Linus Torvalds1da177e2005-04-16 15:20:36 -0700362 return 0;
363}
364
365static int
366gpio_release(struct inode *inode, struct file *filp)
367{
Mikael Starvik7e920422005-07-27 11:44:34 -0700368 struct gpio_private *p;
369 struct gpio_private *todel;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100370 unsigned long flags;
Mikael Starvik7e920422005-07-27 11:44:34 -0700371
Jesper Nilsson45a41272008-01-25 15:42:41 +0100372 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700373
Jesper Nilsson45a41272008-01-25 15:42:41 +0100374 p = alarmlist;
375 todel = (struct gpio_private *)filp->private_data;
Mikael Starvik7e920422005-07-27 11:44:34 -0700376
Linus Torvalds1da177e2005-04-16 15:20:36 -0700377 /* unlink from alarmlist and free the private structure */
378
379 if (p == todel) {
380 alarmlist = todel->next;
381 } else {
382 while (p->next != todel)
383 p = p->next;
384 p->next = todel->next;
385 }
386
387 kfree(todel);
388 /* Check if there are still any alarms set */
389 p = alarmlist;
390 while (p) {
391 if (p->highalarm | p->lowalarm) {
392 gpio_some_alarms = 1;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100393 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700394 }
395 p = p->next;
396 }
397 gpio_some_alarms = 0;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100398out:
399 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700400 return 0;
401}
402
Jesper Nilsson45a41272008-01-25 15:42:41 +0100403/* Main device API. ioctl's to read/set/clear bits, as well as to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404 * set alarms to wait for using a subsequent select().
405 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700406unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
407{
Jesper Nilsson45a41272008-01-25 15:42:41 +0100408 /* Set direction 0=unchanged 1=input,
409 * return mask with 1=input */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100411 *priv->dir = *priv->dir_shadow &=
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 ~((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100415
416 if (priv->minor != GPIO_MINOR_G)
417 return 0;
418
419 /* We must fiddle with R_GEN_CONFIG to change dir */
420 if (((arg & dir_g_in_bits) != arg) &&
421 (arg & changeable_dir_g)) {
422 arg &= changeable_dir_g;
423 /* Clear bits in genconfig to set to input */
424 if (arg & (1<<0)) {
425 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
426 dir_g_in_bits |= (1<<0);
427 dir_g_out_bits &= ~(1<<0);
428 }
429 if ((arg & 0x0000FF00) == 0x0000FF00) {
430 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
431 dir_g_in_bits |= 0x0000FF00;
432 dir_g_out_bits &= ~0x0000FF00;
433 }
434 if ((arg & 0x00FF0000) == 0x00FF0000) {
435 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
436 dir_g_in_bits |= 0x00FF0000;
437 dir_g_out_bits &= ~0x00FF0000;
438 }
439 if (arg & (1<<24)) {
440 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
441 dir_g_in_bits |= (1<<24);
442 dir_g_out_bits &= ~(1<<24);
443 }
444 D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
445 "genconfig to 0x%08lX "
446 "in_bits: 0x%08lX "
447 "out_bits: 0x%08lX\n",
448 (unsigned long)genconfig_shadow,
449 dir_g_in_bits, dir_g_out_bits));
450 *R_GEN_CONFIG = genconfig_shadow;
451 /* Must be a >120 ns delay before writing this again */
452
453 }
454 return dir_g_in_bits;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700455} /* setget_input */
456
457unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
458{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700459 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100460 *priv->dir = *priv->dir_shadow |=
461 ((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700462 return *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100464 if (priv->minor != GPIO_MINOR_G)
465 return 0;
466
467 /* We must fiddle with R_GEN_CONFIG to change dir */
468 if (((arg & dir_g_out_bits) != arg) &&
469 (arg & changeable_dir_g)) {
470 /* Set bits in genconfig to set to output */
471 if (arg & (1<<0)) {
472 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
473 dir_g_out_bits |= (1<<0);
474 dir_g_in_bits &= ~(1<<0);
475 }
476 if ((arg & 0x0000FF00) == 0x0000FF00) {
477 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
478 dir_g_out_bits |= 0x0000FF00;
479 dir_g_in_bits &= ~0x0000FF00;
480 }
481 if ((arg & 0x00FF0000) == 0x00FF0000) {
482 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
483 dir_g_out_bits |= 0x00FF0000;
484 dir_g_in_bits &= ~0x00FF0000;
485 }
486 if (arg & (1<<24)) {
487 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
488 dir_g_out_bits |= (1<<24);
489 dir_g_in_bits &= ~(1<<24);
490 }
491 D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
492 "genconfig to 0x%08lX "
493 "in_bits: 0x%08lX "
494 "out_bits: 0x%08lX\n",
495 (unsigned long)genconfig_shadow,
496 dir_g_in_bits, dir_g_out_bits));
497 *R_GEN_CONFIG = genconfig_shadow;
498 /* Must be a >120 ns delay before writing this again */
499 }
500 return dir_g_out_bits & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700501} /* setget_output */
502
503static int
504gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
505
506static int
507gpio_ioctl(struct inode *inode, struct file *file,
508 unsigned int cmd, unsigned long arg)
509{
510 unsigned long flags;
511 unsigned long val;
Mikael Starvik7e920422005-07-27 11:44:34 -0700512 int ret = 0;
513
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 struct gpio_private *priv = (struct gpio_private *)file->private_data;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100515 if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700516 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517
Jesper Nilsson45a41272008-01-25 15:42:41 +0100518 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700519
Linus Torvalds1da177e2005-04-16 15:20:36 -0700520 switch (_IOC_NR(cmd)) {
521 case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
522 // read the port
523 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700524 ret = *priv->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 } else if (priv->minor == GPIO_MINOR_G) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700526 ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700527 }
528 break;
529 case IO_SETBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700530 // set changeable bits with a 1 in arg
531 if (USE_PORTS(priv)) {
532 *priv->port = *priv->shadow |=
533 ((unsigned char)arg & priv->changeable_bits);
534 } else if (priv->minor == GPIO_MINOR_G) {
535 *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
536 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 break;
538 case IO_CLRBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700539 // clear changeable bits with a 1 in arg
540 if (USE_PORTS(priv)) {
541 *priv->port = *priv->shadow &=
542 ~((unsigned char)arg & priv->changeable_bits);
543 } else if (priv->minor == GPIO_MINOR_G) {
544 *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
545 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700546 break;
547 case IO_HIGHALARM:
548 // set alarm when bits with 1 in arg go high
549 priv->highalarm |= arg;
550 gpio_some_alarms = 1;
551 break;
552 case IO_LOWALARM:
553 // set alarm when bits with 1 in arg go low
554 priv->lowalarm |= arg;
555 gpio_some_alarms = 1;
556 break;
557 case IO_CLRALARM:
558 // clear alarm for bits with 1 in arg
559 priv->highalarm &= ~arg;
560 priv->lowalarm &= ~arg;
561 {
562 /* Must update gpio_some_alarms */
563 struct gpio_private *p = alarmlist;
564 int some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100565 spin_lock_irq(&gpio_lock);
566 p = alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700567 some_alarms = 0;
568 while (p) {
569 if (p->highalarm | p->lowalarm) {
570 some_alarms = 1;
571 break;
572 }
573 p = p->next;
574 }
575 gpio_some_alarms = some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100576 spin_unlock_irq(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700577 }
578 break;
579 case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
580 /* Read direction 0=input 1=output */
581 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700582 ret = *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700583 } else if (priv->minor == GPIO_MINOR_G) {
584 /* Note: Some bits are both in and out,
585 * Those that are dual is set here as well.
586 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700587 ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700589 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700590 case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
591 /* Set direction 0=unchanged 1=input,
592 * return mask with 1=input
593 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700594 ret = setget_input(priv, arg) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700595 break;
596 case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
597 /* Set direction 0=unchanged 1=output,
598 * return mask with 1=output
599 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700600 ret = setget_output(priv, arg) & 0x7FFFFFFF;
601 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700602 case IO_SHUTDOWN:
603 SOFT_SHUTDOWN();
604 break;
605 case IO_GET_PWR_BT:
606#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
Mikael Starvik7e920422005-07-27 11:44:34 -0700607 ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608#else
Mikael Starvik7e920422005-07-27 11:44:34 -0700609 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700610#endif
611 break;
612 case IO_CFG_WRITE_MODE:
613 priv->clk_mask = arg & 0xFF;
614 priv->data_mask = (arg >> 8) & 0xFF;
615 priv->write_msb = (arg >> 16) & 0x01;
616 /* Check if we're allowed to change the bits and
617 * the direction is correct
618 */
619 if (!((priv->clk_mask & priv->changeable_bits) &&
620 (priv->data_mask & priv->changeable_bits) &&
621 (priv->clk_mask & *priv->dir_shadow) &&
622 (priv->data_mask & *priv->dir_shadow)))
623 {
624 priv->clk_mask = 0;
625 priv->data_mask = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700626 ret = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700627 }
628 break;
629 case IO_READ_INBITS:
630 /* *arg is result of reading the input pins */
631 if (USE_PORTS(priv)) {
632 val = *priv->port;
633 } else if (priv->minor == GPIO_MINOR_G) {
634 val = *R_PORT_G_DATA;
635 }
636 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700637 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700638 break;
639 case IO_READ_OUTBITS:
640 /* *arg is result of reading the output shadow */
641 if (USE_PORTS(priv)) {
642 val = *priv->shadow;
643 } else if (priv->minor == GPIO_MINOR_G) {
644 val = port_g_data_shadow;
645 }
646 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700647 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700648 break;
649 case IO_SETGET_INPUT:
650 /* bits set in *arg is set to input,
651 * *arg updated with current input pins.
652 */
653 if (copy_from_user(&val, (unsigned long*)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700654 {
655 ret = -EFAULT;
656 break;
657 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700658 val = setget_input(priv, val);
659 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700660 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700661 break;
662 case IO_SETGET_OUTPUT:
663 /* bits set in *arg is set to output,
664 * *arg updated with current output pins.
665 */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100666 if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700667 ret = -EFAULT;
668 break;
669 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700670 val = setget_output(priv, val);
671 if (copy_to_user((unsigned long*)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700672 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700673 break;
674 default:
675 if (priv->minor == GPIO_MINOR_LEDS)
Mikael Starvik7e920422005-07-27 11:44:34 -0700676 ret = gpio_leds_ioctl(cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 else
Mikael Starvik7e920422005-07-27 11:44:34 -0700678 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700679 } /* switch */
Mikael Starvik7e920422005-07-27 11:44:34 -0700680
Jesper Nilsson45a41272008-01-25 15:42:41 +0100681 spin_unlock_irqrestore(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700682 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700683}
684
685static int
686gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
687{
688 unsigned char green;
689 unsigned char red;
690
691 switch (_IOC_NR(cmd)) {
692 case IO_LEDACTIVE_SET:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100693 green = ((unsigned char)arg) & 1;
694 red = (((unsigned char)arg) >> 1) & 1;
695 CRIS_LED_ACTIVE_SET_G(green);
696 CRIS_LED_ACTIVE_SET_R(red);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700697 break;
698
699 case IO_LED_SETBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100700 CRIS_LED_BIT_SET(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700701 break;
702
703 case IO_LED_CLRBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100704 CRIS_LED_BIT_CLR(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700705 break;
706
707 default:
708 return -EINVAL;
709 } /* switch */
710
711 return 0;
712}
713
Arjan van de Ven5dfe4c92007-02-12 00:55:31 -0800714const struct file_operations gpio_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700715 .owner = THIS_MODULE,
716 .poll = gpio_poll,
717 .ioctl = gpio_ioctl,
718 .write = gpio_write,
719 .open = gpio_open,
720 .release = gpio_release,
721};
722
Mikael Starvik7e920422005-07-27 11:44:34 -0700723void ioif_watcher(const unsigned int gpio_in_available,
724 const unsigned int gpio_out_available,
725 const unsigned char pa_available,
726 const unsigned char pb_available)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700727{
Mikael Starvik7e920422005-07-27 11:44:34 -0700728 unsigned long int flags;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100729
730 D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
731 D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x "
732 "PA: 0x%02x PB: 0x%02x\n",
733 gpio_in_available, gpio_out_available,
734 pa_available, pb_available));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Mikael Starvik7e920422005-07-27 11:44:34 -0700736 spin_lock_irqsave(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700737
Mikael Starvik7e920422005-07-27 11:44:34 -0700738 dir_g_in_bits = gpio_in_available;
739 dir_g_out_bits = gpio_out_available;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700740
741 /* Initialise the dir_g_shadow etc. depending on genconfig */
742 /* 0=input 1=output */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100743 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700744 dir_g_shadow |= (1 << 0);
745 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
746 dir_g_shadow |= 0x0000FF00;
747 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
748 dir_g_shadow |= 0x00FF0000;
749 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
750 dir_g_shadow |= (1 << 24);
751
Mikael Starvik7e920422005-07-27 11:44:34 -0700752 changeable_dir_g = changeable_dir_g_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700753 changeable_dir_g &= dir_g_out_bits;
754 changeable_dir_g &= dir_g_in_bits;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100755
756 /* Correct the bits that can change direction */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700757 dir_g_out_bits &= ~changeable_dir_g;
758 dir_g_out_bits |= dir_g_shadow;
759 dir_g_in_bits &= ~changeable_dir_g;
760 dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
761
Mikael Starvik7e920422005-07-27 11:44:34 -0700762 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700763
Jesper Nilsson45a41272008-01-25 15:42:41 +0100764 printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX "
765 "val: %08lX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700766 dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
767 printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
768 dir_g_shadow, changeable_dir_g);
769}
770
771/* main driver initialization routine, called from mem.c */
772
773static __init int
774gpio_init(void)
775{
776 int res;
777#if defined (CONFIG_ETRAX_CSP0_LEDS)
778 int i;
779#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700780
781 res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
782 if (res < 0) {
783 printk(KERN_ERR "gpio: couldn't get a major number.\n");
784 return res;
785 }
786
787 /* Clear all leds */
788#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
Jesper Nilsson45a41272008-01-25 15:42:41 +0100789 CRIS_LED_NETWORK_SET(0);
790 CRIS_LED_ACTIVE_SET(0);
791 CRIS_LED_DISK_READ(0);
792 CRIS_LED_DISK_WRITE(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700793
794#if defined (CONFIG_ETRAX_CSP0_LEDS)
Jesper Nilsson45a41272008-01-25 15:42:41 +0100795 for (i = 0; i < 32; i++)
796 CRIS_LED_BIT_SET(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700797#endif
798
799#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700800 /* The I/O interface allocation watcher will be called when
801 * registering it. */
802 if (cris_io_interface_register_watcher(ioif_watcher)){
Jesper Nilsson45a41272008-01-25 15:42:41 +0100803 printk(KERN_WARNING "gpio_init: Failed to install IO "
804 "if allocator watcher\n");
Mikael Starvik7e920422005-07-27 11:44:34 -0700805 }
806
Jesper Nilsson45a41272008-01-25 15:42:41 +0100807 printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 "
808 "Axis Communications AB\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700809 /* We call etrax_gpio_wake_up_check() from timer interrupt and
810 * from cpu_idle() in kernel/process.c
811 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
812 * in some tests.
Jesper Nilsson45a41272008-01-25 15:42:41 +0100813 */
814 res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
815 IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name);
816 if (res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700817 printk(KERN_CRIT "err: timer0 irq for gpio\n");
Jesper Nilsson45a41272008-01-25 15:42:41 +0100818 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700819 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100820 res = request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
821 IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name);
822 if (res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700823 printk(KERN_CRIT "err: PA irq for gpio\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700824
825 return res;
826}
827
828/* this makes sure that gpio_init is called during kernel boot */
829
830module_init(gpio_init);