Skip to content

Commit

Permalink
xtensa: support s6000 gpio irqs and alternate function selection
Browse files Browse the repository at this point in the history
Implement an irq chip to handle interrupts via gpio.  The GPIO chip
initialization function now takes a bitmask denoting pins that should
be configured for their alternate function.

changes compared to v1:
- fixed bug on edge interrupt configuration
- accommodated to function name change
- moved definition of VARIANT_NR_IRQS to this patch
- renamed __XTENSA_S6000_IRQ_H to _XTENSA_S6000_IRQ_H as requested

Signed-off-by: Daniel Glöckner <dg@emlix.com>
Signed-off-by: Johannes Weiner <jw@emlix.com>
Signed-off-by: Chris Zankel <chris@zankel.net>
  • Loading branch information
Daniel Glöckner authored and Chris Zankel committed Jun 22, 2009
1 parent f24e552 commit 0b3eb21
Show file tree
Hide file tree
Showing 5 changed files with 171 additions and 10 deletions.
8 changes: 4 additions & 4 deletions arch/xtensa/include/asm/gpio.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,14 @@ static inline int gpio_cansleep(unsigned int gpio)
return __gpio_cansleep(gpio);
}

/*
* Not implemented, yet.
*/
static inline int gpio_to_irq(unsigned int gpio)
{
return -ENOSYS;
return __gpio_to_irq(gpio);
}

/*
* Not implemented, yet.
*/
static inline int irq_to_gpio(unsigned int irq)
{
return -EINVAL;
Expand Down
2 changes: 1 addition & 1 deletion arch/xtensa/platforms/s6105/setup.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ void __init platform_setup(char **cmdline)

void __init platform_init(bp_tag_t *first)
{
s6_gpio_init();
s6_gpio_init(0);
gpio_request(GPIO_LED1_NGREEN, "led1_green");
gpio_request(GPIO_LED1_RED, "led1_red");
gpio_direction_output(GPIO_LED1_NGREEN, 1);
Expand Down
163 changes: 162 additions & 1 deletion arch/xtensa/variants/s6000/gpio.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,20 @@
* Copyright (c) 2009 emlix GmbH
* Authors: Oskar Schirmer <os@emlix.com>
* Johannes Weiner <jw@emlix.com>
* Daniel Gloeckner <dg@emlix.com>
*/
#include <linux/bitops.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio.h>

#include <variant/hardware.h>

#define IRQ_BASE XTENSA_NR_IRQS

#define S6_GPIO_DATA 0x000
#define S6_GPIO_IS 0x404
#define S6_GPIO_IBE 0x408
Expand Down Expand Up @@ -52,19 +57,175 @@ static void set(struct gpio_chip *chip, unsigned int off, int val)
writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
}

static int to_irq(struct gpio_chip *chip, unsigned offset)
{
if (offset < 8)
return offset + IRQ_BASE;
return -EINVAL;
}

static struct gpio_chip gpiochip = {
.owner = THIS_MODULE,
.direction_input = direction_input,
.get = get,
.direction_output = direction_output,
.set = set,
.to_irq = to_irq,
.base = 0,
.ngpio = 24,
.can_sleep = 0, /* no blocking io needed */
.exported = 0, /* no exporting to userspace */
};

int s6_gpio_init(void)
int s6_gpio_init(u32 afsel)
{
writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);
writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);
writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);
return gpiochip_add(&gpiochip);
}

static void ack(unsigned int irq)
{
writeb(1 << (irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
}

static void mask(unsigned int irq)
{
u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
r &= ~(1 << (irq - IRQ_BASE));
writeb(r, S6_REG_GPIO + S6_GPIO_IE);
}

static void unmask(unsigned int irq)
{
u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
m |= 1 << (irq - IRQ_BASE);
writeb(m, S6_REG_GPIO + S6_GPIO_IE);
}

static int set_type(unsigned int irq, unsigned int type)
{
const u8 m = 1 << (irq - IRQ_BASE);
irq_flow_handler_t handler;
struct irq_desc *desc;
u8 reg;

if (type == IRQ_TYPE_PROBE) {
if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
|| (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
|| readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
+ S6_GPIO_MASK(irq - IRQ_BASE)))
return 0;
type = IRQ_TYPE_EDGE_BOTH;
}

reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
reg |= m;
handler = handle_level_irq;
} else {
reg &= ~m;
handler = handle_edge_irq;
}
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
desc = irq_to_desc(irq);
desc->handle_irq = handler;

reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
reg |= m;
else
reg &= ~m;
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);

reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
reg |= m;
else
reg &= ~m;
writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
return 0;
}

static struct irq_chip gpioirqs = {
.name = "GPIO",
.ack = ack,
.mask = mask,
.unmask = unmask,
.set_type = set_type,
};

static u8 demux_masks[4];

static void demux_irqs(unsigned int irq, struct irq_desc *desc)
{
u8 *mask = get_irq_desc_data(desc);
u8 pending;
int cirq;

desc->chip->mask(irq);
desc->chip->ack(irq);
pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
cirq = IRQ_BASE - 1;
while (pending) {
int n = ffs(pending);
cirq += n;
pending >>= n;
generic_handle_irq(cirq);
}
desc->chip->unmask(irq);
}

extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];

void __init variant_init_irq(void)
{
int irq, n;
writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
const signed char *mapping = platform_irq_mappings[irq];
int alone = 1;
u8 mask;
if (!mapping)
continue;
for(mask = 0; *mapping != -1; mapping++)
switch (*mapping) {
case S6_INTC_GPIO(0):
mask |= 1 << 0;
break;
case S6_INTC_GPIO(1):
mask |= 1 << 1;
break;
case S6_INTC_GPIO(2):
mask |= 1 << 2;
break;
case S6_INTC_GPIO(3):
mask |= 0x1f << 3;
break;
default:
alone = 0;
}
if (mask) {
int cirq, i;
if (!alone) {
printk(KERN_ERR "chained irq chips can't share"
" parent irq %i\n", irq);
continue;
}
demux_masks[n] = mask;
cirq = IRQ_BASE - 1;
do {
i = ffs(mask);
cirq += i;
mask >>= i;
set_irq_chip(cirq, &gpioirqs);
set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
} while (mask);
set_irq_data(irq, demux_masks + n);
set_irq_chained_handler(irq, demux_irqs);
if (++n == ARRAY_SIZE(demux_masks))
break;
}
}
}
2 changes: 1 addition & 1 deletion arch/xtensa/variants/s6000/include/variant/gpio.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#ifndef _XTENSA_VARIANT_S6000_GPIO_H
#define _XTENSA_VARIANT_S6000_GPIO_H

extern int s6_gpio_init(void);
extern int s6_gpio_init(u32 afsel);

#endif /* _XTENSA_VARIANT_S6000_GPIO_H */
6 changes: 3 additions & 3 deletions arch/xtensa/variants/s6000/include/variant/irq.h
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
#ifndef __XTENSA_S6000_IRQ_H
#define __XTENSA_S6000_IRQ_H
#ifndef _XTENSA_S6000_IRQ_H
#define _XTENSA_S6000_IRQ_H

#define NO_IRQ (-1)
#define VARIANT_NR_IRQS 8 /* GPIO interrupts */

extern void variant_irq_enable(unsigned int irq);
extern void variant_irq_disable(unsigned int irq);

#endif /* __XTENSA_S6000_IRQ_H */

0 comments on commit 0b3eb21

Please sign in to comment.