Commit fac7fa16 authored by Javier Martinez Canillas's avatar Javier Martinez Canillas Committed by Linus Walleij

gpio/omap: auto-setup a GPIO when used as an IRQ

The OMAP GPIO controller HW requires a pin to be configured in GPIO
input mode in order to operate as an interrupt input. Since drivers
should not be aware of whether an interrupt pin is also a GPIO or not,
the HW should be fully configured/enabled as an IRQ if a driver solely
uses IRQ APIs such as request_irq(), and never calls any GPIO-related
APIs. As such, add the missing HW setup to the OMAP GPIO controller's
irq_chip driver.

Since this bypasses the GPIO subsystem we have to ensure that another
driver won't be able to request the same GPIO pin that is used as an
IRQ and set its direction as output. Requesting the GPIO and setting
its direction as input is allowed though.

This fixes smsc911x ethernet support for tobi and igep OMAP3 boards
and OMAP4 SDP SPI based ethernet that use a GPIO as an interrupt line.

Cc: stable@vger.kernel.org
Acked-by: default avatarStephen Warren <swarren@nvidia.com>
Tested-by: default avatarGeorge Cherian <george.cherian@ti.com>
Tested-by: default avatarAaro Koskinen <aaro.koskinen@iki.fi>
Tested-by: default avatarLars Poeschel <poeschel@lemonage.de>
Reviewed-by: default avatarKevin Hilman <khilman@linaro.org>
Tested-by: default avatarKevin Hilman <khilman@linaro.org>
Acked-by: default avatarSantosh Shilimkar <santosh.shilimkar@ti.com>
Acked-by: default avatarTony Lindgren <tony@atomide.com>
Signed-off-by: default avatarJavier Martinez Canillas <javier.martinez@collabora.co.uk>
Signed-off-by: default avatarLinus Walleij <linus.walleij@linaro.org>
parent fa365e4d
...@@ -424,6 +424,52 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio, ...@@ -424,6 +424,52 @@ static int _set_gpio_triggering(struct gpio_bank *bank, int gpio,
return 0; return 0;
} }
static void _enable_gpio_module(struct gpio_bank *bank, unsigned offset)
{
if (bank->regs->pinctrl) {
void __iomem *reg = bank->base + bank->regs->pinctrl;
/* Claim the pin for MPU */
__raw_writel(__raw_readl(reg) | (1 << offset), reg);
}
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = __raw_readl(reg);
/* Module is enabled, clocks are not gated */
ctrl &= ~GPIO_MOD_CTRL_BIT;
__raw_writel(ctrl, reg);
bank->context.ctrl = ctrl;
}
}
static void _disable_gpio_module(struct gpio_bank *bank, unsigned offset)
{
void __iomem *base = bank->base;
if (bank->regs->wkup_en &&
!LINE_USED(bank->mod_usage, offset) &&
!LINE_USED(bank->irq_usage, offset)) {
/* Disable wake-up during idle for dynamic tick */
_gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
bank->context.wake_en =
__raw_readl(bank->base + bank->regs->wkup_en);
}
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = __raw_readl(reg);
/* Module is disabled, clocks are gated */
ctrl |= GPIO_MOD_CTRL_BIT;
__raw_writel(ctrl, reg);
bank->context.ctrl = ctrl;
}
}
static int gpio_is_input(struct gpio_bank *bank, int mask) static int gpio_is_input(struct gpio_bank *bank, int mask)
{ {
void __iomem *reg = bank->base + bank->regs->direction; void __iomem *reg = bank->base + bank->regs->direction;
...@@ -437,9 +483,10 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) ...@@ -437,9 +483,10 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
unsigned gpio = 0; unsigned gpio = 0;
int retval; int retval;
unsigned long flags; unsigned long flags;
unsigned offset;
if (WARN_ON(!BANK_USED(bank))) if (!BANK_USED(bank))
return -EINVAL; pm_runtime_get_sync(bank->dev);
#ifdef CONFIG_ARCH_OMAP1 #ifdef CONFIG_ARCH_OMAP1
if (d->irq > IH_MPUIO_BASE) if (d->irq > IH_MPUIO_BASE)
...@@ -457,7 +504,16 @@ static int gpio_irq_type(struct irq_data *d, unsigned type) ...@@ -457,7 +504,16 @@ static int gpio_irq_type(struct irq_data *d, unsigned type)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&bank->lock, flags); spin_lock_irqsave(&bank->lock, flags);
retval = _set_gpio_triggering(bank, GPIO_INDEX(bank, gpio), type); offset = GPIO_INDEX(bank, gpio);
retval = _set_gpio_triggering(bank, offset, type);
if (!LINE_USED(bank->mod_usage, offset)) {
_enable_gpio_module(bank, offset);
_set_gpio_direction(bank, offset, 1);
} else if (!gpio_is_input(bank, 1 << offset)) {
spin_unlock_irqrestore(&bank->lock, flags);
return -EINVAL;
}
bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio); bank->irq_usage |= 1 << GPIO_INDEX(bank, gpio);
spin_unlock_irqrestore(&bank->lock, flags); spin_unlock_irqrestore(&bank->lock, flags);
...@@ -620,30 +676,14 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) ...@@ -620,30 +676,14 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
spin_lock_irqsave(&bank->lock, flags); spin_lock_irqsave(&bank->lock, flags);
/* Set trigger to none. You need to enable the desired trigger with /* Set trigger to none. You need to enable the desired trigger with
* request_irq() or set_irq_type(). * request_irq() or set_irq_type(). Only do this if the IRQ line has
* not already been requested.
*/ */
if (!LINE_USED(bank->irq_usage, offset)) {
_set_gpio_triggering(bank, offset, IRQ_TYPE_NONE); _set_gpio_triggering(bank, offset, IRQ_TYPE_NONE);
_enable_gpio_module(bank, offset);
if (bank->regs->pinctrl) {
void __iomem *reg = bank->base + bank->regs->pinctrl;
/* Claim the pin for MPU */
__raw_writel(__raw_readl(reg) | (1 << offset), reg);
}
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = __raw_readl(reg);
/* Module is enabled, clocks are not gated */
ctrl &= ~GPIO_MOD_CTRL_BIT;
__raw_writel(ctrl, reg);
bank->context.ctrl = ctrl;
} }
bank->mod_usage |= 1 << offset; bank->mod_usage |= 1 << offset;
spin_unlock_irqrestore(&bank->lock, flags); spin_unlock_irqrestore(&bank->lock, flags);
return 0; return 0;
...@@ -652,31 +692,11 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset) ...@@ -652,31 +692,11 @@ static int omap_gpio_request(struct gpio_chip *chip, unsigned offset)
static void omap_gpio_free(struct gpio_chip *chip, unsigned offset) static void omap_gpio_free(struct gpio_chip *chip, unsigned offset)
{ {
struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip); struct gpio_bank *bank = container_of(chip, struct gpio_bank, chip);
void __iomem *base = bank->base;
unsigned long flags; unsigned long flags;
spin_lock_irqsave(&bank->lock, flags); spin_lock_irqsave(&bank->lock, flags);
if (bank->regs->wkup_en) {
/* Disable wake-up during idle for dynamic tick */
_gpio_rmw(base, bank->regs->wkup_en, 1 << offset, 0);
bank->context.wake_en =
__raw_readl(bank->base + bank->regs->wkup_en);
}
bank->mod_usage &= ~(1 << offset); bank->mod_usage &= ~(1 << offset);
_disable_gpio_module(bank, offset);
if (bank->regs->ctrl && !BANK_USED(bank)) {
void __iomem *reg = bank->base + bank->regs->ctrl;
u32 ctrl;
ctrl = __raw_readl(reg);
/* Module is disabled, clocks are gated */
ctrl |= GPIO_MOD_CTRL_BIT;
__raw_writel(ctrl, reg);
bank->context.ctrl = ctrl;
}
_reset_gpio(bank, bank->chip.base + offset); _reset_gpio(bank, bank->chip.base + offset);
spin_unlock_irqrestore(&bank->lock, flags); spin_unlock_irqrestore(&bank->lock, flags);
...@@ -778,8 +798,16 @@ static void gpio_irq_shutdown(struct irq_data *d) ...@@ -778,8 +798,16 @@ static void gpio_irq_shutdown(struct irq_data *d)
spin_lock_irqsave(&bank->lock, flags); spin_lock_irqsave(&bank->lock, flags);
bank->irq_usage &= ~(1 << offset); bank->irq_usage &= ~(1 << offset);
_disable_gpio_module(bank, offset);
_reset_gpio(bank, gpio); _reset_gpio(bank, gpio);
spin_unlock_irqrestore(&bank->lock, flags); spin_unlock_irqrestore(&bank->lock, flags);
/*
* If this is the last IRQ to be freed in the bank,
* disable the bank module.
*/
if (!BANK_USED(bank))
pm_runtime_put(bank->dev);
} }
static void gpio_ack_irq(struct irq_data *d) static void gpio_ack_irq(struct irq_data *d)
...@@ -929,13 +957,22 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value) ...@@ -929,13 +957,22 @@ static int gpio_output(struct gpio_chip *chip, unsigned offset, int value)
{ {
struct gpio_bank *bank; struct gpio_bank *bank;
unsigned long flags; unsigned long flags;
int retval = 0;
bank = container_of(chip, struct gpio_bank, chip); bank = container_of(chip, struct gpio_bank, chip);
spin_lock_irqsave(&bank->lock, flags); spin_lock_irqsave(&bank->lock, flags);
if (LINE_USED(bank->irq_usage, offset)) {
retval = -EINVAL;
goto exit;
}
bank->set_dataout(bank, offset, value); bank->set_dataout(bank, offset, value);
_set_gpio_direction(bank, offset, 0); _set_gpio_direction(bank, offset, 0);
exit:
spin_unlock_irqrestore(&bank->lock, flags); spin_unlock_irqrestore(&bank->lock, flags);
return 0; return retval;
} }
static int gpio_debounce(struct gpio_chip *chip, unsigned offset, static int gpio_debounce(struct gpio_chip *chip, unsigned offset,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment