diff options
author | Josh Cartwright <joshc@codeaurora.org> | 2014-02-12 14:44:25 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@linuxfoundation.org> | 2014-02-15 14:55:28 -0500 |
commit | 67b563f1f258739eefa79d9ce6c3177f670481df (patch) | |
tree | e1e5f797f693867651b54b2174c3ddec1178b974 /drivers/spmi | |
parent | 39ae93e3a31d0c9ca99e35b754a9f3c6f1db2bee (diff) |
spmi: pmic_arb: add support for interrupt handling
The Qualcomm PMIC Arbiter, in addition to being a basic SPMI controller,
also implements interrupt handling for slave devices. Note, this is
outside the scope of SPMI, as SPMI leaves interrupt handling completely
unspecified.
Extend the driver to provide a irq_chip implementation and chained irq
handling which allows for these interrupts to be used.
Cc: Thomas Gleixner <tglx@linutronix.de>
Signed-off-by: Josh Cartwright <joshc@codeaurora.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Diffstat (limited to 'drivers/spmi')
-rw-r--r-- | drivers/spmi/Kconfig | 1 | ||||
-rw-r--r-- | drivers/spmi/spmi-pmic-arb.c | 377 |
2 files changed, 376 insertions, 2 deletions
diff --git a/drivers/spmi/Kconfig b/drivers/spmi/Kconfig index 80b79013fd1e..075bd79e1ac4 100644 --- a/drivers/spmi/Kconfig +++ b/drivers/spmi/Kconfig | |||
@@ -13,6 +13,7 @@ if SPMI | |||
13 | config SPMI_MSM_PMIC_ARB | 13 | config SPMI_MSM_PMIC_ARB |
14 | tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" | 14 | tristate "Qualcomm MSM SPMI Controller (PMIC Arbiter)" |
15 | depends on ARM | 15 | depends on ARM |
16 | depends on IRQ_DOMAIN | ||
16 | depends on ARCH_MSM || COMPILE_TEST | 17 | depends on ARCH_MSM || COMPILE_TEST |
17 | default ARCH_MSM | 18 | default ARCH_MSM |
18 | help | 19 | help |
diff --git a/drivers/spmi/spmi-pmic-arb.c b/drivers/spmi/spmi-pmic-arb.c index 2dd27e8d140d..246e03a18c94 100644 --- a/drivers/spmi/spmi-pmic-arb.c +++ b/drivers/spmi/spmi-pmic-arb.c | |||
@@ -13,6 +13,9 @@ | |||
13 | #include <linux/err.h> | 13 | #include <linux/err.h> |
14 | #include <linux/interrupt.h> | 14 | #include <linux/interrupt.h> |
15 | #include <linux/io.h> | 15 | #include <linux/io.h> |
16 | #include <linux/irqchip/chained_irq.h> | ||
17 | #include <linux/irqdomain.h> | ||
18 | #include <linux/irq.h> | ||
16 | #include <linux/kernel.h> | 19 | #include <linux/kernel.h> |
17 | #include <linux/module.h> | 20 | #include <linux/module.h> |
18 | #include <linux/of.h> | 21 | #include <linux/of.h> |
@@ -103,6 +106,14 @@ enum pmic_arb_cmd_op_code { | |||
103 | * @cnfg: address of the PMIC Arbiter configuration registers. | 106 | * @cnfg: address of the PMIC Arbiter configuration registers. |
104 | * @lock: lock to synchronize accesses. | 107 | * @lock: lock to synchronize accesses. |
105 | * @channel: which channel to use for accesses. | 108 | * @channel: which channel to use for accesses. |
109 | * @irq: PMIC ARB interrupt. | ||
110 | * @ee: the current Execution Environment | ||
111 | * @min_apid: minimum APID (used for bounding IRQ search) | ||
112 | * @max_apid: maximum APID | ||
113 | * @mapping_table: in-memory copy of PPID -> APID mapping table. | ||
114 | * @domain: irq domain object for PMIC IRQ domain | ||
115 | * @spmic: SPMI controller object | ||
116 | * @apid_to_ppid: cached mapping from APID to PPID | ||
106 | */ | 117 | */ |
107 | struct spmi_pmic_arb_dev { | 118 | struct spmi_pmic_arb_dev { |
108 | void __iomem *base; | 119 | void __iomem *base; |
@@ -110,6 +121,14 @@ struct spmi_pmic_arb_dev { | |||
110 | void __iomem *cnfg; | 121 | void __iomem *cnfg; |
111 | raw_spinlock_t lock; | 122 | raw_spinlock_t lock; |
112 | u8 channel; | 123 | u8 channel; |
124 | int irq; | ||
125 | u8 ee; | ||
126 | u8 min_apid; | ||
127 | u8 max_apid; | ||
128 | u32 mapping_table[SPMI_MAPPING_TABLE_LEN]; | ||
129 | struct irq_domain *domain; | ||
130 | struct spmi_controller *spmic; | ||
131 | u16 apid_to_ppid[256]; | ||
113 | }; | 132 | }; |
114 | 133 | ||
115 | static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) | 134 | static inline u32 pmic_arb_base_read(struct spmi_pmic_arb_dev *dev, u32 offset) |
@@ -306,12 +325,316 @@ static int pmic_arb_write_cmd(struct spmi_controller *ctrl, u8 opc, u8 sid, | |||
306 | return rc; | 325 | return rc; |
307 | } | 326 | } |
308 | 327 | ||
328 | enum qpnpint_regs { | ||
329 | QPNPINT_REG_RT_STS = 0x10, | ||
330 | QPNPINT_REG_SET_TYPE = 0x11, | ||
331 | QPNPINT_REG_POLARITY_HIGH = 0x12, | ||
332 | QPNPINT_REG_POLARITY_LOW = 0x13, | ||
333 | QPNPINT_REG_LATCHED_CLR = 0x14, | ||
334 | QPNPINT_REG_EN_SET = 0x15, | ||
335 | QPNPINT_REG_EN_CLR = 0x16, | ||
336 | QPNPINT_REG_LATCHED_STS = 0x18, | ||
337 | }; | ||
338 | |||
339 | struct spmi_pmic_arb_qpnpint_type { | ||
340 | u8 type; /* 1 -> edge */ | ||
341 | u8 polarity_high; | ||
342 | u8 polarity_low; | ||
343 | } __packed; | ||
344 | |||
345 | /* Simplified accessor functions for irqchip callbacks */ | ||
346 | static void qpnpint_spmi_write(struct irq_data *d, u8 reg, void *buf, | ||
347 | size_t len) | ||
348 | { | ||
349 | struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); | ||
350 | u8 sid = d->hwirq >> 24; | ||
351 | u8 per = d->hwirq >> 16; | ||
352 | |||
353 | if (pmic_arb_write_cmd(pa->spmic, SPMI_CMD_EXT_WRITEL, sid, | ||
354 | (per << 8) + reg, buf, len)) | ||
355 | dev_err_ratelimited(&pa->spmic->dev, | ||
356 | "failed irqchip transaction on %x\n", | ||
357 | d->irq); | ||
358 | } | ||
359 | |||
360 | static void qpnpint_spmi_read(struct irq_data *d, u8 reg, void *buf, size_t len) | ||
361 | { | ||
362 | struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); | ||
363 | u8 sid = d->hwirq >> 24; | ||
364 | u8 per = d->hwirq >> 16; | ||
365 | |||
366 | if (pmic_arb_read_cmd(pa->spmic, SPMI_CMD_EXT_READL, sid, | ||
367 | (per << 8) + reg, buf, len)) | ||
368 | dev_err_ratelimited(&pa->spmic->dev, | ||
369 | "failed irqchip transaction on %x\n", | ||
370 | d->irq); | ||
371 | } | ||
372 | |||
373 | static void periph_interrupt(struct spmi_pmic_arb_dev *pa, u8 apid) | ||
374 | { | ||
375 | unsigned int irq; | ||
376 | u32 status; | ||
377 | int id; | ||
378 | |||
379 | status = readl_relaxed(pa->intr + SPMI_PIC_IRQ_STATUS(apid)); | ||
380 | while (status) { | ||
381 | id = ffs(status) - 1; | ||
382 | status &= ~(1 << id); | ||
383 | irq = irq_find_mapping(pa->domain, | ||
384 | pa->apid_to_ppid[apid] << 16 | ||
385 | | id << 8 | ||
386 | | apid); | ||
387 | generic_handle_irq(irq); | ||
388 | } | ||
389 | } | ||
390 | |||
391 | static void pmic_arb_chained_irq(unsigned int irq, struct irq_desc *desc) | ||
392 | { | ||
393 | struct spmi_pmic_arb_dev *pa = irq_get_handler_data(irq); | ||
394 | struct irq_chip *chip = irq_get_chip(irq); | ||
395 | void __iomem *intr = pa->intr; | ||
396 | int first = pa->min_apid >> 5; | ||
397 | int last = pa->max_apid >> 5; | ||
398 | u32 status; | ||
399 | int i, id; | ||
400 | |||
401 | chained_irq_enter(chip, desc); | ||
402 | |||
403 | for (i = first; i <= last; ++i) { | ||
404 | status = readl_relaxed(intr + | ||
405 | SPMI_PIC_OWNER_ACC_STATUS(pa->ee, i)); | ||
406 | while (status) { | ||
407 | id = ffs(status) - 1; | ||
408 | status &= ~(1 << id); | ||
409 | periph_interrupt(pa, id + i * 32); | ||
410 | } | ||
411 | } | ||
412 | |||
413 | chained_irq_exit(chip, desc); | ||
414 | } | ||
415 | |||
416 | static void qpnpint_irq_ack(struct irq_data *d) | ||
417 | { | ||
418 | struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); | ||
419 | u8 irq = d->hwirq >> 8; | ||
420 | u8 apid = d->hwirq; | ||
421 | unsigned long flags; | ||
422 | u8 data; | ||
423 | |||
424 | raw_spin_lock_irqsave(&pa->lock, flags); | ||
425 | writel_relaxed(1 << irq, pa->intr + SPMI_PIC_IRQ_CLEAR(apid)); | ||
426 | raw_spin_unlock_irqrestore(&pa->lock, flags); | ||
427 | |||
428 | data = 1 << irq; | ||
429 | qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); | ||
430 | } | ||
431 | |||
432 | static void qpnpint_irq_mask(struct irq_data *d) | ||
433 | { | ||
434 | struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); | ||
435 | u8 irq = d->hwirq >> 8; | ||
436 | u8 apid = d->hwirq; | ||
437 | unsigned long flags; | ||
438 | u32 status; | ||
439 | u8 data; | ||
440 | |||
441 | raw_spin_lock_irqsave(&pa->lock, flags); | ||
442 | status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); | ||
443 | if (status & SPMI_PIC_ACC_ENABLE_BIT) { | ||
444 | status = status & ~SPMI_PIC_ACC_ENABLE_BIT; | ||
445 | writel_relaxed(status, pa->intr + SPMI_PIC_ACC_ENABLE(apid)); | ||
446 | } | ||
447 | raw_spin_unlock_irqrestore(&pa->lock, flags); | ||
448 | |||
449 | data = 1 << irq; | ||
450 | qpnpint_spmi_write(d, QPNPINT_REG_EN_CLR, &data, 1); | ||
451 | } | ||
452 | |||
453 | static void qpnpint_irq_unmask(struct irq_data *d) | ||
454 | { | ||
455 | struct spmi_pmic_arb_dev *pa = irq_data_get_irq_chip_data(d); | ||
456 | u8 irq = d->hwirq >> 8; | ||
457 | u8 apid = d->hwirq; | ||
458 | unsigned long flags; | ||
459 | u32 status; | ||
460 | u8 data; | ||
461 | |||
462 | raw_spin_lock_irqsave(&pa->lock, flags); | ||
463 | status = readl_relaxed(pa->intr + SPMI_PIC_ACC_ENABLE(apid)); | ||
464 | if (!(status & SPMI_PIC_ACC_ENABLE_BIT)) { | ||
465 | writel_relaxed(status | SPMI_PIC_ACC_ENABLE_BIT, | ||
466 | pa->intr + SPMI_PIC_ACC_ENABLE(apid)); | ||
467 | } | ||
468 | raw_spin_unlock_irqrestore(&pa->lock, flags); | ||
469 | |||
470 | data = 1 << irq; | ||
471 | qpnpint_spmi_write(d, QPNPINT_REG_EN_SET, &data, 1); | ||
472 | } | ||
473 | |||
474 | static void qpnpint_irq_enable(struct irq_data *d) | ||
475 | { | ||
476 | u8 irq = d->hwirq >> 8; | ||
477 | u8 data; | ||
478 | |||
479 | qpnpint_irq_unmask(d); | ||
480 | |||
481 | data = 1 << irq; | ||
482 | qpnpint_spmi_write(d, QPNPINT_REG_LATCHED_CLR, &data, 1); | ||
483 | } | ||
484 | |||
485 | static int qpnpint_irq_set_type(struct irq_data *d, unsigned int flow_type) | ||
486 | { | ||
487 | struct spmi_pmic_arb_qpnpint_type type; | ||
488 | u8 irq = d->hwirq >> 8; | ||
489 | |||
490 | qpnpint_spmi_read(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); | ||
491 | |||
492 | if (flow_type & (IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING)) { | ||
493 | type.type |= 1 << irq; | ||
494 | if (flow_type & IRQF_TRIGGER_RISING) | ||
495 | type.polarity_high |= 1 << irq; | ||
496 | if (flow_type & IRQF_TRIGGER_FALLING) | ||
497 | type.polarity_low |= 1 << irq; | ||
498 | } else { | ||
499 | if ((flow_type & (IRQF_TRIGGER_HIGH)) && | ||
500 | (flow_type & (IRQF_TRIGGER_LOW))) | ||
501 | return -EINVAL; | ||
502 | |||
503 | type.type &= ~(1 << irq); /* level trig */ | ||
504 | if (flow_type & IRQF_TRIGGER_HIGH) | ||
505 | type.polarity_high |= 1 << irq; | ||
506 | else | ||
507 | type.polarity_low |= 1 << irq; | ||
508 | } | ||
509 | |||
510 | qpnpint_spmi_write(d, QPNPINT_REG_SET_TYPE, &type, sizeof(type)); | ||
511 | return 0; | ||
512 | } | ||
513 | |||
514 | static struct irq_chip pmic_arb_irqchip = { | ||
515 | .name = "pmic_arb", | ||
516 | .irq_enable = qpnpint_irq_enable, | ||
517 | .irq_ack = qpnpint_irq_ack, | ||
518 | .irq_mask = qpnpint_irq_mask, | ||
519 | .irq_unmask = qpnpint_irq_unmask, | ||
520 | .irq_set_type = qpnpint_irq_set_type, | ||
521 | .flags = IRQCHIP_MASK_ON_SUSPEND | ||
522 | | IRQCHIP_SKIP_SET_WAKE, | ||
523 | }; | ||
524 | |||
525 | struct spmi_pmic_arb_irq_spec { | ||
526 | unsigned slave:4; | ||
527 | unsigned per:8; | ||
528 | unsigned irq:3; | ||
529 | }; | ||
530 | |||
531 | static int search_mapping_table(struct spmi_pmic_arb_dev *pa, | ||
532 | struct spmi_pmic_arb_irq_spec *spec, | ||
533 | u8 *apid) | ||
534 | { | ||
535 | u16 ppid = spec->slave << 8 | spec->per; | ||
536 | u32 *mapping_table = pa->mapping_table; | ||
537 | int index = 0, i; | ||
538 | u32 data; | ||
539 | |||
540 | for (i = 0; i < SPMI_MAPPING_TABLE_TREE_DEPTH; ++i) { | ||
541 | data = mapping_table[index]; | ||
542 | |||
543 | if (ppid & (1 << SPMI_MAPPING_BIT_INDEX(data))) { | ||
544 | if (SPMI_MAPPING_BIT_IS_1_FLAG(data)) { | ||
545 | index = SPMI_MAPPING_BIT_IS_1_RESULT(data); | ||
546 | } else { | ||
547 | *apid = SPMI_MAPPING_BIT_IS_1_RESULT(data); | ||
548 | return 0; | ||
549 | } | ||
550 | } else { | ||
551 | if (SPMI_MAPPING_BIT_IS_0_FLAG(data)) { | ||
552 | index = SPMI_MAPPING_BIT_IS_0_RESULT(data); | ||
553 | } else { | ||
554 | *apid = SPMI_MAPPING_BIT_IS_0_RESULT(data); | ||
555 | return 0; | ||
556 | } | ||
557 | } | ||
558 | } | ||
559 | |||
560 | return -ENODEV; | ||
561 | } | ||
562 | |||
563 | static int qpnpint_irq_domain_dt_translate(struct irq_domain *d, | ||
564 | struct device_node *controller, | ||
565 | const u32 *intspec, | ||
566 | unsigned int intsize, | ||
567 | unsigned long *out_hwirq, | ||
568 | unsigned int *out_type) | ||
569 | { | ||
570 | struct spmi_pmic_arb_dev *pa = d->host_data; | ||
571 | struct spmi_pmic_arb_irq_spec spec; | ||
572 | int err; | ||
573 | u8 apid; | ||
574 | |||
575 | dev_dbg(&pa->spmic->dev, | ||
576 | "intspec[0] 0x%1x intspec[1] 0x%02x intspec[2] 0x%02x\n", | ||
577 | intspec[0], intspec[1], intspec[2]); | ||
578 | |||
579 | if (d->of_node != controller) | ||
580 | return -EINVAL; | ||
581 | if (intsize != 4) | ||
582 | return -EINVAL; | ||
583 | if (intspec[0] > 0xF || intspec[1] > 0xFF || intspec[2] > 0x7) | ||
584 | return -EINVAL; | ||
585 | |||
586 | spec.slave = intspec[0]; | ||
587 | spec.per = intspec[1]; | ||
588 | spec.irq = intspec[2]; | ||
589 | |||
590 | err = search_mapping_table(pa, &spec, &apid); | ||
591 | if (err) | ||
592 | return err; | ||
593 | |||
594 | pa->apid_to_ppid[apid] = spec.slave << 8 | spec.per; | ||
595 | |||
596 | /* Keep track of {max,min}_apid for bounding search during interrupt */ | ||
597 | if (apid > pa->max_apid) | ||
598 | pa->max_apid = apid; | ||
599 | if (apid < pa->min_apid) | ||
600 | pa->min_apid = apid; | ||
601 | |||
602 | *out_hwirq = spec.slave << 24 | ||
603 | | spec.per << 16 | ||
604 | | spec.irq << 8 | ||
605 | | apid; | ||
606 | *out_type = intspec[3] & IRQ_TYPE_SENSE_MASK; | ||
607 | |||
608 | dev_dbg(&pa->spmic->dev, "out_hwirq = %lu\n", *out_hwirq); | ||
609 | |||
610 | return 0; | ||
611 | } | ||
612 | |||
613 | static int qpnpint_irq_domain_map(struct irq_domain *d, | ||
614 | unsigned int virq, | ||
615 | irq_hw_number_t hwirq) | ||
616 | { | ||
617 | struct spmi_pmic_arb_dev *pa = d->host_data; | ||
618 | |||
619 | dev_dbg(&pa->spmic->dev, "virq = %u, hwirq = %lu\n", virq, hwirq); | ||
620 | |||
621 | irq_set_chip_and_handler(virq, &pmic_arb_irqchip, handle_level_irq); | ||
622 | irq_set_chip_data(virq, d->host_data); | ||
623 | irq_set_noprobe(virq); | ||
624 | return 0; | ||
625 | } | ||
626 | |||
627 | static const struct irq_domain_ops pmic_arb_irq_domain_ops = { | ||
628 | .map = qpnpint_irq_domain_map, | ||
629 | .xlate = qpnpint_irq_domain_dt_translate, | ||
630 | }; | ||
631 | |||
309 | static int spmi_pmic_arb_probe(struct platform_device *pdev) | 632 | static int spmi_pmic_arb_probe(struct platform_device *pdev) |
310 | { | 633 | { |
311 | struct spmi_pmic_arb_dev *pa; | 634 | struct spmi_pmic_arb_dev *pa; |
312 | struct spmi_controller *ctrl; | 635 | struct spmi_controller *ctrl; |
313 | struct resource *res; | 636 | struct resource *res; |
314 | u32 channel; | 637 | u32 channel, ee; |
315 | int err, i; | 638 | int err, i; |
316 | 639 | ||
317 | ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); | 640 | ctrl = spmi_controller_alloc(&pdev->dev, sizeof(*pa)); |
@@ -319,6 +642,7 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) | |||
319 | return -ENOMEM; | 642 | return -ENOMEM; |
320 | 643 | ||
321 | pa = spmi_controller_get_drvdata(ctrl); | 644 | pa = spmi_controller_get_drvdata(ctrl); |
645 | pa->spmic = ctrl; | ||
322 | 646 | ||
323 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); | 647 | res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "core"); |
324 | pa->base = devm_ioremap_resource(&ctrl->dev, res); | 648 | pa->base = devm_ioremap_resource(&ctrl->dev, res); |
@@ -341,6 +665,12 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) | |||
341 | goto err_put_ctrl; | 665 | goto err_put_ctrl; |
342 | } | 666 | } |
343 | 667 | ||
668 | pa->irq = platform_get_irq_byname(pdev, "periph_irq"); | ||
669 | if (pa->irq < 0) { | ||
670 | err = pa->irq; | ||
671 | goto err_put_ctrl; | ||
672 | } | ||
673 | |||
344 | err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); | 674 | err = of_property_read_u32(pdev->dev.of_node, "qcom,channel", &channel); |
345 | if (err) { | 675 | if (err) { |
346 | dev_err(&pdev->dev, "channel unspecified.\n"); | 676 | dev_err(&pdev->dev, "channel unspecified.\n"); |
@@ -355,6 +685,29 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) | |||
355 | 685 | ||
356 | pa->channel = channel; | 686 | pa->channel = channel; |
357 | 687 | ||
688 | err = of_property_read_u32(pdev->dev.of_node, "qcom,ee", &ee); | ||
689 | if (err) { | ||
690 | dev_err(&pdev->dev, "EE unspecified.\n"); | ||
691 | goto err_put_ctrl; | ||
692 | } | ||
693 | |||
694 | if (ee > 5) { | ||
695 | dev_err(&pdev->dev, "invalid EE (%u) specified\n", ee); | ||
696 | err = -EINVAL; | ||
697 | goto err_put_ctrl; | ||
698 | } | ||
699 | |||
700 | pa->ee = ee; | ||
701 | |||
702 | for (i = 0; i < ARRAY_SIZE(pa->mapping_table); ++i) | ||
703 | pa->mapping_table[i] = readl_relaxed( | ||
704 | pa->cnfg + SPMI_MAPPING_TABLE_REG(i)); | ||
705 | |||
706 | /* Initialize max_apid/min_apid to the opposite bounds, during | ||
707 | * the irq domain translation, we are sure to update these */ | ||
708 | pa->max_apid = 0; | ||
709 | pa->min_apid = PMIC_ARB_MAX_PERIPHS - 1; | ||
710 | |||
358 | platform_set_drvdata(pdev, ctrl); | 711 | platform_set_drvdata(pdev, ctrl); |
359 | raw_spin_lock_init(&pa->lock); | 712 | raw_spin_lock_init(&pa->lock); |
360 | 713 | ||
@@ -362,15 +715,31 @@ static int spmi_pmic_arb_probe(struct platform_device *pdev) | |||
362 | ctrl->read_cmd = pmic_arb_read_cmd; | 715 | ctrl->read_cmd = pmic_arb_read_cmd; |
363 | ctrl->write_cmd = pmic_arb_write_cmd; | 716 | ctrl->write_cmd = pmic_arb_write_cmd; |
364 | 717 | ||
718 | dev_dbg(&pdev->dev, "adding irq domain\n"); | ||
719 | pa->domain = irq_domain_add_tree(pdev->dev.of_node, | ||
720 | &pmic_arb_irq_domain_ops, pa); | ||
721 | if (!pa->domain) { | ||
722 | dev_err(&pdev->dev, "unable to create irq_domain\n"); | ||
723 | err = -ENOMEM; | ||
724 | goto err_put_ctrl; | ||
725 | } | ||
726 | |||
727 | irq_set_handler_data(pa->irq, pa); | ||
728 | irq_set_chained_handler(pa->irq, pmic_arb_chained_irq); | ||
729 | |||
365 | err = spmi_controller_add(ctrl); | 730 | err = spmi_controller_add(ctrl); |
366 | if (err) | 731 | if (err) |
367 | goto err_put_ctrl; | 732 | goto err_domain_remove; |
368 | 733 | ||
369 | dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", | 734 | dev_dbg(&ctrl->dev, "PMIC Arb Version 0x%x\n", |
370 | pmic_arb_base_read(pa, PMIC_ARB_VERSION)); | 735 | pmic_arb_base_read(pa, PMIC_ARB_VERSION)); |
371 | 736 | ||
372 | return 0; | 737 | return 0; |
373 | 738 | ||
739 | err_domain_remove: | ||
740 | irq_set_chained_handler(pa->irq, NULL); | ||
741 | irq_set_handler_data(pa->irq, NULL); | ||
742 | irq_domain_remove(pa->domain); | ||
374 | err_put_ctrl: | 743 | err_put_ctrl: |
375 | spmi_controller_put(ctrl); | 744 | spmi_controller_put(ctrl); |
376 | return err; | 745 | return err; |
@@ -379,7 +748,11 @@ err_put_ctrl: | |||
379 | static int spmi_pmic_arb_remove(struct platform_device *pdev) | 748 | static int spmi_pmic_arb_remove(struct platform_device *pdev) |
380 | { | 749 | { |
381 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); | 750 | struct spmi_controller *ctrl = platform_get_drvdata(pdev); |
751 | struct spmi_pmic_arb_dev *pa = spmi_controller_get_drvdata(ctrl); | ||
382 | spmi_controller_remove(ctrl); | 752 | spmi_controller_remove(ctrl); |
753 | irq_set_chained_handler(pa->irq, NULL); | ||
754 | irq_set_handler_data(pa->irq, NULL); | ||
755 | irq_domain_remove(pa->domain); | ||
383 | spmi_controller_put(ctrl); | 756 | spmi_controller_put(ctrl); |
384 | return 0; | 757 | return 0; |
385 | } | 758 | } |