blob: 68a998bd10697cade8c194ed01bd96003f712995 [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,
Jesper Nilssonad433f22008-02-06 14:52:40 +010049 unsigned int cmd, unsigned long arg);
50static ssize_t gpio_write(struct file *file, const char __user *buf,
51 size_t count, loff_t *off);
Linus Torvalds1da177e2005-04-16 15:20:36 -070052static 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
Jesper Nilssonad433f22008-02-06 14:52:40 +010077static struct gpio_private *alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -070078
Jesper Nilssonad433f22008-02-06 14:52:40 +010079static int gpio_some_alarms; /* Set if someone uses alarm */
80static unsigned long gpio_pa_irq_enabled_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070081
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;
Jesper Nilssonad433f22008-02-06 14:52:40 +0100148 struct gpio_private *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700149 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 Nilssonad433f22008-02-06 14:52:40 +0100225gpio_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
Jesper Nilssonad433f22008-02-06 14:52:40 +0100275static ssize_t gpio_write(struct file *file, const char __user *buf,
276 size_t count, loff_t *off)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700277{
Jesper Nilssonad433f22008-02-06 14:52:40 +0100278 struct gpio_private *priv = 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 Nilssonad433f22008-02-06 14:52:40 +0100321 priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700322
323 if (!priv)
324 return -ENOMEM;
325
326 priv->minor = p;
327
Jesper Nilsson45a41272008-01-25 15:42:41 +0100328 /* initialize the io/alarm struct */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700329
Linus Torvalds1da177e2005-04-16 15:20:36 -0700330 if (USE_PORTS(priv)) { /* A and B */
331 priv->port = ports[p];
332 priv->shadow = shads[p];
333 priv->dir = dir[p];
334 priv->dir_shadow = dir_shadow[p];
335 priv->changeable_dir = changeable_dir[p];
336 priv->changeable_bits = changeable_bits[p];
337 } else {
338 priv->port = NULL;
339 priv->shadow = NULL;
340 priv->dir = NULL;
341 priv->dir_shadow = NULL;
342 priv->changeable_dir = 0;
343 priv->changeable_bits = 0;
344 }
345
346 priv->highalarm = 0;
347 priv->lowalarm = 0;
348 priv->clk_mask = 0;
349 priv->data_mask = 0;
350 init_waitqueue_head(&priv->alarm_wq);
351
Jesper Nilssonad433f22008-02-06 14:52:40 +0100352 filp->private_data = priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700353
Jesper Nilsson45a41272008-01-25 15:42:41 +0100354 /* link it into our alarmlist */
355 spin_lock_irqsave(&gpio_lock, flags);
356 priv->next = alarmlist;
357 alarmlist = priv;
358 spin_unlock_irqrestore(&gpio_lock, flags);
359
Linus Torvalds1da177e2005-04-16 15:20:36 -0700360 return 0;
361}
362
363static int
364gpio_release(struct inode *inode, struct file *filp)
365{
Mikael Starvik7e920422005-07-27 11:44:34 -0700366 struct gpio_private *p;
367 struct gpio_private *todel;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100368 unsigned long flags;
Mikael Starvik7e920422005-07-27 11:44:34 -0700369
Jesper Nilsson45a41272008-01-25 15:42:41 +0100370 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700371
Jesper Nilsson45a41272008-01-25 15:42:41 +0100372 p = alarmlist;
Jesper Nilssonad433f22008-02-06 14:52:40 +0100373 todel = filp->private_data;
Mikael Starvik7e920422005-07-27 11:44:34 -0700374
Linus Torvalds1da177e2005-04-16 15:20:36 -0700375 /* unlink from alarmlist and free the private structure */
376
377 if (p == todel) {
378 alarmlist = todel->next;
379 } else {
380 while (p->next != todel)
381 p = p->next;
382 p->next = todel->next;
383 }
384
385 kfree(todel);
386 /* Check if there are still any alarms set */
387 p = alarmlist;
388 while (p) {
389 if (p->highalarm | p->lowalarm) {
390 gpio_some_alarms = 1;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100391 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700392 }
393 p = p->next;
394 }
395 gpio_some_alarms = 0;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100396out:
397 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700398 return 0;
399}
400
Jesper Nilsson45a41272008-01-25 15:42:41 +0100401/* Main device API. ioctl's to read/set/clear bits, as well as to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700402 * set alarms to wait for using a subsequent select().
403 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700404unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
405{
Jesper Nilsson45a41272008-01-25 15:42:41 +0100406 /* Set direction 0=unchanged 1=input,
407 * return mask with 1=input */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700408 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100409 *priv->dir = *priv->dir_shadow &=
Linus Torvalds1da177e2005-04-16 15:20:36 -0700410 ~((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700412 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100413
414 if (priv->minor != GPIO_MINOR_G)
415 return 0;
416
417 /* We must fiddle with R_GEN_CONFIG to change dir */
418 if (((arg & dir_g_in_bits) != arg) &&
419 (arg & changeable_dir_g)) {
420 arg &= changeable_dir_g;
421 /* Clear bits in genconfig to set to input */
422 if (arg & (1<<0)) {
423 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
424 dir_g_in_bits |= (1<<0);
425 dir_g_out_bits &= ~(1<<0);
426 }
427 if ((arg & 0x0000FF00) == 0x0000FF00) {
428 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
429 dir_g_in_bits |= 0x0000FF00;
430 dir_g_out_bits &= ~0x0000FF00;
431 }
432 if ((arg & 0x00FF0000) == 0x00FF0000) {
433 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
434 dir_g_in_bits |= 0x00FF0000;
435 dir_g_out_bits &= ~0x00FF0000;
436 }
437 if (arg & (1<<24)) {
438 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
439 dir_g_in_bits |= (1<<24);
440 dir_g_out_bits &= ~(1<<24);
441 }
442 D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
443 "genconfig to 0x%08lX "
444 "in_bits: 0x%08lX "
445 "out_bits: 0x%08lX\n",
446 (unsigned long)genconfig_shadow,
447 dir_g_in_bits, dir_g_out_bits));
448 *R_GEN_CONFIG = genconfig_shadow;
449 /* Must be a >120 ns delay before writing this again */
450
451 }
452 return dir_g_in_bits;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700453} /* setget_input */
454
455unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
456{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700457 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100458 *priv->dir = *priv->dir_shadow |=
459 ((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 return *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700461 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100462 if (priv->minor != GPIO_MINOR_G)
463 return 0;
464
465 /* We must fiddle with R_GEN_CONFIG to change dir */
466 if (((arg & dir_g_out_bits) != arg) &&
467 (arg & changeable_dir_g)) {
468 /* Set bits in genconfig to set to output */
469 if (arg & (1<<0)) {
470 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
471 dir_g_out_bits |= (1<<0);
472 dir_g_in_bits &= ~(1<<0);
473 }
474 if ((arg & 0x0000FF00) == 0x0000FF00) {
475 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
476 dir_g_out_bits |= 0x0000FF00;
477 dir_g_in_bits &= ~0x0000FF00;
478 }
479 if ((arg & 0x00FF0000) == 0x00FF0000) {
480 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
481 dir_g_out_bits |= 0x00FF0000;
482 dir_g_in_bits &= ~0x00FF0000;
483 }
484 if (arg & (1<<24)) {
485 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
486 dir_g_out_bits |= (1<<24);
487 dir_g_in_bits &= ~(1<<24);
488 }
489 D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
490 "genconfig to 0x%08lX "
491 "in_bits: 0x%08lX "
492 "out_bits: 0x%08lX\n",
493 (unsigned long)genconfig_shadow,
494 dir_g_in_bits, dir_g_out_bits));
495 *R_GEN_CONFIG = genconfig_shadow;
496 /* Must be a >120 ns delay before writing this again */
497 }
498 return dir_g_out_bits & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700499} /* setget_output */
500
501static int
502gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
503
504static int
505gpio_ioctl(struct inode *inode, struct file *file,
506 unsigned int cmd, unsigned long arg)
507{
508 unsigned long flags;
509 unsigned long val;
Mikael Starvik7e920422005-07-27 11:44:34 -0700510 int ret = 0;
511
Jesper Nilssonad433f22008-02-06 14:52:40 +0100512 struct gpio_private *priv = file->private_data;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100513 if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700514 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700515
Jesper Nilsson45a41272008-01-25 15:42:41 +0100516 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700517
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518 switch (_IOC_NR(cmd)) {
519 case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
520 // read the port
521 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700522 ret = *priv->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700523 } else if (priv->minor == GPIO_MINOR_G) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700524 ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700525 }
526 break;
527 case IO_SETBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 // set changeable bits with a 1 in arg
529 if (USE_PORTS(priv)) {
530 *priv->port = *priv->shadow |=
531 ((unsigned char)arg & priv->changeable_bits);
532 } else if (priv->minor == GPIO_MINOR_G) {
533 *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
534 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700535 break;
536 case IO_CLRBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700537 // clear changeable bits with a 1 in arg
538 if (USE_PORTS(priv)) {
539 *priv->port = *priv->shadow &=
540 ~((unsigned char)arg & priv->changeable_bits);
541 } else if (priv->minor == GPIO_MINOR_G) {
542 *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
543 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700544 break;
545 case IO_HIGHALARM:
546 // set alarm when bits with 1 in arg go high
547 priv->highalarm |= arg;
548 gpio_some_alarms = 1;
549 break;
550 case IO_LOWALARM:
551 // set alarm when bits with 1 in arg go low
552 priv->lowalarm |= arg;
553 gpio_some_alarms = 1;
554 break;
555 case IO_CLRALARM:
556 // clear alarm for bits with 1 in arg
557 priv->highalarm &= ~arg;
558 priv->lowalarm &= ~arg;
559 {
560 /* Must update gpio_some_alarms */
561 struct gpio_private *p = alarmlist;
562 int some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100563 spin_lock_irq(&gpio_lock);
564 p = alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700565 some_alarms = 0;
566 while (p) {
567 if (p->highalarm | p->lowalarm) {
568 some_alarms = 1;
569 break;
570 }
571 p = p->next;
572 }
573 gpio_some_alarms = some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100574 spin_unlock_irq(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700575 }
576 break;
577 case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
578 /* Read direction 0=input 1=output */
579 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700580 ret = *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700581 } else if (priv->minor == GPIO_MINOR_G) {
582 /* Note: Some bits are both in and out,
583 * Those that are dual is set here as well.
584 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700585 ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700586 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700587 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700588 case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
589 /* Set direction 0=unchanged 1=input,
590 * return mask with 1=input
591 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700592 ret = setget_input(priv, arg) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700593 break;
594 case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
595 /* Set direction 0=unchanged 1=output,
596 * return mask with 1=output
597 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700598 ret = setget_output(priv, arg) & 0x7FFFFFFF;
599 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700600 case IO_SHUTDOWN:
601 SOFT_SHUTDOWN();
602 break;
603 case IO_GET_PWR_BT:
604#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
Mikael Starvik7e920422005-07-27 11:44:34 -0700605 ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700606#else
Mikael Starvik7e920422005-07-27 11:44:34 -0700607 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700608#endif
609 break;
610 case IO_CFG_WRITE_MODE:
611 priv->clk_mask = arg & 0xFF;
612 priv->data_mask = (arg >> 8) & 0xFF;
613 priv->write_msb = (arg >> 16) & 0x01;
614 /* Check if we're allowed to change the bits and
615 * the direction is correct
616 */
617 if (!((priv->clk_mask & priv->changeable_bits) &&
618 (priv->data_mask & priv->changeable_bits) &&
619 (priv->clk_mask & *priv->dir_shadow) &&
620 (priv->data_mask & *priv->dir_shadow)))
621 {
622 priv->clk_mask = 0;
623 priv->data_mask = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700624 ret = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700625 }
626 break;
627 case IO_READ_INBITS:
628 /* *arg is result of reading the input pins */
629 if (USE_PORTS(priv)) {
630 val = *priv->port;
631 } else if (priv->minor == GPIO_MINOR_G) {
632 val = *R_PORT_G_DATA;
633 }
Jesper Nilssonad433f22008-02-06 14:52:40 +0100634 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700635 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700636 break;
637 case IO_READ_OUTBITS:
638 /* *arg is result of reading the output shadow */
639 if (USE_PORTS(priv)) {
640 val = *priv->shadow;
641 } else if (priv->minor == GPIO_MINOR_G) {
642 val = port_g_data_shadow;
643 }
Jesper Nilssonad433f22008-02-06 14:52:40 +0100644 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700645 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700646 break;
647 case IO_SETGET_INPUT:
648 /* bits set in *arg is set to input,
649 * *arg updated with current input pins.
650 */
Jesper Nilssonad433f22008-02-06 14:52:40 +0100651 if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700652 {
653 ret = -EFAULT;
654 break;
655 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700656 val = setget_input(priv, val);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100657 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700658 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 break;
660 case IO_SETGET_OUTPUT:
661 /* bits set in *arg is set to output,
662 * *arg updated with current output pins.
663 */
Jesper Nilssonad433f22008-02-06 14:52:40 +0100664 if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700665 ret = -EFAULT;
666 break;
667 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700668 val = setget_output(priv, val);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100669 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700670 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 break;
672 default:
673 if (priv->minor == GPIO_MINOR_LEDS)
Mikael Starvik7e920422005-07-27 11:44:34 -0700674 ret = gpio_leds_ioctl(cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700675 else
Mikael Starvik7e920422005-07-27 11:44:34 -0700676 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700677 } /* switch */
Mikael Starvik7e920422005-07-27 11:44:34 -0700678
Jesper Nilsson45a41272008-01-25 15:42:41 +0100679 spin_unlock_irqrestore(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700680 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700681}
682
683static int
684gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
685{
686 unsigned char green;
687 unsigned char red;
688
689 switch (_IOC_NR(cmd)) {
690 case IO_LEDACTIVE_SET:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100691 green = ((unsigned char)arg) & 1;
692 red = (((unsigned char)arg) >> 1) & 1;
693 CRIS_LED_ACTIVE_SET_G(green);
694 CRIS_LED_ACTIVE_SET_R(red);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700695 break;
696
697 case IO_LED_SETBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100698 CRIS_LED_BIT_SET(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700699 break;
700
701 case IO_LED_CLRBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100702 CRIS_LED_BIT_CLR(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700703 break;
704
705 default:
706 return -EINVAL;
707 } /* switch */
708
709 return 0;
710}
711
Jesper Nilssonad433f22008-02-06 14:52:40 +0100712static const struct file_operations gpio_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700713 .owner = THIS_MODULE,
714 .poll = gpio_poll,
715 .ioctl = gpio_ioctl,
716 .write = gpio_write,
717 .open = gpio_open,
718 .release = gpio_release,
719};
720
Jesper Nilssonad433f22008-02-06 14:52:40 +0100721static void ioif_watcher(const unsigned int gpio_in_available,
722 const unsigned int gpio_out_available,
723 const unsigned char pa_available,
724 const unsigned char pb_available)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700725{
Mikael Starvik7e920422005-07-27 11:44:34 -0700726 unsigned long int flags;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100727
728 D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
729 D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x "
730 "PA: 0x%02x PB: 0x%02x\n",
731 gpio_in_available, gpio_out_available,
732 pa_available, pb_available));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700733
Mikael Starvik7e920422005-07-27 11:44:34 -0700734 spin_lock_irqsave(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700735
Mikael Starvik7e920422005-07-27 11:44:34 -0700736 dir_g_in_bits = gpio_in_available;
737 dir_g_out_bits = gpio_out_available;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
739 /* Initialise the dir_g_shadow etc. depending on genconfig */
740 /* 0=input 1=output */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100741 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700742 dir_g_shadow |= (1 << 0);
743 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
744 dir_g_shadow |= 0x0000FF00;
745 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
746 dir_g_shadow |= 0x00FF0000;
747 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
748 dir_g_shadow |= (1 << 24);
749
Mikael Starvik7e920422005-07-27 11:44:34 -0700750 changeable_dir_g = changeable_dir_g_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700751 changeable_dir_g &= dir_g_out_bits;
752 changeable_dir_g &= dir_g_in_bits;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100753
754 /* Correct the bits that can change direction */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700755 dir_g_out_bits &= ~changeable_dir_g;
756 dir_g_out_bits |= dir_g_shadow;
757 dir_g_in_bits &= ~changeable_dir_g;
758 dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
759
Mikael Starvik7e920422005-07-27 11:44:34 -0700760 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700761
Jesper Nilsson45a41272008-01-25 15:42:41 +0100762 printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX "
763 "val: %08lX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764 dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
765 printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
766 dir_g_shadow, changeable_dir_g);
767}
768
769/* main driver initialization routine, called from mem.c */
770
Jesper Nilssonad433f22008-02-06 14:52:40 +0100771static int __init gpio_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700772{
773 int res;
774#if defined (CONFIG_ETRAX_CSP0_LEDS)
775 int i;
776#endif
Linus Torvalds1da177e2005-04-16 15:20:36 -0700777
778 res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
779 if (res < 0) {
780 printk(KERN_ERR "gpio: couldn't get a major number.\n");
781 return res;
782 }
783
784 /* Clear all leds */
785#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
Jesper Nilsson45a41272008-01-25 15:42:41 +0100786 CRIS_LED_NETWORK_SET(0);
787 CRIS_LED_ACTIVE_SET(0);
788 CRIS_LED_DISK_READ(0);
789 CRIS_LED_DISK_WRITE(0);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700790
791#if defined (CONFIG_ETRAX_CSP0_LEDS)
Jesper Nilsson45a41272008-01-25 15:42:41 +0100792 for (i = 0; i < 32; i++)
793 CRIS_LED_BIT_SET(i);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700794#endif
795
796#endif
Mikael Starvik7e920422005-07-27 11:44:34 -0700797 /* The I/O interface allocation watcher will be called when
798 * registering it. */
799 if (cris_io_interface_register_watcher(ioif_watcher)){
Jesper Nilsson45a41272008-01-25 15:42:41 +0100800 printk(KERN_WARNING "gpio_init: Failed to install IO "
801 "if allocator watcher\n");
Mikael Starvik7e920422005-07-27 11:44:34 -0700802 }
803
Jesper Nilsson45a41272008-01-25 15:42:41 +0100804 printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 "
805 "Axis Communications AB\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700806 /* We call etrax_gpio_wake_up_check() from timer interrupt and
807 * from cpu_idle() in kernel/process.c
808 * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
809 * in some tests.
Jesper Nilsson45a41272008-01-25 15:42:41 +0100810 */
811 res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
812 IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name);
813 if (res) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700814 printk(KERN_CRIT "err: timer0 irq for gpio\n");
Jesper Nilsson45a41272008-01-25 15:42:41 +0100815 return res;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700816 }
Jesper Nilssonad433f22008-02-06 14:52:40 +0100817 res = request_irq(PA_IRQ_NBR, gpio_interrupt,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100818 IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name);
819 if (res)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700820 printk(KERN_CRIT "err: PA irq for gpio\n");
Linus Torvalds1da177e2005-04-16 15:20:36 -0700821
822 return res;
823}
824
825/* this makes sure that gpio_init is called during kernel boot */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700826module_init(gpio_init);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100827