// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
* Description: CoreSight Trace Memory Controller driver
*/
#include <linux/acpi.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/idr.h>
#include <linux/io.h>
#include <linux/iommu.h>
#include <linux/err.h>
#include <linux/fs.h>
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/property.h>
#include <linux/uaccess.h>
#include <linux/slab.h>
#include <linux/dma-mapping.h>
#include <linux/spinlock.h>
#include <linux/pm_runtime.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_reserved_mem.h>
#include <linux/coresight.h>
#include <linux/amba/bus.h>
#include <linux/platform_device.h>
#include "coresight-priv.h"
#include "coresight-tmc.h"
int tmc_wait_for_tmcready(struct tmc_drvdata *drvdata)
{
struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa = &csdev->access;
/* Ensure formatter, unformatter and hardware fifo are empty */
if (coresight_timeout(csa, TMC_STS, TMC_STS_TMCREADY_BIT, 1)) {
dev_err(&csdev->dev,
"timeout while waiting for TMC to be Ready\n");
return -EBUSY;
}
return 0;
}
void tmc_flush_and_stop(struct tmc_drvdata *drvdata)
{
struct coresight_device *csdev = drvdata->csdev;
struct csdev_access *csa = &csdev->access;
u32 ffcr;
ffcr = readl_relaxed(drvdata->base + TMC_FFCR);
ffcr |= TMC_FFCR_STOP_ON_FLUSH;
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
ffcr |= BIT(TMC_FFCR_FLUSHMAN_BIT);
writel_relaxed(ffcr, drvdata->base + TMC_FFCR);
/* Ensure flush completes */
if (coresight_timeout(csa, TMC_FFCR, TMC_FFCR_FLUSHMAN_BIT, 0)) {
dev_err(&csdev->dev,
"timeout while waiting for completion of Manual Flush\n");
}
tmc_wait_for_tmcready(drvdata);
}
void tmc_enable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(TMC_CTL_CAPT_EN, drvdata->base + TMC_CTL);
}
void tmc_disable_hw(struct tmc_drvdata *drvdata)
{
writel_relaxed(0x0, drvdata->base + TMC_CTL);
}
u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata)
{
u32 mask = 0;
/*
* When moving RRP or an offset address forward, the new values must
* be byte-address aligned to the width of the trace memory databus
* _and_ to a frame boundary (16 byte), whichever is the biggest. For
* example, for 32-bit, 64-bit and 128-bit wide trace memory, the four
* LSBs must be 0s. For 256-bit wide trace memory, the five LSBs must
* be 0s.
*/
switch (drvdata->memwidth) {
case TMC_MEM_INTF_WIDTH_32BITS:
case TMC_MEM_INTF_WIDTH_64BITS:
case TMC_MEM_INTF_WIDTH_128BITS:
mask = GENMASK(31, 4);
break;
case TMC_MEM_INTF_WIDTH_256BITS:
mask = GENMASK(31, 5);
break;
}
return mask;
}
static bool is_tmc_crashdata_valid(struct tmc_drvdata *drvdata)
{
struct tmc_crash_metadata *mdata;
if (!tmc_has_reserved_buffer(drvdata) ||
!tmc_has_crash_mdata_buffer(drvdata))
return false;
mdata = drvdata->crash_mdata.vaddr;
/* Check version match */
if (mdata->version != CS_CRASHDATA_VERSION)
return false;
/* Check for valid metadata */
if (!mdata->valid) {
dev_dbg(&drvdata->csdev->dev,
"Data invalid in tmc crash metadata\n");
return false;
}
/*
* 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 (drvdata->resrv_buf.paddr != mdata->trace_paddr) {
dev_dbg(&drvdata->csdev->dev,
"Trace buffer address of previous boot invalid\n");
return false;
}
/* Check data integrity of metadata */
if (mdata->crc32_mdata != find_crash_metadata_crc(mdata)) {
dev_err(&drvdata->csdev->dev,
"CRC mismatch in tmc crash metadata\n");
return false;
}
/* Check data integrity of tracedata */
if (mdata