123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231 |
- /*
- * s6000 gpio driver
- *
- * 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
- #define S6_GPIO_IEV 0x40C
- #define S6_GPIO_IE 0x410
- #define S6_GPIO_RIS 0x414
- #define S6_GPIO_MIS 0x418
- #define S6_GPIO_IC 0x41C
- #define S6_GPIO_AFSEL 0x420
- #define S6_GPIO_DIR 0x800
- #define S6_GPIO_BANK(nr) ((nr) * 0x1000)
- #define S6_GPIO_MASK(nr) (4 << (nr))
- #define S6_GPIO_OFFSET(nr) \
- (S6_GPIO_BANK((nr) >> 3) + S6_GPIO_MASK((nr) & 7))
- static int direction_input(struct gpio_chip *chip, unsigned int off)
- {
- writeb(0, S6_REG_GPIO + S6_GPIO_DIR + S6_GPIO_OFFSET(off));
- return 0;
- }
- static int get(struct gpio_chip *chip, unsigned int off)
- {
- return readb(S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));
- }
- static int direction_output(struct gpio_chip *chip, unsigned int off, int val)
- {
- unsigned rel = S6_GPIO_OFFSET(off);
- writeb(~0, S6_REG_GPIO + S6_GPIO_DIR + rel);
- writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + rel);
- return 0;
- }
- 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(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(struct irq_data *d)
- {
- writeb(1 << (d->irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);
- }
- static void mask(struct irq_data *d)
- {
- u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);
- r &= ~(1 << (d->irq - IRQ_BASE));
- writeb(r, S6_REG_GPIO + S6_GPIO_IE);
- }
- static void unmask(struct irq_data *d)
- {
- u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);
- m |= 1 << (d->irq - IRQ_BASE);
- writeb(m, S6_REG_GPIO + S6_GPIO_IE);
- }
- static int set_type(struct irq_data *d, unsigned int type)
- {
- const u8 m = 1 << (d->irq - IRQ_BASE);
- irq_flow_handler_t handler;
- 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);
- __irq_set_handler_locked(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",
- .irq_ack = ack,
- .irq_mask = mask,
- .irq_unmask = unmask,
- .irq_set_type = set_type,
- };
- static u8 demux_masks[4];
- static void demux_irqs(unsigned int irq, struct irq_desc *desc)
- {
- struct irq_chip *chip = irq_desc_get_chip(desc);
- u8 *mask = irq_desc_get_handler_data(desc);
- u8 pending;
- int cirq;
- chip->irq_mask(&desc->irq_data);
- chip->irq_ack(&desc->irq_data));
- 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);
- }
- chip->irq_unmask(&desc->irq_data));
- }
- 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;
- irq_set_chip(cirq, &gpioirqs);
- irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);
- } while (mask);
- irq_set_handler_data(irq, demux_masks + n);
- irq_set_chained_handler(irq, demux_irqs);
- if (++n == ARRAY_SIZE(demux_masks))
- break;
- }
- }
- }
|