From c5f72aeb659eb2f809b9531d759651514d42aa3a Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:01:59 +0530 Subject: [PATCH 1/6] pinctrl: qcom: Set IRQCHIP_SET_TYPE_MASKED and IRQCHIP_MASK_ON_SUSPEND flags Both IRQCHIP_SET_TYPE_MASKED and IRQCHIP_MASK_ON_SUSPEND flags are already set for msmgpio's parent PDC irqchip but GPIO interrupts do not get masked during suspend or during setting irq type since genirq checks irqchip flag of msmgpio irqchip which forwards these calls to its parent PDC irqchip. Add irqchip specific flags for msmgpio irqchip to mask non wakeirqs during suspend and mask before setting irq type. Masking before changing type make sures any spurious interrupt is not detected during this operation. Fixes: e35a6ae0eb3a ("pinctrl/msm: Setup GPIO chip in hierarchy") Signed-off-by: Maulik Shah Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Douglas Anderson Acked-by: Bjorn Andersson Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1601267524-20199-2-git-send-email-mkshah@codeaurora.org --- drivers/pinctrl/qcom/pinctrl-msm.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index a2567e772cd5..1c23f5c88fdd 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1243,6 +1243,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.irq_release_resources = msm_gpio_irq_relres; pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity; pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity; + pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND | + IRQCHIP_SET_TYPE_MASKED; np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0); if (np) { From f41aaca593377a4fe3984459fd4539481263b4cd Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:02:00 +0530 Subject: [PATCH 2/6] pinctrl: qcom: Use return value from irq_set_wake() call msmgpio irqchip was not using return value of irq_set_irq_wake() callback since previously GIC-v3 irqchip neither had IRQCHIP_SKIP_SET_WAKE flag nor it implemented .irq_set_wake callback. This lead to irq_set_irq_wake() return error -ENXIO. However from 'commit 4110b5cbb014 ("irqchip/gic-v3: Allow interrupt to be configured as wake-up sources")' GIC irqchip has IRQCHIP_SKIP_SET_WAKE flag. Use return value from irq_set_irq_wake() and irq_chip_set_wake_parent() instead of always returning success. Fixes: e35a6ae0eb3a ("pinctrl/msm: Setup GPIO chip in hierarchy") Signed-off-by: Maulik Shah Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Douglas Anderson Reviewed-by: Stephen Boyd Acked-by: Bjorn Andersson Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1601267524-20199-3-git-send-email-mkshah@codeaurora.org --- drivers/pinctrl/qcom/pinctrl-msm.c | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 1c23f5c88fdd..1df232266f63 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1077,12 +1077,10 @@ static int msm_gpio_irq_set_wake(struct irq_data *d, unsigned int on) * 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); + if (d->parent_data && test_bit(d->hwirq, pctrl->skip_wake_irqs)) + return irq_chip_set_wake_parent(d, on); - irq_set_irq_wake(pctrl->irq, on); - - return 0; + return irq_set_irq_wake(pctrl->irq, on); } static int msm_gpio_irq_reqres(struct irq_data *d) From 90428a8eb4947f9c7c905a178f3520dc7e2ee6d2 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:02:01 +0530 Subject: [PATCH 3/6] genirq/PM: Introduce IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag An interrupt that is disabled/masked but set for wakeup may still need to be able to wake up the system from sleep states like "suspend to RAM". To that effect, introduce the IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag. If the irqchip have this flag set, the irq PM code will enable/unmask the irqs that are marked for wakeup, but that are in a disabled state. On resume, such irqs will be restored back to their disabled state. Suggested-by: Thomas Gleixner Signed-off-by: Maulik Shah [maz: commit message fix-up] Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Thomas Gleixner Reviewed-by: Douglas Anderson Link: https://lore.kernel.org/r/1601267524-20199-4-git-send-email-mkshah@codeaurora.org --- include/linux/irq.h | 49 +++++++++++++++++++++++++++----------------- kernel/irq/debugfs.c | 3 +++ kernel/irq/pm.c | 34 ++++++++++++++++++++++++++---- 3 files changed, 63 insertions(+), 23 deletions(-) diff --git a/include/linux/irq.h b/include/linux/irq.h index 1b7f4dfee35b..a8b84b88e673 100644 --- a/include/linux/irq.h +++ b/include/linux/irq.h @@ -215,6 +215,8 @@ struct irq_data { * from actual interrupt context. * IRQD_AFFINITY_ON_ACTIVATE - Affinity is set on activation. Don't call * irq_chip::irq_set_affinity() when deactivated. + * IRQD_IRQ_ENABLED_ON_SUSPEND - Interrupt is enabled on suspend by irq pm if + * irqchip have flag IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND set. */ enum { IRQD_TRIGGER_MASK = 0xf, @@ -240,6 +242,7 @@ enum { IRQD_MSI_NOMASK_QUIRK = (1 << 27), IRQD_HANDLE_ENFORCE_IRQCTX = (1 << 28), IRQD_AFFINITY_ON_ACTIVATE = (1 << 29), + IRQD_IRQ_ENABLED_ON_SUSPEND = (1 << 30), }; #define __irqd_to_state(d) ACCESS_PRIVATE((d)->common, state_use_accessors) @@ -319,6 +322,11 @@ static inline bool irqd_is_handle_enforce_irqctx(struct irq_data *d) return __irqd_to_state(d) & IRQD_HANDLE_ENFORCE_IRQCTX; } +static inline bool irqd_is_enabled_on_suspend(struct irq_data *d) +{ + return __irqd_to_state(d) & IRQD_IRQ_ENABLED_ON_SUSPEND; +} + static inline bool irqd_is_wakeup_set(struct irq_data *d) { return __irqd_to_state(d) & IRQD_WAKEUP_STATE; @@ -545,27 +553,30 @@ struct irq_chip { /* * irq_chip specific flags * - * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() - * IRQCHIP_EOI_IF_HANDLED: Only issue irq_eoi() when irq was handled - * IRQCHIP_MASK_ON_SUSPEND: Mask non wake irqs in the suspend path - * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks - * when irq enabled - * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip - * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask - * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode - * IRQCHIP_SUPPORTS_LEVEL_MSI Chip can provide two doorbells for Level MSIs - * IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips + * IRQCHIP_SET_TYPE_MASKED: Mask before calling chip.irq_set_type() + * IRQCHIP_EOI_IF_HANDLED: Only issue irq_eoi() when irq was handled + * IRQCHIP_MASK_ON_SUSPEND: Mask non wake irqs in the suspend path + * IRQCHIP_ONOFFLINE_ENABLED: Only call irq_on/off_line callbacks + * when irq enabled + * IRQCHIP_SKIP_SET_WAKE: Skip chip.irq_set_wake(), for this irq chip + * IRQCHIP_ONESHOT_SAFE: One shot does not require mask/unmask + * IRQCHIP_EOI_THREADED: Chip requires eoi() on unmask in threaded mode + * IRQCHIP_SUPPORTS_LEVEL_MSI: Chip can provide two doorbells for Level MSIs + * IRQCHIP_SUPPORTS_NMI: Chip can deliver NMIs, only for root irqchips + * IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND: Invokes __enable_irq()/__disable_irq() for wake irqs + * in the suspend path if they are in disabled state */ enum { - IRQCHIP_SET_TYPE_MASKED = (1 << 0), - IRQCHIP_EOI_IF_HANDLED = (1 << 1), - IRQCHIP_MASK_ON_SUSPEND = (1 << 2), - IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), - IRQCHIP_SKIP_SET_WAKE = (1 << 4), - IRQCHIP_ONESHOT_SAFE = (1 << 5), - IRQCHIP_EOI_THREADED = (1 << 6), - IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7), - IRQCHIP_SUPPORTS_NMI = (1 << 8), + IRQCHIP_SET_TYPE_MASKED = (1 << 0), + IRQCHIP_EOI_IF_HANDLED = (1 << 1), + IRQCHIP_MASK_ON_SUSPEND = (1 << 2), + IRQCHIP_ONOFFLINE_ENABLED = (1 << 3), + IRQCHIP_SKIP_SET_WAKE = (1 << 4), + IRQCHIP_ONESHOT_SAFE = (1 << 5), + IRQCHIP_EOI_THREADED = (1 << 6), + IRQCHIP_SUPPORTS_LEVEL_MSI = (1 << 7), + IRQCHIP_SUPPORTS_NMI = (1 << 8), + IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND = (1 << 9), }; #include diff --git a/kernel/irq/debugfs.c b/kernel/irq/debugfs.c index b95ff5d5f4bd..b6e10945e8be 100644 --- a/kernel/irq/debugfs.c +++ b/kernel/irq/debugfs.c @@ -57,6 +57,7 @@ static const struct irq_bit_descr irqchip_flags[] = { BIT_MASK_DESCR(IRQCHIP_EOI_THREADED), BIT_MASK_DESCR(IRQCHIP_SUPPORTS_LEVEL_MSI), BIT_MASK_DESCR(IRQCHIP_SUPPORTS_NMI), + BIT_MASK_DESCR(IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND), }; static void @@ -125,6 +126,8 @@ static const struct irq_bit_descr irqdata_states[] = { BIT_MASK_DESCR(IRQD_DEFAULT_TRIGGER_SET), BIT_MASK_DESCR(IRQD_HANDLE_ENFORCE_IRQCTX), + + BIT_MASK_DESCR(IRQD_IRQ_ENABLED_ON_SUSPEND), }; static const struct irq_bit_descr irqdesc_states[] = { diff --git a/kernel/irq/pm.c b/kernel/irq/pm.c index c6c7e187ae74..ce0adb22ee96 100644 --- a/kernel/irq/pm.c +++ b/kernel/irq/pm.c @@ -69,12 +69,26 @@ void irq_pm_remove_action(struct irq_desc *desc, struct irqaction *action) static bool suspend_device_irq(struct irq_desc *desc) { + unsigned long chipflags = irq_desc_get_chip(desc)->flags; + struct irq_data *irqd = &desc->irq_data; + if (!desc->action || irq_desc_is_chained(desc) || desc->no_suspend_depth) return false; - if (irqd_is_wakeup_set(&desc->irq_data)) { - irqd_set(&desc->irq_data, IRQD_WAKEUP_ARMED); + if (irqd_is_wakeup_set(irqd)) { + irqd_set(irqd, IRQD_WAKEUP_ARMED); + + if ((chipflags & IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND) && + irqd_irq_disabled(irqd)) { + /* + * Interrupt marked for wakeup is in disabled state. + * Enable interrupt here to unmask/enable in irqchip + * to be able to resume with such interrupts. + */ + __enable_irq(desc); + irqd_set(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND); + } /* * We return true here to force the caller to issue * synchronize_irq(). We need to make sure that the @@ -93,7 +107,7 @@ static bool suspend_device_irq(struct irq_desc *desc) * chip level. The chip implementation indicates that with * IRQCHIP_MASK_ON_SUSPEND. */ - if (irq_desc_get_chip(desc)->flags & IRQCHIP_MASK_ON_SUSPEND) + if (chipflags & IRQCHIP_MASK_ON_SUSPEND) mask_irq(desc); return true; } @@ -137,7 +151,19 @@ EXPORT_SYMBOL_GPL(suspend_device_irqs); static void resume_irq(struct irq_desc *desc) { - irqd_clear(&desc->irq_data, IRQD_WAKEUP_ARMED); + struct irq_data *irqd = &desc->irq_data; + + irqd_clear(irqd, IRQD_WAKEUP_ARMED); + + if (irqd_is_enabled_on_suspend(irqd)) { + /* + * Interrupt marked for wakeup was enabled during suspend + * entry. Disable such interrupts to restore them back to + * original state. + */ + __disable_irq(desc); + irqd_clear(irqd, IRQD_IRQ_ENABLED_ON_SUSPEND); + } if (desc->istate & IRQS_SUSPENDED) goto resume; From dd87bd09822c294a3c7c4daf11f11a9f81222f80 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:02:02 +0530 Subject: [PATCH 4/6] pinctrl: qcom: Set IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag Set IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag to enable/unmask the wakeirqs during suspend entry. Signed-off-by: Maulik Shah Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1601267524-20199-5-git-send-email-mkshah@codeaurora.org --- drivers/pinctrl/qcom/pinctrl-msm.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/pinctrl/qcom/pinctrl-msm.c b/drivers/pinctrl/qcom/pinctrl-msm.c index 1df232266f63..c4bcda90aac4 100644 --- a/drivers/pinctrl/qcom/pinctrl-msm.c +++ b/drivers/pinctrl/qcom/pinctrl-msm.c @@ -1242,7 +1242,8 @@ static int msm_gpio_init(struct msm_pinctrl *pctrl) pctrl->irq_chip.irq_set_affinity = msm_gpio_irq_set_affinity; pctrl->irq_chip.irq_set_vcpu_affinity = msm_gpio_irq_set_vcpu_affinity; pctrl->irq_chip.flags = IRQCHIP_MASK_ON_SUSPEND | - IRQCHIP_SET_TYPE_MASKED; + IRQCHIP_SET_TYPE_MASKED | + IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND; np = of_parse_phandle(pctrl->dev->of_node, "wakeup-parent", 0); if (np) { From 299d7890792e75065b906f83fcb0ca92e5c8c072 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:02:03 +0530 Subject: [PATCH 5/6] irqchip/qcom-pdc: Set IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag Set IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND flag to enable/unmask the wakeirqs during suspend entry. Signed-off-by: Maulik Shah Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1601267524-20199-6-git-send-email-mkshah@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index 6ae9e1f0819d..acc0620f315a 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -205,7 +205,8 @@ static struct irq_chip qcom_pdc_gic_chip = { .irq_set_type = qcom_pdc_gic_set_type, .flags = IRQCHIP_MASK_ON_SUSPEND | IRQCHIP_SET_TYPE_MASKED | - IRQCHIP_SKIP_SET_WAKE, + IRQCHIP_SKIP_SET_WAKE | + IRQCHIP_ENABLE_WAKEUP_ON_SUSPEND, .irq_set_vcpu_affinity = irq_chip_set_vcpu_affinity_parent, .irq_set_affinity = irq_chip_set_affinity_parent, }; From d7bc63fa20b8a3b0d0645bed1887848c65c01529 Mon Sep 17 00:00:00 2001 From: Maulik Shah Date: Mon, 28 Sep 2020 10:02:04 +0530 Subject: [PATCH 6/6] irqchip/qcom-pdc: Reset PDC interrupts during init Kexec can directly boot into a new kernel without going to complete reboot. This can leave the previous kernel's configuration for PDC interrupts as is. Clear previous kernel's configuration during init by setting interrupts in enable bank to zero. The IRQs specified in qcom,pdc-ranges property are the only ones that can be used by the new kernel so clear only those IRQs. The remaining ones may be in use by a different kernel and should not be set by new kernel. Suggested-by: Stephen Boyd Signed-off-by: Maulik Shah Signed-off-by: Marc Zyngier Tested-by: Stephen Boyd Reviewed-by: Stephen Boyd Reviewed-by: Douglas Anderson Acked-by: Linus Walleij Link: https://lore.kernel.org/r/1601267524-20199-7-git-send-email-mkshah@codeaurora.org --- drivers/irqchip/qcom-pdc.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/irqchip/qcom-pdc.c b/drivers/irqchip/qcom-pdc.c index acc0620f315a..bd39e9de6ecf 100644 --- a/drivers/irqchip/qcom-pdc.c +++ b/drivers/irqchip/qcom-pdc.c @@ -341,7 +341,8 @@ static const struct irq_domain_ops qcom_pdc_gpio_ops = { static int pdc_setup_pin_mapping(struct device_node *np) { - int ret, n; + int ret, n, i; + u32 irq_index, reg_index, val; n = of_property_count_elems_of_size(np, "qcom,pdc-ranges", sizeof(u32)); if (n <= 0 || n % 3) @@ -370,6 +371,14 @@ static int pdc_setup_pin_mapping(struct device_node *np) &pdc_region[n].cnt); if (ret) return ret; + + for (i = 0; i < pdc_region[n].cnt; i++) { + reg_index = (i + pdc_region[n].pin_base) >> 5; + irq_index = (i + pdc_region[n].pin_base) & 0x1f; + val = pdc_reg_read(IRQ_ENABLE_BANK, reg_index); + val &= ~BIT(irq_index); + pdc_reg_write(IRQ_ENABLE_BANK, reg_index, val); + } } return 0;