Merge "coresight: Add dual etr sink support"

This commit is contained in:
qctecmdr 2022-08-30 17:13:37 -07:00 committed by Gerrit - the friendly Code Review server
commit 6056678a5e
10 changed files with 1124 additions and 6 deletions

View File

@ -35,6 +35,7 @@ config CORESIGHT_LINK_AND_SINK_TMC
tristate "Coresight generic TMC driver"
depends on CORESIGHT_LINKS_AND_SINKS
select CORESIGHT_CSR
help
This enables support for the Trace Memory Controller driver.
Depending on its configuration the device can act as a link (embedded
@ -226,6 +227,14 @@ config CORESIGHT_HWEVENT
Hardware Event mux control registers to select hardware events
based on user input.
config CORESIGHT_CSR
tristate "CoreSight Slave Register driver"
help
This driver provides support for CoreSight Slave Register block
that hosts miscellaneous configuration registers.
Those configuration registers can be used to control, various
coresight configurations.
config CORESIGHT_TRBE
tristate "Trace Buffer Extension (TRBE) driver"
depends on ARM64 && CORESIGHT_SOURCE_ETM4X

View File

@ -31,3 +31,4 @@ obj-$(CONFIG_CORESIGHT_DUMMY) += coresight-dummy.o
obj-$(CONFIG_CORESIGHT_REMOTE_ETM) += coresight-remote-etm.o
obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o
obj-$(CONFIG_CORESIGHT_HWEVENT) += coresight-hwevent.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o

View File

@ -0,0 +1,64 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
*/
#ifndef _CORESIGHT_COMMON_H
#define _CORESIGHT_COMMON_H
#define BM(lsb, msb) ((BIT(msb) - BIT(lsb)) + BIT(msb))
#define BVAL(val, n) ((val & BIT(n)) >> n)
struct coresight_csr {
const char *name;
struct list_head link;
};
#if IS_ENABLED(CONFIG_CORESIGHT_CSR)
extern void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr);
extern void msm_qdss_csr_enable_flush(struct coresight_csr *csr);
extern void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr);
extern void msm_qdss_csr_disable_flush(struct coresight_csr *csr);
extern void msm_qdss_csr_enable_eth(struct coresight_csr *csr);
extern void msm_qdss_csr_disable_eth(struct coresight_csr *csr);
extern int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr,
uint32_t val);
extern void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset,
uint32_t count);
extern struct coresight_csr *coresight_csr_get(const char *name);
extern int coresight_csr_set_etr_atid(struct coresight_csr *csr,
uint32_t atid_offset, uint32_t atid, bool enable);
#if IS_ENABLED(CONFIG_OF)
extern int of_get_coresight_csr_name(struct device_node *node,
const char **csr_name);
#else
static inline int of_get_coresight_csr_name(struct device_node *node,
const char **csr_name){ return -EINVAL; }
#endif
#else
static inline void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr) {}
static inline void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr) {}
static inline void msm_qdss_csr_disable_flush(struct coresight_csr *csr) {}
static inline void msm_qdss_csr_enable_eth(struct coresight_csr *csr) {}
static inline void msm_qdss_csr_disable_eth(struct coresight_csr *csr) {}
static inline int coresight_csr_hwctrl_set(struct coresight_csr *csr,
uint64_t addr, uint32_t val) { return -EINVAL; }
static inline void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset,
uint32_t count) {}
static inline struct coresight_csr *coresight_csr_get(const char *name)
{ return NULL; }
static inline int coresight_csr_set_etr_atid(struct coresight_csr *csr,
uint32_t atid_offset, uint32_t atid, bool enable)
{return -EINVAL; }
static inline int of_get_coresight_csr_name(struct device_node *node,
const char **csr_name){ return -EINVAL; }
#endif
#if IS_ENABLED(CONFIG_CORESIGHT_CTI) && IS_ENABLED(CONFIG_OF)
extern struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node);
#else
static inline struct coresight_cti_data *of_get_coresight_cti_data(
struct device *dev, struct device_node *node) { return NULL; }
#endif
#endif

View File

@ -22,8 +22,11 @@
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
#include "coresight-common.h"
#include "coresight-syscfg.h"
#define MAX_SINK_NAME 20
static DEFINE_MUTEX(coresight_mutex);
static DEFINE_PER_CPU(struct coresight_device *, csdev_sink);
@ -59,6 +62,8 @@ EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
static const struct cti_assoc_op *cti_assoc_ops;
static const struct csr_set_atid_op *csr_set_atid_ops;
void coresight_set_cti_ops(const struct cti_assoc_op *cti_op)
{
cti_assoc_ops = cti_op;
@ -83,6 +88,18 @@ struct coresight_device *coresight_get_percpu_sink(int cpu)
}
EXPORT_SYMBOL_GPL(coresight_get_percpu_sink);
void coresight_set_csr_ops(const struct csr_set_atid_op *csr_op)
{
csr_set_atid_ops = csr_op;
}
EXPORT_SYMBOL(coresight_set_csr_ops);
void coresight_remove_csr_ops(void)
{
csr_set_atid_ops = NULL;
}
EXPORT_SYMBOL(coresight_remove_csr_ops);
static int coresight_id_match(struct device *dev, void *data)
{
int trace_id, i_trace_id;
@ -492,6 +509,42 @@ static bool coresight_disable_source(struct coresight_device *csdev)
return !csdev->enable;
}
static struct coresight_device *coresight_get_source(struct list_head *path)
{
struct coresight_device *csdev;
if (!path)
return NULL;
csdev = list_first_entry(path, struct coresight_node, link)->csdev;
if (csdev->type != CORESIGHT_DEV_TYPE_SOURCE)
return NULL;
return csdev;
}
static int coresight_set_csr_atid(struct list_head *path,
struct coresight_device *sink_csdev, bool enable)
{
int atid, ret = 0;
struct coresight_device *src_csdev;
src_csdev = coresight_get_source(path);
if (!src_csdev)
ret = -EINVAL;
atid = of_coresight_get_atid(src_csdev);
if (atid < 0)
ret = -EINVAL;
if (csr_set_atid_ops)
ret = csr_set_atid_ops->set_atid(sink_csdev, atid, enable);
else
ret = -EINVAL;
return ret;
}
/*
* coresight_disable_path_from : Disable components in the given path beyond
* @nd in the list. If @nd is NULL, all the components, except the SOURCE are
@ -523,6 +576,9 @@ static void coresight_disable_path_from(struct list_head *path,
switch (type) {
case CORESIGHT_DEV_TYPE_SINK:
if (csdev->type == CORESIGHT_DEV_TYPE_SINK)
coresight_set_csr_atid(path, csdev, false);
coresight_disable_sink(csdev);
break;
case CORESIGHT_DEV_TYPE_SOURCE:
@ -584,6 +640,13 @@ int coresight_enable_path(struct list_head *path, u32 mode, void *sink_data)
*/
if (ret)
goto out;
if (csdev->type == CORESIGHT_DEV_TYPE_SINK) {
ret = coresight_set_csr_atid(path, csdev, true);
if (ret)
dev_dbg(&csdev->dev, "Set csr atid register fail%s\n");
}
break;
case CORESIGHT_DEV_TYPE_SOURCE:
/* sources are enabled from either sysFS or Perf */
@ -678,6 +741,52 @@ coresight_get_enabled_sink(struct coresight_device *source)
return coresight_find_enabled_sink(source);
}
static int coresight_enabled_sink(struct device *dev, const void *data)
{
const bool *reset = data;
struct coresight_device *csdev = to_coresight_device(dev);
if ((csdev->type == CORESIGHT_DEV_TYPE_SINK ||
csdev->type == CORESIGHT_DEV_TYPE_LINKSINK) &&
csdev->activated) {
/*
* Now that we have a handle on the sink for this session,
* disable the sysFS "enable_sink" flag so that possible
* concurrent perf session that wish to use another sink don't
* trip on it. Doing so has no ramification for the current
* session.
*/
if (*reset)
csdev->activated = false;
return 1;
}
return 0;
}
/**
* coresight_get_enabled_sink_from_bus - returns the first enabled sink found on the bus
* @deactivate: Whether the 'enable_sink' flag should be reset
*
* When operated from perf the deactivate parameter should be set to 'true'.
* That way the "enabled_sink" flag of the sink that was selected can be reset,
* allowing for other concurrent perf sessions to choose a different sink.
*
* When operated from sysFS users have full control and as such the deactivate
* parameter should be set to 'false', hence mandating users to explicitly
* clear the flag.
*/
static struct coresight_device *coresight_get_enabled_sink_from_bus(bool deactivate)
{
struct device *dev = NULL;
dev = bus_find_device(&coresight_bustype, NULL, &deactivate,
coresight_enabled_sink);
return dev ? to_coresight_device(dev) : NULL;
}
static int coresight_sink_by_id(struct device *dev, const void *data)
{
struct coresight_device *csdev = to_coresight_device(dev);
@ -1157,7 +1266,12 @@ int coresight_enable(struct coresight_device *csdev)
goto out;
}
sink = coresight_get_enabled_sink(csdev);
if (csdev->def_sink) {
sink = csdev->def_sink;
sink->activated = true;
} else
sink = coresight_get_enabled_sink(csdev);
if (!sink) {
ret = -EINVAL;
goto out;
@ -1250,15 +1364,22 @@ static ssize_t enable_sink_store(struct device *dev,
{
int ret;
unsigned long val;
struct coresight_device *sink;
struct coresight_device *csdev = to_coresight_device(dev);
ret = kstrtoul(buf, 10, &val);
if (ret)
return ret;
if (val)
if (val) {
sink = coresight_get_enabled_sink_from_bus(false);
if (sink && sink->type != csdev->type) {
dev_err(&csdev->dev,
"Another type sink is enabled.\n");
return -EINVAL;
}
csdev->activated = true;
else
} else
csdev->activated = false;
return size;
@ -1298,6 +1419,57 @@ static ssize_t enable_source_store(struct device *dev,
}
static DEVICE_ATTR_RW(enable_source);
static ssize_t sink_name_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct coresight_device *csdev = to_coresight_device(dev);
if (csdev->def_sink)
return scnprintf(buf, PAGE_SIZE, "%s\n", dev_name(&csdev->def_sink->dev));
else
return scnprintf(buf, PAGE_SIZE, "\n");
}
static ssize_t sink_name_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
u32 hash;
char *sink_name;
struct coresight_device *new_sink, *current_sink;
struct coresight_device *csdev = to_coresight_device(dev);
if (size >= MAX_SINK_NAME)
return -EINVAL;
if (size == 0) {
csdev->def_sink = NULL;
return size;
}
sink_name = kstrdup(buf, GFP_KERNEL);
sink_name[size-1] = 0;
hash = hashlen_hash(hashlen_string(NULL, sink_name));
new_sink = coresight_get_sink_by_id(hash);
current_sink = coresight_get_enabled_sink_from_bus(false);
if (!new_sink || (current_sink &&
new_sink && current_sink->type !=
new_sink->type)) {
dev_err(&csdev->dev,
"Sink name is invalid or another type sink is enabled.\n");
kfree(sink_name);
return -EINVAL;
}
csdev->def_sink = new_sink;
kfree(sink_name);
return size;
}
static DEVICE_ATTR_RW(sink_name);
static struct attribute *coresight_sink_attrs[] = {
&dev_attr_enable_sink.attr,
NULL,
@ -1306,6 +1478,7 @@ ATTRIBUTE_GROUPS(coresight_sink);
static struct attribute *coresight_source_attrs[] = {
&dev_attr_enable_source.attr,
&dev_attr_sink_name.attr,
NULL,
};
ATTRIBUTE_GROUPS(coresight_source);

View File

@ -0,0 +1,817 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2013, 2015-2017, 2019-2021 The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#include <linux/err.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/coresight.h>
#include <linux/clk.h>
#include <linux/mutex.h>
#include "coresight-priv.h"
#include "coresight-common.h"
#define csr_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define csr_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define CSR_LOCK(drvdata) \
do { \
mb(); /* ensure configuration take effect before we lock it */ \
csr_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define CSR_UNLOCK(drvdata) \
do { \
csr_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); /* ensure unlock take effect before we configure */ \
} while (0)
#define CSR_SWDBGPWRCTRL (0x000)
#define CSR_SWDBGPWRACK (0x004)
#define CSR_SWSPADREG0 (0x008)
#define CSR_SWSPADREG1 (0x00C)
#define CSR_STMTRANSCTRL (0x010)
#define CSR_STMAWIDCTRL (0x014)
#define CSR_STMCHNOFST0 (0x018)
#define CSR_STMCHNOFST1 (0x01C)
#define CSR_STMEXTHWCTRL0 (0x020)
#define CSR_STMEXTHWCTRL1 (0x024)
#define CSR_STMEXTHWCTRL2 (0x028)
#define CSR_STMEXTHWCTRL3 (0x02C)
#define CSR_USBBAMCTRL (0x030)
#define CSR_USBFLSHCTRL (0x034)
#define CSR_TIMESTAMPCTRL (0x038)
#define CSR_AOTIMEVAL0 (0x03C)
#define CSR_AOTIMEVAL1 (0x040)
#define CSR_QDSSTIMEVAL0 (0x044)
#define CSR_QDSSTIMEVAL1 (0x048)
#define CSR_QDSSTIMELOAD0 (0x04C)
#define CSR_QDSSTIMELOAD1 (0x050)
#define CSR_DAPMSAVAL (0x054)
#define CSR_QDSSCLKVOTE (0x058)
#define CSR_QDSSCLKIPI (0x05C)
#define CSR_QDSSPWRREQIGNORE (0x060)
#define CSR_QDSSSPARE (0x064)
#define CSR_IPCAT (0x068)
#define CSR_BYTECNTVAL (0x06C)
#define CSR_ARADDR_EXT (0x130)
#define CSR_AWADDR_EXT (0x134)
#define MSR_NUM ((drvdata->msr_end - drvdata->msr_start + 1) \
/ sizeof(uint32_t))
#define MSR_MAX_NUM 128
#define BLKSIZE_256 0
#define BLKSIZE_512 1
#define BLKSIZE_1024 2
#define BLKSIZE_2048 3
#define FLUSHPERIOD_1 0x1
#define FLUSHPERIOD_2048 0x800
#define PERFLSHEOT_BIT BIT(18)
#define CSR_ATID_REG_OFFSET(atid, atid_offset) \
((atid / 32) * 4 + atid_offset)
#define CSR_ATID_REG_BIT(atid) (atid % 32)
#define CSR_MAX_ATID 128
#define CSR_ATID_REG_SIZE 0xc
#define CSR_ARADDR_EXT_VAL 0x104
#define CSR_AWADDR_EXT_VAL 0x104
#define CSR_ATID_REG_OFFSET(atid, atid_offset) \
((atid / 32) * 4 + atid_offset)
#define CSR_ATID_REG_BIT(atid) (atid % 32)
#define CSR_MAX_ATID 128
#define CSR_ATID_REG_SIZE 0xc
struct csr_drvdata {
void __iomem *base;
phys_addr_t pbase;
phys_addr_t msr_start;
phys_addr_t msr_end;
struct device *dev;
struct coresight_device *csdev;
uint32_t *msr;
atomic_t *msr_refcnt;
uint32_t blksize;
uint32_t flushperiod;
struct coresight_csr csr;
struct clk *clk;
spinlock_t spin_lock;
bool usb_bam_support;
bool perflsheot_set_support;
bool hwctrl_set_support;
bool set_byte_cntr_support;
bool timestamp_support;
bool enable_flush;
bool msr_support;
};
DEFINE_CORESIGHT_DEVLIST(csr_devs, "csr");
static LIST_HEAD(csr_list);
static DEFINE_MUTEX(csr_lock);
#define to_csr_drvdata(c) container_of(c, struct csr_drvdata, csr)
static void msm_qdss_csr_config_flush_period(struct csr_drvdata *drvdata)
{
uint32_t usbflshctrl;
CSR_UNLOCK(drvdata);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl = (usbflshctrl & ~0x3FFFC) | (drvdata->flushperiod << 2);
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
}
void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
uint32_t usbbamctrl;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
usbbamctrl = (usbbamctrl & ~0x3) | drvdata->blksize;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
usbbamctrl |= 0x4;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_enable_bam_to_usb);
void msm_qdss_csr_enable_flush(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
uint32_t usbflshctrl;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
msm_qdss_csr_config_flush_period(drvdata);
CSR_UNLOCK(drvdata);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl |= 0x2;
if (drvdata->perflsheot_set_support)
usbflshctrl |= PERFLSHEOT_BIT;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
drvdata->enable_flush = true;
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_enable_flush);
void msm_qdss_csr_disable_bam_to_usb(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
uint32_t usbbamctrl;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
usbbamctrl &= (~0x4);
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_bam_to_usb);
void msm_qdss_csr_disable_flush(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
uint32_t usbflshctrl;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support)
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl &= ~0x2;
if (drvdata->perflsheot_set_support)
usbflshctrl &= ~PERFLSHEOT_BIT;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
drvdata->enable_flush = false;
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
void msm_qdss_csr_enable_eth(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata))
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
csr_writel(drvdata, CSR_ARADDR_EXT_VAL, CSR_ARADDR_EXT);
csr_writel(drvdata, CSR_AWADDR_EXT_VAL, CSR_AWADDR_EXT);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_enable_eth);
void msm_qdss_csr_disable_eth(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata))
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
csr_writel(drvdata, 0, CSR_ARADDR_EXT);
csr_writel(drvdata, 0, CSR_AWADDR_EXT);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_eth);
int coresight_csr_hwctrl_set(struct coresight_csr *csr, uint64_t addr,
uint32_t val)
{
struct csr_drvdata *drvdata;
int ret = 0;
unsigned long flags;
if (csr == NULL)
return -EINVAL;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->hwctrl_set_support)
return -EINVAL;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL0))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL0);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL1))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL1);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL2))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL2);
else if (addr == (drvdata->pbase + CSR_STMEXTHWCTRL3))
csr_writel(drvdata, val, CSR_STMEXTHWCTRL3);
else
ret = -EINVAL;
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return ret;
}
EXPORT_SYMBOL(coresight_csr_hwctrl_set);
void coresight_csr_set_byte_cntr(struct coresight_csr *csr, int irqctrl_offset, uint32_t count)
{
struct csr_drvdata *drvdata;
unsigned long flags;
if (csr == NULL)
return;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->set_byte_cntr_support)
return;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
csr_writel(drvdata, count, irqctrl_offset);
/* make sure byte count value is written */
mb();
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(coresight_csr_set_byte_cntr);
int coresight_csr_set_etr_atid(struct coresight_csr *csr,
uint32_t atid_offset, uint32_t atid,
bool enable)
{
struct csr_drvdata *drvdata;
unsigned long flags;
uint32_t reg_offset;
int bit;
uint32_t val;
if (csr == NULL)
return -EINVAL;
drvdata = to_csr_drvdata(csr);
if (IS_ERR_OR_NULL(drvdata))
return -EINVAL;
if (atid < 0 || atid_offset <= 0)
return -EINVAL;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
reg_offset = CSR_ATID_REG_OFFSET(atid, atid_offset);
bit = CSR_ATID_REG_BIT(atid);
if (reg_offset - atid_offset > CSR_ATID_REG_SIZE
|| bit >= CSR_MAX_ATID) {
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return -EINVAL;
}
val = csr_readl(drvdata, reg_offset);
if (enable)
val = val | BIT(bit);
else
val = val & ~BIT(bit);
csr_writel(drvdata, val, reg_offset);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return 0;
}
EXPORT_SYMBOL(coresight_csr_set_etr_atid);
struct coresight_csr *coresight_csr_get(const char *name)
{
struct coresight_csr *csr;
mutex_lock(&csr_lock);
list_for_each_entry(csr, &csr_list, link) {
if (!strcmp(csr->name, name)) {
mutex_unlock(&csr_lock);
return csr;
}
}
mutex_unlock(&csr_lock);
return ERR_PTR(-EINVAL);
}
EXPORT_SYMBOL(coresight_csr_get);
int of_get_coresight_csr_name(struct device_node *node, const char **csr_name)
{
int ret;
struct device_node *csr_node;
csr_node = of_parse_phandle(node, "coresight-csr", 0);
if (!csr_node)
return -EINVAL;
ret = of_property_read_string(csr_node, "coresight-name", csr_name);
of_node_put(csr_node);
return ret;
}
EXPORT_SYMBOL(of_get_coresight_csr_name);
static ssize_t timestamp_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
ssize_t size = 0;
uint64_t time_tick = 0;
uint32_t val, time_val0, time_val1;
int ret;
unsigned long flags;
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->timestamp_support) {
dev_err(dev, "Invalid param\n");
return 0;
}
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
val = csr_readl(drvdata, CSR_TIMESTAMPCTRL);
val = val & ~BIT(0);
csr_writel(drvdata, val, CSR_TIMESTAMPCTRL);
val = val | BIT(0);
csr_writel(drvdata, val, CSR_TIMESTAMPCTRL);
time_val0 = csr_readl(drvdata, CSR_QDSSTIMEVAL0);
time_val1 = csr_readl(drvdata, CSR_QDSSTIMEVAL1);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
clk_disable_unprepare(drvdata->clk);
time_tick |= (uint64_t)time_val1 << 32;
time_tick |= (uint64_t)time_val0;
size = scnprintf(buf, PAGE_SIZE, "%llu\n", time_tick);
dev_dbg(dev, "timestamp : %s\n", buf);
return size;
}
static DEVICE_ATTR_RO(timestamp);
static ssize_t msr_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int i;
ssize_t len = 0;
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support ||
IS_ERR_OR_NULL(drvdata->msr))
return -EINVAL;
for (i = 0; i < MSR_NUM; i++)
len += scnprintf(buf + len, PAGE_SIZE - len, "%d 0x%x\n",
i, drvdata->msr[i]);
return len;
}
static ssize_t msr_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
uint32_t offset, val, rval;
int nval, ret;
unsigned long flags;
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support ||
IS_ERR_OR_NULL(drvdata->msr))
return -EINVAL;
nval = sscanf(buf, "%x %x", &offset, &val);
if (nval != 2)
return -EINVAL;
if (offset >= MSR_NUM)
return -EINVAL;
if (atomic_read(drvdata->msr_refcnt) == 0) {
ret = clk_prepare_enable(drvdata->clk);
if (ret)
return ret;
atomic_inc(drvdata->msr_refcnt);
}
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
csr_writel(drvdata, val, drvdata->msr_start + offset * 4);
rval = csr_readl(drvdata, drvdata->msr_start + offset * 4);
drvdata->msr[offset] = rval;
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return size;
}
static DEVICE_ATTR_RW(msr);
static ssize_t msr_reset_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
unsigned long flags, val;
int i;
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->msr_support ||
IS_ERR_OR_NULL(drvdata->msr))
return -EINVAL;
if (kstrtoul(buf, 0, &val) || val != 1)
return -EINVAL;
if (atomic_read(drvdata->msr_refcnt) == 0)
return -EINVAL;
atomic_set(drvdata->msr_refcnt, 0);
spin_lock_irqsave(&drvdata->spin_lock, flags);
CSR_UNLOCK(drvdata);
for (i = 0; i < MSR_NUM; i++) {
csr_writel(drvdata, 0, drvdata->msr_start + i * 4);
drvdata->msr[i] = 0;
}
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
clk_disable_unprepare(drvdata->clk);
return size;
}
static DEVICE_ATTR_WO(msr_reset);
static ssize_t flushperiod_show(struct device *dev,
struct device_attribute *attr,
char *buf)
{
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) {
dev_err(dev, "Invalid param\n");
return -EINVAL;
}
return scnprintf(buf, PAGE_SIZE, "%#lx\n", drvdata->flushperiod);
}
static ssize_t flushperiod_store(struct device *dev,
struct device_attribute *attr,
const char *buf,
size_t size)
{
unsigned long flags;
unsigned long val;
struct csr_drvdata *drvdata = dev_get_drvdata(dev->parent);
if (IS_ERR_OR_NULL(drvdata) || !drvdata->usb_bam_support) {
dev_err(dev, "Invalid param\n");
return -EINVAL;
}
spin_lock_irqsave(&drvdata->spin_lock, flags);
if (kstrtoul(buf, 0, &val) || val > 0xffff) {
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return -EINVAL;
}
if (drvdata->flushperiod == val)
goto out;
drvdata->flushperiod = val;
if (drvdata->enable_flush)
msm_qdss_csr_config_flush_period(drvdata);
out:
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
return size;
}
static DEVICE_ATTR_RW(flushperiod);
static struct attribute *swao_csr_attrs[] = {
&dev_attr_timestamp.attr,
&dev_attr_msr.attr,
&dev_attr_msr_reset.attr,
NULL,
};
static struct attribute_group swao_csr_attr_grp = {
.attrs = swao_csr_attrs,
};
static const struct attribute_group *swao_csr_attr_grps[] = {
&swao_csr_attr_grp,
NULL,
};
static struct attribute *csr_attrs[] = {
&dev_attr_flushperiod.attr,
NULL,
};
static struct attribute_group csr_attr_grp = {
.attrs = csr_attrs,
};
static const struct attribute_group *csr_attr_grps[] = {
&csr_attr_grp,
NULL,
};
static int csr_probe(struct platform_device *pdev)
{
int ret;
struct device *dev = &pdev->dev;
struct coresight_platform_data *pdata;
struct csr_drvdata *drvdata;
struct resource *res, *msr_res;
struct coresight_desc desc = { 0 };
desc.name = coresight_alloc_device_name(&csr_devs, dev);
if (!desc.name)
return -ENOMEM;
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
pdev->dev.platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &pdev->dev;
platform_set_drvdata(pdev, drvdata);
drvdata->clk = devm_clk_get(dev, "apb_pclk");
if (IS_ERR(drvdata->clk))
dev_dbg(dev, "csr not config clk\n");
res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "csr-base");
if (!res)
return -ENODEV;
drvdata->pbase = res->start;
drvdata->base = devm_ioremap(dev, res->start, resource_size(res));
if (!drvdata->base)
return -ENOMEM;
ret = of_property_read_u32(pdev->dev.of_node, "qcom,blk-size",
&drvdata->blksize);
if (ret)
drvdata->blksize = BLKSIZE_256;
drvdata->usb_bam_support = of_property_read_bool(pdev->dev.of_node,
"qcom,usb-bam-support");
if (!drvdata->usb_bam_support)
dev_dbg(dev, "usb_bam support handled by other subsystem\n");
else
dev_dbg(dev, "usb_bam operation supported\n");
drvdata->hwctrl_set_support = of_property_read_bool(pdev->dev.of_node,
"qcom,hwctrl-set-support");
if (!drvdata->hwctrl_set_support)
dev_dbg(dev, "hwctrl_set_support handled by other subsystem\n");
else
dev_dbg(dev, "hwctrl_set_support operation supported\n");
drvdata->set_byte_cntr_support = of_property_read_bool(
pdev->dev.of_node, "qcom,set-byte-cntr-support");
if (!drvdata->set_byte_cntr_support)
dev_dbg(dev, "set byte_cntr_support handled by other subsystem\n");
else
dev_dbg(dev, "set_byte_cntr_support operation supported\n");
drvdata->timestamp_support = of_property_read_bool(pdev->dev.of_node,
"qcom,timestamp-support");
if (!drvdata->timestamp_support)
dev_dbg(dev, "timestamp_support handled by other subsystem\n");
else
dev_dbg(dev, "timestamp_support operation supported\n");
drvdata->perflsheot_set_support = of_property_read_bool(
pdev->dev.of_node, "qcom,perflsheot-set-support");
if (!drvdata->perflsheot_set_support)
dev_dbg(dev, "perflsheot_set_support handled by other subsystem\n");
else
dev_dbg(dev, "perflsheot_set_support operation supported\n");
if (drvdata->usb_bam_support)
drvdata->flushperiod = FLUSHPERIOD_1;
drvdata->msr_support = of_property_read_bool(pdev->dev.of_node,
"qcom,msr-support");
if (!drvdata->msr_support) {
dev_dbg(dev, "msr_support handled by other subsystem\n");
} else {
msr_res = platform_get_resource_byname(pdev, IORESOURCE_MEM,
"msr-base");
if (!msr_res || msr_res->start < res->start || msr_res->end
> res->end)
return -ENODEV;
drvdata->msr_start = msr_res->start - res->start;
drvdata->msr_end = msr_res->end - res->start;
drvdata->msr = devm_kzalloc(dev, MSR_NUM * sizeof(uint32_t),
GFP_KERNEL);
if (!drvdata->msr)
return -ENOMEM;
drvdata->msr_refcnt = devm_kzalloc(dev, sizeof(atomic_t),
GFP_KERNEL);
if (!drvdata->msr_refcnt)
return -ENOMEM;
atomic_set(drvdata->msr_refcnt, 0);
dev_dbg(dev, "msr_support operation supported\n");
}
desc.type = CORESIGHT_DEV_TYPE_HELPER;
desc.pdata = pdev->dev.platform_data;
desc.dev = &pdev->dev;
if (drvdata->timestamp_support || drvdata->msr_support)
desc.groups = swao_csr_attr_grps;
else if (drvdata->usb_bam_support)
desc.groups = csr_attr_grps;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev))
return PTR_ERR(drvdata->csdev);
/* Store the driver data pointer for use in exported functions */
spin_lock_init(&drvdata->spin_lock);
drvdata->csr.name = desc.name;
mutex_lock(&csr_lock);
list_add_tail(&drvdata->csr.link, &csr_list);
mutex_unlock(&csr_lock);
dev_info(dev, "CSR initialized: %s\n", drvdata->csr.name);
return 0;
}
static int csr_remove(struct platform_device *pdev)
{
struct csr_drvdata *drvdata = platform_get_drvdata(pdev);
mutex_lock(&csr_lock);
list_del(&drvdata->csr.link);
mutex_unlock(&csr_lock);
coresight_unregister(drvdata->csdev);
return 0;
}
static const struct of_device_id csr_match[] = {
{.compatible = "qcom,coresight-csr"},
{}
};
static struct platform_driver csr_driver = {
.probe = csr_probe,
.remove = csr_remove,
.driver = {
.name = "coresight-csr",
.of_match_table = csr_match,
.suppress_bind_attrs = true,
},
};
static int __init csr_init(void)
{
return platform_driver_register(&csr_driver);
}
module_init(csr_init);
static void __exit csr_exit(void)
{
platform_driver_unregister(&csr_driver);
}
module_exit(csr_exit);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("CoreSight CSR driver");

View File

@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
* Copyright (c) 2012, 2021 The Linux Foundation. All rights reserved.
*/
#include <linux/acpi.h>
@ -204,6 +204,16 @@ static int of_coresight_get_cpu(struct device *dev)
return cpu;
}
int of_coresight_get_atid(struct coresight_device *csdev)
{
int atid, ret = 0;
ret = of_property_read_u32(csdev->dev.parent->of_node, "atid", &atid);
if (ret)
return ret;
return atid;
}
/*
* of_coresight_parse_endpoint : Parse the given output endpoint @ep
* and fill the connection information in @conn

View File

@ -1,6 +1,6 @@
/* SPDX-License-Identifier: GPL-2.0 */
/*
* Copyright (c) 2011-2012, The Linux Foundation. All rights reserved.
* Copyright (c) 2011-2012, 2021 The Linux Foundation. All rights reserved.
*/
#ifndef _CORESIGHT_PRIV_H
@ -183,6 +183,14 @@ struct cti_assoc_op {
extern void coresight_set_cti_ops(const struct cti_assoc_op *cti_op);
extern void coresight_remove_cti_ops(void);
struct csr_set_atid_op {
int (*set_atid)(struct coresight_device *csdev, u32 atid, bool enable);
};
extern void coresight_set_csr_ops(const struct csr_set_atid_op *csr_op);
extern void coresight_remove_csr_ops(void);
int of_coresight_get_atid(struct coresight_device *src_dev);
/*
* Macros and inline functions to handle CoreSight UCI data and driver
* private data in AMBA ID table entries, and extract data values.

View File

@ -1,5 +1,5 @@
// SPDX-License-Identifier: GPL-2.0
/* Copyright (c) 2012, The Linux Foundation. All rights reserved.
/* Copyright (c) 2012, 2021 The Linux Foundation. All rights reserved.
*
* Description: CoreSight Trace Memory Controller driver
*/
@ -26,6 +26,7 @@
#include "coresight-priv.h"
#include "coresight-tmc.h"
#include "coresight-common.h"
DEFINE_CORESIGHT_DEVLIST(etb_devs, "tmc_etb");
DEFINE_CORESIGHT_DEVLIST(etf_devs, "tmc_etf");
@ -491,6 +492,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->size = readl_relaxed(drvdata->base + TMC_RSZ) * 4;
}
ret = of_get_coresight_csr_name(adev->dev.of_node, &drvdata->csr_name);
if (ret)
dev_dbg(dev, "No csr data\n");
else {
drvdata->csr = coresight_csr_get(drvdata->csr_name);
if (IS_ERR(drvdata->csr)) {
dev_dbg(dev, "failed to get csr, defer probe\n");
return -EPROBE_DEFER;
}
}
desc.dev = dev;
desc.groups = coresight_tmc_groups;
@ -512,6 +524,10 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = &etr_devs;
if (!of_property_read_u32(dev->of_node, "csr-atid-offset",
&drvdata->atid_offset))
coresight_set_csr_ops(&csr_atid_ops);
break;
case TMC_CONFIG_TYPE_ETF:
desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
@ -589,6 +605,8 @@ static void tmc_remove(struct amba_device *adev)
* etb fops in this case, device is there until last file
* handler to this device is closed.
*/
coresight_remove_csr_ops();
misc_deregister(&drvdata->miscdev);
coresight_unregister(drvdata->csdev);
}

View File

@ -18,6 +18,7 @@
#include "coresight-etm-perf.h"
#include "coresight-priv.h"
#include "coresight-tmc.h"
#include "coresight-common.h"
struct etr_flat_buf {
struct device *dev;
@ -679,6 +680,18 @@ static ssize_t tmc_etr_get_data_flat_buf(struct etr_buf *etr_buf,
return len;
}
static int tmc_etr_set_atid(struct coresight_device *csdev, u32 atid, bool enable)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
return coresight_csr_set_etr_atid(drvdata->csr, drvdata->atid_offset,
atid, enable);
}
const struct csr_set_atid_op csr_atid_ops = {
.set_atid = tmc_etr_set_atid,
};
static const struct etr_buf_operations etr_flat_buf_ops = {
.alloc = tmc_etr_alloc_flat_buf,
.free = tmc_etr_free_flat_buf,

View File

@ -12,6 +12,7 @@
#include <linux/miscdevice.h>
#include <linux/mutex.h>
#include <linux/refcount.h>
#include "coresight-priv.h"
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
@ -211,6 +212,9 @@ struct tmc_drvdata {
struct mutex idr_mutex;
struct etr_buf *sysfs_buf;
struct etr_buf *perf_buf;
struct coresight_csr *csr;
const char *csr_name;
u32 atid_offset;
};
struct etr_buf_operations {
@ -274,6 +278,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata);
int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata);
void tmc_etr_disable_hw(struct tmc_drvdata *drvdata);
extern const struct coresight_ops tmc_etr_cs_ops;
extern const struct csr_set_atid_op csr_atid_ops;
ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
loff_t pos, size_t len, char **bufpp);