aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/mfd/twl6030-irq.c
diff options
context:
space:
mode:
authorGrygorii Strashko <grygorii.strashko@ti.com>2013-07-25 09:15:49 -0400
committerSamuel Ortiz <sameo@linux.intel.com>2013-08-20 04:19:21 -0400
commitb32408f64427096d9cc81066875345397bae0269 (patch)
tree95f2c7e6001e0fa331d4e0e5f87ae1198e4a8528 /drivers/mfd/twl6030-irq.c
parenta820e5686f5f048494f71a394edb55f1c24603c5 (diff)
mfd: twl6030-irq: Convert to use linear irq_domain
Since the TWL6030 PMIC is used with OMAP4 SoCs only and OMAP4 legacy boot is dropped there are no needs to allocate the range of IRQ descriptors during system boot to support TWL6030 IRQs. Hence, convert it to use linear irq_domain and move IRQ configuration in .map()/.unmap() callbacks of irq_domain. So, IRQ mapping and descriptors allocation will be performed dynamically basing on DT configuration. The error message will be reported in case if unmapped IRQ is received by TWL6030 (virq==0). Signed-off-by: Grygorii Strashko <grygorii.strashko@ti.com> Acked-by: Graeme Gregory <gg@slimlogic.co.uk> Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/mfd/twl6030-irq.c')
-rw-r--r--drivers/mfd/twl6030-irq.c119
1 files changed, 69 insertions, 50 deletions
diff --git a/drivers/mfd/twl6030-irq.c b/drivers/mfd/twl6030-irq.c
index f7da2614de80..1b03ce9e9f15 100644
--- a/drivers/mfd/twl6030-irq.c
+++ b/drivers/mfd/twl6030-irq.c
@@ -86,11 +86,11 @@ static int twl6030_interrupt_mapping[24] = {
86}; 86};
87/*----------------------------------------------------------------------*/ 87/*----------------------------------------------------------------------*/
88 88
89static unsigned twl6030_irq_base;
90static int twl_irq; 89static int twl_irq;
91static bool twl_irq_wake_enabled; 90static bool twl_irq_wake_enabled;
92 91
93static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0); 92static atomic_t twl6030_wakeirqs = ATOMIC_INIT(0);
93struct irq_domain *irq_domain;
94 94
95static int twl6030_irq_pm_notifier(struct notifier_block *notifier, 95static int twl6030_irq_pm_notifier(struct notifier_block *notifier,
96 unsigned long pm_event, void *unused) 96 unsigned long pm_event, void *unused)
@@ -138,6 +138,7 @@ static struct notifier_block twl6030_irq_pm_notifier_block = {
138static irqreturn_t twl6030_irq_thread(int irq, void *data) 138static irqreturn_t twl6030_irq_thread(int irq, void *data)
139{ 139{
140 int i, ret; 140 int i, ret;
141 struct irq_domain *irq_domain = (struct irq_domain *)data;
141 union { 142 union {
142 u8 bytes[4]; 143 u8 bytes[4];
143 u32 int_sts; 144 u32 int_sts;
@@ -161,9 +162,14 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data)
161 162
162 for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++) 163 for (i = 0; sts.int_sts; sts.int_sts >>= 1, i++)
163 if (sts.int_sts & 0x1) { 164 if (sts.int_sts & 0x1) {
164 int module_irq = twl6030_irq_base + 165 int module_irq =
165 twl6030_interrupt_mapping[i]; 166 irq_find_mapping(irq_domain,
166 handle_nested_irq(module_irq); 167 twl6030_interrupt_mapping[i]);
168 if (module_irq)
169 handle_nested_irq(module_irq);
170 else
171 pr_err("twl6030_irq: Unmapped PIH ISR %u detected\n",
172 i);
167 pr_debug("twl6030_irq: PIH ISR %u, virq%u\n", 173 pr_debug("twl6030_irq: PIH ISR %u, virq%u\n",
168 i, module_irq); 174 i, module_irq);
169 } 175 }
@@ -186,19 +192,6 @@ static irqreturn_t twl6030_irq_thread(int irq, void *data)
186 192
187/*----------------------------------------------------------------------*/ 193/*----------------------------------------------------------------------*/
188 194
189static inline void activate_irq(int irq)
190{
191#ifdef CONFIG_ARM
192 /* ARM requires an extra step to clear IRQ_NOREQUEST, which it
193 * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
194 */
195 set_irq_flags(irq, IRQF_VALID);
196#else
197 /* same effect on other architectures */
198 irq_set_noprobe(irq);
199#endif
200}
201
202static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on) 195static int twl6030_irq_set_wake(struct irq_data *d, unsigned int on)
203{ 196{
204 if (on) 197 if (on)
@@ -279,7 +272,7 @@ int twl6030_mmc_card_detect_config(void)
279 return ret; 272 return ret;
280 } 273 }
281 274
282 return twl6030_irq_base + MMCDETECT_INTR_OFFSET; 275 return irq_find_mapping(irq_domain, MMCDETECT_INTR_OFFSET);
283} 276}
284EXPORT_SYMBOL(twl6030_mmc_card_detect_config); 277EXPORT_SYMBOL(twl6030_mmc_card_detect_config);
285 278
@@ -308,28 +301,54 @@ int twl6030_mmc_card_detect(struct device *dev, int slot)
308} 301}
309EXPORT_SYMBOL(twl6030_mmc_card_detect); 302EXPORT_SYMBOL(twl6030_mmc_card_detect);
310 303
304static struct irq_chip twl6030_irq_chip;
305
306static int twl6030_irq_map(struct irq_domain *d, unsigned int virq,
307 irq_hw_number_t hwirq)
308{
309 irq_set_chip_data(virq, &twl6030_irq_chip);
310 irq_set_chip_and_handler(virq, &twl6030_irq_chip, handle_simple_irq);
311 irq_set_nested_thread(virq, true);
312 irq_set_parent(virq, twl_irq);
313
314#ifdef CONFIG_ARM
315 /*
316 * ARM requires an extra step to clear IRQ_NOREQUEST, which it
317 * sets on behalf of every irq_chip. Also sets IRQ_NOPROBE.
318 */
319 set_irq_flags(virq, IRQF_VALID);
320#else
321 /* same effect on other architectures */
322 irq_set_noprobe(virq);
323#endif
324
325 return 0;
326}
327
328static void twl6030_irq_unmap(struct irq_domain *d, unsigned int virq)
329{
330#ifdef CONFIG_ARM
331 set_irq_flags(virq, 0);
332#endif
333 irq_set_chip_and_handler(virq, NULL, NULL);
334 irq_set_chip_data(virq, NULL);
335}
336
337static struct irq_domain_ops twl6030_irq_domain_ops = {
338 .map = twl6030_irq_map,
339 .unmap = twl6030_irq_unmap,
340 .xlate = irq_domain_xlate_onetwocell,
341};
342
311int twl6030_init_irq(struct device *dev, int irq_num) 343int twl6030_init_irq(struct device *dev, int irq_num)
312{ 344{
313 struct device_node *node = dev->of_node; 345 struct device_node *node = dev->of_node;
314 int nr_irqs, irq_base, irq_end; 346 int nr_irqs;
315 static struct irq_chip twl6030_irq_chip;
316 int status; 347 int status;
317 int i;
318 u8 mask[3]; 348 u8 mask[3];
319 349
320 nr_irqs = TWL6030_NR_IRQS; 350 nr_irqs = TWL6030_NR_IRQS;
321 351
322 irq_base = irq_alloc_descs(-1, 0, nr_irqs, 0);
323 if (IS_ERR_VALUE(irq_base)) {
324 dev_err(dev, "Fail to allocate IRQ descs\n");
325 return irq_base;
326 }
327
328 irq_domain_add_legacy(node, nr_irqs, irq_base, 0,
329 &irq_domain_simple_ops, NULL);
330
331 irq_end = irq_base + nr_irqs;
332
333 mask[0] = 0xFF; 352 mask[0] = 0xFF;
334 mask[1] = 0xFF; 353 mask[1] = 0xFF;
335 mask[2] = 0xFF; 354 mask[2] = 0xFF;
@@ -346,8 +365,6 @@ int twl6030_init_irq(struct device *dev, int irq_num)
346 return status; 365 return status;
347 } 366 }
348 367
349 twl6030_irq_base = irq_base;
350
351 /* 368 /*
352 * install an irq handler for each of the modules; 369 * install an irq handler for each of the modules;
353 * clone dummy irq_chip since PIH can't *do* anything 370 * clone dummy irq_chip since PIH can't *do* anything
@@ -357,21 +374,18 @@ int twl6030_init_irq(struct device *dev, int irq_num)
357 twl6030_irq_chip.irq_set_type = NULL; 374 twl6030_irq_chip.irq_set_type = NULL;
358 twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake; 375 twl6030_irq_chip.irq_set_wake = twl6030_irq_set_wake;
359 376
360 for (i = irq_base; i < irq_end; i++) { 377 irq_domain = irq_domain_add_linear(node, nr_irqs,
361 irq_set_chip_and_handler(i, &twl6030_irq_chip, 378 &twl6030_irq_domain_ops, NULL);
362 handle_simple_irq); 379 if (!irq_domain) {
363 irq_set_chip_data(i, (void *)irq_num); 380 dev_err(dev, "Can't add irq_domain\n");
364 irq_set_nested_thread(i, true); 381 return -ENOMEM;
365 irq_set_parent(i, irq_num);
366 activate_irq(i);
367 } 382 }
368 383
369 dev_info(dev, "PIH (irq %d) nested IRQs %d..%d\n", 384 dev_info(dev, "PIH (irq %d) nested IRQs\n", irq_num);
370 irq_num, irq_base, irq_end);
371 385
372 /* install an irq handler to demultiplex the TWL6030 interrupt */ 386 /* install an irq handler to demultiplex the TWL6030 interrupt */
373 status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread, 387 status = request_threaded_irq(irq_num, NULL, twl6030_irq_thread,
374 IRQF_ONESHOT, "TWL6030-PIH", NULL); 388 IRQF_ONESHOT, "TWL6030-PIH", irq_domain);
375 if (status < 0) { 389 if (status < 0) {
376 dev_err(dev, "could not claim irq %d: %d\n", irq_num, status); 390 dev_err(dev, "could not claim irq %d: %d\n", irq_num, status);
377 goto fail_irq; 391 goto fail_irq;
@@ -379,23 +393,28 @@ int twl6030_init_irq(struct device *dev, int irq_num)
379 393
380 twl_irq = irq_num; 394 twl_irq = irq_num;
381 register_pm_notifier(&twl6030_irq_pm_notifier_block); 395 register_pm_notifier(&twl6030_irq_pm_notifier_block);
382 return irq_base; 396 return 0;
383 397
384fail_irq: 398fail_irq:
385 for (i = irq_base; i < irq_end; i++) 399 irq_domain_remove(irq_domain);
386 irq_set_chip_and_handler(i, NULL, NULL);
387
388 return status; 400 return status;
389} 401}
390 402
391int twl6030_exit_irq(void) 403int twl6030_exit_irq(void)
392{ 404{
393
394 if (twl_irq) { 405 if (twl_irq) {
395 unregister_pm_notifier(&twl6030_irq_pm_notifier_block); 406 unregister_pm_notifier(&twl6030_irq_pm_notifier_block);
396 free_irq(twl_irq, NULL); 407 free_irq(twl_irq, NULL);
408 /*
409 * TODO: IRQ domain and allocated nested IRQ descriptors
410 * should be freed somehow here. Now It can't be done, because
411 * child devices will not be deleted during removing of
412 * TWL Core driver and they will still contain allocated
413 * virt IRQs in their Resources tables.
414 * The same prevents us from using devm_request_threaded_irq()
415 * in this module.
416 */
397 } 417 }
398
399 return 0; 418 return 0;
400} 419}
401 420