Subject: [PATCH v3 0/3] drm/panfrost: Fix poweroff and sync IRQs for suspend

Changes in v3:
- Removed useless GPU_INT_CLEAR write in suspend path
- Changed to clear suspend bits in job/mmu reset path

This series contains a fast fix for the basic GPU poweroff functionality
and goes further by implementing interrupt masking and synchronization
before suspend.

For more information, please look at the conversation at [1], which
explains the regression seen with the poweroff commit and the initial
approaches taken to solve that.

Cheers!

[1]: https://lore.kernel.org/all/[email protected]/

AngeloGioacchino Del Regno (3):
drm/panfrost: Ignore core_mask for poweroff and disable PWRTRANS irq
drm/panfrost: Add gpu_irq, mmu_irq to struct panfrost_device
drm/panfrost: Synchronize and disable interrupts before powering off

drivers/gpu/drm/panfrost/panfrost_device.c | 3 +++
drivers/gpu/drm/panfrost/panfrost_device.h | 9 +++++++
drivers/gpu/drm/panfrost/panfrost_gpu.c | 28 ++++++++++++++-------
drivers/gpu/drm/panfrost/panfrost_gpu.h | 1 +
drivers/gpu/drm/panfrost/panfrost_job.c | 20 ++++++++++++---
drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
drivers/gpu/drm/panfrost/panfrost_mmu.c | 29 ++++++++++++++++------
drivers/gpu/drm/panfrost/panfrost_mmu.h | 1 +
8 files changed, 72 insertions(+), 20 deletions(-)

--
2.43.0


Subject: [PATCH v3 2/3] drm/panfrost: Add gpu_irq, mmu_irq to struct panfrost_device

In preparation for adding a IRQ synchronization mechanism for PM suspend
add gpu_irq and mmu_irq variables to struct panfrost_device and change
functions panfrost_gpu_init() and panfrost_mmu_init() to use those.

Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
---
drivers/gpu/drm/panfrost/panfrost_device.h | 2 ++
drivers/gpu/drm/panfrost/panfrost_gpu.c | 10 +++++-----
drivers/gpu/drm/panfrost/panfrost_mmu.c | 10 +++++-----
3 files changed, 12 insertions(+), 10 deletions(-)

diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 0fc558db6bfd..54a8aad54259 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -94,6 +94,8 @@ struct panfrost_device {
struct device *dev;
struct drm_device *ddev;
struct platform_device *pdev;
+ int gpu_irq;
+ int mmu_irq;

void __iomem *iomem;
struct clk *clock;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index bd41617c5e4b..7adc4441fa14 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -454,7 +454,7 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)

int panfrost_gpu_init(struct panfrost_device *pfdev)
{
- int err, irq;
+ int err;

err = panfrost_gpu_soft_reset(pfdev);
if (err)
@@ -469,11 +469,11 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)

dma_set_max_seg_size(pfdev->dev, UINT_MAX);

- irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
- if (irq < 0)
- return irq;
+ pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
+ if (pfdev->gpu_irq < 0)
+ return pfdev->gpu_irq;

- err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler,
+ err = devm_request_irq(pfdev->dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev);
if (err) {
dev_err(pfdev->dev, "failed to request gpu irq");
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index 846dd697c410..ac4296c1e54b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -753,13 +753,13 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)

int panfrost_mmu_init(struct panfrost_device *pfdev)
{
- int err, irq;
+ int err;

- irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
- if (irq < 0)
- return irq;
+ pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
+ if (pfdev->mmu_irq < 0)
+ return pfdev->mmu_irq;

- err = devm_request_threaded_irq(pfdev->dev, irq,
+ err = devm_request_threaded_irq(pfdev->dev, pfdev->mmu_irq,
panfrost_mmu_irq_handler,
panfrost_mmu_irq_handler_thread,
IRQF_SHARED, KBUILD_MODNAME "-mmu",
--
2.43.0

Subject: [PATCH v3 3/3] drm/panfrost: Synchronize and disable interrupts before powering off

To make sure that we don't unintentionally perform any unclocked and/or
unpowered R/W operation on GPU registers, before turning off clocks and
regulators we must make sure that no GPU, JOB or MMU ISR execution is
pending: doing that required to add a mechanism to synchronize the
interrupts on suspend.

Add functions panfrost_{gpu,job,mmu}_suspend_irq() which will perform
interrupts masking and ISR execution synchronization, and then call
those in the panfrost_device_runtime_suspend() handler in the exact
sequence of job (may require mmu!) -> mmu -> gpu.

As a side note, JOB and MMU suspend_irq functions needed some special
treatment: as their interrupt handlers will unmask interrupts, it was
necessary to add a bitmap for `is_suspended` which is used to address
the possible corner case of unintentional IRQ unmasking because of ISR
execution after a call to synchronize_irq().

At resume, clear each is_suspended bit in the reset path of JOB/MMU
to allow unmasking the interrupts.

Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
---
drivers/gpu/drm/panfrost/panfrost_device.c | 3 +++
drivers/gpu/drm/panfrost/panfrost_device.h | 7 +++++++
drivers/gpu/drm/panfrost/panfrost_gpu.c | 6 ++++++
drivers/gpu/drm/panfrost/panfrost_gpu.h | 1 +
drivers/gpu/drm/panfrost/panfrost_job.c | 20 +++++++++++++++++---
drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
drivers/gpu/drm/panfrost/panfrost_mmu.c | 19 ++++++++++++++++---
drivers/gpu/drm/panfrost/panfrost_mmu.h | 1 +
8 files changed, 52 insertions(+), 6 deletions(-)

diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
index c90ad5ee34e7..a45e4addcc19 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.c
+++ b/drivers/gpu/drm/panfrost/panfrost_device.c
@@ -421,6 +421,9 @@ static int panfrost_device_runtime_suspend(struct device *dev)
return -EBUSY;

panfrost_devfreq_suspend(pfdev);
+ panfrost_job_suspend_irq(pfdev);
+ panfrost_mmu_suspend_irq(pfdev);
+ panfrost_gpu_suspend_irq(pfdev);
panfrost_gpu_power_off(pfdev);

return 0;
diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
index 54a8aad54259..5c24f01f8904 100644
--- a/drivers/gpu/drm/panfrost/panfrost_device.h
+++ b/drivers/gpu/drm/panfrost/panfrost_device.h
@@ -25,6 +25,12 @@ struct panfrost_perfcnt;
#define NUM_JOB_SLOTS 3
#define MAX_PM_DOMAINS 5

+enum panfrost_drv_comp_bits {
+ PANFROST_COMP_BIT_MMU,
+ PANFROST_COMP_BIT_JOB,
+ PANFROST_COMP_BIT_MAX
+};
+
/**
* enum panfrost_gpu_pm - Supported kernel power management features
* @GPU_PM_CLK_DIS: Allow disabling clocks during system suspend
@@ -109,6 +115,7 @@ struct panfrost_device {

struct panfrost_features features;
const struct panfrost_compatible *comp;
+ DECLARE_BITMAP(is_suspended, PANFROST_COMP_BIT_MAX);

spinlock_t as_lock;
unsigned long as_in_use_mask;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
index 7adc4441fa14..3a6a4fe7aca1 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
@@ -452,6 +452,12 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
dev_err(pfdev->dev, "l2 power transition timeout");
}

+void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
+{
+ gpu_write(pfdev, GPU_INT_MASK, 0);
+ synchronize_irq(pfdev->gpu_irq);
+}
+
int panfrost_gpu_init(struct panfrost_device *pfdev)
{
int err;
diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
index 876fdad9f721..d841b86504ea 100644
--- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
@@ -15,6 +15,7 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev);
int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
void panfrost_gpu_power_on(struct panfrost_device *pfdev);
void panfrost_gpu_power_off(struct panfrost_device *pfdev);
+void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);

void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
void panfrost_cycle_counter_put(struct panfrost_device *pfdev);
diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
index f9446e197428..7600e7741211 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.c
+++ b/drivers/gpu/drm/panfrost/panfrost_job.c
@@ -405,6 +405,8 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
int j;
u32 irq_mask = 0;

+ clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
+
for (j = 0; j < NUM_JOB_SLOTS; j++) {
irq_mask |= MK_JS_MASK(j);
}
@@ -413,6 +415,14 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
job_write(pfdev, JOB_INT_MASK, irq_mask);
}

+void panfrost_job_suspend_irq(struct panfrost_device *pfdev)
+{
+ set_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
+
+ job_write(pfdev, JOB_INT_MASK, 0);
+ synchronize_irq(pfdev->js->irq);
+}
+
static void panfrost_job_handle_err(struct panfrost_device *pfdev,
struct panfrost_job *job,
unsigned int js)
@@ -792,9 +802,13 @@ static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
struct panfrost_device *pfdev = data;

panfrost_job_handle_irqs(pfdev);
- job_write(pfdev, JOB_INT_MASK,
- GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
- GENMASK(NUM_JOB_SLOTS - 1, 0));
+
+ /* Enable interrupts only if we're not about to get suspended */
+ if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
+ job_write(pfdev, JOB_INT_MASK,
+ GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
+ GENMASK(NUM_JOB_SLOTS - 1, 0));
+
return IRQ_HANDLED;
}

diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
index 17ff808dba07..ec581b97852b 100644
--- a/drivers/gpu/drm/panfrost/panfrost_job.h
+++ b/drivers/gpu/drm/panfrost/panfrost_job.h
@@ -47,6 +47,7 @@ int panfrost_job_get_slot(struct panfrost_job *job);
int panfrost_job_push(struct panfrost_job *job);
void panfrost_job_put(struct panfrost_job *job);
void panfrost_job_enable_interrupts(struct panfrost_device *pfdev);
+void panfrost_job_suspend_irq(struct panfrost_device *pfdev);
int panfrost_job_is_idle(struct panfrost_device *pfdev);

#endif
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
index ac4296c1e54b..d79d41fe22fe 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
@@ -231,6 +231,8 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
{
struct panfrost_mmu *mmu, *mmu_tmp;

+ clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+
spin_lock(&pfdev->as_lock);

pfdev->as_alloc_mask = 0;
@@ -744,9 +746,12 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
status = mmu_read(pfdev, MMU_INT_RAWSTAT) & ~pfdev->as_faulty_mask;
}

- spin_lock(&pfdev->as_lock);
- mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
- spin_unlock(&pfdev->as_lock);
+ /* Enable interrupts only if we're not about to get suspended */
+ if (!test_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended)) {
+ spin_lock(&pfdev->as_lock);
+ mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
+ spin_unlock(&pfdev->as_lock);
+ }

return IRQ_HANDLED;
};
@@ -777,3 +782,11 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev)
{
mmu_write(pfdev, MMU_INT_MASK, 0);
}
+
+void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev)
+{
+ set_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
+
+ mmu_write(pfdev, MMU_INT_MASK, 0);
+ synchronize_irq(pfdev->mmu_irq);
+}
diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
index cc2a0d307feb..022a9a74a114 100644
--- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
+++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
@@ -14,6 +14,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping);
int panfrost_mmu_init(struct panfrost_device *pfdev);
void panfrost_mmu_fini(struct panfrost_device *pfdev);
void panfrost_mmu_reset(struct panfrost_device *pfdev);
+void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);

u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
--
2.43.0

2023-12-01 11:02:45

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] drm/panfrost: Add gpu_irq, mmu_irq to struct panfrost_device

On Fri, 1 Dec 2023 11:40:26 +0100
AngeloGioacchino Del Regno <[email protected]>
wrote:

> In preparation for adding a IRQ synchronization mechanism for PM suspend

Maybe add a ',' after 'suspend'

> add gpu_irq and mmu_irq variables to struct panfrost_device and change
> functions panfrost_gpu_init() and panfrost_mmu_init() to use those.
>
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>

Reviewed-by: Boris Brezillon <[email protected]>

> ---
> drivers/gpu/drm/panfrost/panfrost_device.h | 2 ++
> drivers/gpu/drm/panfrost/panfrost_gpu.c | 10 +++++-----
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 10 +++++-----
> 3 files changed, 12 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
> index 0fc558db6bfd..54a8aad54259 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.h
> @@ -94,6 +94,8 @@ struct panfrost_device {
> struct device *dev;
> struct drm_device *ddev;
> struct platform_device *pdev;
> + int gpu_irq;
> + int mmu_irq;
>
> void __iomem *iomem;
> struct clk *clock;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> index bd41617c5e4b..7adc4441fa14 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> @@ -454,7 +454,7 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
>
> int panfrost_gpu_init(struct panfrost_device *pfdev)
> {
> - int err, irq;
> + int err;
>
> err = panfrost_gpu_soft_reset(pfdev);
> if (err)
> @@ -469,11 +469,11 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
>
> dma_set_max_seg_size(pfdev->dev, UINT_MAX);
>
> - irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
> - if (irq < 0)
> - return irq;
> + pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
> + if (pfdev->gpu_irq < 0)
> + return pfdev->gpu_irq;
>
> - err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler,
> + err = devm_request_irq(pfdev->dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
> IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev);
> if (err) {
> dev_err(pfdev->dev, "failed to request gpu irq");
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> index 846dd697c410..ac4296c1e54b 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> @@ -753,13 +753,13 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
>
> int panfrost_mmu_init(struct panfrost_device *pfdev)
> {
> - int err, irq;
> + int err;
>
> - irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
> - if (irq < 0)
> - return irq;
> + pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
> + if (pfdev->mmu_irq < 0)
> + return pfdev->mmu_irq;
>
> - err = devm_request_threaded_irq(pfdev->dev, irq,
> + err = devm_request_threaded_irq(pfdev->dev, pfdev->mmu_irq,
> panfrost_mmu_irq_handler,
> panfrost_mmu_irq_handler_thread,
> IRQF_SHARED, KBUILD_MODNAME "-mmu",

2023-12-01 11:14:59

by Boris Brezillon

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] drm/panfrost: Synchronize and disable interrupts before powering off

On Fri, 1 Dec 2023 11:40:27 +0100
AngeloGioacchino Del Regno <[email protected]>
wrote:

> To make sure that we don't unintentionally perform any unclocked and/or
> unpowered R/W operation on GPU registers, before turning off clocks and
> regulators we must make sure that no GPU, JOB or MMU ISR execution is
> pending: doing that required to add a mechanism to synchronize the

^ requires the addition of a mechanism...

> interrupts on suspend.
>
> Add functions panfrost_{gpu,job,mmu}_suspend_irq() which will perform
> interrupts masking and ISR execution synchronization, and then call
> those in the panfrost_device_runtime_suspend() handler in the exact
> sequence of job (may require mmu!) -> mmu -> gpu.
>
> As a side note, JOB and MMU suspend_irq functions needed some special
> treatment: as their interrupt handlers will unmask interrupts, it was
> necessary to add a bitmap for `is_suspended` which is used to address

to add an `is_suspended` bitmap which is used...

> the possible corner case of unintentional IRQ unmasking because of ISR
> execution after a call to synchronize_irq().

Also fixes the case where the interrupt handler is called when the
device is suspended because the IRQ line is shared with another device.
No need to update the commit message for that though.

>
> At resume, clear each is_suspended bit in the reset path of JOB/MMU
> to allow unmasking the interrupts.
>
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
> ---
> drivers/gpu/drm/panfrost/panfrost_device.c | 3 +++
> drivers/gpu/drm/panfrost/panfrost_device.h | 7 +++++++
> drivers/gpu/drm/panfrost/panfrost_gpu.c | 6 ++++++
> drivers/gpu/drm/panfrost/panfrost_gpu.h | 1 +
> drivers/gpu/drm/panfrost/panfrost_job.c | 20 +++++++++++++++++---
> drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 19 ++++++++++++++++---
> drivers/gpu/drm/panfrost/panfrost_mmu.h | 1 +
> 8 files changed, 52 insertions(+), 6 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
> index c90ad5ee34e7..a45e4addcc19 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
> @@ -421,6 +421,9 @@ static int panfrost_device_runtime_suspend(struct device *dev)
> return -EBUSY;
>
> panfrost_devfreq_suspend(pfdev);
> + panfrost_job_suspend_irq(pfdev);
> + panfrost_mmu_suspend_irq(pfdev);
> + panfrost_gpu_suspend_irq(pfdev);
> panfrost_gpu_power_off(pfdev);
>
> return 0;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
> index 54a8aad54259..5c24f01f8904 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.h
> @@ -25,6 +25,12 @@ struct panfrost_perfcnt;
> #define NUM_JOB_SLOTS 3
> #define MAX_PM_DOMAINS 5
>
> +enum panfrost_drv_comp_bits {
> + PANFROST_COMP_BIT_MMU,
> + PANFROST_COMP_BIT_JOB,

I think we need one for the GPU interrupt too, for the
irq-line-is-shared-with-another-device thing I was mentioning.

> + PANFROST_COMP_BIT_MAX
> +};
> +
> /**
> * enum panfrost_gpu_pm - Supported kernel power management features
> * @GPU_PM_CLK_DIS: Allow disabling clocks during system suspend
> @@ -109,6 +115,7 @@ struct panfrost_device {
>
> struct panfrost_features features;
> const struct panfrost_compatible *comp;
> + DECLARE_BITMAP(is_suspended, PANFROST_COMP_BIT_MAX);
>
> spinlock_t as_lock;
> unsigned long as_in_use_mask;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> index 7adc4441fa14..3a6a4fe7aca1 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> @@ -452,6 +452,12 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
> dev_err(pfdev->dev, "l2 power transition timeout");
> }
>
> +void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
> +{

set_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);

here, and an extra check in panfrost_gpu_irq_handler() to bail out
before the register accesses if PANFROST_COMP_BIT_GPU is set.

> + gpu_write(pfdev, GPU_INT_MASK, 0);
> + synchronize_irq(pfdev->gpu_irq);
> +}
> +
> int panfrost_gpu_init(struct panfrost_device *pfdev)
> {
> int err;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
> index 876fdad9f721..d841b86504ea 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
> @@ -15,6 +15,7 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev);
> int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
> void panfrost_gpu_power_on(struct panfrost_device *pfdev);
> void panfrost_gpu_power_off(struct panfrost_device *pfdev);
> +void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);
>
> void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
> void panfrost_cycle_counter_put(struct panfrost_device *pfdev);
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
> index f9446e197428..7600e7741211 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
> @@ -405,6 +405,8 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
> int j;
> u32 irq_mask = 0;
>
> + clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
> +
> for (j = 0; j < NUM_JOB_SLOTS; j++) {
> irq_mask |= MK_JS_MASK(j);
> }
> @@ -413,6 +415,14 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
> job_write(pfdev, JOB_INT_MASK, irq_mask);
> }
>
> +void panfrost_job_suspend_irq(struct panfrost_device *pfdev)
> +{
> + set_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
> +
> + job_write(pfdev, JOB_INT_MASK, 0);
> + synchronize_irq(pfdev->js->irq);
> +}
> +
> static void panfrost_job_handle_err(struct panfrost_device *pfdev,
> struct panfrost_job *job,
> unsigned int js)
> @@ -792,9 +802,13 @@ static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
> struct panfrost_device *pfdev = data;
>
> panfrost_job_handle_irqs(pfdev);
> - job_write(pfdev, JOB_INT_MASK,
> - GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
> - GENMASK(NUM_JOB_SLOTS - 1, 0));
> +
> + /* Enable interrupts only if we're not about to get suspended */
> + if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
> + job_write(pfdev, JOB_INT_MASK,
> + GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
> + GENMASK(NUM_JOB_SLOTS - 1, 0));
> +

Missing if (test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended)) in
panfrost_job_irq_handler(), to make sure you don't access the registers
if the GPU is suspended.

> return IRQ_HANDLED;
> }
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.h b/drivers/gpu/drm/panfrost/panfrost_job.h
> index 17ff808dba07..ec581b97852b 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_job.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_job.h
> @@ -47,6 +47,7 @@ int panfrost_job_get_slot(struct panfrost_job *job);
> int panfrost_job_push(struct panfrost_job *job);
> void panfrost_job_put(struct panfrost_job *job);
> void panfrost_job_enable_interrupts(struct panfrost_device *pfdev);
> +void panfrost_job_suspend_irq(struct panfrost_device *pfdev);
> int panfrost_job_is_idle(struct panfrost_device *pfdev);
>
> #endif
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> index ac4296c1e54b..d79d41fe22fe 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> @@ -231,6 +231,8 @@ void panfrost_mmu_reset(struct panfrost_device *pfdev)
> {
> struct panfrost_mmu *mmu, *mmu_tmp;
>
> + clear_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
> +
> spin_lock(&pfdev->as_lock);
>
> pfdev->as_alloc_mask = 0;
> @@ -744,9 +746,12 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
> status = mmu_read(pfdev, MMU_INT_RAWSTAT) & ~pfdev->as_faulty_mask;
> }
>
> - spin_lock(&pfdev->as_lock);
> - mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
> - spin_unlock(&pfdev->as_lock);
> + /* Enable interrupts only if we're not about to get suspended */
> + if (!test_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended)) {
> + spin_lock(&pfdev->as_lock);
> + mmu_write(pfdev, MMU_INT_MASK, ~pfdev->as_faulty_mask);
> + spin_unlock(&pfdev->as_lock);
> + }

Ditto, missing if (test_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended))
in panfrost_job_irq_handler(), to make sure you don't access the
registers if the GPU is suspended.

>
> return IRQ_HANDLED;
> };
> @@ -777,3 +782,11 @@ void panfrost_mmu_fini(struct panfrost_device *pfdev)
> {
> mmu_write(pfdev, MMU_INT_MASK, 0);
> }
> +
> +void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev)
> +{
> + set_bit(PANFROST_COMP_BIT_MMU, pfdev->is_suspended);
> +
> + mmu_write(pfdev, MMU_INT_MASK, 0);
> + synchronize_irq(pfdev->mmu_irq);
> +}
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.h b/drivers/gpu/drm/panfrost/panfrost_mmu.h
> index cc2a0d307feb..022a9a74a114 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.h
> @@ -14,6 +14,7 @@ void panfrost_mmu_unmap(struct panfrost_gem_mapping *mapping);
> int panfrost_mmu_init(struct panfrost_device *pfdev);
> void panfrost_mmu_fini(struct panfrost_device *pfdev);
> void panfrost_mmu_reset(struct panfrost_device *pfdev);
> +void panfrost_mmu_suspend_irq(struct panfrost_device *pfdev);
>
> u32 panfrost_mmu_as_get(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);
> void panfrost_mmu_as_put(struct panfrost_device *pfdev, struct panfrost_mmu *mmu);

2023-12-01 12:34:52

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v3 2/3] drm/panfrost: Add gpu_irq, mmu_irq to struct panfrost_device

On 01/12/2023 10:40, AngeloGioacchino Del Regno wrote:
> In preparation for adding a IRQ synchronization mechanism for PM suspend
> add gpu_irq and mmu_irq variables to struct panfrost_device and change
> functions panfrost_gpu_init() and panfrost_mmu_init() to use those.
>
> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>

Reviewed-by: Steven Price <[email protected]>

Although this now makes the job IRQ look out of place - I'm not sure why
we have the struct panfrost_job_slot as a separately allocated structure
either. Anyway - that's irrelevant to this patch!

Steve

> ---
> drivers/gpu/drm/panfrost/panfrost_device.h | 2 ++
> drivers/gpu/drm/panfrost/panfrost_gpu.c | 10 +++++-----
> drivers/gpu/drm/panfrost/panfrost_mmu.c | 10 +++++-----
> 3 files changed, 12 insertions(+), 10 deletions(-)
>
> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
> index 0fc558db6bfd..54a8aad54259 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_device.h
> +++ b/drivers/gpu/drm/panfrost/panfrost_device.h
> @@ -94,6 +94,8 @@ struct panfrost_device {
> struct device *dev;
> struct drm_device *ddev;
> struct platform_device *pdev;
> + int gpu_irq;
> + int mmu_irq;
>
> void __iomem *iomem;
> struct clk *clock;
> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> index bd41617c5e4b..7adc4441fa14 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
> @@ -454,7 +454,7 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
>
> int panfrost_gpu_init(struct panfrost_device *pfdev)
> {
> - int err, irq;
> + int err;
>
> err = panfrost_gpu_soft_reset(pfdev);
> if (err)
> @@ -469,11 +469,11 @@ int panfrost_gpu_init(struct panfrost_device *pfdev)
>
> dma_set_max_seg_size(pfdev->dev, UINT_MAX);
>
> - irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
> - if (irq < 0)
> - return irq;
> + pfdev->gpu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "gpu");
> + if (pfdev->gpu_irq < 0)
> + return pfdev->gpu_irq;
>
> - err = devm_request_irq(pfdev->dev, irq, panfrost_gpu_irq_handler,
> + err = devm_request_irq(pfdev->dev, pfdev->gpu_irq, panfrost_gpu_irq_handler,
> IRQF_SHARED, KBUILD_MODNAME "-gpu", pfdev);
> if (err) {
> dev_err(pfdev->dev, "failed to request gpu irq");
> diff --git a/drivers/gpu/drm/panfrost/panfrost_mmu.c b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> index 846dd697c410..ac4296c1e54b 100644
> --- a/drivers/gpu/drm/panfrost/panfrost_mmu.c
> +++ b/drivers/gpu/drm/panfrost/panfrost_mmu.c
> @@ -753,13 +753,13 @@ static irqreturn_t panfrost_mmu_irq_handler_thread(int irq, void *data)
>
> int panfrost_mmu_init(struct panfrost_device *pfdev)
> {
> - int err, irq;
> + int err;
>
> - irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
> - if (irq < 0)
> - return irq;
> + pfdev->mmu_irq = platform_get_irq_byname(to_platform_device(pfdev->dev), "mmu");
> + if (pfdev->mmu_irq < 0)
> + return pfdev->mmu_irq;
>
> - err = devm_request_threaded_irq(pfdev->dev, irq,
> + err = devm_request_threaded_irq(pfdev->dev, pfdev->mmu_irq,
> panfrost_mmu_irq_handler,
> panfrost_mmu_irq_handler_thread,
> IRQF_SHARED, KBUILD_MODNAME "-mmu",

2023-12-01 12:34:56

by Steven Price

[permalink] [raw]
Subject: Re: [PATCH v3 3/3] drm/panfrost: Synchronize and disable interrupts before powering off

On 01/12/2023 11:14, Boris Brezillon wrote:
> On Fri, 1 Dec 2023 11:40:27 +0100
> AngeloGioacchino Del Regno <[email protected]>
> wrote:
>
>> To make sure that we don't unintentionally perform any unclocked and/or
>> unpowered R/W operation on GPU registers, before turning off clocks and
>> regulators we must make sure that no GPU, JOB or MMU ISR execution is
>> pending: doing that required to add a mechanism to synchronize the
>
> ^ requires the addition of a mechanism...
>
>> interrupts on suspend.
>>
>> Add functions panfrost_{gpu,job,mmu}_suspend_irq() which will perform
>> interrupts masking and ISR execution synchronization, and then call
>> those in the panfrost_device_runtime_suspend() handler in the exact
>> sequence of job (may require mmu!) -> mmu -> gpu.
>>
>> As a side note, JOB and MMU suspend_irq functions needed some special
>> treatment: as their interrupt handlers will unmask interrupts, it was
>> necessary to add a bitmap for `is_suspended` which is used to address
>
> to add an `is_suspended` bitmap which is used...
>
>> the possible corner case of unintentional IRQ unmasking because of ISR
>> execution after a call to synchronize_irq().
>
> Also fixes the case where the interrupt handler is called when the
> device is suspended because the IRQ line is shared with another device.
> No need to update the commit message for that though.
>
>>
>> At resume, clear each is_suspended bit in the reset path of JOB/MMU
>> to allow unmasking the interrupts.
>>
>> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
>> ---

<snip>

>> static void panfrost_job_handle_err(struct panfrost_device *pfdev,
>> struct panfrost_job *job,
>> unsigned int js)
>> @@ -792,9 +802,13 @@ static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
>> struct panfrost_device *pfdev = data;
>>
>> panfrost_job_handle_irqs(pfdev);
>> - job_write(pfdev, JOB_INT_MASK,
>> - GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>> - GENMASK(NUM_JOB_SLOTS - 1, 0));
>> +
>> + /* Enable interrupts only if we're not about to get suspended */
>> + if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
>> + job_write(pfdev, JOB_INT_MASK,
>> + GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>> + GENMASK(NUM_JOB_SLOTS - 1, 0));
>> +
>
> Missing if (test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended)) in
> panfrost_job_irq_handler(), to make sure you don't access the registers
> if the GPU is suspended.

I think generally these IRQ handler functions should simply check the
is_suspended flag and early out if the flag is set. It's not the
re-enabling of the interrupts specifically that we want to gate - it's
any access to the hardware as in the shared-IRQ case the GPU might
already have been powered down/unclocked.

Steve

Subject: Re: [PATCH v3 3/3] drm/panfrost: Synchronize and disable interrupts before powering off

Il 01/12/23 12:14, Boris Brezillon ha scritto:
> On Fri, 1 Dec 2023 11:40:27 +0100
> AngeloGioacchino Del Regno <[email protected]>
> wrote:
>
>> To make sure that we don't unintentionally perform any unclocked and/or
>> unpowered R/W operation on GPU registers, before turning off clocks and
>> regulators we must make sure that no GPU, JOB or MMU ISR execution is
>> pending: doing that required to add a mechanism to synchronize the
>
> ^ requires the addition of a mechanism...
>
>> interrupts on suspend.
>>
>> Add functions panfrost_{gpu,job,mmu}_suspend_irq() which will perform
>> interrupts masking and ISR execution synchronization, and then call
>> those in the panfrost_device_runtime_suspend() handler in the exact
>> sequence of job (may require mmu!) -> mmu -> gpu.
>>
>> As a side note, JOB and MMU suspend_irq functions needed some special
>> treatment: as their interrupt handlers will unmask interrupts, it was
>> necessary to add a bitmap for `is_suspended` which is used to address
>
> to add an `is_suspended` bitmap which is used...
>
>> the possible corner case of unintentional IRQ unmasking because of ISR
>> execution after a call to synchronize_irq().
>
> Also fixes the case where the interrupt handler is called when the
> device is suspended because the IRQ line is shared with another device.
> No need to update the commit message for that though.
>
>>
>> At resume, clear each is_suspended bit in the reset path of JOB/MMU
>> to allow unmasking the interrupts.
>>
>> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
>> ---
>> drivers/gpu/drm/panfrost/panfrost_device.c | 3 +++
>> drivers/gpu/drm/panfrost/panfrost_device.h | 7 +++++++
>> drivers/gpu/drm/panfrost/panfrost_gpu.c | 6 ++++++
>> drivers/gpu/drm/panfrost/panfrost_gpu.h | 1 +
>> drivers/gpu/drm/panfrost/panfrost_job.c | 20 +++++++++++++++++---
>> drivers/gpu/drm/panfrost/panfrost_job.h | 1 +
>> drivers/gpu/drm/panfrost/panfrost_mmu.c | 19 ++++++++++++++++---
>> drivers/gpu/drm/panfrost/panfrost_mmu.h | 1 +
>> 8 files changed, 52 insertions(+), 6 deletions(-)
>>
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.c b/drivers/gpu/drm/panfrost/panfrost_device.c
>> index c90ad5ee34e7..a45e4addcc19 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_device.c
>> +++ b/drivers/gpu/drm/panfrost/panfrost_device.c
>> @@ -421,6 +421,9 @@ static int panfrost_device_runtime_suspend(struct device *dev)
>> return -EBUSY;
>>
>> panfrost_devfreq_suspend(pfdev);
>> + panfrost_job_suspend_irq(pfdev);
>> + panfrost_mmu_suspend_irq(pfdev);
>> + panfrost_gpu_suspend_irq(pfdev);
>> panfrost_gpu_power_off(pfdev);
>>
>> return 0;
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_device.h b/drivers/gpu/drm/panfrost/panfrost_device.h
>> index 54a8aad54259..5c24f01f8904 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_device.h
>> +++ b/drivers/gpu/drm/panfrost/panfrost_device.h
>> @@ -25,6 +25,12 @@ struct panfrost_perfcnt;
>> #define NUM_JOB_SLOTS 3
>> #define MAX_PM_DOMAINS 5
>>
>> +enum panfrost_drv_comp_bits {
>> + PANFROST_COMP_BIT_MMU,
>> + PANFROST_COMP_BIT_JOB,
>
> I think we need one for the GPU interrupt too, for the
> irq-line-is-shared-with-another-device thing I was mentioning.
>

Yes, I've also reordered the entries by name for v4.

>> + PANFROST_COMP_BIT_MAX
>> +};
>> +
>> /**
>> * enum panfrost_gpu_pm - Supported kernel power management features
>> * @GPU_PM_CLK_DIS: Allow disabling clocks during system suspend
>> @@ -109,6 +115,7 @@ struct panfrost_device {
>>
>> struct panfrost_features features;
>> const struct panfrost_compatible *comp;
>> + DECLARE_BITMAP(is_suspended, PANFROST_COMP_BIT_MAX);
>>
>> spinlock_t as_lock;
>> unsigned long as_in_use_mask;
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.c b/drivers/gpu/drm/panfrost/panfrost_gpu.c
>> index 7adc4441fa14..3a6a4fe7aca1 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.c
>> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.c
>> @@ -452,6 +452,12 @@ void panfrost_gpu_power_off(struct panfrost_device *pfdev)
>> dev_err(pfdev->dev, "l2 power transition timeout");
>> }
>>
>> +void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev)
>> +{
>
> set_bit(PANFROST_COMP_BIT_GPU, pfdev->is_suspended);
>
> here, and an extra check in panfrost_gpu_irq_handler() to bail out
> before the register accesses if PANFROST_COMP_BIT_GPU is set.
>

Right.

>> + gpu_write(pfdev, GPU_INT_MASK, 0);
>> + synchronize_irq(pfdev->gpu_irq);
>> +}
>> +
>> int panfrost_gpu_init(struct panfrost_device *pfdev)
>> {
>> int err;
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_gpu.h b/drivers/gpu/drm/panfrost/panfrost_gpu.h
>> index 876fdad9f721..d841b86504ea 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_gpu.h
>> +++ b/drivers/gpu/drm/panfrost/panfrost_gpu.h
>> @@ -15,6 +15,7 @@ u32 panfrost_gpu_get_latest_flush_id(struct panfrost_device *pfdev);
>> int panfrost_gpu_soft_reset(struct panfrost_device *pfdev);
>> void panfrost_gpu_power_on(struct panfrost_device *pfdev);
>> void panfrost_gpu_power_off(struct panfrost_device *pfdev);
>> +void panfrost_gpu_suspend_irq(struct panfrost_device *pfdev);
>>
>> void panfrost_cycle_counter_get(struct panfrost_device *pfdev);
>> void panfrost_cycle_counter_put(struct panfrost_device *pfdev);
>> diff --git a/drivers/gpu/drm/panfrost/panfrost_job.c b/drivers/gpu/drm/panfrost/panfrost_job.c
>> index f9446e197428..7600e7741211 100644
>> --- a/drivers/gpu/drm/panfrost/panfrost_job.c
>> +++ b/drivers/gpu/drm/panfrost/panfrost_job.c
>> @@ -405,6 +405,8 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
>> int j;
>> u32 irq_mask = 0;
>>
>> + clear_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
>> +
>> for (j = 0; j < NUM_JOB_SLOTS; j++) {
>> irq_mask |= MK_JS_MASK(j);
>> }
>> @@ -413,6 +415,14 @@ void panfrost_job_enable_interrupts(struct panfrost_device *pfdev)
>> job_write(pfdev, JOB_INT_MASK, irq_mask);
>> }
>>
>> +void panfrost_job_suspend_irq(struct panfrost_device *pfdev)
>> +{
>> + set_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended);
>> +
>> + job_write(pfdev, JOB_INT_MASK, 0);
>> + synchronize_irq(pfdev->js->irq);
>> +}
>> +
>> static void panfrost_job_handle_err(struct panfrost_device *pfdev,
>> struct panfrost_job *job,
>> unsigned int js)
>> @@ -792,9 +802,13 @@ static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
>> struct panfrost_device *pfdev = data;
>>
>> panfrost_job_handle_irqs(pfdev);
>> - job_write(pfdev, JOB_INT_MASK,
>> - GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>> - GENMASK(NUM_JOB_SLOTS - 1, 0));
>> +
>> + /* Enable interrupts only if we're not about to get suspended */
>> + if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
>> + job_write(pfdev, JOB_INT_MASK,
>> + GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>> + GENMASK(NUM_JOB_SLOTS - 1, 0));
>> +
>
> Missing if (test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended)) in
> panfrost_job_irq_handler(), to make sure you don't access the registers
> if the GPU is suspended.
>

Ok.

Cheers,
Angelo

Subject: Re: [PATCH v3 3/3] drm/panfrost: Synchronize and disable interrupts before powering off

Il 01/12/23 13:34, Steven Price ha scritto:
> On 01/12/2023 11:14, Boris Brezillon wrote:
>> On Fri, 1 Dec 2023 11:40:27 +0100
>> AngeloGioacchino Del Regno <[email protected]>
>> wrote:
>>
>>> To make sure that we don't unintentionally perform any unclocked and/or
>>> unpowered R/W operation on GPU registers, before turning off clocks and
>>> regulators we must make sure that no GPU, JOB or MMU ISR execution is
>>> pending: doing that required to add a mechanism to synchronize the
>>
>> ^ requires the addition of a mechanism...
>>
>>> interrupts on suspend.
>>>
>>> Add functions panfrost_{gpu,job,mmu}_suspend_irq() which will perform
>>> interrupts masking and ISR execution synchronization, and then call
>>> those in the panfrost_device_runtime_suspend() handler in the exact
>>> sequence of job (may require mmu!) -> mmu -> gpu.
>>>
>>> As a side note, JOB and MMU suspend_irq functions needed some special
>>> treatment: as their interrupt handlers will unmask interrupts, it was
>>> necessary to add a bitmap for `is_suspended` which is used to address
>>
>> to add an `is_suspended` bitmap which is used...
>>
>>> the possible corner case of unintentional IRQ unmasking because of ISR
>>> execution after a call to synchronize_irq().
>>
>> Also fixes the case where the interrupt handler is called when the
>> device is suspended because the IRQ line is shared with another device.
>> No need to update the commit message for that though.
>>
>>>
>>> At resume, clear each is_suspended bit in the reset path of JOB/MMU
>>> to allow unmasking the interrupts.
>>>
>>> Signed-off-by: AngeloGioacchino Del Regno <[email protected]>
>>> ---
>
> <snip>
>
>>> static void panfrost_job_handle_err(struct panfrost_device *pfdev,
>>> struct panfrost_job *job,
>>> unsigned int js)
>>> @@ -792,9 +802,13 @@ static irqreturn_t panfrost_job_irq_handler_thread(int irq, void *data)
>>> struct panfrost_device *pfdev = data;
>>>
>>> panfrost_job_handle_irqs(pfdev);
>>> - job_write(pfdev, JOB_INT_MASK,
>>> - GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>>> - GENMASK(NUM_JOB_SLOTS - 1, 0));
>>> +
>>> + /* Enable interrupts only if we're not about to get suspended */
>>> + if (!test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended))
>>> + job_write(pfdev, JOB_INT_MASK,
>>> + GENMASK(16 + NUM_JOB_SLOTS - 1, 16) |
>>> + GENMASK(NUM_JOB_SLOTS - 1, 0));
>>> +
>>
>> Missing if (test_bit(PANFROST_COMP_BIT_JOB, pfdev->is_suspended)) in
>> panfrost_job_irq_handler(), to make sure you don't access the registers
>> if the GPU is suspended.
>
> I think generally these IRQ handler functions should simply check the
> is_suspended flag and early out if the flag is set. It's not the
> re-enabling of the interrupts specifically that we want to gate - it's
> any access to the hardware as in the shared-IRQ case the GPU might
> already have been powered down/unclocked.
>

Yes, in the thread handler we're still powered, because we are synchronizing
irqs - adding the test_bit in the hardirq handler will prevent scheduling the
irq_handler_thread.

What the test_bit() here does is to allow us to handle the last interrupt(s)
(synchronize_irqs() in the suspend function) before cutting off power, without
unwillingly re-enabling the job (or mmu in panfrost_mmu.c) interrupts.

Cheers,
Angelo