summaryrefslogtreecommitdiffstats
path: root/arch/powerpc/sysdev/mpc8xxx_gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/powerpc/sysdev/mpc8xxx_gpio.c')
-rw-r--r--arch/powerpc/sysdev/mpc8xxx_gpio.c75
1 files changed, 67 insertions, 8 deletions
diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/arch/powerpc/sysdev/mpc8xxx_gpio.c
index 2b69084d0f0c..36499394a161 100644
--- a/arch/powerpc/sysdev/mpc8xxx_gpio.c
+++ b/arch/powerpc/sysdev/mpc8xxx_gpio.c
@@ -1,5 +1,5 @@
/*
- * GPIOs on MPC8349/8572/8610 and compatible
+ * GPIOs on MPC512x/8349/8572/8610 and compatible
*
* Copyright (C) 2008 Peter Korsgaard <jacmet@sunsite.dk>
*
@@ -26,6 +26,7 @@
#define GPIO_IER 0x0c
#define GPIO_IMR 0x10
#define GPIO_ICR 0x14
+#define GPIO_ICR2 0x18
struct mpc8xxx_gpio_chip {
struct of_mm_gpio_chip mm_gc;
@@ -37,6 +38,7 @@ struct mpc8xxx_gpio_chip {
*/
u32 data;
struct irq_host *irq;
+ void *of_dev_id_data;
};
static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
@@ -215,6 +217,51 @@ static int mpc8xxx_irq_set_type(unsigned int virq, unsigned int flow_type)
return 0;
}
+static int mpc512x_irq_set_type(unsigned int virq, unsigned int flow_type)
+{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
+ struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
+ unsigned long gpio = virq_to_hw(virq);
+ void __iomem *reg;
+ unsigned int shift;
+ unsigned long flags;
+
+ if (gpio < 16) {
+ reg = mm->regs + GPIO_ICR;
+ shift = (15 - gpio) * 2;
+ } else {
+ reg = mm->regs + GPIO_ICR2;
+ shift = (15 - (gpio % 16)) * 2;
+ }
+
+ switch (flow_type) {
+ case IRQ_TYPE_EDGE_FALLING:
+ case IRQ_TYPE_LEVEL_LOW:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 2 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_RISING:
+ case IRQ_TYPE_LEVEL_HIGH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrsetbits_be32(reg, 3 << shift, 1 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ case IRQ_TYPE_EDGE_BOTH:
+ spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
+ clrbits32(reg, 3 << shift);
+ spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
static struct irq_chip mpc8xxx_irq_chip = {
.name = "mpc8xxx-gpio",
.unmask = mpc8xxx_irq_unmask,
@@ -226,6 +273,11 @@ static struct irq_chip mpc8xxx_irq_chip = {
static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
irq_hw_number_t hw)
{
+ struct mpc8xxx_gpio_chip *mpc8xxx_gc = h->host_data;
+
+ if (mpc8xxx_gc->of_dev_id_data)
+ mpc8xxx_irq_chip.set_type = mpc8xxx_gc->of_dev_id_data;
+
set_irq_chip_data(virq, h->host_data);
set_irq_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq);
set_irq_type(virq, IRQ_TYPE_NONE);
@@ -253,11 +305,20 @@ static struct irq_host_ops mpc8xxx_gpio_irq_ops = {
.xlate = mpc8xxx_gpio_irq_xlate,
};
+static struct of_device_id mpc8xxx_gpio_ids[] __initdata = {
+ { .compatible = "fsl,mpc8349-gpio", },
+ { .compatible = "fsl,mpc8572-gpio", },
+ { .compatible = "fsl,mpc8610-gpio", },
+ { .compatible = "fsl,mpc5121-gpio", .data = mpc512x_irq_set_type, },
+ {}
+};
+
static void __init mpc8xxx_add_controller(struct device_node *np)
{
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
struct of_mm_gpio_chip *mm_gc;
struct gpio_chip *gc;
+ const struct of_device_id *id;
unsigned hwirq;
int ret;
@@ -297,6 +358,10 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
if (!mpc8xxx_gc->irq)
goto skip_irq;
+ id = of_match_node(mpc8xxx_gpio_ids, np);
+ if (id)
+ mpc8xxx_gc->of_dev_id_data = id->data;
+
mpc8xxx_gc->irq->host_data = mpc8xxx_gc;
/* ack and mask all irqs */
@@ -321,13 +386,7 @@ static int __init mpc8xxx_add_gpiochips(void)
{
struct device_node *np;
- for_each_compatible_node(np, NULL, "fsl,mpc8349-gpio")
- mpc8xxx_add_controller(np);
-
- for_each_compatible_node(np, NULL, "fsl,mpc8572-gpio")
- mpc8xxx_add_controller(np);
-
- for_each_compatible_node(np, NULL, "fsl,mpc8610-gpio")
+ for_each_matching_node(np, mpc8xxx_gpio_ids)
mpc8xxx_add_controller(np);
return 0;