From 13dcc9a7c7240f61cce2c1f960d7fed91d3e3cf7 Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Tue, 23 Nov 2021 14:01:37 +0800 Subject: [PATCH] coresight-tmc: Add usb support to coresight tmc etr Add support to transfer trace data from qdss to usb. Use SW path to transfer data from QDSS to USB. Change-Id: I5ea589e0f7051d850addb4ead2812a614bb7b0f2 Signed-off-by: Tao Zhang Signed-off-by: Mao Jinlong --- drivers/hwtracing/coresight/Makefile | 3 +- .../hwtracing/coresight/coresight-byte-cntr.c | 87 ++-- .../hwtracing/coresight/coresight-byte-cntr.h | 7 + drivers/hwtracing/coresight/coresight-core.c | 50 +- .../hwtracing/coresight/coresight-platform.c | 23 +- drivers/hwtracing/coresight/coresight-priv.h | 3 +- .../hwtracing/coresight/coresight-tmc-core.c | 82 +++- .../hwtracing/coresight/coresight-tmc-etf.c | 3 + .../hwtracing/coresight/coresight-tmc-etr.c | 258 ++++++++-- .../hwtracing/coresight/coresight-tmc-usb.c | 463 ++++++++++++++++++ .../hwtracing/coresight/coresight-tmc-usb.h | 29 ++ drivers/hwtracing/coresight/coresight-tmc.h | 21 +- 12 files changed, 933 insertions(+), 96 deletions(-) create mode 100644 drivers/hwtracing/coresight/coresight-tmc-usb.c create mode 100644 drivers/hwtracing/coresight/coresight-tmc-usb.h diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile index 82457dd69c1e..867e868ba757 100644 --- a/drivers/hwtracing/coresight/Makefile +++ b/drivers/hwtracing/coresight/Makefile @@ -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 \ diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.c b/drivers/hwtracing/coresight/coresight-byte-cntr.c index 3433e433a390..80f2a3f6322c 100644 --- a/drivers/hwtracing/coresight/coresight-byte-cntr.c +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.c @@ -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 */ diff --git a/drivers/hwtracing/coresight/coresight-byte-cntr.h b/drivers/hwtracing/coresight/coresight-byte-cntr.h index 470fb73dcfdd..3380fda029f8 100644 --- a/drivers/hwtracing/coresight/coresight-byte-cntr.h +++ b/drivers/hwtracing/coresight/coresight-byte-cntr.h @@ -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); diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index 2eb6dfa3f4f4..e360ece1bd8b 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -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)); diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index b24de3445636..07c7370562eb 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -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); } /* diff --git a/drivers/hwtracing/coresight/coresight-priv.h b/drivers/hwtracing/coresight/coresight-priv.h index bc4404078abc..740549d751ae 100644 --- a/drivers/hwtracing/coresight/coresight-priv.h +++ b/drivers/hwtracing/coresight/coresight-priv.h @@ -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 diff --git a/drivers/hwtracing/coresight/coresight-tmc-core.c b/drivers/hwtracing/coresight/coresight-tmc-core.c index 11fc1e3b3163..111a3ee03846 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-core.c +++ b/drivers/hwtracing/coresight/coresight-tmc-core.c @@ -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); /* diff --git a/drivers/hwtracing/coresight/coresight-tmc-etf.c b/drivers/hwtracing/coresight/coresight-tmc-etf.c index 4c4cbd1f7258..e430e340138f 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etf.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etf.c @@ -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; diff --git a/drivers/hwtracing/coresight/coresight-tmc-etr.c b/drivers/hwtracing/coresight/coresight-tmc-etr.c index f9d2dd5938eb..8724bb349944 100644 --- a/drivers/hwtracing/coresight/coresight-tmc-etr.c +++ b/drivers/hwtracing/coresight/coresight-tmc-etr.c @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -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; } diff --git a/drivers/hwtracing/coresight/coresight-tmc-usb.c b/drivers/hwtracing/coresight/coresight-tmc-usb.c new file mode 100644 index 000000000000..5269ac139ce8 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-usb.c @@ -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 +#include +#include +#include +#include +#include +#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; +} diff --git a/drivers/hwtracing/coresight/coresight-tmc-usb.h b/drivers/hwtracing/coresight/coresight-tmc-usb.h new file mode 100644 index 000000000000..ac5f87525879 --- /dev/null +++ b/drivers/hwtracing/coresight/coresight-tmc-usb.h @@ -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 +#include + +#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 diff --git a/drivers/hwtracing/coresight/coresight-tmc.h b/drivers/hwtracing/coresight/coresight-tmc.h index 7162fd12a02f..5fb9c7e72707 100644 --- a/drivers/hwtracing/coresight/coresight-tmc.h +++ b/drivers/hwtracing/coresight/coresight-tmc.h @@ -14,6 +14,7 @@ #include #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 \