Path: blob/master/arch/xtensa/variants/s6000/gpio.c
10819 views
/*1* s6000 gpio driver2*3* Copyright (c) 2009 emlix GmbH4* Authors: Oskar Schirmer <[email protected]>5* Johannes Weiner <[email protected]>6* Daniel Gloeckner <[email protected]>7*/8#include <linux/bitops.h>9#include <linux/kernel.h>10#include <linux/module.h>11#include <linux/init.h>12#include <linux/io.h>13#include <linux/irq.h>14#include <linux/gpio.h>1516#include <variant/hardware.h>1718#define IRQ_BASE XTENSA_NR_IRQS1920#define S6_GPIO_DATA 0x00021#define S6_GPIO_IS 0x40422#define S6_GPIO_IBE 0x40823#define S6_GPIO_IEV 0x40C24#define S6_GPIO_IE 0x41025#define S6_GPIO_RIS 0x41426#define S6_GPIO_MIS 0x41827#define S6_GPIO_IC 0x41C28#define S6_GPIO_AFSEL 0x42029#define S6_GPIO_DIR 0x80030#define S6_GPIO_BANK(nr) ((nr) * 0x1000)31#define S6_GPIO_MASK(nr) (4 << (nr))32#define S6_GPIO_OFFSET(nr) \33(S6_GPIO_BANK((nr) >> 3) + S6_GPIO_MASK((nr) & 7))3435static int direction_input(struct gpio_chip *chip, unsigned int off)36{37writeb(0, S6_REG_GPIO + S6_GPIO_DIR + S6_GPIO_OFFSET(off));38return 0;39}4041static int get(struct gpio_chip *chip, unsigned int off)42{43return readb(S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));44}4546static int direction_output(struct gpio_chip *chip, unsigned int off, int val)47{48unsigned rel = S6_GPIO_OFFSET(off);49writeb(~0, S6_REG_GPIO + S6_GPIO_DIR + rel);50writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + rel);51return 0;52}5354static void set(struct gpio_chip *chip, unsigned int off, int val)55{56writeb(val ? ~0 : 0, S6_REG_GPIO + S6_GPIO_DATA + S6_GPIO_OFFSET(off));57}5859static int to_irq(struct gpio_chip *chip, unsigned offset)60{61if (offset < 8)62return offset + IRQ_BASE;63return -EINVAL;64}6566static struct gpio_chip gpiochip = {67.owner = THIS_MODULE,68.direction_input = direction_input,69.get = get,70.direction_output = direction_output,71.set = set,72.to_irq = to_irq,73.base = 0,74.ngpio = 24,75.can_sleep = 0, /* no blocking io needed */76.exported = 0, /* no exporting to userspace */77};7879int s6_gpio_init(u32 afsel)80{81writeb(afsel, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL);82writeb(afsel >> 8, S6_REG_GPIO + S6_GPIO_BANK(1) + S6_GPIO_AFSEL);83writeb(afsel >> 16, S6_REG_GPIO + S6_GPIO_BANK(2) + S6_GPIO_AFSEL);84return gpiochip_add(&gpiochip);85}8687static void ack(struct irq_data *d)88{89writeb(1 << (d->irq - IRQ_BASE), S6_REG_GPIO + S6_GPIO_IC);90}9192static void mask(struct irq_data *d)93{94u8 r = readb(S6_REG_GPIO + S6_GPIO_IE);95r &= ~(1 << (d->irq - IRQ_BASE));96writeb(r, S6_REG_GPIO + S6_GPIO_IE);97}9899static void unmask(struct irq_data *d)100{101u8 m = readb(S6_REG_GPIO + S6_GPIO_IE);102m |= 1 << (d->irq - IRQ_BASE);103writeb(m, S6_REG_GPIO + S6_GPIO_IE);104}105106static int set_type(struct irq_data *d, unsigned int type)107{108const u8 m = 1 << (d->irq - IRQ_BASE);109irq_flow_handler_t handler;110u8 reg;111112if (type == IRQ_TYPE_PROBE) {113if ((readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_AFSEL) & m)114|| (readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE) & m)115|| readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_DIR116+ S6_GPIO_MASK(irq - IRQ_BASE)))117return 0;118type = IRQ_TYPE_EDGE_BOTH;119}120121reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);122if (type & (IRQ_TYPE_LEVEL_LOW | IRQ_TYPE_LEVEL_HIGH)) {123reg |= m;124handler = handle_level_irq;125} else {126reg &= ~m;127handler = handle_edge_irq;128}129writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IS);130__irq_set_handler_locked(irq, handler);131132reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);133if (type & (IRQ_TYPE_LEVEL_HIGH | IRQ_TYPE_EDGE_RISING))134reg |= m;135else136reg &= ~m;137writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IEV);138139reg = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);140if ((type & IRQ_TYPE_EDGE_BOTH) == IRQ_TYPE_EDGE_BOTH)141reg |= m;142else143reg &= ~m;144writeb(reg, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IBE);145return 0;146}147148static struct irq_chip gpioirqs = {149.name = "GPIO",150.irq_ack = ack,151.irq_mask = mask,152.irq_unmask = unmask,153.irq_set_type = set_type,154};155156static u8 demux_masks[4];157158static void demux_irqs(unsigned int irq, struct irq_desc *desc)159{160struct irq_chip *chip = irq_desc_get_chip(desc);161u8 *mask = irq_desc_get_handler_data(desc);162u8 pending;163int cirq;164165chip->irq_mask(&desc->irq_data);166chip->irq_ack(&desc->irq_data));167pending = readb(S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_MIS) & *mask;168cirq = IRQ_BASE - 1;169while (pending) {170int n = ffs(pending);171cirq += n;172pending >>= n;173generic_handle_irq(cirq);174}175chip->irq_unmask(&desc->irq_data));176}177178extern const signed char *platform_irq_mappings[XTENSA_NR_IRQS];179180void __init variant_init_irq(void)181{182int irq, n;183writeb(0, S6_REG_GPIO + S6_GPIO_BANK(0) + S6_GPIO_IE);184for (irq = n = 0; irq < XTENSA_NR_IRQS; irq++) {185const signed char *mapping = platform_irq_mappings[irq];186int alone = 1;187u8 mask;188if (!mapping)189continue;190for(mask = 0; *mapping != -1; mapping++)191switch (*mapping) {192case S6_INTC_GPIO(0):193mask |= 1 << 0;194break;195case S6_INTC_GPIO(1):196mask |= 1 << 1;197break;198case S6_INTC_GPIO(2):199mask |= 1 << 2;200break;201case S6_INTC_GPIO(3):202mask |= 0x1f << 3;203break;204default:205alone = 0;206}207if (mask) {208int cirq, i;209if (!alone) {210printk(KERN_ERR "chained irq chips can't share"211" parent irq %i\n", irq);212continue;213}214demux_masks[n] = mask;215cirq = IRQ_BASE - 1;216do {217i = ffs(mask);218cirq += i;219mask >>= i;220irq_set_chip(cirq, &gpioirqs);221irq_set_irq_type(irq, IRQ_TYPE_LEVEL_LOW);222} while (mask);223irq_set_handler_data(irq, demux_masks + n);224irq_set_chained_handler(irq, demux_irqs);225if (++n == ARRAY_SIZE(demux_masks))226break;227}228}229}230231232