On Wed, Apr 22, 2026 at 02:21:54PM +0100, Yeoreum Yun wrote:
[...]
> @@ -573,11 +573,9 @@ static int etm4_enable_hw(struct etmv4_drvdata *drvdata)
> etm4x_relaxed_write32(csa, config->res_ctrl[i], TRCRSCTLRn(i));
>
> for (i = 0; i < caps->nr_ss_cmp; i++) {
> - /* always clear status bit on restart if using single-shot */
> - if (config->ss_ctrl[i] || config->ss_pe_cmp[i])
> - config->ss_status[i] &= ~TRCSSCSRn_STATUS;
> etm4x_relaxed_write32(csa, config->ss_ctrl[i], TRCSSCCRn(i));
> - etm4x_relaxed_write32(csa, config->ss_status[i], TRCSSCSRn(i));
> + /* always clear status and pending bits on restart if using single-shot */
> + etm4x_relaxed_write32(csa, 0x0, TRCSSCSRn(i));
I am not confident what is the right way to handle the pending bit.
I looked a bit Arm ARM but still no clue. In particular, I suspect it
may need to be handled when disabling the trace in etm4_disable_hw().
Let's make it clear with some internal check.
> @@ -1829,8 +1829,8 @@ static ssize_t sshot_ctrl_store(struct device *dev,
> raw_spin_lock(&drvdata->spinlock);
> idx = config->ss_idx;
> config->ss_ctrl[idx] = FIELD_PREP(TRCSSCCRn_SAC_ARC_RST_MASK, val);
> - /* must clear bit 31 in related status register on programming */
> - config->ss_status[idx] &= ~TRCSSCSRn_STATUS;
> + /* must clear bit 31 and 30 in related status register on programming */
> + drvdata->ss_status[idx] &= ~(TRCSSCSRn_STATUS | TRCSSCSRn_PENDING);
Similarly, the question is: if it is in a pending state, how can we
ensure the state machine works properly with the new settings?
> raw_spin_unlock(&drvdata->spinlock);
> return size;
> }
> @@ -1879,8 +1879,8 @@ static ssize_t sshot_pe_ctrl_store(struct device *dev,
> raw_spin_lock(&drvdata->spinlock);
> idx = config->ss_idx;
> config->ss_pe_cmp[idx] = FIELD_PREP(TRCSSPCICRn_PC_MASK, val);
> - /* must clear bit 31 in related status register on programming */
> - config->ss_status[idx] &= ~TRCSSCSRn_STATUS;
> + /* must clear bit 31 and 30 in related status register on programming */
> + drvdata->ss_status[idx] &= ~(TRCSSCSRn_STATUS | TRCSSCSRn_PENDING);
Ditto.
Thanks,
Leo
On Wed, Apr 22, 2026 at 02:21:52PM +0100, Yeoreum Yun wrote:
> TCRSEQEVR<n> is implemented only when TCRIDR5.NUMSEQSTATE is 0b100,
> in which case n ranges from 0 to 2; otherwise, TCRIDR5.NUMSEQSTATE is 0b000.
>
> Therefore, drvdata->nrseqstate should be checked before entering the loop.
Since TRCSEQEVRn (n=0~2), to avoid confusion, we also need to rename
ETM_MAX_SEQ_STATES to ETM_MAX_SEQ_TRANSITIONS and define it as 3:
#define ETM_MAX_SEQ_TRANSITIONS 3
Then we don't allocate 4 items but use only 3 of them.
Thanks,
Leo
System register ETMs and ETE are unlikely to be preserved on CPU power
down. The ETE DT binding also never documented
"arm,coresight-loses-context-with-cpu" so nobody would have legitimately
been able to use that binding to fix it and ACPI has no such binding at
all.
Fix it by hard coding the setting for sysreg ETMs (ETE is always sysreg)
or ACPI boots.
This fixes the following error when using Coresight with ACPI on the FVP
which supports CPU PM:
coresight ete0: External agent took claim tag
WARNING: drivers/hwtracing/coresight/coresight-core.c:248 at coresight_disclaim_device_unlocked+0xe0/0xe8, CPU#0: perf/117
Fixes: 35e1c9163e02 ("coresight: ete: Add support for ETE tracing")
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
Fix PM save on ETE, which is an issue that showed up on the FVP when
booted with ACPI and the newly enabled idle states.
---
Changes in v2:
- Expand rule to also save for ACPI and sysreg ETMs. Now module param is
only read for MMIO ETMs and DT.
- Drop change to save the result in drvdata as the single global is
simpler.
- Link to v1: https://lore.kernel.org/r/20260428-james-cs-ete-pm_save_enable-v1-0-c7a90ca…
---
drivers/hwtracing/coresight/coresight-etm4x-core.c | 42 +++++++++++++++-------
1 file changed, 30 insertions(+), 12 deletions(-)
diff --git a/drivers/hwtracing/coresight/coresight-etm4x-core.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
index d565a73f0042..b6b46c2f8ee8 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x-core.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -56,10 +56,14 @@ MODULE_PARM_DESC(boot_enable, "Enable tracing on boot");
#define PARAM_PM_SAVE_NEVER 1 /* never save any state */
#define PARAM_PM_SAVE_SELF_HOSTED 2 /* save self-hosted state only */
+/*
+ * Save option for ETM4. ETE, sysreg ETM4s and ACPI boots ignore this option and
+ * will always save.
+ */
static int pm_save_enable = PARAM_PM_SAVE_FIRMWARE;
module_param(pm_save_enable, int, 0444);
MODULE_PARM_DESC(pm_save_enable,
- "Save/restore state on power down: 1 = never, 2 = self-hosted");
+ "Save/restore state on power down: 1 = never, 2 = self-hosted. MMIO and DT only.");
static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
static void etm4_set_default_config(struct etmv4_config *config);
@@ -2212,6 +2216,17 @@ static void etm4_pm_clear(void)
}
}
+static bool etm4x_always_pm_save(struct device *dev, struct csdev_access *csa)
+{
+ /*
+ * Only IO mem ETM devices will benefit from skipping PM save and only
+ * DT has the option to control it, not ACPI. Otherwise system register
+ * based ETMs and ETEs will always lose context on CPU power down, so
+ * always save.
+ */
+ return !csa->io_mem || is_acpi_device_node(dev_fwnode(dev));
+}
+
static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
{
int ret;
@@ -2248,6 +2263,20 @@ static int etm4_add_coresight_dev(struct etm4_init_arg *init_arg)
etm4_set_default(&drvdata->config);
+ if (etm4x_always_pm_save(dev, init_arg->csa))
+ pm_save_enable = PARAM_PM_SAVE_SELF_HOSTED;
+ else if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
+ pm_save_enable = coresight_loses_context_with_cpu(dev) ?
+ PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
+
+ if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
+ drvdata->save_state = devm_kmalloc(dev,
+ sizeof(struct etmv4_save_state),
+ GFP_KERNEL);
+ if (!drvdata->save_state)
+ return -ENOMEM;
+ }
+
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
@@ -2305,17 +2334,6 @@ static int etm4_probe(struct device *dev)
if (ret)
return ret;
- if (pm_save_enable == PARAM_PM_SAVE_FIRMWARE)
- pm_save_enable = coresight_loses_context_with_cpu(dev) ?
- PARAM_PM_SAVE_SELF_HOSTED : PARAM_PM_SAVE_NEVER;
-
- if (pm_save_enable != PARAM_PM_SAVE_NEVER) {
- drvdata->save_state = devm_kmalloc(dev,
- sizeof(struct etmv4_save_state), GFP_KERNEL);
- if (!drvdata->save_state)
- return -ENOMEM;
- }
-
raw_spin_lock_init(&drvdata->spinlock);
drvdata->cpu = coresight_get_cpu(dev);
---
base-commit: 971f3474f8898ae8bbab19a9b547819a5e6fbcf1
change-id: 20260420-james-cs-ete-pm_save_enable-4e994e35cdac
Best regards,
--
James Clark <james.clark(a)linaro.org>
Fix PM save on ETE, which is an issue that showed up on the FVP when
booted with ACPI and the newly enabled idle states. Then refactor to
clarify and avoid any probe order issues.
Signed-off-by: James Clark <james.clark(a)linaro.org>
---
James Clark (2):
coresight: ete: Always save state on power down
coresight: etm4x: Refactor pm_save_enable handling
drivers/hwtracing/coresight/coresight-etm4x-core.c | 55 ++++++++++++++++------
drivers/hwtracing/coresight/coresight-etm4x.h | 1 +
2 files changed, 42 insertions(+), 14 deletions(-)
---
base-commit: 971f3474f8898ae8bbab19a9b547819a5e6fbcf1
change-id: 20260420-james-cs-ete-pm_save_enable-4e994e35cdac
Best regards,
--
James Clark <james.clark(a)linaro.org>
On Fri, May 01, 2026 at 01:55:46PM +0100, Yeoreum Yun wrote:
> Gentle ping in case of forgotten.
This is on my todo list - let me finish the task in hand and then I will
play a bit this series.
Thanks,
Leo
On Tue, Apr 28, 2026 at 10:25:11AM +0800, Yingchao Deng (Consultant) wrote:
[...]
> > tg->used_mask = bitmap_zalloc(nr_filter_sigs, GFP_KERNEL);
> "nr_filter_sigs" is the count of entries in the DT property array, if the DT
> property is:
> arm,trig-filters = <22 23>;
> Here nr_filter_sigs=2, so bitmap_zalloc(2) allocates only 1 unsigned long
> (64 bits). set_bit(22/23, used_mask) still fits, but it's logically an OOB,
> and any index >=64 would
> write past the end.
Thanks for explanation. It is correct for me.
On Sun, Apr 26, 2026 at 05:44:39PM +0800, Yingchao Deng wrote:
> Introduce a small encoding to carry the register index together with the
> base offset in a single u32, and use a common helper to compute the final
> MMIO address. This refactors register access to be based on the encoded
> (reg, nr) pair, reducing duplicated arithmetic and making it easier to
> support variants that bank or relocate trigger-indexed registers.
>
> Signed-off-by: Yingchao Deng <yingchao.deng(a)oss.qualcomm.com>
> ---
> drivers/hwtracing/coresight/coresight-cti-core.c | 31 +++++++++++++++--------
> drivers/hwtracing/coresight/coresight-cti-sysfs.c | 4 +--
> drivers/hwtracing/coresight/coresight-cti.h | 16 ++++++++++--
> 3 files changed, 36 insertions(+), 15 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-cti-core.c b/drivers/hwtracing/coresight/coresight-cti-core.c
> index 4e7d12bd2d3e..c4cbeb64365b 100644
> --- a/drivers/hwtracing/coresight/coresight-cti-core.c
> +++ b/drivers/hwtracing/coresight/coresight-cti-core.c
> @@ -42,6 +42,14 @@ static DEFINE_MUTEX(ect_mutex);
> #define csdev_to_cti_drvdata(csdev) \
> dev_get_drvdata(csdev->dev.parent)
>
> +static void __iomem *cti_reg_addr(struct cti_drvdata *drvdata, int reg)
> +{
> + u32 offset = CTI_REG_CLR_NR(reg);
> + u32 nr = CTI_REG_GET_NR(reg);
> +
> + return drvdata->base + offset + sizeof(u32) * nr;
> +}
Could you try below change, which is more straightforward?
static void __iomem *__reg_addr(struct cti_drvdata *drvdata, int off,
int index)
{
return drvdata->base + offset + sizeof(u32) * index;
}
#define reg_addr(drvdata, off) \
__reg_addr((drvdata), (off), 0)
#define reg_index_addr(drvdata, off, i) \
__reg_addr((drvdata), (off), (i))
> +
> /* write set of regs to hardware - call with spinlock claimed */
> void cti_write_all_hw_regs(struct cti_drvdata *drvdata)
> {
> @@ -55,16 +63,17 @@ void cti_write_all_hw_regs(struct cti_drvdata *drvdata)
>
> /* write the CTI trigger registers */
> for (i = 0; i < config->nr_trig_max; i++) {
> - writel_relaxed(config->ctiinen[i], drvdata->base + CTIINEN(i));
> + writel_relaxed(config->ctiinen[i],
> + cti_reg_addr(drvdata, CTI_REG_SET_NR(CTIINEN, i)));
writel_relaxed(config->ctiinen[i],
reg_index_addr(drvdata, CTIINEN, i));
> writel_relaxed(config->ctiouten[i],
> - drvdata->base + CTIOUTEN(i));
> + cti_reg_addr(drvdata, CTI_REG_SET_NR(CTIOUTEN, i)));
writel_relaxed(config->ctiouten[i],
reg_index_addr(drvdata, CTIOUTEN, i));
[...]
> +/*
> + * Encode CTI register offset and register index in one u32:
> + * - bits[0:11] : base register offset (0x000 to 0xFFF)
> + * - bits[24:31] : register index (nr)
> + */
> +#define CTI_REG_NR_MASK GENMASK(31, 24)
> +#define CTI_REG_GET_NR(reg) FIELD_GET(CTI_REG_NR_MASK, (reg))
> +#define CTI_REG_SET_NR_CONST(reg, nr) ((reg) | FIELD_PREP_CONST(CTI_REG_NR_MASK, (nr)))
> +#define CTI_REG_SET_NR(reg, nr) ((reg) | FIELD_PREP(CTI_REG_NR_MASK, (nr)))
> +#define CTI_REG_CLR_NR(reg) ((reg) & (~CTI_REG_NR_MASK))
I know this might come from my suggestion, and it is also will be
heavily used in patch 04. We can have strightforward way to
implement this, please drop these macros.
I will reply in patch 04 separately. Sorry my review might cause
extra effort.
Thanks,
Leo