aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-05 09:34:03 -0400
committerMark Brown <broonie@opensource.wolfsonmicro.com>2012-06-05 09:38:20 -0400
commita43fd50dc99a5f65505f174eca5a421707d73b4c (patch)
tree18292c81dca6e3511c30d148a09bbbed4871b2eb
parentbfd6185ddecc6e6f6bd654c053c307c9e49ca391 (diff)
regmap: Implement support for wake IRQs
Allow chips to provide a bank of registers for controlling the wake state in a similar fashion to the masks and propagate the wake count to the parent interrupt controller. Signed-off-by: Mark Brown <broonie@opensource.wolfsonmicro.com>
-rw-r--r--drivers/base/regmap/regmap-irq.c47
-rw-r--r--include/linux/regmap.h2
2 files changed, 49 insertions, 0 deletions
diff --git a/drivers/base/regmap/regmap-irq.c b/drivers/base/regmap/regmap-irq.c
index b74e14c4dff4..b480b529f020 100644
--- a/drivers/base/regmap/regmap-irq.c
+++ b/drivers/base/regmap/regmap-irq.c
@@ -29,9 +29,13 @@ struct regmap_irq_chip_data {
29 int irq_base; 29 int irq_base;
30 struct irq_domain *domain; 30 struct irq_domain *domain;
31 31
32 int irq;
33 int wake_count;
34
32 unsigned int *status_buf; 35 unsigned int *status_buf;
33 unsigned int *mask_buf; 36 unsigned int *mask_buf;
34 unsigned int *mask_buf_def; 37 unsigned int *mask_buf_def;
38 unsigned int *wake_buf;
35 39
36 unsigned int irq_reg_stride; 40 unsigned int irq_reg_stride;
37}; 41};
@@ -71,6 +75,16 @@ static void regmap_irq_sync_unlock(struct irq_data *data)
71 d->chip->mask_base + (i * map->reg_stride)); 75 d->chip->mask_base + (i * map->reg_stride));
72 } 76 }
73 77
78 /* If we've changed our wakeup count propagate it to the parent */
79 if (d->wake_count < 0)
80 for (i = d->wake_count; i < 0; i++)
81 irq_set_irq_wake(d->irq, 0);
82 else if (d->wake_count > 0)
83 for (i = 0; i < d->wake_count; i++)
84 irq_set_irq_wake(d->irq, 1);
85
86 d->wake_count = 0;
87
74 mutex_unlock(&d->lock); 88 mutex_unlock(&d->lock);
75} 89}
76 90
@@ -92,12 +106,35 @@ static void regmap_irq_disable(struct irq_data *data)
92 d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask; 106 d->mask_buf[irq_data->reg_offset / map->reg_stride] |= irq_data->mask;
93} 107}
94 108
109static int regmap_irq_set_wake(struct irq_data *data, unsigned int on)
110{
111 struct regmap_irq_chip_data *d = irq_data_get_irq_chip_data(data);
112 struct regmap *map = d->map;
113 const struct regmap_irq *irq_data = irq_to_regmap_irq(d, data->hwirq);
114
115 if (!d->chip->wake_base)
116 return -EINVAL;
117
118 if (on) {
119 d->wake_buf[irq_data->reg_offset / map->reg_stride]
120 &= ~irq_data->mask;
121 d->wake_count++;
122 } else {
123 d->wake_buf[irq_data->reg_offset / map->reg_stride]
124 |= irq_data->mask;
125 d->wake_count--;
126 }
127
128 return 0;
129}
130
95static struct irq_chip regmap_irq_chip = { 131static struct irq_chip regmap_irq_chip = {
96 .name = "regmap", 132 .name = "regmap",
97 .irq_bus_lock = regmap_irq_lock, 133 .irq_bus_lock = regmap_irq_lock,
98 .irq_bus_sync_unlock = regmap_irq_sync_unlock, 134 .irq_bus_sync_unlock = regmap_irq_sync_unlock,
99 .irq_disable = regmap_irq_disable, 135 .irq_disable = regmap_irq_disable,
100 .irq_enable = regmap_irq_enable, 136 .irq_enable = regmap_irq_enable,
137 .irq_set_wake = regmap_irq_set_wake,
101}; 138};
102 139
103static irqreturn_t regmap_irq_thread(int irq, void *d) 140static irqreturn_t regmap_irq_thread(int irq, void *d)
@@ -240,6 +277,14 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
240 if (!d->mask_buf_def) 277 if (!d->mask_buf_def)
241 goto err_alloc; 278 goto err_alloc;
242 279
280 if (chip->wake_base) {
281 d->wake_buf = kzalloc(sizeof(unsigned int) * chip->num_regs,
282 GFP_KERNEL);
283 if (!d->wake_buf)
284 goto err_alloc;
285 }
286
287 d->irq = irq;
243 d->map = map; 288 d->map = map;
244 d->chip = chip; 289 d->chip = chip;
245 d->irq_base = irq_base; 290 d->irq_base = irq_base;
@@ -294,6 +339,7 @@ int regmap_add_irq_chip(struct regmap *map, int irq, int irq_flags,
294err_domain: 339err_domain:
295 /* Should really dispose of the domain but... */ 340 /* Should really dispose of the domain but... */
296err_alloc: 341err_alloc:
342 kfree(d->wake_buf);
297 kfree(d->mask_buf_def); 343 kfree(d->mask_buf_def);
298 kfree(d->mask_buf); 344 kfree(d->mask_buf);
299 kfree(d->status_buf); 345 kfree(d->status_buf);
@@ -315,6 +361,7 @@ void regmap_del_irq_chip(int irq, struct regmap_irq_chip_data *d)
315 361
316 free_irq(irq, d); 362 free_irq(irq, d);
317 /* We should unmap the domain but... */ 363 /* We should unmap the domain but... */
364 kfree(d->wake_buf);
318 kfree(d->mask_buf_def); 365 kfree(d->mask_buf_def);
319 kfree(d->mask_buf); 366 kfree(d->mask_buf);
320 kfree(d->status_buf); 367 kfree(d->status_buf);
diff --git a/include/linux/regmap.h b/include/linux/regmap.h
index 56af22ec9aba..58ec0cba0ae6 100644
--- a/include/linux/regmap.h
+++ b/include/linux/regmap.h
@@ -219,6 +219,7 @@ struct regmap_irq {
219 * @status_base: Base status register address. 219 * @status_base: Base status register address.
220 * @mask_base: Base mask register address. 220 * @mask_base: Base mask register address.
221 * @ack_base: Base ack address. If zero then the chip is clear on read. 221 * @ack_base: Base ack address. If zero then the chip is clear on read.
222 * @wake_base: Base address for wake enables. If zero unsupported.
222 * @irq_reg_stride: Stride to use for chips where registers are not contiguous. 223 * @irq_reg_stride: Stride to use for chips where registers are not contiguous.
223 * 224 *
224 * @num_regs: Number of registers in each control bank. 225 * @num_regs: Number of registers in each control bank.
@@ -232,6 +233,7 @@ struct regmap_irq_chip {
232 unsigned int status_base; 233 unsigned int status_base;
233 unsigned int mask_base; 234 unsigned int mask_base;
234 unsigned int ack_base; 235 unsigned int ack_base;
236 unsigned int wake_base;
235 unsigned int irq_reg_stride; 237 unsigned int irq_reg_stride;
236 238
237 int num_regs; 239 int num_regs;