Commit 45a4127c authored by Jesper Nilsson's avatar Jesper Nilsson

CRIS v10: Update drivers/gpio.c, fix locking and general improvements.

- Change all spin_lock/local_irq_save to spin_lock_irqsave.
- Change multiple returns in functions where we have a lock to goto out.
- Correct number of arguments to gpio_poll_timer_interrupt, gpio_pa_interrupt.
- Break out gpio_write logic to smaller functions to make it readable.
- In setget_input and setget_output, avoid extra if-indent level.
- Change name LED_* -> CRIS_LED_* to avoid name clash.
- Don't use braces around single statement ifs.
- Fix whitespace errors.
- Remove useless CVS id and log.
parent 46aac058
/* $Id: gpio.c,v 1.17 2005/06/19 17:06:46 starvik Exp $ /*
*
* Etrax general port I/O device * Etrax general port I/O device
* *
* Copyright (c) 1999, 2000, 2001, 2002 Axis Communications AB * Copyright (c) 1999-2007 Axis Communications AB
* *
* Authors: Bjorn Wesen (initial version) * Authors: Bjorn Wesen (initial version)
* Ola Knutsson (LED handling) * Ola Knutsson (LED handling)
* Johan Adolfsson (read/set directions, write, port G) * Johan Adolfsson (read/set directions, write, port G)
*
* $Log: gpio.c,v $
* Revision 1.17 2005/06/19 17:06:46 starvik
* Merge of Linux 2.6.12.
*
* Revision 1.16 2005/03/07 13:02:29 starvik
* Protect driver global states with spinlock
*
* Revision 1.15 2005/01/05 06:08:55 starvik
* No need to do local_irq_disable after local_irq_save.
*
* Revision 1.14 2004/12/13 12:21:52 starvik
* Added I/O and DMA allocators from Linux 2.4
*
* Revision 1.12 2004/08/24 07:19:59 starvik
* Whitespace cleanup
*
* Revision 1.11 2004/05/14 07:58:03 starvik
* Merge of changes from 2.4
*
* Revision 1.9 2003/09/11 07:29:48 starvik
* Merge of Linux 2.6.0-test5
*
* Revision 1.8 2003/07/04 08:27:37 starvik
* Merge of Linux 2.5.74
*
* Revision 1.7 2003/01/10 07:44:07 starvik
* init_ioremap is now called by kernel before drivers are initialized
*
* Revision 1.6 2002/12/11 13:13:57 starvik
* Added arch/ to v10 specific includes
* Added fix from Linux 2.4 in serial.c (flush_to_flip_buffer)
*
* Revision 1.5 2002/11/20 11:56:11 starvik
* Merge of Linux 2.5.48
*
* Revision 1.4 2002/11/18 10:10:05 starvik
* Linux 2.5 port of latest gpio.c from Linux 2.4
*
* Revision 1.20 2002/10/16 21:16:24 johana
* Added support for PA high level interrupt.
* That gives 2ms response time with iodtest for high levels and 2-12 ms
* response time on low levels if the check is not made in
* process.c:cpu_idle() as well.
*
* Revision 1.19 2002/10/14 18:27:33 johana
* Implemented alarm handling so select() now works.
* Latency is around 6-9 ms with a etrax_gpio_wake_up_check() in
* cpu_idle().
* Otherwise I get 15-18 ms (same as doing the poll in userspace -
* but less overhead).
* TODO? Perhaps we should add the check in IMMEDIATE_BH (or whatever it
* is in 2.4) as well?
* TODO? Perhaps call request_irq()/free_irq() only when needed?
* Increased version to 2.5
*
* Revision 1.18 2002/10/11 15:02:00 johana
* Mask inverted 8 bit value in setget_input().
*
* Revision 1.17 2002/06/17 15:53:01 johana
* Added IO_READ_INBITS, IO_READ_OUTBITS, IO_SETGET_INPUT and IO_SETGET_OUTPUT
* that take a pointer as argument and thus can handle 32 bit ports (G)
* correctly.
* These should be used instead of IO_READBITS, IO_SETINPUT and IO_SETOUTPUT.
* (especially if Port G bit 31 is used)
*
* Revision 1.16 2002/06/17 09:59:51 johana
* Returning 32 bit values in the ioctl return value doesn't work if bit
* 31 is set (could happen for port G), so mask it of with 0x7FFFFFFF.
* A new set of ioctl's will be added.
*
* Revision 1.15 2002/05/06 13:19:13 johana
* IO_SETINPUT returns mask with bit set = inputs for PA and PB as well.
*
* Revision 1.14 2002/04/12 12:01:53 johana
* Use global r_port_g_data_shadow.
* Moved gpio_init_port_g() closer to gpio_init() and marked it __init.
*
* Revision 1.13 2002/04/10 12:03:55 johana
* Added support for port G /dev/gpiog (minor 3).
* Changed indentation on switch cases.
* Fixed other spaces to tabs.
*
* Revision 1.12 2001/11/12 19:42:15 pkj
* * Corrected return values from gpio_leds_ioctl().
* * Fixed compiler warnings.
*
* Revision 1.11 2001/10/30 14:39:12 johana
* Added D() around gpio_write printk.
*
* Revision 1.10 2001/10/25 10:24:42 johana
* Added IO_CFG_WRITE_MODE ioctl and write method that can do fast
* bittoggling in the kernel. (This speeds up programming an FPGA with 450kB
* from ~60 seconds to 4 seconds).
* Added save_flags/cli/restore_flags in ioctl.
*
* Revision 1.9 2001/05/04 14:16:07 matsfg
* Corrected spelling error
*
* Revision 1.8 2001/04/27 13:55:26 matsfg
* Moved initioremap.
* Turns off all LEDS on init.
* Added support for shutdown and powerbutton.
*
* Revision 1.7 2001/04/04 13:30:08 matsfg
* Added bitset and bitclear for leds. Calls init_ioremap to set up memmapping
*
* Revision 1.6 2001/03/26 16:03:06 bjornw
* Needs linux/config.h
*
* Revision 1.5 2001/03/26 14:22:03 bjornw
* Namechange of some config options
*
* Revision 1.4 2001/02/27 13:52:48 bjornw
* malloc.h -> slab.h
*
* Revision 1.3 2001/01/24 15:06:48 bjornw
* gpio_wq correct type
*
* Revision 1.2 2001/01/18 16:07:30 bjornw
* 2.4 port
*
* Revision 1.1 2001/01/18 15:55:16 bjornw
* Verbatim copy of etraxgpio.c from elinux 2.0 added
*
*
*/ */
...@@ -165,7 +38,7 @@ static int dp_cnt; ...@@ -165,7 +38,7 @@ static int dp_cnt;
#else #else
#define DP(x) #define DP(x)
#endif #endif
static char gpio_name[] = "etrax gpio"; static char gpio_name[] = "etrax gpio";
#if 0 #if 0
...@@ -211,12 +84,12 @@ static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */ ...@@ -211,12 +84,12 @@ static DEFINE_SPINLOCK(gpio_lock); /* Protect directions etc */
/* Port A and B use 8 bit access, but Port G is 32 bit */ /* Port A and B use 8 bit access, but Port G is 32 bit */
#define NUM_PORTS (GPIO_MINOR_B+1) #define NUM_PORTS (GPIO_MINOR_B+1)
static volatile unsigned char *ports[NUM_PORTS] = { static volatile unsigned char *ports[NUM_PORTS] = {
R_PORT_PA_DATA, R_PORT_PA_DATA,
R_PORT_PB_DATA, R_PORT_PB_DATA,
}; };
static volatile unsigned char *shads[NUM_PORTS] = { static volatile unsigned char *shads[NUM_PORTS] = {
&port_pa_data_shadow, &port_pa_data_shadow,
&port_pb_data_shadow &port_pb_data_shadow
}; };
...@@ -236,29 +109,29 @@ static volatile unsigned char *shads[NUM_PORTS] = { ...@@ -236,29 +109,29 @@ static volatile unsigned char *shads[NUM_PORTS] = {
#endif #endif
static unsigned char changeable_dir[NUM_PORTS] = { static unsigned char changeable_dir[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_DIR, CONFIG_ETRAX_PA_CHANGEABLE_DIR,
CONFIG_ETRAX_PB_CHANGEABLE_DIR CONFIG_ETRAX_PB_CHANGEABLE_DIR
}; };
static unsigned char changeable_bits[NUM_PORTS] = { static unsigned char changeable_bits[NUM_PORTS] = {
CONFIG_ETRAX_PA_CHANGEABLE_BITS, CONFIG_ETRAX_PA_CHANGEABLE_BITS,
CONFIG_ETRAX_PB_CHANGEABLE_BITS CONFIG_ETRAX_PB_CHANGEABLE_BITS
}; };
static volatile unsigned char *dir[NUM_PORTS] = { static volatile unsigned char *dir[NUM_PORTS] = {
R_PORT_PA_DIR, R_PORT_PA_DIR,
R_PORT_PB_DIR R_PORT_PB_DIR
}; };
static volatile unsigned char *dir_shadow[NUM_PORTS] = { static volatile unsigned char *dir_shadow[NUM_PORTS] = {
&port_pa_dir_shadow, &port_pa_dir_shadow,
&port_pb_dir_shadow &port_pb_dir_shadow
}; };
/* All bits in port g that can change dir. */ /* All bits in port g that can change dir. */
static const unsigned long int changeable_dir_g_mask = 0x01FFFF01; static const unsigned long int changeable_dir_g_mask = 0x01FFFF01;
/* Port G is 32 bit, handle it special, some bits are both inputs /* Port G is 32 bit, handle it special, some bits are both inputs
and outputs at the same time, only some of the bits can change direction and outputs at the same time, only some of the bits can change direction
and some of them in groups of 8 bit. */ and some of them in groups of 8 bit. */
static unsigned long changeable_dir_g; static unsigned long changeable_dir_g;
...@@ -269,18 +142,17 @@ static unsigned long dir_g_shadow; /* 1=output */ ...@@ -269,18 +142,17 @@ static unsigned long dir_g_shadow; /* 1=output */
#define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B) #define USE_PORTS(priv) ((priv)->minor <= GPIO_MINOR_B)
static unsigned int gpio_poll(struct file *file, poll_table *wait)
static unsigned int
gpio_poll(struct file *file,
poll_table *wait)
{ {
unsigned int mask = 0; unsigned int mask = 0;
struct gpio_private *priv = (struct gpio_private *)file->private_data; struct gpio_private *priv = (struct gpio_private *)file->private_data;
unsigned long data; unsigned long data;
spin_lock(&gpio_lock); unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
poll_wait(file, &priv->alarm_wq, wait); poll_wait(file, &priv->alarm_wq, wait);
if (priv->minor == GPIO_MINOR_A) { if (priv->minor == GPIO_MINOR_A) {
unsigned long flags;
unsigned long tmp; unsigned long tmp;
data = *R_PORT_PA_DATA; data = *R_PORT_PA_DATA;
/* PA has support for high level interrupt - /* PA has support for high level interrupt -
...@@ -288,27 +160,25 @@ gpio_poll(struct file *file, ...@@ -288,27 +160,25 @@ gpio_poll(struct file *file,
*/ */
tmp = ~data & priv->highalarm & 0xFF; tmp = ~data & priv->highalarm & 0xFF;
tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR); tmp = (tmp << R_IRQ_MASK1_SET__pa0__BITNR);
local_irq_save(flags);
gpio_pa_irq_enabled_mask |= tmp; gpio_pa_irq_enabled_mask |= tmp;
*R_IRQ_MASK1_SET = tmp; *R_IRQ_MASK1_SET = tmp;
local_irq_restore(flags);
} else if (priv->minor == GPIO_MINOR_B) } else if (priv->minor == GPIO_MINOR_B)
data = *R_PORT_PB_DATA; data = *R_PORT_PB_DATA;
else if (priv->minor == GPIO_MINOR_G) else if (priv->minor == GPIO_MINOR_G)
data = *R_PORT_G_DATA; data = *R_PORT_G_DATA;
else { else {
spin_unlock(&gpio_lock); mask = 0;
return 0; goto out;
} }
if ((data & priv->highalarm) || if ((data & priv->highalarm) ||
(~data & priv->lowalarm)) { (~data & priv->lowalarm)) {
mask = POLLIN|POLLRDNORM; mask = POLLIN|POLLRDNORM;
} }
spin_unlock(&gpio_lock); out:
spin_unlock_irqrestore(&gpio_lock, flags);
DP(printk("gpio_poll ready: mask 0x%08X\n", mask)); DP(printk("gpio_poll ready: mask 0x%08X\n", mask));
return mask; return mask;
...@@ -316,16 +186,19 @@ gpio_poll(struct file *file, ...@@ -316,16 +186,19 @@ gpio_poll(struct file *file,
int etrax_gpio_wake_up_check(void) int etrax_gpio_wake_up_check(void)
{ {
struct gpio_private *priv = alarmlist; struct gpio_private *priv;
unsigned long data = 0; unsigned long data = 0;
int ret = 0; int ret = 0;
spin_lock(&gpio_lock); unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
priv = alarmlist;
while (priv) { while (priv) {
if (USE_PORTS(priv)) { if (USE_PORTS(priv))
data = *priv->port; data = *priv->port;
} else if (priv->minor == GPIO_MINOR_G) { else if (priv->minor == GPIO_MINOR_G)
data = *R_PORT_G_DATA; data = *R_PORT_G_DATA;
}
if ((data & priv->highalarm) || if ((data & priv->highalarm) ||
(~data & priv->lowalarm)) { (~data & priv->lowalarm)) {
DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor)); DP(printk("etrax_gpio_wake_up_check %i\n",priv->minor));
...@@ -334,12 +207,12 @@ int etrax_gpio_wake_up_check(void) ...@@ -334,12 +207,12 @@ int etrax_gpio_wake_up_check(void)
} }
priv = priv->next; priv = priv->next;
} }
spin_unlock(&gpio_lock); spin_unlock_irqrestore(&gpio_lock, flags);
return ret; return ret;
} }
static irqreturn_t static irqreturn_t
gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) gpio_poll_timer_interrupt(int irq, void *dev_id)
{ {
if (gpio_some_alarms) { if (gpio_some_alarms) {
etrax_gpio_wake_up_check(); etrax_gpio_wake_up_check();
...@@ -349,10 +222,13 @@ gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -349,10 +222,13 @@ gpio_poll_timer_interrupt(int irq, void *dev_id, struct pt_regs *regs)
} }
static irqreturn_t static irqreturn_t
gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) gpio_pa_interrupt(int irq, void *dev_id)
{ {
unsigned long tmp; unsigned long tmp;
spin_lock(&gpio_lock); unsigned long flags;
spin_lock_irqsave(&gpio_lock, flags);
/* Find what PA interrupts are active */ /* Find what PA interrupts are active */
tmp = (*R_IRQ_READ1); tmp = (*R_IRQ_READ1);
...@@ -363,75 +239,70 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs) ...@@ -363,75 +239,70 @@ gpio_pa_interrupt(int irq, void *dev_id, struct pt_regs *regs)
*R_IRQ_MASK1_CLR = tmp; *R_IRQ_MASK1_CLR = tmp;
gpio_pa_irq_enabled_mask &= ~tmp; gpio_pa_irq_enabled_mask &= ~tmp;
spin_unlock(&gpio_lock); spin_unlock_irqrestore(&gpio_lock, flags);
if (gpio_some_alarms) { if (gpio_some_alarms)
return IRQ_RETVAL(etrax_gpio_wake_up_check()); return IRQ_RETVAL(etrax_gpio_wake_up_check());
}
return IRQ_NONE; return IRQ_NONE;
} }
static void gpio_write_bit(struct gpio_private *priv,
unsigned char data, int bit)
{
*priv->port = *priv->shadow &= ~(priv->clk_mask);
if (data & 1 << bit)
*priv->port = *priv->shadow |= priv->data_mask;
else
*priv->port = *priv->shadow &= ~(priv->data_mask);
/* For FPGA: min 5.0ns (DCC) before CCLK high */
*priv->port = *priv->shadow |= priv->clk_mask;
}
static void gpio_write_byte(struct gpio_private *priv, unsigned char data)
{
int i;
if (priv->write_msb)
for (i = 7; i >= 0; i--)
gpio_write_bit(priv, data, i);
else
for (i = 0; i <= 7; i++)
gpio_write_bit(priv, data, i);
}
static ssize_t gpio_write(struct file * file, const char * buf, size_t count, static ssize_t gpio_write(struct file * file, const char * buf, size_t count,
loff_t *off) loff_t *off)
{ {
struct gpio_private *priv = (struct gpio_private *)file->private_data; struct gpio_private *priv = (struct gpio_private *)file->private_data;
unsigned char data, clk_mask, data_mask, write_msb;
unsigned long flags; unsigned long flags;
ssize_t retval = count;
spin_lock(&gpio_lock); if (priv->minor != GPIO_MINOR_A && priv->minor != GPIO_MINOR_B)
return -EFAULT;
if (!access_ok(VERIFY_READ, buf, count))
return -EFAULT;
spin_lock_irqsave(&gpio_lock, flags);
ssize_t retval = count;
if (priv->minor !=GPIO_MINOR_A && priv->minor != GPIO_MINOR_B) {
retval = -EFAULT;
goto out;
}
if (!access_ok(VERIFY_READ, buf, count)) {
retval = -EFAULT;
goto out;
}
clk_mask = priv->clk_mask;
data_mask = priv->data_mask;
/* It must have been configured using the IO_CFG_WRITE_MODE */ /* It must have been configured using the IO_CFG_WRITE_MODE */
/* Perhaps a better error code? */ /* Perhaps a better error code? */
if (clk_mask == 0 || data_mask == 0) { if (priv->clk_mask == 0 || priv->data_mask == 0) {
retval = -EPERM; retval = -EPERM;
goto out; goto out;
} }
write_msb = priv->write_msb;
D(printk("gpio_write: %lu to data 0x%02X clk 0x%02X msb: %i\n",count, data_mask, clk_mask, write_msb)); D(printk(KERN_DEBUG "gpio_write: %02X to data 0x%02X "
while (count--) { "clk 0x%02X msb: %i\n",
int i; count, priv->data_mask, priv->clk_mask, priv->write_msb));
data = *buf++;
if (priv->write_msb) { while (count--)
for (i = 7; i >= 0;i--) { gpio_write_byte(priv, *buf++);
local_irq_save(flags);
*priv->port = *priv->shadow &= ~clk_mask;
if (data & 1<<i)
*priv->port = *priv->shadow |= data_mask;
else
*priv->port = *priv->shadow &= ~data_mask;
/* For FPGA: min 5.0ns (DCC) before CCLK high */
*priv->port = *priv->shadow |= clk_mask;
local_irq_restore(flags);
}
} else {
for (i = 0; i <= 7;i++) {
local_irq_save(flags);
*priv->port = *priv->shadow &= ~clk_mask;
if (data & 1<<i)
*priv->port = *priv->shadow |= data_mask;
else
*priv->port = *priv->shadow &= ~data_mask;
/* For FPGA: min 5.0ns (DCC) before CCLK high */
*priv->port = *priv->shadow |= clk_mask;
local_irq_restore(flags);
}
}
}
out: out:
spin_unlock(&gpio_lock); spin_unlock_irqrestore(&gpio_lock, flags);
return retval; return retval;
} }
...@@ -442,22 +313,22 @@ gpio_open(struct inode *inode, struct file *filp) ...@@ -442,22 +313,22 @@ gpio_open(struct inode *inode, struct file *filp)
{ {
struct gpio_private *priv; struct gpio_private *priv;
int p = iminor(inode); int p = iminor(inode);
unsigned long flags;
if (p > GPIO_MINOR_LAST) if (p > GPIO_MINOR_LAST)
return -EINVAL; return -EINVAL;
priv = kmalloc(sizeof(struct gpio_private), priv = kmalloc(sizeof(struct gpio_private), GFP_KERNEL);
GFP_KERNEL);
if (!priv) if (!priv)
return -ENOMEM; return -ENOMEM;
memset(priv, 0, sizeof(*priv));
priv->minor = p; priv->minor = p;
/* initialize the io/alarm struct and link it into our alarmlist */ /* initialize the io/alarm struct */
priv->next = alarmlist;
alarmlist = priv;
if (USE_PORTS(priv)) { /* A and B */ if (USE_PORTS(priv)) { /* A and B */
priv->port = ports[p]; priv->port = ports[p];
priv->shadow = shads[p]; priv->shadow = shads[p];
...@@ -482,6 +353,12 @@ gpio_open(struct inode *inode, struct file *filp) ...@@ -482,6 +353,12 @@ gpio_open(struct inode *inode, struct file *filp)
filp->private_data = (void *)priv; filp->private_data = (void *)priv;
/* link it into our alarmlist */
spin_lock_irqsave(&gpio_lock, flags);
priv->next = alarmlist;
alarmlist = priv;
spin_unlock_irqrestore(&gpio_lock, flags);
return 0; return 0;
} }
...@@ -490,11 +367,12 @@ gpio_release(struct inode *inode, struct file *filp) ...@@ -490,11 +367,12 @@ gpio_release(struct inode *inode, struct file *filp)
{ {
struct gpio_private *p; struct gpio_private *p;
struct gpio_private *todel; struct gpio_private *todel;
unsigned long flags;
spin_lock(&gpio_lock); spin_lock_irqsave(&gpio_lock, flags);
p = alarmlist; p = alarmlist;
todel = (struct gpio_private *)filp->private_data; todel = (struct gpio_private *)filp->private_data;
/* unlink from alarmlist and free the private structure */ /* unlink from alarmlist and free the private structure */
...@@ -512,123 +390,114 @@ gpio_release(struct inode *inode, struct file *filp) ...@@ -512,123 +390,114 @@ gpio_release(struct inode *inode, struct file *filp)
while (p) { while (p) {
if (p->highalarm | p->lowalarm) { if (p->highalarm | p->lowalarm) {
gpio_some_alarms = 1; gpio_some_alarms = 1;
spin_unlock(&gpio_lock); goto out;
return 0;
} }
p = p->next; p = p->next;
} }
gpio_some_alarms = 0; gpio_some_alarms = 0;
spin_unlock(&gpio_lock); out:
spin_unlock_irqrestore(&gpio_lock, flags);
return 0; return 0;
} }
/* Main device API. ioctl's to read/set/clear bits, as well as to /* Main device API. ioctl's to read/set/clear bits, as well as to
* set alarms to wait for using a subsequent select(). * set alarms to wait for using a subsequent select().
*/ */
unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg) unsigned long inline setget_input(struct gpio_private *priv, unsigned long arg)
{ {
/* Set direction 0=unchanged 1=input, /* Set direction 0=unchanged 1=input,
* return mask with 1=input * return mask with 1=input */
*/
unsigned long flags;
if (USE_PORTS(priv)) { if (USE_PORTS(priv)) {
local_irq_save(flags); *priv->dir = *priv->dir_shadow &=
*priv->dir = *priv->dir_shadow &=
~((unsigned char)arg & priv->changeable_dir); ~((unsigned char)arg & priv->changeable_dir);
local_irq_restore(flags);
return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */ return ~(*priv->dir_shadow) & 0xFF; /* Only 8 bits */
} else if (priv->minor == GPIO_MINOR_G) { }
/* We must fiddle with R_GEN_CONFIG to change dir */
local_irq_save(flags); if (priv->minor != GPIO_MINOR_G)
if (((arg & dir_g_in_bits) != arg) && return 0;
(arg & changeable_dir_g)) {
arg &= changeable_dir_g; /* We must fiddle with R_GEN_CONFIG to change dir */
/* Clear bits in genconfig to set to input */ if (((arg & dir_g_in_bits) != arg) &&
if (arg & (1<<0)) { (arg & changeable_dir_g)) {
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g0dir); arg &= changeable_dir_g;
dir_g_in_bits |= (1<<0); /* Clear bits in genconfig to set to input */
dir_g_out_bits &= ~(1<<0); if (arg & (1<<0)) {
} genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g0dir);
if ((arg & 0x0000FF00) == 0x0000FF00) { dir_g_in_bits |= (1<<0);
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g8_15dir); dir_g_out_bits &= ~(1<<0);
dir_g_in_bits |= 0x0000FF00; }
dir_g_out_bits &= ~0x0000FF00; if ((arg & 0x0000FF00) == 0x0000FF00) {
} genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g8_15dir);
if ((arg & 0x00FF0000) == 0x00FF0000) { dir_g_in_bits |= 0x0000FF00;
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g16_23dir); dir_g_out_bits &= ~0x0000FF00;
dir_g_in_bits |= 0x00FF0000; }
dir_g_out_bits &= ~0x00FF0000; if ((arg & 0x00FF0000) == 0x00FF0000) {
} genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g16_23dir);
if (arg & (1<<24)) { dir_g_in_bits |= 0x00FF0000;
genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG,g24dir); dir_g_out_bits &= ~0x00FF0000;
dir_g_in_bits |= (1<<24); }
dir_g_out_bits &= ~(1<<24); if (arg & (1<<24)) {
} genconfig_shadow &= ~IO_MASK(R_GEN_CONFIG, g24dir);
D(printk(KERN_INFO "gpio: SETINPUT on port G set " dir_g_in_bits |= (1<<24);
"genconfig to 0x%08lX " dir_g_out_bits &= ~(1<<24);
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
} }
local_irq_restore(flags); D(printk(KERN_DEBUG "gpio: SETINPUT on port G set "
return dir_g_in_bits; "genconfig to 0x%08lX "
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
} }
return 0; return dir_g_in_bits;
} /* setget_input */ } /* setget_input */
unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg) unsigned long inline setget_output(struct gpio_private *priv, unsigned long arg)
{ {
unsigned long flags;
if (USE_PORTS(priv)) { if (USE_PORTS(priv)) {
local_irq_save(flags); *priv->dir = *priv->dir_shadow |=
*priv->dir = *priv->dir_shadow |= ((unsigned char)arg & priv->changeable_dir);
((unsigned char)arg & priv->changeable_dir);
local_irq_restore(flags);
return *priv->dir_shadow; return *priv->dir_shadow;
} else if (priv->minor == GPIO_MINOR_G) { }
/* We must fiddle with R_GEN_CONFIG to change dir */ if (priv->minor != GPIO_MINOR_G)
local_irq_save(flags); return 0;
if (((arg & dir_g_out_bits) != arg) &&
(arg & changeable_dir_g)) { /* We must fiddle with R_GEN_CONFIG to change dir */
/* Set bits in genconfig to set to output */ if (((arg & dir_g_out_bits) != arg) &&
if (arg & (1<<0)) { (arg & changeable_dir_g)) {
genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g0dir); /* Set bits in genconfig to set to output */
dir_g_out_bits |= (1<<0); if (arg & (1<<0)) {
dir_g_in_bits &= ~(1<<0); genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g0dir);
} dir_g_out_bits |= (1<<0);
if ((arg & 0x0000FF00) == 0x0000FF00) { dir_g_in_bits &= ~(1<<0);
genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g8_15dir); }
dir_g_out_bits |= 0x0000FF00; if ((arg & 0x0000FF00) == 0x0000FF00) {
dir_g_in_bits &= ~0x0000FF00; genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g8_15dir);
} dir_g_out_bits |= 0x0000FF00;
if ((arg & 0x00FF0000) == 0x00FF0000) { dir_g_in_bits &= ~0x0000FF00;
genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g16_23dir); }
dir_g_out_bits |= 0x00FF0000; if ((arg & 0x00FF0000) == 0x00FF0000) {
dir_g_in_bits &= ~0x00FF0000; genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g16_23dir);
} dir_g_out_bits |= 0x00FF0000;
if (arg & (1<<24)) { dir_g_in_bits &= ~0x00FF0000;
genconfig_shadow |= IO_MASK(R_GEN_CONFIG,g24dir);
dir_g_out_bits |= (1<<24);
dir_g_in_bits &= ~(1<<24);
}
D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
"genconfig to 0x%08lX "
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
} }
local_irq_restore(flags); if (arg & (1<<24)) {
return dir_g_out_bits & 0x7FFFFFFF; genconfig_shadow |= IO_MASK(R_GEN_CONFIG, g24dir);
dir_g_out_bits |= (1<<24);
dir_g_in_bits &= ~(1<<24);
}
D(printk(KERN_INFO "gpio: SETOUTPUT on port G set "
"genconfig to 0x%08lX "
"in_bits: 0x%08lX "
"out_bits: 0x%08lX\n",
(unsigned long)genconfig_shadow,
dir_g_in_bits, dir_g_out_bits));
*R_GEN_CONFIG = genconfig_shadow;
/* Must be a >120 ns delay before writing this again */
} }
return 0; return dir_g_out_bits & 0x7FFFFFFF;
} /* setget_output */ } /* setget_output */
static int static int
...@@ -643,11 +512,10 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -643,11 +512,10 @@ gpio_ioctl(struct inode *inode, struct file *file,
int ret = 0; int ret = 0;
struct gpio_private *priv = (struct gpio_private *)file->private_data; struct gpio_private *priv = (struct gpio_private *)file->private_data;
if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE) { if (_IOC_TYPE(cmd) != ETRAXGPIO_IOCTYPE)
return -EINVAL; return -EINVAL;
}
spin_lock(&gpio_lock); spin_lock_irqsave(&gpio_lock, flags);
switch (_IOC_NR(cmd)) { switch (_IOC_NR(cmd)) {
case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */ case IO_READBITS: /* Use IO_READ_INBITS and IO_READ_OUTBITS instead */
...@@ -659,7 +527,6 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -659,7 +527,6 @@ gpio_ioctl(struct inode *inode, struct file *file,
} }
break; break;
case IO_SETBITS: case IO_SETBITS:
local_irq_save(flags);
// set changeable bits with a 1 in arg // set changeable bits with a 1 in arg
if (USE_PORTS(priv)) { if (USE_PORTS(priv)) {
*priv->port = *priv->shadow |= *priv->port = *priv->shadow |=
...@@ -667,10 +534,8 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -667,10 +534,8 @@ gpio_ioctl(struct inode *inode, struct file *file,
} else if (priv->minor == GPIO_MINOR_G) { } else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits); *R_PORT_G_DATA = port_g_data_shadow |= (arg & dir_g_out_bits);
} }
local_irq_restore(flags);
break; break;
case IO_CLRBITS: case IO_CLRBITS:
local_irq_save(flags);
// clear changeable bits with a 1 in arg // clear changeable bits with a 1 in arg
if (USE_PORTS(priv)) { if (USE_PORTS(priv)) {
*priv->port = *priv->shadow &= *priv->port = *priv->shadow &=
...@@ -678,7 +543,6 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -678,7 +543,6 @@ gpio_ioctl(struct inode *inode, struct file *file,
} else if (priv->minor == GPIO_MINOR_G) { } else if (priv->minor == GPIO_MINOR_G) {
*R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits); *R_PORT_G_DATA = port_g_data_shadow &= ~((unsigned long)arg & dir_g_out_bits);
} }
local_irq_restore(flags);
break; break;
case IO_HIGHALARM: case IO_HIGHALARM:
// set alarm when bits with 1 in arg go high // set alarm when bits with 1 in arg go high
...@@ -698,6 +562,8 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -698,6 +562,8 @@ gpio_ioctl(struct inode *inode, struct file *file,
/* Must update gpio_some_alarms */ /* Must update gpio_some_alarms */
struct gpio_private *p = alarmlist; struct gpio_private *p = alarmlist;
int some_alarms; int some_alarms;
spin_lock_irq(&gpio_lock);
p = alarmlist;
some_alarms = 0; some_alarms = 0;
while (p) { while (p) {
if (p->highalarm | p->lowalarm) { if (p->highalarm | p->lowalarm) {
...@@ -707,6 +573,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -707,6 +573,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
p = p->next; p = p->next;
} }
gpio_some_alarms = some_alarms; gpio_some_alarms = some_alarms;
spin_unlock_irq(&gpio_lock);
} }
break; break;
case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */ case IO_READDIR: /* Use IO_SETGET_INPUT/OUTPUT instead! */
...@@ -796,8 +663,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -796,8 +663,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
/* bits set in *arg is set to output, /* bits set in *arg is set to output,
* *arg updated with current output pins. * *arg updated with current output pins.
*/ */
if (copy_from_user(&val, (unsigned long*)arg, sizeof(val))) if (copy_from_user(&val, (unsigned long *)arg, sizeof(val))) {
{
ret = -EFAULT; ret = -EFAULT;
break; break;
} }
...@@ -812,7 +678,7 @@ gpio_ioctl(struct inode *inode, struct file *file, ...@@ -812,7 +678,7 @@ gpio_ioctl(struct inode *inode, struct file *file,
ret = -EINVAL; ret = -EINVAL;
} /* switch */ } /* switch */
spin_unlock(&gpio_lock); spin_unlock_irqrestore(&gpio_lock, flags);
return ret; return ret;
} }
...@@ -824,18 +690,18 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg) ...@@ -824,18 +690,18 @@ gpio_leds_ioctl(unsigned int cmd, unsigned long arg)
switch (_IOC_NR(cmd)) { switch (_IOC_NR(cmd)) {
case IO_LEDACTIVE_SET: case IO_LEDACTIVE_SET:
green = ((unsigned char) arg) & 1; green = ((unsigned char)arg) & 1;
red = (((unsigned char) arg) >> 1) & 1; red = (((unsigned char)arg) >> 1) & 1;
LED_ACTIVE_SET_G(green); CRIS_LED_ACTIVE_SET_G(green);
LED_ACTIVE_SET_R(red); CRIS_LED_ACTIVE_SET_R(red);
break; break;
case IO_LED_SETBIT: case IO_LED_SETBIT:
LED_BIT_SET(arg); CRIS_LED_BIT_SET(arg);
break; break;
case IO_LED_CLRBIT: case IO_LED_CLRBIT:
LED_BIT_CLR(arg); CRIS_LED_BIT_CLR(arg);
break; break;
default: default:
...@@ -854,16 +720,18 @@ const struct file_operations gpio_fops = { ...@@ -854,16 +720,18 @@ const struct file_operations gpio_fops = {
.release = gpio_release, .release = gpio_release,
}; };
void ioif_watcher(const unsigned int gpio_in_available, void ioif_watcher(const unsigned int gpio_in_available,
const unsigned int gpio_out_available, const unsigned int gpio_out_available,
const unsigned char pa_available, const unsigned char pa_available,
const unsigned char pb_available) const unsigned char pb_available)
{ {
unsigned long int flags; unsigned long int flags;
D(printk("gpio.c: ioif_watcher called\n"));
D(printk("gpio.c: G in: 0x%08x G out: 0x%08x PA: 0x%02x PB: 0x%02x\n", D(printk(KERN_DEBUG "gpio.c: ioif_watcher called\n"));
gpio_in_available, gpio_out_available, pa_available, pb_available)); D(printk(KERN_DEBUG "gpio.c: G in: 0x%08x G out: 0x%08x "
"PA: 0x%02x PB: 0x%02x\n",
gpio_in_available, gpio_out_available,
pa_available, pb_available));
spin_lock_irqsave(&gpio_lock, flags); spin_lock_irqsave(&gpio_lock, flags);
...@@ -872,7 +740,7 @@ void ioif_watcher(const unsigned int gpio_in_available, ...@@ -872,7 +740,7 @@ void ioif_watcher(const unsigned int gpio_in_available,
/* Initialise the dir_g_shadow etc. depending on genconfig */ /* Initialise the dir_g_shadow etc. depending on genconfig */
/* 0=input 1=output */ /* 0=input 1=output */
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out)) if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g0dir, out))
dir_g_shadow |= (1 << 0); dir_g_shadow |= (1 << 0);
if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out)) if (genconfig_shadow & IO_STATE(R_GEN_CONFIG, g8_15dir, out))
dir_g_shadow |= 0x0000FF00; dir_g_shadow |= 0x0000FF00;
...@@ -884,7 +752,8 @@ void ioif_watcher(const unsigned int gpio_in_available, ...@@ -884,7 +752,8 @@ void ioif_watcher(const unsigned int gpio_in_available,
changeable_dir_g = changeable_dir_g_mask; changeable_dir_g = changeable_dir_g_mask;
changeable_dir_g &= dir_g_out_bits; changeable_dir_g &= dir_g_out_bits;
changeable_dir_g &= dir_g_in_bits; changeable_dir_g &= dir_g_in_bits;
/* Correct the bits that can change direction */
/* Correct the bits that can change direction */
dir_g_out_bits &= ~changeable_dir_g; dir_g_out_bits &= ~changeable_dir_g;
dir_g_out_bits |= dir_g_shadow; dir_g_out_bits |= dir_g_shadow;
dir_g_in_bits &= ~changeable_dir_g; dir_g_in_bits &= ~changeable_dir_g;
...@@ -892,7 +761,8 @@ void ioif_watcher(const unsigned int gpio_in_available, ...@@ -892,7 +761,8 @@ void ioif_watcher(const unsigned int gpio_in_available,
spin_unlock_irqrestore(&gpio_lock, flags); spin_unlock_irqrestore(&gpio_lock, flags);
printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX val: %08lX\n", printk(KERN_INFO "GPIO port G: in_bits: 0x%08lX out_bits: 0x%08lX "
"val: %08lX\n",
dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA); dir_g_in_bits, dir_g_out_bits, (unsigned long)*R_PORT_G_DATA);
printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n", printk(KERN_INFO "GPIO port G: dir: %08lX changeable: %08lX\n",
dir_g_shadow, changeable_dir_g); dir_g_shadow, changeable_dir_g);
...@@ -907,9 +777,6 @@ gpio_init(void) ...@@ -907,9 +777,6 @@ gpio_init(void)
#if defined (CONFIG_ETRAX_CSP0_LEDS) #if defined (CONFIG_ETRAX_CSP0_LEDS)
int i; int i;
#endif #endif
printk("gpio init\n");
/* do the formalities */
res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops); res = register_chrdev(GPIO_MAJOR, gpio_name, &gpio_fops);
if (res < 0) { if (res < 0) {
...@@ -919,39 +786,41 @@ gpio_init(void) ...@@ -919,39 +786,41 @@ gpio_init(void)
/* Clear all leds */ /* Clear all leds */
#if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS) #if defined (CONFIG_ETRAX_CSP0_LEDS) || defined (CONFIG_ETRAX_PA_LEDS) || defined (CONFIG_ETRAX_PB_LEDS)
LED_NETWORK_SET(0); CRIS_LED_NETWORK_SET(0);
LED_ACTIVE_SET(0); CRIS_LED_ACTIVE_SET(0);
LED_DISK_READ(0); CRIS_LED_DISK_READ(0);
LED_DISK_WRITE(0); CRIS_LED_DISK_WRITE(0);
#if defined (CONFIG_ETRAX_CSP0_LEDS) #if defined (CONFIG_ETRAX_CSP0_LEDS)
for (i = 0; i < 32; i++) { for (i = 0; i < 32; i++)
LED_BIT_SET(i); CRIS_LED_BIT_SET(i);
}
#endif #endif
#endif #endif
/* The I/O interface allocation watcher will be called when /* The I/O interface allocation watcher will be called when
* registering it. */ * registering it. */
if (cris_io_interface_register_watcher(ioif_watcher)){ if (cris_io_interface_register_watcher(ioif_watcher)){
printk(KERN_WARNING "gpio_init: Failed to install IO if allocator watcher\n"); printk(KERN_WARNING "gpio_init: Failed to install IO "
"if allocator watcher\n");
} }
printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001, 2002, 2003, 2004 Axis Communications AB\n"); printk(KERN_INFO "ETRAX 100LX GPIO driver v2.5, (c) 2001-2008 "
"Axis Communications AB\n");
/* We call etrax_gpio_wake_up_check() from timer interrupt and /* We call etrax_gpio_wake_up_check() from timer interrupt and
* from cpu_idle() in kernel/process.c * from cpu_idle() in kernel/process.c
* The check in cpu_idle() reduces latency from ~15 ms to ~6 ms * The check in cpu_idle() reduces latency from ~15 ms to ~6 ms
* in some tests. * in some tests.
*/ */
if (request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt, res = request_irq(TIMER0_IRQ_NBR, gpio_poll_timer_interrupt,
IRQF_SHARED | IRQF_DISABLED,"gpio poll", NULL)) { IRQF_SHARED | IRQF_DISABLED, "gpio poll", gpio_name);
if (res) {
printk(KERN_CRIT "err: timer0 irq for gpio\n"); printk(KERN_CRIT "err: timer0 irq for gpio\n");
return res;
} }
if (request_irq(PA_IRQ_NBR, gpio_pa_interrupt, res = request_irq(PA_IRQ_NBR, gpio_pa_interrupt,
IRQF_SHARED | IRQF_DISABLED,"gpio PA", NULL)) { IRQF_SHARED | IRQF_DISABLED, "gpio PA", gpio_name);
if (res)
printk(KERN_CRIT "err: PA irq for gpio\n"); printk(KERN_CRIT "err: PA irq for gpio\n");
}
return res; return res;
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment