static int tmc_enable(struct tmc_drvdata *drvdata, enum tmc_mode mode) { unsigned long flags; pm_runtime_get_sync(drvdata->dev); spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { spin_unlock_irqrestore(&drvdata->spinlock, flags); pm_runtime_put(drvdata->dev); return -EBUSY; } if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_enable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { tmc_etr_enable_hw(drvdata); } else { if (mode == TMC_MODE_CIRCULAR_BUFFER) tmc_etb_enable_hw(drvdata); else tmc_etf_enable_hw(drvdata); } drvdata->enable = true; spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC enabled\n"); return 0; }
static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, u32 mode) { int ret = 0; long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* This shouldn't be happening */ if (WARN_ON(mode != CS_MODE_PERF)) return -EINVAL; spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EINVAL; goto out; } val = local_xchg(&drvdata->mode, mode); /* * In Perf mode there can be only one writer per sink. There * is also no need to continue if the ETB/ETR is already operated * from sysFS. */ if (val != CS_MODE_DISABLED) { ret = -EINVAL; goto out; } tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; }
static int tmc_enable_etf_sink_perf(struct coresight_device *csdev) { int ret = 0; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); spin_lock_irqsave(&drvdata->spinlock, flags); if (drvdata->reading) { ret = -EINVAL; goto out; } /* * In Perf mode there can be only one writer per sink. There * is also no need to continue if the ETB/ETR is already operated * from sysFS. */ if (drvdata->mode != CS_MODE_DISABLED) { ret = -EINVAL; goto out; } drvdata->mode = CS_MODE_PERF; tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; }
static int tmc_enable_etf_sink_perf(struct coresight_device *csdev, void *data) { int ret = 0; pid_t pid; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); struct perf_output_handle *handle = data; spin_lock_irqsave(&drvdata->spinlock, flags); do { ret = -EINVAL; if (drvdata->reading) break; /* * No need to continue if the ETB/ETF is already operated * from sysFS. */ if (drvdata->mode == CS_MODE_SYSFS) { ret = -EBUSY; break; } /* Get a handle on the pid of the process to monitor */ pid = task_pid_nr(handle->event->owner); if (drvdata->pid != -1 && drvdata->pid != pid) { ret = -EBUSY; break; } ret = tmc_set_etf_buffer(csdev, handle); if (ret) break; /* * No HW configuration is needed if the sink is already in * use for this session. */ if (drvdata->pid == pid) { atomic_inc(csdev->refcnt); break; } ret = tmc_etb_enable_hw(drvdata); if (!ret) { /* Associate with monitored process. */ drvdata->pid = pid; drvdata->mode = CS_MODE_PERF; atomic_inc(csdev->refcnt); } } while (0); spin_unlock_irqrestore(&drvdata->spinlock, flags); return ret; }
int tmc_read_unprepare_etb(struct tmc_drvdata *drvdata) { char *buf = NULL; enum tmc_mode mode; unsigned long flags; /* config types are set a boot time and never change */ if (WARN_ON_ONCE(drvdata->config_type != TMC_CONFIG_TYPE_ETB && drvdata->config_type != TMC_CONFIG_TYPE_ETF)) return -EINVAL; spin_lock_irqsave(&drvdata->spinlock, flags); /* There is no point in reading a TMC in HW FIFO mode */ mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode != TMC_MODE_CIRCULAR_BUFFER) { spin_unlock_irqrestore(&drvdata->spinlock, flags); return -EINVAL; } /* Re-enable the TMC if need be */ if (local_read(&drvdata->mode) == CS_MODE_SYSFS) { /* * The trace run will continue with the same allocated trace * buffer. As such zero-out the buffer so that we don't end * up with stale data. * * Since the tracer is still enabled drvdata::buf * can't be NULL. */ memset(drvdata->buf, 0, drvdata->size); tmc_etb_enable_hw(drvdata); } else { /* * The ETB/ETF is not tracing and the buffer was just read. * As such prepare to free the trace buffer. */ buf = drvdata->buf; drvdata->buf = NULL; } drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); /* * Free allocated memory outside of the spinlock. There is no need * to assert the validity of 'buf' since calling kfree(NULL) is safe. */ kfree(buf); return 0; }
static void tmc_read_unprepare(struct tmc_drvdata *drvdata) { unsigned long flags; enum tmc_mode mode; spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->enable) goto out; if (drvdata->config_type == TMC_CONFIG_TYPE_ETB) { tmc_etb_enable_hw(drvdata); } else if (drvdata->config_type == TMC_CONFIG_TYPE_ETR) { tmc_etr_enable_hw(drvdata); } else { mode = readl_relaxed(drvdata->base + TMC_MODE); if (mode == TMC_MODE_CIRCULAR_BUFFER) tmc_etb_enable_hw(drvdata); } out: drvdata->reading = false; spin_unlock_irqrestore(&drvdata->spinlock, flags); dev_info(drvdata->dev, "TMC read end\n"); }
static int tmc_enable_etf_sink_sysfs(struct coresight_device *csdev, u32 mode) { int ret = 0; bool used = false; char *buf = NULL; long val; unsigned long flags; struct tmc_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); /* This shouldn't be happening */ if (WARN_ON(mode != CS_MODE_SYSFS)) return -EINVAL; /* * If we don't have a buffer release the lock and allocate memory. * Otherwise keep the lock and move along. */ spin_lock_irqsave(&drvdata->spinlock, flags); if (!drvdata->buf) { spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Allocating the memory here while outside of the spinlock */ buf = kzalloc(drvdata->size, GFP_KERNEL); if (!buf) return -ENOMEM; /* Let's try again */ spin_lock_irqsave(&drvdata->spinlock, flags); } if (drvdata->reading) { ret = -EBUSY; goto out; } val = local_xchg(&drvdata->mode, mode); /* * In sysFS mode we can have multiple writers per sink. Since this * sink is already enabled no memory is needed and the HW need not be * touched. */ if (val == CS_MODE_SYSFS) goto out; /* * If drvdata::buf isn't NULL, memory was allocated for a previous * trace run but wasn't read. If so simply zero-out the memory. * Otherwise use the memory allocated above. * * The memory is freed when users read the buffer using the * /dev/xyz.{etf|etb} interface. See tmc_read_unprepare_etf() for * details. */ if (drvdata->buf) { memset(drvdata->buf, 0, drvdata->size); } else { used = true; drvdata->buf = buf; } tmc_etb_enable_hw(drvdata); out: spin_unlock_irqrestore(&drvdata->spinlock, flags); /* Free memory outside the spinlock if need be */ if (!used && buf) kfree(buf); if (!ret) dev_info(drvdata->dev, "TMC-ETB/ETF enabled\n"); return ret; }