[PATCH v9 4/7] coresight: tmc: Enable panic sync handling

Linu Cherian lcherian at marvell.com
Wed Jun 19 21:10:54 PDT 2024


On 2024-06-10 at 18:32:33, Suzuki K Poulose (suzuki.poulose at arm.com) wrote:
> On 05/06/2024 09:17, Linu Cherian wrote:
> > - Get reserved region from device tree node for metadata
> > - Define metadata format for TMC
> > - Add TMC ETR panic sync handler that syncs register snapshot
> >    to metadata region
> > - Add TMC ETF panic sync handler that syncs register snapshot
> >    to metadata region and internal SRAM to reserved trace buffer
> >    region.
> > 
> > Signed-off-by: Linu Cherian <lcherian at marvell.com>
> > Reviewed-by: James Clark <james.clark at arm.com>
> > ---
> > Changelog from v8:
> > Added Reviewed-by tag.
> > 
> >   .../hwtracing/coresight/coresight-tmc-core.c  | 25 +++++++
> >   .../hwtracing/coresight/coresight-tmc-etf.c   | 72 +++++++++++++++++++
> >   .../hwtracing/coresight/coresight-tmc-etr.c   | 70 ++++++++++++++++++
> >   drivers/hwtracing/coresight/coresight-tmc.h   | 45 +++++++++++-
> >   4 files changed, 211 insertions(+), 1 deletion(-)
> > 
> > diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c
> > index 6beb69d74d0a..daad08bc693d 100644
> > --- a/drivers/hwtracing/coresight/coresight-tmc-core.c
> > +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c
> > @@ -443,6 +443,31 @@ static void tmc_get_reserved_region(struct device *parent)
> >   	drvdata->crash_tbuf.paddr = res.start;
> >   	drvdata->crash_tbuf.size  = resource_size(&res);
> > +
> > +	/* Metadata region */
> > +	node = tmc_get_region_byname(parent->of_node, "metadata");
> > +	if (IS_ERR_OR_NULL(node)) {
> > +		dev_dbg(parent, "No metadata memory-region specified\n");
> > +		return;
> > +	}
> > +
> > +	rc = of_address_to_resource(node, 0, &res);
> > +	of_node_put(node);
> > +	if (rc || res.start == 0 || resource_size(&res) == 0) {
> > +		dev_err(parent, "Metadata memory is invalid\n");
> > +		return;
> > +	}
> > +
> > +	drvdata->crash_mdata.vaddr = memremap(res.start,
> > +					       resource_size(&res),
> > +					       MEMREMAP_WC);
> > +	if (IS_ERR_OR_NULL(drvdata->crash_mdata.vaddr)) {
> > +		dev_err(parent, "Metadata memory mapping failed\n");
> > +		return;
> > +	}
> > +
> > +	drvdata->crash_mdata.paddr = res.start;
> > +	drvdata->crash_mdata.size  = resource_size(&res);
> >   }
> >   /* Detect and initialise the capabilities of a TMC ETR */
> > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> > index d4f641cd9de6..f9569585e9f8 100644
> > --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
> > +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
> > @@ -590,6 +590,73 @@ static unsigned long tmc_update_etf_buffer(struct coresight_device *csdev,
> >   	return to_read;
> >   }
> > +static int tmc_panic_sync_etf(struct coresight_device *csdev)
> > +{
> > +	u32 val;
> > +	struct csdev_access *csa;
> > +	struct tmc_crash_metadata *mdata;
> > +	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +
> > +	csa = &drvdata->csdev->access;
> > +
> > +	/* Make sure we have valid reserved memory */
> > +	if (!is_tmc_reserved_region_valid(csdev->dev.parent))
> > +		return 0;
> > +
> > +	mdata = (struct tmc_crash_metadata *)drvdata->crash_mdata.vaddr;
> > +	mdata->valid = false;
> > +
> > +	CS_UNLOCK(drvdata->base);
> > +
> > +	/* Proceed only if ETF is enabled or configured as sink */
> > +	val = readl(drvdata->base + TMC_CTL);
> > +	if (!(val & TMC_CTL_CAPT_EN))
> > +		goto out;
> > +
> > +	val = readl(drvdata->base + TMC_MODE);
> > +	if (val != TMC_MODE_CIRCULAR_BUFFER)
> > +		goto out;
> > +
> > +	val = readl(drvdata->base + TMC_FFSR);
> > +	/* Do manual flush and stop only if its not auto-stopped */
> > +	if (!(val & TMC_FFSR_FT_STOPPED)) {
> > +		dev_info(&csdev->dev,
> > +			 "%s: Triggering manual flush\n", __func__);
> > +		tmc_flush_and_stop(drvdata);
> > +	} else
> > +		tmc_wait_for_tmcready(drvdata);
> > +
> > +	/* Sync registers from hardware to metadata region */
> > +	mdata->sts = csdev_access_relaxed_read32(csa, TMC_STS);
> > +	mdata->trc_paddr = drvdata->crash_tbuf.paddr;
> > +
> > +	/* Sync Internal SRAM to reserved trace buffer region */
> > +	drvdata->buf = drvdata->crash_tbuf.vaddr;
> > +	tmc_etb_dump_hw(drvdata);
> > +	/* Store as per RSZ register convention */
> > +	mdata->size = drvdata->len >> 2;
> > +
> > +	/*
> > +	 * Make sure all previous writes are completed,
> > +	 * before we mark valid
> > +	 */
> > +	dsb(sy);
> > +	mdata->valid = true;
> > +	/*
> > +	 * Below order need to maintained, since crc of metadata
> > +	 * is dependent on first
> > +	 */
> > +	mdata->crc32_tdata = find_crash_tracedata_crc(drvdata, mdata);
> > +	mdata->crc32_mdata = find_crash_metadata_crc(mdata);
> > +
> > +	tmc_disable_hw(drvdata);
> > +
> > +	dev_info(&csdev->dev, "%s: success\n", __func__);
> > +out:
> > +	CS_UNLOCK(drvdata->base);
> > +	return 0;
> > +}
> > +
> >   static const struct coresight_ops_sink tmc_etf_sink_ops = {
> >   	.enable		= tmc_enable_etf_sink,
> >   	.disable	= tmc_disable_etf_sink,
> > @@ -603,6 +670,10 @@ static const struct coresight_ops_link tmc_etf_link_ops = {
> >   	.disable	= tmc_disable_etf_link,
> >   };
> > +static const struct coresight_ops_panic tmc_etf_sync_ops = {
> > +	.sync		= tmc_panic_sync_etf,
> > +};
> > +
> >   const struct coresight_ops tmc_etb_cs_ops = {
> >   	.sink_ops	= &tmc_etf_sink_ops,
> >   };
> > @@ -610,6 +681,7 @@ const struct coresight_ops tmc_etb_cs_ops = {
> >   const struct coresight_ops tmc_etf_cs_ops = {
> >   	.sink_ops	= &tmc_etf_sink_ops,
> >   	.link_ops	= &tmc_etf_link_ops,
> > +	.panic_ops	= &tmc_etf_sync_ops,
> >   };
> >   int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
> > diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > index 041c428dd7cd..be1079e8fd64 100644
> > --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
> > @@ -1813,6 +1813,71 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
> >   	return 0;
> >   }
> > +static int tmc_panic_sync_etr(struct coresight_device *csdev)
> > +{
> > +	u32 val;
> > +	struct csdev_access *csa;
> > +	struct tmc_crash_metadata *mdata;
> > +	struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
> > +
> > +	csa = &drvdata->csdev->access;
> > +
> > +	if (!drvdata->etr_buf)
> > +		return 0;
> > +
> > +	/* Being in RESRV mode implies valid reserved memory as well */
> > +	if (drvdata->etr_buf->mode != ETR_MODE_RESRV)
> > +		return 0;
> > +
> > +	mdata = (struct tmc_crash_metadata *)drvdata->crash_mdata.vaddr;
> > +	mdata->valid = false;
> > +
> > +	CS_UNLOCK(drvdata->base);
> > +
> > +	/* Proceed only if ETR is enabled */
> > +	val = readl(drvdata->base + TMC_CTL);
> > +	if (!(val & TMC_CTL_CAPT_EN))
> > +		goto out;
> > +
> > +	val = readl(drvdata->base + TMC_FFSR);
> > +	/* Do manual flush and stop only if its not auto-stopped */
> > +	if (!(val & TMC_FFSR_FT_STOPPED)) {
> > +		dev_info(&csdev->dev,
> > +			 "%s: Triggering manual flush\n", __func__);
> > +		tmc_flush_and_stop(drvdata);
> > +	} else
> > +		tmc_wait_for_tmcready(drvdata);
> > +
> > +	/* Sync registers from hardware to metadata region */
> > +	mdata->size = csdev_access_relaxed_read32(csa, TMC_RSZ);
> > +	mdata->sts = csdev_access_relaxed_read32(csa, TMC_STS);
> > +	mdata->rrp = tmc_read_rrp(drvdata);
> > +	mdata->rwp = tmc_read_rwp(drvdata);
> > +	mdata->dba = tmc_read_dba(drvdata);
> > +	mdata->trc_paddr = drvdata->crash_tbuf.paddr;
> > +
> > +	/*
> > +	 * Make sure all previous writes are completed,
> > +	 * before we mark valid
> > +	 */
> > +	dsb(sy);
> > +	mdata->valid = true;
> > +	/*
> > +	 * Below order need to maintained, since crc of metadata
> > +	 * is dependent on first
> > +	 */
> > +	mdata->crc32_tdata = find_crash_tracedata_crc(drvdata, mdata);
> > +	mdata->crc32_mdata = find_crash_metadata_crc(mdata);
> > +
> > +	tmc_disable_hw(drvdata);
> > +
> > +	dev_info(&csdev->dev, "%s: success\n", __func__);
> > +out:
> > +	CS_UNLOCK(drvdata->base);
> > +
> > +	return 0;
> > +}
> > +
> >   static const struct coresight_ops_sink tmc_etr_sink_ops = {
> >   	.enable		= tmc_enable_etr_sink,
> >   	.disable	= tmc_disable_etr_sink,
> > @@ -1821,8 +1886,13 @@ static const struct coresight_ops_sink tmc_etr_sink_ops = {
> >   	.free_buffer	= tmc_free_etr_buffer,
> >   };
> > +static const struct coresight_ops_panic tmc_etr_sync_ops = {
> > +	.sync		= tmc_panic_sync_etr,
> > +};
> > +
> >   const struct coresight_ops tmc_etr_cs_ops = {
> >   	.sink_ops	= &tmc_etr_sink_ops,
> > +	.panic_ops	= &tmc_etr_sync_ops,
> >   };
> >   int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
> > diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
> > index c23dc9917ab9..35beee53584a 100644
> > --- a/drivers/hwtracing/coresight/coresight-tmc.h
> > +++ b/drivers/hwtracing/coresight/coresight-tmc.h
> > @@ -12,6 +12,7 @@
> >   #include <linux/miscdevice.h>
> >   #include <linux/mutex.h>
> >   #include <linux/refcount.h>
> > +#include <linux/crc32.h>
> >   #define TMC_RSZ			0x004
> >   #define TMC_STS			0x00c
> > @@ -76,6 +77,9 @@
> >   #define TMC_AXICTL_AXCACHE_OS	(0xf << 2)
> >   #define TMC_AXICTL_ARCACHE_OS	(0xf << 16)
> > +/* TMC_FFSR - 0x300 */
> > +#define TMC_FFSR_FT_STOPPED	BIT(1)
> > +
> >   /* TMC_FFCR - 0x304 */
> >   #define TMC_FFCR_FLUSHMAN_BIT	6
> >   #define TMC_FFCR_EN_FMT		BIT(0)
> > @@ -131,6 +135,21 @@ enum tmc_mem_intf_width {
> >   #define CORESIGHT_SOC_600_ETR_CAPS	\
> >   	(TMC_ETR_SAVE_RESTORE | TMC_ETR_AXI_ARCACHE)
> 
> 
> 
> 
> > +/* TMC metadata region for ETR and ETF configurations */
> 
> I strongly think we should version the data layout to handle
> the future changes better (if at all).


> 
> 
> > +struct tmc_crash_metadata {
> > +	uint32_t crc32_mdata;	/* crc of metadata */
> > +	uint32_t crc32_tdata;	/* crc of tracedata */
> 
> 	uint32_t version;	/* Version of the structure = 1 */

Ack.

> 
> > +	uint32_t valid;         /* Indicate if this ETF/ETR was enabled */
> > +	uint32_t size;          /* Ram Size register */
> 
> Please save the size in bytes and also rename it :
> 
> 	uint32_t trace_size;	/* Trace size in Bytes */

The idea was to only take a register snapshot by kernel/firmware at the time of
panic/watchdog reset and thought that keeping that panic handler/watchdog reset
handler without any conversions would be better when external entities like firmware
is involved.

Please let me know if you still prefer adding this field.

> 
> 
> > +	uint32_t sts;           /* Status register */
> > +	uint32_t reserved32[3];
> > +	uint64_t rrp;           /* Ram Read pointer register */
> > +	uint64_t rwp;           /* Ram Write pointer register */
> 
> 
> 
> > +	uint64_t dba;		/* Data buffer address register */
> 
> Is this field useful ? And we store RRP/RWP relative to the DBA ? Could

RRP/RWP/DBA fields are just register snapshots.
Yes we use the DBA to calculate offsets. 
ie. etr_buf->offset = rrp - dba;


> we instead :
> 
> 1. Drop DBA
> 2. Store RRP and RWP as offsets from DBA. Or even convert them to the
> actual PADDRs relative to the trc_paddr.

Converting to actual PADDRs would be difficult in case of watchdog reset
scenarios where firmware is involved.

> 
> DBA could be a "DMA" Address and not necessarily the PA Address.
> We already have the trc_paddr below. (And for ETF, we already copy
> the buffer to the reserved buffer). So all the user needs to know
> is where the pointers are within the buffer. Having them relative
> to the "actual" location of the buffer is much useful than basing
> it on some unusable base address.

As pointed out above, the idea was to only take a register snapshot without
any conversions. Please let me know if you still prefer dropping the DBA.

> 
> > +	uint64_t trc_paddr;	/* Phys address of trace buffer */
> 
> s/trc/trace
> 
> Move RRP and RWP, after the above field.
> 
> For the sake of completeness, you are also missing :
> 
> 1) FFCR register => That tells you whether the Formatting was enabled or not
> (among other things) ? Though we always enable it, its good to
> capture it, if we ever decide to turn off the formatting.
> 
> 
> 2) MODE => Which mode was selected. Again, CIRCULAR_BUFFER for now,
> but lets seal it for the future, so that you can infer the trace buffer
> correctly with RRP/RWP.
> 
> 3) And may be FFSR, just in case the flush never completed and the
> data is not reliable ?

Sure will add the above three fields.

> 
> > +	uint64_t reserved64[3];
> > +};
> 
> 
> 
> 
> > +
> >   enum etr_mode {
> >   	ETR_MODE_FLAT,		/* Uses contiguous flat buffer */
> >   	ETR_MODE_ETR_SG,	/* Uses in-built TMC ETR SG mechanism */
> > @@ -204,6 +223,8 @@ struct tmc_resrv_buf {
> >    *		retention (after crash) only when ETR_MODE_RESRV buffer
> >    *		mode is enabled. Used by ETF for trace data retention
> >    *		(after crash) by default.
> > + * @crash_mdata: Reserved memory for storing tmc crash metadata.
> > + *		 Used by ETR/ETF.
> >    */
> >   struct tmc_drvdata {
> >   	struct clk		*pclk;
> > @@ -230,6 +251,7 @@ struct tmc_drvdata {
> >   	struct etr_buf		*sysfs_buf;
> >   	struct etr_buf		*perf_buf;
> >   	struct tmc_resrv_buf	crash_tbuf;
> > +	struct tmc_resrv_buf	crash_mdata;
> >   };
> >   struct etr_buf_operations {
> > @@ -352,11 +374,32 @@ static inline bool is_tmc_reserved_region_valid(struct device *dev)
> >   	struct tmc_drvdata *drvdata = dev_get_drvdata(dev);
> >   	if (drvdata->crash_tbuf.paddr &&
> > -		drvdata->crash_tbuf.size)
> > +		drvdata->crash_tbuf.size &&
> > +		drvdata->crash_mdata.paddr &&
> > +		drvdata->crash_mdata.size)
> 
> Why do we need to tie the "reserved" region to metdata region availability ?
> It is perfectly possible for another usecase
> to dedicate a buffer for trace and use it without metadata ?

Okay that can be reworked.

> 
> Suzuki
> 
> 
> >   		return true;
> >   	return false;
> >   }
> > +static inline uint32_t find_crash_metadata_crc(struct tmc_crash_metadata *md)
> > +{
> > +	unsigned long crc_size;
> > +
> > +	crc_size = sizeof(struct tmc_crash_metadata) -
> > +		offsetof(struct tmc_crash_metadata, crc32_tdata);
> > +	return crc32_le(0, (void *)&md->crc32_tdata, crc_size);
> > +}
> > +
> > +static inline uint32_t find_crash_tracedata_crc(struct tmc_drvdata *drvdata,
> > +						struct tmc_crash_metadata *md)
> > +{
> > +	unsigned long crc_size;
> > +
> > +	/* Take CRC of configured buffer size to keep it simple */
> > +	crc_size = md->size << 2;
> > +	return crc32_le(0, (void *)drvdata->crash_tbuf.vaddr, crc_size);
> > +}
> > +
> >   struct coresight_device *tmc_etr_get_catu_device(struct tmc_drvdata *drvdata);
> >   void tmc_etr_set_catu_ops(const struct etr_buf_operations *catu);
> 
> 
> _______________________________________________
> linux-arm-kernel mailing list
> linux-arm-kernel at lists.infradead.org
> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel



More information about the linux-arm-kernel mailing list