blob: 86048e697eb5eb57c1cf241f5124f72e76b9cf97 [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>
Jonathan Corbet0c401df2008-05-15 09:10:18 -060019#include <linux/smp_lock.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070020#include <linux/string.h>
21#include <linux/poll.h>
22#include <linux/init.h>
23#include <linux/interrupt.h>
24
25#include <asm/etraxgpio.h>
26#include <asm/arch/svinto.h>
27#include <asm/io.h>
28#include <asm/system.h>
29#include <asm/irq.h>
Mikael Starvik7e920422005-07-27 11:44:34 -070030#include <asm/arch/io_interface_mux.h>
Linus Torvalds1da177e2005-04-16 15:20:36 -070031
32#define GPIO_MAJOR 120 /* experimental MAJOR number */
33
34#define D(x)
35
36#if 0
37static int dp_cnt;
38#define DP(x) do { dp_cnt++; if (dp_cnt % 1000 == 0) x; }while(0)
39#else
40#define DP(x)
41#endif
Jesper Nilsson45a41272008-01-25 15:42:41 +010042
Linus Torvalds1da177e2005-04-16 15:20:36 -070043static char gpio_name[] = "etrax gpio";
44
45#if 0
46static wait_queue_head_t *gpio_wq;
47#endif
48
49static int gpio_ioctl(struct inode *inode, struct file *file,
Jesper Nilssonad433f22008-02-06 14:52:40 +010050 unsigned int cmd, unsigned long arg);
51static ssize_t gpio_write(struct file *file, const char __user *buf,
52 size_t count, loff_t *off);
Linus Torvalds1da177e2005-04-16 15:20:36 -070053static int gpio_open(struct inode *inode, struct file *filp);
54static int gpio_release(struct inode *inode, struct file *filp);
55static unsigned int gpio_poll(struct file *filp, struct poll_table_struct *wait);
56
57/* private data per open() of this driver */
58
59struct gpio_private {
60 struct gpio_private *next;
61 /* These fields are for PA and PB only */
62 volatile unsigned char *port, *shadow;
63 volatile unsigned char *dir, *dir_shadow;
64 unsigned char changeable_dir;
65 unsigned char changeable_bits;
66 unsigned char clk_mask;
67 unsigned char data_mask;
68 unsigned char write_msb;
69 unsigned char pad1, pad2, pad3;
70 /* These fields are generic */
71 unsigned long highalarm, lowalarm;
72 wait_queue_head_t alarm_wq;
73 int minor;
74};
75
76/* linked list of alarms to check for */
77
Jesper Nilssonad433f22008-02-06 14:52:40 +010078static struct gpio_private *alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -070079
Jesper Nilssonad433f22008-02-06 14:52:40 +010080static int gpio_some_alarms; /* Set if someone uses alarm */
81static unsigned long gpio_pa_irq_enabled_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -070082
Mikael Starvik7e920422005-07-27 11:44:34 -070083static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
84
Linus Torvalds1da177e2005-04-16 15:20:36 -070085/* Port A and B use 8 bit access, but Port G is 32 bit */
86#define NUM_PORTS (GPIO_MINOR_B+1)
87
Jesper Nilsson45a41272008-01-25 15:42:41 +010088static volatile unsigned char *ports[NUM_PORTS] = {
89 R_PORT_PA_DATA,
Linus Torvalds1da177e2005-04-16 15:20:36 -070090 R_PORT_PB_DATA,
91};
92static volatile unsigned char *shads[NUM_PORTS] = {
Jesper Nilsson45a41272008-01-25 15:42:41 +010093 &port_pa_data_shadow,
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 &port_pb_data_shadow
95};
96
97/* What direction bits that are user changeable 1=changeable*/
98#ifndef CONFIG_ETRAX_PA_CHANGEABLE_DIR
99#define CONFIG_ETRAX_PA_CHANGEABLE_DIR 0x00
100#endif
101#ifndef CONFIG_ETRAX_PB_CHANGEABLE_DIR
102#define CONFIG_ETRAX_PB_CHANGEABLE_DIR 0x00
103#endif
104
105#ifndef CONFIG_ETRAX_PA_CHANGEABLE_BITS
106#define CONFIG_ETRAX_PA_CHANGEABLE_BITS 0xFF
107#endif
108#ifndef CONFIG_ETRAX_PB_CHANGEABLE_BITS
109#define CONFIG_ETRAX_PB_CHANGEABLE_BITS 0xFF
110#endif
111
112
Jesper Nilsson45a41272008-01-25 15:42:41 +0100113static unsigned char changeable_dir[NUM_PORTS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700114 CONFIG_ETRAX_PA_CHANGEABLE_DIR,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100115 CONFIG_ETRAX_PB_CHANGEABLE_DIR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700116};
Jesper Nilsson45a41272008-01-25 15:42:41 +0100117static unsigned char changeable_bits[NUM_PORTS] = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700118 CONFIG_ETRAX_PA_CHANGEABLE_BITS,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100119 CONFIG_ETRAX_PB_CHANGEABLE_BITS
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120};
121
Jesper Nilsson45a41272008-01-25 15:42:41 +0100122static volatile unsigned char *dir[NUM_PORTS] = {
123 R_PORT_PA_DIR,
124 R_PORT_PB_DIR
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125};
126
127static volatile unsigned char *dir_shadow[NUM_PORTS] = {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100128 &port_pa_dir_shadow,
129 &port_pb_dir_shadow
Linus Torvalds1da177e2005-04-16 15:20:36 -0700130};
131
Mikael Starvik7e920422005-07-27 11:44:34 -0700132/* All bits in port g that can change dir. */
133static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
134
Jesper Nilsson45a41272008-01-25 15:42:41 +0100135/* Port G is 32 bit, handle it special, some bits are both inputs
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 and outputs at the same time, only some of the bits can change direction
137 and some of them in groups of 8 bit. */
138static unsigned long changeable_dir_g;
139static unsigned long dir_g_in_bits;
140static unsigned long dir_g_out_bits;
141static unsigned long dir_g_shadow; /* 1=output */
142
143#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
144
145
Jesper Nilsson45a41272008-01-25 15:42:41 +0100146static unsigned int gpio_poll(struct file *file, poll_table *wait)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700147{
148 unsigned int mask = 0;
Jesper Nilssonad433f22008-02-06 14:52:40 +0100149 struct gpio_private *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700150 unsigned long data;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100151 unsigned long flags;
152
153 spin_lock_irqsave(&gpio_lock, flags);
154
Linus Torvalds1da177e2005-04-16 15:20:36 -0700155 poll_wait(file, &priv->alarm_wq, wait);
156 if (priv->minor == GPIO_MINOR_A) {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700157 unsigned long tmp;
158 data = *R_PORT_PA_DATA;
159 /* PA has support for high level interrupt -
160 * lets activate for those low and with highalarm set
161 */
162 tmp = ~data & priv->highalarm & 0xFF;
163 tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
Jesper Nilsson45a41272008-01-25 15:42:41 +0100164
Linus Torvalds1da177e2005-04-16 15:20:36 -0700165 gpio_pa_irq_enabled_mask |= tmp;
166 *R_IRQ_MASK1_SET = tmp;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700167 } else if (priv->minor == GPIO_MINOR_B)
168 data = *R_PORT_PB_DATA;
169 else if (priv->minor == GPIO_MINOR_G)
170 data = *R_PORT_G_DATA;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800171 else {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100172 mask = 0;
173 goto out;
Roel Kluin5c6ff792007-11-14 17:00:06 -0800174 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100175
Linus Torvalds1da177e2005-04-16 15:20:36 -0700176 if ((data & priv->highalarm) ||
177 (~data & priv->lowalarm)) {
178 mask = POLLIN|POLLRDNORM;
179 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700180
Jesper Nilsson45a41272008-01-25 15:42:41 +0100181out:
182 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700183 DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
Mikael Starvik7e920422005-07-27 11:44:34 -0700184
Linus Torvalds1da177e2005-04-16 15:20:36 -0700185 return mask;
186}
187
188int etrax_gpio_wake_up_check(void)
189{
Jesper Nilsson45a41272008-01-25 15:42:41 +0100190 struct gpio_private *priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700191 unsigned long data = 0;
192 int ret = 0;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100193 unsigned long flags;
194
195 spin_lock_irqsave(&gpio_lock, flags);
196 priv = alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700197 while (priv) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100198 if (USE_PORTS(priv))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700199 data = *priv->port;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100200 else if (priv->minor == GPIO_MINOR_G)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700201 data = *R_PORT_G_DATA;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100202
Linus Torvalds1da177e2005-04-16 15:20:36 -0700203 if ((data & priv->highalarm) ||
204 (~data & priv->lowalarm)) {
205 DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
206 wake_up_interruptible(&priv->alarm_wq);
207 ret = 1;
208 }
209 priv = priv->next;
210 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100211 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700212 return ret;
213}
214
215static irqreturn_t
Jesper Nilsson45a41272008-01-25 15:42:41 +0100216gpio_poll_timer_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700217{
218 if (gpio_some_alarms) {
219 etrax_gpio_wake_up_check();
220 return IRQ_HANDLED;
221 }
222 return IRQ_NONE;
223}
224
225static irqreturn_t
Jesper Nilssonad433f22008-02-06 14:52:40 +0100226gpio_interrupt(int irq, void *dev_id)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700227{
228 unsigned long tmp;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100229 unsigned long flags;
230
231 spin_lock_irqsave(&gpio_lock, flags);
232
Linus Torvalds1da177e2005-04-16 15:20:36 -0700233 /* Find what PA interrupts are active */
234 tmp = (*R_IRQ_READ1);
235
236 /* Find those that we have enabled */
237 tmp &= gpio_pa_irq_enabled_mask;
238
239 /* Clear them.. */
240 *R_IRQ_MASK1_CLR = tmp;
241 gpio_pa_irq_enabled_mask &= ~tmp;
242
Jesper Nilsson45a41272008-01-25 15:42:41 +0100243 spin_unlock_irqrestore(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700244
Jesper Nilsson45a41272008-01-25 15:42:41 +0100245 if (gpio_some_alarms)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700246 return IRQ_RETVAL(etrax_gpio_wake_up_check());
Jesper Nilsson45a41272008-01-25 15:42:41 +0100247
Linus Torvalds1da177e2005-04-16 15:20:36 -0700248 return IRQ_NONE;
249}
250
Jesper Nilsson45a41272008-01-25 15:42:41 +0100251static void gpio_write_bit(struct gpio_private *priv,
252 unsigned char data, int bit)
253{
254 *priv->port = *priv->shadow &= ~(priv->clk_mask);
255 if (data & 1 << bit)
256 *priv->port = *priv->shadow |= priv->data_mask;
257 else
258 *priv->port = *priv->shadow &= ~(priv->data_mask);
259
260 /* For FPGA: min 5.0ns (DCC) before CCLK high */
261 *priv->port = *priv->shadow |= priv->clk_mask;
262}
263
264static void gpio_write_byte(struct gpio_private *priv, unsigned char data)
265{
266 int i;
267
268 if (priv->write_msb)
269 for (i = 7; i >= 0; i--)
270 gpio_write_bit(priv, data, i);
271 else
272 for (i = 0; i <= 7; i++)
273 gpio_write_bit(priv, data, i);
274}
Linus Torvalds1da177e2005-04-16 15:20:36 -0700275
Jesper Nilssonad433f22008-02-06 14:52:40 +0100276static ssize_t gpio_write(struct file *file, const char __user *buf,
277 size_t count, loff_t *off)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700278{
Jesper Nilssonad433f22008-02-06 14:52:40 +0100279 struct gpio_private *priv = file->private_data;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700280 unsigned long flags;
281 ssize_t retval = count;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100282
283 if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B)
284 return -EFAULT;
285
286 if (!access_ok(VERIFY_READ, buf, count))
287 return -EFAULT;
288
289 spin_lock_irqsave(&gpio_lock, flags);
290
Linus Torvalds1da177e2005-04-16 15:20:36 -0700291 /* It must have been configured using the IO_CFG_WRITE_MODE */
292 /* Perhaps a better error code? */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100293 if (priv->clk_mask == 0 || priv->data_mask == 0) {
Roel Kluin5c6ff792007-11-14 17:00:06 -0800294 retval = -EPERM;
295 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700296 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100297
298 D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X "
299 "clk 0x%02X msb: %i\n",
300 count, priv->data_mask, priv->clk_mask, priv->write_msb));
301
302 while (count--)
303 gpio_write_byte(priv, *buf++);
304
Roel Kluin5c6ff792007-11-14 17:00:06 -0800305out:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100306 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700307 return retval;
308}
309
310
311
312static int
313gpio_open(struct inode *inode, struct file *filp)
314{
315 struct gpio_private *priv;
Eric Sesterhenn32ea0862006-07-10 04:45:02 -0700316 int p = iminor(inode);
Jesper Nilsson45a41272008-01-25 15:42:41 +0100317 unsigned long flags;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700318
319 if (p > GPIO_MINOR_LAST)
320 return -EINVAL;
321
Jesper Nilssonad433f22008-02-06 14:52:40 +0100322 priv = kzalloc(sizeof(struct gpio_private), GFP_KERNEL);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700323
324 if (!priv)
325 return -ENOMEM;
326
Jonathan Corbet0c401df2008-05-15 09:10:18 -0600327 lock_kernel();
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
Jesper Nilssonad433f22008-02-06 14:52:40 +0100354 filp->private_data = priv;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700355
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
Jonathan Corbet0c401df2008-05-15 09:10:18 -0600362 unlock_kernel();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700363 return 0;
364}
365
366static int
367gpio_release(struct inode *inode, struct file *filp)
368{
Mikael Starvik7e920422005-07-27 11:44:34 -0700369 struct gpio_private *p;
370 struct gpio_private *todel;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100371 unsigned long flags;
Mikael Starvik7e920422005-07-27 11:44:34 -0700372
Jesper Nilsson45a41272008-01-25 15:42:41 +0100373 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700374
Jesper Nilsson45a41272008-01-25 15:42:41 +0100375 p = alarmlist;
Jesper Nilssonad433f22008-02-06 14:52:40 +0100376 todel = filp->private_data;
Mikael Starvik7e920422005-07-27 11:44:34 -0700377
Linus Torvalds1da177e2005-04-16 15:20:36 -0700378 /* unlink from alarmlist and free the private structure */
379
380 if (p == todel) {
381 alarmlist = todel->next;
382 } else {
383 while (p->next != todel)
384 p = p->next;
385 p->next = todel->next;
386 }
387
388 kfree(todel);
389 /* Check if there are still any alarms set */
390 p = alarmlist;
391 while (p) {
392 if (p->highalarm | p->lowalarm) {
393 gpio_some_alarms = 1;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100394 goto out;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700395 }
396 p = p->next;
397 }
398 gpio_some_alarms = 0;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100399out:
400 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700401 return 0;
402}
403
Jesper Nilsson45a41272008-01-25 15:42:41 +0100404/* Main device API. ioctl's to read/set/clear bits, as well as to
Linus Torvalds1da177e2005-04-16 15:20:36 -0700405 * set alarms to wait for using a subsequent select().
406 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700407unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
408{
Jesper Nilsson45a41272008-01-25 15:42:41 +0100409 /* Set direction 0=unchanged 1=input,
410 * return mask with 1=input */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700411 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100412 *priv->dir = *priv->dir_shadow &=
Linus Torvalds1da177e2005-04-16 15:20:36 -0700413 ~((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700414 return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700415 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100416
417 if (priv->minor != GPIO_MINOR_G)
418 return 0;
419
420 /* We must fiddle with R_GEN_CONFIG to change dir */
421 if (((arg & dir_g_in_bits) != arg) &&
422 (arg & changeable_dir_g)) {
423 arg &= changeable_dir_g;
424 /* Clear bits in genconfig to set to input */
425 if (arg & (1<<0)) {
426 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
427 dir_g_in_bits |= (1<<0);
428 dir_g_out_bits &= ~(1<<0);
429 }
430 if ((arg & 0x0000FF00) == 0x0000FF00) {
431 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
432 dir_g_in_bits |= 0x0000FF00;
433 dir_g_out_bits &= ~0x0000FF00;
434 }
435 if ((arg & 0x00FF0000) == 0x00FF0000) {
436 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
437 dir_g_in_bits |= 0x00FF0000;
438 dir_g_out_bits &= ~0x00FF0000;
439 }
440 if (arg & (1<<24)) {
441 genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
442 dir_g_in_bits |= (1<<24);
443 dir_g_out_bits &= ~(1<<24);
444 }
445 D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
446 "genconfig to 0x%08lX "
447 "in_bits: 0x%08lX "
448 "out_bits: 0x%08lX\n",
449 (unsigned long)genconfig_shadow,
450 dir_g_in_bits, dir_g_out_bits));
451 *R_GEN_CONFIG = genconfig_shadow;
452 /* Must be a >120 ns delay before writing this again */
453
454 }
455 return dir_g_in_bits;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700456} /* setget_input */
457
458unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
459{
Linus Torvalds1da177e2005-04-16 15:20:36 -0700460 if (USE_PORTS(priv)) {
Jesper Nilsson45a41272008-01-25 15:42:41 +0100461 *priv->dir = *priv->dir_shadow |=
462 ((unsigned char)arg & priv->changeable_dir);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700463 return *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700464 }
Jesper Nilsson45a41272008-01-25 15:42:41 +0100465 if (priv->minor != GPIO_MINOR_G)
466 return 0;
467
468 /* We must fiddle with R_GEN_CONFIG to change dir */
469 if (((arg & dir_g_out_bits) != arg) &&
470 (arg & changeable_dir_g)) {
471 /* Set bits in genconfig to set to output */
472 if (arg & (1<<0)) {
473 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
474 dir_g_out_bits |= (1<<0);
475 dir_g_in_bits &= ~(1<<0);
476 }
477 if ((arg & 0x0000FF00) == 0x0000FF00) {
478 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
479 dir_g_out_bits |= 0x0000FF00;
480 dir_g_in_bits &= ~0x0000FF00;
481 }
482 if ((arg & 0x00FF0000) == 0x00FF0000) {
483 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
484 dir_g_out_bits |= 0x00FF0000;
485 dir_g_in_bits &= ~0x00FF0000;
486 }
487 if (arg & (1<<24)) {
488 genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
489 dir_g_out_bits |= (1<<24);
490 dir_g_in_bits &= ~(1<<24);
491 }
492 D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
493 "genconfig to 0x%08lX "
494 "in_bits: 0x%08lX "
495 "out_bits: 0x%08lX\n",
496 (unsigned long)genconfig_shadow,
497 dir_g_in_bits, dir_g_out_bits));
498 *R_GEN_CONFIG = genconfig_shadow;
499 /* Must be a >120 ns delay before writing this again */
500 }
501 return dir_g_out_bits & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700502} /* setget_output */
503
504static int
505gpio_leds_ioctl(unsigned int cmd, unsigned long arg);
506
507static int
508gpio_ioctl(struct inode *inode, struct file *file,
509 unsigned int cmd, unsigned long arg)
510{
511 unsigned long flags;
512 unsigned long val;
Mikael Starvik7e920422005-07-27 11:44:34 -0700513 int ret = 0;
514
Jesper Nilssonad433f22008-02-06 14:52:40 +0100515 struct gpio_private *priv = file->private_data;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100516 if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700517 return -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700518
Jesper Nilsson45a41272008-01-25 15:42:41 +0100519 spin_lock_irqsave(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700520
Linus Torvalds1da177e2005-04-16 15:20:36 -0700521 switch (_IOC_NR(cmd)) {
522 case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
523 // read the port
524 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700525 ret = *priv->port;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700526 } else if (priv->minor == GPIO_MINOR_G) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700527 ret = (*R_PORT_G_DATA) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700528 }
529 break;
530 case IO_SETBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700531 // set changeable bits with a 1 in arg
532 if (USE_PORTS(priv)) {
533 *priv->port = *priv->shadow |=
534 ((unsigned char)arg & priv->changeable_bits);
535 } else if (priv->minor == GPIO_MINOR_G) {
536 *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
537 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700538 break;
539 case IO_CLRBITS:
Linus Torvalds1da177e2005-04-16 15:20:36 -0700540 // clear changeable bits with a 1 in arg
541 if (USE_PORTS(priv)) {
542 *priv->port = *priv->shadow &=
543 ~((unsigned char)arg & priv->changeable_bits);
544 } else if (priv->minor == GPIO_MINOR_G) {
545 *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
546 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700547 break;
548 case IO_HIGHALARM:
549 // set alarm when bits with 1 in arg go high
550 priv->highalarm |= arg;
551 gpio_some_alarms = 1;
552 break;
553 case IO_LOWALARM:
554 // set alarm when bits with 1 in arg go low
555 priv->lowalarm |= arg;
556 gpio_some_alarms = 1;
557 break;
558 case IO_CLRALARM:
559 // clear alarm for bits with 1 in arg
560 priv->highalarm &= ~arg;
561 priv->lowalarm &= ~arg;
562 {
563 /* Must update gpio_some_alarms */
564 struct gpio_private *p = alarmlist;
565 int some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100566 spin_lock_irq(&gpio_lock);
567 p = alarmlist;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700568 some_alarms = 0;
569 while (p) {
570 if (p->highalarm | p->lowalarm) {
571 some_alarms = 1;
572 break;
573 }
574 p = p->next;
575 }
576 gpio_some_alarms = some_alarms;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100577 spin_unlock_irq(&gpio_lock);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700578 }
579 break;
580 case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
581 /* Read direction 0=input 1=output */
582 if (USE_PORTS(priv)) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700583 ret = *priv->dir_shadow;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700584 } else if (priv->minor == GPIO_MINOR_G) {
585 /* Note: Some bits are both in and out,
586 * Those that are dual is set here as well.
587 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700588 ret = (dir_g_shadow | dir_g_out_bits) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700589 }
Mikael Starvik7e920422005-07-27 11:44:34 -0700590 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700591 case IO_SETINPUT: /* Use IO_SETGET_INPUT instead! */
592 /* Set direction 0=unchanged 1=input,
593 * return mask with 1=input
594 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700595 ret = setget_input(priv, arg) & 0x7FFFFFFF;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700596 break;
597 case IO_SETOUTPUT: /* Use IO_SETGET_OUTPUT instead! */
598 /* Set direction 0=unchanged 1=output,
599 * return mask with 1=output
600 */
Mikael Starvik7e920422005-07-27 11:44:34 -0700601 ret = setget_output(priv, arg) & 0x7FFFFFFF;
602 break;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700603 case IO_SHUTDOWN:
604 SOFT_SHUTDOWN();
605 break;
606 case IO_GET_PWR_BT:
607#if defined (CONFIG_ETRAX_SOFT_SHUTDOWN)
Mikael Starvik7e920422005-07-27 11:44:34 -0700608 ret = (*R_PORT_G_DATA & ( 1 << CONFIG_ETRAX_POWERBUTTON_BIT));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700609#else
Mikael Starvik7e920422005-07-27 11:44:34 -0700610 ret = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700611#endif
612 break;
613 case IO_CFG_WRITE_MODE:
614 priv->clk_mask = arg & 0xFF;
615 priv->data_mask = (arg >> 8) & 0xFF;
616 priv->write_msb = (arg >> 16) & 0x01;
617 /* Check if we're allowed to change the bits and
618 * the direction is correct
619 */
620 if (!((priv->clk_mask & priv->changeable_bits) &&
621 (priv->data_mask & priv->changeable_bits) &&
622 (priv->clk_mask & *priv->dir_shadow) &&
623 (priv->data_mask & *priv->dir_shadow)))
624 {
625 priv->clk_mask = 0;
626 priv->data_mask = 0;
Mikael Starvik7e920422005-07-27 11:44:34 -0700627 ret = -EPERM;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700628 }
629 break;
630 case IO_READ_INBITS:
631 /* *arg is result of reading the input pins */
632 if (USE_PORTS(priv)) {
633 val = *priv->port;
634 } else if (priv->minor == GPIO_MINOR_G) {
635 val = *R_PORT_G_DATA;
636 }
Jesper Nilssonad433f22008-02-06 14:52:40 +0100637 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700638 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700639 break;
640 case IO_READ_OUTBITS:
641 /* *arg is result of reading the output shadow */
642 if (USE_PORTS(priv)) {
643 val = *priv->shadow;
644 } else if (priv->minor == GPIO_MINOR_G) {
645 val = port_g_data_shadow;
646 }
Jesper Nilssonad433f22008-02-06 14:52:40 +0100647 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700648 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700649 break;
650 case IO_SETGET_INPUT:
651 /* bits set in *arg is set to input,
652 * *arg updated with current input pins.
653 */
Jesper Nilssonad433f22008-02-06 14:52:40 +0100654 if (copy_from_user(&val, (void __user *)arg, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700655 {
656 ret = -EFAULT;
657 break;
658 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700659 val = setget_input(priv, val);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100660 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700661 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700662 break;
663 case IO_SETGET_OUTPUT:
664 /* bits set in *arg is set to output,
665 * *arg updated with current output pins.
666 */
Jesper Nilssonad433f22008-02-06 14:52:40 +0100667 if (copy_from_user(&val, (void __user *)arg, sizeof(val))) {
Mikael Starvik7e920422005-07-27 11:44:34 -0700668 ret = -EFAULT;
669 break;
670 }
Linus Torvalds1da177e2005-04-16 15:20:36 -0700671 val = setget_output(priv, val);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100672 if (copy_to_user((void __user *)arg, &val, sizeof(val)))
Mikael Starvik7e920422005-07-27 11:44:34 -0700673 ret = -EFAULT;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700674 break;
675 default:
676 if (priv->minor == GPIO_MINOR_LEDS)
Mikael Starvik7e920422005-07-27 11:44:34 -0700677 ret = gpio_leds_ioctl(cmd, arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700678 else
Mikael Starvik7e920422005-07-27 11:44:34 -0700679 ret = -EINVAL;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700680 } /* switch */
Mikael Starvik7e920422005-07-27 11:44:34 -0700681
Jesper Nilsson45a41272008-01-25 15:42:41 +0100682 spin_unlock_irqrestore(&gpio_lock, flags);
Mikael Starvik7e920422005-07-27 11:44:34 -0700683 return ret;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700684}
685
686static int
687gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
688{
689 unsigned char green;
690 unsigned char red;
691
692 switch (_IOC_NR(cmd)) {
693 case IO_LEDACTIVE_SET:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100694 green = ((unsigned char)arg) & 1;
695 red = (((unsigned char)arg) >> 1) & 1;
696 CRIS_LED_ACTIVE_SET_G(green);
697 CRIS_LED_ACTIVE_SET_R(red);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700698 break;
699
700 case IO_LED_SETBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100701 CRIS_LED_BIT_SET(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700702 break;
703
704 case IO_LED_CLRBIT:
Jesper Nilsson45a41272008-01-25 15:42:41 +0100705 CRIS_LED_BIT_CLR(arg);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700706 break;
707
708 default:
709 return -EINVAL;
710 } /* switch */
711
712 return 0;
713}
714
Jesper Nilssonad433f22008-02-06 14:52:40 +0100715static const struct file_operations gpio_fops = {
Linus Torvalds1da177e2005-04-16 15:20:36 -0700716 .owner = THIS_MODULE,
717 .poll = gpio_poll,
718 .ioctl = gpio_ioctl,
719 .write = gpio_write,
720 .open = gpio_open,
721 .release = gpio_release,
722};
723
Jesper Nilssonad433f22008-02-06 14:52:40 +0100724static void ioif_watcher(const unsigned int gpio_in_available,
725 const unsigned int gpio_out_available,
726 const unsigned char pa_available,
727 const unsigned char pb_available)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700728{
Mikael Starvik7e920422005-07-27 11:44:34 -0700729 unsigned long int flags;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100730
731 D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
732 D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x "
733 "PA: 0x%02x PB: 0x%02x\n",
734 gpio_in_available, gpio_out_available,
735 pa_available, pb_available));
Linus Torvalds1da177e2005-04-16 15:20:36 -0700736
Mikael Starvik7e920422005-07-27 11:44:34 -0700737 spin_lock_irqsave(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700738
Mikael Starvik7e920422005-07-27 11:44:34 -0700739 dir_g_in_bits = gpio_in_available;
740 dir_g_out_bits = gpio_out_available;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700741
742 /* Initialise the dir_g_shadow etc. depending on genconfig */
743 /* 0=input 1=output */
Jesper Nilsson45a41272008-01-25 15:42:41 +0100744 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
Linus Torvalds1da177e2005-04-16 15:20:36 -0700745 dir_g_shadow |= (1 << 0);
746 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
747 dir_g_shadow |= 0x0000FF00;
748 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g16_23dir, out))
749 dir_g_shadow |= 0x00FF0000;
750 if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g24dir, out))
751 dir_g_shadow |= (1 << 24);
752
Mikael Starvik7e920422005-07-27 11:44:34 -0700753 changeable_dir_g = changeable_dir_g_mask;
Linus Torvalds1da177e2005-04-16 15:20:36 -0700754 changeable_dir_g &= dir_g_out_bits;
755 changeable_dir_g &= dir_g_in_bits;
Jesper Nilsson45a41272008-01-25 15:42:41 +0100756
757 /* Correct the bits that can change direction */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700758 dir_g_out_bits &= ~changeable_dir_g;
759 dir_g_out_bits |= dir_g_shadow;
760 dir_g_in_bits &= ~changeable_dir_g;
761 dir_g_in_bits |= (~dir_g_shadow & changeable_dir_g);
762
Mikael Starvik7e920422005-07-27 11:44:34 -0700763 spin_unlock_irqrestore(&gpio_lock, flags);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700764
Jesper Nilsson45a41272008-01-25 15:42:41 +0100765 printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX "
766 "val: %08lX\n",
Linus Torvalds1da177e2005-04-16 15:20:36 -0700767 dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
768 printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
769 dir_g_shadow, changeable_dir_g);
770}
771
772/* main driver initialization routine, called from mem.c */
773
Jesper Nilssonad433f22008-02-06 14:52:40 +0100774static int __init gpio_init(void)
Linus Torvalds1da177e2005-04-16 15:20:36 -0700775{
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 Nilssonad433f22008-02-06 14:52:40 +0100820 res = request_irq(PA_IRQ_NBR, gpio_interrupt,
Jesper Nilsson45a41272008-01-25 15:42:41 +0100821 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 */
Linus Torvalds1da177e2005-04-16 15:20:36 -0700829module_init(gpio_init);
Jesper Nilssonad433f22008-02-06 14:52:40 +0100830