diff --git a/drivers/hwtracing/coresight/Kconfig b/drivers/hwtracing/coresight/Kconfig
index 8fd9fd139cf3..d6e107bbd30b 100644
--- a/drivers/hwtracing/coresight/Kconfig
+++ b/drivers/hwtracing/coresight/Kconfig
@@ -78,7 +78,7 @@ config CORESIGHT_SOURCE_ETM3X
 	  module will be called coresight-etm3x.
 
 config CORESIGHT_SOURCE_ETM4X
-	bool "CoreSight Embedded Trace Macrocell 4.x driver"
+	tristate "CoreSight Embedded Trace Macrocell 4.x driver"
 	depends on ARM64
 	select CORESIGHT_LINKS_AND_SINKS
 	select PID_IN_CONTEXTIDR
@@ -88,6 +88,9 @@ config CORESIGHT_SOURCE_ETM4X
 	  for instruction level tracing. Depending on the implemented version
 	  data tracing may also be available.
 
+	  To compile this driver as a module, choose M here: the
+	  module will be called coresight-etm4x.
+
 config CORESIGHT_STM
 	tristate "CoreSight System Trace Macrocell driver"
 	depends on (ARM && !(CPU_32v3 || CPU_32v4 || CPU_32v4T)) || ARM64
diff --git a/drivers/hwtracing/coresight/Makefile b/drivers/hwtracing/coresight/Makefile
index d619cfd0abd8..271dc255454f 100644
--- a/drivers/hwtracing/coresight/Makefile
+++ b/drivers/hwtracing/coresight/Makefile
@@ -14,8 +14,8 @@ obj-$(CONFIG_CORESIGHT_LINKS_AND_SINKS) += coresight-funnel.o \
 obj-$(CONFIG_CORESIGHT_SOURCE_ETM3X) += coresight-etm3x.o
 coresight-etm3x-y := coresight-etm3x-core.o coresight-etm-cp14.o \
 					coresight-etm3x-sysfs.o
-obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o \
-					coresight-etm4x-sysfs.o
+obj-$(CONFIG_CORESIGHT_SOURCE_ETM4X) += coresight-etm4x.o
+coresight-etm4x-y := coresight-etm4x-core.o coresight-etm4x-sysfs.o
 obj-$(CONFIG_CORESIGHT_STM) += coresight-stm.o
 obj-$(CONFIG_CORESIGHT_CPU_DEBUG) += coresight-cpu-debug.o
 obj-$(CONFIG_CORESIGHT_CATU) += coresight-catu.o
diff --git a/drivers/hwtracing/coresight/coresight-etm4x.c b/drivers/hwtracing/coresight/coresight-etm4x-core.c
similarity index 95%
rename from drivers/hwtracing/coresight/coresight-etm4x.c
rename to drivers/hwtracing/coresight/coresight-etm4x-core.c
index fddfd93b9a7b..ae9847e194a3 100644
--- a/drivers/hwtracing/coresight/coresight-etm4x.c
+++ b/drivers/hwtracing/coresight/coresight-etm4x-core.c
@@ -48,6 +48,7 @@ module_param(pm_save_enable, int, 0444);
 MODULE_PARM_DESC(pm_save_enable,
 	"Save/restore state on power down: 1 = never, 2 = self-hosted");
 
+static DEFINE_PER_CPU(struct mutex, etmdrvdata_lock);
 static struct etmv4_drvdata *etmdrvdata[NR_CPUS];
 static void etm4_set_default_config(struct etmv4_config *config);
 static int etm4_set_event_filters(struct etmv4_drvdata *drvdata,
@@ -1089,18 +1090,25 @@ void etm4_config_trace_mode(struct etmv4_config *config)
 
 static int etm4_online_cpu(unsigned int cpu)
 {
-	if (!etmdrvdata[cpu])
+	mutex_lock(&per_cpu(etmdrvdata_lock, cpu));
+	if (!etmdrvdata[cpu]) {
+		mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 		return 0;
+	}
 
 	if (etmdrvdata[cpu]->boot_enable && !etmdrvdata[cpu]->sticky_enable)
 		coresight_enable(etmdrvdata[cpu]->csdev);
+	mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 	return 0;
 }
 
 static int etm4_starting_cpu(unsigned int cpu)
 {
-	if (!etmdrvdata[cpu])
+	mutex_lock(&per_cpu(etmdrvdata_lock, cpu));
+	if (!etmdrvdata[cpu]) {
+		mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 		return 0;
+	}
 
 	spin_lock(&etmdrvdata[cpu]->spinlock);
 	if (!etmdrvdata[cpu]->os_unlock)
@@ -1109,18 +1117,23 @@ static int etm4_starting_cpu(unsigned int cpu)
 	if (local_read(&etmdrvdata[cpu]->mode))
 		etm4_enable_hw(etmdrvdata[cpu]);
 	spin_unlock(&etmdrvdata[cpu]->spinlock);
+	mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 	return 0;
 }
 
 static int etm4_dying_cpu(unsigned int cpu)
 {
-	if (!etmdrvdata[cpu])
+	mutex_lock(&per_cpu(etmdrvdata_lock, cpu));
+	if (!etmdrvdata[cpu]) {
+		mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 		return 0;
+	}
 
 	spin_lock(&etmdrvdata[cpu]->spinlock);
 	if (local_read(&etmdrvdata[cpu]->mode))
 		etm4_disable_hw(etmdrvdata[cpu]);
 	spin_unlock(&etmdrvdata[cpu]->spinlock);
+	mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
 	return 0;
 }
 
@@ -1360,24 +1373,30 @@ static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
 {
 	struct etmv4_drvdata *drvdata;
 	unsigned int cpu = smp_processor_id();
+	int ret = NOTIFY_OK;
 
+	mutex_lock(&per_cpu(etmdrvdata_lock, cpu));
 	if (!etmdrvdata[cpu])
-		return NOTIFY_OK;
+		goto out;
 
 	drvdata = etmdrvdata[cpu];
 
 	if (!drvdata->save_state)
-		return NOTIFY_OK;
+		goto out;
 
-	if (WARN_ON_ONCE(drvdata->cpu != cpu))
-		return NOTIFY_BAD;
+	if (WARN_ON_ONCE(drvdata->cpu != cpu)) {
+		ret = NOTIFY_BAD;
+		goto out;
+	}
 
 	switch (cmd) {
 	case CPU_PM_ENTER:
 		/* save the state if self-hosted coresight is in use */
 		if (local_read(&drvdata->mode))
-			if (etm4_cpu_save(drvdata))
-				return NOTIFY_BAD;
+			if (etm4_cpu_save(drvdata)) {
+				ret = NOTIFY_BAD;
+				goto out;
+			}
 		break;
 	case CPU_PM_EXIT:
 		/* fallthrough */
@@ -1386,10 +1405,12 @@ static int etm4_cpu_pm_notify(struct notifier_block *nb, unsigned long cmd,
 			etm4_cpu_restore(drvdata);
 		break;
 	default:
-		return NOTIFY_DONE;
+		goto out;
 	}
 
-	return NOTIFY_OK;
+out:
+	mutex_unlock(&per_cpu(etmdrvdata_lock, cpu));
+	return ret;
 }
 
 static struct notifier_block etm4_cpu_pm_nb = {
@@ -1430,7 +1451,7 @@ static int __init etm4_pm_setup(void)
 	return ret;
 }
 
-static void __init etm4_pm_clear(void)
+static void etm4_pm_clear(void)
 {
 	cpu_pm_unregister_notifier(&etm4_cpu_pm_nb);
 	cpuhp_remove_state_nocalls(CPUHP_AP_ARM_CORESIGHT_STARTING);
@@ -1487,25 +1508,20 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 	if (!desc.name)
 		return -ENOMEM;
 
-	etmdrvdata[drvdata->cpu] = drvdata;
-
 	if (smp_call_function_single(drvdata->cpu,
 				etm4_init_arch_data,  drvdata, 1))
 		dev_err(dev, "ETM arch init failed\n");
 
-	if (etm4_arch_supported(drvdata->arch) == false) {
-		ret = -EINVAL;
-		goto err_arch_supported;
-	}
+	if (etm4_arch_supported(drvdata->arch) == false)
+		return -EINVAL;
 
 	etm4_init_trace_id(drvdata);
 	etm4_set_default(&drvdata->config);
 
 	pdata = coresight_get_platform_data(dev);
-	if (IS_ERR(pdata)) {
-		ret = PTR_ERR(pdata);
-		goto err_arch_supported;
-	}
+	if (IS_ERR(pdata))
+		return PTR_ERR(pdata);
+
 	adev->dev.platform_data = pdata;
 
 	desc.type = CORESIGHT_DEV_TYPE_SOURCE;
@@ -1515,17 +1531,19 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 	desc.dev = dev;
 	desc.groups = coresight_etmv4_groups;
 	drvdata->csdev = coresight_register(&desc);
-	if (IS_ERR(drvdata->csdev)) {
-		ret = PTR_ERR(drvdata->csdev);
-		goto err_arch_supported;
-	}
+	if (IS_ERR(drvdata->csdev))
+		return PTR_ERR(drvdata->csdev);
 
 	ret = etm_perf_symlink(drvdata->csdev, true);
 	if (ret) {
 		coresight_unregister(drvdata->csdev);
-		goto err_arch_supported;
+		return ret;
 	}
 
+	mutex_lock(&per_cpu(etmdrvdata_lock, drvdata->cpu));
+	etmdrvdata[drvdata->cpu] = drvdata;
+	mutex_unlock(&per_cpu(etmdrvdata_lock, drvdata->cpu));
+
 	pm_runtime_put(&adev->dev);
 	dev_info(&drvdata->csdev->dev, "CPU%d: ETM v%d.%d initialized\n",
 		 drvdata->cpu, drvdata->arch >> 4, drvdata->arch & 0xf);
@@ -1536,10 +1554,6 @@ static int etm4_probe(struct amba_device *adev, const struct amba_id *id)
 	}
 
 	return 0;
-
-err_arch_supported:
-	etmdrvdata[drvdata->cpu] = NULL;
-	return ret;
 }
 
 static struct amba_cs_uci_id uci_id_etm4[] = {
@@ -1551,6 +1565,22 @@ static struct amba_cs_uci_id uci_id_etm4[] = {
 	}
 };
 
+static int __exit etm4_remove(struct amba_device *adev)
+{
+	struct etmv4_drvdata *drvdata = dev_get_drvdata(&adev->dev);
+
+	etm_perf_symlink(drvdata->csdev, false);
+
+	mutex_lock(&per_cpu(etmdrvdata_lock, drvdata->cpu));
+	etmdrvdata[drvdata->cpu] = NULL;
+	mutex_unlock(&per_cpu(etmdrvdata_lock, drvdata->cpu));
+
+	coresight_unregister(drvdata->csdev);
+
+	return 0;
+}
+
+
 static const struct amba_id etm4_ids[] = {
 	CS_AMBA_ID(0x000bb95d),			/* Cortex-A53 */
 	CS_AMBA_ID(0x000bb95e),			/* Cortex-A57 */
@@ -1568,18 +1598,26 @@ static const struct amba_id etm4_ids[] = {
 	{},
 };
 
+MODULE_DEVICE_TABLE(amba, etm4_ids);
+
 static struct amba_driver etm4x_driver = {
 	.drv = {
 		.name   = "coresight-etm4x",
+		.owner  = THIS_MODULE,
 		.suppress_bind_attrs = true,
 	},
 	.probe		= etm4_probe,
+	.remove         = etm4_remove,
 	.id_table	= etm4_ids,
 };
 
 static int __init etm4x_init(void)
 {
 	int ret;
+	int cpu;
+
+	for_each_possible_cpu(cpu)
+		mutex_init(&per_cpu(etmdrvdata_lock, cpu));
 
 	ret = etm4_pm_setup();
 
@@ -1595,4 +1633,20 @@ static int __init etm4x_init(void)
 
 	return ret;
 }
-device_initcall(etm4x_init);
+
+static void __exit etm4x_exit(void)
+{
+	int cpu;
+
+	amba_driver_unregister(&etm4x_driver);
+	etm4_pm_clear();
+	for_each_possible_cpu(cpu)
+		mutex_destroy(&per_cpu(etmdrvdata_lock, cpu));
+}
+module_init(etm4x_init);
+module_exit(etm4x_exit);
+
+MODULE_AUTHOR("Pratik Patel <pratikp@codeaurora.org>");
+MODULE_AUTHOR("Mathieu Poirier <mathieu.poirier@linaro.org>");
+MODULE_DESCRIPTION("Arm CoreSight Program Flow Trace v4 driver");
+MODULE_LICENSE("GPL v2");
