.. | .. |
---|
| 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"); |
---|