regmap: irq: add support for configuration of trigger type
authorLaxman Dewangan <ldewangan@nvidia.com>
Tue, 22 Dec 2015 12:55:26 +0000 (18:25 +0530)
committerMark Brown <broonie@kernel.org>
Tue, 5 Jan 2016 17:47:18 +0000 (17:47 +0000)
Some of devices supports the trigger level for interrupt
like rising/falling edge specially for GPIOs. The interrupt
support of such devices may have uses the generic regmap irq
framework for implementation.

Add support to configure the trigger type device interrupt
register via regmap-irq framework. The regmap-irq framework
configures the trigger register only if the details of trigger
type registers are provided.

[Fixed use of terery operator for legibility -- broonie]

Signed-off-by: Laxman Dewangan <ldewangan@nvidia.com>
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/base/regmap/regmap-irq.c
include/linux/regmap.h

index 8d16db533527362efa638f67b69673607d069327..dbe7a0d15a26693db214e1270be5f470796e0b6b 100644 (file)
@@ -39,8 +39,11 @@ struct regmap_irq_chip_data {
        unsigned int *mask_buf;
        unsigned int *mask_buf_def;
        unsigned int *wake_buf;
+       unsigned int *type_buf;
+       unsigned int *type_buf_def;
 
        unsigned int irq_reg_stride;
+       unsigned int type_reg_stride;
 };
 
 static inline const
@@ -144,6 +147,22 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
                }
        }
 
+       for (i = 0; i < d->chip->num_type_reg; i++) {
+               if (!d->type_buf_def[i])
+                       continue;
+               reg = d->chip->type_base +
+                       (i * map->reg_stride * d->type_reg_stride);
+               if (d->chip->type_invert)
+                       ret = regmap_update_bits(d->map, reg,
+                               d->type_buf_def[i], ~d->type_buf[i]);
+               else
+                       ret = regmap_update_bits(d->map, reg,
+                               d->type_buf_def[i], d->type_buf[i]);
+               if (ret != 0)
+                       dev_err(d->map->dev, "Failed to sync type in %x\n",
+                               reg);
+       }
+
        if (d->chip->runtime_pm)
                pm_runtime_put(map->dev);
 
@@ -178,6 +197,38 @@ static void regmap_irq_disable(struct irq_data *data)
        d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
 }
 
+static int regmap_irq_set_type(struct irq_data *data, unsigned int type)
+{
+       struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
+       struct regmap *map = d->map;
+       const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
+       int reg = irq_data->type_reg_offset / map->reg_stride;
+
+       if (!(irq_data->type_rising_mask | irq_data->type_falling_mask))
+               return 0;
+
+       d->type_buf[reg] &= ~(irq_data->type_falling_mask |
+                                       irq_data->type_rising_mask);
+       switch (type) {
+       case IRQ_TYPE_EDGE_FALLING:
+               d->type_buf[reg] |= irq_data->type_falling_mask;
+               break;
+
+       case IRQ_TYPE_EDGE_RISING:
+               d->type_buf[reg] |= irq_data->type_rising_mask;
+               break;
+
+       case IRQ_TYPE_EDGE_BOTH:
+               d->type_buf[reg] |= (irq_data->type_falling_mask |
+                                       irq_data->type_rising_mask);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+       return 0;
+}
+
 static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
 {
        struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
@@ -204,6 +255,7 @@ static const struct irq_chip regmap_irq_chip = {
        .irq_bus_sync_unlock    = regmap_irq_sync_unlock,
        .irq_disable            = regmap_irq_disable,
        .irq_enable             = regmap_irq_enable,
+       .irq_set_type           = regmap_irq_set_type,
        .irq_set_wake           = regmap_irq_set_wake,
 };
 
@@ -408,6 +460,18 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                        goto err_alloc;
        }
 
+       if (chip->num_type_reg) {
+               d->type_buf_def = kcalloc(chip->num_type_reg,
+                                       sizeof(unsigned int), GFP_KERNEL);
+               if (!d->type_buf_def)
+                       goto err_alloc;
+
+               d->type_buf = kcalloc(chip->num_type_reg, sizeof(unsigned int),
+                                     GFP_KERNEL);
+               if (!d->type_buf)
+                       goto err_alloc;
+       }
+
        d->irq_chip = regmap_irq_chip;
        d->irq_chip.name = chip->name;
        d->irq = irq;
@@ -420,6 +484,11 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
        else
                d->irq_reg_stride = 1;
 
+       if (chip->type_reg_stride)
+               d->type_reg_stride = chip->type_reg_stride;
+       else
+               d->type_reg_stride = 1;
+
        if (!map->use_single_read && map->reg_stride == 1 &&
            d->irq_reg_stride == 1) {
                d->status_reg_buf = kmalloc(map->format.val_bytes *
@@ -511,6 +580,33 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
                }
        }
 
+       if (chip->num_type_reg) {
+               for (i = 0; i < chip->num_irqs; i++) {
+                       reg = chip->irqs[i].type_reg_offset / map->reg_stride;
+                       d->type_buf_def[reg] |= chip->irqs[i].type_rising_mask |
+                                       chip->irqs[i].type_falling_mask;
+               }
+               for (i = 0; i < chip->num_type_reg; ++i) {
+                       if (!d->type_buf_def[i])
+                               continue;
+
+                       reg = chip->type_base +
+                               (i * map->reg_stride * d->type_reg_stride);
+                       if (chip->type_invert)
+                               ret = regmap_update_bits(map, reg,
+                                       d->type_buf_def[i], 0xFF);
+                       else
+                               ret = regmap_update_bits(map, reg,
+                                       d->type_buf_def[i], 0x0);
+                       if (ret != 0) {
+                               dev_err(map->dev,
+                                       "Failed to set type in 0x%x: %x\n",
+                                       reg, ret);
+                               goto err_alloc;
+                       }
+               }
+       }
+
        if (irq_base)
                d->domain = irq_domain_add_legacy(map->dev->of_node,
                                                  chip->num_irqs, irq_base, 0,
@@ -541,6 +637,8 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
 err_domain:
        /* Should really dispose of the domain but... */
 err_alloc:
+       kfree(d->type_buf);
+       kfree(d->type_buf_def);
        kfree(d->wake_buf);
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
@@ -564,6 +662,8 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
 
        free_irq(irq, d);
        irq_domain_remove(d->domain);
+       kfree(d->type_buf);
+       kfree(d->type_buf_def);
        kfree(d->wake_buf);
        kfree(d->mask_buf_def);
        kfree(d->mask_buf);
index d68bb402120e2c1a6d640720ef384663ceaefebe..95b6636d3a599a2908a2bb668fca4c76d1309de1 100644 (file)
@@ -788,10 +788,16 @@ int regmap_fields_update_bits(struct regmap_field *field,  unsigned int id,
  *
  * @reg_offset: Offset of the status/mask register within the bank
  * @mask:       Mask used to flag/control the register.
+ * @type_reg_offset: Offset register for the irq type setting.
+ * @type_rising_mask: Mask bit to configure RISING type irq.
+ * @type_falling_mask: Mask bit to configure FALLING type irq.
  */
 struct regmap_irq {
        unsigned int reg_offset;
        unsigned int mask;
+       unsigned int type_reg_offset;
+       unsigned int type_rising_mask;
+       unsigned int type_falling_mask;
 };
 
 #define REGMAP_IRQ_REG(_irq, _off, _mask)              \
@@ -811,18 +817,23 @@ struct regmap_irq {
  * @ack_base:    Base ack address. If zero then the chip is clear on read.
  *               Using zero value is possible with @use_ack bit.
  * @wake_base:   Base address for wake enables.  If zero unsupported.
+ * @type_base:   Base address for irq type.  If zero unsupported.
  * @irq_reg_stride:  Stride to use for chips where registers are not contiguous.
  * @init_ack_masked: Ack all masked interrupts once during initalization.
  * @mask_invert: Inverted mask register: cleared bits are masked out.
  * @use_ack:     Use @ack register even if it is zero.
  * @ack_invert:  Inverted ack register: cleared bits for ack.
  * @wake_invert: Inverted wake register: cleared bits are wake enabled.
+ * @type_invert: Invert the type flags.
  * @runtime_pm:  Hold a runtime PM lock on the device when accessing it.
  *
  * @num_regs:    Number of registers in each control bank.
  * @irqs:        Descriptors for individual IRQs.  Interrupt numbers are
  *               assigned based on the index in the array of the interrupt.
  * @num_irqs:    Number of descriptors.
+ * @num_type_reg:    Number of type registers.
+ * @type_reg_stride: Stride to use for chips where type registers are not
+ *                     contiguous.
  */
 struct regmap_irq_chip {
        const char *name;
@@ -832,6 +843,7 @@ struct regmap_irq_chip {
        unsigned int unmask_base;
        unsigned int ack_base;
        unsigned int wake_base;
+       unsigned int type_base;
        unsigned int irq_reg_stride;
        bool init_ack_masked:1;
        bool mask_invert:1;
@@ -839,11 +851,15 @@ struct regmap_irq_chip {
        bool ack_invert:1;
        bool wake_invert:1;
        bool runtime_pm:1;
+       bool type_invert:1;
 
        int num_regs;
 
        const struct regmap_irq *irqs;
        int num_irqs;
+
+       int num_type_reg;
+       unsigned int type_reg_stride;
 };
 
 struct regmap_irq_chip_data;