coresight: etm3x: adding operation mode for etm_enable()
[cascardo/linux.git] / drivers / hwtracing / coresight / coresight-etm3x.c
index 1952dc3..e16501b 100644 (file)
@@ -306,7 +306,7 @@ int etm_get_trace_id(struct etm_drvdata *drvdata)
        if (!drvdata)
                goto out;
 
-       if (!drvdata->enable)
+       if (!local_read(&drvdata->mode))
                return drvdata->traceid;
 
        pm_runtime_get_sync(drvdata->dev);
@@ -332,7 +332,7 @@ static int etm_trace_id(struct coresight_device *csdev)
        return etm_get_trace_id(drvdata);
 }
 
-static int etm_enable(struct coresight_device *csdev)
+static int etm_enable_sysfs(struct coresight_device *csdev)
 {
        struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
        int ret;
@@ -351,18 +351,44 @@ static int etm_enable(struct coresight_device *csdev)
                        goto err;
        }
 
-       drvdata->enable = true;
        drvdata->sticky_enable = true;
-
        spin_unlock(&drvdata->spinlock);
 
        dev_info(drvdata->dev, "ETM tracing enabled\n");
        return 0;
+
 err:
        spin_unlock(&drvdata->spinlock);
        return ret;
 }
 
+static int etm_enable(struct coresight_device *csdev, u32 mode)
+{
+       int ret;
+       u32 val;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       val = local_cmpxchg(&drvdata->mode, CS_MODE_DISABLED, mode);
+
+       /* Someone is already using the tracer */
+       if (val)
+               return -EBUSY;
+
+       switch (mode) {
+       case CS_MODE_SYSFS:
+               ret = etm_enable_sysfs(csdev);
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+       /* The tracer didn't start */
+       if (ret)
+               local_set(&drvdata->mode, CS_MODE_DISABLED);
+
+       return ret;
+}
+
 static void etm_disable_hw(void *info)
 {
        int i;
@@ -387,7 +413,7 @@ static void etm_disable_hw(void *info)
        dev_dbg(drvdata->dev, "cpu: %d disable smp call done\n", drvdata->cpu);
 }
 
-static void etm_disable(struct coresight_device *csdev)
+static void etm_disable_sysfs(struct coresight_device *csdev)
 {
        struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
 
@@ -405,7 +431,6 @@ static void etm_disable(struct coresight_device *csdev)
         * ensures that register writes occur when cpu is powered.
         */
        smp_call_function_single(drvdata->cpu, etm_disable_hw, drvdata, 1);
-       drvdata->enable = false;
 
        spin_unlock(&drvdata->spinlock);
        put_online_cpus();
@@ -413,6 +438,33 @@ static void etm_disable(struct coresight_device *csdev)
        dev_info(drvdata->dev, "ETM tracing disabled\n");
 }
 
+static void etm_disable(struct coresight_device *csdev)
+{
+       u32 mode;
+       struct etm_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent);
+
+       /*
+        * For as long as the tracer isn't disabled another entity can't
+        * change its status.  As such we can read the status here without
+        * fearing it will change under us.
+        */
+       mode = local_read(&drvdata->mode);
+
+       switch (mode) {
+       case CS_MODE_DISABLED:
+               break;
+       case CS_MODE_SYSFS:
+               etm_disable_sysfs(csdev);
+               break;
+       default:
+               WARN_ON_ONCE(mode);
+               return;
+       }
+
+       if (mode)
+               local_set(&drvdata->mode, CS_MODE_DISABLED);
+}
+
 static const struct coresight_ops_source etm_source_ops = {
        .cpu_id         = etm_cpu_id,
        .trace_id       = etm_trace_id,
@@ -440,7 +492,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
                        etmdrvdata[cpu]->os_unlock = true;
                }
 
-               if (etmdrvdata[cpu]->enable)
+               if (local_read(&etmdrvdata[cpu]->mode))
                        etm_enable_hw(etmdrvdata[cpu]);
                spin_unlock(&etmdrvdata[cpu]->spinlock);
                break;
@@ -453,7 +505,7 @@ static int etm_cpu_callback(struct notifier_block *nfb, unsigned long action,
 
        case CPU_DYING:
                spin_lock(&etmdrvdata[cpu]->spinlock);
-               if (etmdrvdata[cpu]->enable)
+               if (local_read(&etmdrvdata[cpu]->mode))
                        etm_disable_hw(etmdrvdata[cpu]);
                spin_unlock(&etmdrvdata[cpu]->spinlock);
                break;