PageRenderTime 120ms CodeModel.GetById 50ms app.highlight 65ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/xtensa/variants/s6000/gpio.c

https://bitbucket.org/ndreys/linux-sunxi
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}