ARM: sa1111: implement a gpio_chip for SA1111 GPIOs

Add a gpio_chip instance for SA1111 GPIOs.  This allows us to use
gpiolib to lookup and manipulate SA1111 GPIOs.

Signed-off-by: Russell King <rmk+kernel@armlinux.org.uk>
This commit is contained in:
Russell King 2016-08-31 08:49:45 +01:00
parent ccb7d854d6
commit 17cf50116e
1 changed files with 166 additions and 0 deletions

View File

@ -15,6 +15,7 @@
* from machine specific code with proper arguments when required.
*/
#include <linux/module.h>
#include <linux/gpio/driver.h>
#include <linux/init.h>
#include <linux/irq.h>
#include <linux/kernel.h>
@ -107,6 +108,7 @@ struct sa1111 {
spinlock_t lock;
void __iomem *base;
struct sa1111_platform_data *pdata;
struct gpio_chip gc;
#ifdef CONFIG_PM
void *saved_state;
#endif
@ -527,6 +529,163 @@ static void sa1111_remove_irq(struct sa1111 *sachip)
}
}
enum {
SA1111_GPIO_PXDDR = (SA1111_GPIO_PADDR - SA1111_GPIO_PADDR),
SA1111_GPIO_PXDRR = (SA1111_GPIO_PADRR - SA1111_GPIO_PADDR),
SA1111_GPIO_PXDWR = (SA1111_GPIO_PADWR - SA1111_GPIO_PADDR),
SA1111_GPIO_PXSDR = (SA1111_GPIO_PASDR - SA1111_GPIO_PADDR),
SA1111_GPIO_PXSSR = (SA1111_GPIO_PASSR - SA1111_GPIO_PADDR),
};
static struct sa1111 *gc_to_sa1111(struct gpio_chip *gc)
{
return container_of(gc, struct sa1111, gc);
}
static void __iomem *sa1111_gpio_map_reg(struct sa1111 *sachip, unsigned offset)
{
void __iomem *reg = sachip->base + SA1111_GPIO;
if (offset < 4)
return reg + SA1111_GPIO_PADDR;
if (offset < 10)
return reg + SA1111_GPIO_PBDDR;
if (offset < 18)
return reg + SA1111_GPIO_PCDDR;
return NULL;
}
static u32 sa1111_gpio_map_bit(unsigned offset)
{
if (offset < 4)
return BIT(offset);
if (offset < 10)
return BIT(offset - 4);
if (offset < 18)
return BIT(offset - 10);
return 0;
}
static void sa1111_gpio_modify(void __iomem *reg, u32 mask, u32 set)
{
u32 val;
val = readl_relaxed(reg);
val &= ~mask;
val |= mask & set;
writel_relaxed(val, reg);
}
static int sa1111_gpio_get_direction(struct gpio_chip *gc, unsigned offset)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
u32 mask = sa1111_gpio_map_bit(offset);
return !!(readl_relaxed(reg + SA1111_GPIO_PXDDR) & mask);
}
static int sa1111_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
unsigned long flags;
void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
u32 mask = sa1111_gpio_map_bit(offset);
spin_lock_irqsave(&sachip->lock, flags);
sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, mask);
sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, mask);
spin_unlock_irqrestore(&sachip->lock, flags);
return 0;
}
static int sa1111_gpio_direction_output(struct gpio_chip *gc, unsigned offset,
int value)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
unsigned long flags;
void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
u32 mask = sa1111_gpio_map_bit(offset);
spin_lock_irqsave(&sachip->lock, flags);
sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
sa1111_gpio_modify(reg + SA1111_GPIO_PXDDR, mask, 0);
sa1111_gpio_modify(reg + SA1111_GPIO_PXSDR, mask, 0);
spin_unlock_irqrestore(&sachip->lock, flags);
return 0;
}
static int sa1111_gpio_get(struct gpio_chip *gc, unsigned offset)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
u32 mask = sa1111_gpio_map_bit(offset);
return !!(readl_relaxed(reg + SA1111_GPIO_PXDRR) & mask);
}
static void sa1111_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
unsigned long flags;
void __iomem *reg = sa1111_gpio_map_reg(sachip, offset);
u32 mask = sa1111_gpio_map_bit(offset);
spin_lock_irqsave(&sachip->lock, flags);
sa1111_gpio_modify(reg + SA1111_GPIO_PXDWR, mask, value ? mask : 0);
sa1111_gpio_modify(reg + SA1111_GPIO_PXSSR, mask, value ? mask : 0);
spin_unlock_irqrestore(&sachip->lock, flags);
}
static void sa1111_gpio_set_multiple(struct gpio_chip *gc, unsigned long *mask,
unsigned long *bits)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
unsigned long flags;
void __iomem *reg = sachip->base + SA1111_GPIO;
u32 msk, val;
msk = *mask;
val = *bits;
spin_lock_irqsave(&sachip->lock, flags);
sa1111_gpio_modify(reg + SA1111_GPIO_PADWR, msk & 15, val);
sa1111_gpio_modify(reg + SA1111_GPIO_PASSR, msk & 15, val);
sa1111_gpio_modify(reg + SA1111_GPIO_PBDWR, (msk >> 4) & 255, val >> 4);
sa1111_gpio_modify(reg + SA1111_GPIO_PBSSR, (msk >> 4) & 255, val >> 4);
sa1111_gpio_modify(reg + SA1111_GPIO_PCDWR, (msk >> 12) & 255, val >> 12);
sa1111_gpio_modify(reg + SA1111_GPIO_PCSSR, (msk >> 12) & 255, val >> 12);
spin_unlock_irqrestore(&sachip->lock, flags);
}
static int sa1111_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
{
struct sa1111 *sachip = gc_to_sa1111(gc);
return sachip->irq_base + offset;
}
static int sa1111_setup_gpios(struct sa1111 *sachip)
{
sachip->gc.label = "sa1111";
sachip->gc.parent = sachip->dev;
sachip->gc.owner = THIS_MODULE;
sachip->gc.get_direction = sa1111_gpio_get_direction;
sachip->gc.direction_input = sa1111_gpio_direction_input;
sachip->gc.direction_output = sa1111_gpio_direction_output;
sachip->gc.get = sa1111_gpio_get;
sachip->gc.set = sa1111_gpio_set;
sachip->gc.set_multiple = sa1111_gpio_set_multiple;
sachip->gc.to_irq = sa1111_gpio_to_irq;
sachip->gc.base = -1;
sachip->gc.ngpio = 18;
return devm_gpiochip_add_data(sachip->dev, &sachip->gc, sachip);
}
/*
* Bring the SA1111 out of reset. This requires a set procedure:
* 1. nRESET asserted (by hardware)
@ -773,6 +932,11 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
goto err_clk;
}
/* Setup the GPIOs - should really be done after the IRQ setup */
ret = sa1111_setup_gpios(sachip);
if (ret)
goto err_irq;
#ifdef CONFIG_ARCH_SA1100
{
unsigned int val;
@ -815,6 +979,8 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
return 0;
err_irq:
sa1111_remove_irq(sachip);
err_clk:
clk_disable(sachip->clk);
err_unmap: