* Description: CoreSight Embedded Trace Buffer driver
*/
-#include <asm/local.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/types.h>
* @miscdev: specifics to handle "/dev/xyz.etb" entry.
* @spinlock: only one at a time pls.
* @reading: synchronise user space access to etb buffer.
- * @mode: this ETB is being used.
* @buf: area of memory where ETB buffer content gets sent.
+ * @mode: this ETB is being used.
* @buffer_depth: size of @buf.
* @trigger_cntr: amount of words to store after a trigger.
*/
struct miscdevice miscdev;
spinlock_t spinlock;
local_t reading;
- local_t mode;
u8 *buf;
+ u32 mode;
u32 buffer_depth;
u32 trigger_cntr;
};
static int etb_enable(struct coresight_device *csdev, u32 mode, void *data)
{
int ret = 0;
- u32 val;
unsigned long flags;
struct etb_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
- /*
- * We don't have an internal state to clean up if we fail to setup
- * the perf buffer. So we can perform the step before we turn the
- * ETB on and leave without cleaning up.
- */
- if (mode == CS_MODE_PERF) {
- ret = etb_set_buffer(csdev, (struct perf_output_handle *)data);
- if (ret)
- goto out;
- }
+ spin_lock_irqsave(&drvdata->spinlock, flags);
- val = local_cmpxchg(&drvdata->mode,
- CS_MODE_DISABLED, mode);
/*
* When accessing from Perf, a HW buffer can be handled
* by a single trace entity. In sysFS mode many tracers
* can be logging to the same HW buffer.
*/
- if (val == CS_MODE_PERF)
- return -EBUSY;
+ if (drvdata->mode == CS_MODE_PERF) {
+ ret = -EBUSY;
+ goto out;
+ }
/* Don't let perf disturb sysFS sessions */
- if (val == CS_MODE_SYSFS && mode == CS_MODE_PERF)
- return -EBUSY;
+ if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_PERF) {
+ ret = -EBUSY;
+ goto out;
+ }
/* Nothing to do, the tracer is already enabled. */
- if (val == CS_MODE_SYSFS)
+ if (drvdata->mode == CS_MODE_SYSFS && mode == CS_MODE_SYSFS)
goto out;
- spin_lock_irqsave(&drvdata->spinlock, flags);
+ /*
+ * We don't have an internal state to clean up if we fail to setup
+ * the perf buffer. So we can perform the step before we turn the
+ * ETB on and leave without cleaning up.
+ */
+ if (mode == CS_MODE_PERF) {
+ ret = etb_set_buffer(csdev, (struct perf_output_handle *)data);
+ if (ret)
+ goto out;
+ }
+
+ drvdata->mode = mode;
etb_enable_hw(drvdata);
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
out:
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
+
if (!ret)
dev_dbg(drvdata->dev, "ETB enabled\n");
return ret;
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
- etb_disable_hw(drvdata);
- etb_dump_hw(drvdata);
- spin_unlock_irqrestore(&drvdata->spinlock, flags);
- local_set(&drvdata->mode, CS_MODE_DISABLED);
+ /* Disable the ETB only if it needs to */
+ if (drvdata->mode != CS_MODE_DISABLED) {
+ etb_disable_hw(drvdata);
+ etb_dump_hw(drvdata);
+ drvdata->mode = CS_MODE_DISABLED;
+ }
+ spin_unlock_irqrestore(&drvdata->spinlock, flags);
dev_dbg(drvdata->dev, "ETB disabled\n");
}
unsigned long flags;
spin_lock_irqsave(&drvdata->spinlock, flags);
- if (local_read(&drvdata->mode) == CS_MODE_SYSFS) {
+ if (drvdata->mode == CS_MODE_SYSFS) {
etb_disable_hw(drvdata);
etb_dump_hw(drvdata);
etb_enable_hw(drvdata);