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:
parent
ccb7d854d6
commit
17cf50116e
|
@ -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:
|
||||
|
|
Loading…
Reference in New Issue