|
@@ -15,6 +15,7 @@
|
|
* from machine specific code with proper arguments when required.
|
|
* from machine specific code with proper arguments when required.
|
|
*/
|
|
*/
|
|
#include <linux/module.h>
|
|
#include <linux/module.h>
|
|
|
|
+#include <linux/gpio/driver.h>
|
|
#include <linux/init.h>
|
|
#include <linux/init.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/irq.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/kernel.h>
|
|
@@ -107,6 +108,7 @@ struct sa1111 {
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
void __iomem *base;
|
|
void __iomem *base;
|
|
struct sa1111_platform_data *pdata;
|
|
struct sa1111_platform_data *pdata;
|
|
|
|
+ struct gpio_chip gc;
|
|
#ifdef CONFIG_PM
|
|
#ifdef CONFIG_PM
|
|
void *saved_state;
|
|
void *saved_state;
|
|
#endif
|
|
#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:
|
|
* Bring the SA1111 out of reset. This requires a set procedure:
|
|
* 1. nRESET asserted (by hardware)
|
|
* 1. nRESET asserted (by hardware)
|
|
@@ -773,6 +932,11 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
|
|
goto err_clk;
|
|
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
|
|
#ifdef CONFIG_ARCH_SA1100
|
|
{
|
|
{
|
|
unsigned int val;
|
|
unsigned int val;
|
|
@@ -815,6 +979,8 @@ static int __sa1111_probe(struct device *me, struct resource *mem, int irq)
|
|
|
|
|
|
return 0;
|
|
return 0;
|
|
|
|
|
|
|
|
+ err_irq:
|
|
|
|
+ sa1111_remove_irq(sachip);
|
|
err_clk:
|
|
err_clk:
|
|
clk_disable(sachip->clk);
|
|
clk_disable(sachip->clk);
|
|
err_unmap:
|
|
err_unmap:
|