diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-03 20:29:50 +0300 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2019-12-03 20:29:50 +0300 |
commit | b22bfea7f16cda6e08918a3e9c7b4b99eb95ea70 (patch) | |
tree | 4e4bf56f53d7c4e6eef8d90b0a593e6c0a21b5d0 /drivers/pinctrl | |
parent | 2dff2a1c9b7cc83529119eb04cef1d7b68e11352 (diff) | |
parent | 407e62f52aadd8124dcba407f18a03aedce9b86a (diff) | |
download | linux-b22bfea7f16cda6e08918a3e9c7b4b99eb95ea70.tar.xz |
Merge branch 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip
Pull irq updates from Ingo Molnar:
"Most of the IRQ subsystem changes in this cycle were irq-chip driver
updates:
- Qualcomm PDC wakeup interrupt support
- Layerscape external IRQ support
- Broadcom bcm7038 PM and wakeup support
- Ingenic driver cleanup and modernization
- GICv3 ITS preparation for GICv4.1 updates
- GICv4 fixes
There's also the series from Frederic Weisbecker that fixes memory
ordering bugs for the irq-work logic, whose primary fix is to turn
work->irq_work.flags into an atomic variable and then convert the
complex (and buggy) atomic_cmpxchg() loop in irq_work_claim() into a
much simpler atomic_fetch_or() call.
There are also various smaller cleanups"
* 'irq-core-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tip/tip: (44 commits)
pinctrl/sdm845: Add PDC wakeup interrupt map for GPIOs
pinctrl/msm: Setup GPIO chip in hierarchy
irqchip/qcom-pdc: Add irqchip set/get state calls
irqchip/qcom-pdc: Add irqdomain for wakeup capable GPIOs
irqchip/qcom-pdc: Do not toggle IRQ_ENABLE during mask/unmask
irqchip/qcom-pdc: Update max PDC interrupts
of/irq: Document properties for wakeup interrupt parent
genirq: Introduce irq_chip_get/set_parent_state calls
irqdomain: Add bus token DOMAIN_BUS_WAKEUP
genirq: Fix function documentation of __irq_alloc_descs()
irq_work: Fix IRQ_WORK_BUSY bit clearing
irqchip/ti-sci-inta: Use ERR_CAST inlined function instead of ERR_PTR(PTR_ERR(...))
irq_work: Slightly simplify IRQ_WORK_PENDING clearing
irq_work: Fix irq_work_claim() memory ordering
irq_work: Convert flags to atomic_t
irqchip: Ingenic: Add process for more than one irq at the same time.
irqchip: ingenic: Alloc generic chips from IRQ domain
irqchip: ingenic: Get virq number from IRQ domain
irqchip: ingenic: Error out if IRQ domain creation failed
irqchip: ingenic: Drop redundant irq_suspend / irq_resume functions
...
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r-- | drivers/pinctrl/qcom/pinctrl-msm.c | 112 | ||||
-rw-r--r-- | drivers/pinctrl/qcom/pinctrl-msm.h | 14 | ||||
-rw-r--r-- | drivers/pinctrl/qcom/pinctrl-sdm845.c | 23 |
3 files changed, 146 insertions, 3 deletions
diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 62fcae9f05ae..5d6f9f61ce02 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -23,6 +23,8 @@ #include <linux/pm.h> #include <linux/log2.h> +#include <linux/soc/qcom/irq.h> + #include "../core.h" #include "../pinconf.h" #include "pinctrl-msm.h" @@ -44,6 +46,7 @@ * @enabled_irqs: Bitmap of currently enabled irqs. * @dual_edge_irqs: Bitmap of irqs that need sw emulated dual edge * detection. + * @skip_wake_irqs: Skip IRQs that are handled by wakeup interrupt controller * @soc; Reference to soc_data of platform specific data. * @regs: Base addresses for the TLMM tiles. */ @@ -61,6 +64,7 @@ struct msm_pinctrl { DECLARE_BITMAP(dual_edge_irqs, MAX_NR_GPIO); DECLARE_BITMAP(enabled_irqs, MAX_NR_GPIO); + DECLARE_BITMAP(skip_wake_irqs, MAX_NR_GPIO); const struct msm_pinctrl_soc_data *soc; void __iomem *regs[MAX_NR_TILES]; @@ -707,6 +711,12 @@ static void msm_gpio_irq_mask(struct irq_data *d) unsigned long flags; u32 val; + if (d->parent_data) + irq_chip_mask_parent(d); + + if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return; + g = &pctrl->soc->groups[d->hwirq]; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -751,6 +761,12 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear) unsigned long flags; u32 val; + if (d->parent_data) + irq_chip_unmask_parent(d); + + if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return; + g = &pctrl->soc->groups[d->hwirq]; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -778,10 +794,35 @@ static void msm_gpio_irq_clear_unmask(struct irq_data *d, bool status_clear) static void msm_gpio_irq_enable(struct irq_data *d) { + /* + * Clear the interrupt that may be pending before we enable + * the line. + * This is especially a problem with the GPIOs routed to the + * PDC. These GPIOs are direct-connect interrupts to the GIC. + * Disabling the interrupt line at the PDC does not prevent + * the interrupt from being latched at the GIC. The state at + * GIC needs to be cleared before enabling. + */ + if (d->parent_data) { + irq_chip_set_parent_state(d, IRQCHIP_STATE_PENDING, 0); + irq_chip_enable_parent(d); + } msm_gpio_irq_clear_unmask(d, true); } +static void msm_gpio_irq_disable(struct irq_data *d) +{ + struct gpio_chip *gc = irq_data_get_irq_chip_data(d); + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + + if (d->parent_data) + irq_chip_disable_parent(d); + + if (!test_bit(d->hwirq, pctrl->skip_wake_irqs)) + msm_gpio_irq_mask(d); +} + static void msm_gpio_irq_unmask(struct irq_data *d) { msm_gpio_irq_clear_unmask(d, false); @@ -795,6 +836,9 @@ static void msm_gpio_irq_ack(struct irq_data *d) unsigned long flags; u32 val; + if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return; + g = &pctrl->soc->groups[d->hwirq]; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -820,6 +864,12 @@ static int msm_gpio_irq_set_type(struct irq_data *d, unsigned int type) unsigned long flags; u32 val; + if (d->parent_data) + irq_chip_set_type_parent(d, type); + + if (test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return 0; + g = &pctrl->soc->groups[d->hwirq]; raw_spin_lock_irqsave(&pctrl->lock, flags); @@ -912,6 +962,15 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) struct msm_pinctrl *pctrl = gpiochip_get_data(gc); unsigned long flags; + /* + * While they may not wake up when the TLMM is powered off, + * some GPIOs would like to wakeup the system from suspend + * when TLMM is powered on. To allow that, enable the GPIO + * summary line to be wakeup capable at GIC. + */ + if (d->parent_data) + irq_chip_set_wake_parent(d, on); + raw_spin_lock_irqsave(&pctrl->lock, flags); irq_set_irq_wake(pctrl->irq, on); @@ -990,6 +1049,30 @@ static void msm_gpio_irq_handler(struct irq_desc *desc) chained_irq_exit(chip, desc); } +static int msm_gpio_wakeirq(struct gpio_chip *gc, + unsigned int child, + unsigned int child_type, + unsigned int *parent, + unsigned int *parent_type) +{ + struct msm_pinctrl *pctrl = gpiochip_get_data(gc); + const struct msm_gpio_wakeirq_map *map; + int i; + + *parent = GPIO_NO_WAKE_IRQ; + *parent_type = IRQ_TYPE_EDGE_RISING; + + for (i = 0; i < pctrl->soc->nwakeirq_map; i++) { + map = &pctrl->soc->wakeirq_map[i]; + if (map->gpio == child) { + *parent = map->wakeirq; + break; + } + } + + return 0; +} + static bool msm_gpio_needs_valid_mask(struct msm_pinctrl *pctrl) { if (pctrl->soc->reserved_gpios) @@ -1002,8 +1085,10 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) { struct gpio_chip *chip; struct gpio_irq_chip *girq; - int ret; - unsigned ngpio = pctrl->soc->ngpios; + int i, ret; + unsigned gpio, ngpio = pctrl->soc->ngpios; + struct device_node *np; + bool skip; if (WARN_ON(ngpio > MAX_NR_GPIO)) return -EINVAL; @@ -1020,17 +1105,40 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.name = "msmgpio"; pctrl->irq_chip.irq_enable = msm_gpio_irq_enable; + pctrl->irq_chip.irq_disable = msm_gpio_irq_disable; pctrl->irq_chip.irq_mask = msm_gpio_irq_mask; pctrl->irq_chip.irq_unmask = msm_gpio_irq_unmask; pctrl->irq_chip.irq_ack = msm_gpio_irq_ack; + pctrl->irq_chip.irq_eoi = irq_chip_eoi_parent; pctrl->irq_chip.irq_set_type = msm_gpio_irq_set_type; pctrl->irq_chip.irq_set_wake = msm_gpio_irq_set_wake; pctrl->irq_chip.irq_request_resources = msm_gpio_irq_reqres; pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres; + np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0); + if (np) { + chip->irq.parent_domain = irq_find_matching_host(np, + DOMAIN_BUS_WAKEUP); + of_node_put(np); + if (!chip->irq.parent_domain) + return -EPROBE_DEFER; + chip->irq.child_to_parent_hwirq = msm_gpio_wakeirq; + + /* + * Let's skip handling the GPIOs, if the parent irqchip + * is handling the direct connect IRQ of the GPIO. + */ + skip = irq_domain_qcom_handle_wakeup(chip->irq.parent_domain); + for (i = 0; skip && i < pctrl->soc->nwakeirq_map; i++) { + gpio = pctrl->soc->wakeirq_map[i].gpio; + set_bit(gpio, pctrl->skip_wake_irqs); + } + } + girq = &chip->irq; girq->chip = &pctrl->irq_chip; girq->parent_handler = msm_gpio_irq_handler; + girq->fwnode = pctrl->dev->fwnode; girq->num_parents = 1; girq->parents = devm_kcalloc(pctrl->dev, 1, sizeof(*girq->parents), GFP_KERNEL); diff --git a/drivers/pinctrl/qcom/pinctrl-msm.h b/drivers/pinctrl/qcom/pinctrl-msm.h index 48569cda8471..9452da18a78b 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.h +++ b/drivers/pinctrl/qcom/pinctrl-msm.h @@ -92,6 +92,16 @@ struct msm_pingroup { }; /** + * struct msm_gpio_wakeirq_map - Map of GPIOs and their wakeup pins + * @gpio: The GPIOs that are wakeup capable + * @wakeirq: The interrupt at the always-on interrupt controller + */ +struct msm_gpio_wakeirq_map { + unsigned int gpio; + unsigned int wakeirq; +}; + +/** * struct msm_pinctrl_soc_data - Qualcomm pin controller driver configuration * @pins: An array describing all pins the pin controller affects. * @npins: The number of entries in @pins. @@ -101,6 +111,8 @@ struct msm_pingroup { * @ngroups: The numbmer of entries in @groups. * @ngpio: The number of pingroups the driver should expose as GPIOs. * @pull_no_keeper: The SoC does not support keeper bias. + * @wakeirq_map: The map of wakeup capable GPIOs and the pin at PDC/MPM + * @nwakeirq_map: The number of entries in @wakeirq_map */ struct msm_pinctrl_soc_data { const struct pinctrl_pin_desc *pins; @@ -114,6 +126,8 @@ struct msm_pinctrl_soc_data { const char *const *tiles; unsigned int ntiles; const int *reserved_gpios; + const struct msm_gpio_wakeirq_map *wakeirq_map; + unsigned int nwakeirq_map; }; extern const struct dev_pm_ops msm_pinctrl_dev_pm_ops; diff --git a/drivers/pinctrl/qcom/pinctrl-sdm845.c b/drivers/pinctrl/qcom/pinctrl-sdm845.c index ce495970459d..2834d2c1338c 100644 --- a/drivers/pinctrl/qcom/pinctrl-sdm845.c +++ b/drivers/pinctrl/qcom/pinctrl-sdm845.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2016-2018, The Linux Foundation. All rights reserved. + * Copyright (c) 2016-2019, The Linux Foundation. All rights reserved. */ #include <linux/acpi.h> @@ -1282,6 +1282,24 @@ static const int sdm845_acpi_reserved_gpios[] = { 0, 1, 2, 3, 81, 82, 83, 84, -1 }; +static const struct msm_gpio_wakeirq_map sdm845_pdc_map[] = { + { 1, 30 }, { 3, 31 }, { 5, 32 }, { 10, 33 }, { 11, 34 }, + { 20, 35 }, { 22, 36 }, { 24, 37 }, { 26, 38 }, { 30, 39 }, + { 31, 117 }, { 32, 41 }, { 34, 42 }, { 36, 43 }, { 37, 44 }, + { 38, 45 }, { 39, 46 }, { 40, 47 }, { 41, 115 }, { 43, 49 }, + { 44, 50 }, { 46, 51 }, { 48, 52 }, { 49, 118 }, { 52, 54 }, + { 53, 55 }, { 54, 56 }, { 56, 57 }, { 57, 58 }, { 58, 59 }, + { 59, 60 }, { 60, 61 }, { 61, 62 }, { 62, 63 }, { 63, 64 }, + { 64, 65 }, { 66, 66 }, { 68, 67 }, { 71, 68 }, { 73, 69 }, + { 77, 70 }, { 78, 71 }, { 79, 72 }, { 80, 73 }, { 84, 74 }, + { 85, 75 }, { 86, 76 }, { 88, 77 }, { 89, 116 }, { 91, 79 }, + { 92, 80 }, { 95, 81 }, { 96, 82 }, { 97, 83 }, { 101, 84 }, + { 103, 85 }, { 104, 86 }, { 115, 90 }, { 116, 91 }, { 117, 92 }, + { 118, 93 }, { 119, 94 }, { 120, 95 }, { 121, 96 }, { 122, 97 }, + { 123, 98 }, { 124, 99 }, { 125, 100 }, { 127, 102 }, { 128, 103 }, + { 129, 104 }, { 130, 105 }, { 132, 106 }, { 133, 107 }, { 145, 108 }, +}; + static const struct msm_pinctrl_soc_data sdm845_pinctrl = { .pins = sdm845_pins, .npins = ARRAY_SIZE(sdm845_pins), @@ -1290,6 +1308,9 @@ static const struct msm_pinctrl_soc_data sdm845_pinctrl = { .groups = sdm845_groups, .ngroups = ARRAY_SIZE(sdm845_groups), .ngpios = 151, + .wakeirq_map = sdm845_pdc_map, + .nwakeirq_map = ARRAY_SIZE(sdm845_pdc_map), + }; static const struct msm_pinctrl_soc_data sdm845_acpi_pinctrl = { |