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 | |
| 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')
| -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 | } |
