lkml.org 
[lkml]   [2010]   [Jun]   [11]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/2 v2] gpio: msm7200a: Add irq support to msm-gpiolib.
    Date
    Signed-off-by: Gregory Bean <gbean@codeaurora.org>
    ---
    drivers/gpio/msm7200a-gpio.c | 203 ++++++++++++++++++++++++++++++++++++++++-
    include/linux/msm7200a-gpio.h | 7 ++
    2 files changed, 209 insertions(+), 1 deletions(-)

    diff --git a/drivers/gpio/msm7200a-gpio.c b/drivers/gpio/msm7200a-gpio.c
    index 62db753..05c367d 100644
    --- a/drivers/gpio/msm7200a-gpio.c
    +++ b/drivers/gpio/msm7200a-gpio.c
    @@ -23,13 +23,25 @@
    #include <linux/kernel.h>
    #include <linux/gpio.h>
    #include <linux/io.h>
    +#include <linux/interrupt.h>
    +#include <linux/irq.h>
    #include <linux/module.h>
    #include <linux/platform_device.h>
    #include <linux/msm7200a-gpio.h>

    +/*
    + * The INT_STATUS register latches both edge- and level-detection events,
    + * which is atypical. Turning on DONT_LATCH_LEVEL_IRQS causes level irq
    + * triggers to be forgotten across mask/unmask calls, emulating a more
    + * traditional setup.
    + */
    +#define MSM_GPIO_DONT_LATCH_LEVEL_IRQS 1
    +
    struct msm_gpio_dev {
    struct gpio_chip gpio_chip;
    spinlock_t lock;
    + unsigned irq_base;
    + unsigned irq_summary;
    struct msm7200a_gpio_regs regs;
    };

    @@ -119,12 +131,160 @@ static void gpio_chip_set(struct gpio_chip *chip, unsigned offset, int value)
    spin_unlock_irqrestore(&msm_gpio->lock, irq_flags);
    }

    +static int gpio_chip_to_irq(struct gpio_chip *chip, unsigned offset)
    +{
    + struct msm_gpio_dev *msm_gpio = TO_MSM_GPIO_DEV(chip);
    + return msm_gpio->irq_base + offset;
    +}
    +
    +#if MSM_GPIO_DONT_LATCH_LEVEL_IRQS
    +static inline void forget_level_irq(struct msm_gpio_dev *msm_gpio,
    + unsigned offset)
    +{
    + unsigned v = readl(msm_gpio->regs.int_edge);
    + unsigned b = bit(offset);
    +
    + if (!(v & b))
    + writel(b, msm_gpio->regs.int_clear);
    +
    +}
    +#else
    +static inline void forget_level_irq(struct msm_gpio_dev *msm, unsigned off)
    +{
    +}
    +#endif
    +
    +static void msm_gpio_irq_mask(unsigned int irq)
    +{
    + unsigned long irq_flags;
    + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq);
    + unsigned offset = irq - msm_gpio->irq_base;
    +
    + spin_lock_irqsave(&msm_gpio->lock, irq_flags);
    + forget_level_irq(msm_gpio, offset);
    + clr_gpio_bit(offset, msm_gpio->regs.int_en);
    + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags);
    +}
    +
    +static void msm_gpio_irq_unmask(unsigned int irq)
    +{
    + unsigned long irq_flags;
    + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq);
    + unsigned offset = irq - msm_gpio->irq_base;
    +
    + spin_lock_irqsave(&msm_gpio->lock, irq_flags);
    + forget_level_irq(msm_gpio, offset);
    + set_gpio_bit(offset, msm_gpio->regs.int_en);
    + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags);
    +}
    +
    +static int msm_gpio_irq_set_type(unsigned int irq, unsigned int flow_type)
    +{
    + unsigned long irq_flags;
    + struct msm_gpio_dev *msm_gpio = get_irq_chip_data(irq);
    + unsigned offset = irq - msm_gpio->irq_base;
    +
    + if ((flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) ==
    + (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING))
    + return -ENOTSUPP;
    +
    + if ((flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW)) ==
    + (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_LOW))
    + return -ENOTSUPP;
    +
    + spin_lock_irqsave(&msm_gpio->lock, irq_flags);
    +
    + if (flow_type & (IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING)) {
    + set_gpio_bit(offset, msm_gpio->regs.int_edge);
    + irq_desc[irq].handle_irq = handle_edge_irq;
    + } else {
    + clr_gpio_bit(offset, msm_gpio->regs.int_edge);
    + irq_desc[irq].handle_irq = handle_level_irq;
    + }
    +
    + if (flow_type & (IRQF_TRIGGER_HIGH | IRQF_TRIGGER_RISING))
    + set_gpio_bit(offset, msm_gpio->regs.int_pos);
    + else
    + clr_gpio_bit(offset, msm_gpio->regs.int_pos);
    +
    + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags);
    +
    + return 0;
    +}
    +
    +static void msm_gpio_irq_mask_ack(unsigned int irq)
    +{
    + msm_gpio_irq_mask(irq);
    +}
    +
    +static int msm_gpio_irq_set_affinity(unsigned int irq,
    + const struct cpumask *dest)
    +{
    + return -ENOTSUPP;
    +}
    +
    +static int msm_gpio_irq_retrigger(unsigned int irq)
    +{
    + return -ENOTSUPP;
    +}
    +
    +static int msm_gpio_irq_set_wake(unsigned int irq, unsigned int on)
    +{
    + return -ENOTSUPP;
    +}
    +
    +static irqreturn_t msm_gpio_irq_handler(int irq, void *dev)
    +{
    + unsigned long irq_flags;
    + int b, m;
    + unsigned e, s, v;
    +
    + struct msm_gpio_dev *msm_gpio = (struct msm_gpio_dev *)dev;
    +
    + /*
    + * The int_status register latches trigger events whether or not
    + * the gpio line is enabled as an interrupt source. Therefore,
    + * the set of pins which defines the interrupts which need to fire
    + * is the intersection of int_status and int_en - int_status
    + * alone provides an incomplete picture.
    + */
    + spin_lock_irqsave(&msm_gpio->lock, irq_flags);
    + s = readl(msm_gpio->regs.int_status);
    + e = readl(msm_gpio->regs.int_en);
    + v = s & e;
    + if (v)
    + writel(v, msm_gpio->regs.int_clear);
    + spin_unlock_irqrestore(&msm_gpio->lock, irq_flags);
    +
    + if (!v)
    + return IRQ_NONE;
    +
    + while (v) {
    + m = v & -v;
    + b = fls(m) - 1;
    + v &= ~m;
    + generic_handle_irq(msm_gpio->irq_base + b);
    + }
    + return IRQ_HANDLED;
    +}
    +
    +static struct irq_chip msm_gpio_irq_chip = {
    + .name = "msm_gpio",
    + .mask = msm_gpio_irq_mask,
    + .mask_ack = msm_gpio_irq_mask_ack,
    + .unmask = msm_gpio_irq_unmask,
    + .set_affinity = msm_gpio_irq_set_affinity,
    + .retrigger = msm_gpio_irq_retrigger,
    + .set_type = msm_gpio_irq_set_type,
    + .set_wake = msm_gpio_irq_set_wake,
    +};
    +
    static int msm_gpio_probe(struct platform_device *dev)
    {
    struct msm_gpio_dev *msm_gpio;
    struct msm7200a_gpio_platform_data *pdata =
    (struct msm7200a_gpio_platform_data *)dev->dev.platform_data;
    - int ret;
    + int i, irq, ret;

    if (!pdata)
    return -EINVAL;
    @@ -146,12 +306,52 @@ static int msm_gpio_probe(struct platform_device *dev)
    msm_gpio->gpio_chip.direction_output = gpio_chip_direction_output;
    msm_gpio->gpio_chip.get = gpio_chip_get;
    msm_gpio->gpio_chip.set = gpio_chip_set;
    + msm_gpio->gpio_chip.to_irq = gpio_chip_to_irq;
    + msm_gpio->irq_base = pdata->irq_base;
    + msm_gpio->irq_summary = pdata->irq_summary;

    ret = gpiochip_add(&msm_gpio->gpio_chip);
    if (ret < 0)
    goto err_post_malloc;

    + for (i = 0; i < msm_gpio->gpio_chip.ngpio; ++i) {
    + irq = msm_gpio->irq_base + i;
    + set_irq_chip_data(irq, msm_gpio);
    + set_irq_chip(irq, &msm_gpio_irq_chip);
    + set_irq_handler(irq, handle_level_irq);
    + set_irq_flags(irq, IRQF_VALID);
    + }
    +
    + /*
    + * We use a level-triggered interrupt because of the nature
    + * of the shared GPIO-group interrupt.
    + *
    + * Many GPIO chips may be sharing the same group IRQ line, and
    + * it is possible for GPIO interrupt to re-occur while the system
    + * is still servicing the group interrupt associated with it.
    + * The group IRQ line would not de-assert and re-assert, and
    + * we'd get no second edge to cause the group IRQ to be handled again.
    + *
    + * Using a level interrupt guarantees that the group IRQ handlers
    + * will continue to be called as long as any GPIO chip in the group
    + * is asserting, even if the condition began while the group
    + * handler was in mid-pass.
    + */
    + ret = request_irq(msm_gpio->irq_summary,
    + msm_gpio_irq_handler,
    + IRQF_SHARED | IRQF_TRIGGER_HIGH,
    + dev->name,
    + msm_gpio);
    + if (ret < 0)
    + goto err_post_gpiochip_add;
    +
    return ret;
    +err_post_gpiochip_add:
    + /*
    + * Under no circumstances should a line be held on a gpiochip
    + * which hasn't finished probing.
    + */
    + BUG_ON(gpiochip_remove(&msm_gpio->gpio_chip) < 0);
    err_post_malloc:
    kfree(msm_gpio);
    return ret;
    @@ -165,6 +365,7 @@ static int msm_gpio_remove(struct platform_device *dev)
    if (ret < 0)
    return ret;

    + free_irq(msm_gpio->irq_summary, msm_gpio);
    kfree(msm_gpio);

    return 0;
    diff --git a/include/linux/msm7200a-gpio.h b/include/linux/msm7200a-gpio.h
    index 3f1ef38..7af4dd6 100644
    --- a/include/linux/msm7200a-gpio.h
    +++ b/include/linux/msm7200a-gpio.h
    @@ -33,11 +33,18 @@ struct msm7200a_gpio_regs {
    void __iomem *in;
    void __iomem *out;
    void __iomem *oe;
    + void __iomem *int_status;
    + void __iomem *int_clear;
    + void __iomem *int_en;
    + void __iomem *int_edge;
    + void __iomem *int_pos;
    };

    struct msm7200a_gpio_platform_data {
    unsigned gpio_base;
    unsigned ngpio;
    + unsigned irq_base;
    + unsigned irq_summary;
    struct msm7200a_gpio_regs regs;
    };

    --
    1.7.0.4
    --
    Employee of Qualcomm Innovation Center, Inc.
    Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum.


    \
     
     \ /
      Last update: 2010-06-12 02:21    [W:0.034 / U:123.556 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site