diff mbox

[RFC] ARM: SOCFPGA: Enable PMU through the CTI

Message ID 1368586682-19848-1-git-send-email-dinguyen@altera.com (mailing list archive)
State New, archived
Headers show

Commit Message

Dinh Nguyen May 15, 2013, 2:58 a.m. UTC
From: Dinh Nguyen <dinguyen@altera.com>

SOCFPGA has a PMU(Performance Measurement Unit) that has its
IRQs routed through Coresight using the CTI(Cross-Trigger Interface).

This patch enables the PMU and initializes the CTI for usage by
oprofile/perfmon.

A lot of the code/idea was taken from this:
https://patchwork.kernel.org/patch/1432541/

Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
CC: Ming Lei <ming.lei@canonical.com>
CC: Will Deacon <will.deacon@arm.com>
Cc: Paul Walmsley <paul@pwsan.com>
Cc: Russell King <linux@arm.linux.org.uk>
CC: Arnd Bergmann <arnd@arndb.de>
CC: Olof Johansson <olof@lixom.net>
Cc: Pavel Machek <pavel@denx.de>
---
 arch/arm/boot/dts/socfpga.dtsi      |   18 +++++
 arch/arm/include/asm/pmu.h          |    3 +
 arch/arm/kernel/perf_event.c        |   16 +++++
 arch/arm/mach-socfpga/Makefile      |    1 +
 arch/arm/mach-socfpga/socfpga.c     |   21 +++++-
 arch/arm/mach-socfpga/socfpga_cti.c |  125 +++++++++++++++++++++++++++++++++++
 arch/arm/mach-socfpga/socfpga_cti.h |   16 +++++
 7 files changed, 199 insertions(+), 1 deletion(-)
 create mode 100644 arch/arm/mach-socfpga/socfpga_cti.c
 create mode 100644 arch/arm/mach-socfpga/socfpga_cti.h

Comments

Will Deacon May 15, 2013, 9:12 a.m. UTC | #1
Hello,

[adding Jon Hunter]

On Wed, May 15, 2013 at 03:58:02AM +0100, dinguyen@altera.com wrote:
> From: Dinh Nguyen <dinguyen@altera.com>
> 
> SOCFPGA has a PMU(Performance Measurement Unit) that has its
> IRQs routed through Coresight using the CTI(Cross-Trigger Interface).
> 
> This patch enables the PMU and initializes the CTI for usage by
> oprofile/perfmon.
> 
> A lot of the code/idea was taken from this:
> https://patchwork.kernel.org/patch/1432541/
> 
> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
> CC: Ming Lei <ming.lei@canonical.com>
> CC: Will Deacon <will.deacon@arm.com>
> Cc: Paul Walmsley <paul@pwsan.com>
> Cc: Russell King <linux@arm.linux.org.uk>
> CC: Arnd Bergmann <arnd@arndb.de>
> CC: Olof Johansson <olof@lixom.net>
> Cc: Pavel Machek <pavel@denx.de>
> ---
>  arch/arm/boot/dts/socfpga.dtsi      |   18 +++++
>  arch/arm/include/asm/pmu.h          |    3 +
>  arch/arm/kernel/perf_event.c        |   16 +++++
>  arch/arm/mach-socfpga/Makefile      |    1 +
>  arch/arm/mach-socfpga/socfpga.c     |   21 +++++-
>  arch/arm/mach-socfpga/socfpga_cti.c |  125 +++++++++++++++++++++++++++++++++++
>  arch/arm/mach-socfpga/socfpga_cti.h |   16 +++++
>  7 files changed, 199 insertions(+), 1 deletion(-)
>  create mode 100644 arch/arm/mach-socfpga/socfpga_cti.c
>  create mode 100644 arch/arm/mach-socfpga/socfpga_cti.h

Whilst it's great to see somebody else making use of CTI, I think this would
benefit from Jon's separate CTI driver that he posted once:

  http://lists.infradead.org/pipermail/linux-arm-kernel/2012-December/137156.html

Jon -- would you be interested in reviving that work so the socfpga guys can
make use of it?

> diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
> index f24edad..60324b3 100644
> --- a/arch/arm/include/asm/pmu.h
> +++ b/arch/arm/include/asm/pmu.h
> @@ -38,6 +38,9 @@ struct arm_pmu_platdata {
>  				  irq_handler_t pmu_handler);
>  	int (*runtime_resume)(struct device *dev);
>  	int (*runtime_suspend)(struct device *dev);
> +	int (*init)(struct platform_device *pdev);
> +	int (*start)(struct platform_device *pdev);
> +	int (*stop)(struct platform_device *pdev);

For omap4, we were planning to use the runtime PM callbacks for managing the
CTI iirc.

Will
Pavel Machek May 15, 2013, 9:46 a.m. UTC | #2
Hi!

> SOCFPGA has a PMU(Performance Measurement Unit) that has its
> IRQs routed through Coresight using the CTI(Cross-Trigger Interface).
> 
> This patch enables the PMU and initializes the CTI for usage by
> oprofile/perfmon.
> 
> A lot of the code/idea was taken from this:
> https://patchwork.kernel.org/patch/1432541/

> --- a/arch/arm/kernel/perf_event.c
> +++ b/arch/arm/kernel/perf_event.c
> @@ -305,6 +305,11 @@ static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
>  static void
>  armpmu_release_hardware(struct arm_pmu *armpmu)
>  {
> +	struct platform_device *plat_device = armpmu->plat_device;
> +	struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev);
> +
...
> @@ -314,6 +319,7 @@ armpmu_reserve_hardware(struct arm_pmu *armpmu)
>  {
>  	int err;
>  	struct platform_device *pmu_device = armpmu->plat_device;

pmu_device == plat_device, AFAICT. One of them should be renamed for
consistency. Or maybe even make it "pdev" ... shorter and used in the
code below...

> +int socfpga_init_cti(struct platform_device *pdev)
> +{

> +	/*configure CTI0 for pmu irq routing*/

Comments should normally be "/* word */"...

I'll take a more detailed look later.

Thanks,
									Pavel
Jon Hunter June 13, 2013, 4:41 p.m. UTC | #3
On 15/05/13 10:12, Will Deacon wrote:
> Hello,
> 
> [adding Jon Hunter]
> 
> On Wed, May 15, 2013 at 03:58:02AM +0100, dinguyen@altera.com wrote:
>> From: Dinh Nguyen <dinguyen@altera.com>
>>
>> SOCFPGA has a PMU(Performance Measurement Unit) that has its
>> IRQs routed through Coresight using the CTI(Cross-Trigger Interface).
>>
>> This patch enables the PMU and initializes the CTI for usage by
>> oprofile/perfmon.
>>
>> A lot of the code/idea was taken from this:
>> https://patchwork.kernel.org/patch/1432541/
>>
>> Signed-off-by: Dinh Nguyen <dinguyen@altera.com>
>> CC: Ming Lei <ming.lei@canonical.com>
>> CC: Will Deacon <will.deacon@arm.com>
>> Cc: Paul Walmsley <paul@pwsan.com>
>> Cc: Russell King <linux@arm.linux.org.uk>
>> CC: Arnd Bergmann <arnd@arndb.de>
>> CC: Olof Johansson <olof@lixom.net>
>> Cc: Pavel Machek <pavel@denx.de>
>> ---
>>  arch/arm/boot/dts/socfpga.dtsi      |   18 +++++
>>  arch/arm/include/asm/pmu.h          |    3 +
>>  arch/arm/kernel/perf_event.c        |   16 +++++
>>  arch/arm/mach-socfpga/Makefile      |    1 +
>>  arch/arm/mach-socfpga/socfpga.c     |   21 +++++-
>>  arch/arm/mach-socfpga/socfpga_cti.c |  125 +++++++++++++++++++++++++++++++++++
>>  arch/arm/mach-socfpga/socfpga_cti.h |   16 +++++
>>  7 files changed, 199 insertions(+), 1 deletion(-)
>>  create mode 100644 arch/arm/mach-socfpga/socfpga_cti.c
>>  create mode 100644 arch/arm/mach-socfpga/socfpga_cti.h
> 
> Whilst it's great to see somebody else making use of CTI, I think this would
> benefit from Jon's separate CTI driver that he posted once:
> 
>   http://lists.infradead.org/pipermail/linux-arm-kernel/2012-December/137156.html
> 
> Jon -- would you be interested in reviving that work so the socfpga guys can
> make use of it?

Sorry the delay I have been moving house!

Anyway, although I would be interesting in reviving and completing the
work on cti, realistically, I am not sure when I would get to it now. I
do have the latest code on my github account [1]. However, probably
still needs more work before it is ready for the mainline in sorting out
the DT bindings, etc. I was also thinking about making the cti look like
a interrupt controller in DT as that is the way it is currently being
used. However, I am not implemented that but should not be much work.

Cheers
Jon

[1] https://github.com/jonhunter/linux/commits/dev-cti
diff mbox

Patch

diff --git a/arch/arm/boot/dts/socfpga.dtsi b/arch/arm/boot/dts/socfpga.dtsi
index 16a6e13..0f10a0a 100644
--- a/arch/arm/boot/dts/socfpga.dtsi
+++ b/arch/arm/boot/dts/socfpga.dtsi
@@ -255,6 +255,24 @@ 
 			cache-level = <2>;
 		};
 
+		pmu {
+			#address-cells = <1>;
+			#size-cells = <1>;
+			compatible = "arm,cortex-a9-pmu";
+			interrupts = <0 176 4>, <0 177 4>;
+			ranges;
+
+			cti0: cti0@ff118000 {
+				compatible = "arm,coresight-cti";
+				reg = <0xff118000 0x100>;
+			};
+
+			cti1: cti1@ff119000 {
+				compatible = "arm,coresight-cti";
+				reg = <0xff119000 0x100>;
+			};
+		};
+
 		/* Local timer */
 		timer@fffec600 {
 			compatible = "arm,cortex-a9-twd-timer";
diff --git a/arch/arm/include/asm/pmu.h b/arch/arm/include/asm/pmu.h
index f24edad..60324b3 100644
--- a/arch/arm/include/asm/pmu.h
+++ b/arch/arm/include/asm/pmu.h
@@ -38,6 +38,9 @@  struct arm_pmu_platdata {
 				  irq_handler_t pmu_handler);
 	int (*runtime_resume)(struct device *dev);
 	int (*runtime_suspend)(struct device *dev);
+	int (*init)(struct platform_device *pdev);
+	int (*start)(struct platform_device *pdev);
+	int (*stop)(struct platform_device *pdev);
 };
 
 #ifdef CONFIG_HW_PERF_EVENTS
diff --git a/arch/arm/kernel/perf_event.c b/arch/arm/kernel/perf_event.c
index 8c3094d..66e9206 100644
--- a/arch/arm/kernel/perf_event.c
+++ b/arch/arm/kernel/perf_event.c
@@ -305,6 +305,11 @@  static irqreturn_t armpmu_dispatch_irq(int irq, void *dev)
 static void
 armpmu_release_hardware(struct arm_pmu *armpmu)
 {
+	struct platform_device *plat_device = armpmu->plat_device;
+	struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev);
+
+	if (plat->stop)
+		plat->stop(plat_device);
 	armpmu->free_irq(armpmu);
 	pm_runtime_put_sync(&armpmu->plat_device->dev);
 }
@@ -314,6 +319,7 @@  armpmu_reserve_hardware(struct arm_pmu *armpmu)
 {
 	int err;
 	struct platform_device *pmu_device = armpmu->plat_device;
+	struct arm_pmu_platdata *plat = dev_get_platdata(&pmu_device->dev);
 
 	if (!pmu_device)
 		return -ENODEV;
@@ -324,6 +330,8 @@  armpmu_reserve_hardware(struct arm_pmu *armpmu)
 		armpmu_release_hardware(armpmu);
 		return err;
 	}
+	if (plat->start)
+		plat->start(pmu_device);
 
 	return 0;
 }
@@ -506,10 +514,18 @@  static void armpmu_init(struct arm_pmu *armpmu)
 
 int armpmu_register(struct arm_pmu *armpmu, int type)
 {
+	struct platform_device *plat_device = armpmu->plat_device;
+	struct arm_pmu_platdata *plat = dev_get_platdata(&plat_device->dev);
+
 	armpmu_init(armpmu);
 	pm_runtime_enable(&armpmu->plat_device->dev);
 	pr_info("enabled with %s PMU driver, %d counters available\n",
 			armpmu->name, armpmu->num_events);
+
+	/* Platform specific initialization. ie. CTI enable */
+	if (plat->init)
+		plat->init(plat_device);
+
 	return perf_pmu_register(&armpmu->pmu, armpmu->name, type);
 }
 
diff --git a/arch/arm/mach-socfpga/Makefile b/arch/arm/mach-socfpga/Makefile
index 6dd7a93..c7401d3 100644
--- a/arch/arm/mach-socfpga/Makefile
+++ b/arch/arm/mach-socfpga/Makefile
@@ -4,3 +4,4 @@ 
 
 obj-y					:= socfpga.o
 obj-$(CONFIG_SMP)	+= headsmp.o platsmp.o
+obj-$(CONFIG_HW_PERF_EVENTS) += socfpga_cti.o
diff --git a/arch/arm/mach-socfpga/socfpga.c b/arch/arm/mach-socfpga/socfpga.c
index 46a0513..64f90a5 100644
--- a/arch/arm/mach-socfpga/socfpga.c
+++ b/arch/arm/mach-socfpga/socfpga.c
@@ -24,8 +24,10 @@ 
 #include <asm/hardware/cache-l2x0.h>
 #include <asm/mach/arch.h>
 #include <asm/mach/map.h>
+#include <asm/pmu.h>
 
 #include "core.h"
+#include "socfpga_cti.h"
 
 void __iomem *socfpga_scu_base_addr = ((void __iomem *)(SOCFPGA_SCU_VIRT_BASE));
 void __iomem *sys_manager_base_addr;
@@ -33,6 +35,22 @@  void __iomem *rst_manager_base_addr;
 void __iomem *clk_mgr_base_addr;
 unsigned long cpu1start_addr;
 
+#ifdef CONFIG_HW_PERF_EVENTS
+static struct arm_pmu_platdata socfpga_pmu_platdata = {
+	.handle_irq = socfpga_pmu_handler,
+	.init = socfpga_init_cti,
+	.start = socfpga_start_cti,
+	.stop = socfpga_stop_cti,
+};
+#endif
+
+static const struct of_dev_auxdata socfpga_auxdata_lookup[] __initconst = {
+#ifdef CONFIG_HW_PERF_EVENTS
+	OF_DEV_AUXDATA("arm,cortex-a9-pmu", 0, "arm-pmu", &socfpga_pmu_platdata),
+#endif
+	{ /* sentinel */ }
+};
+
 static struct map_desc scu_io_desc __initdata = {
 	.virtual	= SOCFPGA_SCU_VIRT_BASE,
 	.pfn		= 0, /* run-time */
@@ -106,7 +124,8 @@  static void socfpga_cyclone5_restart(char mode, const char *cmd)
 static void __init socfpga_cyclone5_init(void)
 {
 	l2x0_of_init(0, ~0UL);
-	of_platform_populate(NULL, of_default_bus_match_table, NULL, NULL);
+	of_platform_populate(NULL, of_default_bus_match_table,
+			socfpga_auxdata_lookup, NULL);
 	of_clk_init(NULL);
 	socfpga_init_clocks();
 }
diff --git a/arch/arm/mach-socfpga/socfpga_cti.c b/arch/arm/mach-socfpga/socfpga_cti.c
new file mode 100644
index 0000000..10072c8
--- /dev/null
+++ b/arch/arm/mach-socfpga/socfpga_cti.c
@@ -0,0 +1,125 @@ 
+/*
+ *  Copyright (C) 2013 Altera Corporation
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+#include <linux/platform_device.h>
+#include <linux/of_address.h>
+#include <asm/mach/map.h>
+#include <asm/cti.h>
+#include <asm/pmu.h>
+
+#include "core.h"
+#include "socfpga_cti.h"
+
+#define SOCFPGA_NUM_CTI	2
+
+struct cti socfpga_cti_data[SOCFPGA_NUM_CTI];
+
+irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler)
+{
+	unsigned int handled = 0;
+	int i;
+
+	for (i = 0; i < SOCFPGA_NUM_CTI; i++)
+		if (irq == socfpga_cti_data[i].irq) {
+			cti_irq_ack(&socfpga_cti_data[i]);
+			handled = handler(irq, dev);
+		}
+
+	return IRQ_RETVAL(handled);
+}
+
+int socfpga_init_cti(struct platform_device *pdev)
+{
+	struct device_node *np, *np2;
+	void __iomem *cti0_addr;
+	void __iomem *cti1_addr;
+	u32 irq0, irq1;
+
+	np = pdev->dev.of_node;
+	np2 = of_find_compatible_node(np, NULL, "arm,coresight-cti");
+	if (!np2) {
+		dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n");
+		return -1;
+	}
+	cti0_addr = of_iomap(np2, 0);
+	if (!cti0_addr) {
+		dev_err(&pdev->dev, "PMU: ioremap for CTI failed\n");
+		return -1;
+	}
+
+	irq0 = platform_get_irq(pdev, 0);
+	if (irq0 < 0)
+		goto free_irq0;
+
+	np2 = of_find_compatible_node(np2, NULL, "arm,coresight-cti");
+	if (!np2) {
+		dev_err(&pdev->dev, "PMU: Unable to find coresight-cti\n");
+		goto err_iounmap;
+	}
+	cti1_addr = of_iomap(np2, 0);
+	if (!cti1_addr)
+		goto err_iounmap;
+
+	irq1 = platform_get_irq(pdev, 1);
+	if (irq1 < 0)
+		goto free_irq1;
+
+	/*configure CTI0 for pmu irq routing*/
+	cti_init(&socfpga_cti_data[0], cti0_addr,
+		irq0, CTI_MPU_IRQ_TRIG_OUT);
+	cti_unlock(&socfpga_cti_data[0]);
+	cti_map_trigger(&socfpga_cti_data[0],
+		CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_0);
+
+	/*configure CTI1 for pmu irq routing*/
+	cti_init(&socfpga_cti_data[1], cti1_addr,
+		irq1, CTI_MPU_IRQ_TRIG_OUT);
+	cti_unlock(&socfpga_cti_data[1]);
+	cti_map_trigger(&socfpga_cti_data[1],
+		CTI_MPU_IRQ_TRIG_IN, CTI_MPU_IRQ_TRIG_OUT, PMU_CHANNEL_1);
+
+	dev_info(&pdev->dev, "PMU:CTI successfully enabled\n");
+	return 0;
+
+free_irq1:
+	iounmap(cti1_addr);
+err_iounmap:
+	free_irq(irq0, pdev);
+free_irq0:
+	iounmap(cti0_addr);
+	return -1;
+}
+
+int socfpga_start_cti(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < SOCFPGA_NUM_CTI; i++)
+		cti_enable(&socfpga_cti_data[i]);
+
+	return 0;
+}
+
+int socfpga_stop_cti(struct platform_device *pdev)
+{
+	int i;
+
+	for (i = 0; i < SOCFPGA_NUM_CTI; i++)
+		cti_disable(&socfpga_cti_data[i]);
+
+	return 0;
+}
+
diff --git a/arch/arm/mach-socfpga/socfpga_cti.h b/arch/arm/mach-socfpga/socfpga_cti.h
new file mode 100644
index 0000000..cbeb063
--- /dev/null
+++ b/arch/arm/mach-socfpga/socfpga_cti.h
@@ -0,0 +1,16 @@ 
+#ifndef __SOCFPGA_CTI_H
+#define __SOCFPGA_CTI_H
+
+#define CTI_MPU_IRQ_TRIG_IN	1
+#define CTI_MPU_IRQ_TRIG_OUT	6
+
+#define PMU_CHANNEL_0	0
+#define PMU_CHANNEL_1	1
+
+#ifdef CONFIG_HW_PERF_EVENTS
+extern irqreturn_t socfpga_pmu_handler(int irq, void *dev, irq_handler_t handler);
+extern int socfpga_init_cti(struct platform_device *pdev);
+extern int socfpga_start_cti(struct platform_device *pdev);
+extern int socfpga_stop_cti(struct platform_device *pdev);
+#endif
+#endif /* __SOCFPGA_CTI_H */