Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 17cf5011 authored by Russell King's avatar Russell King
Browse files

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: default avatarRussell King <rmk+kernel@armlinux.org.uk>
parent ccb7d854
Loading
Loading
Loading
Loading
+166 −0
Original line number Diff line number Diff line
@@ -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: