Commit 0b3eb21b2f2222c4c1e3e21fc3cd427404d3991a

Authored by Daniel Glöckner
Committed by Chris Zankel
1 parent f24e552c2d

xtensa: support s6000 gpio irqs and alternate function selection

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>

Showing 5 changed files with 171 additions and 10 deletions Side-by-side Diff

arch/xtensa/include/asm/gpio.h
... ... @@ -38,14 +38,14 @@
38 38 return __gpio_cansleep(gpio);
39 39 }
40 40  
41   -/*
42   - * Not implemented, yet.
43   - */
44 41 static inline int gpio_to_irq(unsigned int gpio)
45 42 {
46   - return -ENOSYS;
  43 + return __gpio_to_irq(gpio);
47 44 }
48 45  
  46 +/*
  47 + * Not implemented, yet.
  48 + */
49 49 static inline int irq_to_gpio(unsigned int irq)
50 50 {
51 51 return -EINVAL;
arch/xtensa/platforms/s6105/setup.c
... ... @@ -49,7 +49,7 @@
49 49  
50 50 void __init platform_init(bp_tag_t *first)
51 51 {
52   - s6_gpio_init();
  52 + s6_gpio_init(0);
53 53 gpio_request(GPIO_LED1_NGREEN, "led1_green");
54 54 gpio_request(GPIO_LED1_RED, "led1_red");
55 55 gpio_direction_output(GPIO_LED1_NGREEN, 1);
arch/xtensa/variants/s6000/gpio.c
... ... @@ -4,15 +4,20 @@
4 4 * Copyright (c) 2009 emlix GmbH
5 5 * Authors: Oskar Schirmer <os@emlix.com>
6 6 * Johannes Weiner <jw@emlix.com>
  7 + * Daniel Gloeckner <dg@emlix.com>
7 8 */
  9 +#include <linux/bitops.h>
8 10 #include <linux/kernel.h>
9 11 #include <linux/module.h>
10 12 #include <linux/init.h>
11 13 #include <linux/io.h>
  14 +#include <linux/irq.h>
12 15 #include <linux/gpio.h>
13 16  
14 17 #include <variant/hardware.h>
15 18  
  19 +#define IRQ_BASE XTENSA_NR_IRQS
  20 +
16 21 #define S6_GPIO_DATA 0x000
17 22 #define S6_GPIO_IS 0x404
18 23 #define S6_GPIO_IBE 0x408
19 24  
20 25  
21 26  
22 27  
... ... @@ -52,20 +57,176 @@
52 57 writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
53 58 }
54 59  
  60 +static int to_irq(struct gpio_chip *chip, unsigned offset)
  61 +{
  62 + if (offset < 8)
  63 + return offset + IRQ_BASE;
  64 + return -EINVAL;
  65 +}
  66 +
55 67 static struct gpio_chip gpiochip = {
56 68 .owner = THIS_MODULE,
57 69 .direction_input = direction_input,
58 70 .get = get,
59 71 .direction_output = direction_output,
60 72 .set = set,
  73 + .to_irq = to_irq,
61 74 .base = 0,
62 75 .ngpio = 24,
63 76 .can_sleep = 0, /* no blocking io needed */
64 77 .exported = 0, /* no exporting to userspace */
65 78 };
66 79  
67   -int s6_gpio_init(void)
  80 +int s6_gpio_init(u32 afsel)
68 81 {
  82 + writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);
  83 + writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);
  84 + writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);
69 85 return gpiochip_add(&gpiochip);
  86 +}
  87 +
  88 +static void ack(unsigned int irq)
  89 +{
  90 + writeb(1 << (irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
  91 +}
  92 +
  93 +static void mask(unsigned int irq)
  94 +{
  95 + u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
  96 + r &= ~(1 << (irq - IRQ_BASE));
  97 + writeb(r, S6_REG_GPIO + S6_GPIO_IE);
  98 +}
  99 +
  100 +static void unmask(unsigned int irq)
  101 +{
  102 + u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
  103 + m |= 1 << (irq - IRQ_BASE);
  104 + writeb(m, S6_REG_GPIO + S6_GPIO_IE);
  105 +}
  106 +
  107 +static int set_type(unsigned int irq, unsigned int type)
  108 +{
  109 + const u8 m = 1 << (irq - IRQ_BASE);
  110 + irq_flow_handler_t handler;
  111 + struct irq_desc *desc;
  112 + u8 reg;
  113 +
  114 + if (type == IRQ_TYPE_PROBE) {
  115 + if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)
  116 + || (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)
  117 + || readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR
  118 + + S6_GPIO_MASK(irq - IRQ_BASE)))
  119 + return 0;
  120 + type = IRQ_TYPE_EDGE_BOTH;
  121 + }
  122 +
  123 + reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
  124 + if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {
  125 + reg |= m;
  126 + handler = handle_level_irq;
  127 + } else {
  128 + reg &= ~m;
  129 + handler = handle_edge_irq;
  130 + }
  131 + writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);
  132 + desc = irq_to_desc(irq);
  133 + desc->handle_irq = handler;
  134 +
  135 + reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
  136 + if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))
  137 + reg |= m;
  138 + else
  139 + reg &= ~m;
  140 + writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);
  141 +
  142 + reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
  143 + if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)
  144 + reg |= m;
  145 + else
  146 + reg &= ~m;
  147 + writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);
  148 + return 0;
  149 +}
  150 +
  151 +static struct irq_chip gpioirqs = {
  152 + .name = "GPIO",
  153 + .ack = ack,
  154 + .mask = mask,
  155 + .unmask = unmask,
  156 + .set_type = set_type,
  157 +};
  158 +
  159 +static u8 demux_masks[4];
  160 +
  161 +static void demux_irqs(unsigned int irq, struct irq_desc *desc)
  162 +{
  163 + u8 *mask = get_irq_desc_data(desc);
  164 + u8 pending;
  165 + int cirq;
  166 +
  167 + desc->chip->mask(irq);
  168 + desc->chip->ack(irq);
  169 + pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;
  170 + cirq = IRQ_BASE - 1;
  171 + while (pending) {
  172 + int n = ffs(pending);
  173 + cirq += n;
  174 + pending >>= n;
  175 + generic_handle_irq(cirq);
  176 + }
  177 + desc->chip->unmask(irq);
  178 +}
  179 +
  180 +extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
  181 +
  182 +void __init variant_init_irq(void)
  183 +{
  184 + int irq, n;
  185 + writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);
  186 + for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {
  187 + const signed char *mapping = platform_irq_mappings[irq];
  188 + int alone = 1;
  189 + u8 mask;
  190 + if (!mapping)
  191 + continue;
  192 + for(mask = 0; *mapping != -1; mapping++)
  193 + switch (*mapping) {
  194 + case S6_INTC_GPIO(0):
  195 + mask |= 1 << 0;
  196 + break;
  197 + case S6_INTC_GPIO(1):
  198 + mask |= 1 << 1;
  199 + break;
  200 + case S6_INTC_GPIO(2):
  201 + mask |= 1 << 2;
  202 + break;
  203 + case S6_INTC_GPIO(3):
  204 + mask |= 0x1f << 3;
  205 + break;
  206 + default:
  207 + alone = 0;
  208 + }
  209 + if (mask) {
  210 + int cirq, i;
  211 + if (!alone) {
  212 + printk(KERN_ERR "chained irq chips can't share"
  213 + " parent irq %i\n", irq);
  214 + continue;
  215 + }
  216 + demux_masks[n] = mask;
  217 + cirq = IRQ_BASE - 1;
  218 + do {
  219 + i = ffs(mask);
  220 + cirq += i;
  221 + mask >>= i;
  222 + set_irq_chip(cirq, &gpioirqs);
  223 + set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
  224 + } while (mask);
  225 + set_irq_data(irq, demux_masks + n);
  226 + set_irq_chained_handler(irq, demux_irqs);
  227 + if (++n == ARRAY_SIZE(demux_masks))
  228 + break;
  229 + }
  230 + }
70 231 }
arch/xtensa/variants/s6000/include/variant/gpio.h
1 1 #ifndef _XTENSA_VARIANT_S6000_GPIO_H
2 2 #define _XTENSA_VARIANT_S6000_GPIO_H
3 3  
4   -extern int s6_gpio_init(void);
  4 +extern int s6_gpio_init(u32 afsel);
5 5  
6 6 #endif /* _XTENSA_VARIANT_S6000_GPIO_H */
arch/xtensa/variants/s6000/include/variant/irq.h
1   -#ifndef __XTENSA_S6000_IRQ_H
2   -#define __XTENSA_S6000_IRQ_H
  1 +#ifndef _XTENSA_S6000_IRQ_H
  2 +#define _XTENSA_S6000_IRQ_H
3 3  
4 4 #define NO_IRQ (-1)
  5 +#define VARIANT_NR_IRQS 8 /* GPIO interrupts */
5 6  
6 7 extern void variant_irq_enable(unsigned int irq);
7   -extern void variant_irq_disable(unsigned int irq);
8 8  
9 9 #endif /* __XTENSA_S6000_IRQ_H */