Merge "coresight-tmc: Add usb support to coresight tmc etr"

This commit is contained in:
qctecmdr 2022-09-15 13:28:34 -07:00 committed by Gerrit - the friendly Code Review server
commit 82fe4ec23e
12 changed files with 933 additions and 96 deletions

View File

@ -9,7 +9,8 @@ coresight-y := coresight-core.o coresight-etm-perf.o coresight-platform.o \
coresight-syscfg-configfs.o
obj-$(CONFIG_CORESIGHT_LINK_AND_SINK_TMC) += coresight-tmc.o
coresight-tmc-y := coresight-tmc-core.o coresight-tmc-etf.o \
coresight-tmc-etr.o coresight-byte-cntr.o
coresight-tmc-etr.o coresight-byte-cntr.o \
coresight-tmc-usb.o
obj-$(CONFIG_CORESIGHT_SINK_TPIU) += coresight-tpiu.o
obj-$(CONFIG_CORESIGHT_SINK_ETBV10) += coresight-etb10.o
obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \

View File

@ -40,35 +40,44 @@ static void tmc_etr_read_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos,
static irqreturn_t etr_handler(int irq, void *data)
{
struct byte_cntr *byte_cntr_data = data;
struct tmc_drvdata *tmcdrvdata = byte_cntr_data->tmcdrvdata;
atomic_inc(&byte_cntr_data->irq_cnt);
wake_up(&byte_cntr_data->wq);
if (tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
atomic_inc(&byte_cntr_data->irq_cnt);
wake_up(&byte_cntr_data->usb_wait_wq);
} else if (tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_MEM) {
atomic_inc(&byte_cntr_data->irq_cnt);
wake_up(&byte_cntr_data->wq);
}
return IRQ_HANDLED;
}
static void tmc_etr_flush_bytes(struct byte_cntr *byte_cntr_data, loff_t *ppos,
size_t bytes, size_t *len)
static long tmc_etr_flush_remaining_bytes(struct tmc_drvdata *tmcdrvdata, loff_t *ppos,
char **bufpp)
{
uint32_t rwp = 0;
struct tmc_drvdata *tmcdrvdata = byte_cntr_data->tmcdrvdata;
dma_addr_t paddr = tmcdrvdata->sysfs_buf->hwaddr;
long rwp_offset, req_size, actual = 0;
struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf;
rwp = readl_relaxed(tmcdrvdata->base + TMC_RWP);
rwp_offset = tmc_get_rwp_offset(tmcdrvdata);
req_size = ((rwp_offset < *ppos) ? tmcdrvdata->size : 0) +
rwp_offset - *ppos;
if (rwp >= (paddr + *ppos)) {
if (bytes > (rwp - paddr - *ppos))
*len = rwp - paddr - *ppos;
}
if (req_size > 0)
actual = tmc_etr_buf_get_data(etr_buf, *ppos, req_size, bufpp);
return actual;
}
static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data,
size_t len, loff_t *ppos)
{
struct byte_cntr *byte_cntr_data = fp->private_data;
struct tmc_drvdata *tmcdrvdata = byte_cntr_data->tmcdrvdata;
char *bufp;
char *bufp = NULL;
long actual;
int ret = 0;
if (!data)
@ -76,8 +85,14 @@ static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data,
mutex_lock(&byte_cntr_data->byte_cntr_lock);
if (!byte_cntr_data->read_active) {
ret = -EINVAL;
goto err0;
actual = tmc_etr_flush_remaining_bytes(tmcdrvdata, ppos, &bufp);
if (actual > 0) {
len = actual;
goto copy;
} else {
ret = -EINVAL;
goto err0;
}
}
if (byte_cntr_data->enable) {
@ -89,30 +104,32 @@ static ssize_t tmc_etr_byte_cntr_read(struct file *fp, char __user *data,
return -ERESTARTSYS;
mutex_lock(&byte_cntr_data->byte_cntr_lock);
if (!byte_cntr_data->read_active) {
ret = -EINVAL;
goto err0;
actual = tmc_etr_flush_remaining_bytes(tmcdrvdata, ppos, &bufp);
if (actual > 0) {
len = actual;
goto copy;
} else {
ret = -EINVAL;
goto err0;
}
}
}
tmc_etr_read_bytes(byte_cntr_data, ppos,
byte_cntr_data->block_size, &len, &bufp);
} else {
if (!atomic_read(&byte_cntr_data->irq_cnt)) {
tmc_etr_flush_bytes(byte_cntr_data, ppos, byte_cntr_data->block_size,
&len);
if (!len) {
ret = -EINVAL;
goto err0;
}
actual = tmc_etr_flush_remaining_bytes(tmcdrvdata, ppos, &bufp);
if (actual > 0) {
len = actual;
goto copy;
} else {
tmc_etr_read_bytes(byte_cntr_data, ppos,
byte_cntr_data->block_size,
&len, &bufp);
ret = -EINVAL;
goto err0;
}
}
copy:
if (copy_to_user(data, bufp, len)) {
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
dev_dbg(&tmcdrvdata->csdev->dev,
@ -180,8 +197,12 @@ static int tmc_etr_byte_cntr_release(struct inode *in, struct file *fp)
byte_cntr_data->read_active = false;
atomic_set(&byte_cntr_data->irq_cnt, 0);
coresight_csr_set_byte_cntr(byte_cntr_data->csr,
if (byte_cntr_data->enable)
coresight_csr_set_byte_cntr(byte_cntr_data->csr,
byte_cntr_data->irqctrl_offset, 0);
disable_irq_wake(byte_cntr_data->byte_cntr_irq);
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
return 0;
@ -195,12 +216,18 @@ static int tmc_etr_byte_cntr_open(struct inode *in, struct file *fp)
mutex_lock(&byte_cntr_data->byte_cntr_lock);
if (byte_cntr_data->read_active) {
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
return -EBUSY;
}
if (tmcdrvdata->mode != CS_MODE_SYSFS ||
!byte_cntr_data->block_size) {
mutex_unlock(&byte_cntr_data->byte_cntr_lock);
return -EINVAL;
}
enable_irq_wake(byte_cntr_data->byte_cntr_irq);
/* IRQ is a '8- byte' counter and to observe interrupt at
* 'block_size' bytes of data
*/

View File

@ -20,13 +20,20 @@ struct byte_cntr {
uint32_t block_size;
int byte_cntr_irq;
atomic_t irq_cnt;
atomic_t usb_free_buf;
wait_queue_head_t wq;
wait_queue_head_t usb_wait_wq;
struct workqueue_struct *usb_wq;
struct qdss_request *usb_req;
struct work_struct read_work;
struct mutex usb_bypass_lock;
struct mutex byte_cntr_lock;
struct coresight_csr *csr;
struct tmc_drvdata *tmcdrvdata;
const char *name;
const char *class_name;
int irqctrl_offset;
unsigned long offset;
};
extern void tmc_etr_byte_cntr_start(struct byte_cntr *byte_cntr_data);

View File

@ -526,22 +526,42 @@ static struct coresight_device *coresight_get_source(struct list_head *path)
static int coresight_set_csr_atid(struct list_head *path,
struct coresight_device *sink_csdev, bool enable)
{
int atid, ret = 0;
int i, num, ret = 0;
struct coresight_device *src_csdev;
u32 *atid;
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
if (!src_csdev) {
ret = -EINVAL;
return ret;
}
num = of_coresight_get_atid_number(src_csdev);
if (num < 0)
return num;
atid = kcalloc(num, sizeof(*atid), GFP_KERNEL);
if (!atid)
return -ENOMEM;
ret = of_coresight_get_atid(src_csdev, atid, num);
if (ret < 0) {
kfree(atid);
return ret;
}
if (csr_set_atid_ops) {
for (i = 0; i < num; i++) {
ret = csr_set_atid_ops->set_atid(sink_csdev, atid[i], enable);
if (ret < 0) {
kfree(atid);
return ret;
}
}
} else
ret = -EINVAL;
kfree(atid);
return ret;
}
@ -1013,6 +1033,7 @@ void coresight_release_path(struct list_head *path)
}
kfree(path);
path = NULL;
}
/* return true if the device is a suitable type for a default sink */
@ -1265,8 +1286,8 @@ int coresight_enable(struct coresight_device *csdev)
path = coresight_build_path(csdev, sink);
if (IS_ERR(path)) {
pr_err("building path(s) failed\n");
ret = PTR_ERR(path);
pr_err("building path(s) failed %d\n", ret);
goto out;
}
@ -1306,6 +1327,9 @@ static void __coresight_disable(struct coresight_device *csdev)
if (ret)
return;
if (csdev->def_sink)
csdev->def_sink = NULL;
if (!csdev->enable || !coresight_disable_source(csdev))
return;
@ -1434,6 +1458,8 @@ static ssize_t sink_name_store(struct device *dev,
}
sink_name = kstrdup(buf, GFP_KERNEL);
if (!sink_name)
return -ENOMEM;
sink_name[size-1] = 0;
hash = hashlen_hash(hashlen_string(NULL, sink_name));

View File

@ -204,14 +204,25 @@ static int of_coresight_get_cpu(struct device *dev)
return cpu;
}
int of_coresight_get_atid(struct coresight_device *csdev)
/*
* of_coresight_get_atid_number: Get the atid number of a source device.
*
* Returns the number of the atid. If the result is less than zero, it means
* failure.
*/
int of_coresight_get_atid_number(struct coresight_device *csdev)
{
int atid, ret = 0;
return of_property_count_u32_elems(csdev->dev.parent->of_node, "atid");
}
ret = of_property_read_u32(csdev->dev.parent->of_node, "atid", &atid);
if (ret)
return ret;
return atid;
/*
* of_coresight_get_atid: Get the atid array of a source device.
*
* Returns 0 on success.
*/
int of_coresight_get_atid(struct coresight_device *csdev, u32 *atid, int atid_num)
{
return of_property_read_u32_array(csdev->dev.parent->of_node, "atid", atid, atid_num);
}
/*

View File

@ -189,7 +189,8 @@ struct csr_set_atid_op {
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);
int of_coresight_get_atid(struct coresight_device *src_dev, u32 *atid, int atid_num);
int of_coresight_get_atid_number(struct coresight_device *csdev);
/*
* Macros and inline functions to handle CoreSight UCI data and driver

View File

@ -181,19 +181,25 @@ static ssize_t tmc_read(struct file *file, char __user *data, size_t len,
ssize_t actual;
struct tmc_drvdata *drvdata = container_of(file->private_data,
struct tmc_drvdata, miscdev);
mutex_lock(&drvdata->mem_lock);
actual = tmc_get_sysfs_trace(drvdata, *ppos, len, &bufp);
if (actual <= 0)
if (actual <= 0) {
mutex_unlock(&drvdata->mem_lock);
return 0;
}
if (copy_to_user(data, bufp, actual)) {
dev_dbg(&drvdata->csdev->dev,
"%s: copy_to_user failed\n", __func__);
mutex_unlock(&drvdata->mem_lock);
return -EFAULT;
}
*ppos += actual;
dev_dbg(&drvdata->csdev->dev, "%zu bytes copied\n", actual);
mutex_unlock(&drvdata->mem_lock);
return actual;
}
@ -296,7 +302,7 @@ static ssize_t trigger_cntr_show(struct device *dev,
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
unsigned long val = drvdata->trigger_cntr;
return sprintf(buf, "%#lx\n", val);
return scnprintf(buf, PAGE_SIZE, "%#lx\n", val);
}
static ssize_t trigger_cntr_store(struct device *dev,
@ -321,7 +327,7 @@ static ssize_t buffer_size_show(struct device *dev,
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return sprintf(buf, "%#x\n", drvdata->size);
return scnprintf(buf, PAGE_SIZE, "%#x\n", drvdata->size);
}
static ssize_t buffer_size_store(struct device *dev,
@ -389,24 +395,68 @@ static ssize_t block_size_store(struct device *dev,
}
static DEVICE_ATTR_RW(block_size);
static struct attribute *coresight_tmc_attrs[] = {
static ssize_t out_mode_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
return scnprintf(buf, PAGE_SIZE, "%s\n",
str_tmc_etr_out_mode[drvdata->out_mode]);
}
static ssize_t out_mode_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t size)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(dev->parent);
char str[10] = "";
int ret;
if (strlen(buf) >= 10)
return -EINVAL;
if (sscanf(buf, "%s", str) != 1)
return -EINVAL;
ret = tmc_etr_switch_mode(drvdata, str);
return ret ? ret : size;
}
static DEVICE_ATTR_RW(out_mode);
static struct attribute *coresight_tmc_etr_attrs[] = {
&dev_attr_trigger_cntr.attr,
&dev_attr_buffer_size.attr,
&dev_attr_block_size.attr,
&dev_attr_out_mode.attr,
NULL,
};
static const struct attribute_group coresight_tmc_group = {
.attrs = coresight_tmc_attrs,
static struct attribute *coresight_tmc_etf_attrs[] = {
&dev_attr_trigger_cntr.attr,
NULL,
};
static const struct attribute_group coresight_tmc_etr_group = {
.attrs = coresight_tmc_etr_attrs,
};
static const struct attribute_group coresight_tmc_etf_group = {
.attrs = coresight_tmc_etf_attrs,
};
static const struct attribute_group coresight_tmc_mgmt_group = {
.attrs = coresight_tmc_mgmt_attrs,
.name = "mgmt",
};
static const struct attribute_group *coresight_tmc_groups[] = {
&coresight_tmc_group,
static const struct attribute_group *coresight_tmc_etr_groups[] = {
&coresight_tmc_etr_group,
&coresight_tmc_mgmt_group,
NULL,
};
static const struct attribute_group *coresight_tmc_etf_groups[] = {
&coresight_tmc_etf_group,
&coresight_tmc_mgmt_group,
NULL,
};
@ -520,6 +570,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
desc.access = CSDEV_ACCESS_IOMEM(base);
spin_lock_init(&drvdata->spinlock);
mutex_init(&drvdata->mem_lock);
devid = readl_relaxed(drvdata->base + CORESIGHT_DEVID);
drvdata->config_type = BMVAL(devid, 6, 7);
@ -528,6 +579,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->pid = -1;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) {
drvdata->out_mode = TMC_ETR_OUT_MODE_MEM;
drvdata->size = tmc_etr_get_default_buffer_size(dev);
drvdata->max_burst_size = tmc_etr_get_max_burst_size(dev);
} else {
@ -546,23 +598,25 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
}
desc.dev = dev;
desc.groups = coresight_tmc_groups;
switch (drvdata->config_type) {
case TMC_CONFIG_TYPE_ETB:
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.ops = &tmc_etb_cs_ops;
desc.groups = coresight_tmc_etf_groups;
dev_list = &etb_devs;
break;
case TMC_CONFIG_TYPE_ETR:
desc.type = CORESIGHT_DEV_TYPE_SINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_SYSMEM;
desc.ops = &tmc_etr_cs_ops;
desc.groups = coresight_tmc_etr_groups;
ret = tmc_etr_setup_caps(dev, devid,
coresight_get_uci_data(id));
if (ret)
goto out;
idr_init(&drvdata->idr);
mutex_init(&drvdata->idr_mutex);
dev_list = &etr_devs;
@ -573,12 +627,17 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id)
drvdata->byte_cntr = byte_cntr_init(adev, drvdata);
ret = tmc_etr_usb_init(adev, drvdata);
if (ret)
goto out;
break;
case TMC_CONFIG_TYPE_ETF:
desc.type = CORESIGHT_DEV_TYPE_LINKSINK;
desc.subtype.sink_subtype = CORESIGHT_DEV_SUBTYPE_SINK_BUFFER;
desc.subtype.link_subtype = CORESIGHT_DEV_SUBTYPE_LINK_FIFO;
desc.ops = &tmc_etf_cs_ops;
desc.groups = coresight_tmc_etf_groups;
dev_list = &etf_devs;
break;
default:
@ -629,7 +688,10 @@ static void tmc_shutdown(struct amba_device *adev)
if (drvdata->mode == CS_MODE_DISABLED)
goto out;
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR)
if (drvdata->config_type == TMC_CONFIG_TYPE_ETR &&
(drvdata->out_mode == TMC_ETR_OUT_MODE_MEM ||
(drvdata->out_mode == TMC_ETR_OUT_MODE_USB &&
drvdata->usb_data->usb_mode == TMC_ETR_USB_SW)))
tmc_etr_disable_hw(drvdata);
/*

View File

@ -231,6 +231,9 @@ static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data)
struct perf_output_handle *handle = data;
struct cs_buffers *buf = etm_perf_sink_config(handle);
if (buf == NULL)
return -EINVAL;
spin_lock_irqsave(&drvdata->spinlock, flags);
do {
ret = -EINVAL;

View File

@ -10,6 +10,7 @@
#include <linux/iommu.h>
#include <linux/idr.h>
#include <linux/mutex.h>
#include <linux/qcom-iommu-util.h>
#include <linux/refcount.h>
#include <linux/slab.h>
#include <linux/types.h>
@ -51,6 +52,9 @@ struct etr_perf_buffer {
/* Lower limit for ETR hardware buffer */
#define TMC_ETR_PERF_MIN_BUF_SIZE SZ_1M
/* SW USB reserved memory size */
#define TMC_ETR_SW_USB_BUF_SIZE SZ_64M
/*
* The TMC ETR SG has a page size of 4K. The SG table contains pointers
* to 4KB buffers. However, the OS may use a PAGE_SIZE different from
@ -171,8 +175,15 @@ static void tmc_pages_free(struct tmc_pages *tmc_pages,
__free_page(tmc_pages->pages[i]);
}
kfree(tmc_pages->pages);
kfree(tmc_pages->daddrs);
if (is_vmalloc_addr(tmc_pages->pages))
vfree(tmc_pages->pages);
else
kfree(tmc_pages->pages);
if (is_vmalloc_addr(tmc_pages->daddrs))
vfree(tmc_pages->daddrs);
else
kfree(tmc_pages->daddrs);
tmc_pages->pages = NULL;
tmc_pages->daddrs = NULL;
tmc_pages->nr_pages = 0;
@ -198,14 +209,24 @@ static int tmc_pages_alloc(struct tmc_pages *tmc_pages,
nr_pages = tmc_pages->nr_pages;
tmc_pages->daddrs = kcalloc(nr_pages, sizeof(*tmc_pages->daddrs),
GFP_KERNEL);
if (!tmc_pages->daddrs)
return -ENOMEM;
if (!tmc_pages->daddrs) {
tmc_pages->daddrs = vmalloc(sizeof(*tmc_pages->daddrs) * nr_pages);
if (!tmc_pages->daddrs)
return -ENOMEM;
}
tmc_pages->pages = kcalloc(nr_pages, sizeof(*tmc_pages->pages),
GFP_KERNEL);
if (!tmc_pages->pages) {
kfree(tmc_pages->daddrs);
tmc_pages->daddrs = NULL;
return -ENOMEM;
tmc_pages->pages = vmalloc(sizeof(*tmc_pages->pages) * nr_pages);
if (!tmc_pages->pages) {
if (is_vmalloc_addr(tmc_pages->daddrs))
vfree(tmc_pages->daddrs);
else
kfree(tmc_pages->daddrs);
tmc_pages->daddrs = NULL;
return -ENOMEM;
}
}
for (i = 0; i < nr_pages; i++) {
@ -258,6 +279,39 @@ void tmc_free_sg_table(struct tmc_sg_table *sg_table)
}
EXPORT_SYMBOL_GPL(tmc_free_sg_table);
static long tmc_flat_get_rwp_offset(struct tmc_drvdata *drvdata)
{
dma_addr_t paddr = drvdata->sysfs_buf->hwaddr;
u64 rwp;
rwp = tmc_read_rwp(drvdata);
return rwp - paddr;
}
static long tmc_sg_get_rwp_offset(struct tmc_drvdata *drvdata)
{
struct etr_buf *etr_buf = drvdata->sysfs_buf;
struct etr_sg_table *etr_table = etr_buf->private;
struct tmc_sg_table *table = etr_table->sg_table;
u64 rwp;
long w_offset;
rwp = tmc_read_rwp(drvdata);
w_offset = tmc_sg_get_data_page_offset(table, rwp);
return w_offset;
}
long tmc_get_rwp_offset(struct tmc_drvdata *drvdata)
{
struct etr_buf *etr_buf = drvdata->sysfs_buf;
if (etr_buf->mode == ETR_MODE_FLAT)
return tmc_flat_get_rwp_offset(drvdata);
else
return tmc_sg_get_rwp_offset(drvdata);
}
/*
* Alloc pages for the table. Since this will be used by the device,
* allocate the pages closer to the device (i.e, dev_to_node(dev)
@ -879,9 +933,23 @@ static struct etr_buf *tmc_alloc_etr_buf(struct tmc_drvdata *drvdata,
bool has_sg, has_catu;
struct etr_buf *etr_buf;
struct device *dev = &drvdata->csdev->dev;
int mapping_config = 0;
struct iommu_domain *domain;
has_etr_sg = tmc_etr_has_cap(drvdata, TMC_ETR_SG);
has_iommu = iommu_get_domain_for_dev(dev->parent);
domain = iommu_get_domain_for_dev(dev->parent);
if (domain) {
mapping_config = qcom_iommu_get_mappings_configuration(domain);
if (mapping_config < 0)
return ERR_PTR(-ENOMEM);
if (mapping_config & QCOM_IOMMU_MAPPING_CONF_S1_BYPASS)
has_iommu = false;
else
has_iommu = true;
} else {
has_iommu = false;
}
has_catu = !!tmc_etr_get_catu_device(drvdata);
has_sg = has_catu || has_etr_sg;
@ -989,7 +1057,7 @@ static void tmc_sync_etr_buf(struct tmc_drvdata *drvdata)
return;
}
etr_buf->full = !!(status & TMC_STS_FULL);
etr_buf->full = status & TMC_STS_FULL;
WARN_ON(!etr_buf->ops || !etr_buf->ops->sync);
@ -1111,7 +1179,12 @@ ssize_t tmc_etr_get_sysfs_trace(struct tmc_drvdata *drvdata,
static struct etr_buf *
tmc_etr_setup_sysfs_buf(struct tmc_drvdata *drvdata)
{
return tmc_alloc_etr_buf(drvdata, drvdata->size,
if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB &&
drvdata->usb_data->usb_mode == TMC_ETR_USB_SW)
return tmc_alloc_etr_buf(drvdata, TMC_ETR_SW_USB_BUF_SIZE,
0, cpu_to_node(0), NULL);
else
return tmc_alloc_etr_buf(drvdata, drvdata->size,
0, cpu_to_node(0), NULL);
}
@ -1185,22 +1258,31 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
* with the lock released.
*/
spin_lock_irqsave(&drvdata->spinlock, flags);
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (sysfs_buf->size != drvdata->size)) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if ((drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
|| (drvdata->out_mode == TMC_ETR_OUT_MODE_USB &&
drvdata->usb_data->usb_mode ==
TMC_ETR_USB_SW)) {
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM
&& sysfs_buf->size != drvdata->size)
|| (drvdata->out_mode == TMC_ETR_OUT_MODE_USB
&& drvdata->usb_data->usb_mode == TMC_ETR_USB_SW
&& sysfs_buf->size != TMC_ETR_SW_USB_BUF_SIZE)) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
/* Allocate memory with the locks released */
free_buf = new_buf = tmc_etr_setup_sysfs_buf(drvdata);
if (IS_ERR(new_buf))
return PTR_ERR(new_buf);
/* Allocate memory with the locks released */
free_buf = new_buf = tmc_etr_setup_sysfs_buf(drvdata);
if (IS_ERR(new_buf))
return PTR_ERR(new_buf);
/* Let's try again */
spin_lock_irqsave(&drvdata->spinlock, flags);
/* Let's try again */
spin_lock_irqsave(&drvdata->spinlock, flags);
}
}
if (drvdata->reading || drvdata->mode == CS_MODE_PERF) {
ret = -EBUSY;
goto out;
goto unlock_out;
}
/*
@ -1210,33 +1292,52 @@ static int tmc_enable_etr_sink_sysfs(struct coresight_device *csdev)
*/
if (drvdata->mode == CS_MODE_SYSFS) {
atomic_inc(csdev->refcnt);
goto out;
goto unlock_out;
}
/*
* If we don't have a buffer or it doesn't match the requested size,
* use the buffer allocated above. Otherwise reuse the existing buffer.
*/
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (new_buf && sysfs_buf->size != new_buf->size)) {
free_buf = sysfs_buf;
drvdata->sysfs_buf = new_buf;
if ((drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) ||
(drvdata->out_mode == TMC_ETR_OUT_MODE_USB &&
drvdata->usb_data->usb_mode ==
TMC_ETR_USB_SW)) {
sysfs_buf = READ_ONCE(drvdata->sysfs_buf);
if (!sysfs_buf || (new_buf && sysfs_buf->size != new_buf->size)) {
free_buf = sysfs_buf;
drvdata->sysfs_buf = new_buf;
}
ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
if (ret)
goto unlock_out;
}
ret = tmc_etr_enable_hw(drvdata, drvdata->sysfs_buf);
if (!ret) {
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(csdev->refcnt);
drvdata->mode = CS_MODE_SYSFS;
atomic_inc(csdev->refcnt);
spin_unlock_irqrestore(&drvdata->spinlock, flags);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
ret = tmc_usb_enable(drvdata->usb_data);
if (ret) {
atomic_dec(csdev->refcnt);
drvdata->mode = CS_MODE_DISABLED;
}
}
out:
goto out;
unlock_out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
out:
/* Free memory outside the spinlock if need be */
if (free_buf)
tmc_etr_free_sysfs_buf(free_buf);
if (!ret) {
tmc_etr_byte_cntr_start(drvdata->byte_cntr);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM)
tmc_etr_byte_cntr_start(drvdata->byte_cntr);
dev_dbg(&csdev->dev, "TMC-ETR enabled\n");
}
@ -1670,9 +1771,15 @@ static int tmc_enable_etr_sink_perf(struct coresight_device *csdev, void *data)
static int tmc_enable_etr_sink(struct coresight_device *csdev,
u32 mode, void *data)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
switch (mode) {
case CS_MODE_SYSFS:
return tmc_enable_etr_sink_sysfs(csdev);
mutex_lock(&drvdata->mem_lock);
ret = tmc_enable_etr_sink_sysfs(csdev);
mutex_unlock(&drvdata->mem_lock);
return ret;
case CS_MODE_PERF:
return tmc_enable_etr_sink_perf(csdev, data);
}
@ -1681,7 +1788,8 @@ static int tmc_enable_etr_sink(struct coresight_device *csdev,
return -EINVAL;
}
static int tmc_disable_etr_sink(struct coresight_device *csdev)
static int _tmc_disable_etr_sink(struct coresight_device *csdev,
bool mode_switch)
{
unsigned long flags;
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
@ -1693,14 +1801,28 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
return -EBUSY;
}
if (atomic_dec_return(csdev->refcnt)) {
if (atomic_dec_return(csdev->refcnt) && !mode_switch) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
return -EBUSY;
}
/* Complain if we (somehow) got out of sync */
WARN_ON_ONCE(drvdata->mode == CS_MODE_DISABLED);
tmc_etr_disable_hw(drvdata);
if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM ||
(drvdata->out_mode == TMC_ETR_OUT_MODE_USB &&
drvdata->usb_data->usb_mode == TMC_ETR_USB_SW)) {
if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
tmc_usb_disable(drvdata->usb_data);
spin_lock_irqsave(&drvdata->spinlock, flags);
}
tmc_etr_disable_hw(drvdata);
} else {
spin_unlock_irqrestore(&drvdata->spinlock, flags);
tmc_usb_disable(drvdata->usb_data);
spin_lock_irqsave(&drvdata->spinlock, flags);
}
/* Dissociate from monitored process. */
drvdata->pid = -1;
drvdata->mode = CS_MODE_DISABLED;
@ -1714,6 +1836,67 @@ static int tmc_disable_etr_sink(struct coresight_device *csdev)
return 0;
}
static int tmc_disable_etr_sink(struct coresight_device *csdev)
{
struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
int ret;
mutex_lock(&drvdata->mem_lock);
ret = _tmc_disable_etr_sink(csdev, false);
mutex_unlock(&drvdata->mem_lock);
return ret;
}
int tmc_etr_switch_mode(struct tmc_drvdata *drvdata, const char *out_mode)
{
enum tmc_etr_out_mode new_mode, old_mode;
mutex_lock(&drvdata->mem_lock);
if (!strcmp(out_mode, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_MEM]))
new_mode = TMC_ETR_OUT_MODE_MEM;
else if (!strcmp(out_mode, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_USB])) {
if (drvdata->usb_data->usb_mode == TMC_ETR_USB_NONE) {
dev_err(&drvdata->csdev->dev,
"USB mode is not supported.\n");
mutex_unlock(&drvdata->mem_lock);
return -EINVAL;
}
new_mode = TMC_ETR_OUT_MODE_USB;
} else {
mutex_unlock(&drvdata->mem_lock);
return -EINVAL;
}
if (new_mode == drvdata->out_mode) {
mutex_unlock(&drvdata->mem_lock);
return 0;
}
if (drvdata->mode == CS_MODE_DISABLED) {
drvdata->out_mode = new_mode;
mutex_unlock(&drvdata->mem_lock);
return 0;
}
_tmc_disable_etr_sink(drvdata->csdev, true);
old_mode = drvdata->out_mode;
drvdata->out_mode = new_mode;
if (tmc_enable_etr_sink_sysfs(drvdata->csdev)) {
drvdata->out_mode = old_mode;
tmc_enable_etr_sink_sysfs(drvdata->csdev);
dev_err(&drvdata->csdev->dev,
"Switch to %s failed. Fall back to %s.\n",
str_tmc_etr_out_mode[new_mode],
str_tmc_etr_out_mode[old_mode]);
mutex_unlock(&drvdata->mem_lock);
return -EINVAL;
}
mutex_unlock(&drvdata->mem_lock);
return 0;
}
static const struct coresight_ops_sink tmc_etr_sink_ops = {
.enable = tmc_enable_etr_sink,
.disable = tmc_disable_etr_sink,
@ -1735,6 +1918,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
mutex_lock(&drvdata->mem_lock);
spin_lock_irqsave(&drvdata->spinlock, flags);
if (drvdata->reading) {
ret = -EBUSY;
@ -1763,6 +1947,7 @@ int tmc_read_prepare_etr(struct tmc_drvdata *drvdata)
drvdata->reading = true;
out:
spin_unlock_irqrestore(&drvdata->spinlock, flags);
mutex_unlock(&drvdata->mem_lock);
return ret;
}
@ -1776,6 +1961,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETR))
return -EINVAL;
mutex_lock(&drvdata->mem_lock);
spin_lock_irqsave(&drvdata->spinlock, flags);
/* RE-enable the TMC if need be */
@ -1802,5 +1988,7 @@ int tmc_read_unprepare_etr(struct tmc_drvdata *drvdata)
if (sysfs_buf)
tmc_etr_free_sysfs_buf(sysfs_buf);
mutex_unlock(&drvdata->mem_lock);
return 0;
}

View File

@ -0,0 +1,463 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
*
* Description: CoreSight TMC USB driver
*/
#include <linux/of_address.h>
#include <linux/delay.h>
#include <linux/qcom-iommu-util.h>
#include <linux/usb/usb_qdss.h>
#include <linux/time.h>
#include <linux/slab.h>
#include "coresight-tmc-usb.h"
#include "coresight-priv.h"
#include "coresight-common.h"
#include "coresight-tmc.h"
#define USB_BLK_SIZE 65536
#define USB_SG_NUM (USB_BLK_SIZE / PAGE_SIZE)
#define USB_BUF_NUM 255
#define USB_TIME_OUT (5 * HZ)
#define TMC_AXICTL_VALUE (0xf02)
#define TMC_FFCR_VALUE (0x133)
static int usb_bypass_start(struct byte_cntr *byte_cntr_data)
{
long offset;
struct tmc_drvdata *tmcdrvdata;
if (!byte_cntr_data)
return -ENOMEM;
tmcdrvdata = byte_cntr_data->tmcdrvdata;
mutex_lock(&byte_cntr_data->usb_bypass_lock);
dev_info(&tmcdrvdata->csdev->dev,
"%s: Start usb bypass\n", __func__);
if (tmcdrvdata->mode != CS_MODE_SYSFS) {
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
return -EINVAL;
}
offset = tmc_get_rwp_offset(tmcdrvdata);
if (offset < 0) {
dev_err(&tmcdrvdata->csdev->dev,
"%s: invalid rwp offset value\n", __func__);
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
return offset;
}
byte_cntr_data->offset = offset;
/*Ensure usbch is ready*/
if (!tmcdrvdata->usb_data->usbch) {
int i;
for (i = TIMEOUT_US; i > 0; i--) {
if (tmcdrvdata->usb_data->usbch)
break;
if (i - 1)
udelay(1);
else {
dev_err(&tmcdrvdata->csdev->dev,
"timeout while waiting usbch to be ready\n");
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
return -EAGAIN;
}
}
}
atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM);
byte_cntr_data->read_active = true;
/*
* IRQ is a '8- byte' counter and to observe interrupt at
* 'block_size' bytes of data
*/
coresight_csr_set_byte_cntr(byte_cntr_data->csr,
byte_cntr_data->irqctrl_offset,
USB_BLK_SIZE / 8);
atomic_set(&byte_cntr_data->irq_cnt, 0);
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
return 0;
}
static void usb_bypass_stop(struct byte_cntr *byte_cntr_data)
{
if (!byte_cntr_data)
return;
mutex_lock(&byte_cntr_data->usb_bypass_lock);
if (byte_cntr_data->read_active)
byte_cntr_data->read_active = false;
else {
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
return;
}
wake_up(&byte_cntr_data->usb_wait_wq);
pr_info("coresight: stop usb bypass\n");
coresight_csr_set_byte_cntr(byte_cntr_data->csr, byte_cntr_data->irqctrl_offset, 0);
mutex_unlock(&byte_cntr_data->usb_bypass_lock);
}
static int usb_transfer_small_packet(struct byte_cntr *drvdata, size_t *small_size)
{
int ret = 0;
struct tmc_drvdata *tmcdrvdata = drvdata->tmcdrvdata;
struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf;
struct qdss_request *usb_req = NULL;
size_t req_size;
long actual;
long w_offset;
w_offset = tmc_get_rwp_offset(tmcdrvdata);
if (w_offset < 0) {
ret = w_offset;
dev_err_ratelimited(&tmcdrvdata->csdev->dev,
"%s: RWP offset is invalid\n", __func__);
goto out;
}
req_size = ((w_offset < drvdata->offset) ? etr_buf->size : 0) +
w_offset - drvdata->offset;
req_size = ((req_size + *small_size) < USB_BLK_SIZE) ? req_size :
(USB_BLK_SIZE - *small_size);
while (req_size > 0) {
usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL);
if (!usb_req) {
ret = -EFAULT;
goto out;
}
actual = tmc_etr_buf_get_data(etr_buf, drvdata->offset,
req_size, &usb_req->buf);
if (actual <= 0 || actual > req_size) {
kfree(usb_req);
usb_req = NULL;
dev_err_ratelimited(&tmcdrvdata->csdev->dev,
"%s: Invalid data in ETR\n", __func__);
ret = -EINVAL;
goto out;
}
usb_req->length = actual;
drvdata->usb_req = usb_req;
req_size -= actual;
if ((drvdata->offset + actual) >=
tmcdrvdata->sysfs_buf->size)
drvdata->offset = 0;
else
drvdata->offset += actual;
*small_size += actual;
if (atomic_read(&drvdata->usb_free_buf) > 0) {
ret = usb_qdss_write(tmcdrvdata->usb_data->usbch, usb_req);
if (ret) {
kfree(usb_req);
usb_req = NULL;
drvdata->usb_req = NULL;
dev_err_ratelimited(&tmcdrvdata->csdev->dev,
"Write data failed:%d\n", ret);
goto out;
}
atomic_dec(&drvdata->usb_free_buf);
} else {
dev_dbg(&tmcdrvdata->csdev->dev,
"Drop data, offset = %d, len = %d\n",
drvdata->offset, req_size);
kfree(usb_req);
drvdata->usb_req = NULL;
}
}
out:
return ret;
}
static void usb_read_work_fn(struct work_struct *work)
{
int ret, i, seq = 0;
struct qdss_request *usb_req = NULL;
size_t req_size, req_sg_num, small_size = 0;
long actual;
ssize_t actual_total = 0;
char *buf;
struct byte_cntr *drvdata =
container_of(work, struct byte_cntr, read_work);
struct tmc_drvdata *tmcdrvdata = drvdata->tmcdrvdata;
struct etr_buf *etr_buf = tmcdrvdata->sysfs_buf;
while (tmcdrvdata->mode == CS_MODE_SYSFS
&& tmcdrvdata->out_mode == TMC_ETR_OUT_MODE_USB) {
if (!atomic_read(&drvdata->irq_cnt)) {
ret = wait_event_interruptible_timeout(
drvdata->usb_wait_wq,
atomic_read(&drvdata->irq_cnt) > 0
|| tmcdrvdata->mode != CS_MODE_SYSFS || tmcdrvdata->out_mode
!= TMC_ETR_OUT_MODE_USB
|| !drvdata->read_active, USB_TIME_OUT);
if (ret == -ERESTARTSYS || tmcdrvdata->mode != CS_MODE_SYSFS
|| tmcdrvdata->out_mode != TMC_ETR_OUT_MODE_USB
|| !drvdata->read_active)
break;
if (ret == 0) {
ret = usb_transfer_small_packet(drvdata, &small_size);
if (ret && ret != -EAGAIN)
return;
continue;
}
}
req_size = USB_BLK_SIZE - small_size;
small_size = 0;
actual_total = 0;
if (req_size > 0) {
seq++;
req_sg_num = (req_size - 1) / PAGE_SIZE + 1;
usb_req = kzalloc(sizeof(*usb_req), GFP_KERNEL);
if (!usb_req)
return;
usb_req->sg = kcalloc(req_sg_num,
sizeof(*(usb_req->sg)), GFP_KERNEL);
if (!usb_req->sg) {
kfree(usb_req);
usb_req = NULL;
return;
}
for (i = 0; i < req_sg_num; i++) {
actual = tmc_etr_buf_get_data(etr_buf,
drvdata->offset,
PAGE_SIZE, &buf);
if (actual <= 0 || actual > PAGE_SIZE) {
kfree(usb_req->sg);
kfree(usb_req);
usb_req = NULL;
dev_err_ratelimited(
&tmcdrvdata->csdev->dev,
"Invalid data in ETR\n");
return;
}
sg_set_buf(&usb_req->sg[i], buf, actual);
if (i == 0)
usb_req->buf = buf;
if (i == req_sg_num - 1)
sg_mark_end(&usb_req->sg[i]);
if ((drvdata->offset + actual) >=
tmcdrvdata->sysfs_buf->size)
drvdata->offset = 0;
else
drvdata->offset += actual;
actual_total += actual;
}
usb_req->length = actual_total;
drvdata->usb_req = usb_req;
usb_req->num_sgs = i;
if (atomic_read(&drvdata->usb_free_buf) > 0) {
ret = usb_qdss_write(tmcdrvdata->usb_data->usbch,
drvdata->usb_req);
if (ret) {
kfree(usb_req->sg);
kfree(usb_req);
usb_req = NULL;
drvdata->usb_req = NULL;
dev_err_ratelimited(
&tmcdrvdata->csdev->dev,
"Write data failed:%d\n", ret);
if (ret == -EAGAIN)
continue;
return;
}
atomic_dec(&drvdata->usb_free_buf);
} else {
dev_dbg(&tmcdrvdata->csdev->dev,
"Drop data, offset = %d, seq = %d, irq = %d\n",
drvdata->offset, seq,
atomic_read(&drvdata->irq_cnt));
kfree(usb_req->sg);
kfree(usb_req);
drvdata->usb_req = NULL;
}
}
if (atomic_read(&drvdata->irq_cnt) > 0)
atomic_dec(&drvdata->irq_cnt);
}
dev_err(&tmcdrvdata->csdev->dev, "TMC has been stopped.\n");
}
static void usb_write_done(struct byte_cntr *drvdata,
struct qdss_request *d_req)
{
atomic_inc(&drvdata->usb_free_buf);
if (d_req->status)
pr_err_ratelimited("USB write failed err:%d\n", d_req->status);
kfree(d_req->sg);
kfree(d_req);
}
static int usb_bypass_init(struct byte_cntr *byte_cntr_data)
{
byte_cntr_data->usb_wq = create_singlethread_workqueue("byte-cntr");
if (!byte_cntr_data->usb_wq)
return -ENOMEM;
byte_cntr_data->offset = 0;
mutex_init(&byte_cntr_data->usb_bypass_lock);
init_waitqueue_head(&byte_cntr_data->usb_wait_wq);
atomic_set(&byte_cntr_data->usb_free_buf, USB_BUF_NUM);
INIT_WORK(&(byte_cntr_data->read_work), usb_read_work_fn);
return 0;
}
void usb_notifier(void *priv, unsigned int event, struct qdss_request *d_req,
struct usb_qdss_ch *ch)
{
struct tmc_drvdata *drvdata = priv;
int ret = 0;
if (!drvdata)
return;
if (drvdata->out_mode != TMC_ETR_OUT_MODE_USB) {
dev_err(&drvdata->csdev->dev,
"%s: ETR is not USB mode.\n", __func__);
return;
}
switch (event) {
case USB_QDSS_CONNECT:
if (drvdata->mode == CS_MODE_DISABLED) {
dev_err_ratelimited(&drvdata->csdev->dev,
"%s: ETR is disabled.\n", __func__);
return;
}
if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW) {
ret = usb_bypass_start(drvdata->byte_cntr);
if (ret < 0)
return;
usb_qdss_alloc_req(ch, USB_BUF_NUM);
queue_work(drvdata->byte_cntr->usb_wq, &(drvdata->byte_cntr->read_work));
}
break;
case USB_QDSS_DISCONNECT:
if (drvdata->mode == CS_MODE_DISABLED) {
dev_err_ratelimited(&drvdata->csdev->dev,
"%s: ETR is disabled.\n", __func__);
return;
}
if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW) {
usb_bypass_stop(drvdata->byte_cntr);
flush_work(&((drvdata->byte_cntr->read_work)));
usb_qdss_free_req(drvdata->usb_data->usbch);
}
break;
case USB_QDSS_DATA_WRITE_DONE:
if (drvdata->usb_data->usb_mode == TMC_ETR_USB_SW)
usb_write_done(drvdata->byte_cntr, d_req);
break;
default:
break;
}
}
static bool tmc_etr_support_usb_bypass(struct device *dev)
{
return fwnode_property_present(dev->fwnode, "qcom,sw-usb");
}
int tmc_usb_enable(struct tmc_usb_data *usb_data)
{
struct tmc_drvdata *tmcdrvdata;
if (!usb_data)
return -EINVAL;
tmcdrvdata = usb_data->tmcdrvdata;
if (usb_data->usb_mode == TMC_ETR_USB_SW)
usb_data->usbch = usb_qdss_open(USB_QDSS_CH_SW, tmcdrvdata, usb_notifier);
if (IS_ERR_OR_NULL(usb_data->usbch)) {
dev_err(&tmcdrvdata->csdev->dev, "usb_qdss_open failed for qdss.\n");
return -ENODEV;
}
return 0;
}
void tmc_usb_disable(struct tmc_usb_data *usb_data)
{
struct tmc_drvdata *tmcdrvdata = usb_data->tmcdrvdata;
if (usb_data->usb_mode == TMC_ETR_USB_SW)
usb_bypass_stop(tmcdrvdata->byte_cntr);
if (usb_data->usbch)
usb_qdss_close(usb_data->usbch);
else
dev_err(&tmcdrvdata->csdev->dev, "usb channel is null.\n");
}
int tmc_etr_usb_init(struct amba_device *adev,
struct tmc_drvdata *drvdata)
{
struct device *dev = &adev->dev;
struct tmc_usb_data *usb_data;
struct byte_cntr *byte_cntr_data;
int ret;
usb_data = devm_kzalloc(dev, sizeof(*usb_data), GFP_KERNEL);
if (!usb_data)
return -ENOMEM;
drvdata->usb_data = usb_data;
drvdata->usb_data->tmcdrvdata = drvdata;
byte_cntr_data = drvdata->byte_cntr;
if (tmc_etr_support_usb_bypass(dev)) {
usb_data->usb_mode = TMC_ETR_USB_SW;
if (!byte_cntr_data)
return -EINVAL;
ret = usb_bypass_init(byte_cntr_data);
if (ret)
return -EINVAL;
return 0;
}
usb_data->usb_mode = TMC_ETR_USB_NONE;
pr_err("%s: ETR usb property is not configured!\n",
__func__, dev_name(dev));
return 0;
}

View File

@ -0,0 +1,29 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2021 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#ifndef _CORESIGHT_TMC_USB_H
#define _CORESIGHT_TMC_USB_H
#include <linux/amba/bus.h>
#include <linux/usb/usb_qdss.h>
#define TMC_USB_BAM_PIPE_INDEX 0
#define TMC_USB_BAM_NR_PIPES 2
enum tmc_etr_usb_mode {
TMC_ETR_USB_NONE,
TMC_ETR_USB_SW,
};
struct tmc_usb_data {
struct usb_qdss_ch *usbch;
enum tmc_etr_usb_mode usb_mode;
struct tmc_drvdata *tmcdrvdata;
};
extern int tmc_usb_enable(struct tmc_usb_data *usb_data);
extern void tmc_usb_disable(struct tmc_usb_data *usb_data);
#endif

View File

@ -14,6 +14,7 @@
#include <linux/refcount.h>
#include "coresight-priv.h"
#include "coresight-byte-cntr.h"
#include "coresight-tmc-usb.h"
#define TMC_RSZ 0x004
#define TMC_STS 0x00c
@ -141,6 +142,18 @@ enum etr_mode {
struct etr_buf_operations;
enum tmc_etr_out_mode {
TMC_ETR_OUT_MODE_NONE,
TMC_ETR_OUT_MODE_MEM,
TMC_ETR_OUT_MODE_USB,
};
static const char * const str_tmc_etr_out_mode[] = {
[TMC_ETR_OUT_MODE_NONE] = "none",
[TMC_ETR_OUT_MODE_MEM] = "mem",
[TMC_ETR_OUT_MODE_USB] = "usb",
};
/**
* struct etr_buf - Details of the buffer used by ETR
* refcount ; Number of sources currently using this etr_buf.
@ -207,6 +220,7 @@ struct tmc_drvdata {
u32 mode;
enum tmc_config_type config_type;
enum tmc_mem_intf_width memwidth;
struct mutex mem_lock;
u32 trigger_cntr;
u32 etr_caps;
struct idr idr;
@ -217,6 +231,8 @@ struct tmc_drvdata {
struct coresight_csr *csr;
const char *csr_name;
u32 atid_offset;
enum tmc_etr_out_mode out_mode;
struct tmc_usb_data *usb_data;
};
struct etr_buf_operations {
@ -264,6 +280,8 @@ struct tmc_sg_table {
void tmc_wait_for_tmcready(struct tmc_drvdata *drvdata);
void tmc_flush_and_stop(struct tmc_drvdata *drvdata);
void tmc_enable_hw(struct tmc_drvdata *drvdata);
extern int tmc_etr_usb_init(struct amba_device *adev,
struct tmc_drvdata *drvdata);
void tmc_disable_hw(struct tmc_drvdata *drvdata);
u32 tmc_get_memwidth_mask(struct tmc_drvdata *drvdata);
@ -288,7 +306,8 @@ 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);
long tmc_get_rwp_offset(struct tmc_drvdata *drvdata);
int tmc_etr_switch_mode(struct tmc_drvdata *drvdata, const char *out_mode);
#define TMC_REG_PAIR(name, lo_off, hi_off) \
static inline u64 \