/arch/xtensa/variants/s6000/gpio.c
C | 230 lines | 198 code | 24 blank | 8 comment | 22 complexity | f195987525f5637796d2f432d1bdc6a7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
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(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}