out:
return ret;
}
@@ -672,7 +809,8 @@ static void tmc_shutdown(struct amba_device *adev)
spin_lock_irqsave(&drvdata->spinlock, flags);
- if (coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED)
+ if ((coresight_get_mode(drvdata->csdev) == CS_MODE_DISABLED) ||
+ (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA))
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c
index f9569585e9f8..a8cabbf6679b 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etf.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c
@@ -657,6 +657,56 @@ static int tmc_panic_sync_etf(struct coresight_device *csdev)
return 0;
}
+static int tmc_etb_setup_crashdata_buf(struct tmc_drvdata *drvdata)
+{
+ unsigned long size;
+ struct tmc_crash_metadata *mdata;
+ struct device *dev = &drvdata->csdev->dev;
+
+ mdata = drvdata->crash_mdata.vaddr;
+ size = mdata->size << 2;
+
+ /*
+ * Buffer address given by metadata for retrieval of trace data
+ * from previous boot is expected to be same as the reserved
+ * trace buffer memory region provided through DTS
+ */
+ if (is_tmc_reserved_region_valid(dev->parent)
+ && (drvdata->crash_tbuf.paddr == mdata->trc_paddr))
+ drvdata->buf = drvdata->crash_tbuf.vaddr;
+ else {
+ dev_dbg(dev, "Trace buffer address of previous boot invalid\n");
+ return -EINVAL;
+ }
+
+ drvdata->len = size;
+ return 0;
+}
+
+static void tmc_etb_free_crashdata_buf(struct tmc_drvdata *drvdata)
+{
+ void *buf = drvdata->buf;
+
+ if (!buf)
+ return;
+ drvdata->buf = NULL;
+}
+
+static int tmc_etb_prepare_crashdata(struct coresight_device *csdev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ return tmc_etb_setup_crashdata_buf(drvdata);
+}
+
+static int tmc_etb_unprepare_crashdata(struct coresight_device *csdev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ tmc_etb_free_crashdata_buf(drvdata);
+ return 0;
+}
+
static const struct coresight_ops_sink tmc_etf_sink_ops = {
.enable = tmc_enable_etf_sink,
.disable = tmc_disable_etf_sink,
@@ -674,6 +724,11 @@ static const struct coresight_ops_panic tmc_etf_sync_ops = {
.sync = tmc_panic_sync_etf,
};
+static const struct coresight_ops_crashdata tmc_etf_crashdata_ops = {
+ .prepare = tmc_etb_prepare_crashdata,
+ .unprepare = tmc_etb_unprepare_crashdata,
+};
+
const struct coresight_ops tmc_etb_cs_ops = {
.sink_ops = &tmc_etf_sink_ops,
};
@@ -682,6 +737,7 @@ 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,
+ .crashdata_ops = &tmc_etf_crashdata_ops,
};
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
@@ -702,6 +758,14 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
goto out;
}
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA) {
+ ret = tmc_read_prepare_crashdata(drvdata);
+ if (ret)
+ goto out;
+ else
+ goto mode_valid;
+ }
+
/* Don't interfere if operated from Perf */
if (coresight_get_mode(drvdata->csdev) == CS_MODE_PERF) {
ret = -EINVAL;
@@ -725,6 +789,7 @@ int tmc_read_prepare_etb(struct tmc_drvdata *drvdata)
__tmc_etb_disable_hw(drvdata);
}
+mode_valid:
drvdata->reading = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -746,6 +811,13 @@ int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA) {
+ tmc_read_unprepare_crashdata(drvdata);
+ drvdata->reading = false;
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
+ return 0;
+ }
+
/* Re-enable the TMC if need be */
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/* There is no point in reading a TMC in HW FIFO mode */
diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c
index be1079e8fd64..b12ae7cc3372 100644
--- a/drivers/hwtracing/coresight/coresight-tmc-etr.c
+++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c
@@ -1160,7 +1160,12 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
{
s64 offset;
ssize_t actual = len;
- struct etr_buf *etr_buf = drvdata->sysfs_buf;
+ struct etr_buf *etr_buf;
+
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA)
+ etr_buf = drvdata->sysfs_crash_buf;
+ else
+ etr_buf = drvdata->sysfs_buf;
if (pos + actual > etr_buf->len)
actual = etr_buf->len - pos;
@@ -1878,6 +1883,128 @@ static int tmc_panic_sync_etr(struct coresight_device *csdev)
return 0;
}
+static int tmc_etr_setup_crashdata_buf(struct tmc_drvdata *drvdata)
+{
+ int rc = 0;
+ struct etr_buf *etr_buf;
+ struct etr_flat_buf *resrv_buf;
+ struct tmc_crash_metadata *mdata;
+ struct device *dev = &drvdata->csdev->dev;
+
+ mdata = drvdata->crash_mdata.vaddr;
+
+ etr_buf = kzalloc(sizeof(*etr_buf), GFP_ATOMIC);
+ if (!etr_buf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ etr_buf->size = drvdata->crash_tbuf.size;
+
+ resrv_buf = kzalloc(sizeof(*resrv_buf), GFP_ATOMIC);
+ if (!resrv_buf) {
+ rc = -ENOMEM;
+ goto rmem_err;
+ }
+
+ /*
+ * Buffer address given by metadata for retrieval of trace data
+ * from previous boot is expected to be same as the reserved
+ * trace buffer memory region provided through DTS
+ */
+ if (is_tmc_reserved_region_valid(dev->parent)
+ && (drvdata->crash_tbuf.paddr == mdata->trc_paddr))
+ resrv_buf->vaddr = drvdata->crash_tbuf.vaddr;
+ else {
+ dev_dbg(dev, "Trace buffer address of previous boot invalid\n");
+ rc = -EINVAL;
+ goto map_err;
+ }
+
+ resrv_buf->size = etr_buf->size;
+ resrv_buf->dev = &drvdata->csdev->dev;
+ etr_buf->mode = ETR_MODE_RESRV;
+ etr_buf->private = resrv_buf;
+ etr_buf->ops = etr_buf_ops[ETR_MODE_RESRV];
+
+ drvdata->sysfs_crash_buf = etr_buf;
+
+ return 0;
+
+map_err:
+ kfree(resrv_buf);
+
+rmem_err:
+ kfree(etr_buf);
+
+out:
+ return rc;
+}
+
+static int tmc_etr_sync_crashdata_buf(struct tmc_drvdata *drvdata)
+{
+ u32 status;
+ u64 rrp, rwp, dba;
+ struct tmc_crash_metadata *mdata;
+ struct etr_buf *etr_buf = drvdata->sysfs_crash_buf;
+
+ mdata = drvdata->crash_mdata.vaddr;
+
+ rrp = mdata->rrp;
+ rwp = mdata->rwp;
+ dba = mdata->dba;
+ status = mdata->sts;
+
+ etr_buf->full = !!(status & TMC_STS_FULL);
+
+ /* Sync the buffer pointers */
+ etr_buf->offset = rrp - dba;
+ if (etr_buf->full)
+ etr_buf->len = etr_buf->size;
+ else
+ etr_buf->len = rwp - rrp;
+
+ /* Additional sanity checks for validating metadata */
+ if ((etr_buf->offset > etr_buf->size) ||
+ (etr_buf->len > etr_buf->size)) {
+ dev_dbg(&drvdata->csdev->dev,
+ "Offset and length invalid in tmc crash metadata\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void tmc_etr_free_crashdata_buf(struct tmc_drvdata *drvdata)
+{
+ struct etr_buf *etr_buf = drvdata->sysfs_crash_buf;
+
+ if (!etr_buf)
+ return;
+ drvdata->sysfs_crash_buf = NULL;
+}
+
+static int tmc_etr_prepare_crashdata(struct coresight_device *csdev)
+{
+ int ret = 0;
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ ret = tmc_etr_setup_crashdata_buf(drvdata);
+ if (ret)
+ goto out;
+ ret = tmc_etr_sync_crashdata_buf(drvdata);
+
+out:
+ return ret;
+}
+
+static int tmc_etr_unprepare_crashdata(struct coresight_device *csdev)
+{
+ struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+ tmc_etr_free_crashdata_buf(drvdata);
+ return 0;
+}
+
static const struct coresight_ops_sink tmc_etr_sink_ops = {
.enable = tmc_enable_etr_sink,
.disable = tmc_disable_etr_sink,
@@ -1890,9 +2017,15 @@ static const struct coresight_ops_panic tmc_etr_sync_ops = {
.sync = tmc_panic_sync_etr,
};
+static const struct coresight_ops_crashdata tmc_etr_crashdata_ops = {
+ .prepare = tmc_etr_prepare_crashdata,
+ .unprepare = tmc_etr_unprepare_crashdata,
+};
+
const struct coresight_ops tmc_etr_cs_ops = {
.sink_ops = &tmc_etr_sink_ops,
.panic_ops = &tmc_etr_sync_ops,
+ .crashdata_ops = &tmc_etr_crashdata_ops,
};
int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
@@ -1910,6 +2043,14 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
goto out;
}
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA) {
+ ret = tmc_read_prepare_crashdata(drvdata);
+ if (ret)
+ goto out;
+ else
+ goto mode_valid;
+ }
+
/*
* We can safely allow reads even if the ETR is operating in PERF mode,
* since the sysfs session is captured in mode specific data.
@@ -1924,6 +2065,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS)
__tmc_etr_disable_hw(drvdata);
+mode_valid:
drvdata->reading = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
@@ -1942,6 +2084,12 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
spin_lock_irqsave(&drvdata->spinlock, flags);
+ if (coresight_get_mode(drvdata->csdev) == CS_MODE_READ_CRASHDATA) {
+ sysfs_buf = drvdata->sysfs_crash_buf;
+ tmc_read_unprepare_crashdata(drvdata);
+ goto out;
+ }
+
/* RE-enable the TMC if need be */
if (coresight_get_mode(drvdata->csdev) == CS_MODE_SYSFS) {
/*
@@ -1959,6 +2107,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
drvdata->sysfs_buf = NULL;
}
+out:
drvdata->reading = false;
spin_unlock_irqrestore(&drvdata->spinlock, flags);
diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h
index 35beee53584a..6102eea3fc79 100644
--- a/drivers/hwtracing/coresight/coresight-tmc.h
+++ b/drivers/hwtracing/coresight/coresight-tmc.h
@@ -201,6 +201,8 @@ struct tmc_resrv_buf {
* @base: memory mapped base address for this component.
* @csdev: component vitals needed by the framework.
* @miscdev: specifics to handle "/dev/xyz.tmc" entry.
+ * @crashdev: specifics to handle "/dev/crash_tmc_xyz" entry for reading
+ * crash tracedata.
* @spinlock: only one at a time pls.
* @pid: Process ID of the process being monitored by the session
* that is using this component.
@@ -219,7 +221,10 @@ struct tmc_resrv_buf {
* @idr_mutex: Access serialisation for idr.
* @sysfs_buf: SYSFS buffer for ETR.
* @perf_buf: PERF buffer for ETR.
- * @crash_tbuf: Used by ETR as hardware trace buffer and for trace data
+ * @sysfs_crash_buf: Sysfs crashdata buffer for ETR. This is a special purpose
+ * buffer that is used only for mapping the trace buffer from
+ * previous crash and not for capturing trace.
+ * @crash_tbuf: Used by ETR as hardware trace buffer and for trace data
* retention (after crash) only when ETR_MODE_RESRV buffer
* mode is enabled. Used by ETF for trace data retention
* (after crash) by default.
@@ -231,6 +236,7 @@ struct tmc_drvdata {
void __iomem *base;
struct coresight_device *csdev;
struct miscdevice miscdev;
+ struct miscdevice crashdev;
spinlock_t spinlock;
pid_t pid;
bool reading;
@@ -250,6 +256,7 @@ struct tmc_drvdata {
struct mutex idr_mutex;
struct etr_buf *sysfs_buf;
struct etr_buf *perf_buf;
+ struct etr_buf *sysfs_crash_buf;
struct tmc_resrv_buf crash_tbuf;
struct tmc_resrv_buf crash_mdata;
};
@@ -301,6 +308,8 @@ void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
void tmc_enable_hw(struct tmc_drvdata *drvdata);
void tmc_disable_hw(struct tmc_drvdata *drvdata);
u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata);
+int tmc_read_prepare_crashdata(struct tmc_drvdata *drvdata);
+int tmc_read_unprepare_crashdata(struct tmc_drvdata *drvdata);
/* ETB/ETF functions */
int tmc_read_prepare_etb(struct tmc_drvdata *drvdata);
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 6aa54cdb66a2..a7c9fe27ef40 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -309,6 +309,7 @@ enum cs_mode {
CS_MODE_DISABLED,
CS_MODE_SYSFS,
CS_MODE_PERF,
+ CS_MODE_READ_CRASHDATA, /* Trace data from previous crash */
};
#define source_ops(csdev) csdev->ops->source_ops
@@ -317,6 +318,7 @@ enum cs_mode {
#define helper_ops(csdev) csdev->ops->helper_ops
#define ect_ops(csdev) csdev->ops->ect_ops
#define panic_ops(csdev) csdev->ops->panic_ops
+#define crashdata_ops(csdev) csdev->ops->crashdata_ops
/**
* struct coresight_ops_sink - basic operations for a sink
@@ -396,12 +398,23 @@ struct coresight_ops_panic {
int (*sync)(struct coresight_device *csdev);
};
+/**
+ * struct coresight_ops_crashdata - Generic device ops for crashdata mode
+ *
+ * @prepare : Preparation for reading crashdata mode
+ */
+struct coresight_ops_crashdata {
+ int (*prepare)(struct coresight_device *csdev);
+ int (*unprepare)(struct coresight_device *csdev);
+};
+
struct coresight_ops {
const struct coresight_ops_sink *sink_ops;
const struct coresight_ops_link *link_ops;
const struct coresight_ops_source *source_ops;
const struct coresight_ops_helper *helper_ops;
const struct coresight_ops_panic *panic_ops;
+ const struct coresight_ops_crashdata *crashdata_ops;
};
static inline u32 csdev_access_relaxed_read32(struct csdev_access *csa,