[PATCH v4 13/15] coresight: trbe: Save and restore state across CPU low power state
James Clark
james.clark at linaro.org
Mon Nov 10 04:01:03 PST 2025
On 04/11/2025 3:21 pm, Leo Yan wrote:
> From: Yabin Cui <yabinc at google.com>
>
> Similar to ETE, TRBE may lose its context when a CPU enters low power
> state. To make things worse, if ETE is restored without TRBE being
> restored, an enabled source device with no enabled sink devices can
> cause CPU hang on some devices (e.g., Pixel 9).
>
> The save and restore flows are described in the section K5.5 "Context
> switching" of Arm ARM (ARM DDI 0487 L.a). This commit adds save and
> restore callbacks with following the software usages defined in the
> architecture manual.
>
> Signed-off-by: Yabin Cui <yabinc at google.com>
> Co-developed-by: Leo Yan <leo.yan at arm.com>
> Signed-off-by: Leo Yan <leo.yan at arm.com>
> ---
> drivers/hwtracing/coresight/coresight-trbe.c | 84 +++++++++++++++++++++++++++-
> 1 file changed, 83 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-trbe.c b/drivers/hwtracing/coresight/coresight-trbe.c
> index 3c82ab4394fde1e3dd4371a9b1703da4d8f6db9d..9888a153bc2f99b4826f6103bdbc12304c9c94cb 100644
> --- a/drivers/hwtracing/coresight/coresight-trbe.c
> +++ b/drivers/hwtracing/coresight/coresight-trbe.c
> @@ -116,6 +116,20 @@ static int trbe_errata_cpucaps[] = {
> */
> #define TRBE_WORKAROUND_OVERWRITE_FILL_MODE_SKIP_BYTES 256
>
> +/*
> + * struct trbe_save_state: Register values representing TRBE state
> + * @trblimitr - Trace Buffer Limit Address Register value
> + * @trbbaser - Trace Buffer Base Register value
> + * @trbptr - Trace Buffer Write Pointer Register value
> + * @trbsr - Trace Buffer Status Register value
> + */
> +struct trbe_save_state {
> + u64 trblimitr;
> + u64 trbbaser;
> + u64 trbptr;
> + u64 trbsr;
> +};
> +
> /*
> * struct trbe_cpudata: TRBE instance specific data
> * @trbe_flag - TRBE dirty/access flag support
> @@ -134,6 +148,7 @@ struct trbe_cpudata {
> enum cs_mode mode;
> struct trbe_buf *buf;
> struct trbe_drvdata *drvdata;
> + struct trbe_save_state save_state;
> DECLARE_BITMAP(errata, TRBE_ERRATA_MAX);
> };
>
> @@ -1189,6 +1204,71 @@ static irqreturn_t arm_trbe_irq_handler(int irq, void *dev)
> return IRQ_HANDLED;
> }
>
> +static int arm_trbe_save(struct coresight_device *csdev)
> +{
> + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
> + struct trbe_save_state *state = &cpudata->save_state;
> +
> + if (cpudata->mode == CS_MODE_DISABLED)
> + return 0;
> +
> + /*
> + * According to the section K5.5 Context switching, Arm ARM (ARM DDI
> + * 0487 L.a), the software usage VKHHY requires a TSB CSYNC instruction
> + * to ensure the program-flow trace is flushed, which has been executed
> + * in ETM driver.
> + */
> +
> + /* Disable trace buffer unit */
> + state->trblimitr = read_sysreg_s(SYS_TRBLIMITR_EL1);
> + write_sysreg_s(state->trblimitr & ~TRBLIMITR_EL1_E, SYS_TRBLIMITR_EL1);
> +
> + /*
> + * Execute a further Context synchronization event. Ensure the writes to
> + * memory are complete.
> + */
> + trbe_drain_buffer();
> +
> + /* Synchronize the TRBE disabling */
> + isb();
Can we use set_trbe_disabled() here, and set_trbe_enabled() for restore
below. There is a bit of duplication and they do the same thing except
this version here always has trbe_drain_buffer() rather than it being
conditional. I'm not sure if that is correct as they should both be the
same?
set_trbe_disabled() already reads TRBLIMITR_EL1 so you can return it.
> +
> + state->trbbaser = read_sysreg_s(SYS_TRBBASER_EL1);
> + state->trbptr = read_sysreg_s(SYS_TRBPTR_EL1);
> + state->trbsr = read_sysreg_s(SYS_TRBSR_EL1);
> + return 0;
> +}
> +
> +static void arm_trbe_restore(struct coresight_device *csdev)
> +{
> + struct trbe_cpudata *cpudata = dev_get_drvdata(&csdev->dev);
> + struct trbe_save_state *state = &cpudata->save_state;
> +
> + if (cpudata->mode == CS_MODE_DISABLED)
> + return;
> +
> + write_sysreg_s(state->trbbaser, SYS_TRBBASER_EL1);
> + write_sysreg_s(state->trbptr, SYS_TRBPTR_EL1);
> + write_sysreg_s(state->trbsr, SYS_TRBSR_EL1);
> + write_sysreg_s(state->trblimitr & ~TRBLIMITR_EL1_E, SYS_TRBLIMITR_EL1);
> +
> + /*
> + * According to the section K5.5 Context switching, Arm ARM (ARM DDI
> + * 0487 L.a), the software usage PKLXF requires a Context
> + * synchronization event to guarantee the Trace Buffer Unit will observe
> + * the new values of the System registers.
> + */
> + isb();
> +
> + /* Enable the Trace Buffer Unit */
> + write_sysreg_s(state->trblimitr, SYS_TRBLIMITR_EL1);
> +
> + /* Synchronize the TRBE enable event */
> + isb();
> +
> + if (trbe_needs_ctxt_sync_after_enable(cpudata))
> + isb();
Especially this double isb() part shouldn't be duplicated as that could
get broken in a refactor in the future.
> +}
> +
> static const struct coresight_ops_sink arm_trbe_sink_ops = {
> .enable = arm_trbe_enable,
> .disable = arm_trbe_disable,
> @@ -1198,7 +1278,9 @@ static const struct coresight_ops_sink arm_trbe_sink_ops = {
> };
>
> static const struct coresight_ops arm_trbe_cs_ops = {
> - .sink_ops = &arm_trbe_sink_ops,
> + .pm_save_disable = arm_trbe_save,
> + .pm_restore_enable = arm_trbe_restore,
> + .sink_ops = &arm_trbe_sink_ops,
> };
>
> static ssize_t align_show(struct device *dev, struct device_attribute *attr, char *buf)
>
More information about the linux-arm-kernel
mailing list