powerpc: Add interrupt support to mpc8xxx_gpio
Signed-off-by: Peter Korsgaard <jacmet@sunsite.dk> Acked-by: Anton Vorontsov <avorontsov@ru.mvista.com> Signed-off-by: Kumar Gala <galak@kernel.crashing.org>
This commit is contained in:
parent
1ed31d6db9
commit
345e5c8a1c
2 changed files with 168 additions and 1 deletions
|
@ -11,7 +11,7 @@ Required properties:
|
||||||
83xx, "fsl,mpc8572-gpio" for 85xx and "fsl,mpc8610-gpio" for 86xx.
|
83xx, "fsl,mpc8572-gpio" for 85xx and "fsl,mpc8610-gpio" for 86xx.
|
||||||
- #gpio-cells : Should be two. The first cell is the pin number and the
|
- #gpio-cells : Should be two. The first cell is the pin number and the
|
||||||
second cell is used to specify optional parameters (currently unused).
|
second cell is used to specify optional parameters (currently unused).
|
||||||
- interrupts : Interrupt mapping for GPIO IRQ (currently unused).
|
- interrupts : Interrupt mapping for GPIO IRQ.
|
||||||
- interrupt-parent : Phandle for the interrupt controller that
|
- interrupt-parent : Phandle for the interrupt controller that
|
||||||
services interrupts for this device.
|
services interrupts for this device.
|
||||||
- gpio-controller : Marks the port as GPIO controller.
|
- gpio-controller : Marks the port as GPIO controller.
|
||||||
|
@ -38,3 +38,23 @@ Example of gpio-controller nodes for a MPC8347 SoC:
|
||||||
|
|
||||||
See booting-without-of.txt for details of how to specify GPIO
|
See booting-without-of.txt for details of how to specify GPIO
|
||||||
information for devices.
|
information for devices.
|
||||||
|
|
||||||
|
To use GPIO pins as interrupt sources for peripherals, specify the
|
||||||
|
GPIO controller as the interrupt parent and define GPIO number +
|
||||||
|
trigger mode using the interrupts property, which is defined like
|
||||||
|
this:
|
||||||
|
|
||||||
|
interrupts = <number trigger>, where:
|
||||||
|
- number: GPIO pin (0..31)
|
||||||
|
- trigger: trigger mode:
|
||||||
|
2 = trigger on falling edge
|
||||||
|
3 = trigger on both edges
|
||||||
|
|
||||||
|
Example of device using this is:
|
||||||
|
|
||||||
|
funkyfpga@0 {
|
||||||
|
compatible = "funky-fpga";
|
||||||
|
...
|
||||||
|
interrupts = <4 3>;
|
||||||
|
interrupt-parent = <&gpio1>;
|
||||||
|
};
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/of_gpio.h>
|
#include <linux/of_gpio.h>
|
||||||
#include <linux/gpio.h>
|
#include <linux/gpio.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/irq.h>
|
||||||
|
|
||||||
#define MPC8XXX_GPIO_PINS 32
|
#define MPC8XXX_GPIO_PINS 32
|
||||||
|
|
||||||
|
@ -35,6 +36,7 @@ struct mpc8xxx_gpio_chip {
|
||||||
* open drain mode safely
|
* open drain mode safely
|
||||||
*/
|
*/
|
||||||
u32 data;
|
u32 data;
|
||||||
|
struct irq_host *irq;
|
||||||
};
|
};
|
||||||
|
|
||||||
static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
|
static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
|
||||||
|
@ -128,12 +130,136 @@ static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
|
||||||
|
{
|
||||||
|
struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
|
||||||
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
|
||||||
|
|
||||||
|
if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS)
|
||||||
|
return irq_create_mapping(mpc8xxx_gc->irq, offset);
|
||||||
|
else
|
||||||
|
return -ENXIO;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc)
|
||||||
|
{
|
||||||
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_desc_data(desc);
|
||||||
|
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
|
||||||
|
unsigned int mask;
|
||||||
|
|
||||||
|
mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR);
|
||||||
|
if (mask)
|
||||||
|
generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
|
||||||
|
32 - ffs(mask)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mpc8xxx_irq_unmask(unsigned int virq)
|
||||||
|
{
|
||||||
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
|
||||||
|
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
|
||||||
|
|
||||||
|
setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(virq_to_hw(virq)));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mpc8xxx_irq_mask(unsigned int virq)
|
||||||
|
{
|
||||||
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
|
||||||
|
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
|
||||||
|
unsigned long flags;
|
||||||
|
|
||||||
|
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
|
||||||
|
|
||||||
|
clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(virq_to_hw(virq)));
|
||||||
|
|
||||||
|
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void mpc8xxx_irq_ack(unsigned int virq)
|
||||||
|
{
|
||||||
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
|
||||||
|
struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
|
||||||
|
|
||||||
|
out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(virq_to_hw(virq)));
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mpc8xxx_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 flags;
|
||||||
|
|
||||||
|
switch (flow_type) {
|
||||||
|
case IRQ_TYPE_EDGE_FALLING:
|
||||||
|
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
|
||||||
|
setbits32(mm->regs + GPIO_ICR,
|
||||||
|
mpc8xxx_gpio2mask(virq_to_hw(virq)));
|
||||||
|
spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case IRQ_TYPE_EDGE_BOTH:
|
||||||
|
spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
|
||||||
|
clrbits32(mm->regs + GPIO_ICR,
|
||||||
|
mpc8xxx_gpio2mask(virq_to_hw(virq)));
|
||||||
|
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,
|
||||||
|
.mask = mpc8xxx_irq_mask,
|
||||||
|
.ack = mpc8xxx_irq_ack,
|
||||||
|
.set_type = mpc8xxx_irq_set_type,
|
||||||
|
};
|
||||||
|
|
||||||
|
static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
|
||||||
|
irq_hw_number_t hw)
|
||||||
|
{
|
||||||
|
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);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int mpc8xxx_gpio_irq_xlate(struct irq_host *h, struct device_node *ct,
|
||||||
|
const u32 *intspec, unsigned int intsize,
|
||||||
|
irq_hw_number_t *out_hwirq,
|
||||||
|
unsigned int *out_flags)
|
||||||
|
|
||||||
|
{
|
||||||
|
/* interrupt sense values coming from the device tree equal either
|
||||||
|
* EDGE_FALLING or EDGE_BOTH
|
||||||
|
*/
|
||||||
|
*out_hwirq = intspec[0];
|
||||||
|
*out_flags = intspec[1];
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static struct irq_host_ops mpc8xxx_gpio_irq_ops = {
|
||||||
|
.map = mpc8xxx_gpio_irq_map,
|
||||||
|
.xlate = mpc8xxx_gpio_irq_xlate,
|
||||||
|
};
|
||||||
|
|
||||||
static void __init mpc8xxx_add_controller(struct device_node *np)
|
static void __init mpc8xxx_add_controller(struct device_node *np)
|
||||||
{
|
{
|
||||||
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
|
struct mpc8xxx_gpio_chip *mpc8xxx_gc;
|
||||||
struct of_mm_gpio_chip *mm_gc;
|
struct of_mm_gpio_chip *mm_gc;
|
||||||
struct of_gpio_chip *of_gc;
|
struct of_gpio_chip *of_gc;
|
||||||
struct gpio_chip *gc;
|
struct gpio_chip *gc;
|
||||||
|
unsigned hwirq;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL);
|
mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL);
|
||||||
|
@ -158,11 +284,32 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
|
||||||
else
|
else
|
||||||
gc->get = mpc8xxx_gpio_get;
|
gc->get = mpc8xxx_gpio_get;
|
||||||
gc->set = mpc8xxx_gpio_set;
|
gc->set = mpc8xxx_gpio_set;
|
||||||
|
gc->to_irq = mpc8xxx_gpio_to_irq;
|
||||||
|
|
||||||
ret = of_mm_gpiochip_add(np, mm_gc);
|
ret = of_mm_gpiochip_add(np, mm_gc);
|
||||||
if (ret)
|
if (ret)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
|
hwirq = irq_of_parse_and_map(np, 0);
|
||||||
|
if (hwirq == NO_IRQ)
|
||||||
|
goto skip_irq;
|
||||||
|
|
||||||
|
mpc8xxx_gc->irq =
|
||||||
|
irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS,
|
||||||
|
&mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS);
|
||||||
|
if (!mpc8xxx_gc->irq)
|
||||||
|
goto skip_irq;
|
||||||
|
|
||||||
|
mpc8xxx_gc->irq->host_data = mpc8xxx_gc;
|
||||||
|
|
||||||
|
/* ack and mask all irqs */
|
||||||
|
out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
|
||||||
|
out_be32(mm_gc->regs + GPIO_IMR, 0);
|
||||||
|
|
||||||
|
set_irq_data(hwirq, mpc8xxx_gc);
|
||||||
|
set_irq_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade);
|
||||||
|
|
||||||
|
skip_irq:
|
||||||
return;
|
return;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
|
|
Loading…
Add table
Reference in a new issue