linux/arch/arm/mach-gemini/gpio.c
Russell King 7f77c5c39d ARM: gemini: Fix race in installing GPIO chained IRQ handler
The gemini code was installing its chained interrupt handler (which
enables the interrupt) before it was setting its data, which is bad if
the IRQ was previously pending.  Avoid this problem by converting it to
irq_set_chained_handler_and_data().

Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
Cc: Alexandre Courbot <gnurou@gmail.com>
Cc: Hans Ulli Kroll <ulli.kroll@googlemail.com>
Cc: Jason Cooper <jason@lakedaemon.net>
Cc: Lee Jones <lee.jones@linaro.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Cc: Thierry Reding <thierry.reding@gmail.com>
Cc: linux-arm-kernel@lists.infradead.org
Link: http://lkml.kernel.org/r/E1Z4z07-0002SO-Gv@rmk-PC.arm.linux.org.uk
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
2015-06-18 14:03:08 +02:00

231 lines
5.7 KiB
C

/*
* Gemini gpiochip and interrupt routines
*
* Copyright (C) 2008-2009 Paulius Zaleckas <paulius.zaleckas@teltonika.lt>
*
* Based on plat-mxc/gpio.c:
* MXC GPIO support. (c) 2008 Daniel Mack <daniel@caiaq.de>
* Copyright 2008 Juergen Beisert, kernel@pengutronix.de
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/io.h>
#include <linux/irq.h>
#include <linux/gpio.h>
#include <mach/hardware.h>
#include <mach/irqs.h>
#define GPIO_BASE(x) IO_ADDRESS(GEMINI_GPIO_BASE(x))
#define irq_to_gpio(x) ((x) - GPIO_IRQ_BASE)
/* GPIO registers definition */
#define GPIO_DATA_OUT 0x0
#define GPIO_DATA_IN 0x4
#define GPIO_DIR 0x8
#define GPIO_DATA_SET 0x10
#define GPIO_DATA_CLR 0x14
#define GPIO_PULL_EN 0x18
#define GPIO_PULL_TYPE 0x1C
#define GPIO_INT_EN 0x20
#define GPIO_INT_STAT 0x24
#define GPIO_INT_MASK 0x2C
#define GPIO_INT_CLR 0x30
#define GPIO_INT_TYPE 0x34
#define GPIO_INT_BOTH_EDGE 0x38
#define GPIO_INT_LEVEL 0x3C
#define GPIO_DEBOUNCE_EN 0x40
#define GPIO_DEBOUNCE_PRESCALE 0x44
#define GPIO_PORT_NUM 3
static void _set_gpio_irqenable(void __iomem *base, unsigned int index,
int enable)
{
unsigned int reg;
reg = __raw_readl(base + GPIO_INT_EN);
reg = (reg & (~(1 << index))) | (!!enable << index);
__raw_writel(reg, base + GPIO_INT_EN);
}
static void gpio_ack_irq(struct irq_data *d)
{
unsigned int gpio = irq_to_gpio(d->irq);
void __iomem *base = GPIO_BASE(gpio / 32);
__raw_writel(1 << (gpio % 32), base + GPIO_INT_CLR);
}
static void gpio_mask_irq(struct irq_data *d)
{
unsigned int gpio = irq_to_gpio(d->irq);
void __iomem *base = GPIO_BASE(gpio / 32);
_set_gpio_irqenable(base, gpio % 32, 0);
}
static void gpio_unmask_irq(struct irq_data *d)
{
unsigned int gpio = irq_to_gpio(d->irq);
void __iomem *base = GPIO_BASE(gpio / 32);
_set_gpio_irqenable(base, gpio % 32, 1);
}
static int gpio_set_irq_type(struct irq_data *d, unsigned int type)
{
unsigned int gpio = irq_to_gpio(d->irq);
unsigned int gpio_mask = 1 << (gpio % 32);
void __iomem *base = GPIO_BASE(gpio / 32);
unsigned int reg_both, reg_level, reg_type;
reg_type = __raw_readl(base + GPIO_INT_TYPE);
reg_level = __raw_readl(base + GPIO_INT_LEVEL);
reg_both = __raw_readl(base + GPIO_INT_BOTH_EDGE);
switch (type) {
case IRQ_TYPE_EDGE_BOTH:
reg_type &= ~gpio_mask;
reg_both |= gpio_mask;
break;
case IRQ_TYPE_EDGE_RISING:
reg_type &= ~gpio_mask;
reg_both &= ~gpio_mask;
reg_level &= ~gpio_mask;
break;
case IRQ_TYPE_EDGE_FALLING:
reg_type &= ~gpio_mask;
reg_both &= ~gpio_mask;
reg_level |= gpio_mask;
break;
case IRQ_TYPE_LEVEL_HIGH:
reg_type |= gpio_mask;
reg_level &= ~gpio_mask;
break;
case IRQ_TYPE_LEVEL_LOW:
reg_type |= gpio_mask;
reg_level |= gpio_mask;
break;
default:
return -EINVAL;
}
__raw_writel(reg_type, base + GPIO_INT_TYPE);
__raw_writel(reg_level, base + GPIO_INT_LEVEL);
__raw_writel(reg_both, base + GPIO_INT_BOTH_EDGE);
gpio_ack_irq(d);
return 0;
}
static void gpio_irq_handler(unsigned int irq, struct irq_desc *desc)
{
unsigned int port = (unsigned int)irq_desc_get_handler_data(desc);
unsigned int gpio_irq_no, irq_stat;
irq_stat = __raw_readl(GPIO_BASE(port) + GPIO_INT_STAT);
gpio_irq_no = GPIO_IRQ_BASE + port * 32;
for (; irq_stat != 0; irq_stat >>= 1, gpio_irq_no++) {
if ((irq_stat & 1) == 0)
continue;
generic_handle_irq(gpio_irq_no);
}
}
static struct irq_chip gpio_irq_chip = {
.name = "GPIO",
.irq_ack = gpio_ack_irq,
.irq_mask = gpio_mask_irq,
.irq_unmask = gpio_unmask_irq,
.irq_set_type = gpio_set_irq_type,
};
static void _set_gpio_direction(struct gpio_chip *chip, unsigned offset,
int dir)
{
void __iomem *base = GPIO_BASE(offset / 32);
unsigned int reg;
reg = __raw_readl(base + GPIO_DIR);
if (dir)
reg |= 1 << (offset % 32);
else
reg &= ~(1 << (offset % 32));
__raw_writel(reg, base + GPIO_DIR);
}
static void gemini_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
{
void __iomem *base = GPIO_BASE(offset / 32);
if (value)
__raw_writel(1 << (offset % 32), base + GPIO_DATA_SET);
else
__raw_writel(1 << (offset % 32), base + GPIO_DATA_CLR);
}
static int gemini_gpio_get(struct gpio_chip *chip, unsigned offset)
{
void __iomem *base = GPIO_BASE(offset / 32);
return (__raw_readl(base + GPIO_DATA_IN) >> (offset % 32)) & 1;
}
static int gemini_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
_set_gpio_direction(chip, offset, 0);
return 0;
}
static int gemini_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
_set_gpio_direction(chip, offset, 1);
gemini_gpio_set(chip, offset, value);
return 0;
}
static struct gpio_chip gemini_gpio_chip = {
.label = "Gemini",
.direction_input = gemini_gpio_direction_input,
.get = gemini_gpio_get,
.direction_output = gemini_gpio_direction_output,
.set = gemini_gpio_set,
.base = 0,
.ngpio = GPIO_PORT_NUM * 32,
};
void __init gemini_gpio_init(void)
{
int i, j;
for (i = 0; i < GPIO_PORT_NUM; i++) {
/* disable, unmask and clear all interrupts */
__raw_writel(0x0, GPIO_BASE(i) + GPIO_INT_EN);
__raw_writel(0x0, GPIO_BASE(i) + GPIO_INT_MASK);
__raw_writel(~0x0, GPIO_BASE(i) + GPIO_INT_CLR);
for (j = GPIO_IRQ_BASE + i * 32;
j < GPIO_IRQ_BASE + (i + 1) * 32; j++) {
irq_set_chip_and_handler(j, &gpio_irq_chip,
handle_edge_irq);
set_irq_flags(j, IRQF_VALID);
}
irq_set_chained_handler_and_data(IRQ_GPIO(i), gpio_irq_handler,
(void *)i);
}
BUG_ON(gpiochip_add(&gemini_gpio_chip));
}