Loading drivers/hwtracing/coresight/coresight-tmc.c +376 −4 Original line number Diff line number Diff line Loading @@ -25,9 +25,13 @@ #include <linux/spinlock.h> #include <linux/clk.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/coresight.h> #include <linux/amba/bus.h> #include <asm/cacheflush.h> #include <linux/msm-sps.h> #include <linux/usb_bam.h> #include <linux/usb/usb_qdss.h> #include "coresight-priv.h" Loading Loading @@ -86,6 +90,9 @@ #define TMC_ETR_SG_NXT_TBL(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x3) #define TMC_ETR_SG_LST_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x1) #define TMC_ETR_BAM_PIPE_INDEX 0 #define TMC_ETR_BAM_NR_PIPES 2 enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, Loading Loading @@ -115,6 +122,31 @@ static const char * const str_tmc_etr_mem_type[] = { [TMC_ETR_MEM_TYPE_SG] = "sg", }; 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 tmc_etr_bam_data { struct sps_bam_props props; unsigned long handle; struct sps_pipe *pipe; struct sps_connect connect; uint32_t src_pipe_idx; unsigned long dest; uint32_t dest_pipe_idx; struct sps_mem_buffer desc_fifo; struct sps_mem_buffer data_fifo; bool enable; }; /** * struct tmc_drvdata - specifics associated to an TMC component * @base: memory mapped base address for this component. Loading Loading @@ -154,6 +186,10 @@ struct tmc_drvdata { enum tmc_etr_mem_type memtype; u32 delta_bottom; int sg_blk_num; enum tmc_etr_out_mode out_mode; struct usb_qdss_ch *usbch; struct tmc_etr_bam_data *bamdata; bool enable_to_bam; }; static void tmc_wait_for_ready(struct tmc_drvdata *drvdata) Loading Loading @@ -533,6 +569,175 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } static void tmc_etr_fill_usb_bam_data(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; get_qdss_bam_connection_info(&bamdata->dest, &bamdata->dest_pipe_idx, &bamdata->src_pipe_idx, &bamdata->desc_fifo, &bamdata->data_fifo, NULL); } static void __tmc_etr_enable_to_bam(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; uint32_t axictl; if (drvdata->enable_to_bam) return; /* Configure and enable required CSR registers */ msm_qdss_csr_enable_bam_to_usb(); /* Configure and enable ETR for usb bam output */ CS_UNLOCK(drvdata->base); writel_relaxed(bamdata->data_fifo.size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); axictl |= (0xF << 8); writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl &= ~(0x1 << 7); writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl = (axictl & ~0x3) | 0x2; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); writel_relaxed((uint32_t)bamdata->data_fifo.phys_base, drvdata->base + TMC_DBALO); writel_relaxed((((uint64_t)bamdata->data_fifo.phys_base) >> 32) & 0xFF, drvdata->base + TMC_DBAHI); /* Set FOnFlIn for periodic flush */ writel_relaxed(0x133, drvdata->base + TMC_FFCR); writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); tmc_enable_hw(drvdata); CS_LOCK(drvdata->base); drvdata->enable_to_bam = true; } static int tmc_etr_bam_enable(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; int ret; if (bamdata->enable) return 0; /* Reset bam to start with */ ret = sps_device_reset(bamdata->handle); if (ret) goto err0; /* Now configure and enable bam */ bamdata->pipe = sps_alloc_endpoint(); if (!bamdata->pipe) return -ENOMEM; ret = sps_get_config(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->connect.mode = SPS_MODE_SRC; bamdata->connect.source = bamdata->handle; bamdata->connect.event_thresh = 0x4; bamdata->connect.src_pipe_index = TMC_ETR_BAM_PIPE_INDEX; bamdata->connect.options = SPS_O_AUTO_ENABLE; bamdata->connect.destination = bamdata->dest; bamdata->connect.dest_pipe_index = bamdata->dest_pipe_idx; bamdata->connect.desc = bamdata->desc_fifo; bamdata->connect.data = bamdata->data_fifo; ret = sps_connect(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->enable = true; return 0; err1: sps_free_endpoint(bamdata->pipe); err0: return ret; } static void tmc_wait_for_flush(struct tmc_drvdata *drvdata) { int count; /* Ensure no flush is in progress */ for (count = TIMEOUT_US; BVAL(readl_relaxed(drvdata->base + TMC_FFSR), 0) != 0 && count > 0; count--) udelay(1); WARN(count == 0, "timeout while waiting for TMC flush, TMC_FFSR: %#x\n", readl_relaxed(drvdata->base + TMC_FFSR)); } static void __tmc_etr_disable_to_bam(struct tmc_drvdata *drvdata) { if (!drvdata->enable_to_bam) return; /* Ensure periodic flush is disabled in CSR block */ msm_qdss_csr_disable_flush(); CS_UNLOCK(drvdata->base); tmc_wait_for_flush(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata); /* Disable CSR configuration */ msm_qdss_csr_disable_bam_to_usb(); drvdata->enable_to_bam = false; } static void tmc_etr_bam_disable(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; if (!bamdata->enable) return; sps_disconnect(bamdata->pipe); sps_free_endpoint(bamdata->pipe); bamdata->enable = false; } static void usb_notifier(void *priv, unsigned int event, struct qdss_request *d_req, struct usb_qdss_ch *ch) { struct tmc_drvdata *drvdata = priv; unsigned long flags; int ret = 0; mutex_lock(&drvdata->mem_lock); if (event == USB_QDSS_CONNECT) { tmc_etr_fill_usb_bam_data(drvdata); ret = tmc_etr_bam_enable(drvdata); if (ret) dev_err(drvdata->dev, "ETR BAM enable failed\n"); spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_enable_to_bam(drvdata); spin_unlock_irqrestore(&drvdata->spinlock, flags); } else if (event == USB_QDSS_DISCONNECT) { spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_disable_to_bam(drvdata); spin_unlock_irqrestore(&drvdata->spinlock, flags); tmc_etr_bam_disable(drvdata); } mutex_unlock(&drvdata->mem_lock); } static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { int ret; Loading @@ -543,7 +748,8 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) return ret; mutex_lock(&drvdata->mem_lock); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) { /* * ETR DDR memory is not allocated until user enables * tmc at least once. If user specifies different ETR Loading @@ -564,12 +770,26 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) mutex_unlock(&drvdata->mem_lock); return ret; } } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) { drvdata->usbch = usb_qdss_open("qdss", drvdata, usb_notifier); if (IS_ERR(drvdata->usbch)) { dev_err(drvdata->dev, "usb_qdss_open failed\n"); ret = PTR_ERR(drvdata->usbch); clk_disable_unprepare(drvdata->clk); mutex_unlock(&drvdata->mem_lock); return ret; } } mutex_unlock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) usb_qdss_close(drvdata->usbch); clk_disable_unprepare(drvdata->clk); return -EBUSY; } Loading @@ -577,6 +797,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_enable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) tmc_etr_enable_hw(drvdata); } else { if (mode == TMC_MODE_CIRCULAR_BUFFER) Loading Loading @@ -760,6 +981,9 @@ static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_disable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) __tmc_etr_disable_to_bam(drvdata); else tmc_etr_disable_hw(drvdata); } else { if (mode == TMC_MODE_CIRCULAR_BUFFER) Loading @@ -771,6 +995,12 @@ out: drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) { tmc_etr_bam_disable(drvdata); usb_qdss_close(drvdata->usbch); } clk_disable_unprepare(drvdata->clk); dev_info(drvdata->dev, "TMC disabled\n"); Loading Loading @@ -1070,6 +1300,47 @@ out: return 0; } static int tmc_etr_bam_init(struct amba_device *adev, struct tmc_drvdata *drvdata) { int ret; struct device *dev = &adev->dev; struct resource res; struct tmc_etr_bam_data *bamdata; bamdata = devm_kzalloc(dev, sizeof(*bamdata), GFP_KERNEL); if (!bamdata) return -ENOMEM; drvdata->bamdata = bamdata; ret = of_address_to_resource(adev->dev.of_node, 1, &res); if (ret) return -ENODEV; bamdata->props.phys_addr = res.start; bamdata->props.virt_addr = devm_ioremap(dev, res.start, resource_size(&res)); if (!bamdata->props.virt_addr) return -ENOMEM; bamdata->props.virt_size = resource_size(&res); bamdata->props.event_threshold = 0x4; /* Pipe event threshold */ bamdata->props.summing_threshold = 0x10; /* BAM event threshold */ bamdata->props.irq = 0; bamdata->props.num_pipes = TMC_ETR_BAM_NR_PIPES; return sps_register_bam_device(&bamdata->props, &bamdata->handle); } static void tmc_etr_bam_exit(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; if (!bamdata->handle) return; sps_deregister_bam_device(bamdata->handle); } static const struct file_operations tmc_fops = { .owner = THIS_MODULE, .open = tmc_open, Loading Loading @@ -1224,6 +1495,99 @@ static ssize_t mem_type_store(struct device *dev, } static DEVICE_ATTR_RW(mem_type); 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] = ""; unsigned long flags; int ret; if (strlen(buf) >= 10) return -EINVAL; if (sscanf(buf, "%s", str) != 1) return -EINVAL; mutex_lock(&drvdata->mem_lock); if (!strcmp(str, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_MEM])) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) goto out; spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->enable) { drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; } __tmc_etr_disable_to_bam(drvdata); tmc_etr_enable_hw(drvdata); drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; spin_unlock_irqrestore(&drvdata->spinlock, flags); tmc_etr_bam_disable(drvdata); usb_qdss_close(drvdata->usbch); } else if (!strcmp(str, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_USB])) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) goto out; spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->enable) { drvdata->out_mode = TMC_ETR_OUT_MODE_USB; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; } if (drvdata->reading) { ret = -EBUSY; goto err1; } tmc_etr_disable_hw(drvdata); drvdata->out_mode = TMC_ETR_OUT_MODE_USB; spin_unlock_irqrestore(&drvdata->spinlock, flags); drvdata->usbch = usb_qdss_open("qdss", drvdata, usb_notifier); if (IS_ERR(drvdata->usbch)) { dev_err(drvdata->dev, "usb_qdss_open failed\n"); ret = PTR_ERR(drvdata->usbch); goto err0; } } out: mutex_unlock(&drvdata->mem_lock); return size; err1: spin_unlock_irqrestore(&drvdata->spinlock, flags); err0: mutex_unlock(&drvdata->mem_lock); return ret; } static DEVICE_ATTR_RW(out_mode); static ssize_t available_out_modes_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; int i; for (i = 0; i < ARRAY_SIZE(str_tmc_etr_out_mode); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", str_tmc_etr_out_mode[i]); len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } static DEVICE_ATTR_RO(available_out_modes); static struct attribute *coresight_etb_attrs[] = { &dev_attr_trigger_cntr.attr, &dev_attr_status.attr, Loading @@ -1232,8 +1596,10 @@ static struct attribute *coresight_etb_attrs[] = { ATTRIBUTE_GROUPS(coresight_etb); static struct attribute *coresight_etr_attrs[] = { &dev_attr_available_out_modes.attr, &dev_attr_mem_size.attr, &dev_attr_mem_type.attr, &dev_attr_out_mode.attr, &dev_attr_trigger_cntr.attr, &dev_attr_status.attr, NULL, Loading Loading @@ -1292,6 +1658,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->config_type = BMVAL(devid, 6, 7); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; if (np) ret = of_property_read_u32(np, "arm,buffer-size", Loading @@ -1308,7 +1675,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) clk_disable_unprepare(drvdata->clk); if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ret = tmc_etr_bam_init(adev, drvdata); if (ret) return ret; } else { drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL); if (!drvdata->buf) return -ENOMEM; Loading Loading @@ -1364,6 +1735,7 @@ static int tmc_remove(struct amba_device *adev) coresight_unregister(drvdata->csdev); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) tmc_etr_free_mem(drvdata); tmc_etr_bam_exit(drvdata); return 0; } Loading Loading
drivers/hwtracing/coresight/coresight-tmc.c +376 −4 Original line number Diff line number Diff line Loading @@ -25,9 +25,13 @@ #include <linux/spinlock.h> #include <linux/clk.h> #include <linux/of.h> #include <linux/of_address.h> #include <linux/coresight.h> #include <linux/amba/bus.h> #include <asm/cacheflush.h> #include <linux/msm-sps.h> #include <linux/usb_bam.h> #include <linux/usb/usb_qdss.h> #include "coresight-priv.h" Loading Loading @@ -86,6 +90,9 @@ #define TMC_ETR_SG_NXT_TBL(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x3) #define TMC_ETR_SG_LST_ENT(phys_pte) (((phys_pte >> PAGE_SHIFT) << 4) | 0x1) #define TMC_ETR_BAM_PIPE_INDEX 0 #define TMC_ETR_BAM_NR_PIPES 2 enum tmc_config_type { TMC_CONFIG_TYPE_ETB, TMC_CONFIG_TYPE_ETR, Loading Loading @@ -115,6 +122,31 @@ static const char * const str_tmc_etr_mem_type[] = { [TMC_ETR_MEM_TYPE_SG] = "sg", }; 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 tmc_etr_bam_data { struct sps_bam_props props; unsigned long handle; struct sps_pipe *pipe; struct sps_connect connect; uint32_t src_pipe_idx; unsigned long dest; uint32_t dest_pipe_idx; struct sps_mem_buffer desc_fifo; struct sps_mem_buffer data_fifo; bool enable; }; /** * struct tmc_drvdata - specifics associated to an TMC component * @base: memory mapped base address for this component. Loading Loading @@ -154,6 +186,10 @@ struct tmc_drvdata { enum tmc_etr_mem_type memtype; u32 delta_bottom; int sg_blk_num; enum tmc_etr_out_mode out_mode; struct usb_qdss_ch *usbch; struct tmc_etr_bam_data *bamdata; bool enable_to_bam; }; static void tmc_wait_for_ready(struct tmc_drvdata *drvdata) Loading Loading @@ -533,6 +569,175 @@ static void tmc_etf_enable_hw(struct tmc_drvdata *drvdata) CS_LOCK(drvdata->base); } static void tmc_etr_fill_usb_bam_data(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; get_qdss_bam_connection_info(&bamdata->dest, &bamdata->dest_pipe_idx, &bamdata->src_pipe_idx, &bamdata->desc_fifo, &bamdata->data_fifo, NULL); } static void __tmc_etr_enable_to_bam(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; uint32_t axictl; if (drvdata->enable_to_bam) return; /* Configure and enable required CSR registers */ msm_qdss_csr_enable_bam_to_usb(); /* Configure and enable ETR for usb bam output */ CS_UNLOCK(drvdata->base); writel_relaxed(bamdata->data_fifo.size / 4, drvdata->base + TMC_RSZ); writel_relaxed(TMC_MODE_CIRCULAR_BUFFER, drvdata->base + TMC_MODE); axictl = readl_relaxed(drvdata->base + TMC_AXICTL); axictl |= (0xF << 8); writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl &= ~(0x1 << 7); writel_relaxed(axictl, drvdata->base + TMC_AXICTL); axictl = (axictl & ~0x3) | 0x2; writel_relaxed(axictl, drvdata->base + TMC_AXICTL); writel_relaxed((uint32_t)bamdata->data_fifo.phys_base, drvdata->base + TMC_DBALO); writel_relaxed((((uint64_t)bamdata->data_fifo.phys_base) >> 32) & 0xFF, drvdata->base + TMC_DBAHI); /* Set FOnFlIn for periodic flush */ writel_relaxed(0x133, drvdata->base + TMC_FFCR); writel_relaxed(drvdata->trigger_cntr, drvdata->base + TMC_TRG); tmc_enable_hw(drvdata); CS_LOCK(drvdata->base); drvdata->enable_to_bam = true; } static int tmc_etr_bam_enable(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; int ret; if (bamdata->enable) return 0; /* Reset bam to start with */ ret = sps_device_reset(bamdata->handle); if (ret) goto err0; /* Now configure and enable bam */ bamdata->pipe = sps_alloc_endpoint(); if (!bamdata->pipe) return -ENOMEM; ret = sps_get_config(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->connect.mode = SPS_MODE_SRC; bamdata->connect.source = bamdata->handle; bamdata->connect.event_thresh = 0x4; bamdata->connect.src_pipe_index = TMC_ETR_BAM_PIPE_INDEX; bamdata->connect.options = SPS_O_AUTO_ENABLE; bamdata->connect.destination = bamdata->dest; bamdata->connect.dest_pipe_index = bamdata->dest_pipe_idx; bamdata->connect.desc = bamdata->desc_fifo; bamdata->connect.data = bamdata->data_fifo; ret = sps_connect(bamdata->pipe, &bamdata->connect); if (ret) goto err1; bamdata->enable = true; return 0; err1: sps_free_endpoint(bamdata->pipe); err0: return ret; } static void tmc_wait_for_flush(struct tmc_drvdata *drvdata) { int count; /* Ensure no flush is in progress */ for (count = TIMEOUT_US; BVAL(readl_relaxed(drvdata->base + TMC_FFSR), 0) != 0 && count > 0; count--) udelay(1); WARN(count == 0, "timeout while waiting for TMC flush, TMC_FFSR: %#x\n", readl_relaxed(drvdata->base + TMC_FFSR)); } static void __tmc_etr_disable_to_bam(struct tmc_drvdata *drvdata) { if (!drvdata->enable_to_bam) return; /* Ensure periodic flush is disabled in CSR block */ msm_qdss_csr_disable_flush(); CS_UNLOCK(drvdata->base); tmc_wait_for_flush(drvdata); tmc_disable_hw(drvdata); CS_LOCK(drvdata); /* Disable CSR configuration */ msm_qdss_csr_disable_bam_to_usb(); drvdata->enable_to_bam = false; } static void tmc_etr_bam_disable(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; if (!bamdata->enable) return; sps_disconnect(bamdata->pipe); sps_free_endpoint(bamdata->pipe); bamdata->enable = false; } static void usb_notifier(void *priv, unsigned int event, struct qdss_request *d_req, struct usb_qdss_ch *ch) { struct tmc_drvdata *drvdata = priv; unsigned long flags; int ret = 0; mutex_lock(&drvdata->mem_lock); if (event == USB_QDSS_CONNECT) { tmc_etr_fill_usb_bam_data(drvdata); ret = tmc_etr_bam_enable(drvdata); if (ret) dev_err(drvdata->dev, "ETR BAM enable failed\n"); spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_enable_to_bam(drvdata); spin_unlock_irqrestore(&drvdata->spinlock, flags); } else if (event == USB_QDSS_DISCONNECT) { spin_lock_irqsave(&drvdata->spinlock, flags); __tmc_etr_disable_to_bam(drvdata); spin_unlock_irqrestore(&drvdata->spinlock, flags); tmc_etr_bam_disable(drvdata); } mutex_unlock(&drvdata->mem_lock); } static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { int ret; Loading @@ -543,7 +748,8 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) return ret; mutex_lock(&drvdata->mem_lock); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) { /* * ETR DDR memory is not allocated until user enables * tmc at least once. If user specifies different ETR Loading @@ -564,12 +770,26 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) mutex_unlock(&drvdata->mem_lock); return ret; } } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) { drvdata->usbch = usb_qdss_open("qdss", drvdata, usb_notifier); if (IS_ERR(drvdata->usbch)) { dev_err(drvdata->dev, "usb_qdss_open failed\n"); ret = PTR_ERR(drvdata->usbch); clk_disable_unprepare(drvdata->clk); mutex_unlock(&drvdata->mem_lock); return ret; } } mutex_unlock(&drvdata->mem_lock); spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) usb_qdss_close(drvdata->usbch); clk_disable_unprepare(drvdata->clk); return -EBUSY; } Loading @@ -577,6 +797,7 @@ static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_enable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) tmc_etr_enable_hw(drvdata); } else { if (mode == TMC_MODE_CIRCULAR_BUFFER) Loading Loading @@ -760,6 +981,9 @@ static void tmc_disable(struct tmc_drvdata *drvdata, enum tmc_mode mode) if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_disable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) __tmc_etr_disable_to_bam(drvdata); else tmc_etr_disable_hw(drvdata); } else { if (mode == TMC_MODE_CIRCULAR_BUFFER) Loading @@ -771,6 +995,12 @@ out: drvdata->enable = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR && drvdata->out_mode == TMC_ETR_OUT_MODE_USB) { tmc_etr_bam_disable(drvdata); usb_qdss_close(drvdata->usbch); } clk_disable_unprepare(drvdata->clk); dev_info(drvdata->dev, "TMC disabled\n"); Loading Loading @@ -1070,6 +1300,47 @@ out: return 0; } static int tmc_etr_bam_init(struct amba_device *adev, struct tmc_drvdata *drvdata) { int ret; struct device *dev = &adev->dev; struct resource res; struct tmc_etr_bam_data *bamdata; bamdata = devm_kzalloc(dev, sizeof(*bamdata), GFP_KERNEL); if (!bamdata) return -ENOMEM; drvdata->bamdata = bamdata; ret = of_address_to_resource(adev->dev.of_node, 1, &res); if (ret) return -ENODEV; bamdata->props.phys_addr = res.start; bamdata->props.virt_addr = devm_ioremap(dev, res.start, resource_size(&res)); if (!bamdata->props.virt_addr) return -ENOMEM; bamdata->props.virt_size = resource_size(&res); bamdata->props.event_threshold = 0x4; /* Pipe event threshold */ bamdata->props.summing_threshold = 0x10; /* BAM event threshold */ bamdata->props.irq = 0; bamdata->props.num_pipes = TMC_ETR_BAM_NR_PIPES; return sps_register_bam_device(&bamdata->props, &bamdata->handle); } static void tmc_etr_bam_exit(struct tmc_drvdata *drvdata) { struct tmc_etr_bam_data *bamdata = drvdata->bamdata; if (!bamdata->handle) return; sps_deregister_bam_device(bamdata->handle); } static const struct file_operations tmc_fops = { .owner = THIS_MODULE, .open = tmc_open, Loading Loading @@ -1224,6 +1495,99 @@ static ssize_t mem_type_store(struct device *dev, } static DEVICE_ATTR_RW(mem_type); 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] = ""; unsigned long flags; int ret; if (strlen(buf) >= 10) return -EINVAL; if (sscanf(buf, "%s", str) != 1) return -EINVAL; mutex_lock(&drvdata->mem_lock); if (!strcmp(str, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_MEM])) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_MEM) goto out; spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->enable) { drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; } __tmc_etr_disable_to_bam(drvdata); tmc_etr_enable_hw(drvdata); drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; spin_unlock_irqrestore(&drvdata->spinlock, flags); tmc_etr_bam_disable(drvdata); usb_qdss_close(drvdata->usbch); } else if (!strcmp(str, str_tmc_etr_out_mode[TMC_ETR_OUT_MODE_USB])) { if (drvdata->out_mode == TMC_ETR_OUT_MODE_USB) goto out; spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->enable) { drvdata->out_mode = TMC_ETR_OUT_MODE_USB; spin_unlock_irqrestore(&drvdata->spinlock, flags); goto out; } if (drvdata->reading) { ret = -EBUSY; goto err1; } tmc_etr_disable_hw(drvdata); drvdata->out_mode = TMC_ETR_OUT_MODE_USB; spin_unlock_irqrestore(&drvdata->spinlock, flags); drvdata->usbch = usb_qdss_open("qdss", drvdata, usb_notifier); if (IS_ERR(drvdata->usbch)) { dev_err(drvdata->dev, "usb_qdss_open failed\n"); ret = PTR_ERR(drvdata->usbch); goto err0; } } out: mutex_unlock(&drvdata->mem_lock); return size; err1: spin_unlock_irqrestore(&drvdata->spinlock, flags); err0: mutex_unlock(&drvdata->mem_lock); return ret; } static DEVICE_ATTR_RW(out_mode); static ssize_t available_out_modes_show(struct device *dev, struct device_attribute *attr, char *buf) { ssize_t len = 0; int i; for (i = 0; i < ARRAY_SIZE(str_tmc_etr_out_mode); i++) len += scnprintf(buf + len, PAGE_SIZE - len, "%s ", str_tmc_etr_out_mode[i]); len += scnprintf(buf + len, PAGE_SIZE - len, "\n"); return len; } static DEVICE_ATTR_RO(available_out_modes); static struct attribute *coresight_etb_attrs[] = { &dev_attr_trigger_cntr.attr, &dev_attr_status.attr, Loading @@ -1232,8 +1596,10 @@ static struct attribute *coresight_etb_attrs[] = { ATTRIBUTE_GROUPS(coresight_etb); static struct attribute *coresight_etr_attrs[] = { &dev_attr_available_out_modes.attr, &dev_attr_mem_size.attr, &dev_attr_mem_type.attr, &dev_attr_out_mode.attr, &dev_attr_trigger_cntr.attr, &dev_attr_status.attr, NULL, Loading Loading @@ -1292,6 +1658,7 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) drvdata->config_type = BMVAL(devid, 6, 7); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { drvdata->out_mode = TMC_ETR_OUT_MODE_MEM; if (np) ret = of_property_read_u32(np, "arm,buffer-size", Loading @@ -1308,7 +1675,11 @@ static int tmc_probe(struct amba_device *adev, const struct amba_id *id) clk_disable_unprepare(drvdata->clk); if (drvdata->config_type != TMC_CONFIG_TYPE_ETR) { if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { ret = tmc_etr_bam_init(adev, drvdata); if (ret) return ret; } else { drvdata->buf = devm_kzalloc(dev, drvdata->size, GFP_KERNEL); if (!drvdata->buf) return -ENOMEM; Loading Loading @@ -1364,6 +1735,7 @@ static int tmc_remove(struct amba_device *adev) coresight_unregister(drvdata->csdev); if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) tmc_etr_free_mem(drvdata); tmc_etr_bam_exit(drvdata); return 0; } Loading