Merge "coresight: Add snapshot of Coresight tgu driver"

This commit is contained in:
qctecmdr 2019-09-11 15:37:00 -07:00 • committed by Gerrit - the friendly Code Review server
commit 94756db720
4 changed files with 996 additions and 0 deletions

View file

@ -26,6 +26,7 @@ config CORESIGHT_LINKS_AND_SINKS
config CORESIGHT_LINK_AND_SINK_TMC
bool "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
@ -135,4 +136,24 @@ config CORESIGHT_TPDM_DEFAULT_ENABLE
If unsure, say 'N' here to avoid potential power and performance
penalty.
config CORESIGHT_CSR
bool "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_TGU
bool "CoreSight Trigger Generation Unit driver"
help
This driver provides support for Trigger Generation Unit that is
used to detect patterns or sequences on a given set of signals.
TGU is used to monitor a particular bus within a given region to
detect illegal transaction sequences or slave responses. It is also
used to monitor a data stream to detect protocol violations and to
provide a trigger point for centering data around a specific event
within the trace data buffer.
endif

View file

@ -19,3 +19,5 @@ obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
obj-$(CONFIG_CORESIGHT_TPDA) += coresight-tpda.o
obj-$(CONFIG_CORESIGHT_TPDM) += coresight-tpdm.o
obj-$(CONFIG_CORESIGHT_CSR) += coresight-csr.o
obj-$(CONFIG_CORESIGHT_TGU) += coresight-tgu.o

View file

@ -0,0 +1,444 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2012-2013, 2015-2-17 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"
#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 BLKSIZE_256 0
#define BLKSIZE_512 1
#define BLKSIZE_1024 2
#define BLKSIZE_2048 3
struct csr_drvdata {
void __iomem *base;
phys_addr_t pbase;
struct device *dev;
struct coresight_device *csdev;
uint32_t blksize;
struct coresight_csr csr;
struct clk *clk;
spinlock_t spin_lock;
bool usb_bam_support;
bool hwctrl_set_support;
bool set_byte_cntr_support;
bool timestamp_support;
};
DEFINE_CORESIGHT_DEVLIST(csr_devs, "tpdm");
static LIST_HEAD(csr_list);
static DEFINE_MUTEX(csr_lock);
#define to_csr_drvdata(c) container_of(c, struct csr_drvdata, csr)
void msm_qdss_csr_enable_bam_to_usb(struct coresight_csr *csr)
{
struct csr_drvdata *drvdata;
uint32_t usbbamctrl, 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);
usbbamctrl = csr_readl(drvdata, CSR_USBBAMCTRL);
usbbamctrl = (usbbamctrl & ~0x3) | drvdata->blksize;
csr_writel(drvdata, usbbamctrl, CSR_USBBAMCTRL);
usbflshctrl = csr_readl(drvdata, CSR_USBFLSHCTRL);
usbflshctrl = (usbflshctrl & ~0x3FFFC) | (0xFFFF << 2);
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
usbflshctrl |= 0x2;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
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_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;
csr_writel(drvdata, usbflshctrl, CSR_USBFLSHCTRL);
CSR_LOCK(drvdata);
spin_unlock_irqrestore(&drvdata->spin_lock, flags);
}
EXPORT_SYMBOL(msm_qdss_csr_disable_flush);
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, 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, CSR_BYTECNTVAL);
/* 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);
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);
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 struct attribute *csr_attrs[] = {
&dev_attr_timestamp.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;
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");
desc.type = CORESIGHT_DEV_TYPE_NONE;
desc.pdata = pdev->dev.platform_data;
desc.dev = &pdev->dev;
if (drvdata->timestamp_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 v2");
MODULE_DESCRIPTION("CoreSight CSR driver");

View file

@ -0,0 +1,529 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2017, 2019 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/mutex.h>
#include <linux/amba/bus.h>
#include <linux/topology.h>
#include <linux/of.h>
#include <linux/coresight.h>
#include "coresight-priv.h"
#define tgu_writel(drvdata, val, off) __raw_writel((val), drvdata->base + off)
#define tgu_readl(drvdata, off) __raw_readl(drvdata->base + off)
#define TGU_LOCK(drvdata) \
do { \
mb(); /* ensure configuration take effect before we lock it */ \
tgu_writel(drvdata, 0x0, CORESIGHT_LAR); \
} while (0)
#define TGU_UNLOCK(drvdata) \
do { \
tgu_writel(drvdata, CORESIGHT_UNLOCK, CORESIGHT_LAR); \
mb(); /* ensure unlock take effect before we configure */ \
} while (0)
#define TGU_CONTROL 0x0000
#define TIMER0_STATUS 0x0004
#define COUNTER0_STATUS 0x000C
#define TGU_STATUS 0x0014
#define TIMER0_COMPARE_STEP(n) (0x0040 + 0x1D8 * n)
#define COUNTER0_COMPARE_STEP(n) (0x0048 + 0x1D8 * n)
#define GROUP_REG_STEP(grp, reg, step) (0x0074 + 0x60 * grp + 0x4 * reg + \
0x1D8 * step)
#define CONDITION_DECODE_STEP(m, n) (0x0050 + 0x4 * m + 0x1D8 * n)
#define CONDITION_SELECT_STEP(m, n) (0x0060 + 0x4 * m + 0x1D8 * n)
#define GROUP0 0x0074
#define GROUP1 0x00D4
#define GROUP2 0x0134
#define GROUP3 0x0194
#define TGU_LAR 0x0FB0
#define MAX_GROUP_SETS 256
#define MAX_GROUPS 4
#define MAX_CONDITION_SETS 64
#define MAX_TIMER_COUNTER_SETS 8
#define to_tgu_drvdata(c) container_of(c, struct tgu_drvdata, tgu)
struct Trigger_group_data {
unsigned long grpaddr;
unsigned long value;
};
struct Trigger_condition_data {
unsigned long condaddr;
unsigned long value;
};
struct Trigger_select_data {
unsigned long selectaddr;
unsigned long value;
};
struct Trigger_timer_data {
unsigned long timeraddr;
unsigned long value;
};
struct Trigger_counter_data {
unsigned long counteraddr;
unsigned long value;
};
struct tgu_drvdata {
void __iomem *base;
struct device *dev;
struct coresight_device *csdev;
struct clk *clk;
spinlock_t spinlock;
int max_steps;
int max_conditions;
int max_regs;
int max_timer_counter;
struct Trigger_group_data *grp_data;
struct Trigger_condition_data *condition_data;
struct Trigger_select_data *select_data;
struct Trigger_timer_data *timer_data;
struct Trigger_counter_data *counter_data;
int grp_refcnt;
int cond_refcnt;
int select_refcnt;
int timer_refcnt;
int counter_refcnt;
bool enable;
};
DEFINE_CORESIGHT_DEVLIST(tgu_devs, "tgu");
static ssize_t enable_tgu_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long value;
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
int ret, i, j;
if (kstrtoul(buf, 16, &value))
return -EINVAL;
/* Enable clock */
ret = pm_runtime_get_sync(drvdata->dev);
if (ret < 0) {
pm_runtime_put(drvdata->dev);
return ret;
}
spin_lock(&drvdata->spinlock);
/* Unlock the TGU LAR */
TGU_UNLOCK(drvdata);
if (value) {
/* Disable TGU to program the triggers */
tgu_writel(drvdata, 0, TGU_CONTROL);
/* program the TGU Group data for the desired use case*/
for (i = 0; i <= drvdata->grp_refcnt; i++)
tgu_writel(drvdata, drvdata->grp_data[i].value,
drvdata->grp_data[i].grpaddr);
/* program the unused Condition Decode registers NOT bits to 1*/
for (i = 0; i <= drvdata->max_conditions; i++) {
for (j = 0; j <= drvdata->max_steps; j++)
tgu_writel(drvdata, 0x1000000,
CONDITION_DECODE_STEP(i, j));
}
/* program the TGU Condition Decode for the desired use case*/
for (i = 0; i <= drvdata->cond_refcnt; i++)
tgu_writel(drvdata, drvdata->condition_data[i].value,
drvdata->condition_data[i].condaddr);
/* program the TGU Condition Select for the desired use case*/
for (i = 0; i <= drvdata->select_refcnt; i++)
tgu_writel(drvdata, drvdata->select_data[i].value,
drvdata->select_data[i].selectaddr);
/* Timer and Counter Check */
for (i = 0; i <= drvdata->timer_refcnt; i++)
tgu_writel(drvdata, drvdata->timer_data[i].value,
drvdata->timer_data[i].timeraddr);
for (i = 0; i <= drvdata->counter_refcnt; i++)
tgu_writel(drvdata, drvdata->counter_data[i].value,
drvdata->counter_data[i].counteraddr);
/* Enable TGU to program the triggers */
tgu_writel(drvdata, 1, TGU_CONTROL);
drvdata->enable = true;
dev_dbg(dev, "Coresight-TGU enabled\n");
} else {
/* Disable TGU to program the triggers */
tgu_writel(drvdata, 0, TGU_CONTROL);
pm_runtime_put(drvdata->dev);
dev_dbg(dev, "Coresight-TGU disabled\n");
}
TGU_LOCK(drvdata);
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(enable_tgu);
static ssize_t reset_tgu_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
unsigned long value;
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
int ret;
if (kstrtoul(buf, 16, &value))
return -EINVAL;
if (!drvdata->enable) {
/* Enable clock */
ret = pm_runtime_get_sync(drvdata->dev);
if (ret < 0) {
pm_runtime_put(drvdata->dev);
return ret;
}
}
spin_lock(&drvdata->spinlock);
/* Unlock the TGU LAR */
TGU_UNLOCK(drvdata);
if (value) {
/* Disable TGU to program the triggers */
tgu_writel(drvdata, 0, TGU_CONTROL);
/* Reset the Reference counters*/
drvdata->grp_refcnt = 0;
drvdata->cond_refcnt = 0;
drvdata->select_refcnt = 0;
drvdata->timer_refcnt = 0;
drvdata->counter_refcnt = 0;
dev_dbg(dev, "Coresight-TGU disabled\n");
} else
dev_dbg(dev, "Invalid input to reset the TGU\n");
TGU_LOCK(drvdata);
spin_unlock(&drvdata->spinlock);
pm_runtime_put(drvdata->dev);
return size;
}
static DEVICE_ATTR_WO(reset_tgu);
static ssize_t set_group_store(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
int grp, reg, step;
unsigned long value;
if (drvdata->grp_refcnt >= MAX_GROUP_SETS) {
dev_err(drvdata->dev, " Too many groups are being configured\n");
return -EINVAL;
}
if (sscanf(buf, "%d %d %d %lx", &grp, &reg, &step, &value) != 4)
return -EINVAL;
spin_lock(&drvdata->spinlock);
if ((grp <= MAX_GROUPS) && (reg <= drvdata->max_regs)) {
drvdata->grp_data[drvdata->grp_refcnt].grpaddr =
GROUP_REG_STEP(grp, reg, step);
drvdata->grp_data[drvdata->grp_refcnt].value = value;
drvdata->grp_refcnt++;
} else
dev_err(drvdata->dev, "Invalid group data\n");
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(set_group);
static ssize_t set_condition_store(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long value;
int cond, step;
if (drvdata->cond_refcnt >= MAX_CONDITION_SETS) {
dev_err(drvdata->dev, " Too many groups are being configured\n");
return -EINVAL;
}
if (sscanf(buf, "%d %d %lx", &cond, &step, &value) != 3)
return -EINVAL;
spin_lock(&drvdata->spinlock);
if ((cond <= drvdata->max_conditions) && (step <=
drvdata->max_steps)) {
drvdata->condition_data[drvdata->cond_refcnt].condaddr =
CONDITION_DECODE_STEP(cond, step);
drvdata->condition_data[drvdata->cond_refcnt].value = value;
drvdata->cond_refcnt++;
} else
dev_err(drvdata->dev, "Invalid condition decode data\n");
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(set_condition);
static ssize_t set_select_store(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long value;
int select, step;
if (drvdata->select_refcnt >= MAX_CONDITION_SETS) {
dev_err(drvdata->dev, " Too many groups are being configured\n");
return -EINVAL;
}
if (sscanf(buf, "%d %d %lx", &select, &step, &value) != 3)
return -EINVAL;
spin_lock(&drvdata->spinlock);
if ((select <= drvdata->max_conditions) && (step <=
drvdata->max_steps)) {
drvdata->select_data[drvdata->select_refcnt].selectaddr =
CONDITION_SELECT_STEP(select, step);
drvdata->select_data[drvdata->select_refcnt].value = value;
drvdata->select_refcnt++;
} else
dev_err(drvdata->dev, "Invalid select decode data\n");
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(set_select);
static ssize_t set_timer_store(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long value;
int step;
if (drvdata->timer_refcnt >= MAX_TIMER_COUNTER_SETS) {
dev_err(drvdata->dev, " Too many groups are being configured\n");
return -EINVAL;
}
if (sscanf(buf, "%d %lx", &step, &value) != 2)
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (step <= drvdata->max_timer_counter) {
drvdata->timer_data[drvdata->timer_refcnt].timeraddr =
TIMER0_COMPARE_STEP(step);
drvdata->timer_data[drvdata->timer_refcnt].value = value;
drvdata->timer_refcnt++;
} else
dev_err(drvdata->dev, "Invalid TGU timer data\n");
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(set_timer);
static ssize_t set_counter_store(struct device *dev, struct device_attribute
*attr, const char *buf, size_t size)
{
struct tgu_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long value;
int step;
if (drvdata->counter_refcnt >= MAX_TIMER_COUNTER_SETS) {
dev_err(drvdata->dev, " Too many groups are being configured\n");
return -EINVAL;
}
if (sscanf(buf, "%d %lx", &step, &value) != 2)
return -EINVAL;
spin_lock(&drvdata->spinlock);
if (step <= drvdata->max_timer_counter) {
drvdata->counter_data[drvdata->counter_refcnt].counteraddr =
COUNTER0_COMPARE_STEP(step);
drvdata->counter_data[drvdata->counter_refcnt].value = value;
drvdata->counter_refcnt++;
} else
dev_err(drvdata->dev, "Invalid TGU counter data\n");
spin_unlock(&drvdata->spinlock);
return size;
}
static DEVICE_ATTR_WO(set_counter);
static struct attribute *tgu_attrs[] = {
&dev_attr_enable_tgu.attr,
&dev_attr_reset_tgu.attr,
&dev_attr_set_group.attr,
&dev_attr_set_condition.attr,
&dev_attr_set_select.attr,
&dev_attr_set_timer.attr,
&dev_attr_set_counter.attr,
NULL,
};
static struct attribute_group tgu_attr_grp = {
.attrs = tgu_attrs,
};
static const struct attribute_group *tgu_attr_grps[] = {
&tgu_attr_grp,
NULL,
};
static int tgu_probe(struct amba_device *adev, const struct amba_id *id)
{
int ret = 0;
struct device *dev = &adev->dev;
struct coresight_platform_data *pdata;
struct tgu_drvdata *drvdata;
struct coresight_desc desc = { 0 };
desc.name = coresight_alloc_device_name(&tgu_devs, dev);
if (!desc.name)
return -ENOMEM;
pdata = coresight_get_platform_data(dev);
if (IS_ERR(pdata))
return PTR_ERR(pdata);
adev->dev.platform_data = pdata;
drvdata = devm_kzalloc(dev, sizeof(*drvdata), GFP_KERNEL);
if (!drvdata)
return -ENOMEM;
drvdata->dev = &adev->dev;
dev_set_drvdata(dev, drvdata);
drvdata->base = devm_ioremap_resource(dev, &adev->res);
if (!drvdata->base)
return -ENOMEM;
spin_lock_init(&drvdata->spinlock);
ret = of_property_read_u32(adev->dev.of_node, "tgu-steps",
&drvdata->max_steps);
if (ret)
return -EINVAL;
ret = of_property_read_u32(adev->dev.of_node, "tgu-conditions",
&drvdata->max_conditions);
if (ret)
return -EINVAL;
ret = of_property_read_u32(adev->dev.of_node, "tgu-regs",
&drvdata->max_regs);
if (ret)
return -EINVAL;
ret = of_property_read_u32(adev->dev.of_node, "tgu-timer-counters",
&drvdata->max_timer_counter);
if (ret)
return -EINVAL;
/* Alloc memory for Grps, Conditions and Steps */
drvdata->grp_data = devm_kzalloc(dev, MAX_GROUP_SETS *
sizeof(*drvdata->grp_data),
GFP_KERNEL);
if (!drvdata->grp_data)
return -ENOMEM;
drvdata->condition_data = devm_kzalloc(dev, MAX_CONDITION_SETS *
sizeof(*drvdata->condition_data),
GFP_KERNEL);
if (!drvdata->condition_data)
return -ENOMEM;
drvdata->select_data = devm_kzalloc(dev, MAX_CONDITION_SETS *
sizeof(*drvdata->select_data),
GFP_KERNEL);
if (!drvdata->select_data)
return -ENOMEM;
drvdata->timer_data = devm_kzalloc(dev, MAX_TIMER_COUNTER_SETS *
sizeof(*drvdata->timer_data),
GFP_KERNEL);
if (!drvdata->timer_data)
return -ENOMEM;
drvdata->counter_data = devm_kzalloc(dev, MAX_TIMER_COUNTER_SETS *
sizeof(*drvdata->counter_data),
GFP_KERNEL);
if (!drvdata->counter_data)
return -ENOMEM;
drvdata->enable = false;
desc.type = CORESIGHT_DEV_TYPE_NONE;
desc.pdata = adev->dev.platform_data;
desc.dev = &adev->dev;
desc.groups = tgu_attr_grps;
drvdata->csdev = coresight_register(&desc);
if (IS_ERR(drvdata->csdev)) {
ret = PTR_ERR(drvdata->csdev);
goto err;
}
pm_runtime_put(&adev->dev);
dev_dbg(dev, "TGU initialized\n");
return 0;
err:
pm_runtime_put(&adev->dev);
return ret;
}
static struct amba_id tgu_ids[] = {
{
.id = 0x0003b999,
.mask = 0x0003ffff,
.data = "TGU",
},
{ 0, 0},
};
static struct amba_driver tgu_driver = {
.drv = {
.name = "coresight-tgu",
.owner = THIS_MODULE,
.suppress_bind_attrs = true,
},
.probe = tgu_probe,
.id_table = tgu_ids,
};
builtin_amba_driver(tgu_driver);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("CoreSight TGU driver");