Skip to content

Commit e6d9607

Browse files
Prathamesh Shetethierryreding
authored andcommitted
soc/tegra: pmc: Fix unsafe generic_handle_irq() call
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> Reviewed-by: Jon Hunter <jonathanh@nvidia.com> Tested-by: Jon Hunter <jonathanh@nvidia.com> [treding@nvidia.com: miscellaneous cleanups] Signed-off-by: Thierry Reding <treding@nvidia.com>
1 parent 8f0b4cc commit e6d9607

1 file changed

Lines changed: 74 additions & 30 deletions

File tree

drivers/soc/tegra/pmc.c

Lines changed: 74 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
#include <linux/iopoll.h>
2929
#include <linux/irqdomain.h>
3030
#include <linux/irq.h>
31+
#include <linux/irq_work.h>
3132
#include <linux/kernel.h>
3233
#include <linux/of_address.h>
3334
#include <linux/of_clk.h>
@@ -468,6 +469,10 @@ struct tegra_pmc {
468469
unsigned long *wake_sw_status_map;
469470
unsigned long *wake_cntrl_level_map;
470471
struct syscore syscore;
472+
473+
/* Pending wake IRQ processing */
474+
struct irq_work wake_work;
475+
u32 *wake_status;
471476
};
472477

473478
static struct tegra_pmc *pmc = &(struct tegra_pmc) {
@@ -1905,6 +1910,50 @@ static int tegra_pmc_parse_dt(struct tegra_pmc *pmc, struct device_node *np)
19051910
return 0;
19061911
}
19071912

1913+
/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
1914+
static void tegra186_pmc_wake_handler(struct irq_work *work)
1915+
{
1916+
struct tegra_pmc *pmc = container_of(work, struct tegra_pmc, wake_work);
1917+
unsigned int i, wake;
1918+
1919+
for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
1920+
unsigned long status = pmc->wake_status[i];
1921+
1922+
for_each_set_bit(wake, &status, 32) {
1923+
irq_hw_number_t hwirq = wake + (i * 32);
1924+
struct irq_desc *desc;
1925+
unsigned int irq;
1926+
1927+
irq = irq_find_mapping(pmc->domain, hwirq);
1928+
if (!irq) {
1929+
dev_warn(pmc->dev,
1930+
"No IRQ found for WAKE#%lu!\n",
1931+
hwirq);
1932+
continue;
1933+
}
1934+
1935+
dev_dbg(pmc->dev,
1936+
"Resume caused by WAKE#%lu mapped to IRQ#%u\n",
1937+
hwirq, irq);
1938+
1939+
desc = irq_to_desc(irq);
1940+
if (!desc) {
1941+
dev_warn(pmc->dev,
1942+
"No descriptor found for IRQ#%u\n",
1943+
irq);
1944+
continue;
1945+
}
1946+
1947+
if (!desc->action || !desc->action->name)
1948+
continue;
1949+
1950+
generic_handle_irq(irq);
1951+
}
1952+
1953+
pmc->wake_status[i] = 0;
1954+
}
1955+
}
1956+
19081957
static int tegra_pmc_init(struct tegra_pmc *pmc)
19091958
{
19101959
if (pmc->soc->max_wake_events > 0) {
@@ -1923,6 +1972,18 @@ static int tegra_pmc_init(struct tegra_pmc *pmc)
19231972
pmc->wake_cntrl_level_map = bitmap_zalloc(pmc->soc->max_wake_events, GFP_KERNEL);
19241973
if (!pmc->wake_cntrl_level_map)
19251974
return -ENOMEM;
1975+
1976+
pmc->wake_status = kcalloc(pmc->soc->max_wake_vectors, sizeof(u32), GFP_KERNEL);
1977+
if (!pmc->wake_status)
1978+
return -ENOMEM;
1979+
1980+
/*
1981+
* Initialize IRQ work for processing wake IRQs. Must use
1982+
* HARD_IRQ variant to run in hard IRQ context on PREEMPT_RT
1983+
* because we call generic_handle_irq() which requires hard
1984+
* IRQ context.
1985+
*/
1986+
pmc->wake_work = IRQ_WORK_INIT_HARD(tegra186_pmc_wake_handler);
19261987
}
19271988

19281989
if (pmc->soc->init)
@@ -3129,47 +3190,30 @@ static void wke_clear_wake_status(struct tegra_pmc *pmc)
31293190
}
31303191
}
31313192

3132-
/* translate sc7 wake sources back into IRQs to catch edge triggered wakeups */
3133-
static void tegra186_pmc_process_wake_events(struct tegra_pmc *pmc, unsigned int index,
3134-
unsigned long status)
3135-
{
3136-
unsigned int wake;
3137-
3138-
dev_dbg(pmc->dev, "Wake[%d:%d] status=%#lx\n", (index * 32) + 31, index * 32, status);
3139-
3140-
for_each_set_bit(wake, &status, 32) {
3141-
irq_hw_number_t hwirq = wake + 32 * index;
3142-
struct irq_desc *desc;
3143-
unsigned int irq;
3144-
3145-
irq = irq_find_mapping(pmc->domain, hwirq);
3146-
3147-
desc = irq_to_desc(irq);
3148-
if (!desc || !desc->action || !desc->action->name) {
3149-
dev_dbg(pmc->dev, "Resume caused by WAKE%ld, IRQ %d\n", hwirq, irq);
3150-
continue;
3151-
}
3152-
3153-
dev_dbg(pmc->dev, "Resume caused by WAKE%ld, %s\n", hwirq, desc->action->name);
3154-
generic_handle_irq(irq);
3155-
}
3156-
}
3157-
31583193
static void tegra186_pmc_wake_syscore_resume(void *data)
31593194
{
3160-
u32 status, mask;
31613195
unsigned int i;
3196+
u32 mask;
31623197

31633198
for (i = 0; i < pmc->soc->max_wake_vectors; i++) {
31643199
mask = readl(pmc->wake + WAKE_AOWAKE_TIER2_ROUTING(i));
3165-
status = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
3166-
3167-
tegra186_pmc_process_wake_events(pmc, i, status);
3200+
pmc->wake_status[i] = readl(pmc->wake + WAKE_AOWAKE_STATUS_R(i)) & mask;
31683201
}
3202+
3203+
/* Schedule IRQ work to process wake IRQs (if any) */
3204+
irq_work_queue(&pmc->wake_work);
31693205
}
31703206

31713207
static int tegra186_pmc_wake_syscore_suspend(void *data)
31723208
{
3209+
unsigned int i;
3210+
3211+
/* Check if there are unhandled wake IRQs */
3212+
for (i = 0; i < pmc->soc->max_wake_vectors; i++)
3213+
if (pmc->wake_status[i])
3214+
dev_warn(pmc->dev,
3215+
"Unhandled wake IRQs pending vector[%u]: 0x%x\n",
3216+
i, pmc->wake_status[i]);
31733217
wke_read_sw_wake_status(pmc);
31743218

31753219
/* flip the wakeup trigger for dual-edge triggered pads

0 commit comments

Comments
 (0)