| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-only |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * Copyright (c) 2015, Sony Mobile Communications AB. |
|---|
| 3 | 4 | * Copyright (c) 2013, The Linux Foundation. All rights reserved. |
|---|
| 4 | | - * |
|---|
| 5 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 6 | | - * it under the terms of the GNU General Public License version 2 and |
|---|
| 7 | | - * only version 2 as published by the Free Software Foundation. |
|---|
| 8 | | - * |
|---|
| 9 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 10 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 11 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 12 | | - * GNU General Public License for more details. |
|---|
| 13 | 5 | */ |
|---|
| 14 | 6 | |
|---|
| 15 | 7 | #include <linux/module.h> |
|---|
| .. | .. |
|---|
| 20 | 12 | #include <linux/pinctrl/pinconf-generic.h> |
|---|
| 21 | 13 | #include <linux/slab.h> |
|---|
| 22 | 14 | #include <linux/regmap.h> |
|---|
| 23 | | -#include <linux/gpio.h> |
|---|
| 15 | +#include <linux/gpio/driver.h> |
|---|
| 24 | 16 | #include <linux/interrupt.h> |
|---|
| 25 | 17 | #include <linux/of_device.h> |
|---|
| 26 | 18 | #include <linux/of_irq.h> |
|---|
| .. | .. |
|---|
| 55 | 47 | |
|---|
| 56 | 48 | #define PM8XXX_MAX_GPIOS 44 |
|---|
| 57 | 49 | |
|---|
| 50 | +#define PM8XXX_GPIO_PHYSICAL_OFFSET 1 |
|---|
| 51 | + |
|---|
| 58 | 52 | /* custom pinconf parameters */ |
|---|
| 59 | 53 | #define PM8XXX_QCOM_DRIVE_STRENGH (PIN_CONFIG_END + 1) |
|---|
| 60 | 54 | #define PM8XXX_QCOM_PULL_UP_STRENGTH (PIN_CONFIG_END + 2) |
|---|
| .. | .. |
|---|
| 62 | 56 | /** |
|---|
| 63 | 57 | * struct pm8xxx_pin_data - dynamic configuration for a pin |
|---|
| 64 | 58 | * @reg: address of the control register |
|---|
| 65 | | - * @irq: IRQ from the PMIC interrupt controller |
|---|
| 66 | 59 | * @power_source: logical selected voltage source, mapping in static data |
|---|
| 67 | 60 | * is used translate to register values |
|---|
| 68 | 61 | * @mode: operating mode for the pin (input/output) |
|---|
| .. | .. |
|---|
| 78 | 71 | */ |
|---|
| 79 | 72 | struct pm8xxx_pin_data { |
|---|
| 80 | 73 | unsigned reg; |
|---|
| 81 | | - int irq; |
|---|
| 82 | 74 | u8 power_source; |
|---|
| 83 | 75 | u8 mode; |
|---|
| 84 | 76 | bool open_drain; |
|---|
| .. | .. |
|---|
| 354 | 346 | return -EINVAL; |
|---|
| 355 | 347 | } |
|---|
| 356 | 348 | pin->pull_up_strength = arg; |
|---|
| 357 | | - /* FALLTHROUGH */ |
|---|
| 349 | + fallthrough; |
|---|
| 358 | 350 | case PIN_CONFIG_BIAS_PULL_UP: |
|---|
| 359 | 351 | pin->bias = pin->pull_up_strength; |
|---|
| 360 | 352 | banks |= BIT(2); |
|---|
| .. | .. |
|---|
| 447 | 439 | .pin_config_group_set = pm8xxx_pin_config_set, |
|---|
| 448 | 440 | }; |
|---|
| 449 | 441 | |
|---|
| 450 | | -static struct pinctrl_desc pm8xxx_pinctrl_desc = { |
|---|
| 442 | +static const struct pinctrl_desc pm8xxx_pinctrl_desc = { |
|---|
| 451 | 443 | .name = "pm8xxx_gpio", |
|---|
| 452 | 444 | .pctlops = &pm8xxx_pinctrl_ops, |
|---|
| 453 | 445 | .pmxops = &pm8xxx_pinmux_ops, |
|---|
| .. | .. |
|---|
| 494 | 486 | { |
|---|
| 495 | 487 | struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); |
|---|
| 496 | 488 | struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; |
|---|
| 489 | + int ret, irq; |
|---|
| 497 | 490 | bool state; |
|---|
| 498 | | - int ret; |
|---|
| 499 | 491 | |
|---|
| 500 | | - if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) { |
|---|
| 501 | | - ret = pin->output_value; |
|---|
| 502 | | - } else { |
|---|
| 503 | | - ret = irq_get_irqchip_state(pin->irq, IRQCHIP_STATE_LINE_LEVEL, &state); |
|---|
| 492 | + if (pin->mode == PM8XXX_GPIO_MODE_OUTPUT) |
|---|
| 493 | + return pin->output_value; |
|---|
| 494 | + |
|---|
| 495 | + irq = chip->to_irq(chip, offset); |
|---|
| 496 | + if (irq >= 0) { |
|---|
| 497 | + ret = irq_get_irqchip_state(irq, IRQCHIP_STATE_LINE_LEVEL, |
|---|
| 498 | + &state); |
|---|
| 504 | 499 | if (!ret) |
|---|
| 505 | 500 | ret = !!state; |
|---|
| 506 | | - } |
|---|
| 501 | + } else |
|---|
| 502 | + ret = -EINVAL; |
|---|
| 507 | 503 | |
|---|
| 508 | 504 | return ret; |
|---|
| 509 | 505 | } |
|---|
| .. | .. |
|---|
| 533 | 529 | if (flags) |
|---|
| 534 | 530 | *flags = gpio_desc->args[1]; |
|---|
| 535 | 531 | |
|---|
| 536 | | - return gpio_desc->args[0] - 1; |
|---|
| 532 | + return gpio_desc->args[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; |
|---|
| 537 | 533 | } |
|---|
| 538 | 534 | |
|---|
| 539 | | - |
|---|
| 540 | | -static int pm8xxx_gpio_to_irq(struct gpio_chip *chip, unsigned offset) |
|---|
| 541 | | -{ |
|---|
| 542 | | - struct pm8xxx_gpio *pctrl = gpiochip_get_data(chip); |
|---|
| 543 | | - struct pm8xxx_pin_data *pin = pctrl->desc.pins[offset].drv_data; |
|---|
| 544 | | - |
|---|
| 545 | | - return pin->irq; |
|---|
| 546 | | -} |
|---|
| 547 | 535 | |
|---|
| 548 | 536 | #ifdef CONFIG_DEBUG_FS |
|---|
| 549 | 537 | #include <linux/seq_file.h> |
|---|
| .. | .. |
|---|
| 571 | 559 | "no", "high", "medium", "low" |
|---|
| 572 | 560 | }; |
|---|
| 573 | 561 | |
|---|
| 574 | | - seq_printf(s, " gpio%-2d:", offset + 1); |
|---|
| 562 | + seq_printf(s, " gpio%-2d:", offset + PM8XXX_GPIO_PHYSICAL_OFFSET); |
|---|
| 575 | 563 | if (pin->disable) { |
|---|
| 576 | 564 | seq_puts(s, " ---"); |
|---|
| 577 | 565 | } else { |
|---|
| .. | .. |
|---|
| 608 | 596 | .get = pm8xxx_gpio_get, |
|---|
| 609 | 597 | .set = pm8xxx_gpio_set, |
|---|
| 610 | 598 | .of_xlate = pm8xxx_gpio_of_xlate, |
|---|
| 611 | | - .to_irq = pm8xxx_gpio_to_irq, |
|---|
| 612 | 599 | .dbg_show = pm8xxx_gpio_dbg_show, |
|---|
| 613 | 600 | .owner = THIS_MODULE, |
|---|
| 614 | 601 | }; |
|---|
| .. | .. |
|---|
| 664 | 651 | return 0; |
|---|
| 665 | 652 | } |
|---|
| 666 | 653 | |
|---|
| 654 | +static struct irq_chip pm8xxx_irq_chip = { |
|---|
| 655 | + .name = "ssbi-gpio", |
|---|
| 656 | + .irq_mask_ack = irq_chip_mask_ack_parent, |
|---|
| 657 | + .irq_unmask = irq_chip_unmask_parent, |
|---|
| 658 | + .irq_set_type = irq_chip_set_type_parent, |
|---|
| 659 | + .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SKIP_SET_WAKE, |
|---|
| 660 | +}; |
|---|
| 661 | + |
|---|
| 662 | +static int pm8xxx_domain_translate(struct irq_domain *domain, |
|---|
| 663 | + struct irq_fwspec *fwspec, |
|---|
| 664 | + unsigned long *hwirq, |
|---|
| 665 | + unsigned int *type) |
|---|
| 666 | +{ |
|---|
| 667 | + struct pm8xxx_gpio *pctrl = container_of(domain->host_data, |
|---|
| 668 | + struct pm8xxx_gpio, chip); |
|---|
| 669 | + |
|---|
| 670 | + if (fwspec->param_count != 2 || fwspec->param[0] < 1 || |
|---|
| 671 | + fwspec->param[0] > pctrl->chip.ngpio) |
|---|
| 672 | + return -EINVAL; |
|---|
| 673 | + |
|---|
| 674 | + *hwirq = fwspec->param[0] - PM8XXX_GPIO_PHYSICAL_OFFSET; |
|---|
| 675 | + *type = fwspec->param[1]; |
|---|
| 676 | + |
|---|
| 677 | + return 0; |
|---|
| 678 | +} |
|---|
| 679 | + |
|---|
| 680 | +static unsigned int pm8xxx_child_offset_to_irq(struct gpio_chip *chip, |
|---|
| 681 | + unsigned int offset) |
|---|
| 682 | +{ |
|---|
| 683 | + return offset + PM8XXX_GPIO_PHYSICAL_OFFSET; |
|---|
| 684 | +} |
|---|
| 685 | + |
|---|
| 686 | +static int pm8xxx_child_to_parent_hwirq(struct gpio_chip *chip, |
|---|
| 687 | + unsigned int child_hwirq, |
|---|
| 688 | + unsigned int child_type, |
|---|
| 689 | + unsigned int *parent_hwirq, |
|---|
| 690 | + unsigned int *parent_type) |
|---|
| 691 | +{ |
|---|
| 692 | + *parent_hwirq = child_hwirq + 0xc0; |
|---|
| 693 | + *parent_type = child_type; |
|---|
| 694 | + |
|---|
| 695 | + return 0; |
|---|
| 696 | +} |
|---|
| 697 | + |
|---|
| 667 | 698 | static const struct of_device_id pm8xxx_gpio_of_match[] = { |
|---|
| 668 | | - { .compatible = "qcom,pm8018-gpio" }, |
|---|
| 669 | | - { .compatible = "qcom,pm8038-gpio" }, |
|---|
| 670 | | - { .compatible = "qcom,pm8058-gpio" }, |
|---|
| 671 | | - { .compatible = "qcom,pm8917-gpio" }, |
|---|
| 672 | | - { .compatible = "qcom,pm8921-gpio" }, |
|---|
| 673 | | - { .compatible = "qcom,ssbi-gpio" }, |
|---|
| 699 | + { .compatible = "qcom,pm8018-gpio", .data = (void *) 6 }, |
|---|
| 700 | + { .compatible = "qcom,pm8038-gpio", .data = (void *) 12 }, |
|---|
| 701 | + { .compatible = "qcom,pm8058-gpio", .data = (void *) 44 }, |
|---|
| 702 | + { .compatible = "qcom,pm8917-gpio", .data = (void *) 38 }, |
|---|
| 703 | + { .compatible = "qcom,pm8921-gpio", .data = (void *) 44 }, |
|---|
| 674 | 704 | { }, |
|---|
| 675 | 705 | }; |
|---|
| 676 | 706 | MODULE_DEVICE_TABLE(of, pm8xxx_gpio_of_match); |
|---|
| .. | .. |
|---|
| 678 | 708 | static int pm8xxx_gpio_probe(struct platform_device *pdev) |
|---|
| 679 | 709 | { |
|---|
| 680 | 710 | struct pm8xxx_pin_data *pin_data; |
|---|
| 711 | + struct irq_domain *parent_domain; |
|---|
| 712 | + struct device_node *parent_node; |
|---|
| 681 | 713 | struct pinctrl_pin_desc *pins; |
|---|
| 714 | + struct gpio_irq_chip *girq; |
|---|
| 682 | 715 | struct pm8xxx_gpio *pctrl; |
|---|
| 683 | | - int ret; |
|---|
| 684 | | - int i, npins; |
|---|
| 716 | + int ret, i; |
|---|
| 685 | 717 | |
|---|
| 686 | 718 | pctrl = devm_kzalloc(&pdev->dev, sizeof(*pctrl), GFP_KERNEL); |
|---|
| 687 | 719 | if (!pctrl) |
|---|
| 688 | 720 | return -ENOMEM; |
|---|
| 689 | 721 | |
|---|
| 690 | 722 | pctrl->dev = &pdev->dev; |
|---|
| 691 | | - npins = platform_irq_count(pdev); |
|---|
| 692 | | - if (!npins) |
|---|
| 693 | | - return -EINVAL; |
|---|
| 694 | | - if (npins < 0) |
|---|
| 695 | | - return npins; |
|---|
| 696 | | - pctrl->npins = npins; |
|---|
| 723 | + pctrl->npins = (uintptr_t) device_get_match_data(&pdev->dev); |
|---|
| 697 | 724 | |
|---|
| 698 | 725 | pctrl->regmap = dev_get_regmap(pdev->dev.parent, NULL); |
|---|
| 699 | 726 | if (!pctrl->regmap) { |
|---|
| .. | .. |
|---|
| 720 | 747 | |
|---|
| 721 | 748 | for (i = 0; i < pctrl->desc.npins; i++) { |
|---|
| 722 | 749 | pin_data[i].reg = SSBI_REG_ADDR_GPIO(i); |
|---|
| 723 | | - pin_data[i].irq = platform_get_irq(pdev, i); |
|---|
| 724 | | - if (pin_data[i].irq < 0) { |
|---|
| 725 | | - dev_err(&pdev->dev, |
|---|
| 726 | | - "missing interrupts for pin %d\n", i); |
|---|
| 727 | | - return pin_data[i].irq; |
|---|
| 728 | | - } |
|---|
| 729 | 750 | |
|---|
| 730 | 751 | ret = pm8xxx_pin_populate(pctrl, &pin_data[i]); |
|---|
| 731 | 752 | if (ret) |
|---|
| .. | .. |
|---|
| 756 | 777 | pctrl->chip.of_gpio_n_cells = 2; |
|---|
| 757 | 778 | pctrl->chip.label = dev_name(pctrl->dev); |
|---|
| 758 | 779 | pctrl->chip.ngpio = pctrl->npins; |
|---|
| 780 | + |
|---|
| 781 | + parent_node = of_irq_find_parent(pctrl->dev->of_node); |
|---|
| 782 | + if (!parent_node) |
|---|
| 783 | + return -ENXIO; |
|---|
| 784 | + |
|---|
| 785 | + parent_domain = irq_find_host(parent_node); |
|---|
| 786 | + of_node_put(parent_node); |
|---|
| 787 | + if (!parent_domain) |
|---|
| 788 | + return -ENXIO; |
|---|
| 789 | + |
|---|
| 790 | + girq = &pctrl->chip.irq; |
|---|
| 791 | + girq->chip = &pm8xxx_irq_chip; |
|---|
| 792 | + girq->default_type = IRQ_TYPE_NONE; |
|---|
| 793 | + girq->handler = handle_level_irq; |
|---|
| 794 | + girq->fwnode = of_node_to_fwnode(pctrl->dev->of_node); |
|---|
| 795 | + girq->parent_domain = parent_domain; |
|---|
| 796 | + girq->child_to_parent_hwirq = pm8xxx_child_to_parent_hwirq; |
|---|
| 797 | + girq->populate_parent_alloc_arg = gpiochip_populate_parent_fwspec_twocell; |
|---|
| 798 | + girq->child_offset_to_irq = pm8xxx_child_offset_to_irq; |
|---|
| 799 | + girq->child_irq_domain_ops.translate = pm8xxx_domain_translate; |
|---|
| 800 | + |
|---|
| 759 | 801 | ret = gpiochip_add_data(&pctrl->chip, pctrl); |
|---|
| 760 | 802 | if (ret) { |
|---|
| 761 | 803 | dev_err(&pdev->dev, "failed register gpiochip\n"); |
|---|