public inbox for linux-tegra@vger.kernel.org
 help / color / mirror / Atom feed
From: Jon Hunter <jonathanh@nvidia.com>
To: Prathamesh Shete <pshete@nvidia.com>,
	thierry.reding@gmail.com, ulf.hansson@linaro.org,
	chleroy@kernel.org, jirislaby@kernel.org, haotienh@nvidia.com,
	linux-tegra@vger.kernel.org, linux-kernel@vger.kernel.org
Cc: petlozup@nvidia.com
Subject: Re: [PATCH] soc/tegra: pmc: Fix unsafe generic_handle_irq() call
Date: Fri, 9 Jan 2026 18:36:37 +0000	[thread overview]
Message-ID: <e6e83a4e-5eca-4ddd-b0cb-e78921d1e5a7@nvidia.com> (raw)
In-Reply-To: <20260108050103.126008-1-pshete@nvidia.com>



On 08/01/2026 05:01, Prathamesh Shete wrote:
> Currently, when resuming from system suspend on Tegra platforms,
> the following warning is observed:
> 
> WARNING: CPU: 0 PID: 14459 at kernel/irq/irqdesc.c:666
> Call trace:
>   handle_irq_desc+0x20/0x58 (P)
>   tegra186_pmc_wake_syscore_resume+0xe4/0x15c
>   syscore_resume+0x3c/0xb8
>   suspend_devices_and_enter+0x510/0x540
>   pm_suspend+0x16c/0x1d8
> 
> The warning occurs because generic_handle_irq() is being called from
> a non-interrupt context which is considered as unsafe.
> 
> Fix this warning by deferring generic_handle_irq() call to an IRQ work
> which gets executed in hard IRQ context where generic_handle_irq()
> can be called safely.
> 
> When PREEMPT_RT kernels are used, regular IRQ work (initialized with
> init_irq_work) is deferred to run in per-CPU kthreads in preemptible
> context rather than hard IRQ context. Hence, use the IRQ_WORK_INIT_HARD
> variant so that with PREEMPT_RT kernels, the IRQ work is processed in
> hardirq context instead of being deferred to a thread which is required
> for calling generic_handle_irq().
> 
> On non-PREEMPT_RT kernels, both init_irq_work() and IRQ_WORK_INIT_HARD()
> execute in IRQ context, so this change has no functional impact for
> standard kernel configurations.
> 
> Signed-off-by: Petlozu Pravareshwar <petlozup@nvidia.com>
> Signed-off-by: Prathamesh Shete <pshete@nvidia.com>
> ---
>   drivers/soc/tegra/pmc.c | 106 ++++++++++++++++++++++++++++------------
>   1 file changed, 76 insertions(+), 30 deletions(-)
> 
> diff --git a/drivers/soc/tegra/pmc.c b/drivers/soc/tegra/pmc.c
> index f3760a3b3026..a0cc276e235a 100644
> --- a/drivers/soc/tegra/pmc.c
> +++ b/drivers/soc/tegra/pmc.c
> @@ -28,6 +28,7 @@
>   #include <linux/iopoll.h>
>   #include <linux/irqdomain.h>
>   #include <linux/irq.h>
> +#include <linux/irq_work.h>
>   #include <linux/kernel.h>
>   #include <linux/of_address.h>
>   #include <linux/of_clk.h>
> @@ -336,6 +337,8 @@ struct tegra_wake_event {
>   		},					\
>   	}
>   
> +#define TEGRA_PMC_MAX_WAKE_VECTORS	4
> +
>   struct tegra_pmc_soc {
>   	unsigned int num_powergates;
>   	const char *const *powergates;
> @@ -468,6 +471,10 @@ struct tegra_pmc {
>   	unsigned long *wake_sw_status_map;
>   	unsigned long *wake_cntrl_level_map;
>   	struct syscore syscore;
> +
> +	/* Pending wake IRQ processing */
> +	u32 pending_wake_status[TEGRA_PMC_MAX_WAKE_VECTORS];
> +	struct irq_work pending_wake_irq_work;
>   };
>   
>   static struct tegra_pmc *pmc = &(struct tegra_pmc) {
> @@ -1905,8 +1912,59 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
>   	return 0;
>   }
>   
> +/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
> +static void tegra186_pmc_wake_irq_work_handler(struct irq_work *work)
> +{
> +	struct tegra_pmc *pmc = container_of(work, struct tegra_pmc,
> +					      pending_wake_irq_work);
> +	unsigned int i, wake;
> +	unsigned long pending_wake_status;
> +
> +	for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
> +		pending_wake_status = pmc->pending_wake_status[i];
> +
> +		for_each_set_bit(wake, &pending_wake_status, 32) {
> +			irq_hw_number_t hwirq = wake + (i * 32);
> +			struct irq_desc *desc;
> +			unsigned int irq;
> +
> +			irq = irq_find_mapping(pmc->domain, hwirq);
> +			if (!irq) {
> +				dev_warn(pmc->dev, "No IRQ found for WAKE%lu!\n",
> +					 hwirq);
> +				continue;
> +			}
> +
> +			dev_dbg(pmc->dev,
> +				"Resume caused by WAKE%lu mapped to IRQ %d\n",
> +				hwirq, irq);
> +
> +			desc = irq_to_desc(irq);
> +			if (!desc) {
> +				dev_warn(pmc->dev,
> +					 "No descriptor found for IRQ %d\n",
> +					irq);
> +				continue;
> +			}
> +
> +			if (!desc->action || !desc->action->name)
> +				continue;
> +
> +			generic_handle_irq(irq);
> +		}
> +
> +		pmc->pending_wake_status[i] = 0;
> +	}
> +}
> +
>   static int tegra_pmc_init(struct tegra_pmc *pmc)
>   {
> +	if (pmc->soc->max_wake_vectors > TEGRA_PMC_MAX_WAKE_VECTORS) {
> +		dev_err(pmc->dev, "max_wake_vectors (%u) exceeds maximum (%u)\n",
> +			pmc->soc->max_wake_vectors, TEGRA_PMC_MAX_WAKE_VECTORS);
> +		return -EINVAL;
> +	}
> +
>   	if (pmc->soc->max_wake_events > 0) {
>   		pmc->wake_type_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
>   		if (!pmc->wake_type_level_map)
> @@ -1923,6 +1981,12 @@ static int tegra_pmc_init(struct tegra_pmc *pmc)
>   		pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
>   		if (!pmc->wake_cntrl_level_map)
>   			return -ENOMEM;
> +
> +		/* Initialize IRQ work for processing wake IRQs
> +		 * Must use HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT
> +		 * because we call generic_handle_irq() which requires hard IRQ context.
> +		 */
> +		pmc->pending_wake_irq_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_irq_work_handler);
>   	}
>   
>   	if (pmc->soc->init)
> @@ -3129,47 +3193,29 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc)
>   	}
>   }
>   
> -/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
> -static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index,
> -					     unsigned long status)
> -{
> -	unsigned int wake;
> -
> -	dev_dbg(pmc->dev, "Wake[%d:%d]  status=%#lx\n", (index * 32) + 31, index * 32, status);
> -
> -	for_each_set_bit(wake, &status, 32) {
> -		irq_hw_number_t hwirq = wake + 32 * index;
> -		struct irq_desc *desc;
> -		unsigned int irq;
> -
> -		irq = irq_find_mapping(pmc->domain, hwirq);
> -
> -		desc = irq_to_desc(irq);
> -		if (!desc || !desc->action || !desc->action->name) {
> -			dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq);
> -			continue;
> -		}
> -
> -		dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name);
> -		generic_handle_irq(irq);
> -	}
> -}
> -
>   static void tegra186_pmc_wake_syscore_resume(void *data)
>   {
> -	u32 status, mask;
> +	u32 mask;
>   	unsigned int i;
>   
>   	for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
>   		mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i));
> -		status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
> -
> -		tegra186_pmc_process_wake_events(pmc, i, status);
> +		pmc->pending_wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
>   	}

Missing a newline here.

> +	/* Schedule IRQ work to process wake IRQs (if any) */
> +	irq_work_queue(&pmc->pending_wake_irq_work);
>   }
>   
>   static int tegra186_pmc_wake_syscore_suspend(void *data)
>   {
> +	unsigned int i;
> +
> +	/* Check if there are unhandled wake IRQs */
> +	for (i = 0; i < pmc->soc->max_wake_vectors; i++)
> +		if (pmc->pending_wake_status[i])
> +			dev_warn(pmc->dev,
> +				 "Unhandled wake IRQs pending vector[%u]: 0x%x\n",
> +				 i, pmc->pending_wake_status[i]);

Missing a newline here.

>   	wke_read_sw_wake_status(pmc);
>   
>   	/* flip the wakeup trigger for dual-edge triggered pads


Thierry may be able to fix up the missing newlines when applying. With 
that ...

Reviewed-by: Jon Hunter <jonathanh@nvidia.com>
Tested-by: Jon Hunter <jonathanh@nvidia.com>

Thanks!
Jon

-- 
nvpublic


      reply	other threads:[~2026-01-09 18:36 UTC|newest]

Thread overview: 2+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-01-08  5:01 [PATCH] soc/tegra: pmc: Fix unsafe generic_handle_irq() call Prathamesh Shete
2026-01-09 18:36 ` Jon Hunter [this message]

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=e6e83a4e-5eca-4ddd-b0cb-e78921d1e5a7@nvidia.com \
    --to=jonathanh@nvidia.com \
    --cc=chleroy@kernel.org \
    --cc=haotienh@nvidia.com \
    --cc=jirislaby@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-tegra@vger.kernel.org \
    --cc=petlozup@nvidia.com \
    --cc=pshete@nvidia.com \
    --cc=thierry.reding@gmail.com \
    --cc=ulf.hansson@linaro.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox