blob: a4deeb35288bbe086fea5de67d1d7561ed45a9c1 [file] [log] [blame]
/*
* Atheros AR71XX/AR724X/AR913X GPIO API support
*
* Copyright (c) 2013 The Linux Foundation. All rights reserved.
* Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
* Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
* Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
*
* Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#include <linux/ioport.h>
#include <linux/gpio.h>
#include <linux/irq.h>
#include <linux/interrupt.h>
#include <linux/of.h>
#include <asm/mach-ath79/ar71xx_regs.h>
#include <asm/mach-ath79/ath79.h>
#include <asm/mach-ath79/irq.h>
#include "common.h"
void __iomem *ath79_gpio_base;
EXPORT_SYMBOL_GPL(ath79_gpio_base);
static unsigned long ath79_gpio_count;
static DEFINE_SPINLOCK(ath79_gpio_lock);
/*
* gpio_both_edge is a bitmask of which gpio pins need to have
* the detect priority flipped from the interrupt handler to
* emulate IRQ_TYPE_EDGE_BOTH.
*/
static unsigned long gpio_both_edge = 0;
static void __ath79_gpio_set_value(unsigned gpio, int value)
{
void __iomem *base = ath79_gpio_base;
if (value)
__raw_writel(1 << gpio, base + AR71XX_GPIO_REG_SET);
else
__raw_writel(1 << gpio, base + AR71XX_GPIO_REG_CLEAR);
}
static int __ath79_gpio_get_value(unsigned gpio)
{
return (__raw_readl(ath79_gpio_base + AR71XX_GPIO_REG_IN) >> gpio) & 1;
}
static int ath79_gpio_get_value(struct gpio_chip *chip, unsigned offset)
{
return __ath79_gpio_get_value(offset);
}
static void ath79_gpio_set_value(struct gpio_chip *chip,
unsigned offset, int value)
{
__ath79_gpio_set_value(offset, value);
}
static int ath79_gpio_direction_input(struct gpio_chip *chip,
unsigned offset)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
static int ath79_gpio_direction_output(struct gpio_chip *chip,
unsigned offset, int value)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
if (value)
__raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
else
__raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
static int ar934x_gpio_direction_input(struct gpio_chip *chip, unsigned offset)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << offset),
base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
static int ar934x_gpio_direction_output(struct gpio_chip *chip, unsigned offset,
int value)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
if (value)
__raw_writel(1 << offset, base + AR71XX_GPIO_REG_SET);
else
__raw_writel(1 << offset, base + AR71XX_GPIO_REG_CLEAR);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << offset),
base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
int ath79_gpio_direction_select(unsigned gpio, bool oe)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
bool ieq_1 = (soc_is_ar934x() ||
soc_is_qca953x() ||
soc_is_qca955x() ||
soc_is_qca956x());
if (gpio >= ath79_gpio_count)
return -1;
spin_lock_irqsave(&ath79_gpio_lock, flags);
if ((ieq_1 && oe) || (!ieq_1 && !oe))
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) & ~(1 << gpio),
base + AR71XX_GPIO_REG_OE);
else
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_OE) | (1 << gpio),
base + AR71XX_GPIO_REG_OE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
static struct gpio_chip ath79_gpio_chip = {
.label = "ath79",
.get = ath79_gpio_get_value,
.set = ath79_gpio_set_value,
.direction_input = ath79_gpio_direction_input,
.direction_output = ath79_gpio_direction_output,
.base = 0,
};
static void __iomem *ath79_gpio_get_function_reg(void)
{
u32 reg = 0;
if (soc_is_ar71xx() ||
soc_is_ar724x() ||
soc_is_ar913x() ||
soc_is_ar933x())
reg = AR71XX_GPIO_REG_FUNC;
else if (soc_is_ar934x() || soc_is_qca953x() || soc_is_qca956x())
reg = AR934X_GPIO_REG_FUNC;
else
BUG();
return ath79_gpio_base + reg;
}
void ath79_gpio_function_enable(u32 mask)
{
void __iomem *reg = ath79_gpio_get_function_reg();
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
__raw_writel(__raw_readl(reg) | mask, reg);
/* flush write */
__raw_readl(reg);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
}
void ath79_gpio_function_disable(u32 mask)
{
void __iomem *reg = ath79_gpio_get_function_reg();
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
__raw_writel(__raw_readl(reg) & ~mask, reg);
/* flush write */
__raw_readl(reg);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
}
void ath79_gpio_function_setup(u32 set, u32 clear)
{
void __iomem *reg = ath79_gpio_get_function_reg();
unsigned long flags;
spin_lock_irqsave(&ath79_gpio_lock, flags);
__raw_writel((__raw_readl(reg) & ~clear) | set, reg);
/* flush write */
__raw_readl(reg);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
}
void __init ath79_gpio_input_select(unsigned gpio, u8 val)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
unsigned int reg;
u32 t, s;
BUG_ON(!soc_is_ar934x());
if (gpio >= ath79_gpio_count)
return;
reg = AR934X_GPIO_REG_IN_ENABLE0 + 4 * (val / 4);
s = 8 * (val % 4);
spin_lock_irqsave(&ath79_gpio_lock, flags);
t = __raw_readl(base + reg);
t &= ~(0xff << s);
t |= gpio << s;
__raw_writel(t, base + reg);
/* flush write */
(void) __raw_readl(base + reg);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
}
void __init ath79_gpio_output_select(unsigned gpio, u8 val)
{
void __iomem *base = ath79_gpio_base;
unsigned long flags;
unsigned int reg;
u32 t, s;
BUG_ON(!soc_is_ar934x() && !soc_is_qca953x() && !soc_is_qca956x());
if (gpio >= ath79_gpio_count)
return;
reg = AR934X_GPIO_REG_OUT_FUNC0 + 4 * (gpio / 4);
s = 8 * (gpio % 4);
spin_lock_irqsave(&ath79_gpio_lock, flags);
t = __raw_readl(base + reg);
t &= ~(0xff << s);
t |= val << s;
__raw_writel(t, base + reg);
/* flush write */
(void) __raw_readl(base + reg);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
}
static int ath79_gpio_irq_type(struct irq_data *d, unsigned type)
{
int offset = d->irq - ATH79_GPIO_IRQ_BASE;
void __iomem *base = ath79_gpio_base;
unsigned long flags;
unsigned long int_type;
unsigned long int_polarity;
unsigned long bit = (1 << offset);
spin_lock_irqsave(&ath79_gpio_lock, flags);
int_type = __raw_readl(base + AR71XX_GPIO_REG_INT_TYPE);
int_polarity = __raw_readl(base + AR71XX_GPIO_REG_INT_POLARITY);
gpio_both_edge &= ~bit;
switch (type) {
case IRQ_TYPE_EDGE_RISING:
int_type &= ~bit;
int_polarity |= bit;
break;
case IRQ_TYPE_EDGE_FALLING:
int_type &= ~bit;
int_polarity &= ~bit;
break;
case IRQ_TYPE_LEVEL_HIGH:
int_type |= bit;
int_polarity |= bit;
break;
case IRQ_TYPE_LEVEL_LOW:
int_type |= bit;
int_polarity &= ~bit;
break;
case IRQ_TYPE_EDGE_BOTH:
int_type |= bit;
/* set polarity based on current value */
if (gpio_get_value(offset)) {
int_polarity &= ~bit;
} else {
int_polarity |= bit;
}
/* flip this gpio in the interrupt handler */
gpio_both_edge |= bit;
break;
default:
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return -EINVAL;
}
__raw_writel(int_type, base + AR71XX_GPIO_REG_INT_TYPE);
__raw_writel(int_polarity, base + AR71XX_GPIO_REG_INT_POLARITY);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_MODE) | (1 << offset),
base + AR71XX_GPIO_REG_INT_MODE);
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) & ~(1 << offset),
base + AR71XX_GPIO_REG_INT_ENABLE);
spin_unlock_irqrestore(&ath79_gpio_lock, flags);
return 0;
}
static void ath79_gpio_irq_enable(struct irq_data *d)
{
int offset = d->irq - ATH79_GPIO_IRQ_BASE;
void __iomem *base = ath79_gpio_base;
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) | (1 << offset),
base + AR71XX_GPIO_REG_INT_ENABLE);
}
static void ath79_gpio_irq_disable(struct irq_data *d)
{
int offset = d->irq - ATH79_GPIO_IRQ_BASE;
void __iomem *base = ath79_gpio_base;
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_ENABLE) & ~(1 << offset),
base + AR71XX_GPIO_REG_INT_ENABLE);
}
static struct irq_chip ath79_gpio_irqchip = {
.name = "GPIO",
.irq_enable = ath79_gpio_irq_enable,
.irq_disable = ath79_gpio_irq_disable,
.irq_set_type = ath79_gpio_irq_type,
};
static irqreturn_t ath79_gpio_irq(int irq, void *dev)
{
void __iomem *base = ath79_gpio_base;
unsigned int stat = __raw_readl(base + AR71XX_GPIO_REG_INT_PENDING);
int irq_base = ATH79_GPIO_IRQ_BASE;
while (stat) {
int bit_num = __ffs(stat);
unsigned long bit = (1<<bit_num);
if (bit & gpio_both_edge) {
__raw_writel(__raw_readl(base + AR71XX_GPIO_REG_INT_POLARITY) ^ bit,
base + AR71XX_GPIO_REG_INT_POLARITY);
}
handle_nested_irq(irq_base + bit_num);
stat &= ~bit;
}
return IRQ_HANDLED;
}
static int __devinit ath79_gpio_irq_init(struct gpio_chip *chip)
{
int irq;
int irq_base = ATH79_GPIO_IRQ_BASE;
for (irq = irq_base; irq < irq_base + chip->ngpio; irq++) {
irq_set_chip_data(irq, chip);
irq_set_chip_and_handler(irq, &ath79_gpio_irqchip, handle_simple_irq);
irq_set_nested_thread(irq, 1);
irq_set_noprobe(irq);
}
return 0;
}
void __init ath79_gpio_init(void)
{
int err;
if (soc_is_ar71xx())
ath79_gpio_count = AR71XX_GPIO_COUNT;
else if (soc_is_ar724x())
ath79_gpio_count = AR724X_GPIO_COUNT;
else if (soc_is_ar913x())
ath79_gpio_count = AR913X_GPIO_COUNT;
else if (soc_is_ar933x())
ath79_gpio_count = AR933X_GPIO_COUNT;
else if (soc_is_ar934x())
ath79_gpio_count = AR934X_GPIO_COUNT;
else if (soc_is_qca953x())
ath79_gpio_count = QCA953X_GPIO_COUNT;
else if (soc_is_qca955x())
ath79_gpio_count = QCA955X_GPIO_COUNT;
else if (soc_is_qca956x())
ath79_gpio_count = QCA956X_GPIO_COUNT;
else
BUG();
ath79_gpio_base = ioremap_nocache(AR71XX_GPIO_BASE, AR71XX_GPIO_SIZE);
ath79_gpio_chip.ngpio = ath79_gpio_count;
if (soc_is_ar934x() || soc_is_qca953x() || soc_is_qca955x() ||
soc_is_qca956x()) {
ath79_gpio_chip.direction_input = ar934x_gpio_direction_input;
ath79_gpio_chip.direction_output = ar934x_gpio_direction_output;
}
ath79_gpio_chip.of_node = of_find_node_by_path("/ath79-gpio");
err = gpiochip_add(&ath79_gpio_chip);
if (err)
panic("cannot add AR71xx GPIO chip, error=%d", err);
ath79_gpio_irq_init(&ath79_gpio_chip);
request_threaded_irq(ATH79_MISC_IRQ_GPIO, NULL, ath79_gpio_irq, IRQF_ONESHOT, "ath79-gpio", NULL);
}
int gpio_get_value(unsigned gpio)
{
if (gpio < ath79_gpio_count)
return __ath79_gpio_get_value(gpio);
return __gpio_get_value(gpio);
}
EXPORT_SYMBOL(gpio_get_value);
void gpio_set_value(unsigned gpio, int value)
{
if (gpio < ath79_gpio_count)
__ath79_gpio_set_value(gpio, value);
else
__gpio_set_value(gpio, value);
}
EXPORT_SYMBOL(gpio_set_value);
int gpio_to_irq(unsigned gpio)
{
if (gpio > ath79_gpio_count) {
return -EINVAL;
}
return ATH79_GPIO_IRQ_BASE + gpio;
}
EXPORT_SYMBOL(gpio_to_irq);
int irq_to_gpio(unsigned irq)
{
unsigned gpio = irq - ATH79_GPIO_IRQ_BASE;
if (gpio > ath79_gpio_count) {
return -EINVAL;
}
return gpio;
}
EXPORT_SYMBOL(irq_to_gpio);