[PATCH 3/3] mpc8xxx_gpio: add interrupt support
Peter Korsgaard
jacmet at sunsite.dk
Wed Dec 9 18:33:00 EST 2009
>>>>> "Peter" == Peter Korsgaard <jacmet at sunsite.dk> writes:
Comments?
Peter> Signed-off-by: Peter Korsgaard <jacmet at sunsite.dk>
Peter> ---
Peter> arch/powerpc/sysdev/mpc8xxx_gpio.c | 147 ++++++++++++++++++++++++++++++++++++
Peter> 1 files changed, 147 insertions(+), 0 deletions(-)
Peter> diff --git a/arch/powerpc/sysdev/mpc8xxx_gpio.c b/arch/powerpc/sysdev/mpc8xxx_gpio.c
Peter> index 103eace..b46f28b 100644
Peter> --- a/arch/powerpc/sysdev/mpc8xxx_gpio.c
Peter> +++ b/arch/powerpc/sysdev/mpc8xxx_gpio.c
Peter> @@ -15,6 +15,7 @@
Peter> #include <linux/of.h>
Peter> #include <linux/of_gpio.h>
Peter> #include <linux/gpio.h>
Peter> +#include <linux/irq.h>
Peter> #define MPC8XXX_GPIO_PINS 32
Peter> @@ -34,6 +35,7 @@ struct mpc8xxx_gpio_chip {
Peter> * open drain mode safely
Peter> */
Peter> u32 data;
Peter> + struct irq_host *irq;
Peter> };
Peter> static inline u32 mpc8xxx_gpio2mask(unsigned int gpio)
Peter> @@ -111,12 +113,136 @@ static int mpc8xxx_gpio_dir_out(struct gpio_chip *gc, unsigned int gpio, int val
Peter> return 0;
Peter> }
Peter> +static int mpc8xxx_gpio_to_irq(struct gpio_chip *gc, unsigned offset)
Peter> +{
Peter> + struct of_mm_gpio_chip *mm = to_of_mm_gpio_chip(gc);
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = to_mpc8xxx_gpio_chip(mm);
Peter> +
Peter> + if (mpc8xxx_gc->irq && offset < MPC8XXX_GPIO_PINS)
Peter> + return irq_create_mapping(mpc8xxx_gc->irq, offset);
Peter> + else
Peter> + return -ENXIO;
Peter> +}
Peter> +
Peter> +static void mpc8xxx_gpio_irq_cascade(unsigned int irq, struct irq_desc *desc)
Peter> +{
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_desc_data(desc);
Peter> + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
Peter> + unsigned int mask;
Peter> +
Peter> + mask = in_be32(mm->regs + GPIO_IER) & in_be32(mm->regs + GPIO_IMR);
Peter> + if (mask)
Peter> + generic_handle_irq(irq_linear_revmap(mpc8xxx_gc->irq,
Peter> + 32 - ffs(mask)));
Peter> +}
Peter> +
Peter> +static void mpc8xxx_irq_unmask(unsigned int virq)
Peter> +{
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
Peter> + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
Peter> + unsigned long flags;
Peter> +
Peter> + spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
Peter> +
Peter> + setbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(virq_to_hw(virq)));
Peter> +
Peter> + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
Peter> +}
Peter> +
Peter> +static void mpc8xxx_irq_mask(unsigned int virq)
Peter> +{
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
Peter> + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
Peter> + unsigned long flags;
Peter> +
Peter> + spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
Peter> +
Peter> + clrbits32(mm->regs + GPIO_IMR, mpc8xxx_gpio2mask(virq_to_hw(virq)));
Peter> +
Peter> + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
Peter> +}
Peter> +
Peter> +static void mpc8xxx_irq_ack(unsigned int virq)
Peter> +{
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
Peter> + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
Peter> +
Peter> + out_be32(mm->regs + GPIO_IER, mpc8xxx_gpio2mask(virq_to_hw(virq)));
Peter> +}
Peter> +
Peter> +static int mpc8xxx_irq_set_type(unsigned int virq, unsigned int flow_type)
Peter> +{
Peter> + struct mpc8xxx_gpio_chip *mpc8xxx_gc = get_irq_chip_data(virq);
Peter> + struct of_mm_gpio_chip *mm = &mpc8xxx_gc->mm_gc;
Peter> + unsigned long flags;
Peter> +
Peter> + switch (flow_type) {
Peter> + case IRQ_TYPE_EDGE_FALLING:
Peter> + spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
Peter> + setbits32(mm->regs + GPIO_ICR,
Peter> + mpc8xxx_gpio2mask(virq_to_hw(virq)));
Peter> + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
Peter> + break;
Peter> +
Peter> + case IRQ_TYPE_EDGE_BOTH:
Peter> + spin_lock_irqsave(&mpc8xxx_gc->lock, flags);
Peter> + clrbits32(mm->regs + GPIO_ICR,
Peter> + mpc8xxx_gpio2mask(virq_to_hw(virq)));
Peter> + spin_unlock_irqrestore(&mpc8xxx_gc->lock, flags);
Peter> + break;
Peter> +
Peter> + default:
Peter> + return -EINVAL;
Peter> + }
Peter> +
Peter> + return 0;
Peter> +}
Peter> +
Peter> +static struct irq_chip mpc8xxx_irq_chip = {
Peter> + .name = "mpc8xxx-gpio",
Peter> + .unmask = mpc8xxx_irq_unmask,
Peter> + .mask = mpc8xxx_irq_mask,
Peter> + .ack = mpc8xxx_irq_ack,
Peter> + .set_type = mpc8xxx_irq_set_type,
Peter> +};
Peter> +
Peter> +static int mpc8xxx_gpio_irq_map(struct irq_host *h, unsigned int virq,
Peter> + irq_hw_number_t hw)
Peter> +{
Peter> + set_irq_chip_data(virq, h->host_data);
Peter> + set_irq_chip_and_handler(virq, &mpc8xxx_irq_chip, handle_level_irq);
Peter> + set_irq_type(virq, IRQ_TYPE_NONE);
Peter> +
Peter> + return 0;
Peter> +}
Peter> +
Peter> +static int mpc8xxx_gpio_irq_xlate(struct irq_host *h, struct device_node *ct,
Peter> + u32 *intspec, unsigned int intsize,
Peter> + irq_hw_number_t *out_hwirq,
Peter> + unsigned int *out_flags)
Peter> +
Peter> +{
Peter> + /* interrupt sense values coming from the device tree equal either
Peter> + * EDGE_FALLING or EDGE_BOTH
Peter> + */
Peter> + *out_hwirq = intspec[0];
Peter> + *out_flags = intspec[1];
Peter> +
Peter> + return 0;
Peter> +}
Peter> +
Peter> +static struct irq_host_ops mpc8xxx_gpio_irq_ops = {
Peter> + .map = mpc8xxx_gpio_irq_map,
Peter> + .xlate = mpc8xxx_gpio_irq_xlate,
Peter> +};
Peter> +
Peter> static void __init mpc8xxx_add_controller(struct device_node *np)
Peter> {
Peter> struct mpc8xxx_gpio_chip *mpc8xxx_gc;
Peter> struct of_mm_gpio_chip *mm_gc;
Peter> struct of_gpio_chip *of_gc;
Peter> struct gpio_chip *gc;
Peter> + unsigned hwirq;
Peter> int ret;
Peter> mpc8xxx_gc = kzalloc(sizeof(*mpc8xxx_gc), GFP_KERNEL);
Peter> @@ -138,11 +264,32 @@ static void __init mpc8xxx_add_controller(struct device_node *np)
gc-> direction_output = mpc8xxx_gpio_dir_out;
gc-> get = mpc8xxx_gpio_get;
gc-> set = mpc8xxx_gpio_set;
Peter> + gc->to_irq = mpc8xxx_gpio_to_irq;
Peter> ret = of_mm_gpiochip_add(np, mm_gc);
Peter> if (ret)
Peter> goto err;
Peter> + hwirq = irq_of_parse_and_map(np, 0);
Peter> + if (hwirq == NO_IRQ)
Peter> + goto skip_irq;
Peter> +
Peter> + mpc8xxx_gc->irq =
Peter> + irq_alloc_host(np, IRQ_HOST_MAP_LINEAR, MPC8XXX_GPIO_PINS,
Peter> + &mpc8xxx_gpio_irq_ops, MPC8XXX_GPIO_PINS);
Peter> + if (!mpc8xxx_gc->irq)
Peter> + goto skip_irq;
Peter> +
Peter> + mpc8xxx_gc->irq->host_data = mpc8xxx_gc;
Peter> +
Peter> + /* ack and mask all irqs */
Peter> + out_be32(mm_gc->regs + GPIO_IER, 0xffffffff);
Peter> + out_be32(mm_gc->regs + GPIO_IMR, 0);
Peter> +
Peter> + set_irq_data(hwirq, mpc8xxx_gc);
Peter> + set_irq_chained_handler(hwirq, mpc8xxx_gpio_irq_cascade);
Peter> +
Peter> +skip_irq:
Peter> return;
Peter> err:
Peter> --
Peter> 1.6.5
--
Bye, Peter Korsgaard
More information about the Linuxppc-dev
mailing list