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