diff options
author | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-05 09:34:03 -0400 |
---|---|---|
committer | Mark Brown <broonie@opensource.wolfsonmicro.com> | 2012-06-05 09:38:20 -0400 |
commit | a43fd50dc99a5f65505f174eca5a421707d73b4c (patch) | |
tree | 18292c81dca6e3511c30d148a09bbbed4871b2eb /drivers/base/regmap | |
parent | bfd6185ddecc6e6f6bd654c053c307c9e49ca391 (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>
Diffstat (limited to 'drivers/base/regmap')
-rw-r--r-- | drivers/base/regmap/regmap-irq.c | 47 |
1 files changed, 47 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 | ||
109 | static 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 | |||
95 | static struct irq_chip regmap_irq_chip = { | 131 | static 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 | ||
103 | static irqreturn_t regmap_irq_thread(int irq, void *d) | 140 | static 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, | |||
294 | err_domain: | 339 | err_domain: |
295 | /* Should really dispose of the domain but... */ | 340 | /* Should really dispose of the domain but... */ |
296 | err_alloc: | 341 | err_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); |