linux/arch/xtensa/variants/s6000/gpio.c
<<
>>
Prefs
   1/*
   2 * s6000 gpio driver
   3 *
   4 * Copyright (c) 2009 emlix GmbH
   5 * Authors:     Oskar Schirmer <os@emlix.com>
   6 *              Johannes Weiner <jw@emlix.com>
   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(unsigned int irq)
  89{
  90        writeb(1 << (irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
  91}
  92
  93static 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
 100static 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
 107static 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
 151static struct irq_chip gpioirqs = {
 152        .name = "GPIO",
 153        .ack = ack,
 154        .mask = mask,
 155        .unmask = unmask,
 156        .set_type = set_type,
 157};
 158
 159static u8 demux_masks[4];
 160
 161static 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
 180extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];
 181
 182void __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        }
 231}
 232