All of lore.kernel.org
 help / color / mirror / Atom feed
* [RESEND PATCH v1 00/11] perf: arm64: Support for Hisilicon SoC Hardware event counters
@ 2016-11-03  5:41 ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA
  Cc: guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA,
	anurupvasu-Re5JQEeQqe8AvxtiuMwx3w

[Resending after adding maintainers in --to]

Provide Support for Hisilicon SoC(Hip05/06/07) Hardware event counters.
The Hisilicon SoC Hip0x series has many uncore or non-CPU performance
events and counters units.

This initial patch series is implemented refering to arm-cci, Intel/AMD uncore and
also the cavium thunderX and xgene uncore pmu patches.

Support for Hisilicon L3 cache(L3C), MN and DDR hardware events and
counters are added in this implementation.

The Hisilicon uncore PMUs can be found under /sys/bus/event_source/devices.
The counters are exported via sysfs in the corresponding events files
under the PMU directory so the perf tool can list the event names.

ToDo:
	1) The counter overflow handling is currently unsupported in this
	   patch series.
	2) ACPI support.

Anurup M (8):
  arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
  Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event
    counting.
  dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  perf: hisi: Update Kconfig for Hisilicon PMU support
  perf: hisi: Add support for Hisilicon SoC event counters
  perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
  perf: hisi: Support for Hisilicon DDRC PMU.
  dts: arm64: hip06: Add Hisilicon SoC PMU support

Shaokun Zhang (1):
  perf: hisi: Miscellanous node(MN) event counting in perf

Tan Xiaojun (2):
  dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts
    bindings
  drivers: soc: hisi: Add support for Hisilicon Djtag driver

 .../bindings/arm/hisilicon/hisilicon.txt           |  82 +++
 .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 ++++
 Documentation/perf/hisi-pmu.txt                    |  80 +++
 MAINTAINERS                                        |  10 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi           | 116 ++++
 drivers/perf/Kconfig                               |   9 +
 drivers/perf/Makefile                              |   1 +
 drivers/perf/hisilicon/Makefile                    |   1 +
 drivers/perf/hisilicon/hisi_uncore_ddrc.c          | 444 ++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_ddrc.h          |  73 +++
 drivers/perf/hisilicon/hisi_uncore_l3c.c           | 628 ++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_l3c.h           |  67 +++
 drivers/perf/hisilicon/hisi_uncore_mn.c            | 571 ++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_mn.h            |  68 +++
 drivers/perf/hisilicon/hisi_uncore_pmu.c           | 371 ++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h           | 130 +++++
 drivers/soc/Kconfig                                |   1 +
 drivers/soc/Makefile                               |   1 +
 drivers/soc/hisilicon/Kconfig                      |  12 +
 drivers/soc/hisilicon/Makefile                     |   1 +
 drivers/soc/hisilicon/djtag.c                      | 639 +++++++++++++++++++++
 include/linux/soc/hisilicon/djtag.h                |  38 ++
 22 files changed, 3470 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
 create mode 100644 Documentation/perf/hisi-pmu.txt
 create mode 100644 drivers/perf/hisilicon/Makefile
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/djtag.c
 create mode 100644 include/linux/soc/hisilicon/djtag.h

-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 00/11] perf: arm64: Support for Hisilicon SoC Hardware event counters
@ 2016-11-03  5:41 ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: linux-arm-kernel

[Resending after adding maintainers in --to]

Provide Support for Hisilicon SoC(Hip05/06/07) Hardware event counters.
The Hisilicon SoC Hip0x series has many uncore or non-CPU performance
events and counters units.

This initial patch series is implemented refering to arm-cci, Intel/AMD uncore and
also the cavium thunderX and xgene uncore pmu patches.

Support for Hisilicon L3 cache(L3C), MN and DDR hardware events and
counters are added in this implementation.

The Hisilicon uncore PMUs can be found under /sys/bus/event_source/devices.
The counters are exported via sysfs in the corresponding events files
under the PMU directory so the perf tool can list the event names.

ToDo:
	1) The counter overflow handling is currently unsupported in this
	   patch series.
	2) ACPI support.

Anurup M (8):
  arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
  Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event
    counting.
  dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  perf: hisi: Update Kconfig for Hisilicon PMU support
  perf: hisi: Add support for Hisilicon SoC event counters
  perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
  perf: hisi: Support for Hisilicon DDRC PMU.
  dts: arm64: hip06: Add Hisilicon SoC PMU support

Shaokun Zhang (1):
  perf: hisi: Miscellanous node(MN) event counting in perf

Tan Xiaojun (2):
  dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts
    bindings
  drivers: soc: hisi: Add support for Hisilicon Djtag driver

 .../bindings/arm/hisilicon/hisilicon.txt           |  82 +++
 .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 ++++
 Documentation/perf/hisi-pmu.txt                    |  80 +++
 MAINTAINERS                                        |  10 +
 arch/arm64/boot/dts/hisilicon/hip06.dtsi           | 116 ++++
 drivers/perf/Kconfig                               |   9 +
 drivers/perf/Makefile                              |   1 +
 drivers/perf/hisilicon/Makefile                    |   1 +
 drivers/perf/hisilicon/hisi_uncore_ddrc.c          | 444 ++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_ddrc.h          |  73 +++
 drivers/perf/hisilicon/hisi_uncore_l3c.c           | 628 ++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_l3c.h           |  67 +++
 drivers/perf/hisilicon/hisi_uncore_mn.c            | 571 ++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_mn.h            |  68 +++
 drivers/perf/hisilicon/hisi_uncore_pmu.c           | 371 ++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h           | 130 +++++
 drivers/soc/Kconfig                                |   1 +
 drivers/soc/Makefile                               |   1 +
 drivers/soc/hisilicon/Kconfig                      |  12 +
 drivers/soc/hisilicon/Makefile                     |   1 +
 drivers/soc/hisilicon/djtag.c                      | 639 +++++++++++++++++++++
 include/linux/soc/hisilicon/djtag.h                |  38 ++
 22 files changed, 3470 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
 create mode 100644 Documentation/perf/hisi-pmu.txt
 create mode 100644 drivers/perf/hisilicon/Makefile
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/djtag.c
 create mode 100644 include/linux/soc/hisilicon/djtag.h

-- 
2.1.4

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 01/11] arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:41     ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA
  Cc: guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA,
	anurupvasu-Re5JQEeQqe8AvxtiuMwx3w

	Add support for Hisilicon SoC hardware event counters
	for HIP05/06/07 chip versions.

Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b224caa..839abc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5725,6 +5725,16 @@ S:	Maintained
 F:	drivers/net/ethernet/hisilicon/
 F:	Documentation/devicetree/bindings/net/hisilicon*.txt
 
+HISILICON SOC PMU
+M:	Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
+W:	http://www.hisilicon.com
+S:	Supported
+F:	drivers/perf/hisilicon/*
+F:	drivers/soc/hisilicon/djtag.c
+F:	include/linux/soc/hisilicon/djtag.h
+F:	Documentation/perf/hisi-pmu.txt
+F:	Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
+
 HISILICON ROCE DRIVER
 M:	Lijun Ou <oulijun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
 M:	Wei Hu(Xavier) <xavier.huwei-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 01/11] arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support
@ 2016-11-03  5:41     ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: linux-arm-kernel

	Add support for Hisilicon SoC hardware event counters
	for HIP05/06/07 chip versions.

Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 MAINTAINERS | 10 ++++++++++
 1 file changed, 10 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index b224caa..839abc8 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -5725,6 +5725,16 @@ S:	Maintained
 F:	drivers/net/ethernet/hisilicon/
 F:	Documentation/devicetree/bindings/net/hisilicon*.txt
 
+HISILICON SOC PMU
+M:	Anurup M <anurup.m@huawei.com>
+W:	http://www.hisilicon.com
+S:	Supported
+F:	drivers/perf/hisilicon/*
+F:	drivers/soc/hisilicon/djtag.c
+F:	include/linux/soc/hisilicon/djtag.h
+F:	Documentation/perf/hisi-pmu.txt
+F:	Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
+
 HISILICON ROCE DRIVER
 M:	Lijun Ou <oulijun@huawei.com>
 M:	Wei Hu(Xavier) <xavier.huwei@huawei.com>
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:41     ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA
  Cc: guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA,
	anurupvasu-Re5JQEeQqe8AvxtiuMwx3w

From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>

	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
	   bindings.
	2) Add Hisilicon Djtag dts binding.

Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
---
 .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 7df79a7..341cbb9 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -270,3 +270,85 @@ Required Properties:
   [1]: bootwrapper size
   [2]: relocation physical address
   [3]: relocation size
+
+-----------------------------------------------------------------------
+The Hisilicon Djtag in CPU die is an independent component which connects with
+some other components in the SoC by Debug Bus. This driver can be configured
+to access the registers of connecting components (like L3 cache, l3 cache PMU
+ etc.) during real time debugging by sysctrl. These components appear as child
+nodes of djtag.
+
+The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
+components (such as clock, reset, soft reset, secure debugger, etc.).
+The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
+by djtag driver for use by connecting components.
+
+Hisilicon HiP05 CPU system controller
+Required properties:
+  - compatible : "hisilicon,hip05-cpu-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06 djtag for CPU sysctrl
+Required properties:
+- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
+- reg : Register address and size
+- djtag :
+  - compatible : "hisilicon,hip06-cpu-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP07 djtag for CPU sysctrl
+Required properties:
+  - compatible : "hisilicon,hip07-cpu-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for CPU sysctrl */
+	djtag0: djtag@80010000 {
+		compatible = "hisilicon,hip05-cpu-djtag-v1";
+		reg = <0x0 0x80010000 0x0 0x10000>;
+
+		/* For L3 cache PMU */
+		pmul3c0 {
+			compatible = "hisilicon,hisi-pmu-l3c-v1";
+			scl-id = <0x02>;
+			num-events = <0x16>;
+			num-counters = <0x08>;
+			module-id = <0x04>;
+			num-banks = <0x04>;
+			cfgen-map = <0x02 0x04 0x01 0x08>;
+			counter-reg = <0x170>;
+			evctrl-reg = <0x04>;
+			event-en = <0x1000000>;
+			evtype-reg = <0x140>;
+		};
+	};
+
+-----------------------------------------------------------------------
+The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
+of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
+controller in CPU die and it manage different components, like RSA, etc.
+The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
+the sysctrl registers for use by connecting components.
+All connecting components shall appear as child nodes of djtag.
+
+Hisilicon HiP05 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip05-io-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip06-io-djtag-v2"
+  - reg : Register address and size
+
+Hisilicon HiP07 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip07-io-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for alg sysctrl */
+	djtag0: djtag@d0000000 {
+	       compatible = "hisilicon,hip05-io-djtag-v1";
+		reg = <0x0 0xd0000000 0x0 0x10000>;
+	};
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
@ 2016-11-03  5:41     ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: linux-arm-kernel

From: Tan Xiaojun <tanxiaojun@huawei.com>

	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
	   bindings.
	2) Add Hisilicon Djtag dts binding.

Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
 1 file changed, 82 insertions(+)

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
index 7df79a7..341cbb9 100644
--- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
+++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
@@ -270,3 +270,85 @@ Required Properties:
   [1]: bootwrapper size
   [2]: relocation physical address
   [3]: relocation size
+
+-----------------------------------------------------------------------
+The Hisilicon Djtag in CPU die is an independent component which connects with
+some other components in the SoC by Debug Bus. This driver can be configured
+to access the registers of connecting components (like L3 cache, l3 cache PMU
+ etc.) during real time debugging by sysctrl. These components appear as child
+nodes of djtag.
+
+The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
+components (such as clock, reset, soft reset, secure debugger, etc.).
+The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
+by djtag driver for use by connecting components.
+
+Hisilicon HiP05 CPU system controller
+Required properties:
+  - compatible : "hisilicon,hip05-cpu-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06 djtag for CPU sysctrl
+Required properties:
+- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
+- reg : Register address and size
+- djtag :
+  - compatible : "hisilicon,hip06-cpu-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP07 djtag for CPU sysctrl
+Required properties:
+  - compatible : "hisilicon,hip07-cpu-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for CPU sysctrl */
+	djtag0: djtag at 80010000 {
+		compatible = "hisilicon,hip05-cpu-djtag-v1";
+		reg = <0x0 0x80010000 0x0 0x10000>;
+
+		/* For L3 cache PMU */
+		pmul3c0 {
+			compatible = "hisilicon,hisi-pmu-l3c-v1";
+			scl-id = <0x02>;
+			num-events = <0x16>;
+			num-counters = <0x08>;
+			module-id = <0x04>;
+			num-banks = <0x04>;
+			cfgen-map = <0x02 0x04 0x01 0x08>;
+			counter-reg = <0x170>;
+			evctrl-reg = <0x04>;
+			event-en = <0x1000000>;
+			evtype-reg = <0x140>;
+		};
+	};
+
+-----------------------------------------------------------------------
+The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
+of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
+controller in CPU die and it manage different components, like RSA, etc.
+The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
+the sysctrl registers for use by connecting components.
+All connecting components shall appear as child nodes of djtag.
+
+Hisilicon HiP05 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip05-io-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip06-io-djtag-v2"
+  - reg : Register address and size
+
+Hisilicon HiP07 djtag for ALGSUB sysctrl
+Required properties:
+  - compatible : "hisilicon,hip07-io-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for alg sysctrl */
+	djtag0: djtag at d0000000 {
+	       compatible = "hisilicon,hip05-io-djtag-v1";
+		reg = <0x0 0xd0000000 0x0 0x10000>;
+	};
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:41   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

From: Tan Xiaojun <tanxiaojun@huawei.com>

	The Hisilicon Djtag is an independent component which connects
	with some other components in the SoC by Debug Bus. This driver
	can be configured to access the registers of connecting components
	(like L3 cache) during real time debugging.

Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 drivers/soc/Kconfig                 |   1 +
 drivers/soc/Makefile                |   1 +
 drivers/soc/hisilicon/Kconfig       |  12 +
 drivers/soc/hisilicon/Makefile      |   1 +
 drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
 include/linux/soc/hisilicon/djtag.h |  38 +++
 6 files changed, 692 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/djtag.c
 create mode 100644 include/linux/soc/hisilicon/djtag.h

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index e6e90e8..89ecd42 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
 source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/fsl/qbman/Kconfig"
 source "drivers/soc/fsl/qe/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
 source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 50c23d0..ce2beb5 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -6,6 +6,7 @@ obj-y				+= bcm/
 obj-$(CONFIG_ARCH_DOVE)		+= dove/
 obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
+obj-$(CONFIG_ARCH_HISI)		+= hisilicon/
 obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
 obj-$(CONFIG_ARCH_QCOM)		+= qcom/
 obj-$(CONFIG_ARCH_RENESAS)	+= renesas/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 0000000..6dd4ba0
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,12 @@
+#
+# Hisilicon SoC drivers
+#
+config HISI_DJTAG
+	bool "Hisilicon Djtag Support"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say y here to enable the Hisilicon Djtag support. It is
+	  an independent component which connects with some other
+	  components in the SoC by Debug Bus. This driver can be
+	  configured to access the registers of connecting
+	  components during real time debugging.
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 0000000..35a7b4b
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_DJTAG)	+= djtag.o
diff --git a/drivers/soc/hisilicon/djtag.c b/drivers/soc/hisilicon/djtag.c
new file mode 100644
index 0000000..a87c8b6
--- /dev/null
+++ b/drivers/soc/hisilicon/djtag.c
@@ -0,0 +1,639 @@
+/*
+ * Driver for Hisilicon Djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm-generic/delay.h>
+#include <linux/soc/hisilicon/djtag.h>
+
+#define SC_DJTAG_TIMEOUT		100000	/* 100ms */
+
+/* for djtag v1 */
+#define SC_DJTAG_MSTR_EN		0x6800
+#define DJTAG_NOR_CFG			BIT(1)	/* accelerate R,W */
+#define DJTAG_MSTR_EN			BIT(0)
+#define SC_DJTAG_MSTR_START_EN		0x6804
+#define DJTAG_MSTR_START_EN		0x1
+#define SC_DJTAG_DEBUG_MODULE_SEL	0x680c
+#define SC_DJTAG_MSTR_WR		0x6810
+#define DJTAG_MSTR_W			0x1
+#define DJTAG_MSTR_R			0x0
+#define SC_DJTAG_CHAIN_UNIT_CFG_EN	0x6814
+#define CHAIN_UNIT_CFG_EN		0xFFFF
+#define SC_DJTAG_MSTR_ADDR		0x6818
+#define SC_DJTAG_MSTR_DATA		0x681c
+#define SC_DJTAG_RD_DATA_BASE		0xe800
+
+/* for djtag v2 */
+#define SC_DJTAG_SEC_ACC_EN_EX		0xd800
+#define DJTAG_SEC_ACC_EN_EX		0x1
+#define SC_DJTAG_MSTR_CFG_EX		0xd818
+#define DJTAG_MSTR_RW_SHIFT_EX		29
+#define DJTAG_MSTR_RD_EX		(0x0 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DJTAG_MSTR_WR_EX		(0x1 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DEBUG_MODULE_SEL_SHIFT_EX	16
+#define CHAIN_UNIT_CFG_EN_EX		0xFFFF
+#define SC_DJTAG_MSTR_ADDR_EX		0xd810
+#define SC_DJTAG_MSTR_DATA_EX		0xd814
+#define SC_DJTAG_MSTR_START_EN_EX	0xd81c
+#define DJTAG_MSTR_START_EN_EX		0x1
+#define SC_DJTAG_RD_DATA_BASE_EX	0xe800
+#define SC_DJTAG_OP_ST_EX		0xe828
+#define DJTAG_OP_DONE_EX		BIT(8)
+
+#define DJTAG_PREFIX "hisi-djtag-dev-"
+
+DEFINE_IDR(djtag_hosts_idr);
+
+struct hisi_djtag_host {
+	spinlock_t lock;
+	int id;
+	struct device dev;
+	struct list_head client_list;
+	void __iomem *sysctl_reg_map;
+	struct device_node *of_node;
+	int (*djtag_readwrite)(void __iomem *regs_base, u32 offset,
+			u32 mod_sel, u32 mod_mask, bool is_w,
+			u32 wval, int chain_id, u32 *rval);
+};
+
+#define to_hisi_djtag_client(d) container_of(d, struct hisi_djtag_client, dev)
+#define to_hisi_djtag_driver(d) container_of(d, struct hisi_djtag_driver, \
+								 driver)
+#define MODULE_PREFIX "hisi_djtag:"
+
+static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	*value = readl_relaxed(reg_addr);
+}
+
+static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	writel(val, reg_addr);
+}
+
+/*
+ * djtag_readwrite_v1/v2: djtag read/write interface
+ * @reg_base:	djtag register base address
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules for write
+ * @is_w:	write -> true, read -> false
+ * @wval:	value to register for write
+ * @chain_id:	which sub module for read
+ * @rval:	value in register for read
+ *
+ * Return non-zero if error, else return 0.
+ */
+static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
+		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	/* djtag mster enable & accelerate R,W */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
+			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
+
+	/* select module */
+	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
+	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
+				mod_mask & CHAIN_UNIT_CFG_EN);
+
+	if (is_w) {
+		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
+		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
+	} else
+		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
+
+	/* address offset */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
+
+	/* start to write to djtag register */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
+
+	/* ensure the djtag operation is done */
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
+		if (!(rd & DJTAG_MSTR_EN))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0) {
+		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+		return -EBUSY;
+	}
+
+	if (!is_w)
+		djtag_read32_relaxed(regs_base,
+			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
+
+	return 0;
+}
+
+static int djtag_readwrite_v2(void __iomem *regs_base, u32 offset, u32 mod_sel,
+		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN_EX)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	/* djtag mster enable */
+	djtag_write32(regs_base, SC_DJTAG_SEC_ACC_EN_EX, DJTAG_SEC_ACC_EN_EX);
+
+	if (is_w) {
+		djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_WR_EX
+				| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+				| (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA_EX, wval);
+	} else
+		djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_RD_EX
+				| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+				| (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+
+	/* address offset */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR_EX, offset);
+
+	/* start to write to djtag register */
+	djtag_write32(regs_base,
+		      SC_DJTAG_MSTR_START_EN_EX, DJTAG_MSTR_START_EN_EX);
+
+	/* ensure the djtag operation is done */
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN_EX, &rd);
+
+		if (!(rd & DJTAG_MSTR_START_EN_EX))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	timeout = SC_DJTAG_TIMEOUT;
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_OP_ST_EX, &rd);
+
+		if (rd & DJTAG_OP_DONE_EX)
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	if (!is_w)
+		djtag_read32_relaxed(regs_base,
+				     SC_DJTAG_RD_DATA_BASE_EX + chain_id * 0x4,
+									rval);
+
+	return 0;
+
+timeout:
+	pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+	return -EBUSY;
+}
+
+
+/**
+ * djtag_writel - write registers via djtag
+ * @node:	djtag node
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules
+ * @val:	value to write to register
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+							u32 mod_mask, u32 val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_readwrite(reg_map, offset, mod_sel, mod_mask,
+					true, val, 0, NULL);
+	if (ret)
+		pr_err("djtag_writel: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_writel);
+
+/**
+ * djtag_readl - read registers via djtag
+ * @node:	djtag node
+ * @offset:	register's offset
+ * @mod_sel:	module type selection
+ * @chain_id:	chain_id number, mostly is 0
+ * @val:	register's value
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+							int chain_id, u32 *val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_readwrite(reg_map, offset, mod_sel,
+			0xffff, false, 0, chain_id, val);
+	if (ret)
+		pr_err("djtag_readl: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_readl);
+
+static const struct of_device_id djtag_of_match[] = {
+	/* for hip05(D02) cpu die */
+	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip05(D02) io die */
+	{ .compatible = "hisilicon,hip05-io-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip06(D03) cpu die */
+	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip06(D03) io die */
+	{ .compatible = "hisilicon,hip06-io-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	/* for hip07(D05) cpu die */
+	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	/* for hip07(D05) io die */
+	{ .compatible = "hisilicon,hip07-io-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, djtag_of_match);
+
+static ssize_t
+show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
+
+	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
+}
+static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
+
+static struct attribute *hisi_djtag_dev_attrs[] = {
+	NULL,
+	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(hisi_djtag_dev);
+
+static struct device_type hisi_djtag_client_type = {
+	.groups		= hisi_djtag_dev_groups,
+};
+
+struct hisi_djtag_client *hisi_djtag_verify_client(struct device *dev)
+{
+	return (dev->type == &hisi_djtag_client_type)
+			? to_hisi_djtag_client(dev)
+			: NULL;
+}
+
+static int hisi_djtag_device_probe(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->probe(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_remove(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->remove(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_match(struct device *dev,
+					struct device_driver *drv)
+{
+	const struct of_device_id *p;
+	struct hisi_djtag_client *client = hisi_djtag_verify_client(dev);
+
+	if (!client)
+		return false;
+
+	if (of_driver_match_device(dev, drv))
+		return true;
+
+	p = of_match_device(drv->of_match_table, dev);
+	if (!p)
+		return false;
+
+	return true;
+}
+
+struct bus_type hisi_djtag_bus = {
+	.name		= "hisi-djtag",
+	.match		= hisi_djtag_device_match,
+	.probe		= hisi_djtag_device_probe,
+	.remove		= hisi_djtag_device_remove,
+};
+
+struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
+						struct device_node *node)
+{
+	struct hisi_djtag_client *client;
+	int status;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+
+	client->host = host;
+
+	client->dev.parent = &client->host->dev;
+	client->dev.bus = &hisi_djtag_bus;
+	client->dev.type = &hisi_djtag_client_type;
+	client->dev.of_node = node;
+	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
+					DJTAG_PREFIX, node->name);
+	dev_set_name(&client->dev, "%s", client->name);
+
+	status = device_register(&client->dev);
+	if (status < 0) {
+		pr_err("error adding new device, status=%d\n", status);
+		kfree(client);
+		return NULL;
+	}
+
+	return client;
+}
+
+static struct hisi_djtag_client *hisi_djtag_of_register_device(
+						struct hisi_djtag_host *host,
+						struct device_node *node)
+{
+	struct hisi_djtag_client *client;
+
+	client = hisi_djtag_new_device(host, node);
+	if (client == NULL) {
+		dev_err(&host->dev, "error registering device %s\n",
+			node->full_name);
+		of_node_put(node);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return client;
+}
+
+static void djtag_register_devices(struct hisi_djtag_host *host)
+{
+	struct device_node *node;
+	struct hisi_djtag_client *client;
+
+	if (!host->of_node)
+		return;
+
+	for_each_available_child_of_node(host->of_node, node) {
+		if (of_node_test_and_set_flag(node, OF_POPULATED))
+			continue;
+		client = hisi_djtag_of_register_device(host, node);
+		list_add(&client->next, &host->client_list);
+	}
+}
+
+static int hisi_djtag_add_host(struct hisi_djtag_host *host)
+{
+	int rc;
+
+	host->dev.bus = &hisi_djtag_bus;
+
+	rc = idr_alloc(&djtag_hosts_idr, host, 0, 0, GFP_KERNEL);
+	if (rc < 0) {
+		dev_err(&host->dev, "No available djtag host ID'!s\n");
+		return rc;
+	}
+	host->id = rc;
+
+	/* Suffix the unique ID and set djtag hostname */
+	dev_set_name(&host->dev, "djtag-host-%d", host->id);
+
+	rc = device_register(&host->dev);
+	if (rc < 0) {
+		dev_err(&host->dev, "add_host dev register failed, rc=%d\n",
+									rc);
+		idr_remove(&djtag_hosts_idr, host->id);
+		return rc;
+	}
+
+	djtag_register_devices(host);
+
+	return 0;
+}
+
+static int djtag_host_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct hisi_djtag_host *host;
+	const struct of_device_id *of_id;
+	struct resource *res;
+	int rc;
+
+	of_id = of_match_device(djtag_of_match, dev);
+	if (!of_id)
+		return -EINVAL;
+
+	host = kzalloc(sizeof(*host), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->of_node = dev->of_node;
+	host->djtag_readwrite = of_id->data;
+	spin_lock_init(&host->lock);
+
+	INIT_LIST_HEAD(&host->client_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No reg resorces!\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	if (!resource_size(res)) {
+		dev_err(&pdev->dev, "Zero reg entry!\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
+	if (IS_ERR(host->sysctl_reg_map)) {
+		dev_warn(dev, "Unable to map sysctl registers.\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	platform_set_drvdata(pdev, host);
+
+	rc = hisi_djtag_add_host(host);
+	if (rc) {
+		dev_err(dev, "add host failed, rc=%d\n", rc);
+		kfree(host);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int djtag_host_remove(struct platform_device *pdev)
+{
+	struct hisi_djtag_host *host;
+	struct hisi_djtag_client *client, *tmp;
+	struct list_head *client_list;
+
+	host = platform_get_drvdata(pdev);
+	client_list = &host->client_list;
+
+	list_for_each_entry_safe(client, tmp, client_list, next) {
+		list_del(&client->next);
+		kfree(client);
+	}
+
+	device_unregister(&host->dev);
+	idr_remove(&djtag_hosts_idr, host->id);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver djtag_dev_driver = {
+	.driver = {
+		.name = "hisi-djtag",
+		.of_match_table = djtag_of_match,
+	},
+	.probe = djtag_host_probe,
+	.remove = djtag_host_remove,
+};
+module_platform_driver(djtag_dev_driver);
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver)
+{
+	int rc;
+
+	driver->driver.owner = owner;
+	driver->driver.bus = &hisi_djtag_bus;
+
+	rc = driver_register(&driver->driver);
+	if (rc < 0)
+		pr_err("%s register failed, rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver)
+{
+	driver->driver.bus = &hisi_djtag_bus;
+	driver_unregister(&driver->driver);
+}
+
+static int __init hisi_djtag_init(void)
+{
+	int rc;
+
+	rc = bus_register(&hisi_djtag_bus);
+	if (rc) {
+		pr_err("hisi  djtag init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_djtag_init);
+
+static void __exit hisi_djtag_exit(void)
+{
+	bus_unregister(&hisi_djtag_bus);
+}
+module_exit(hisi_djtag_exit);
+
+MODULE_DESCRIPTION("Hisilicon djtag driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/include/linux/soc/hisilicon/djtag.h b/include/linux/soc/hisilicon/djtag.h
new file mode 100644
index 0000000..aae3bca
--- /dev/null
+++ b/include/linux/soc/hisilicon/djtag.h
@@ -0,0 +1,38 @@
+/*
+ * Driver for Hisilicon djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016-2017 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __HISI_DJTAG_H
+#define __HISI_DJTAG_H
+
+#define DJTAG_CLIENT_NAME_LEN 24
+
+struct hisi_djtag_client {
+	struct hisi_djtag_host *host;
+	struct list_head next;
+	char name[DJTAG_CLIENT_NAME_LEN];
+	struct device dev;
+};
+
+struct hisi_djtag_driver {
+	struct device_driver driver;
+	int (*probe)(struct hisi_djtag_client *);
+	int (*remove)(struct hisi_djtag_client *);
+};
+
+extern struct bus_type hisi_djtag_bus;
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver);
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver);
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, int chain_id, u32 *val);
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, u32 mod_mask, u32 val);
+#endif /* __HISI_DJTAG_H */
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
@ 2016-11-03  5:41   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:41 UTC (permalink / raw)
  To: linux-arm-kernel

From: Tan Xiaojun <tanxiaojun@huawei.com>

	The Hisilicon Djtag is an independent component which connects
	with some other components in the SoC by Debug Bus. This driver
	can be configured to access the registers of connecting components
	(like L3 cache) during real time debugging.

Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
Signed-off-by: John Garry <john.garry@huawei.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 drivers/soc/Kconfig                 |   1 +
 drivers/soc/Makefile                |   1 +
 drivers/soc/hisilicon/Kconfig       |  12 +
 drivers/soc/hisilicon/Makefile      |   1 +
 drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
 include/linux/soc/hisilicon/djtag.h |  38 +++
 6 files changed, 692 insertions(+)
 create mode 100644 drivers/soc/hisilicon/Kconfig
 create mode 100644 drivers/soc/hisilicon/Makefile
 create mode 100644 drivers/soc/hisilicon/djtag.c
 create mode 100644 include/linux/soc/hisilicon/djtag.h

diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index e6e90e8..89ecd42 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -3,6 +3,7 @@ menu "SOC (System On Chip) specific Drivers"
 source "drivers/soc/bcm/Kconfig"
 source "drivers/soc/fsl/qbman/Kconfig"
 source "drivers/soc/fsl/qe/Kconfig"
+source "drivers/soc/hisilicon/Kconfig"
 source "drivers/soc/mediatek/Kconfig"
 source "drivers/soc/qcom/Kconfig"
 source "drivers/soc/rockchip/Kconfig"
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index 50c23d0..ce2beb5 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -6,6 +6,7 @@ obj-y				+= bcm/
 obj-$(CONFIG_ARCH_DOVE)		+= dove/
 obj-$(CONFIG_MACH_DOVE)		+= dove/
 obj-y				+= fsl/
+obj-$(CONFIG_ARCH_HISI)		+= hisilicon/
 obj-$(CONFIG_ARCH_MEDIATEK)	+= mediatek/
 obj-$(CONFIG_ARCH_QCOM)		+= qcom/
 obj-$(CONFIG_ARCH_RENESAS)	+= renesas/
diff --git a/drivers/soc/hisilicon/Kconfig b/drivers/soc/hisilicon/Kconfig
new file mode 100644
index 0000000..6dd4ba0
--- /dev/null
+++ b/drivers/soc/hisilicon/Kconfig
@@ -0,0 +1,12 @@
+#
+# Hisilicon SoC drivers
+#
+config HISI_DJTAG
+	bool "Hisilicon Djtag Support"
+	depends on ARCH_HISI || COMPILE_TEST
+	help
+	  Say y here to enable the Hisilicon Djtag support. It is
+	  an independent component which connects with some other
+	  components in the SoC by Debug Bus. This driver can be
+	  configured to access the registers of connecting
+	  components during real time debugging.
diff --git a/drivers/soc/hisilicon/Makefile b/drivers/soc/hisilicon/Makefile
new file mode 100644
index 0000000..35a7b4b
--- /dev/null
+++ b/drivers/soc/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_DJTAG)	+= djtag.o
diff --git a/drivers/soc/hisilicon/djtag.c b/drivers/soc/hisilicon/djtag.c
new file mode 100644
index 0000000..a87c8b6
--- /dev/null
+++ b/drivers/soc/hisilicon/djtag.c
@@ -0,0 +1,639 @@
+/*
+ * Driver for Hisilicon Djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/bitops.h>
+#include <linux/init.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include <asm-generic/delay.h>
+#include <linux/soc/hisilicon/djtag.h>
+
+#define SC_DJTAG_TIMEOUT		100000	/* 100ms */
+
+/* for djtag v1 */
+#define SC_DJTAG_MSTR_EN		0x6800
+#define DJTAG_NOR_CFG			BIT(1)	/* accelerate R,W */
+#define DJTAG_MSTR_EN			BIT(0)
+#define SC_DJTAG_MSTR_START_EN		0x6804
+#define DJTAG_MSTR_START_EN		0x1
+#define SC_DJTAG_DEBUG_MODULE_SEL	0x680c
+#define SC_DJTAG_MSTR_WR		0x6810
+#define DJTAG_MSTR_W			0x1
+#define DJTAG_MSTR_R			0x0
+#define SC_DJTAG_CHAIN_UNIT_CFG_EN	0x6814
+#define CHAIN_UNIT_CFG_EN		0xFFFF
+#define SC_DJTAG_MSTR_ADDR		0x6818
+#define SC_DJTAG_MSTR_DATA		0x681c
+#define SC_DJTAG_RD_DATA_BASE		0xe800
+
+/* for djtag v2 */
+#define SC_DJTAG_SEC_ACC_EN_EX		0xd800
+#define DJTAG_SEC_ACC_EN_EX		0x1
+#define SC_DJTAG_MSTR_CFG_EX		0xd818
+#define DJTAG_MSTR_RW_SHIFT_EX		29
+#define DJTAG_MSTR_RD_EX		(0x0 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DJTAG_MSTR_WR_EX		(0x1 << DJTAG_MSTR_RW_SHIFT_EX)
+#define DEBUG_MODULE_SEL_SHIFT_EX	16
+#define CHAIN_UNIT_CFG_EN_EX		0xFFFF
+#define SC_DJTAG_MSTR_ADDR_EX		0xd810
+#define SC_DJTAG_MSTR_DATA_EX		0xd814
+#define SC_DJTAG_MSTR_START_EN_EX	0xd81c
+#define DJTAG_MSTR_START_EN_EX		0x1
+#define SC_DJTAG_RD_DATA_BASE_EX	0xe800
+#define SC_DJTAG_OP_ST_EX		0xe828
+#define DJTAG_OP_DONE_EX		BIT(8)
+
+#define DJTAG_PREFIX "hisi-djtag-dev-"
+
+DEFINE_IDR(djtag_hosts_idr);
+
+struct hisi_djtag_host {
+	spinlock_t lock;
+	int id;
+	struct device dev;
+	struct list_head client_list;
+	void __iomem *sysctl_reg_map;
+	struct device_node *of_node;
+	int (*djtag_readwrite)(void __iomem *regs_base, u32 offset,
+			u32 mod_sel, u32 mod_mask, bool is_w,
+			u32 wval, int chain_id, u32 *rval);
+};
+
+#define to_hisi_djtag_client(d) container_of(d, struct hisi_djtag_client, dev)
+#define to_hisi_djtag_driver(d) container_of(d, struct hisi_djtag_driver, \
+								 driver)
+#define MODULE_PREFIX "hisi_djtag:"
+
+static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	*value = readl_relaxed(reg_addr);
+}
+
+static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	writel(val, reg_addr);
+}
+
+/*
+ * djtag_readwrite_v1/v2: djtag read/write interface
+ * @reg_base:	djtag register base address
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules for write
+ * @is_w:	write -> true, read -> false
+ * @wval:	value to register for write
+ * @chain_id:	which sub module for read
+ * @rval:	value in register for read
+ *
+ * Return non-zero if error, else return 0.
+ */
+static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
+		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	/* djtag mster enable & accelerate R,W */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
+			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
+
+	/* select module */
+	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
+	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
+				mod_mask & CHAIN_UNIT_CFG_EN);
+
+	if (is_w) {
+		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
+		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
+	} else
+		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
+
+	/* address offset */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
+
+	/* start to write to djtag register */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
+
+	/* ensure the djtag operation is done */
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
+		if (!(rd & DJTAG_MSTR_EN))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0) {
+		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+		return -EBUSY;
+	}
+
+	if (!is_w)
+		djtag_read32_relaxed(regs_base,
+			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
+
+	return 0;
+}
+
+static int djtag_readwrite_v2(void __iomem *regs_base, u32 offset, u32 mod_sel,
+		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
+{
+	u32 rd;
+	int timeout = SC_DJTAG_TIMEOUT;
+
+	if (!(mod_mask & CHAIN_UNIT_CFG_EN_EX)) {
+		pr_warn("djtag: do nothing.\n");
+		return 0;
+	}
+
+	/* djtag mster enable */
+	djtag_write32(regs_base, SC_DJTAG_SEC_ACC_EN_EX, DJTAG_SEC_ACC_EN_EX);
+
+	if (is_w) {
+		djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_WR_EX
+				| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+				| (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA_EX, wval);
+	} else
+		djtag_write32(regs_base, SC_DJTAG_MSTR_CFG_EX, DJTAG_MSTR_RD_EX
+				| (mod_sel << DEBUG_MODULE_SEL_SHIFT_EX)
+				| (mod_mask & CHAIN_UNIT_CFG_EN_EX));
+
+	/* address offset */
+	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR_EX, offset);
+
+	/* start to write to djtag register */
+	djtag_write32(regs_base,
+		      SC_DJTAG_MSTR_START_EN_EX, DJTAG_MSTR_START_EN_EX);
+
+	/* ensure the djtag operation is done */
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN_EX, &rd);
+
+		if (!(rd & DJTAG_MSTR_START_EN_EX))
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	timeout = SC_DJTAG_TIMEOUT;
+	do {
+		djtag_read32_relaxed(regs_base, SC_DJTAG_OP_ST_EX, &rd);
+
+		if (rd & DJTAG_OP_DONE_EX)
+			break;
+
+		udelay(1);
+	} while (timeout--);
+
+	if (timeout < 0)
+		goto timeout;
+
+	if (!is_w)
+		djtag_read32_relaxed(regs_base,
+				     SC_DJTAG_RD_DATA_BASE_EX + chain_id * 0x4,
+									rval);
+
+	return 0;
+
+timeout:
+	pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
+	return -EBUSY;
+}
+
+
+/**
+ * djtag_writel - write registers via djtag
+ * @node:	djtag node
+ * @offset:	register's offset
+ * @mod_sel:	module selection
+ * @mod_mask:	mask to select specific modules
+ * @val:	value to write to register
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+							u32 mod_mask, u32 val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_readwrite(reg_map, offset, mod_sel, mod_mask,
+					true, val, 0, NULL);
+	if (ret)
+		pr_err("djtag_writel: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_writel);
+
+/**
+ * djtag_readl - read registers via djtag
+ * @node:	djtag node
+ * @offset:	register's offset
+ * @mod_sel:	module type selection
+ * @chain_id:	chain_id number, mostly is 0
+ * @val:	register's value
+ *
+ * If error return errno, otherwise return 0.
+ */
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset, u32 mod_sel,
+							int chain_id, u32 *val)
+{
+	void __iomem *reg_map = client->host->sysctl_reg_map;
+	unsigned long flags;
+	int ret = 0;
+
+	spin_lock_irqsave(&client->host->lock, flags);
+	ret = client->host->djtag_readwrite(reg_map, offset, mod_sel,
+			0xffff, false, 0, chain_id, val);
+	if (ret)
+		pr_err("djtag_readl: error! ret=%d\n", ret);
+	spin_unlock_irqrestore(&client->host->lock, flags);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(hisi_djtag_readl);
+
+static const struct of_device_id djtag_of_match[] = {
+	/* for hip05(D02) cpu die */
+	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip05(D02) io die */
+	{ .compatible = "hisilicon,hip05-io-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip06(D03) cpu die */
+	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
+		.data = (void *)djtag_readwrite_v1 },
+	/* for hip06(D03) io die */
+	{ .compatible = "hisilicon,hip06-io-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	/* for hip07(D05) cpu die */
+	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	/* for hip07(D05) io die */
+	{ .compatible = "hisilicon,hip07-io-djtag-v2",
+		.data = (void *)djtag_readwrite_v2 },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, djtag_of_match);
+
+static ssize_t
+show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
+{
+	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
+
+	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
+}
+static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
+
+static struct attribute *hisi_djtag_dev_attrs[] = {
+	NULL,
+	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
+	&dev_attr_modalias.attr,
+	NULL
+};
+ATTRIBUTE_GROUPS(hisi_djtag_dev);
+
+static struct device_type hisi_djtag_client_type = {
+	.groups		= hisi_djtag_dev_groups,
+};
+
+struct hisi_djtag_client *hisi_djtag_verify_client(struct device *dev)
+{
+	return (dev->type == &hisi_djtag_client_type)
+			? to_hisi_djtag_client(dev)
+			: NULL;
+}
+
+static int hisi_djtag_device_probe(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->probe(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_remove(struct device *dev)
+{
+	struct hisi_djtag_driver *driver;
+	struct hisi_djtag_client *client;
+	int rc;
+
+	client = hisi_djtag_verify_client(dev);
+	if (!client) {
+		dev_err(dev, "could not find client\n");
+		return -ENODEV;
+	}
+
+	driver = to_hisi_djtag_driver(dev->driver);
+	if (!driver) {
+		dev_err(dev, "could not find driver\n");
+		return -ENODEV;
+	}
+
+	rc = driver->remove(client);
+	if (rc < 0) {
+		dev_err(dev, "client probe failed\n");
+		return rc;
+	}
+
+	return 0;
+}
+
+static int hisi_djtag_device_match(struct device *dev,
+					struct device_driver *drv)
+{
+	const struct of_device_id *p;
+	struct hisi_djtag_client *client = hisi_djtag_verify_client(dev);
+
+	if (!client)
+		return false;
+
+	if (of_driver_match_device(dev, drv))
+		return true;
+
+	p = of_match_device(drv->of_match_table, dev);
+	if (!p)
+		return false;
+
+	return true;
+}
+
+struct bus_type hisi_djtag_bus = {
+	.name		= "hisi-djtag",
+	.match		= hisi_djtag_device_match,
+	.probe		= hisi_djtag_device_probe,
+	.remove		= hisi_djtag_device_remove,
+};
+
+struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
+						struct device_node *node)
+{
+	struct hisi_djtag_client *client;
+	int status;
+
+	client = kzalloc(sizeof(*client), GFP_KERNEL);
+	if (!client)
+		return NULL;
+
+	client->host = host;
+
+	client->dev.parent = &client->host->dev;
+	client->dev.bus = &hisi_djtag_bus;
+	client->dev.type = &hisi_djtag_client_type;
+	client->dev.of_node = node;
+	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
+					DJTAG_PREFIX, node->name);
+	dev_set_name(&client->dev, "%s", client->name);
+
+	status = device_register(&client->dev);
+	if (status < 0) {
+		pr_err("error adding new device, status=%d\n", status);
+		kfree(client);
+		return NULL;
+	}
+
+	return client;
+}
+
+static struct hisi_djtag_client *hisi_djtag_of_register_device(
+						struct hisi_djtag_host *host,
+						struct device_node *node)
+{
+	struct hisi_djtag_client *client;
+
+	client = hisi_djtag_new_device(host, node);
+	if (client == NULL) {
+		dev_err(&host->dev, "error registering device %s\n",
+			node->full_name);
+		of_node_put(node);
+		return ERR_PTR(-EINVAL);
+	}
+
+	return client;
+}
+
+static void djtag_register_devices(struct hisi_djtag_host *host)
+{
+	struct device_node *node;
+	struct hisi_djtag_client *client;
+
+	if (!host->of_node)
+		return;
+
+	for_each_available_child_of_node(host->of_node, node) {
+		if (of_node_test_and_set_flag(node, OF_POPULATED))
+			continue;
+		client = hisi_djtag_of_register_device(host, node);
+		list_add(&client->next, &host->client_list);
+	}
+}
+
+static int hisi_djtag_add_host(struct hisi_djtag_host *host)
+{
+	int rc;
+
+	host->dev.bus = &hisi_djtag_bus;
+
+	rc = idr_alloc(&djtag_hosts_idr, host, 0, 0, GFP_KERNEL);
+	if (rc < 0) {
+		dev_err(&host->dev, "No available djtag host ID'!s\n");
+		return rc;
+	}
+	host->id = rc;
+
+	/* Suffix the unique ID and set djtag hostname */
+	dev_set_name(&host->dev, "djtag-host-%d", host->id);
+
+	rc = device_register(&host->dev);
+	if (rc < 0) {
+		dev_err(&host->dev, "add_host dev register failed, rc=%d\n",
+									rc);
+		idr_remove(&djtag_hosts_idr, host->id);
+		return rc;
+	}
+
+	djtag_register_devices(host);
+
+	return 0;
+}
+
+static int djtag_host_probe(struct platform_device *pdev)
+{
+	struct device *dev = &pdev->dev;
+	struct hisi_djtag_host *host;
+	const struct of_device_id *of_id;
+	struct resource *res;
+	int rc;
+
+	of_id = of_match_device(djtag_of_match, dev);
+	if (!of_id)
+		return -EINVAL;
+
+	host = kzalloc(sizeof(*host), GFP_KERNEL);
+	if (!host)
+		return -ENOMEM;
+
+	host->of_node = dev->of_node;
+	host->djtag_readwrite = of_id->data;
+	spin_lock_init(&host->lock);
+
+	INIT_LIST_HEAD(&host->client_list);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	if (!res) {
+		dev_err(&pdev->dev, "No reg resorces!\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	if (!resource_size(res)) {
+		dev_err(&pdev->dev, "Zero reg entry!\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
+	if (IS_ERR(host->sysctl_reg_map)) {
+		dev_warn(dev, "Unable to map sysctl registers.\n");
+		kfree(host);
+		return -EINVAL;
+	}
+
+	platform_set_drvdata(pdev, host);
+
+	rc = hisi_djtag_add_host(host);
+	if (rc) {
+		dev_err(dev, "add host failed, rc=%d\n", rc);
+		kfree(host);
+		return rc;
+	}
+
+	return 0;
+}
+
+static int djtag_host_remove(struct platform_device *pdev)
+{
+	struct hisi_djtag_host *host;
+	struct hisi_djtag_client *client, *tmp;
+	struct list_head *client_list;
+
+	host = platform_get_drvdata(pdev);
+	client_list = &host->client_list;
+
+	list_for_each_entry_safe(client, tmp, client_list, next) {
+		list_del(&client->next);
+		kfree(client);
+	}
+
+	device_unregister(&host->dev);
+	idr_remove(&djtag_hosts_idr, host->id);
+	kfree(host);
+
+	return 0;
+}
+
+static struct platform_driver djtag_dev_driver = {
+	.driver = {
+		.name = "hisi-djtag",
+		.of_match_table = djtag_of_match,
+	},
+	.probe = djtag_host_probe,
+	.remove = djtag_host_remove,
+};
+module_platform_driver(djtag_dev_driver);
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver)
+{
+	int rc;
+
+	driver->driver.owner = owner;
+	driver->driver.bus = &hisi_djtag_bus;
+
+	rc = driver_register(&driver->driver);
+	if (rc < 0)
+		pr_err("%s register failed, rc=%d\n", __func__, rc);
+
+	return rc;
+}
+
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver)
+{
+	driver->driver.bus = &hisi_djtag_bus;
+	driver_unregister(&driver->driver);
+}
+
+static int __init hisi_djtag_init(void)
+{
+	int rc;
+
+	rc = bus_register(&hisi_djtag_bus);
+	if (rc) {
+		pr_err("hisi  djtag init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_djtag_init);
+
+static void __exit hisi_djtag_exit(void)
+{
+	bus_unregister(&hisi_djtag_bus);
+}
+module_exit(hisi_djtag_exit);
+
+MODULE_DESCRIPTION("Hisilicon djtag driver");
+MODULE_LICENSE("GPL");
+MODULE_VERSION("1.0");
diff --git a/include/linux/soc/hisilicon/djtag.h b/include/linux/soc/hisilicon/djtag.h
new file mode 100644
index 0000000..aae3bca
--- /dev/null
+++ b/include/linux/soc/hisilicon/djtag.h
@@ -0,0 +1,38 @@
+/*
+ * Driver for Hisilicon djtag r/w via System Controller.
+ *
+ * Copyright (C) 2016-2017 Hisilicon Ltd.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __HISI_DJTAG_H
+#define __HISI_DJTAG_H
+
+#define DJTAG_CLIENT_NAME_LEN 24
+
+struct hisi_djtag_client {
+	struct hisi_djtag_host *host;
+	struct list_head next;
+	char name[DJTAG_CLIENT_NAME_LEN];
+	struct device dev;
+};
+
+struct hisi_djtag_driver {
+	struct device_driver driver;
+	int (*probe)(struct hisi_djtag_client *);
+	int (*remove)(struct hisi_djtag_client *);
+};
+
+extern struct bus_type hisi_djtag_bus;
+
+int hisi_djtag_register_driver(struct module *owner,
+				struct hisi_djtag_driver *driver);
+void hisi_djtag_unregister_driver(struct hisi_djtag_driver *driver);
+int hisi_djtag_readl(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, int chain_id, u32 *val);
+int hisi_djtag_writel(struct hisi_djtag_client *client, u32 offset,
+					u32 mod_sel, u32 mod_mask, u32 val);
+#endif /* __HISI_DJTAG_H */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 04/11] Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event counting.
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

	Documentation for perf usage and Hisilicon SoC PMU uncore events.
	The Hisilicon SOC has event counters for hardware modules like
	L3 cache, Miscellaneous node, DDR cntroller etc. These events are
	all uncore.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 Documentation/perf/hisi-pmu.txt | 80 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/perf/hisi-pmu.txt

diff --git a/Documentation/perf/hisi-pmu.txt b/Documentation/perf/hisi-pmu.txt
new file mode 100644
index 0000000..670a9df
--- /dev/null
+++ b/Documentation/perf/hisi-pmu.txt
@@ -0,0 +1,80 @@
+Hisilicon SoC PMU (Performance Monitoring Unit)
+================================================
+The Hisilicon SoC hip05/06/07 chips consist of varous independent system
+device PMU's such as L3 cache(L3C), Miscellaneous Nodes(MN) and DDR
+controllers. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+Hip0x chips are encapsulated by multiple CPU and IO die's. The CPU die is
+called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+Each SCCL has 1 L3 cache and 1 MN units.
+
+The L3 cache is shared by all CPU cores in a CPU die. The L3C has four banks
+(or instances). Each bank or instance of L3C has Eight 32-bit counter
+registers. The hip05/06 chip L3 cache has 22 statistics events. The hip07
+chip has 66 statistics events. These events are very useful for debugging.
+
+The MN module is also shared by all CPU cores in a CPU die. It receives
+barriers and DVM(Distributed Virtual Memory) messages from cpu or smmu, and
+perform the required actions and return response messages. These events are
+very useful for debugging. The MN has total 9 statistics events and support
+four 32-bit counter registers in hip05/06/07 chips.
+
+The DDR conroller supports various statistics events. Every SCCL has fot 2
+DDR channels and hence 2 DDR controllers. The Hip05/06/07 has support for a
+total of 13 statistics events.
+
+There is no memory mapping for L3 cache and MN registers. It can be accessed
+by using the Hisilicon djtag interface. The Djtag in a SCCL is an independent
+module which connects with some modules in the SoC by Debug Bus.
+
+Hisilicon SoC (hip05/06/07) PMU driver
+--------------------------------------
+The hip0x PMU driver shall register perf PMU drivers like L3 cache, MN, DDRC
+etc.
+Separate PMU shall be registered for L3 cache and MN for each Super CPU
+cluster.
+For DRR controller separate PMU shall be registered for each channel in a
+Super CPU cluster.
+
+The available events and configuration options shall be described in the sysfs.
+The "perf list" shall list the available events from sysfs.
+eg. hisi_l3c2/read_allocate/ [kernel PMU event]
+
+The Super Cluster ID will be the number suffix to PMU name
+e.g. hisi_l3c2. Here Super cluster ID is 2 and so hisi_l3c2/read_allocate
+is the event for read_allocate of SCCL #2.
+
+For DDRC the channel number will be suffix at the end.
+eg: hisi_ddrc2_0/flux_read/. Here Super cluster ID is 2 and the channel number
+is 0 for the event flux_read.
+
+The event code is represented by 12 bits.
+	i) event 0-11
+		The event code will be represented using the LSB 12 bits.
+
+The driver also provides a "cpumask" sysfs attribute, which shows the CPU core
+ID used to count the uncore PMU event.
+
+Example usage of perf:
+$# perf list
+hisi_l3c2/read_hit/ [kernel PMU event]
+hisi_l3c2/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_l3c1/read_hit/ [kernel PMU event]
+hisi_l3c1/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_mn2/read_req/ [kernel PMU event]
+hisi_mn2/write_req/ [kernel PMU event]
+------------------------------------------
+hisi_ddrc2_0/flux_read/ [kernel PMU event]
+------------------------------------------
+
+$# perf stat -a -e hisi_l3c2/read_allocate/ sleep 5
+
+The current driver doesnot support sampling. so "perf record" is unsupported.
+Also attach to a task is unsupported as the events are all uncore.
+
+Note: Please contact the maintainer for a complete list of events supported for
+the PMU devices in the SoC and its information if needed.
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 04/11] Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event counting.
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	Documentation for perf usage and Hisilicon SoC PMU uncore events.
	The Hisilicon SOC has event counters for hardware modules like
	L3 cache, Miscellaneous node, DDR cntroller etc. These events are
	all uncore.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 Documentation/perf/hisi-pmu.txt | 80 +++++++++++++++++++++++++++++++++++++++++
 1 file changed, 80 insertions(+)
 create mode 100644 Documentation/perf/hisi-pmu.txt

diff --git a/Documentation/perf/hisi-pmu.txt b/Documentation/perf/hisi-pmu.txt
new file mode 100644
index 0000000..670a9df
--- /dev/null
+++ b/Documentation/perf/hisi-pmu.txt
@@ -0,0 +1,80 @@
+Hisilicon SoC PMU (Performance Monitoring Unit)
+================================================
+The Hisilicon SoC hip05/06/07 chips consist of varous independent system
+device PMU's such as L3 cache(L3C), Miscellaneous Nodes(MN) and DDR
+controllers. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+Hip0x chips are encapsulated by multiple CPU and IO die's. The CPU die is
+called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+Each SCCL has 1 L3 cache and 1 MN units.
+
+The L3 cache is shared by all CPU cores in a CPU die. The L3C has four banks
+(or instances). Each bank or instance of L3C has Eight 32-bit counter
+registers. The hip05/06 chip L3 cache has 22 statistics events. The hip07
+chip has 66 statistics events. These events are very useful for debugging.
+
+The MN module is also shared by all CPU cores in a CPU die. It receives
+barriers and DVM(Distributed Virtual Memory) messages from cpu or smmu, and
+perform the required actions and return response messages. These events are
+very useful for debugging. The MN has total 9 statistics events and support
+four 32-bit counter registers in hip05/06/07 chips.
+
+The DDR conroller supports various statistics events. Every SCCL has fot 2
+DDR channels and hence 2 DDR controllers. The Hip05/06/07 has support for a
+total of 13 statistics events.
+
+There is no memory mapping for L3 cache and MN registers. It can be accessed
+by using the Hisilicon djtag interface. The Djtag in a SCCL is an independent
+module which connects with some modules in the SoC by Debug Bus.
+
+Hisilicon SoC (hip05/06/07) PMU driver
+--------------------------------------
+The hip0x PMU driver shall register perf PMU drivers like L3 cache, MN, DDRC
+etc.
+Separate PMU shall be registered for L3 cache and MN for each Super CPU
+cluster.
+For DRR controller separate PMU shall be registered for each channel in a
+Super CPU cluster.
+
+The available events and configuration options shall be described in the sysfs.
+The "perf list" shall list the available events from sysfs.
+eg. hisi_l3c2/read_allocate/ [kernel PMU event]
+
+The Super Cluster ID will be the number suffix to PMU name
+e.g. hisi_l3c2. Here Super cluster ID is 2 and so hisi_l3c2/read_allocate
+is the event for read_allocate of SCCL #2.
+
+For DDRC the channel number will be suffix at the end.
+eg: hisi_ddrc2_0/flux_read/. Here Super cluster ID is 2 and the channel number
+is 0 for the event flux_read.
+
+The event code is represented by 12 bits.
+	i) event 0-11
+		The event code will be represented using the LSB 12 bits.
+
+The driver also provides a "cpumask" sysfs attribute, which shows the CPU core
+ID used to count the uncore PMU event.
+
+Example usage of perf:
+$# perf list
+hisi_l3c2/read_hit/ [kernel PMU event]
+hisi_l3c2/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_l3c1/read_hit/ [kernel PMU event]
+hisi_l3c1/write_hit/ [kernel PMU event]
+------------------------------------------
+hisi_mn2/read_req/ [kernel PMU event]
+hisi_mn2/write_req/ [kernel PMU event]
+------------------------------------------
+hisi_ddrc2_0/flux_read/ [kernel PMU event]
+------------------------------------------
+
+$# perf stat -a -e hisi_l3c2/read_allocate/ sleep 5
+
+The current driver doesnot support sampling. so "perf record" is unsupported.
+Also attach to a task is unsupported as the events are all uncore.
+
+Note: Please contact the maintainer for a complete list of events supported for
+the PMU devices in the SoC and its information if needed.
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

	1) Device tree bindings for Hisilicon SoC PMU.
	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
 1 file changed, 127 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
new file mode 100644
index 0000000..e7b35e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
@@ -0,0 +1,127 @@
+Hisilicon SoC hip05/06/07 ARMv8 PMU
+===================================
+
+The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
+system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
+comtroller. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
+is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
+
+The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
+For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
+the parent node will be the djtag node of the corresponding CPU die(SCCL).
+
+For uncore PMU devices there are some common required properties as detailed
+below.
+
+Required properties:
+	- compatible : This field contain two values. The first value is
+		always "hisilicon" and second value is the Module type as shown
+		in below examples:
+		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
+			device (Version 1)
+		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
+			device (Version 1)
+		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
+			device (Version 1)
+		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
+
+	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
+		   or IO die in the chip.
+
+	- num-events : No of events supported by this PMU device.
+
+	- num-counters : No of hardware counters available for counting.
+
+L3 cache
+--------
+The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
+for L3 cache for each SCCL. For L3 cache PMU the additional required properties
+are
+	- counter-reg : Counter register offset.
+
+	- evtype-reg : Event select register offset.
+
+	- evctrl-reg : Event counting control(LAUCTRL) register offset.
+
+	- event-en : Event enable value.
+
+	- module-id : Module ID to input for djtag. This property is an array of
+		      module_id for each L3 cache banks.
+
+	- num-banks : Number of banks or instances of the device.
+
+	- cfgen-map : Config enable array to select the bank.
+
+Miscellaneous Node
+-------------------
+The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
+for each SCCL. For MN PMU the additional required properties are
+	- counter-reg : Counter register offset.
+
+	- evtype-reg : Event select register offset.
+
+	- evctrl-reg : Event counting control register offset.
+
+	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
+		      this property is a single value.
+
+	- cfgen-map : Config enable to select the bank. For MN it is a single value
+
+	- event-en : Event enable value.
+
+Example:
+
+	djtag0: djtag@0 {
+		compatible = "hisilicon,hip05-cpu-djtag-v1";
+		pmul3c0 {
+			compatible = "hisilicon,hisi-pmu-l3c-v1";
+			scl-id = <0x02>;
+			num-events = <0x16>;
+			num-counters = <0x08>;
+			module-id = <0x04 0x04 0x04 0x04>;
+			num-banks = <0x04>;
+			cfgen-map = <0x02 0x04 0x01 0x08>;
+			counter-reg = <0x170>;
+			evctrl-reg = <0x04>;
+			event-en = <0x1000000>;
+			evtype-reg = <0x140>;
+		};
+
+		pmumn0 {
+			compatible = "hisilicon,hisi-pmu-mn-v1";
+			scl-id = <0x02>;
+			num-events = <0x09>;
+			num-counters = <0x04>;
+			module-id = <0x0b>;
+			cfgen-map = <0x01>;
+			counter-reg = <0x30>;
+			evctrl-reg = <0x40>;
+			event-en = <0x01>;
+			evtype-reg = <0x48>;
+		};
+	};
+
+DDR controller
+--------------
+Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
+There are separate DT nodes for each DDR channel.
+For DDRC PMU the additional required properties are
+
+	- ch-id : DDRC Channel ID.
+	- reg : Register base address and range for the DDRC channel.
+
+Example:
+	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
+	pmu_sccl0_ddrc1: pmu_ddrc1@80358000 {
+		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
+		 scl-id = <0x02>;
+		 ch-id = <0x1>;
+		 num-events = <0x0D>;
+		 num-counters = <0x04>;
+		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
+	 };
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1) Device tree bindings for Hisilicon SoC PMU.
	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
 1 file changed, 127 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt

diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
new file mode 100644
index 0000000..e7b35e0
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
@@ -0,0 +1,127 @@
+Hisilicon SoC hip05/06/07 ARMv8 PMU
+===================================
+
+The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
+system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
+comtroller. These PMU devices are independent and have hardware logic to
+gather statistics and performance information.
+
+HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
+is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
+is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
+e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
+
+The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
+For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
+the parent node will be the djtag node of the corresponding CPU die(SCCL).
+
+For uncore PMU devices there are some common required properties as detailed
+below.
+
+Required properties:
+	- compatible : This field contain two values. The first value is
+		always "hisilicon" and second value is the Module type as shown
+		in below examples:
+		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
+			device (Version 1)
+		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
+			device (Version 1)
+		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
+			device (Version 1)
+		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
+
+	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
+		   or IO die in the chip.
+
+	- num-events : No of events supported by this PMU device.
+
+	- num-counters : No of hardware counters available for counting.
+
+L3 cache
+--------
+The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
+for L3 cache for each SCCL. For L3 cache PMU the additional required properties
+are
+	- counter-reg : Counter register offset.
+
+	- evtype-reg : Event select register offset.
+
+	- evctrl-reg : Event counting control(LAUCTRL) register offset.
+
+	- event-en : Event enable value.
+
+	- module-id : Module ID to input for djtag. This property is an array of
+		      module_id for each L3 cache banks.
+
+	- num-banks : Number of banks or instances of the device.
+
+	- cfgen-map : Config enable array to select the bank.
+
+Miscellaneous Node
+-------------------
+The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
+for each SCCL. For MN PMU the additional required properties are
+	- counter-reg : Counter register offset.
+
+	- evtype-reg : Event select register offset.
+
+	- evctrl-reg : Event counting control register offset.
+
+	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
+		      this property is a single value.
+
+	- cfgen-map : Config enable to select the bank. For MN it is a single value
+
+	- event-en : Event enable value.
+
+Example:
+
+	djtag0: djtag at 0 {
+		compatible = "hisilicon,hip05-cpu-djtag-v1";
+		pmul3c0 {
+			compatible = "hisilicon,hisi-pmu-l3c-v1";
+			scl-id = <0x02>;
+			num-events = <0x16>;
+			num-counters = <0x08>;
+			module-id = <0x04 0x04 0x04 0x04>;
+			num-banks = <0x04>;
+			cfgen-map = <0x02 0x04 0x01 0x08>;
+			counter-reg = <0x170>;
+			evctrl-reg = <0x04>;
+			event-en = <0x1000000>;
+			evtype-reg = <0x140>;
+		};
+
+		pmumn0 {
+			compatible = "hisilicon,hisi-pmu-mn-v1";
+			scl-id = <0x02>;
+			num-events = <0x09>;
+			num-counters = <0x04>;
+			module-id = <0x0b>;
+			cfgen-map = <0x01>;
+			counter-reg = <0x30>;
+			evctrl-reg = <0x40>;
+			event-en = <0x01>;
+			evtype-reg = <0x48>;
+		};
+	};
+
+DDR controller
+--------------
+Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
+There are separate DT nodes for each DDR channel.
+For DDRC PMU the additional required properties are
+
+	- ch-id : DDRC Channel ID.
+	- reg : Register base address and range for the DDRC channel.
+
+Example:
+	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
+	pmu_sccl0_ddrc1: pmu_ddrc1 at 80358000 {
+		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
+		 scl-id = <0x02>;
+		 ch-id = <0x1>;
+		 num-events = <0x0D>;
+		 num-counters = <0x04>;
+		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
+	 };
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 06/11] perf: hisi: Update Kconfig for Hisilicon PMU support
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

	1. Update Kconfig for Hip05/06/07 PMU support.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 drivers/perf/Kconfig | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9..da8dd97 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -19,4 +19,13 @@ config XGENE_PMU
         help
           Say y if you want to use APM X-Gene SoC performance monitors.
 
+config HISI_PMU
+	bool "Enable hardware event counter support for HiSilicon SoC"
+	depends on HW_PERF_EVENTS && ARM64
+	depends on HISI_DJTAG
+	help
+	  Enable hardware event counter support for hardware event counters
+	  in Hisilicon hip05/06/07 SoC. The hardware modules like L3C, MN and
+	  DDRC have hardware events and counters.
+
 endmenu
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 06/11] perf: hisi: Update Kconfig for Hisilicon PMU support
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1. Update Kconfig for Hip05/06/07 PMU support.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 drivers/perf/Kconfig | 9 +++++++++
 1 file changed, 9 insertions(+)

diff --git a/drivers/perf/Kconfig b/drivers/perf/Kconfig
index 4d5c5f9..da8dd97 100644
--- a/drivers/perf/Kconfig
+++ b/drivers/perf/Kconfig
@@ -19,4 +19,13 @@ config XGENE_PMU
         help
           Say y if you want to use APM X-Gene SoC performance monitors.
 
+config HISI_PMU
+	bool "Enable hardware event counter support for HiSilicon SoC"
+	depends on HW_PERF_EVENTS && ARM64
+	depends on HISI_DJTAG
+	help
+	  Enable hardware event counter support for hardware event counters
+	  in Hisilicon hip05/06/07 SoC. The hardware modules like L3C, MN and
+	  DDRC have hardware events and counters.
+
 endmenu
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

	1. Hip05/06/07 uncore PMU to support different hardware
	   event counters.
	2. Hisilicon PMU shall use the DJTAG hardware interface
	   to access hardware event counters and configuration
	   register.
	3. Routines to initialize and setup PMU.
	4. Routines to enable/disable/add/del/start/stop hardware
	   event counting.
	5. Add support to count L3 cache hardware events.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 drivers/perf/Makefile                    |   1 +
 drivers/perf/hisilicon/Makefile          |   1 +
 drivers/perf/hisilicon/hisi_uncore_l3c.c | 571 +++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_l3c.h |  67 ++++
 drivers/perf/hisilicon/hisi_uncore_pmu.c | 331 ++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h | 108 ++++++
 6 files changed, 1079 insertions(+)
 create mode 100644 drivers/perf/hisilicon/Makefile
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h

diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..061f229 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-$(CONFIG_HISI_PMU) += hisilicon/
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
new file mode 100644
index 0000000..e1766cf
--- /dev/null
+++ b/drivers/perf/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
new file mode 100644
index 0000000..f78f7b2
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_l3c.h"
+
+static inline int hisi_l3c_counter_valid(int idx)
+{
+	return (idx >= HISI_IDX_L3C_COUNTER0 &&
+			idx <= HISI_IDX_L3C_COUNTER_MAX);
+}
+
+static u32 hisi_read_l3c_counter(struct hisi_l3c_data *l3c_hwmod_data,
+						int cntr_idx, int bank_idx)
+{
+	struct hisi_djtag_client *client = l3c_hwmod_data->client;
+	u32 module_id = l3c_hwmod_data->l3c_hwcfg.module_id[bank_idx];
+	u32 cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[bank_idx];
+	u32 reg_offset, value;
+
+	reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+							(cntr_idx * 4);
+
+	hisi_djtag_readreg(module_id, cfg_en, reg_offset, client, &value);
+
+	return value;
+}
+
+static u64 hisi_l3c_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *pl3c_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u64 delta, prev_raw_count, total_raw_count = 0, avg_raw_count = 0;
+	u32 num_banks = l3c_hwmod_data->l3c_hwcfg.num_banks;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev, "Unsupported event index:%d!\n", idx);
+		return 0;
+	}
+
+	/* Check if the L3C data is initialized for this SCCL */
+	if (!l3c_hwmod_data->client) {
+		dev_err(pl3c_pmu->dev, "SCL=%d not initialized!\n",
+							pl3c_pmu->scl_id);
+		return 0;
+	}
+
+	do {
+		/* Get count from individual L3C banks and sum them up */
+		for (i = 0; i < num_banks; i++) {
+			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
+									idx, i);
+		}
+		prev_raw_count = local64_read(&hwc->prev_count);
+
+		/*
+		 * As prev_raw_count is updated with average value of
+		 * L3 cache banks, we multiply it by no of banks and
+		 * compute the delta
+		 */
+		delta = (total_raw_count - (prev_raw_count * num_banks)) &
+								HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+
+		/*
+		 * Divide by num of banks to get average count and
+		 * update prev_count with this value
+		 */
+		avg_raw_count = total_raw_count / num_banks;
+	} while (local64_cmpxchg(
+			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
+							 prev_raw_count);
+
+	return total_raw_count;
+}
+
+static void hisi_set_l3c_evtype(struct hisi_pmu *pl3c_pmu, int idx, u32 val)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+	u32 event_value, value = 0;
+	u32 cfg_en, module_id;
+	int i;
+
+	event_value = (val -
+			HISI_HWEVENT_L3C_READ_ALLOCATE);
+
+	/* Select the appropriate Event select register */
+	if (idx > 3)
+		reg_offset += 4;
+
+	/* Value to write to event type register */
+	val = event_value << (8 * idx);
+
+	/* Find the djtag Identifier of the Unit */
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Set the event in L3C_EVENT_TYPEx Register
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(0xff << (8 * idx));
+		value |= val;
+
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static u32 hisi_write_l3c_counter(struct hisi_pmu *pl3c_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset, cfg_en, module_id;
+	int i, ret = 0;
+	int idx = GET_CNTR_IDX(hwc);
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return -EINVAL;
+	}
+
+	reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+							(idx * 4);
+
+	client = l3c_hwmod_data->client;
+
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		ret = hisi_djtag_writereg(module_id,
+					cfg_en,
+					reg_offset,
+					value,
+					client);
+		if (!ret)
+			ret = value;
+	}
+
+	return ret;
+}
+
+static void hisi_enable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+	u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+	u32 value, cfg_en, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Set the event_bus_en bit in L3C AUCNTRL to enable counting
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value |= eventen;
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static void hisi_disable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+	u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+	u32 value, cfg_en, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Clear the event_bus_en bit in L3C AUCNTRL if no other
+	 * event counting for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(eventen);
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static void hisi_clear_l3c_event_idx(struct hisi_pmu *pl3c_pmu,
+							int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+	void *bitmap_addr;
+	u32 cfg_en, value, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	bitmap_addr = l3c_hwmod_data->hisi_l3c_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+
+	/* Clear Counting in L3C event config register */
+	if (idx > 3)
+		reg_offset += 4;
+
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Clear the event in L3C_EVENT_TYPEx Register
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(0xff << (8 * idx));
+		value |= (0xff << (8 * idx));
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
+{
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			l3c_hwmod_data->hisi_l3c_event_used_mask,
+					 pl3c_pmu->num_counters);
+
+	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		l3c_hwmod_data->hisi_l3c_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_l3c_data(struct hisi_pmu *pl3c_pmu)
+{
+	kfree(pl3c_pmu->hwmod_data);
+	pl3c_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_l3c_hwcfg(struct device *dev,
+				struct hisi_l3c_data *pl3c_data)
+{
+	struct hisi_l3c_hwcfg *pl3c_hwcfg = &pl3c_data->l3c_hwcfg;
+	struct device_node *node = dev->of_node;
+	u32 prop_len;
+	int ret;
+
+	if (of_property_read_u32(node, "counter-reg",
+				     &pl3c_hwcfg->counter_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read counter-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evctrl-reg",
+				     &pl3c_hwcfg->event_ctrl_reg_off)) {
+		dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "event-en",
+				     &pl3c_hwcfg->event_enable)) {
+		dev_err(dev, "DT:Couldnot read event-en!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evtype-reg",
+				     &pl3c_hwcfg->evtype_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "num-banks",
+				     &pl3c_hwcfg->num_banks)) {
+		dev_err(dev, "DT:Couldnot read num-banks!\n");
+		return -EINVAL;
+	}
+
+	prop_len = of_property_count_u32_elems(node, "module-id");
+	if (prop_len != pl3c_hwcfg->num_banks) {
+		dev_err(dev, "DT:module-id entry not valid!\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(node, "module-id",
+					  &pl3c_hwcfg->module_id[0],
+						  pl3c_hwcfg->num_banks);
+	if (ret < 0) {
+		dev_err(dev, "DT:Couldnot read module-id!\n");
+		return -EINVAL;
+	}
+
+	prop_len = of_property_count_u32_elems(node, "cfgen-map");
+	if (prop_len != pl3c_hwcfg->num_banks) {
+		dev_err(dev, "DT:cfgen-map entrynot valid!\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(node, "cfgen-map",
+					&pl3c_hwcfg->bank_cfgen[0],
+						pl3c_hwcfg->num_banks);
+	if (ret < 0) {
+		dev_err(dev, "DT:Couldnot read cfgen-map!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int init_hisi_l3c_data(struct device *dev,
+					struct hisi_pmu *pl3c_pmu,
+					struct hisi_djtag_client *client)
+{
+	struct hisi_l3c_data *l3c_hwmod_data = NULL;
+	int ret;
+
+	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
+							GFP_KERNEL);
+	if (!l3c_hwmod_data)
+		return -ENOMEM;
+
+	/* Set the djtag Identifier */
+	l3c_hwmod_data->client = client;
+
+	pl3c_pmu->hw_events.events = devm_kcalloc(dev,
+					pl3c_pmu->num_counters,
+					sizeof(*pl3c_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!pl3c_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&pl3c_pmu->hw_events.pmu_lock);
+
+	pl3c_pmu->hwmod_data = l3c_hwmod_data;
+
+	ret = init_hisi_l3c_hwcfg(dev, l3c_hwmod_data);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	hisi_free_l3c_data(pl3c_pmu);
+	return ret;
+}
+
+static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
+	.set_evtype = hisi_set_l3c_evtype,
+	.set_event_period = hisi_pmu_set_event_period,
+	.get_event_idx = hisi_l3c_get_event_idx,
+	.clear_event_idx = hisi_clear_l3c_event_idx,
+	.event_update = hisi_l3c_event_update,
+	.enable_counter = hisi_enable_l3c_counter,
+	.disable_counter = hisi_disable_l3c_counter,
+	.write_counter = hisi_write_l3c_counter,
+};
+
+static int hisi_l3c_pmu_init(struct device *dev,
+					struct hisi_pmu *pl3c_pmu)
+{
+	int ret;
+
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, pl3c_pmu);
+	if (ret)
+		return ret;
+
+	pl3c_pmu->name = kasprintf(GFP_KERNEL, "hisi_l3c%d",
+						pl3c_pmu->scl_id);
+	pl3c_pmu->ops = &hisi_uncore_l3c_ops;
+	pl3c_pmu->dev = dev;
+
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &pl3c_pmu->cpu);
+
+	return 0;
+}
+
+static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pl3c_pmu = NULL;
+	struct device *dev = &client->dev;
+	int ret;
+
+	pl3c_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(pl3c_pmu))
+		return PTR_ERR(pl3c_pmu);
+
+	ret = hisi_l3c_pmu_init(dev, pl3c_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_l3c_data(dev, pl3c_pmu, client);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	pl3c_pmu->pmu = (struct pmu) {
+		.name = pl3c_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+	};
+
+	ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
+	if (ret) {
+		dev_err(dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	/* Set the drv data to l3c_pmu */
+	dev_set_drvdata(dev, pl3c_pmu);
+
+	return 0;
+
+fail:
+	hisi_free_l3c_data(pl3c_pmu);
+
+fail_init:
+	dev_err(dev, "%s failed\n", __func__);
+	return ret;
+}
+
+static int hisi_pmu_l3c_dev_remove(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pl3c_pmu = NULL;
+	struct device *dev = &client->dev;
+
+	pl3c_pmu = dev_get_drvdata(dev);
+
+	perf_pmu_unregister(&pl3c_pmu->pmu);
+	hisi_free_l3c_data(pl3c_pmu);
+
+	return 0;
+}
+
+static const struct of_device_id l3c_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-l3c-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, l3c_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_l3c_driver = {
+	.driver = {
+		.name = "hisi-pmu-l3c",
+		.of_match_table = l3c_of_match,
+	},
+	.probe = hisi_pmu_l3c_dev_probe,
+	.remove = hisi_pmu_l3c_dev_remove,
+};
+
+static int __init hisi_pmu_l3c_init(void)
+{
+	int rc;
+
+	rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_l3c_driver);
+	if (rc < 0) {
+		pr_err("hisi pmu l3c init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_pmu_l3c_init);
+
+static void __exit hisi_pmu_l3c_exit(void)
+{
+	hisi_djtag_unregister_driver(&hisi_pmu_l3c_driver);
+
+}
+module_exit(hisi_pmu_l3c_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x L3C PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.h b/drivers/perf/hisilicon/hisi_uncore_l3c.h
new file mode 100644
index 0000000..a4a1777
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.h
@@ -0,0 +1,67 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_L3C_H__
+#define __HISI_UNCORE_L3C_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon L3C RAW event types.
+ */
+enum armv8_hisi_l3c_event_types {
+	HISI_HWEVENT_L3C_READ_ALLOCATE		= 0x0,
+	HISI_HWEVENT_L3C_WRITE_ALLOCATE		= 0x01,
+	HISI_HWEVENT_L3C_READ_NOALLOCATE	= 0x02,
+	HISI_HWEVENT_L3C_WRITE_NOALLOCATE	= 0x03,
+	HISI_HWEVENT_L3C_READ_HIT		= 0x04,
+	HISI_HWEVENT_L3C_WRITE_HIT		= 0x05,
+	HISI_HWEVENT_L3C_EVENT_MAX		= 0x15,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_l3c_counters {
+	HISI_IDX_L3C_COUNTER0		= 0x0,
+	HISI_IDX_L3C_COUNTER_MAX	= 0x7,
+};
+
+#define HISI_MAX_CFG_L3C_CNTR	0x08
+
+struct hisi_l3c_hwcfg {
+	u32 evtype_reg0_off;
+	u32 counter_reg0_off;
+	u32 event_ctrl_reg_off;
+	u32 event_enable;
+	u32 module_id[MAX_BANKS];
+	u32 num_banks;
+	u32 bank_cfgen[MAX_BANKS];
+};
+
+struct hisi_l3c_data {
+	struct hisi_djtag_client *client;
+	DECLARE_BITMAP(hisi_l3c_event_used_mask,
+				HISI_MAX_CFG_L3C_CNTR);
+	struct hisi_l3c_hwcfg l3c_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_L3C_H__ */
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
new file mode 100644
index 0000000..8d29fcc
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -0,0 +1,331 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_pmu.h"
+
+/* djtag read interface - Call djtag driver to access SoC registers */
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+				struct hisi_djtag_client *client, u32 *pvalue)
+{
+	int ret;
+	u32 chain_id = 0;
+
+	while (bank != 1) {
+		bank = (bank >> 0x1);
+		chain_id++;
+	}
+
+	ret = hisi_djtag_readl(client, offset, module_id,
+						chain_id, pvalue);
+	if (ret)
+		dev_err(&client->dev, "read failed, ret=%d!\n", ret);
+
+	return ret;
+}
+
+/* djtag write interface - Call djtag driver  to access SoC registers */
+int hisi_djtag_writereg(int module_id, int bank,
+				u32 offset, u32 value,
+				struct hisi_djtag_client *client)
+{
+	int ret;
+
+	ret = hisi_djtag_writel(client, offset, module_id,
+						HISI_DJTAG_MOD_MASK, value);
+	if (ret)
+		dev_err(&client->dev, "write failed, ret=%d!\n", ret);
+
+	return ret;
+}
+
+static int pmu_map_event(struct perf_event *event)
+{
+	return (int)(event->attr.config & HISI_EVTYPE_EVENT);
+}
+
+static int
+__hw_perf_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct device *dev = phisi_pmu->dev;
+	int mapping;
+
+	mapping = pmu_map_event(event);
+	if (mapping < 0) {
+		dev_err(dev, "event %x:%llx not supported\n", event->attr.type,
+							 event->attr.config);
+		return mapping;
+	}
+
+	/*
+	 * We don't assign an index until we actually place the event onto
+	 * hardware. Use -1 to signify that we haven't decided where to put it
+	 * yet.
+	 */
+	hwc->idx		= -1;
+	hwc->config		= 0;
+	hwc->event_base		= 0;
+
+	/* For HiSilicon SoC L3C update config_base based on event encoding */
+	hwc->config_base = event->attr.config;
+
+	return 0;
+}
+
+int hisi_uncore_pmu_event_init(struct perf_event *event)
+{
+	int err;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	if (event->attr.type != event->pmu->type)
+		return -ENOENT;
+
+	/* we do not support sampling as the counters are all
+	 * shared by all CPU cores in a CPU die(SCCL). Also we
+	 * donot support attach to a task(per-process mode)
+	 */
+	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
+		return -EOPNOTSUPP;
+
+	/* counters do not have these bits */
+	if (event->attr.exclude_user	||
+	    event->attr.exclude_kernel	||
+	    event->attr.exclude_host	||
+	    event->attr.exclude_guest	||
+	    event->attr.exclude_hv	||
+	    event->attr.exclude_idle)
+		return -EINVAL;
+
+	if (event->cpu < 0)
+		return -EINVAL;
+
+	event->cpu = cpumask_first(&phisi_pmu->cpu);
+
+	err = __hw_perf_event_init(event);
+
+	return err;
+}
+
+/*
+ * Enable counter and set the counter to count
+ * the event that we're interested in.
+ */
+void hisi_uncore_pmu_enable_event(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/* Disable the hardware event counting */
+	if (phisi_pmu->ops->disable_counter)
+		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+
+	/*
+	 * Set event (if destined for Hisilicon SoC counters).
+	 */
+	if (phisi_pmu->ops->set_evtype)
+		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
+							hwc->config_base);
+
+	/* Enable the hardware event counting */
+	if (phisi_pmu->ops->enable_counter)
+		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+}
+
+void hisi_pmu_set_event_period(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/*
+	 * The Hisilicon PMU counters have a period of 2^32. To account for the
+	 * possiblity of extreme interrupt latency we program for a period of
+	 * half that. Hopefully we can handle the interrupt before another 2^31
+	 * events occur and the counter overtakes its previous value.
+	 */
+	u64 val = 1ULL << 31;
+
+	local64_set(&hwc->prev_count, val);
+
+	/* Write to the hardware event counter */
+	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
+}
+
+void hisi_uncore_pmu_start(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+		return;
+
+	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+	hwc->state = 0;
+
+	if (phisi_pmu->ops->set_event_period)
+		phisi_pmu->ops->set_event_period(event);
+
+	if (flags & PERF_EF_RELOAD) {
+		u64 prev_raw_count =  local64_read(&hwc->prev_count);
+
+		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
+						(u32)prev_raw_count);
+	}
+
+	hisi_uncore_pmu_enable_event(event);
+	perf_event_update_userpage(event);
+}
+
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	if (hwc->state & PERF_HES_UPTODATE)
+		return;
+
+	/*
+	 * We always reprogram the counter, so ignore PERF_EF_UPDATE.
+	 * See hisi_uncore_pmu_start()
+	 */
+	if (phisi_pmu->ops->disable_counter)
+		phisi_pmu->ops->disable_counter(phisi_pmu,
+						GET_CNTR_IDX(hwc));
+
+	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+	hwc->state |= PERF_HES_STOPPED;
+	if (hwc->state & PERF_HES_UPTODATE)
+		return;
+
+	/* Read hardware counter and update the Perf counter statistics */
+	phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+	hwc->state |= PERF_HES_UPTODATE;
+}
+
+int hisi_uncore_pmu_add(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+	int idx;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+
+	/* If we don't have a free counter then return early. */
+	idx = phisi_pmu->ops->get_event_idx(phisi_pmu);
+	if (idx < 0)
+		return -EAGAIN;
+
+	event->hw.idx = idx;
+	hw_events->events[idx] = event;
+
+	if (flags & PERF_EF_START)
+		hisi_uncore_pmu_start(event, PERF_EF_RELOAD);
+
+	/* Propagate our changes to the userspace mapping. */
+	perf_event_update_userpage(event);
+
+	return 0;
+}
+
+void hisi_uncore_pmu_del(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	hisi_uncore_pmu_stop(event, PERF_EF_UPDATE);
+
+	phisi_pmu->ops->clear_event_idx(phisi_pmu, GET_CNTR_IDX(hwc));
+	perf_event_update_userpage(event);
+	hw_events->events[GET_CNTR_IDX(hwc)] = NULL;
+}
+
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev)
+{
+	struct hisi_pmu *phisi_pmu;
+
+	phisi_pmu = devm_kzalloc(dev, sizeof(*phisi_pmu), GFP_KERNEL);
+	if (!phisi_pmu)
+		return ERR_PTR(-ENOMEM);
+
+	return phisi_pmu;
+}
+
+void hisi_uncore_pmu_read(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/* Read hardware counter and update the Perf counter statistics */
+	phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+}
+
+int hisi_uncore_common_fwprop_read(struct device *dev,
+					struct hisi_pmu *phisi_pmu)
+{
+	if (device_property_read_u32(dev, "num-events",
+					&phisi_pmu->num_events)) {
+		dev_err(dev, "Cant read num-events from DT!\n");
+		return -EINVAL;
+	}
+
+	if (device_property_read_u32(dev, "num-counters",
+				     &phisi_pmu->num_counters)) {
+		dev_err(dev, "Cant read num-counters from DT!\n");
+		return -EINVAL;
+	}
+
+	/* Find the SCL ID */
+	if (device_property_read_u32(dev, "scl-id",
+					&phisi_pmu->scl_id)) {
+		dev_err(dev, "Cant read scl-id!\n");
+		return -EINVAL;
+	}
+
+	if (phisi_pmu->scl_id == 0 ||
+		phisi_pmu->scl_id >= MAX_UNITS) {
+		dev_err(dev, "Invalid SCL=%d!\n",
+					phisi_pmu->scl_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu,
+					const char *pmu_name)
+{
+	/* Register the events with perf */
+	return perf_pmu_register(&phisi_pmu->pmu, pmu_name, -1);
+}
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
new file mode 100644
index 0000000..b6b16df
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -0,0 +1,108 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_PMU_H__
+#define __HISI_UNCORE_PMU_H__
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/soc/hisilicon/djtag.h>
+#include <linux/types.h>
+#include <asm/local64.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "hisi_pmu: " fmt
+
+#define HISI_DJTAG_MOD_MASK (0xFFFF)
+#define HISI_CNTR_SCCL_MASK    (0xF00)
+
+#define HISI_EVTYPE_EVENT	0xfff
+#define HISI_MAX_PERIOD ((1LLU << 32) - 1)
+
+#define MAX_BANKS 8
+#define MAX_COUNTERS 30
+#define MAX_UNITS 8
+
+#define GET_CNTR_IDX(hwc) (hwc->idx)
+#define to_hisi_pmu(c)	(container_of(c, struct hisi_pmu, pmu))
+
+#define GET_UNIT_IDX(event_code)		\
+	(((event_code & HISI_SCCL_MASK) >>	\
+			   HISI_SCCL_SHIFT) - 1)
+
+struct hisi_pmu;
+
+struct hisi_uncore_ops {
+	void (*set_evtype)(struct hisi_pmu *, int, u32);
+	void (*set_event_period)(struct perf_event *);
+	int (*get_event_idx)(struct hisi_pmu *);
+	void (*clear_event_idx)(struct hisi_pmu *, int);
+	u64 (*event_update)(struct perf_event *,
+			     struct hw_perf_event *, int);
+	u32 (*read_counter)(struct hisi_pmu *, int, int);
+	u32 (*write_counter)(struct hisi_pmu *,
+				struct hw_perf_event *, u32);
+	void (*enable_counter)(struct hisi_pmu *, int);
+	void (*disable_counter)(struct hisi_pmu *, int);
+};
+
+struct hisi_pmu_hw_events {
+	struct perf_event **events;
+	raw_spinlock_t pmu_lock;
+};
+
+/* Generic pmu struct for different pmu types */
+struct hisi_pmu {
+	const char *name;
+	struct hisi_pmu_hw_events hw_events;
+	struct hisi_uncore_ops *ops;
+	struct device *dev;
+	void *hwmod_data; /* Hardware module specific data */
+	cpumask_t cpu;
+	struct pmu pmu;
+	u32 scl_id;
+	int num_counters;
+	int num_events;
+	int num_units;
+};
+
+void hisi_uncore_pmu_read(struct perf_event *event);
+void hisi_uncore_pmu_del(struct perf_event *event, int flags);
+int hisi_uncore_pmu_add(struct perf_event *event, int flags);
+void hisi_uncore_pmu_start(struct perf_event *event, int flags);
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags);
+void hisi_pmu_set_event_period(struct perf_event *event);
+void hisi_uncore_pmu_enable_event(struct perf_event *event);
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu, const char *pmu_name);
+int hisi_uncore_pmu_event_init(struct perf_event *event);
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+				struct hisi_djtag_client *client,
+							u32 *pvalue);
+int hisi_djtag_writereg(int module_id, int bank,
+				u32 offset, u32 value,
+				struct hisi_djtag_client *client);
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
+int hisi_uncore_common_fwprop_read(struct device *dev,
+					struct hisi_pmu *phisi_pmu);
+#endif /* __HISI_UNCORE_PMU_H__ */
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1. Hip05/06/07 uncore PMU to support different hardware
	   event counters.
	2. Hisilicon PMU shall use the DJTAG hardware interface
	   to access hardware event counters and configuration
	   register.
	3. Routines to initialize and setup PMU.
	4. Routines to enable/disable/add/del/start/stop hardware
	   event counting.
	5. Add support to count L3 cache hardware events.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 drivers/perf/Makefile                    |   1 +
 drivers/perf/hisilicon/Makefile          |   1 +
 drivers/perf/hisilicon/hisi_uncore_l3c.c | 571 +++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_l3c.h |  67 ++++
 drivers/perf/hisilicon/hisi_uncore_pmu.c | 331 ++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h | 108 ++++++
 6 files changed, 1079 insertions(+)
 create mode 100644 drivers/perf/hisilicon/Makefile
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_l3c.h
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_pmu.h

diff --git a/drivers/perf/Makefile b/drivers/perf/Makefile
index b116e98..061f229 100644
--- a/drivers/perf/Makefile
+++ b/drivers/perf/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_ARM_PMU) += arm_pmu.o
 obj-$(CONFIG_XGENE_PMU) += xgene_pmu.o
+obj-$(CONFIG_HISI_PMU) += hisilicon/
diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
new file mode 100644
index 0000000..e1766cf
--- /dev/null
+++ b/drivers/perf/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
new file mode 100644
index 0000000..f78f7b2
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_l3c.h"
+
+static inline int hisi_l3c_counter_valid(int idx)
+{
+	return (idx >= HISI_IDX_L3C_COUNTER0 &&
+			idx <= HISI_IDX_L3C_COUNTER_MAX);
+}
+
+static u32 hisi_read_l3c_counter(struct hisi_l3c_data *l3c_hwmod_data,
+						int cntr_idx, int bank_idx)
+{
+	struct hisi_djtag_client *client = l3c_hwmod_data->client;
+	u32 module_id = l3c_hwmod_data->l3c_hwcfg.module_id[bank_idx];
+	u32 cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[bank_idx];
+	u32 reg_offset, value;
+
+	reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+							(cntr_idx * 4);
+
+	hisi_djtag_readreg(module_id, cfg_en, reg_offset, client, &value);
+
+	return value;
+}
+
+static u64 hisi_l3c_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *pl3c_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u64 delta, prev_raw_count, total_raw_count = 0, avg_raw_count = 0;
+	u32 num_banks = l3c_hwmod_data->l3c_hwcfg.num_banks;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev, "Unsupported event index:%d!\n", idx);
+		return 0;
+	}
+
+	/* Check if the L3C data is initialized for this SCCL */
+	if (!l3c_hwmod_data->client) {
+		dev_err(pl3c_pmu->dev, "SCL=%d not initialized!\n",
+							pl3c_pmu->scl_id);
+		return 0;
+	}
+
+	do {
+		/* Get count from individual L3C banks and sum them up */
+		for (i = 0; i < num_banks; i++) {
+			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
+									idx, i);
+		}
+		prev_raw_count = local64_read(&hwc->prev_count);
+
+		/*
+		 * As prev_raw_count is updated with average value of
+		 * L3 cache banks, we multiply it by no of banks and
+		 * compute the delta
+		 */
+		delta = (total_raw_count - (prev_raw_count * num_banks)) &
+								HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+
+		/*
+		 * Divide by num of banks to get average count and
+		 * update prev_count with this value
+		 */
+		avg_raw_count = total_raw_count / num_banks;
+	} while (local64_cmpxchg(
+			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
+							 prev_raw_count);
+
+	return total_raw_count;
+}
+
+static void hisi_set_l3c_evtype(struct hisi_pmu *pl3c_pmu, int idx, u32 val)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+	u32 event_value, value = 0;
+	u32 cfg_en, module_id;
+	int i;
+
+	event_value = (val -
+			HISI_HWEVENT_L3C_READ_ALLOCATE);
+
+	/* Select the appropriate Event select register */
+	if (idx > 3)
+		reg_offset += 4;
+
+	/* Value to write to event type register */
+	val = event_value << (8 * idx);
+
+	/* Find the djtag Identifier of the Unit */
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Set the event in L3C_EVENT_TYPEx Register
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(0xff << (8 * idx));
+		value |= val;
+
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static u32 hisi_write_l3c_counter(struct hisi_pmu *pl3c_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset, cfg_en, module_id;
+	int i, ret = 0;
+	int idx = GET_CNTR_IDX(hwc);
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return -EINVAL;
+	}
+
+	reg_offset = l3c_hwmod_data->l3c_hwcfg.counter_reg0_off +
+							(idx * 4);
+
+	client = l3c_hwmod_data->client;
+
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		ret = hisi_djtag_writereg(module_id,
+					cfg_en,
+					reg_offset,
+					value,
+					client);
+		if (!ret)
+			ret = value;
+	}
+
+	return ret;
+}
+
+static void hisi_enable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+	u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+	u32 value, cfg_en, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Set the event_bus_en bit in L3C AUCNTRL to enable counting
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value |= eventen;
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static void hisi_disable_l3c_counter(struct hisi_pmu *pl3c_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.event_ctrl_reg_off;
+	u32 eventen = l3c_hwmod_data->l3c_hwcfg.event_enable;
+	u32 value, cfg_en, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Clear the event_bus_en bit in L3C AUCNTRL if no other
+	 * event counting for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(eventen);
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static void hisi_clear_l3c_event_idx(struct hisi_pmu *pl3c_pmu,
+							int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	u32 reg_offset = l3c_hwmod_data->l3c_hwcfg.evtype_reg0_off;
+	void *bitmap_addr;
+	u32 cfg_en, value, module_id;
+	int i;
+
+	if (!hisi_l3c_counter_valid(idx)) {
+		dev_err(pl3c_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	bitmap_addr = l3c_hwmod_data->hisi_l3c_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+
+	/* Clear Counting in L3C event config register */
+	if (idx > 3)
+		reg_offset += 4;
+
+	client = l3c_hwmod_data->client;
+
+	/*
+	 * Clear the event in L3C_EVENT_TYPEx Register
+	 * for all L3C banks
+	 */
+	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
+		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
+		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
+		hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+		value &= ~(0xff << (8 * idx));
+		value |= (0xff << (8 * idx));
+		hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+	}
+}
+
+static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
+{
+	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			l3c_hwmod_data->hisi_l3c_event_used_mask,
+					 pl3c_pmu->num_counters);
+
+	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		l3c_hwmod_data->hisi_l3c_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_l3c_data(struct hisi_pmu *pl3c_pmu)
+{
+	kfree(pl3c_pmu->hwmod_data);
+	pl3c_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_l3c_hwcfg(struct device *dev,
+				struct hisi_l3c_data *pl3c_data)
+{
+	struct hisi_l3c_hwcfg *pl3c_hwcfg = &pl3c_data->l3c_hwcfg;
+	struct device_node *node = dev->of_node;
+	u32 prop_len;
+	int ret;
+
+	if (of_property_read_u32(node, "counter-reg",
+				     &pl3c_hwcfg->counter_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read counter-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evctrl-reg",
+				     &pl3c_hwcfg->event_ctrl_reg_off)) {
+		dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "event-en",
+				     &pl3c_hwcfg->event_enable)) {
+		dev_err(dev, "DT:Couldnot read event-en!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evtype-reg",
+				     &pl3c_hwcfg->evtype_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "num-banks",
+				     &pl3c_hwcfg->num_banks)) {
+		dev_err(dev, "DT:Couldnot read num-banks!\n");
+		return -EINVAL;
+	}
+
+	prop_len = of_property_count_u32_elems(node, "module-id");
+	if (prop_len != pl3c_hwcfg->num_banks) {
+		dev_err(dev, "DT:module-id entry not valid!\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(node, "module-id",
+					  &pl3c_hwcfg->module_id[0],
+						  pl3c_hwcfg->num_banks);
+	if (ret < 0) {
+		dev_err(dev, "DT:Couldnot read module-id!\n");
+		return -EINVAL;
+	}
+
+	prop_len = of_property_count_u32_elems(node, "cfgen-map");
+	if (prop_len != pl3c_hwcfg->num_banks) {
+		dev_err(dev, "DT:cfgen-map entrynot valid!\n");
+		return -EINVAL;
+	}
+
+	ret = of_property_read_u32_array(node, "cfgen-map",
+					&pl3c_hwcfg->bank_cfgen[0],
+						pl3c_hwcfg->num_banks);
+	if (ret < 0) {
+		dev_err(dev, "DT:Couldnot read cfgen-map!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int init_hisi_l3c_data(struct device *dev,
+					struct hisi_pmu *pl3c_pmu,
+					struct hisi_djtag_client *client)
+{
+	struct hisi_l3c_data *l3c_hwmod_data = NULL;
+	int ret;
+
+	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
+							GFP_KERNEL);
+	if (!l3c_hwmod_data)
+		return -ENOMEM;
+
+	/* Set the djtag Identifier */
+	l3c_hwmod_data->client = client;
+
+	pl3c_pmu->hw_events.events = devm_kcalloc(dev,
+					pl3c_pmu->num_counters,
+					sizeof(*pl3c_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!pl3c_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&pl3c_pmu->hw_events.pmu_lock);
+
+	pl3c_pmu->hwmod_data = l3c_hwmod_data;
+
+	ret = init_hisi_l3c_hwcfg(dev, l3c_hwmod_data);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	hisi_free_l3c_data(pl3c_pmu);
+	return ret;
+}
+
+static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
+	.set_evtype = hisi_set_l3c_evtype,
+	.set_event_period = hisi_pmu_set_event_period,
+	.get_event_idx = hisi_l3c_get_event_idx,
+	.clear_event_idx = hisi_clear_l3c_event_idx,
+	.event_update = hisi_l3c_event_update,
+	.enable_counter = hisi_enable_l3c_counter,
+	.disable_counter = hisi_disable_l3c_counter,
+	.write_counter = hisi_write_l3c_counter,
+};
+
+static int hisi_l3c_pmu_init(struct device *dev,
+					struct hisi_pmu *pl3c_pmu)
+{
+	int ret;
+
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, pl3c_pmu);
+	if (ret)
+		return ret;
+
+	pl3c_pmu->name = kasprintf(GFP_KERNEL, "hisi_l3c%d",
+						pl3c_pmu->scl_id);
+	pl3c_pmu->ops = &hisi_uncore_l3c_ops;
+	pl3c_pmu->dev = dev;
+
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &pl3c_pmu->cpu);
+
+	return 0;
+}
+
+static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pl3c_pmu = NULL;
+	struct device *dev = &client->dev;
+	int ret;
+
+	pl3c_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(pl3c_pmu))
+		return PTR_ERR(pl3c_pmu);
+
+	ret = hisi_l3c_pmu_init(dev, pl3c_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_l3c_data(dev, pl3c_pmu, client);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	pl3c_pmu->pmu = (struct pmu) {
+		.name = pl3c_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+	};
+
+	ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
+	if (ret) {
+		dev_err(dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	/* Set the drv data to l3c_pmu */
+	dev_set_drvdata(dev, pl3c_pmu);
+
+	return 0;
+
+fail:
+	hisi_free_l3c_data(pl3c_pmu);
+
+fail_init:
+	dev_err(dev, "%s failed\n", __func__);
+	return ret;
+}
+
+static int hisi_pmu_l3c_dev_remove(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pl3c_pmu = NULL;
+	struct device *dev = &client->dev;
+
+	pl3c_pmu = dev_get_drvdata(dev);
+
+	perf_pmu_unregister(&pl3c_pmu->pmu);
+	hisi_free_l3c_data(pl3c_pmu);
+
+	return 0;
+}
+
+static const struct of_device_id l3c_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-l3c-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, l3c_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_l3c_driver = {
+	.driver = {
+		.name = "hisi-pmu-l3c",
+		.of_match_table = l3c_of_match,
+	},
+	.probe = hisi_pmu_l3c_dev_probe,
+	.remove = hisi_pmu_l3c_dev_remove,
+};
+
+static int __init hisi_pmu_l3c_init(void)
+{
+	int rc;
+
+	rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_l3c_driver);
+	if (rc < 0) {
+		pr_err("hisi pmu l3c init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_pmu_l3c_init);
+
+static void __exit hisi_pmu_l3c_exit(void)
+{
+	hisi_djtag_unregister_driver(&hisi_pmu_l3c_driver);
+
+}
+module_exit(hisi_pmu_l3c_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x L3C PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.h b/drivers/perf/hisilicon/hisi_uncore_l3c.h
new file mode 100644
index 0000000..a4a1777
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.h
@@ -0,0 +1,67 @@
+/*
+ * HiSilicon SoC L3C Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_L3C_H__
+#define __HISI_UNCORE_L3C_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon L3C RAW event types.
+ */
+enum armv8_hisi_l3c_event_types {
+	HISI_HWEVENT_L3C_READ_ALLOCATE		= 0x0,
+	HISI_HWEVENT_L3C_WRITE_ALLOCATE		= 0x01,
+	HISI_HWEVENT_L3C_READ_NOALLOCATE	= 0x02,
+	HISI_HWEVENT_L3C_WRITE_NOALLOCATE	= 0x03,
+	HISI_HWEVENT_L3C_READ_HIT		= 0x04,
+	HISI_HWEVENT_L3C_WRITE_HIT		= 0x05,
+	HISI_HWEVENT_L3C_EVENT_MAX		= 0x15,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_l3c_counters {
+	HISI_IDX_L3C_COUNTER0		= 0x0,
+	HISI_IDX_L3C_COUNTER_MAX	= 0x7,
+};
+
+#define HISI_MAX_CFG_L3C_CNTR	0x08
+
+struct hisi_l3c_hwcfg {
+	u32 evtype_reg0_off;
+	u32 counter_reg0_off;
+	u32 event_ctrl_reg_off;
+	u32 event_enable;
+	u32 module_id[MAX_BANKS];
+	u32 num_banks;
+	u32 bank_cfgen[MAX_BANKS];
+};
+
+struct hisi_l3c_data {
+	struct hisi_djtag_client *client;
+	DECLARE_BITMAP(hisi_l3c_event_used_mask,
+				HISI_MAX_CFG_L3C_CNTR);
+	struct hisi_l3c_hwcfg l3c_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_L3C_H__ */
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
new file mode 100644
index 0000000..8d29fcc
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -0,0 +1,331 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_pmu.h"
+
+/* djtag read interface - Call djtag driver to access SoC registers */
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+				struct hisi_djtag_client *client, u32 *pvalue)
+{
+	int ret;
+	u32 chain_id = 0;
+
+	while (bank != 1) {
+		bank = (bank >> 0x1);
+		chain_id++;
+	}
+
+	ret = hisi_djtag_readl(client, offset, module_id,
+						chain_id, pvalue);
+	if (ret)
+		dev_err(&client->dev, "read failed, ret=%d!\n", ret);
+
+	return ret;
+}
+
+/* djtag write interface - Call djtag driver  to access SoC registers */
+int hisi_djtag_writereg(int module_id, int bank,
+				u32 offset, u32 value,
+				struct hisi_djtag_client *client)
+{
+	int ret;
+
+	ret = hisi_djtag_writel(client, offset, module_id,
+						HISI_DJTAG_MOD_MASK, value);
+	if (ret)
+		dev_err(&client->dev, "write failed, ret=%d!\n", ret);
+
+	return ret;
+}
+
+static int pmu_map_event(struct perf_event *event)
+{
+	return (int)(event->attr.config & HISI_EVTYPE_EVENT);
+}
+
+static int
+__hw_perf_event_init(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct device *dev = phisi_pmu->dev;
+	int mapping;
+
+	mapping = pmu_map_event(event);
+	if (mapping < 0) {
+		dev_err(dev, "event %x:%llx not supported\n", event->attr.type,
+							 event->attr.config);
+		return mapping;
+	}
+
+	/*
+	 * We don't assign an index until we actually place the event onto
+	 * hardware. Use -1 to signify that we haven't decided where to put it
+	 * yet.
+	 */
+	hwc->idx		= -1;
+	hwc->config		= 0;
+	hwc->event_base		= 0;
+
+	/* For HiSilicon SoC L3C update config_base based on event encoding */
+	hwc->config_base = event->attr.config;
+
+	return 0;
+}
+
+int hisi_uncore_pmu_event_init(struct perf_event *event)
+{
+	int err;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	if (event->attr.type != event->pmu->type)
+		return -ENOENT;
+
+	/* we do not support sampling as the counters are all
+	 * shared by all CPU cores in a CPU die(SCCL). Also we
+	 * donot support attach to a task(per-process mode)
+	 */
+	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
+		return -EOPNOTSUPP;
+
+	/* counters do not have these bits */
+	if (event->attr.exclude_user	||
+	    event->attr.exclude_kernel	||
+	    event->attr.exclude_host	||
+	    event->attr.exclude_guest	||
+	    event->attr.exclude_hv	||
+	    event->attr.exclude_idle)
+		return -EINVAL;
+
+	if (event->cpu < 0)
+		return -EINVAL;
+
+	event->cpu = cpumask_first(&phisi_pmu->cpu);
+
+	err = __hw_perf_event_init(event);
+
+	return err;
+}
+
+/*
+ * Enable counter and set the counter to count
+ * the event that we're interested in.
+ */
+void hisi_uncore_pmu_enable_event(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/* Disable the hardware event counting */
+	if (phisi_pmu->ops->disable_counter)
+		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+
+	/*
+	 * Set event (if destined for Hisilicon SoC counters).
+	 */
+	if (phisi_pmu->ops->set_evtype)
+		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
+							hwc->config_base);
+
+	/* Enable the hardware event counting */
+	if (phisi_pmu->ops->enable_counter)
+		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
+}
+
+void hisi_pmu_set_event_period(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/*
+	 * The Hisilicon PMU counters have a period of 2^32. To account for the
+	 * possiblity of extreme interrupt latency we program for a period of
+	 * half that. Hopefully we can handle the interrupt before another 2^31
+	 * events occur and the counter overtakes its previous value.
+	 */
+	u64 val = 1ULL << 31;
+
+	local64_set(&hwc->prev_count, val);
+
+	/* Write to the hardware event counter */
+	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
+}
+
+void hisi_uncore_pmu_start(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
+		return;
+
+	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
+	hwc->state = 0;
+
+	if (phisi_pmu->ops->set_event_period)
+		phisi_pmu->ops->set_event_period(event);
+
+	if (flags & PERF_EF_RELOAD) {
+		u64 prev_raw_count =  local64_read(&hwc->prev_count);
+
+		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
+						(u32)prev_raw_count);
+	}
+
+	hisi_uncore_pmu_enable_event(event);
+	perf_event_update_userpage(event);
+}
+
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	if (hwc->state & PERF_HES_UPTODATE)
+		return;
+
+	/*
+	 * We always reprogram the counter, so ignore PERF_EF_UPDATE.
+	 * See hisi_uncore_pmu_start()
+	 */
+	if (phisi_pmu->ops->disable_counter)
+		phisi_pmu->ops->disable_counter(phisi_pmu,
+						GET_CNTR_IDX(hwc));
+
+	WARN_ON_ONCE(hwc->state & PERF_HES_STOPPED);
+	hwc->state |= PERF_HES_STOPPED;
+	if (hwc->state & PERF_HES_UPTODATE)
+		return;
+
+	/* Read hardware counter and update the Perf counter statistics */
+	phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+	hwc->state |= PERF_HES_UPTODATE;
+}
+
+int hisi_uncore_pmu_add(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+	int idx;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	hwc->state = PERF_HES_STOPPED | PERF_HES_UPTODATE;
+
+	/* If we don't have a free counter then return early. */
+	idx = phisi_pmu->ops->get_event_idx(phisi_pmu);
+	if (idx < 0)
+		return -EAGAIN;
+
+	event->hw.idx = idx;
+	hw_events->events[idx] = event;
+
+	if (flags & PERF_EF_START)
+		hisi_uncore_pmu_start(event, PERF_EF_RELOAD);
+
+	/* Propagate our changes to the userspace mapping. */
+	perf_event_update_userpage(event);
+
+	return 0;
+}
+
+void hisi_uncore_pmu_del(struct perf_event *event, int flags)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_pmu_hw_events *hw_events;
+
+	hw_events = &phisi_pmu->hw_events;
+
+	hisi_uncore_pmu_stop(event, PERF_EF_UPDATE);
+
+	phisi_pmu->ops->clear_event_idx(phisi_pmu, GET_CNTR_IDX(hwc));
+	perf_event_update_userpage(event);
+	hw_events->events[GET_CNTR_IDX(hwc)] = NULL;
+}
+
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev)
+{
+	struct hisi_pmu *phisi_pmu;
+
+	phisi_pmu = devm_kzalloc(dev, sizeof(*phisi_pmu), GFP_KERNEL);
+	if (!phisi_pmu)
+		return ERR_PTR(-ENOMEM);
+
+	return phisi_pmu;
+}
+
+void hisi_uncore_pmu_read(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
+
+	/* Read hardware counter and update the Perf counter statistics */
+	phisi_pmu->ops->event_update(event, hwc, GET_CNTR_IDX(hwc));
+}
+
+int hisi_uncore_common_fwprop_read(struct device *dev,
+					struct hisi_pmu *phisi_pmu)
+{
+	if (device_property_read_u32(dev, "num-events",
+					&phisi_pmu->num_events)) {
+		dev_err(dev, "Cant read num-events from DT!\n");
+		return -EINVAL;
+	}
+
+	if (device_property_read_u32(dev, "num-counters",
+				     &phisi_pmu->num_counters)) {
+		dev_err(dev, "Cant read num-counters from DT!\n");
+		return -EINVAL;
+	}
+
+	/* Find the SCL ID */
+	if (device_property_read_u32(dev, "scl-id",
+					&phisi_pmu->scl_id)) {
+		dev_err(dev, "Cant read scl-id!\n");
+		return -EINVAL;
+	}
+
+	if (phisi_pmu->scl_id == 0 ||
+		phisi_pmu->scl_id >= MAX_UNITS) {
+		dev_err(dev, "Invalid SCL=%d!\n",
+					phisi_pmu->scl_id);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu,
+					const char *pmu_name)
+{
+	/* Register the events with perf */
+	return perf_pmu_register(&phisi_pmu->pmu, pmu_name, -1);
+}
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
new file mode 100644
index 0000000..b6b16df
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -0,0 +1,108 @@
+/*
+ * HiSilicon SoC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_PMU_H__
+#define __HISI_UNCORE_PMU_H__
+
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <linux/soc/hisilicon/djtag.h>
+#include <linux/types.h>
+#include <asm/local64.h>
+
+#undef pr_fmt
+#define pr_fmt(fmt)     "hisi_pmu: " fmt
+
+#define HISI_DJTAG_MOD_MASK (0xFFFF)
+#define HISI_CNTR_SCCL_MASK    (0xF00)
+
+#define HISI_EVTYPE_EVENT	0xfff
+#define HISI_MAX_PERIOD ((1LLU << 32) - 1)
+
+#define MAX_BANKS 8
+#define MAX_COUNTERS 30
+#define MAX_UNITS 8
+
+#define GET_CNTR_IDX(hwc) (hwc->idx)
+#define to_hisi_pmu(c)	(container_of(c, struct hisi_pmu, pmu))
+
+#define GET_UNIT_IDX(event_code)		\
+	(((event_code & HISI_SCCL_MASK) >>	\
+			   HISI_SCCL_SHIFT) - 1)
+
+struct hisi_pmu;
+
+struct hisi_uncore_ops {
+	void (*set_evtype)(struct hisi_pmu *, int, u32);
+	void (*set_event_period)(struct perf_event *);
+	int (*get_event_idx)(struct hisi_pmu *);
+	void (*clear_event_idx)(struct hisi_pmu *, int);
+	u64 (*event_update)(struct perf_event *,
+			     struct hw_perf_event *, int);
+	u32 (*read_counter)(struct hisi_pmu *, int, int);
+	u32 (*write_counter)(struct hisi_pmu *,
+				struct hw_perf_event *, u32);
+	void (*enable_counter)(struct hisi_pmu *, int);
+	void (*disable_counter)(struct hisi_pmu *, int);
+};
+
+struct hisi_pmu_hw_events {
+	struct perf_event **events;
+	raw_spinlock_t pmu_lock;
+};
+
+/* Generic pmu struct for different pmu types */
+struct hisi_pmu {
+	const char *name;
+	struct hisi_pmu_hw_events hw_events;
+	struct hisi_uncore_ops *ops;
+	struct device *dev;
+	void *hwmod_data; /* Hardware module specific data */
+	cpumask_t cpu;
+	struct pmu pmu;
+	u32 scl_id;
+	int num_counters;
+	int num_events;
+	int num_units;
+};
+
+void hisi_uncore_pmu_read(struct perf_event *event);
+void hisi_uncore_pmu_del(struct perf_event *event, int flags);
+int hisi_uncore_pmu_add(struct perf_event *event, int flags);
+void hisi_uncore_pmu_start(struct perf_event *event, int flags);
+void hisi_uncore_pmu_stop(struct perf_event *event, int flags);
+void hisi_pmu_set_event_period(struct perf_event *event);
+void hisi_uncore_pmu_enable_event(struct perf_event *event);
+int hisi_uncore_pmu_setup(struct hisi_pmu *phisi_pmu, const char *pmu_name);
+int hisi_uncore_pmu_event_init(struct perf_event *event);
+int hisi_djtag_readreg(int module_id, int bank, u32 offset,
+				struct hisi_djtag_client *client,
+							u32 *pvalue);
+int hisi_djtag_writereg(int module_id, int bank,
+				u32 offset, u32 value,
+				struct hisi_djtag_client *client);
+struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
+int hisi_uncore_common_fwprop_read(struct device *dev,
+					struct hisi_pmu *phisi_pmu);
+#endif /* __HISI_UNCORE_PMU_H__ */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 08/11] perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42     ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA
  Cc: guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA,
	anurupvasu-Re5JQEeQqe8AvxtiuMwx3w

	1. Add L3 caches events to /sys/devices/hisi_l3c2/events/
	  The events can be selected as shown in perf list
	  e.g.: For L3C_READ_ALLOCATE event for Super CPU cluster 2 the
	  event format is
	    -e "hisi_l3c2/read_allocate/"
	2. Add cpu_mask attribute group for showing the available CPU
	   for counting.

Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Signed-off-by: Shaokun Zhang <zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
---
 drivers/perf/hisilicon/hisi_uncore_l3c.c | 57 ++++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.c | 40 ++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h | 22 ++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
index f78f7b2..428fba0 100644
--- a/drivers/perf/hisilicon/hisi_uncore_l3c.c
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -436,6 +436,62 @@ static int init_hisi_l3c_data(struct device *dev,
 	return ret;
 }
 
+static struct attribute *hisi_l3c_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_l3c_format_group = {
+	.name = "format",
+	.attrs = hisi_l3c_format_attr,
+};
+
+static struct attribute *hisi_l3c_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(read_allocate,
+					"event=0x0"),
+	HISI_PMU_EVENT_ATTR_STR(write_allocate,
+					"event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(read_noallocate,
+					"event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(write_noallocate,
+					"event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(read_hit, "event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(write_hit, "event=0x05"),
+	NULL,
+};
+
+static struct attribute_group hisi_l3c_events_group = {
+	.name = "events",
+	.attrs = hisi_l3c_events_attr,
+};
+
+static struct attribute *hisi_l3c_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_l3c_attr_group = {
+	.attrs = hisi_l3c_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_l3c_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_l3c_cpumask_attr_group = {
+	.attrs = hisi_l3c_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
+	&hisi_l3c_attr_group,
+	&hisi_l3c_format_group,
+	&hisi_l3c_events_group,
+	&hisi_l3c_cpumask_attr_group,
+	NULL,
+};
+
 static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
 	.set_evtype = hisi_set_l3c_evtype,
 	.set_event_period = hisi_pmu_set_event_period,
@@ -496,6 +552,7 @@ static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
 		.start = hisi_uncore_pmu_start,
 		.stop = hisi_uncore_pmu_stop,
 		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_l3c_pmu_attr_groups,
 	};
 
 	ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
index 8d29fcc..d0a911a 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.c
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -26,6 +26,46 @@
 #include <linux/perf_event.h>
 #include "hisi_uncore_pmu.h"
 
+/*
+ * PMU format attributes
+ */
+ssize_t hisi_format_sysfs_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct dev_ext_attribute *eattr;
+
+	eattr = container_of(attr, struct dev_ext_attribute,
+					     attr);
+	return sprintf(buf, "%s\n", (char *) eattr->var);
+}
+
+/*
+ * PMU event attributes
+ */
+ssize_t hisi_event_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct perf_pmu_events_attr *pmu_attr =
+		container_of(attr, struct perf_pmu_events_attr, attr);
+
+	if (pmu_attr->event_str)
+		return sprintf(buf, "%s", pmu_attr->event_str);
+
+	return 0;
+}
+
+/*
+ * sysfs cpumask attributes
+ */
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct pmu *pmu = dev_get_drvdata(dev);
+	struct hisi_pmu *hisi_pmu = to_hisi_pmu(pmu);
+
+	return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->cpu);
+}
+
 /* djtag read interface - Call djtag driver to access SoC registers */
 int hisi_djtag_readreg(int module_id, int bank, u32 offset,
 				struct hisi_djtag_client *client, u32 *pvalue)
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
index b6b16df..a948752 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.h
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -51,6 +51,22 @@
 	(((event_code & HISI_SCCL_MASK) >>	\
 			   HISI_SCCL_SHIFT) - 1)
 
+#define HISI_PMU_FORMAT_ATTR(_name, _config)		\
+	(&((struct dev_ext_attribute[]) {		\
+		{ .attr = __ATTR(_name, 0444,	\
+			hisi_format_sysfs_show, NULL),	\
+		  .var = (void *) _config,		\
+		}					\
+	})[0].attr.attr)
+
+#define HISI_PMU_EVENT_ATTR_STR(_name, _str)		\
+	(&((struct perf_pmu_events_attr[]) {		\
+		{ .attr = __ATTR(_name, 0444,	\
+			 hisi_event_sysfs_show, NULL),	\
+		  .event_str = _str,			\
+		}					\
+	  })[0].attr.attr)
+
 struct hisi_pmu;
 
 struct hisi_uncore_ops {
@@ -105,4 +121,10 @@ int hisi_djtag_writereg(int module_id, int bank,
 struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
 int hisi_uncore_common_fwprop_read(struct device *dev,
 					struct hisi_pmu *phisi_pmu);
+ssize_t hisi_event_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+ssize_t hisi_format_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
 #endif /* __HISI_UNCORE_PMU_H__ */
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 08/11] perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU
@ 2016-11-03  5:42     ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1. Add L3 caches events to /sys/devices/hisi_l3c2/events/
	  The events can be selected as shown in perf list
	  e.g.: For L3C_READ_ALLOCATE event for Super CPU cluster 2 the
	  event format is
	    -e "hisi_l3c2/read_allocate/"
	2. Add cpu_mask attribute group for showing the available CPU
	   for counting.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 drivers/perf/hisilicon/hisi_uncore_l3c.c | 57 ++++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.c | 40 ++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_pmu.h | 22 ++++++++++++
 3 files changed, 119 insertions(+)

diff --git a/drivers/perf/hisilicon/hisi_uncore_l3c.c b/drivers/perf/hisilicon/hisi_uncore_l3c.c
index f78f7b2..428fba0 100644
--- a/drivers/perf/hisilicon/hisi_uncore_l3c.c
+++ b/drivers/perf/hisilicon/hisi_uncore_l3c.c
@@ -436,6 +436,62 @@ static int init_hisi_l3c_data(struct device *dev,
 	return ret;
 }
 
+static struct attribute *hisi_l3c_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_l3c_format_group = {
+	.name = "format",
+	.attrs = hisi_l3c_format_attr,
+};
+
+static struct attribute *hisi_l3c_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(read_allocate,
+					"event=0x0"),
+	HISI_PMU_EVENT_ATTR_STR(write_allocate,
+					"event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(read_noallocate,
+					"event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(write_noallocate,
+					"event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(read_hit, "event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(write_hit, "event=0x05"),
+	NULL,
+};
+
+static struct attribute_group hisi_l3c_events_group = {
+	.name = "events",
+	.attrs = hisi_l3c_events_attr,
+};
+
+static struct attribute *hisi_l3c_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_l3c_attr_group = {
+	.attrs = hisi_l3c_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_l3c_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_l3c_cpumask_attr_group = {
+	.attrs = hisi_l3c_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_l3c_pmu_attr_groups[] = {
+	&hisi_l3c_attr_group,
+	&hisi_l3c_format_group,
+	&hisi_l3c_events_group,
+	&hisi_l3c_cpumask_attr_group,
+	NULL,
+};
+
 static struct hisi_uncore_ops hisi_uncore_l3c_ops = {
 	.set_evtype = hisi_set_l3c_evtype,
 	.set_event_period = hisi_pmu_set_event_period,
@@ -496,6 +552,7 @@ static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
 		.start = hisi_uncore_pmu_start,
 		.stop = hisi_uncore_pmu_stop,
 		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_l3c_pmu_attr_groups,
 	};
 
 	ret = hisi_uncore_pmu_setup(pl3c_pmu, pl3c_pmu->name);
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.c b/drivers/perf/hisilicon/hisi_uncore_pmu.c
index 8d29fcc..d0a911a 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.c
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.c
@@ -26,6 +26,46 @@
 #include <linux/perf_event.h>
 #include "hisi_uncore_pmu.h"
 
+/*
+ * PMU format attributes
+ */
+ssize_t hisi_format_sysfs_show(struct device *dev,
+			struct device_attribute *attr, char *buf)
+{
+	struct dev_ext_attribute *eattr;
+
+	eattr = container_of(attr, struct dev_ext_attribute,
+					     attr);
+	return sprintf(buf, "%s\n", (char *) eattr->var);
+}
+
+/*
+ * PMU event attributes
+ */
+ssize_t hisi_event_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf)
+{
+	struct perf_pmu_events_attr *pmu_attr =
+		container_of(attr, struct perf_pmu_events_attr, attr);
+
+	if (pmu_attr->event_str)
+		return sprintf(buf, "%s", pmu_attr->event_str);
+
+	return 0;
+}
+
+/*
+ * sysfs cpumask attributes
+ */
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+				struct device_attribute *attr, char *buf)
+{
+	struct pmu *pmu = dev_get_drvdata(dev);
+	struct hisi_pmu *hisi_pmu = to_hisi_pmu(pmu);
+
+	return cpumap_print_to_pagebuf(true, buf, &hisi_pmu->cpu);
+}
+
 /* djtag read interface - Call djtag driver to access SoC registers */
 int hisi_djtag_readreg(int module_id, int bank, u32 offset,
 				struct hisi_djtag_client *client, u32 *pvalue)
diff --git a/drivers/perf/hisilicon/hisi_uncore_pmu.h b/drivers/perf/hisilicon/hisi_uncore_pmu.h
index b6b16df..a948752 100644
--- a/drivers/perf/hisilicon/hisi_uncore_pmu.h
+++ b/drivers/perf/hisilicon/hisi_uncore_pmu.h
@@ -51,6 +51,22 @@
 	(((event_code & HISI_SCCL_MASK) >>	\
 			   HISI_SCCL_SHIFT) - 1)
 
+#define HISI_PMU_FORMAT_ATTR(_name, _config)		\
+	(&((struct dev_ext_attribute[]) {		\
+		{ .attr = __ATTR(_name, 0444,	\
+			hisi_format_sysfs_show, NULL),	\
+		  .var = (void *) _config,		\
+		}					\
+	})[0].attr.attr)
+
+#define HISI_PMU_EVENT_ATTR_STR(_name, _str)		\
+	(&((struct perf_pmu_events_attr[]) {		\
+		{ .attr = __ATTR(_name, 0444,	\
+			 hisi_event_sysfs_show, NULL),	\
+		  .event_str = _str,			\
+		}					\
+	  })[0].attr.attr)
+
 struct hisi_pmu;
 
 struct hisi_uncore_ops {
@@ -105,4 +121,10 @@ int hisi_djtag_writereg(int module_id, int bank,
 struct hisi_pmu *hisi_pmu_alloc(struct device *dev);
 int hisi_uncore_common_fwprop_read(struct device *dev,
 					struct hisi_pmu *phisi_pmu);
+ssize_t hisi_event_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+ssize_t hisi_format_sysfs_show(struct device *dev,
+				  struct device_attribute *attr, char *buf);
+ssize_t hisi_cpumask_sysfs_show(struct device *dev,
+				struct device_attribute *attr, char *buf);
 #endif /* __HISI_UNCORE_PMU_H__ */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 09/11] perf: hisi: Miscellanous node(MN) event counting in perf
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

From: Shaokun Zhang <zhangshaokun@hisilicon.com>

	1. Add support to count MN hardware events.
	2. Mn events are listed in sysfs at /sys/devices/hisi_mn2/events/
	   The events can be selected as shown in perf list
	   e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
		event format is
		-e "hisi_mn2/read_req/"

Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 drivers/perf/hisilicon/Makefile         |   2 +-
 drivers/perf/hisilicon/hisi_uncore_mn.c | 571 ++++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_mn.h |  68 ++++
 3 files changed, 640 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index e1766cf..8975104 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 0000000..920e346
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_mn.h"
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+	return (idx >= HISI_IDX_MN_COUNTER0 &&
+			idx <= HISI_IDX_MN_COUNTER_MAX);
+}
+
+static u32 hisi_read_mn_counter(struct hisi_mn_data *mn_hwmod_data,
+								int idx)
+{
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	struct hisi_djtag_client *client = mn_hwmod_data->client;
+	u32 cfg_en, reg_offset, value;
+
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+	reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off + (idx * 4);
+
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *pmn_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_mn_data *mn_hwmod_data;
+	u64 delta, prev_raw_count, new_raw_count = 0;
+	u32 cfg_en;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return 0;
+	}
+
+	mn_hwmod_data = pmn_pmu->hwmod_data;
+
+	/* Check if the MN data is initialized for this SCCL */
+	if (!mn_hwmod_data->client) {
+		dev_err(pmn_pmu->dev,
+			"SCL=%d not initialized!\n", pmn_pmu->scl_id);
+		return 0;
+	}
+
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	do {
+		prev_raw_count = local64_read(&hwc->prev_count);
+		new_raw_count =
+			hisi_read_mn_counter(mn_hwmod_data, idx);
+		delta = (new_raw_count - prev_raw_count) &
+						HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+	} while (local64_cmpxchg(
+			&hwc->prev_count, prev_raw_count, new_raw_count) !=
+							prev_raw_count);
+
+	return new_raw_count;
+}
+
+static void hisi_set_mn_evtype(struct hisi_pmu *pmn_pmu, int idx, u32 val)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 cfg_en, event_value, value = 0;
+
+	event_value = (val -
+			HISI_HWEVENT_MN_EO_BARR_REQ);
+
+	/* Value to write to event type register */
+	val = event_value << (8 * idx);
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Set the event in MN_EVENT_TYPE Register
+	 */
+	hisi_djtag_readreg(module_id,
+			   cfg_en,
+			   reg_offset,
+			   client, &value);
+
+	value &= ~(0xff << (8 * idx));
+	value |= val;
+
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static u32 hisi_write_mn_counter(struct hisi_pmu *pmn_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 cfg_en, reg_offset;
+	int ret;
+	int idx = GET_CNTR_IDX(hwc);
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return -EINVAL;
+	}
+
+	reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off +
+							(idx * 4);
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	ret = hisi_djtag_writereg(module_id,
+				  cfg_en,
+				  reg_offset,
+				  value,
+				  client);
+
+	return ret;
+}
+
+static void hisi_enable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Set the event_bus_en bit in MN_EVENT_CTRL to enable counting
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client,
+				&value);
+
+	value |= event_en;
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static void hisi_disable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Clear the event_bus_en bit in MN event control
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	value &= ~(event_en);
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static void hisi_clear_mn_event_idx(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	void *bitmap_addr;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	bitmap_addr = mn_hwmod_data->hisi_mn_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Clear the event in MN_EVENT_TYPE Register
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	value &= ~(0xff << (8 * idx));
+	value |= (0xff << (8 * idx));
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static int hisi_mn_get_event_idx(struct hisi_pmu *pmn_pmu)
+{
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			mn_hwmod_data->hisi_mn_event_used_mask,
+					HISI_MAX_CFG_MN_CNTR);
+
+	if (event_idx == HISI_MAX_CFG_MN_CNTR)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		mn_hwmod_data->hisi_mn_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_mn_data(struct hisi_pmu *pmn_pmu)
+{
+	kfree(pmn_pmu->hwmod_data);
+	pmn_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_mn_hwcfg(struct device *dev,
+				struct hisi_mn_data *pmn_data)
+{
+	struct hisi_mn_hwcfg *pmn_hwcfg = &pmn_data->mn_hwcfg;
+	struct device_node *node = dev->of_node;
+
+	if (of_property_read_u32(node, "counter-reg",
+				     &pmn_hwcfg->counter_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read counter-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evctrl-reg",
+				     &pmn_hwcfg->event_ctrl_reg_off)) {
+		dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "event-en",
+				     &pmn_hwcfg->event_enable)) {
+		dev_err(dev, "DT:Couldnot read event-en property!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evtype-reg",
+				     &pmn_hwcfg->evtype_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "module-id",
+				     &pmn_hwcfg->module_id)) {
+		dev_err(dev, "DT:Couldnot read module-id property!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "cfgen-map",
+					&pmn_hwcfg->bank_cfgen)) {
+		dev_err(dev, "DT:Couldnot read cfgen-map property!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int init_hisi_mn_data(struct device *dev,
+					struct hisi_pmu *pmn_pmu,
+					struct hisi_djtag_client *client)
+{
+	struct hisi_mn_data *mn_hwmod_data;
+	int ret;
+
+	mn_hwmod_data = kzalloc(sizeof(struct hisi_mn_data),
+							GFP_KERNEL);
+	if (!mn_hwmod_data)
+		return -ENOMEM;
+
+	/* Set the djtag Identifier */
+	mn_hwmod_data->client = client;
+
+	pmn_pmu->hw_events.events = devm_kcalloc(dev,
+					pmn_pmu->num_counters,
+					sizeof(*pmn_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!pmn_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&pmn_pmu->hw_events.pmu_lock);
+
+	pmn_pmu->hwmod_data = mn_hwmod_data;
+
+	ret = init_hisi_mn_hwcfg(dev, mn_hwmod_data);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	hisi_free_mn_data(pmn_pmu);
+	return ret;
+}
+
+static struct attribute *hisi_mn_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_mn_format_group = {
+	.name = "format",
+	.attrs = hisi_mn_format_attr,
+};
+
+static struct attribute *hisi_mn_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(eo_barrier_req,
+				"event=0x0"),
+	HISI_PMU_EVENT_ATTR_STR(ec_barrier_req,
+				"event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(dvm_op_req,
+				"event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(dvm_sync_req,
+				"event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(read_req,
+				"event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(write_req,
+				"event=0x05"),
+	NULL,
+};
+
+static struct attribute_group hisi_mn_events_group = {
+	.name = "events",
+	.attrs = hisi_mn_events_attr,
+};
+
+static struct attribute *hisi_mn_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_mn_attr_group = {
+	.attrs = hisi_mn_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_mn_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_mn_cpumask_attr_group = {
+	.attrs = hisi_mn_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_mn_pmu_attr_groups[] = {
+	&hisi_mn_attr_group,
+	&hisi_mn_format_group,
+	&hisi_mn_events_group,
+	&hisi_mn_cpumask_attr_group,
+	NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_mn_ops = {
+	.set_evtype = hisi_set_mn_evtype,
+	.set_event_period = hisi_pmu_set_event_period,
+	.get_event_idx = hisi_mn_get_event_idx,
+	.clear_event_idx = hisi_clear_mn_event_idx,
+	.event_update = hisi_mn_event_update,
+	.enable_counter = hisi_enable_mn_counter,
+	.disable_counter = hisi_disable_mn_counter,
+	.write_counter = hisi_write_mn_counter,
+};
+
+static int hisi_mn_pmu_init(struct device *dev,
+					struct hisi_pmu *pmn_pmu)
+{
+	int ret;
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, pmn_pmu);
+	if (ret)
+		return ret;
+
+	pmn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn%d",
+						pmn_pmu->scl_id);
+	pmn_pmu->ops = &hisi_uncore_mn_ops;
+
+	pmn_pmu->dev = dev;
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &pmn_pmu->cpu);
+
+	return 0;
+}
+
+static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pmn_pmu = NULL;
+	struct device *dev = &client->dev;
+	int ret;
+
+	pmn_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(pmn_pmu))
+		return PTR_ERR(pmn_pmu);
+
+	ret = hisi_mn_pmu_init(dev, pmn_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_mn_data(dev, pmn_pmu, client);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	pmn_pmu->pmu = (struct pmu) {
+		.name = pmn_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_mn_pmu_attr_groups,
+	};
+
+	ret = hisi_uncore_pmu_setup(pmn_pmu, pmn_pmu->name);
+	if (ret) {
+		dev_err(pmn_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	/* Set the drv data to mn_pmu */
+	dev_set_drvdata(dev, pmn_pmu);
+
+	return 0;
+
+fail:
+	hisi_free_mn_data(pmn_pmu);
+
+fail_init:
+	if (pmn_pmu)
+		devm_kfree(dev, pmn_pmu);
+	dev_err(pmn_pmu->dev, "%s failed\n", __func__);
+
+	return ret;
+}
+
+static int hisi_pmu_mn_dev_remove(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pmn_pmu = NULL;
+	struct device *dev = &client->dev;
+
+	pmn_pmu = dev_get_drvdata(dev);
+
+	perf_pmu_unregister(&pmn_pmu->pmu);
+	hisi_free_mn_data(pmn_pmu);
+
+	return 0;
+}
+
+static const struct of_device_id mn_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-mn-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mn_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_mn_driver = {
+	.driver = {
+		.name = "hisi-pmu-mn",
+		.of_match_table = mn_of_match,
+	},
+	.probe = hisi_pmu_mn_dev_probe,
+	.remove = hisi_pmu_mn_dev_remove,
+};
+
+static int __init hisi_pmu_mn_init(void)
+{
+	int rc;
+
+	rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_mn_driver);
+	if (rc < 0) {
+		pr_err("hisi pmu mn init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_pmu_mn_init);
+
+static void __exit hisi_pmu_mn_exit(void)
+{
+	hisi_djtag_unregister_driver(&hisi_pmu_mn_driver);
+}
+module_exit(hisi_pmu_mn_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x MN PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shaokun Zhang");
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.h b/drivers/perf/hisilicon/hisi_uncore_mn.h
new file mode 100644
index 0000000..4cac2f3
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.h
@@ -0,0 +1,68 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_MN_H__
+#define __HISI_UNCORE_MN_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN RAW event types.
+ */
+enum armv8_hisi_mn_event_types {
+	HISI_HWEVENT_MN_EO_BARR_REQ	= 0x0,
+	HISI_HWEVENT_MN_EC_BARR_REQ	= 0x01,
+	HISI_HWEVENT_MN_DVM_OP_REQ	= 0x02,
+	HISI_HWEVENT_MN_DVM_SYNC_REQ	= 0x03,
+	HISI_HWEVENT_MN_READ_REQ	= 0x04,
+	HISI_HWEVENT_MN_WRITE_REQ	= 0x05,
+	HISI_HWEVENT_MN_COPYBK_REQ	= 0x06,
+	HISI_HWEVENT_MN_OTHER_REQ	= 0x07,
+	HISI_HWEVENT_MN_EVENT_MAX	= 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+	HISI_IDX_MN_COUNTER0	= 0x0,
+	HISI_IDX_MN_COUNTER_MAX	= 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR	0x04
+
+struct hisi_mn_hwcfg {
+	u32 evtype_reg0_off;
+	u32 counter_reg0_off;
+	u32 event_ctrl_reg_off;
+	u32 event_enable;
+	u32 module_id;
+	u32 bank_cfgen;
+};
+
+struct hisi_mn_data {
+	struct hisi_djtag_client *client;
+	DECLARE_BITMAP(hisi_mn_event_used_mask,
+				HISI_MAX_CFG_MN_CNTR);
+	struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_MN_H__ */
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 09/11] perf: hisi: Miscellanous node(MN) event counting in perf
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

From: Shaokun Zhang <zhangshaokun@hisilicon.com>

	1. Add support to count MN hardware events.
	2. Mn events are listed in sysfs at /sys/devices/hisi_mn2/events/
	   The events can be selected as shown in perf list
	   e.g.: For MN_READ_REQUEST event for Super CPU cluster 2 the
		event format is
		-e "hisi_mn2/read_req/"

Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: Anurup M <anurup.m@huawei.com>
---
 drivers/perf/hisilicon/Makefile         |   2 +-
 drivers/perf/hisilicon/hisi_uncore_mn.c | 571 ++++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_mn.h |  68 ++++
 3 files changed, 640 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_mn.h

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index e1766cf..8975104 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.c b/drivers/perf/hisilicon/hisi_uncore_mn.c
new file mode 100644
index 0000000..920e346
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.c
@@ -0,0 +1,571 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_mn.h"
+
+static inline int hisi_mn_counter_valid(int idx)
+{
+	return (idx >= HISI_IDX_MN_COUNTER0 &&
+			idx <= HISI_IDX_MN_COUNTER_MAX);
+}
+
+static u32 hisi_read_mn_counter(struct hisi_mn_data *mn_hwmod_data,
+								int idx)
+{
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	struct hisi_djtag_client *client = mn_hwmod_data->client;
+	u32 cfg_en, reg_offset, value;
+
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+	reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off + (idx * 4);
+
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	return value;
+}
+
+static u64 hisi_mn_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *pmn_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_mn_data *mn_hwmod_data;
+	u64 delta, prev_raw_count, new_raw_count = 0;
+	u32 cfg_en;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return 0;
+	}
+
+	mn_hwmod_data = pmn_pmu->hwmod_data;
+
+	/* Check if the MN data is initialized for this SCCL */
+	if (!mn_hwmod_data->client) {
+		dev_err(pmn_pmu->dev,
+			"SCL=%d not initialized!\n", pmn_pmu->scl_id);
+		return 0;
+	}
+
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	do {
+		prev_raw_count = local64_read(&hwc->prev_count);
+		new_raw_count =
+			hisi_read_mn_counter(mn_hwmod_data, idx);
+		delta = (new_raw_count - prev_raw_count) &
+						HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+	} while (local64_cmpxchg(
+			&hwc->prev_count, prev_raw_count, new_raw_count) !=
+							prev_raw_count);
+
+	return new_raw_count;
+}
+
+static void hisi_set_mn_evtype(struct hisi_pmu *pmn_pmu, int idx, u32 val)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 cfg_en, event_value, value = 0;
+
+	event_value = (val -
+			HISI_HWEVENT_MN_EO_BARR_REQ);
+
+	/* Value to write to event type register */
+	val = event_value << (8 * idx);
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Set the event in MN_EVENT_TYPE Register
+	 */
+	hisi_djtag_readreg(module_id,
+			   cfg_en,
+			   reg_offset,
+			   client, &value);
+
+	value &= ~(0xff << (8 * idx));
+	value |= val;
+
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static u32 hisi_write_mn_counter(struct hisi_pmu *pmn_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 cfg_en, reg_offset;
+	int ret;
+	int idx = GET_CNTR_IDX(hwc);
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return -EINVAL;
+	}
+
+	reg_offset = mn_hwmod_data->mn_hwcfg.counter_reg0_off +
+							(idx * 4);
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	ret = hisi_djtag_writereg(module_id,
+				  cfg_en,
+				  reg_offset,
+				  value,
+				  client);
+
+	return ret;
+}
+
+static void hisi_enable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Set the event_bus_en bit in MN_EVENT_CTRL to enable counting
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client,
+				&value);
+
+	value |= event_en;
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static void hisi_disable_mn_counter(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.event_ctrl_reg_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	u32 event_en = mn_hwmod_data->mn_hwcfg.event_enable;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Clear the event_bus_en bit in MN event control
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	value &= ~(event_en);
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static void hisi_clear_mn_event_idx(struct hisi_pmu *pmn_pmu, int idx)
+{
+	struct hisi_djtag_client *client;
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	u32 reg_offset = mn_hwmod_data->mn_hwcfg.evtype_reg0_off;
+	u32 module_id = mn_hwmod_data->mn_hwcfg.module_id;
+	void *bitmap_addr;
+	u32 cfg_en, value;
+
+	if (!hisi_mn_counter_valid(idx)) {
+		dev_err(pmn_pmu->dev,
+				"Unsupported event index:%d!\n", idx);
+		return;
+	}
+
+	bitmap_addr = mn_hwmod_data->hisi_mn_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+
+	/* Find the djtag Identifier of the Unit */
+	client = mn_hwmod_data->client;
+	cfg_en = mn_hwmod_data->mn_hwcfg.bank_cfgen;
+
+	/*
+	 * Clear the event in MN_EVENT_TYPE Register
+	 */
+	hisi_djtag_readreg(module_id,
+				cfg_en,
+				reg_offset,
+				client, &value);
+
+	value &= ~(0xff << (8 * idx));
+	value |= (0xff << (8 * idx));
+	hisi_djtag_writereg(module_id,
+				cfg_en,
+				reg_offset,
+				value,
+				client);
+}
+
+static int hisi_mn_get_event_idx(struct hisi_pmu *pmn_pmu)
+{
+	struct hisi_mn_data *mn_hwmod_data = pmn_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			mn_hwmod_data->hisi_mn_event_used_mask,
+					HISI_MAX_CFG_MN_CNTR);
+
+	if (event_idx == HISI_MAX_CFG_MN_CNTR)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		mn_hwmod_data->hisi_mn_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_mn_data(struct hisi_pmu *pmn_pmu)
+{
+	kfree(pmn_pmu->hwmod_data);
+	pmn_pmu->hwmod_data = NULL;
+}
+
+static int init_hisi_mn_hwcfg(struct device *dev,
+				struct hisi_mn_data *pmn_data)
+{
+	struct hisi_mn_hwcfg *pmn_hwcfg = &pmn_data->mn_hwcfg;
+	struct device_node *node = dev->of_node;
+
+	if (of_property_read_u32(node, "counter-reg",
+				     &pmn_hwcfg->counter_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read counter-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evctrl-reg",
+				     &pmn_hwcfg->event_ctrl_reg_off)) {
+		dev_err(dev, "DT:Couldnot read evctrl-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "event-en",
+				     &pmn_hwcfg->event_enable)) {
+		dev_err(dev, "DT:Couldnot read event-en property!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "evtype-reg",
+				     &pmn_hwcfg->evtype_reg0_off)) {
+		dev_err(dev, "DT:Couldnot read evtype-reg!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "module-id",
+				     &pmn_hwcfg->module_id)) {
+		dev_err(dev, "DT:Couldnot read module-id property!\n");
+		return -EINVAL;
+	}
+
+	if (of_property_read_u32(node, "cfgen-map",
+					&pmn_hwcfg->bank_cfgen)) {
+		dev_err(dev, "DT:Couldnot read cfgen-map property!\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int init_hisi_mn_data(struct device *dev,
+					struct hisi_pmu *pmn_pmu,
+					struct hisi_djtag_client *client)
+{
+	struct hisi_mn_data *mn_hwmod_data;
+	int ret;
+
+	mn_hwmod_data = kzalloc(sizeof(struct hisi_mn_data),
+							GFP_KERNEL);
+	if (!mn_hwmod_data)
+		return -ENOMEM;
+
+	/* Set the djtag Identifier */
+	mn_hwmod_data->client = client;
+
+	pmn_pmu->hw_events.events = devm_kcalloc(dev,
+					pmn_pmu->num_counters,
+					sizeof(*pmn_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!pmn_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&pmn_pmu->hw_events.pmu_lock);
+
+	pmn_pmu->hwmod_data = mn_hwmod_data;
+
+	ret = init_hisi_mn_hwcfg(dev, mn_hwmod_data);
+	if (ret)
+		goto fail;
+
+	return 0;
+
+fail:
+	hisi_free_mn_data(pmn_pmu);
+	return ret;
+}
+
+static struct attribute *hisi_mn_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_mn_format_group = {
+	.name = "format",
+	.attrs = hisi_mn_format_attr,
+};
+
+static struct attribute *hisi_mn_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(eo_barrier_req,
+				"event=0x0"),
+	HISI_PMU_EVENT_ATTR_STR(ec_barrier_req,
+				"event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(dvm_op_req,
+				"event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(dvm_sync_req,
+				"event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(read_req,
+				"event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(write_req,
+				"event=0x05"),
+	NULL,
+};
+
+static struct attribute_group hisi_mn_events_group = {
+	.name = "events",
+	.attrs = hisi_mn_events_attr,
+};
+
+static struct attribute *hisi_mn_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_mn_attr_group = {
+	.attrs = hisi_mn_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_mn_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_mn_cpumask_attr_group = {
+	.attrs = hisi_mn_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_mn_pmu_attr_groups[] = {
+	&hisi_mn_attr_group,
+	&hisi_mn_format_group,
+	&hisi_mn_events_group,
+	&hisi_mn_cpumask_attr_group,
+	NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_mn_ops = {
+	.set_evtype = hisi_set_mn_evtype,
+	.set_event_period = hisi_pmu_set_event_period,
+	.get_event_idx = hisi_mn_get_event_idx,
+	.clear_event_idx = hisi_clear_mn_event_idx,
+	.event_update = hisi_mn_event_update,
+	.enable_counter = hisi_enable_mn_counter,
+	.disable_counter = hisi_disable_mn_counter,
+	.write_counter = hisi_write_mn_counter,
+};
+
+static int hisi_mn_pmu_init(struct device *dev,
+					struct hisi_pmu *pmn_pmu)
+{
+	int ret;
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, pmn_pmu);
+	if (ret)
+		return ret;
+
+	pmn_pmu->name = kasprintf(GFP_KERNEL, "hisi_mn%d",
+						pmn_pmu->scl_id);
+	pmn_pmu->ops = &hisi_uncore_mn_ops;
+
+	pmn_pmu->dev = dev;
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &pmn_pmu->cpu);
+
+	return 0;
+}
+
+static int hisi_pmu_mn_dev_probe(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pmn_pmu = NULL;
+	struct device *dev = &client->dev;
+	int ret;
+
+	pmn_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(pmn_pmu))
+		return PTR_ERR(pmn_pmu);
+
+	ret = hisi_mn_pmu_init(dev, pmn_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_mn_data(dev, pmn_pmu, client);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	pmn_pmu->pmu = (struct pmu) {
+		.name = pmn_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_mn_pmu_attr_groups,
+	};
+
+	ret = hisi_uncore_pmu_setup(pmn_pmu, pmn_pmu->name);
+	if (ret) {
+		dev_err(pmn_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	/* Set the drv data to mn_pmu */
+	dev_set_drvdata(dev, pmn_pmu);
+
+	return 0;
+
+fail:
+	hisi_free_mn_data(pmn_pmu);
+
+fail_init:
+	if (pmn_pmu)
+		devm_kfree(dev, pmn_pmu);
+	dev_err(pmn_pmu->dev, "%s failed\n", __func__);
+
+	return ret;
+}
+
+static int hisi_pmu_mn_dev_remove(struct hisi_djtag_client *client)
+{
+	struct hisi_pmu *pmn_pmu = NULL;
+	struct device *dev = &client->dev;
+
+	pmn_pmu = dev_get_drvdata(dev);
+
+	perf_pmu_unregister(&pmn_pmu->pmu);
+	hisi_free_mn_data(pmn_pmu);
+
+	return 0;
+}
+
+static const struct of_device_id mn_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-mn-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, mn_of_match);
+
+static struct hisi_djtag_driver hisi_pmu_mn_driver = {
+	.driver = {
+		.name = "hisi-pmu-mn",
+		.of_match_table = mn_of_match,
+	},
+	.probe = hisi_pmu_mn_dev_probe,
+	.remove = hisi_pmu_mn_dev_remove,
+};
+
+static int __init hisi_pmu_mn_init(void)
+{
+	int rc;
+
+	rc = hisi_djtag_register_driver(THIS_MODULE, &hisi_pmu_mn_driver);
+	if (rc < 0) {
+		pr_err("hisi pmu mn init failed, rc=%d\n", rc);
+		return rc;
+	}
+
+	return 0;
+}
+module_init(hisi_pmu_mn_init);
+
+static void __exit hisi_pmu_mn_exit(void)
+{
+	hisi_djtag_unregister_driver(&hisi_pmu_mn_driver);
+}
+module_exit(hisi_pmu_mn_exit);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x MN PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Shaokun Zhang");
diff --git a/drivers/perf/hisilicon/hisi_uncore_mn.h b/drivers/perf/hisilicon/hisi_uncore_mn.h
new file mode 100644
index 0000000..4cac2f3
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_mn.h
@@ -0,0 +1,68 @@
+/*
+ * HiSilicon SoC MN Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Shaokun Zhang <zhangshaokun@hisilicon.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_MN_H__
+#define __HISI_UNCORE_MN_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon MN RAW event types.
+ */
+enum armv8_hisi_mn_event_types {
+	HISI_HWEVENT_MN_EO_BARR_REQ	= 0x0,
+	HISI_HWEVENT_MN_EC_BARR_REQ	= 0x01,
+	HISI_HWEVENT_MN_DVM_OP_REQ	= 0x02,
+	HISI_HWEVENT_MN_DVM_SYNC_REQ	= 0x03,
+	HISI_HWEVENT_MN_READ_REQ	= 0x04,
+	HISI_HWEVENT_MN_WRITE_REQ	= 0x05,
+	HISI_HWEVENT_MN_COPYBK_REQ	= 0x06,
+	HISI_HWEVENT_MN_OTHER_REQ	= 0x07,
+	HISI_HWEVENT_MN_EVENT_MAX	= 0x08,
+};
+
+/*
+ * ARMv8 HiSilicon Hardware counter Index.
+ */
+enum armv8_hisi_mn_counters {
+	HISI_IDX_MN_COUNTER0	= 0x0,
+	HISI_IDX_MN_COUNTER_MAX	= 0x4,
+};
+
+#define HISI_MAX_CFG_MN_CNTR	0x04
+
+struct hisi_mn_hwcfg {
+	u32 evtype_reg0_off;
+	u32 counter_reg0_off;
+	u32 event_ctrl_reg_off;
+	u32 event_enable;
+	u32 module_id;
+	u32 bank_cfgen;
+};
+
+struct hisi_mn_data {
+	struct hisi_djtag_client *client;
+	DECLARE_BITMAP(hisi_mn_event_used_mask,
+				HISI_MAX_CFG_MN_CNTR);
+	struct hisi_mn_hwcfg mn_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_MN_H__ */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 10/11] perf: hisi: Support for Hisilicon DDRC PMU.
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42     ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA
  Cc: guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA,
	anurupvasu-Re5JQEeQqe8AvxtiuMwx3w

	1. Add support for counting Hisilicon DDRC
	   statistics events in perf.
	2. Support a total of 13 statistics events.
	3. Events listed in /sys/devices/<pmu_name>/

Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
Signed-off-by: Shaokun Zhang <zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
---
 drivers/perf/hisilicon/Makefile           |   2 +-
 drivers/perf/hisilicon/hisi_uncore_ddrc.c | 444 ++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_ddrc.h |  73 +++++
 3 files changed, 518 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 8975104..8e9df2e 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o hisi_uncore_ddrc.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.c b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
new file mode 100644
index 0000000..b89a72e
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
@@ -0,0 +1,444 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_ddrc.h"
+
+static inline int hisi_ddrc_counter_valid(int idx, struct hisi_pmu *ddrc_pmu)
+{
+	return (idx >= 0 && idx < ddrc_pmu->num_counters);
+}
+
+static u32 hisi_ddrc_read32_relaxed(void __iomem *regs_base, u32 off)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	return readl_relaxed(reg_addr);
+}
+
+static void hisi_ddrc_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	writel(val, reg_addr);
+}
+
+static u32 hisi_read_ddrc_counter(struct hisi_ddrc_data *ddrc_hwmod_data,
+					unsigned long event_code, int idx)
+{
+	u32 value;
+	u32 reg_off;
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+	value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+								reg_off);
+	return value;
+}
+
+static u64 hisi_ddrc_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_ddrc_data *ddrc_hwmod_data;
+	u64 delta, prev_raw_count, new_raw_count = 0;
+
+	if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+		dev_err(ddrc_pmu->dev,
+			"%s: Unsupported event index:%d!\n", __func__, idx);
+		return 0;
+	}
+
+	ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+
+	/* Check if the DDRC data is initialized for this SCCL */
+	if (!ddrc_hwmod_data->regs_base) {
+		dev_err(ddrc_pmu->dev, "DDRC registers not mapped!\n");
+		return 0;
+	}
+
+	do {
+		prev_raw_count = local64_read(&hwc->prev_count);
+		new_raw_count =
+			hisi_read_ddrc_counter(ddrc_hwmod_data,
+						hwc->config_base, idx);
+		delta = (new_raw_count - prev_raw_count) &
+						HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+	} while (local64_cmpxchg(
+			&hwc->prev_count, prev_raw_count, new_raw_count) !=
+							prev_raw_count);
+
+	return new_raw_count;
+}
+
+static u32 hisi_write_ddrc_counter(struct hisi_pmu *ddrc_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	u32 reg_off;
+	u32 event_code = hwc->config_base;
+
+	if (!(event_code >= HISI_HWEVENT_DDRC_FLUX_WR &&
+		event_code < HISI_HWEVENT_DDRC_MAX_EVENT)) {
+		dev_err(ddrc_pmu->dev, "Unknown DDR evevnt!");
+		return 0;
+	}
+
+	if (!ddrc_hwmod_data->regs_base) {
+		dev_err(ddrc_pmu->dev,
+				"DDR reg address not mapped!\n");
+		return 0;
+	}
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+	hisi_ddrc_write32(ddrc_hwmod_data->regs_base,
+						reg_off, value);
+
+	return value;
+}
+
+static void hisi_ddrc_set_event_period(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	u32 event_code = hwc->config_base;
+	u32 reg_off;
+	u32 value;
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+	/*
+	 * For Hisilicon DDRC PMU we save the current counter value
+	 * to prev_count, as we have enabled continuous counting for
+	 * DDRC.
+	 */
+	value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+								reg_off);
+	local64_set(&hwc->prev_count, value);
+}
+
+static void hisi_clear_ddrc_event_idx(struct hisi_pmu *ddrc_pmu, int idx)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	void *bitmap_addr;
+
+	if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+		dev_err(ddrc_pmu->dev,
+			"%s:Unsupported event index:%d!\n", __func__, idx);
+		return;
+	}
+
+	bitmap_addr = ddrc_hwmod_data->hisi_ddrc_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+}
+
+static int hisi_ddrc_get_event_idx(struct hisi_pmu *ddrc_pmu)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			ddrc_hwmod_data->hisi_ddrc_event_used_mask,
+						ddrc_pmu->num_counters);
+
+	if (event_idx == ddrc_pmu->num_counters)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		ddrc_hwmod_data->hisi_ddrc_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_ddrc_data(struct hisi_pmu *ddrc_pmu)
+{
+	kfree(ddrc_pmu->hwmod_data);
+	ddrc_pmu->hwmod_data = NULL;
+}
+
+static void init_hisi_ddr(void __iomem *reg_base)
+{
+	u32 value;
+
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 0);
+
+	value = hisi_ddrc_read32_relaxed(reg_base, HISI_DDRC_CFG_PERF_REG_OFF);
+	value &= 0x2fffffff;
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CFG_PERF_REG_OFF, value);
+
+	/* Enable Continuous counting */
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 1);
+}
+
+static int init_hisi_ddrc_dts_data(struct platform_device *pdev,
+					struct hisi_ddrc_data *ddrc_hwmod_data)
+{
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	/* Continue for zero entries */
+	if (!res) {
+		dev_err(&pdev->dev, "No DDR reg resorces!\n");
+		return -EINVAL;
+	}
+
+	if (!resource_size(res)) {
+		dev_err(&pdev->dev, "Zero DDR reg entry!\n");
+		return -EINVAL;
+	}
+
+	ddrc_hwmod_data->regs_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ddrc_hwmod_data->regs_base))
+		return PTR_ERR(ddrc_hwmod_data->regs_base);
+
+	init_hisi_ddr(ddrc_hwmod_data->regs_base);
+
+	return 0;
+}
+
+static int init_hisi_ddrc_data(struct platform_device *pdev,
+					struct hisi_pmu *ddrc_pmu)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ddrc_pmu->hw_events.events = devm_kcalloc(dev,
+					ddrc_pmu->num_counters,
+					sizeof(*ddrc_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!ddrc_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&ddrc_pmu->hw_events.pmu_lock);
+
+	init_hisi_ddrc_dts_data(pdev, ddrc_pmu->hwmod_data);
+
+	return 0;
+
+fail:
+	hisi_free_ddrc_data(ddrc_pmu);
+	return ret;
+}
+
+static struct attribute *hisi_ddrc_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_ddrc_format_group = {
+	.name = "format",
+	.attrs = hisi_ddrc_format_attr,
+};
+
+static struct attribute *hisi_ddrc_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(flux_write, "event=0x00"),
+	HISI_PMU_EVENT_ATTR_STR(flux_read, "event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(flux_write_cmd, "event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(flux_read_cmd, "event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_write, "event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_read, "event=0x05"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_write_cmd, "event=0x06"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_read_cmd, "event=0x07"),
+	HISI_PMU_EVENT_ATTR_STR(write_latency_cnt0,
+					"event=0x08"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt0,
+					"event=0x09"),
+	HISI_PMU_EVENT_ATTR_STR(write_latency_cnt1,
+					"event=0x0A"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt1,
+						"event=0x0B"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt_inher,
+						"event=0x0C"),
+	NULL,
+};
+
+static struct attribute_group hisi_ddrc_events_group = {
+	.name = "events",
+	.attrs = hisi_ddrc_events_attr,
+};
+
+static struct attribute *hisi_ddrc_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_ddrc_attr_group = {
+	.attrs = hisi_ddrc_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_ddrc_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_ddrc_cpumask_attr_group = {
+	.attrs = hisi_ddrc_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = {
+	&hisi_ddrc_attr_group,
+	&hisi_ddrc_format_group,
+	&hisi_ddrc_events_group,
+	&hisi_ddrc_cpumask_attr_group,
+	NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_ddrc_ops = {
+	.set_event_period = hisi_ddrc_set_event_period,
+	.get_event_idx = hisi_ddrc_get_event_idx,
+	.clear_event_idx = hisi_clear_ddrc_event_idx,
+	.event_update = hisi_ddrc_event_update,
+	.write_counter = hisi_write_ddrc_counter,
+};
+
+static int hisi_ddrc_pmu_init(struct device *dev,
+					struct hisi_pmu *ddrc_pmu)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data;
+	struct hisi_ddrc_hwcfg *ddrc_hwcfg;
+	int ret;
+
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, ddrc_pmu);
+	if (ret)
+		return ret;
+
+	ddrc_hwmod_data = kzalloc(sizeof(struct hisi_ddrc_data),
+							GFP_KERNEL);
+	if (!ddrc_hwmod_data)
+		return -ENOMEM;
+
+	ddrc_hwcfg = &ddrc_hwmod_data->ddrc_hwcfg;
+	if (of_property_read_u32(dev->of_node, "ch-id",
+					 &ddrc_hwcfg->channel_id)) {
+		kfree(ddrc_hwmod_data);
+		return -EINVAL;
+	}
+
+	ddrc_pmu->name = kasprintf(GFP_KERNEL, "hisi_ddrc%d_%d",
+			ddrc_pmu->scl_id, ddrc_hwcfg->channel_id);
+
+	ddrc_pmu->ops = &hisi_uncore_ddrc_ops;
+	ddrc_pmu->hwmod_data = ddrc_hwmod_data;
+	ddrc_pmu->dev = dev;
+
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &ddrc_pmu->cpu);
+
+	return 0;
+}
+
+static const struct of_device_id ddrc_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-ddrc-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ddrc_of_match);
+
+static int hisi_pmu_ddrc_dev_probe(struct platform_device *pdev)
+{
+	struct hisi_pmu *ddrc_pmu;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *of_id;
+	int ret;
+
+	of_id = of_match_device(ddrc_of_match, dev);
+	if (!of_id)
+		return -EINVAL;
+
+	ddrc_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(ddrc_pmu))
+		return PTR_ERR(ddrc_pmu);
+
+	ret = hisi_ddrc_pmu_init(dev, ddrc_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_ddrc_data(pdev, ddrc_pmu);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	ddrc_pmu->pmu = (struct pmu) {
+		.name = ddrc_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_ddrc_pmu_attr_groups,
+	};
+
+	ret = hisi_uncore_pmu_setup(ddrc_pmu, ddrc_pmu->name);
+	if (ret) {
+		dev_err(ddrc_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, ddrc_pmu);
+
+	return 0;
+fail:
+	hisi_free_ddrc_data(ddrc_pmu);
+
+fail_init:
+	dev_err(ddrc_pmu->dev, "%s failed\n", __func__);
+
+	return ret;
+}
+
+static int hisi_pmu_ddrc_dev_remove(struct platform_device *pdev)
+{
+	struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev);
+
+	perf_pmu_unregister(&ddrc_pmu->pmu);
+	hisi_free_ddrc_data(ddrc_pmu);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver hisi_pmu_ddrc_driver = {
+	.driver = {
+		.name = "hisi-pmu-ddrc",
+		.of_match_table = ddrc_of_match,
+	},
+	.probe = hisi_pmu_ddrc_dev_probe,
+	.remove = hisi_pmu_ddrc_dev_remove,
+};
+module_platform_driver(hisi_pmu_ddrc_driver);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x DDRC PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.h b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
new file mode 100644
index 0000000..89eab6b
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
@@ -0,0 +1,73 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_DDRC_H__
+#define __HISI_UNCORE_DDRC_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon DDRC event types.
+ */
+enum armv8_hisi_ddrc_event_types {
+	HISI_HWEVENT_DDRC_FLUX_WR		= 0x0,
+	HISI_HWEVENT_DDRC_FLUX_RD		= 0x01,
+	HISI_HWEVENT_DDRC_FLUX_WCMD		= 0x02,
+	HISI_HWEVENT_DDRC_FLUX_RCMD		= 0x03,
+	HISI_HWEVENT_DDRC_FLUXID_WD		= 0x04,
+	HISI_HWEVENT_DDRC_FLUXID_RD		= 0x05,
+	HISI_HWEVENT_DDRC_FLUXID_WCMD		= 0x06,
+	HISI_HWEVENT_DDRC_FLUXID_RCMD		= 0x07,
+	HISI_HWEVENT_DDRC_WLAT_CNT0		= 0x08,
+	HISI_HWEVENT_DDRC_RLAT_CNT0		= 0x09,
+	HISI_HWEVENT_DDRC_WLAT_CNT1		= 0x0A,
+	HISI_HWEVENT_DDRC_RLAT_CNT1		= 0x0B,
+	HISI_HWEVENT_DDRC_INHERE_RLAT_CNT	= 0x0C,
+	HISI_HWEVENT_DDRC_MAX_EVENT,
+};
+
+#define HISI_DDRC_CTRL_PERF_REG_OFF		0x010
+#define HISI_DDRC_CFG_PERF_REG_OFF		0x270
+#define HISI_DDRC_FLUX_WR_REG_OFF		0x380
+#define HISI_DDRC_FLUX_RD_REG_OFF		0x384
+#define HISI_DDRC_FLUX_WCMD_REG_OFF		0x388
+#define HISI_DDRC_FLUX_RCMD_REG_OFF		0x38C
+#define HISI_DDRC_FLUXID_WR_REG_OFF		0x390
+#define HISI_DDRC_FLUXID_RD_REG_OFF		0x394
+#define HISI_DDRC_FLUXID_WCMD_REG_OFF		0x398
+#define HISI_DDRC_FLUXID_RCMD_REG_OFF		0x39C
+#define HISI_DDRC_FLUX_WLATCNT0_REG_OFF		0x3A0
+#define HISI_DDRC_FLUX_RLAT_CNT0_REG_OFF	0x3A4
+#define HISI_DDRC_FLUX_WLATCNT1_REG_OFF		0x3A8
+#define HISI_DDRC_FLUX_RLAT_CNT1_REG_OFF	0x3AC
+
+struct hisi_ddrc_hwcfg {
+	u32 channel_id;
+};
+
+struct hisi_ddrc_data {
+	void __iomem *regs_base;
+	DECLARE_BITMAP(hisi_ddrc_event_used_mask,
+				HISI_HWEVENT_DDRC_MAX_EVENT);
+	struct hisi_ddrc_hwcfg ddrc_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_DDRC_H__ */
-- 
2.1.4

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 10/11] perf: hisi: Support for Hisilicon DDRC PMU.
@ 2016-11-03  5:42     ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1. Add support for counting Hisilicon DDRC
	   statistics events in perf.
	2. Support a total of 13 statistics events.
	3. Events listed in /sys/devices/<pmu_name>/

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
---
 drivers/perf/hisilicon/Makefile           |   2 +-
 drivers/perf/hisilicon/hisi_uncore_ddrc.c | 444 ++++++++++++++++++++++++++++++
 drivers/perf/hisilicon/hisi_uncore_ddrc.h |  73 +++++
 3 files changed, 518 insertions(+), 1 deletion(-)
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.c
 create mode 100644 drivers/perf/hisilicon/hisi_uncore_ddrc.h

diff --git a/drivers/perf/hisilicon/Makefile b/drivers/perf/hisilicon/Makefile
index 8975104..8e9df2e 100644
--- a/drivers/perf/hisilicon/Makefile
+++ b/drivers/perf/hisilicon/Makefile
@@ -1 +1 @@
-obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o
+obj-$(CONFIG_HISI_PMU) += hisi_uncore_pmu.o hisi_uncore_l3c.o hisi_uncore_mn.o hisi_uncore_ddrc.o
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.c b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
new file mode 100644
index 0000000..b89a72e
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.c
@@ -0,0 +1,444 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/bitmap.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/perf_event.h>
+#include "hisi_uncore_ddrc.h"
+
+static inline int hisi_ddrc_counter_valid(int idx, struct hisi_pmu *ddrc_pmu)
+{
+	return (idx >= 0 && idx < ddrc_pmu->num_counters);
+}
+
+static u32 hisi_ddrc_read32_relaxed(void __iomem *regs_base, u32 off)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	return readl_relaxed(reg_addr);
+}
+
+static void hisi_ddrc_write32(void __iomem *regs_base, u32 off, u32 val)
+{
+	void __iomem *reg_addr = regs_base + off;
+
+	writel(val, reg_addr);
+}
+
+static u32 hisi_read_ddrc_counter(struct hisi_ddrc_data *ddrc_hwmod_data,
+					unsigned long event_code, int idx)
+{
+	u32 value;
+	u32 reg_off;
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+	value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+								reg_off);
+	return value;
+}
+
+static u64 hisi_ddrc_event_update(struct perf_event *event,
+				struct hw_perf_event *hwc, int idx)
+{
+	struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_ddrc_data *ddrc_hwmod_data;
+	u64 delta, prev_raw_count, new_raw_count = 0;
+
+	if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+		dev_err(ddrc_pmu->dev,
+			"%s: Unsupported event index:%d!\n", __func__, idx);
+		return 0;
+	}
+
+	ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+
+	/* Check if the DDRC data is initialized for this SCCL */
+	if (!ddrc_hwmod_data->regs_base) {
+		dev_err(ddrc_pmu->dev, "DDRC registers not mapped!\n");
+		return 0;
+	}
+
+	do {
+		prev_raw_count = local64_read(&hwc->prev_count);
+		new_raw_count =
+			hisi_read_ddrc_counter(ddrc_hwmod_data,
+						hwc->config_base, idx);
+		delta = (new_raw_count - prev_raw_count) &
+						HISI_MAX_PERIOD;
+
+		local64_add(delta, &event->count);
+	} while (local64_cmpxchg(
+			&hwc->prev_count, prev_raw_count, new_raw_count) !=
+							prev_raw_count);
+
+	return new_raw_count;
+}
+
+static u32 hisi_write_ddrc_counter(struct hisi_pmu *ddrc_pmu,
+				struct hw_perf_event *hwc, u32 value)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	u32 reg_off;
+	u32 event_code = hwc->config_base;
+
+	if (!(event_code >= HISI_HWEVENT_DDRC_FLUX_WR &&
+		event_code < HISI_HWEVENT_DDRC_MAX_EVENT)) {
+		dev_err(ddrc_pmu->dev, "Unknown DDR evevnt!");
+		return 0;
+	}
+
+	if (!ddrc_hwmod_data->regs_base) {
+		dev_err(ddrc_pmu->dev,
+				"DDR reg address not mapped!\n");
+		return 0;
+	}
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+
+	hisi_ddrc_write32(ddrc_hwmod_data->regs_base,
+						reg_off, value);
+
+	return value;
+}
+
+static void hisi_ddrc_set_event_period(struct perf_event *event)
+{
+	struct hw_perf_event *hwc = &event->hw;
+	struct hisi_pmu *ddrc_pmu = to_hisi_pmu(event->pmu);
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	u32 event_code = hwc->config_base;
+	u32 reg_off;
+	u32 value;
+
+	reg_off = HISI_DDRC_FLUX_WR_REG_OFF + (event_code * 4);
+	/*
+	 * For Hisilicon DDRC PMU we save the current counter value
+	 * to prev_count, as we have enabled continuous counting for
+	 * DDRC.
+	 */
+	value = hisi_ddrc_read32_relaxed(ddrc_hwmod_data->regs_base,
+								reg_off);
+	local64_set(&hwc->prev_count, value);
+}
+
+static void hisi_clear_ddrc_event_idx(struct hisi_pmu *ddrc_pmu, int idx)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	void *bitmap_addr;
+
+	if (!hisi_ddrc_counter_valid(idx, ddrc_pmu)) {
+		dev_err(ddrc_pmu->dev,
+			"%s:Unsupported event index:%d!\n", __func__, idx);
+		return;
+	}
+
+	bitmap_addr = ddrc_hwmod_data->hisi_ddrc_event_used_mask;
+
+	__clear_bit(idx, bitmap_addr);
+}
+
+static int hisi_ddrc_get_event_idx(struct hisi_pmu *ddrc_pmu)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data = ddrc_pmu->hwmod_data;
+	int event_idx;
+
+	event_idx =
+		find_first_zero_bit(
+			ddrc_hwmod_data->hisi_ddrc_event_used_mask,
+						ddrc_pmu->num_counters);
+
+	if (event_idx == ddrc_pmu->num_counters)
+		return -EAGAIN;
+
+	__set_bit(event_idx,
+		ddrc_hwmod_data->hisi_ddrc_event_used_mask);
+
+	return event_idx;
+}
+
+static void hisi_free_ddrc_data(struct hisi_pmu *ddrc_pmu)
+{
+	kfree(ddrc_pmu->hwmod_data);
+	ddrc_pmu->hwmod_data = NULL;
+}
+
+static void init_hisi_ddr(void __iomem *reg_base)
+{
+	u32 value;
+
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 0);
+
+	value = hisi_ddrc_read32_relaxed(reg_base, HISI_DDRC_CFG_PERF_REG_OFF);
+	value &= 0x2fffffff;
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CFG_PERF_REG_OFF, value);
+
+	/* Enable Continuous counting */
+	hisi_ddrc_write32(reg_base, HISI_DDRC_CTRL_PERF_REG_OFF, 1);
+}
+
+static int init_hisi_ddrc_dts_data(struct platform_device *pdev,
+					struct hisi_ddrc_data *ddrc_hwmod_data)
+{
+	struct resource *res;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	/* Continue for zero entries */
+	if (!res) {
+		dev_err(&pdev->dev, "No DDR reg resorces!\n");
+		return -EINVAL;
+	}
+
+	if (!resource_size(res)) {
+		dev_err(&pdev->dev, "Zero DDR reg entry!\n");
+		return -EINVAL;
+	}
+
+	ddrc_hwmod_data->regs_base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(ddrc_hwmod_data->regs_base))
+		return PTR_ERR(ddrc_hwmod_data->regs_base);
+
+	init_hisi_ddr(ddrc_hwmod_data->regs_base);
+
+	return 0;
+}
+
+static int init_hisi_ddrc_data(struct platform_device *pdev,
+					struct hisi_pmu *ddrc_pmu)
+{
+	struct device *dev = &pdev->dev;
+	int ret;
+
+	ddrc_pmu->hw_events.events = devm_kcalloc(dev,
+					ddrc_pmu->num_counters,
+					sizeof(*ddrc_pmu->hw_events.events),
+					GFP_KERNEL);
+	if (!ddrc_pmu->hw_events.events) {
+		ret = -ENOMEM;
+		goto fail;
+	}
+
+	raw_spin_lock_init(&ddrc_pmu->hw_events.pmu_lock);
+
+	init_hisi_ddrc_dts_data(pdev, ddrc_pmu->hwmod_data);
+
+	return 0;
+
+fail:
+	hisi_free_ddrc_data(ddrc_pmu);
+	return ret;
+}
+
+static struct attribute *hisi_ddrc_format_attr[] = {
+	HISI_PMU_FORMAT_ATTR(event, "config:0-11"),
+	NULL,
+};
+
+static struct attribute_group hisi_ddrc_format_group = {
+	.name = "format",
+	.attrs = hisi_ddrc_format_attr,
+};
+
+static struct attribute *hisi_ddrc_events_attr[] = {
+	HISI_PMU_EVENT_ATTR_STR(flux_write, "event=0x00"),
+	HISI_PMU_EVENT_ATTR_STR(flux_read, "event=0x01"),
+	HISI_PMU_EVENT_ATTR_STR(flux_write_cmd, "event=0x02"),
+	HISI_PMU_EVENT_ATTR_STR(flux_read_cmd, "event=0x03"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_write, "event=0x04"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_read, "event=0x05"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_write_cmd, "event=0x06"),
+	HISI_PMU_EVENT_ATTR_STR(fluxid_read_cmd, "event=0x07"),
+	HISI_PMU_EVENT_ATTR_STR(write_latency_cnt0,
+					"event=0x08"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt0,
+					"event=0x09"),
+	HISI_PMU_EVENT_ATTR_STR(write_latency_cnt1,
+					"event=0x0A"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt1,
+						"event=0x0B"),
+	HISI_PMU_EVENT_ATTR_STR(read_latency_cnt_inher,
+						"event=0x0C"),
+	NULL,
+};
+
+static struct attribute_group hisi_ddrc_events_group = {
+	.name = "events",
+	.attrs = hisi_ddrc_events_attr,
+};
+
+static struct attribute *hisi_ddrc_attrs[] = {
+	NULL,
+};
+
+struct attribute_group hisi_ddrc_attr_group = {
+	.attrs = hisi_ddrc_attrs,
+};
+
+static DEVICE_ATTR(cpumask, 0444, hisi_cpumask_sysfs_show, NULL);
+
+static struct attribute *hisi_ddrc_cpumask_attrs[] = {
+	&dev_attr_cpumask.attr,
+	NULL,
+};
+
+static const struct attribute_group hisi_ddrc_cpumask_attr_group = {
+	.attrs = hisi_ddrc_cpumask_attrs,
+};
+
+static const struct attribute_group *hisi_ddrc_pmu_attr_groups[] = {
+	&hisi_ddrc_attr_group,
+	&hisi_ddrc_format_group,
+	&hisi_ddrc_events_group,
+	&hisi_ddrc_cpumask_attr_group,
+	NULL,
+};
+
+static struct hisi_uncore_ops hisi_uncore_ddrc_ops = {
+	.set_event_period = hisi_ddrc_set_event_period,
+	.get_event_idx = hisi_ddrc_get_event_idx,
+	.clear_event_idx = hisi_clear_ddrc_event_idx,
+	.event_update = hisi_ddrc_event_update,
+	.write_counter = hisi_write_ddrc_counter,
+};
+
+static int hisi_ddrc_pmu_init(struct device *dev,
+					struct hisi_pmu *ddrc_pmu)
+{
+	struct hisi_ddrc_data *ddrc_hwmod_data;
+	struct hisi_ddrc_hwcfg *ddrc_hwcfg;
+	int ret;
+
+	/* Read common PMU properties */
+	ret = hisi_uncore_common_fwprop_read(dev, ddrc_pmu);
+	if (ret)
+		return ret;
+
+	ddrc_hwmod_data = kzalloc(sizeof(struct hisi_ddrc_data),
+							GFP_KERNEL);
+	if (!ddrc_hwmod_data)
+		return -ENOMEM;
+
+	ddrc_hwcfg = &ddrc_hwmod_data->ddrc_hwcfg;
+	if (of_property_read_u32(dev->of_node, "ch-id",
+					 &ddrc_hwcfg->channel_id)) {
+		kfree(ddrc_hwmod_data);
+		return -EINVAL;
+	}
+
+	ddrc_pmu->name = kasprintf(GFP_KERNEL, "hisi_ddrc%d_%d",
+			ddrc_pmu->scl_id, ddrc_hwcfg->channel_id);
+
+	ddrc_pmu->ops = &hisi_uncore_ddrc_ops;
+	ddrc_pmu->hwmod_data = ddrc_hwmod_data;
+	ddrc_pmu->dev = dev;
+
+	/* Pick one core to use for cpumask attributes */
+	cpumask_set_cpu(smp_processor_id(), &ddrc_pmu->cpu);
+
+	return 0;
+}
+
+static const struct of_device_id ddrc_of_match[] = {
+	{ .compatible = "hisilicon,hisi-pmu-ddrc-v1", },
+	{},
+};
+MODULE_DEVICE_TABLE(of, ddrc_of_match);
+
+static int hisi_pmu_ddrc_dev_probe(struct platform_device *pdev)
+{
+	struct hisi_pmu *ddrc_pmu;
+	struct device *dev = &pdev->dev;
+	const struct of_device_id *of_id;
+	int ret;
+
+	of_id = of_match_device(ddrc_of_match, dev);
+	if (!of_id)
+		return -EINVAL;
+
+	ddrc_pmu = hisi_pmu_alloc(dev);
+	if (IS_ERR(ddrc_pmu))
+		return PTR_ERR(ddrc_pmu);
+
+	ret = hisi_ddrc_pmu_init(dev, ddrc_pmu);
+	if (ret)
+		return ret;
+
+	ret = init_hisi_ddrc_data(pdev, ddrc_pmu);
+	if (ret)
+		goto fail_init;
+
+	/* Register with perf PMU */
+	ddrc_pmu->pmu = (struct pmu) {
+		.name = ddrc_pmu->name,
+		.task_ctx_nr = perf_invalid_context,
+		.event_init = hisi_uncore_pmu_event_init,
+		.add = hisi_uncore_pmu_add,
+		.del = hisi_uncore_pmu_del,
+		.start = hisi_uncore_pmu_start,
+		.stop = hisi_uncore_pmu_stop,
+		.read = hisi_uncore_pmu_read,
+		.attr_groups = hisi_ddrc_pmu_attr_groups,
+	};
+
+	ret = hisi_uncore_pmu_setup(ddrc_pmu, ddrc_pmu->name);
+	if (ret) {
+		dev_err(ddrc_pmu->dev, "hisi_uncore_pmu_init FAILED!!\n");
+		goto fail;
+	}
+
+	platform_set_drvdata(pdev, ddrc_pmu);
+
+	return 0;
+fail:
+	hisi_free_ddrc_data(ddrc_pmu);
+
+fail_init:
+	dev_err(ddrc_pmu->dev, "%s failed\n", __func__);
+
+	return ret;
+}
+
+static int hisi_pmu_ddrc_dev_remove(struct platform_device *pdev)
+{
+	struct hisi_pmu *ddrc_pmu = platform_get_drvdata(pdev);
+
+	perf_pmu_unregister(&ddrc_pmu->pmu);
+	hisi_free_ddrc_data(ddrc_pmu);
+	platform_set_drvdata(pdev, NULL);
+	return 0;
+}
+
+static struct platform_driver hisi_pmu_ddrc_driver = {
+	.driver = {
+		.name = "hisi-pmu-ddrc",
+		.of_match_table = ddrc_of_match,
+	},
+	.probe = hisi_pmu_ddrc_dev_probe,
+	.remove = hisi_pmu_ddrc_dev_remove,
+};
+module_platform_driver(hisi_pmu_ddrc_driver);
+
+MODULE_DESCRIPTION("HiSilicon SoC HIP0x DDRC PMU driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Anurup M");
diff --git a/drivers/perf/hisilicon/hisi_uncore_ddrc.h b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
new file mode 100644
index 0000000..89eab6b
--- /dev/null
+++ b/drivers/perf/hisilicon/hisi_uncore_ddrc.h
@@ -0,0 +1,73 @@
+/*
+ * HiSilicon SoC DDRC Hardware event counters support
+ *
+ * Copyright (C) 2016 Huawei Technologies Limited
+ * Author: Anurup M <anurup.m@huawei.com>
+ *
+ * This code is based on the uncore PMU's like arm-cci and
+ * arm-ccn.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * 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/>.
+ */
+#ifndef __HISI_UNCORE_DDRC_H__
+#define __HISI_UNCORE_DDRC_H__
+
+#include "hisi_uncore_pmu.h"
+
+/*
+ * ARMv8 HiSilicon DDRC event types.
+ */
+enum armv8_hisi_ddrc_event_types {
+	HISI_HWEVENT_DDRC_FLUX_WR		= 0x0,
+	HISI_HWEVENT_DDRC_FLUX_RD		= 0x01,
+	HISI_HWEVENT_DDRC_FLUX_WCMD		= 0x02,
+	HISI_HWEVENT_DDRC_FLUX_RCMD		= 0x03,
+	HISI_HWEVENT_DDRC_FLUXID_WD		= 0x04,
+	HISI_HWEVENT_DDRC_FLUXID_RD		= 0x05,
+	HISI_HWEVENT_DDRC_FLUXID_WCMD		= 0x06,
+	HISI_HWEVENT_DDRC_FLUXID_RCMD		= 0x07,
+	HISI_HWEVENT_DDRC_WLAT_CNT0		= 0x08,
+	HISI_HWEVENT_DDRC_RLAT_CNT0		= 0x09,
+	HISI_HWEVENT_DDRC_WLAT_CNT1		= 0x0A,
+	HISI_HWEVENT_DDRC_RLAT_CNT1		= 0x0B,
+	HISI_HWEVENT_DDRC_INHERE_RLAT_CNT	= 0x0C,
+	HISI_HWEVENT_DDRC_MAX_EVENT,
+};
+
+#define HISI_DDRC_CTRL_PERF_REG_OFF		0x010
+#define HISI_DDRC_CFG_PERF_REG_OFF		0x270
+#define HISI_DDRC_FLUX_WR_REG_OFF		0x380
+#define HISI_DDRC_FLUX_RD_REG_OFF		0x384
+#define HISI_DDRC_FLUX_WCMD_REG_OFF		0x388
+#define HISI_DDRC_FLUX_RCMD_REG_OFF		0x38C
+#define HISI_DDRC_FLUXID_WR_REG_OFF		0x390
+#define HISI_DDRC_FLUXID_RD_REG_OFF		0x394
+#define HISI_DDRC_FLUXID_WCMD_REG_OFF		0x398
+#define HISI_DDRC_FLUXID_RCMD_REG_OFF		0x39C
+#define HISI_DDRC_FLUX_WLATCNT0_REG_OFF		0x3A0
+#define HISI_DDRC_FLUX_RLAT_CNT0_REG_OFF	0x3A4
+#define HISI_DDRC_FLUX_WLATCNT1_REG_OFF		0x3A8
+#define HISI_DDRC_FLUX_RLAT_CNT1_REG_OFF	0x3AC
+
+struct hisi_ddrc_hwcfg {
+	u32 channel_id;
+};
+
+struct hisi_ddrc_data {
+	void __iomem *regs_base;
+	DECLARE_BITMAP(hisi_ddrc_event_used_mask,
+				HISI_HWEVENT_DDRC_MAX_EVENT);
+	struct hisi_ddrc_hwcfg ddrc_hwcfg;
+};
+
+#endif /* __HISI_UNCORE_DDRC_H__ */
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 11/11] dts: arm64: hip06: Add Hisilicon SoC PMU support
  2016-11-03  5:41 ` Anurup M
@ 2016-11-03  5:42   ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang
  Cc: guohanjun, shyju.pv, linuxarm, anurupvasu

	1. Add nodes for hip06 L3 cache to support uncore events.
	2. Add nodes for hip06 MN to support uncore events.
	3. Add nodes for hip06 DDRC to support uncore events.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 arch/arm64/boot/dts/hisilicon/hip06.dtsi | 116 +++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index cb9e018..9ff3afe 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -980,6 +980,122 @@
 			status = "disabled";
 		};
 
+		djtag0: djtag@60010000 {
+			compatible = "hisilicon,hip06-cpu-djtag-v1";
+			reg = <0x0 0x60010000 0x0 0x10000>;
+
+			/* L3 cache for socket0 CPU die scl#2 */
+			pmul3c0 {
+				compatible = "hisilicon,hisi-pmu-l3c-v1";
+				scl-id = <0x02>;
+				num-events = <0x16>;
+				num-counters = <0x08>;
+				module-id = <0x04 0x04 0x04 0x04>;
+				num-banks = <0x04>;
+				cfgen-map = <0x02 0x04 0x01 0x08>;
+				counter-reg = <0x170>;
+				evctrl-reg = <0x04>;
+				event-en = <0x1000000>;
+				evtype-reg = <0x140>;
+			};
+
+			/* Miscellaneous node for socket0
+			 * CPU die scl#2
+			 */
+			pmumn0 {
+				compatible = "hisilicon,hisi-pmu-mn-v1";
+				scl-id = <0x02>;
+				num-events = <0x09>;
+				num-counters = <0x04>;
+				module-id = <0x0b>;
+				cfgen-map = <0x01>;
+				counter-reg = <0x30>;
+				evctrl-reg = <0x40>;
+				event-en = <0x01>;
+				evtype-reg = <0x48>;
+			};
+		};
+
+		djtag1: djtag@40010000 {
+			compatible = "hisilicon,hip06-cpu-djtag-v1";
+			reg = <0x0 0x40010000 0x0 0x10000>;
+
+			/* L3 cache for socket0 CPU die scl#1 */
+			pmul3c1 {
+				compatible = "hisilicon,hisi-pmu-l3c-v1";
+				scl-id = <0x01>;
+				num-events = <0x16>;
+				num-counters = <0x08>;
+				module-id = <0x04 0x04 0x04 0x04>;
+				num-banks = <0x04>;
+				cfgen-map = <0x02 0x04 0x01 0x08>;
+				counter-reg = <0x170>;
+				evctrl-reg = <0x04>;
+				event-en = <0x1000000>;
+				evtype-reg = <0x140>;
+			};
+
+			/* Miscellaneous node for socket0
+			 * CPU die scl#1
+			 */
+			pmumn1 {
+				compatible = "hisilicon,hisi-pmu-mn-v1";
+				scl-id = <0x01>;
+				num-events = <0x09>;
+				num-counters = <0x04>;
+				module-id = <0x0b>;
+				cfgen-map = <0x01>;
+				counter-reg = <0x30>;
+				evctrl-reg = <0x40>;
+				event-en = <0x01>;
+				evtype-reg = <0x48>;
+			};
+		};
+
+		/* DDRC for CPU die scl #1 Channel #0 */
+		pmu_sccl0_ddrc0: pmu_ddrc0@40348000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x01>;
+			ch-id = <0x0>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x40348000 0x0 0x10000>; /* TOTEMA DDRC0 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #1 Channel #1 */
+		pmu_sccl0_ddrc1: pmu_ddrc1@40358000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x01>;
+			ch-id = <0x01>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x40358000 0x0 0x10000>; /* TOTEMA DDRC1 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #2 Channel #0 */
+		pmu_sccl1_ddrc0: pmu_ddrc0@60348000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x02>;
+			ch-id = <0x0>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x60348000 0x0 0x10000>; /* TOTEMC DDRC0 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #2 Channel #1 */
+		pmu_sccl1_ddrc1: pmu_ddrc1@60358000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x02>;
+			ch-id = <0x01>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x60358000 0x0 0x10000>; /* TOTEMC DDRC1 */
+			status = "okay";
+		};
+
 		sas1: sas@a2000000 {
 			compatible = "hisilicon,hip06-sas-v2";
 			reg = <0 0xa2000000 0 0x10000>;
-- 
2.1.4


^ permalink raw reply related	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 11/11] dts: arm64: hip06: Add Hisilicon SoC PMU support
@ 2016-11-03  5:42   ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-03  5:42 UTC (permalink / raw)
  To: linux-arm-kernel

	1. Add nodes for hip06 L3 cache to support uncore events.
	2. Add nodes for hip06 MN to support uncore events.
	3. Add nodes for hip06 DDRC to support uncore events.

Signed-off-by: Anurup M <anurup.m@huawei.com>
Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
Signed-off-by: John Garry <john.garry@huawei.com>
---
 arch/arm64/boot/dts/hisilicon/hip06.dtsi | 116 +++++++++++++++++++++++++++++++
 1 file changed, 116 insertions(+)

diff --git a/arch/arm64/boot/dts/hisilicon/hip06.dtsi b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
index cb9e018..9ff3afe 100644
--- a/arch/arm64/boot/dts/hisilicon/hip06.dtsi
+++ b/arch/arm64/boot/dts/hisilicon/hip06.dtsi
@@ -980,6 +980,122 @@
 			status = "disabled";
 		};
 
+		djtag0: djtag at 60010000 {
+			compatible = "hisilicon,hip06-cpu-djtag-v1";
+			reg = <0x0 0x60010000 0x0 0x10000>;
+
+			/* L3 cache for socket0 CPU die scl#2 */
+			pmul3c0 {
+				compatible = "hisilicon,hisi-pmu-l3c-v1";
+				scl-id = <0x02>;
+				num-events = <0x16>;
+				num-counters = <0x08>;
+				module-id = <0x04 0x04 0x04 0x04>;
+				num-banks = <0x04>;
+				cfgen-map = <0x02 0x04 0x01 0x08>;
+				counter-reg = <0x170>;
+				evctrl-reg = <0x04>;
+				event-en = <0x1000000>;
+				evtype-reg = <0x140>;
+			};
+
+			/* Miscellaneous node for socket0
+			 * CPU die scl#2
+			 */
+			pmumn0 {
+				compatible = "hisilicon,hisi-pmu-mn-v1";
+				scl-id = <0x02>;
+				num-events = <0x09>;
+				num-counters = <0x04>;
+				module-id = <0x0b>;
+				cfgen-map = <0x01>;
+				counter-reg = <0x30>;
+				evctrl-reg = <0x40>;
+				event-en = <0x01>;
+				evtype-reg = <0x48>;
+			};
+		};
+
+		djtag1: djtag at 40010000 {
+			compatible = "hisilicon,hip06-cpu-djtag-v1";
+			reg = <0x0 0x40010000 0x0 0x10000>;
+
+			/* L3 cache for socket0 CPU die scl#1 */
+			pmul3c1 {
+				compatible = "hisilicon,hisi-pmu-l3c-v1";
+				scl-id = <0x01>;
+				num-events = <0x16>;
+				num-counters = <0x08>;
+				module-id = <0x04 0x04 0x04 0x04>;
+				num-banks = <0x04>;
+				cfgen-map = <0x02 0x04 0x01 0x08>;
+				counter-reg = <0x170>;
+				evctrl-reg = <0x04>;
+				event-en = <0x1000000>;
+				evtype-reg = <0x140>;
+			};
+
+			/* Miscellaneous node for socket0
+			 * CPU die scl#1
+			 */
+			pmumn1 {
+				compatible = "hisilicon,hisi-pmu-mn-v1";
+				scl-id = <0x01>;
+				num-events = <0x09>;
+				num-counters = <0x04>;
+				module-id = <0x0b>;
+				cfgen-map = <0x01>;
+				counter-reg = <0x30>;
+				evctrl-reg = <0x40>;
+				event-en = <0x01>;
+				evtype-reg = <0x48>;
+			};
+		};
+
+		/* DDRC for CPU die scl #1 Channel #0 */
+		pmu_sccl0_ddrc0: pmu_ddrc0 at 40348000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x01>;
+			ch-id = <0x0>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x40348000 0x0 0x10000>; /* TOTEMA DDRC0 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #1 Channel #1 */
+		pmu_sccl0_ddrc1: pmu_ddrc1 at 40358000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x01>;
+			ch-id = <0x01>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x40358000 0x0 0x10000>; /* TOTEMA DDRC1 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #2 Channel #0 */
+		pmu_sccl1_ddrc0: pmu_ddrc0 at 60348000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x02>;
+			ch-id = <0x0>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x60348000 0x0 0x10000>; /* TOTEMC DDRC0 */
+			status = "okay";
+		};
+
+		/* DDRC for CPU die scl #2 Channel #1 */
+		pmu_sccl1_ddrc1: pmu_ddrc1 at 60358000 {
+			compatible = "hisilicon,hisi-pmu-ddrc-v1";
+			scl-id = <0x02>;
+			ch-id = <0x01>;
+			num-events = <0x0d>;
+			num-counters = <0x04>;
+			reg = <0x0 0x60358000 0x0 0x10000>; /* TOTEMC DDRC1 */
+			status = "okay";
+		};
+
 		sas1: sas at a2000000 {
 			compatible = "hisilicon,hip06-sas-v2";
 			reg = <0 0xa2000000 0 0x10000>;
-- 
2.1.4

^ permalink raw reply related	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-03  5:42   ` Anurup M
@ 2016-11-03 18:26     ` Krzysztof Kozlowski
  -1 siblings, 0 replies; 52+ messages in thread
From: Krzysztof Kozlowski @ 2016-11-03 18:26 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, mark.rutland,
	will.deacon, corbet, catalin.marinas, robh+dt, arnd, f.fainelli,
	rmk+kernel, krzk, anurup.m, zhangshaokun, tanxiaojun, xuwei5,
	sanil.kumar, john.garry, gabriele.paoloni, shiju.jose,
	wangkefeng.wang, guohanjun, shyju.pv, linuxarm

On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
> 	1) Device tree bindings for Hisilicon SoC PMU.
> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.

Get rid of this weird indentation in all patches.


> 
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
> ---
>  .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>  1 file changed, 127 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> new file mode 100644
> index 0000000..e7b35e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> @@ -0,0 +1,127 @@
> +Hisilicon SoC hip05/06/07 ARMv8 PMU
> +===================================
> +
> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
> +comtroller. These PMU devices are independent and have hardware logic to
> +gather statistics and performance information.
> +
> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
> +
> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
> +
> +For uncore PMU devices there are some common required properties as detailed
> +below.
> +
> +Required properties:
> +	- compatible : This field contain two values. The first value is
> +		always "hisilicon" and second value is the Module type as shown
> +		in below examples:

Over-complicated sentence. Just:

	- compatible : One of:
		"hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
			device (Version 1)
		...
		...

BTW, No need of CC-ing me. I am not a maintainer of relevant subsystems.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-03 18:26     ` Krzysztof Kozlowski
  0 siblings, 0 replies; 52+ messages in thread
From: Krzysztof Kozlowski @ 2016-11-03 18:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
> 	1) Device tree bindings for Hisilicon SoC PMU.
> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.

Get rid of this weird indentation in all patches.


> 
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
> ---
>  .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>  1 file changed, 127 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> new file mode 100644
> index 0000000..e7b35e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> @@ -0,0 +1,127 @@
> +Hisilicon SoC hip05/06/07 ARMv8 PMU
> +===================================
> +
> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
> +comtroller. These PMU devices are independent and have hardware logic to
> +gather statistics and performance information.
> +
> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
> +
> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
> +
> +For uncore PMU devices there are some common required properties as detailed
> +below.
> +
> +Required properties:
> +	- compatible : This field contain two values. The first value is
> +		always "hisilicon" and second value is the Module type as shown
> +		in below examples:

Over-complicated sentence. Just:

	- compatible : One of:
		"hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
			device (Version 1)
		...
		...

BTW, No need of CC-ing me. I am not a maintainer of relevant subsystems.

Best regards,
Krzysztof

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-03 18:26     ` Krzysztof Kozlowski
@ 2016-11-04  5:06       ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-04  5:06 UTC (permalink / raw)
  To: Krzysztof Kozlowski
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, mark.rutland-5wv7dgnIgG8,
	will.deacon-5wv7dgnIgG8, corbet-T1hC0tSOHrs,
	catalin.marinas-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	arnd-r2nGTMty4D4, f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA



On Thursday 03 November 2016 11:56 PM, Krzysztof Kozlowski wrote:
> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>> 	1) Device tree bindings for Hisilicon SoC PMU.
>> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
> Get rid of this weird indentation in all patches.
Thanks. I shall remove the TAB from the commit message in all patches.
>
>> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Shaokun Zhang <zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>>   1 file changed, 127 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..e7b35e0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,127 @@
>> +Hisilicon SoC hip05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
>> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
>> +comtroller. These PMU devices are independent and have hardware logic to
>> +gather statistics and performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
>> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +
>> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
>> +
>> +For uncore PMU devices there are some common required properties as detailed
>> +below.
>> +
>> +Required properties:
>> +	- compatible : This field contain two values. The first value is
>> +		always "hisilicon" and second value is the Module type as shown
>> +		in below examples:
> Over-complicated sentence. Just:
>
> 	- compatible : One of:
> 		"hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
> 			device (Version 1)
> 		...
> 		...
Thanks. Shall refine it in next version.
> BTW, No need of CC-ing me. I am not a maintainer of relevant subsystems.
Sure.

Thanks,
Anurup
> Best regards,
> Krzysztof

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-04  5:06       ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-04  5:06 UTC (permalink / raw)
  To: linux-arm-kernel



On Thursday 03 November 2016 11:56 PM, Krzysztof Kozlowski wrote:
> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>> 	1) Device tree bindings for Hisilicon SoC PMU.
>> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
> Get rid of this weird indentation in all patches.
Thanks. I shall remove the TAB from the commit message in all patches.
>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>>   1 file changed, 127 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..e7b35e0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,127 @@
>> +Hisilicon SoC hip05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
>> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
>> +comtroller. These PMU devices are independent and have hardware logic to
>> +gather statistics and performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
>> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +
>> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
>> +
>> +For uncore PMU devices there are some common required properties as detailed
>> +below.
>> +
>> +Required properties:
>> +	- compatible : This field contain two values. The first value is
>> +		always "hisilicon" and second value is the Module type as shown
>> +		in below examples:
> Over-complicated sentence. Just:
>
> 	- compatible : One of:
> 		"hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
> 			device (Version 1)
> 		...
> 		...
Thanks. Shall refine it in next version.
> BTW, No need of CC-ing me. I am not a maintainer of relevant subsystems.
Sure.

Thanks,
Anurup
> Best regards,
> Krzysztof

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
  2016-11-03  5:41     ` Anurup M
@ 2016-11-10 17:23       ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 17:23 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm

Hi,

On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun@huawei.com>
> 
> 	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
> 	   bindings.
> 	2) Add Hisilicon Djtag dts binding.
> 
> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> ---
>  .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
>  1 file changed, 82 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 7df79a7..341cbb9 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -270,3 +270,85 @@ Required Properties:
>    [1]: bootwrapper size
>    [2]: relocation physical address
>    [3]: relocation size


> +-----------------------------------------------------------------------
> +The Hisilicon Djtag in CPU die is an independent component which connects with
> +some other components in the SoC by Debug Bus. This driver can be configured
> +to access the registers of connecting components (like L3 cache, l3 cache PMU
> + etc.) during real time debugging by sysctrl. These components appear as child
> +nodes of djtag.

Please put the djtag binding in a new file. 

It's clearly unrelated to many other things in this file, which should
also be split out.

> +The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
> +components (such as clock, reset, soft reset, secure debugger, etc.).
> +The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
> +by djtag driver for use by connecting components.

The djtag driver is irrelvant here.

If there's a realtionship between the two, please define that in the
binding rather than implicitly assuming it in the driver.

> +
> +Required properties:
> +  - compatible : "hisilicon,hip05-cpu-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for CPU sysctrl
> +Required properties:
> +- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";

This looks messy. Why is this syscon and a simple-mfd?

We should kill off / deprecate the syscon binding. It's completely
meaningless.

> +- reg : Register address and size
> +- djtag :
> +  - compatible : "hisilicon,hip06-cpu-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for CPU sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip07-cpu-djtag-v2"
> +  - reg : Register address and size
> +
> +Example:
> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
> +	djtag0: djtag@80010000 {
> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
> +		reg = <0x0 0x80010000 0x0 0x10000>;
> +
> +		/* For L3 cache PMU */
> +		pmul3c0 {
> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
> +			scl-id = <0x02>;
> +			num-events = <0x16>;
> +			num-counters = <0x08>;
> +			module-id = <0x04>;
> +			num-banks = <0x04>;
> +			cfgen-map = <0x02 0x04 0x01 0x08>;
> +			counter-reg = <0x170>;
> +			evctrl-reg = <0x04>;
> +			event-en = <0x1000000>;
> +			evtype-reg = <0x140>;
> +		};

This sub-node needs a binding document.

These properties are completely opaque

> +	};
> +
> +-----------------------------------------------------------------------
> +The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
> +of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
> +controller in CPU die and it manage different components, like RSA, etc.
> +The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
> +the sysctrl registers for use by connecting components.
> +All connecting components shall appear as child nodes of djtag.

I don't follow the above. This describes an ALGSUB system controllerm
but the documentation below is all about djtag.

Thanks,
Mark.

> +Hisilicon HiP05 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip05-io-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip06-io-djtag-v2"
> +  - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip07-io-djtag-v2"
> +  - reg : Register address and size
> +
> +Example:
> +	/* for Hisilicon HiP05 djtag for alg sysctrl */
> +	djtag0: djtag@d0000000 {
> +	       compatible = "hisilicon,hip05-io-djtag-v1";
> +		reg = <0x0 0xd0000000 0x0 0x10000>;
> +	};
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
@ 2016-11-10 17:23       ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 17:23 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun@huawei.com>
> 
> 	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
> 	   bindings.
> 	2) Add Hisilicon Djtag dts binding.
> 
> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> ---
>  .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
>  1 file changed, 82 insertions(+)
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> index 7df79a7..341cbb9 100644
> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
> @@ -270,3 +270,85 @@ Required Properties:
>    [1]: bootwrapper size
>    [2]: relocation physical address
>    [3]: relocation size


> +-----------------------------------------------------------------------
> +The Hisilicon Djtag in CPU die is an independent component which connects with
> +some other components in the SoC by Debug Bus. This driver can be configured
> +to access the registers of connecting components (like L3 cache, l3 cache PMU
> + etc.) during real time debugging by sysctrl. These components appear as child
> +nodes of djtag.

Please put the djtag binding in a new file. 

It's clearly unrelated to many other things in this file, which should
also be split out.

> +The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
> +components (such as clock, reset, soft reset, secure debugger, etc.).
> +The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
> +by djtag driver for use by connecting components.

The djtag driver is irrelvant here.

If there's a realtionship between the two, please define that in the
binding rather than implicitly assuming it in the driver.

> +
> +Required properties:
> +  - compatible : "hisilicon,hip05-cpu-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for CPU sysctrl
> +Required properties:
> +- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";

This looks messy. Why is this syscon and a simple-mfd?

We should kill off / deprecate the syscon binding. It's completely
meaningless.

> +- reg : Register address and size
> +- djtag :
> +  - compatible : "hisilicon,hip06-cpu-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for CPU sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip07-cpu-djtag-v2"
> +  - reg : Register address and size
> +
> +Example:
> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
> +	djtag0: djtag at 80010000 {
> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
> +		reg = <0x0 0x80010000 0x0 0x10000>;
> +
> +		/* For L3 cache PMU */
> +		pmul3c0 {
> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
> +			scl-id = <0x02>;
> +			num-events = <0x16>;
> +			num-counters = <0x08>;
> +			module-id = <0x04>;
> +			num-banks = <0x04>;
> +			cfgen-map = <0x02 0x04 0x01 0x08>;
> +			counter-reg = <0x170>;
> +			evctrl-reg = <0x04>;
> +			event-en = <0x1000000>;
> +			evtype-reg = <0x140>;
> +		};

This sub-node needs a binding document.

These properties are completely opaque

> +	};
> +
> +-----------------------------------------------------------------------
> +The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
> +of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
> +controller in CPU die and it manage different components, like RSA, etc.
> +The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
> +the sysctrl registers for use by connecting components.
> +All connecting components shall appear as child nodes of djtag.

I don't follow the above. This describes an ALGSUB system controllerm
but the documentation below is all about djtag.

Thanks,
Mark.

> +Hisilicon HiP05 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip05-io-djtag-v1"
> +  - reg : Register address and size
> +
> +Hisilicon HiP06 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip06-io-djtag-v2"
> +  - reg : Register address and size
> +
> +Hisilicon HiP07 djtag for ALGSUB sysctrl
> +Required properties:
> +  - compatible : "hisilicon,hip07-io-djtag-v2"
> +  - reg : Register address and size
> +
> +Example:
> +	/* for Hisilicon HiP05 djtag for alg sysctrl */
> +	djtag0: djtag at d0000000 {
> +	       compatible = "hisilicon,hip05-io-djtag-v1";
> +		reg = <0x0 0xd0000000 0x0 0x10000>;
> +	};
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
  2016-11-03  5:41   ` Anurup M
@ 2016-11-10 17:55       ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 17:55 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
	corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA

On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> 
> 	The Hisilicon Djtag is an independent component which connects
> 	with some other components in the SoC by Debug Bus. This driver
> 	can be configured to access the registers of connecting components
> 	(like L3 cache) during real time debugging.
> 

Just to check, is this likely to be used in multi-socket hardware, and
if so, are instances always-on?

> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/soc/Kconfig                 |   1 +
>  drivers/soc/Makefile                |   1 +
>  drivers/soc/hisilicon/Kconfig       |  12 +
>  drivers/soc/hisilicon/Makefile      |   1 +
>  drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
>  include/linux/soc/hisilicon/djtag.h |  38 +++
>  6 files changed, 692 insertions(+)
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/djtag.c
>  create mode 100644 include/linux/soc/hisilicon/djtag.h

Other than the PMU driver(s), what is going to use this?

If you don't have something in particular, please also place this under
drivers/perf/hisilicon, along with the PMU driver(s).

We can always move it later if necessary.

[...]

> +#define SC_DJTAG_TIMEOUT		100000	/* 100ms */

This would be better as:

#define SC_DJTAG_TIMEOUT_US	(100 * USEC_PER_MSEC)

(you'll need to include <linux/time64.h>)

[...]

> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
> +{
> +	void __iomem *reg_addr = regs_base + off;
> +
> +	*value = readl_relaxed(reg_addr);
> +}
> +
> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
> +{
> +	void __iomem *reg_addr = regs_base + off;
> +
> +	writel(val, reg_addr);
> +}

I think these make the driver harder to read, especially given the read
function is void and takes an output pointer.

In either case you can call readl/writel directly with base + off for
the __iomem ptr. Please do that.

> +
> +/*
> + * djtag_readwrite_v1/v2: djtag read/write interface
> + * @reg_base:	djtag register base address
> + * @offset:	register's offset
> + * @mod_sel:	module selection
> + * @mod_mask:	mask to select specific modules for write
> + * @is_w:	write -> true, read -> false
> + * @wval:	value to register for write
> + * @chain_id:	which sub module for read
> + * @rval:	value in register for read
> + *
> + * Return non-zero if error, else return 0.
> + */
> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
> +		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
> +{
> +	u32 rd;
> +	int timeout = SC_DJTAG_TIMEOUT;
> +
> +	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
> +		pr_warn("djtag: do nothing.\n");
> +		return 0;
> +	}
> +
> +	/* djtag mster enable & accelerate R,W */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
> +			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
> +
> +	/* select module */
> +	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
> +	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
> +				mod_mask & CHAIN_UNIT_CFG_EN);
> +
> +	if (is_w) {
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
> +	} else
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
> +
> +	/* address offset */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
> +
> +	/* start to write to djtag register */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
> +
> +	/* ensure the djtag operation is done */
> +	do {
> +		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
> +		if (!(rd & DJTAG_MSTR_EN))
> +			break;
> +
> +		udelay(1);
> +	} while (timeout--);
> +
> +	if (timeout < 0) {
> +		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
> +		return -EBUSY;
> +	}
> +
> +	if (!is_w)
> +		djtag_read32_relaxed(regs_base,
> +			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
> +
> +	return 0;
> +}

Please factor out the common bits into helpers and have separate
read/write functions. It's incredibly difficult to follow the code with
read/write hidden behind a boolean parameter.

> +static const struct of_device_id djtag_of_match[] = {
> +	/* for hip05(D02) cpu die */
> +	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip05(D02) io die */
> +	{ .compatible = "hisilicon,hip05-io-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip06(D03) cpu die */
> +	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip06(D03) io die */
> +	{ .compatible = "hisilicon,hip06-io-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	/* for hip07(D05) cpu die */
> +	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	/* for hip07(D05) io die */
> +	{ .compatible = "hisilicon,hip07-io-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	{},
> +};

Binding documentation for all of these should be added *before* this
patch, per Documentation/devicetree/bindings/submitting-patches.txt.
Please move any relevant binding documentation earlier in the series.

> +MODULE_DEVICE_TABLE(of, djtag_of_match);
> +
> +static ssize_t
> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
> +
> +	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
> +}
> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
> +
> +static struct attribute *hisi_djtag_dev_attrs[] = {
> +	NULL,
> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
> +	&dev_attr_modalias.attr,
> +	NULL
> +};
> +ATTRIBUTE_GROUPS(hisi_djtag_dev);

Why do we need to expose this under sysfs?

> +struct bus_type hisi_djtag_bus = {
> +	.name		= "hisi-djtag",
> +	.match		= hisi_djtag_device_match,
> +	.probe		= hisi_djtag_device_probe,
> +	.remove		= hisi_djtag_device_remove,
> +};

I take it the core bus code handles reference-counting users of the bus?

> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
> +						struct device_node *node)
> +{
> +	struct hisi_djtag_client *client;
> +	int status;
> +
> +	client = kzalloc(sizeof(*client), GFP_KERNEL);
> +	if (!client)
> +		return NULL;
> +
> +	client->host = host;
> +
> +	client->dev.parent = &client->host->dev;
> +	client->dev.bus = &hisi_djtag_bus;
> +	client->dev.type = &hisi_djtag_client_type;
> +	client->dev.of_node = node;

I suspect that we should do:

	client->dev.of_node = of_node_get(node);

... so that it's guaranteed we retain a reference.

> +	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
> +					DJTAG_PREFIX, node->name);
> +	dev_set_name(&client->dev, "%s", client->name);
> +
> +	status = device_register(&client->dev);
> +	if (status < 0) {
> +		pr_err("error adding new device, status=%d\n", status);
> +		kfree(client);
> +		return NULL;
> +	}
> +
> +	return client;
> +}
> +
> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
> +						struct hisi_djtag_host *host,
> +						struct device_node *node)
> +{
> +	struct hisi_djtag_client *client;
> +
> +	client = hisi_djtag_new_device(host, node);
> +	if (client == NULL) {
> +		dev_err(&host->dev, "error registering device %s\n",
> +			node->full_name);
> +		of_node_put(node);

I don't think this should be here, given what djtag_register_devices()
does.

> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return client;
> +}
> +
> +static void djtag_register_devices(struct hisi_djtag_host *host)
> +{
> +	struct device_node *node;
> +	struct hisi_djtag_client *client;
> +
> +	if (!host->of_node)
> +		return;
> +
> +	for_each_available_child_of_node(host->of_node, node) {
> +		if (of_node_test_and_set_flag(node, OF_POPULATED))
> +			continue;
> +		client = hisi_djtag_of_register_device(host, node);
> +		list_add(&client->next, &host->client_list);
> +	}
> +}

Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
list_add is not safe.

> +static int djtag_host_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct hisi_djtag_host *host;
> +	const struct of_device_id *of_id;
> +	struct resource *res;
> +	int rc;
> +
> +	of_id = of_match_device(djtag_of_match, dev);
> +	if (!of_id)
> +		return -EINVAL;
> +
> +	host = kzalloc(sizeof(*host), GFP_KERNEL);
> +	if (!host)
> +		return -ENOMEM;
> +
> +	host->of_node = dev->of_node;

	host->of_node = of_node_get(dev->of_node);

> +	host->djtag_readwrite = of_id->data;
> +	spin_lock_init(&host->lock);
> +
> +	INIT_LIST_HEAD(&host->client_list);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No reg resorces!\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}
> +
> +	if (!resource_size(res)) {
> +		dev_err(&pdev->dev, "Zero reg entry!\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}
> +
> +	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(host->sysctl_reg_map)) {
> +		dev_warn(dev, "Unable to map sysctl registers.\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}

Please have a common error path rather than duplicating this free.

e.g. at the end of the function have:

	err_free:
		kfree(host);
		return err;

... and in cases like this, have:

	if (whatever()) {
		dev_warn(dev, "this failed xxx\n");
		err = -EINVAL;
		goto err_free;
	}

Thanks,
Mark.
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
@ 2016-11-10 17:55       ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 17:55 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
> From: Tan Xiaojun <tanxiaojun@huawei.com>
> 
> 	The Hisilicon Djtag is an independent component which connects
> 	with some other components in the SoC by Debug Bus. This driver
> 	can be configured to access the registers of connecting components
> 	(like L3 cache) during real time debugging.
> 

Just to check, is this likely to be used in multi-socket hardware, and
if so, are instances always-on?

> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
> Signed-off-by: John Garry <john.garry@huawei.com>
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> ---
>  drivers/soc/Kconfig                 |   1 +
>  drivers/soc/Makefile                |   1 +
>  drivers/soc/hisilicon/Kconfig       |  12 +
>  drivers/soc/hisilicon/Makefile      |   1 +
>  drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
>  include/linux/soc/hisilicon/djtag.h |  38 +++
>  6 files changed, 692 insertions(+)
>  create mode 100644 drivers/soc/hisilicon/Kconfig
>  create mode 100644 drivers/soc/hisilicon/Makefile
>  create mode 100644 drivers/soc/hisilicon/djtag.c
>  create mode 100644 include/linux/soc/hisilicon/djtag.h

Other than the PMU driver(s), what is going to use this?

If you don't have something in particular, please also place this under
drivers/perf/hisilicon, along with the PMU driver(s).

We can always move it later if necessary.

[...]

> +#define SC_DJTAG_TIMEOUT		100000	/* 100ms */

This would be better as:

#define SC_DJTAG_TIMEOUT_US	(100 * USEC_PER_MSEC)

(you'll need to include <linux/time64.h>)

[...]

> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
> +{
> +	void __iomem *reg_addr = regs_base + off;
> +
> +	*value = readl_relaxed(reg_addr);
> +}
> +
> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
> +{
> +	void __iomem *reg_addr = regs_base + off;
> +
> +	writel(val, reg_addr);
> +}

I think these make the driver harder to read, especially given the read
function is void and takes an output pointer.

In either case you can call readl/writel directly with base + off for
the __iomem ptr. Please do that.

> +
> +/*
> + * djtag_readwrite_v1/v2: djtag read/write interface
> + * @reg_base:	djtag register base address
> + * @offset:	register's offset
> + * @mod_sel:	module selection
> + * @mod_mask:	mask to select specific modules for write
> + * @is_w:	write -> true, read -> false
> + * @wval:	value to register for write
> + * @chain_id:	which sub module for read
> + * @rval:	value in register for read
> + *
> + * Return non-zero if error, else return 0.
> + */
> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
> +		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
> +{
> +	u32 rd;
> +	int timeout = SC_DJTAG_TIMEOUT;
> +
> +	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
> +		pr_warn("djtag: do nothing.\n");
> +		return 0;
> +	}
> +
> +	/* djtag mster enable & accelerate R,W */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
> +			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
> +
> +	/* select module */
> +	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
> +	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
> +				mod_mask & CHAIN_UNIT_CFG_EN);
> +
> +	if (is_w) {
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
> +	} else
> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
> +
> +	/* address offset */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
> +
> +	/* start to write to djtag register */
> +	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
> +
> +	/* ensure the djtag operation is done */
> +	do {
> +		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
> +		if (!(rd & DJTAG_MSTR_EN))
> +			break;
> +
> +		udelay(1);
> +	} while (timeout--);
> +
> +	if (timeout < 0) {
> +		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
> +		return -EBUSY;
> +	}
> +
> +	if (!is_w)
> +		djtag_read32_relaxed(regs_base,
> +			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
> +
> +	return 0;
> +}

Please factor out the common bits into helpers and have separate
read/write functions. It's incredibly difficult to follow the code with
read/write hidden behind a boolean parameter.

> +static const struct of_device_id djtag_of_match[] = {
> +	/* for hip05(D02) cpu die */
> +	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip05(D02) io die */
> +	{ .compatible = "hisilicon,hip05-io-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip06(D03) cpu die */
> +	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
> +		.data = (void *)djtag_readwrite_v1 },
> +	/* for hip06(D03) io die */
> +	{ .compatible = "hisilicon,hip06-io-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	/* for hip07(D05) cpu die */
> +	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	/* for hip07(D05) io die */
> +	{ .compatible = "hisilicon,hip07-io-djtag-v2",
> +		.data = (void *)djtag_readwrite_v2 },
> +	{},
> +};

Binding documentation for all of these should be added *before* this
patch, per Documentation/devicetree/bindings/submitting-patches.txt.
Please move any relevant binding documentation earlier in the series.

> +MODULE_DEVICE_TABLE(of, djtag_of_match);
> +
> +static ssize_t
> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
> +{
> +	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
> +
> +	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
> +}
> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
> +
> +static struct attribute *hisi_djtag_dev_attrs[] = {
> +	NULL,
> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
> +	&dev_attr_modalias.attr,
> +	NULL
> +};
> +ATTRIBUTE_GROUPS(hisi_djtag_dev);

Why do we need to expose this under sysfs?

> +struct bus_type hisi_djtag_bus = {
> +	.name		= "hisi-djtag",
> +	.match		= hisi_djtag_device_match,
> +	.probe		= hisi_djtag_device_probe,
> +	.remove		= hisi_djtag_device_remove,
> +};

I take it the core bus code handles reference-counting users of the bus?

> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
> +						struct device_node *node)
> +{
> +	struct hisi_djtag_client *client;
> +	int status;
> +
> +	client = kzalloc(sizeof(*client), GFP_KERNEL);
> +	if (!client)
> +		return NULL;
> +
> +	client->host = host;
> +
> +	client->dev.parent = &client->host->dev;
> +	client->dev.bus = &hisi_djtag_bus;
> +	client->dev.type = &hisi_djtag_client_type;
> +	client->dev.of_node = node;

I suspect that we should do:

	client->dev.of_node = of_node_get(node);

... so that it's guaranteed we retain a reference.

> +	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
> +					DJTAG_PREFIX, node->name);
> +	dev_set_name(&client->dev, "%s", client->name);
> +
> +	status = device_register(&client->dev);
> +	if (status < 0) {
> +		pr_err("error adding new device, status=%d\n", status);
> +		kfree(client);
> +		return NULL;
> +	}
> +
> +	return client;
> +}
> +
> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
> +						struct hisi_djtag_host *host,
> +						struct device_node *node)
> +{
> +	struct hisi_djtag_client *client;
> +
> +	client = hisi_djtag_new_device(host, node);
> +	if (client == NULL) {
> +		dev_err(&host->dev, "error registering device %s\n",
> +			node->full_name);
> +		of_node_put(node);

I don't think this should be here, given what djtag_register_devices()
does.

> +		return ERR_PTR(-EINVAL);
> +	}
> +
> +	return client;
> +}
> +
> +static void djtag_register_devices(struct hisi_djtag_host *host)
> +{
> +	struct device_node *node;
> +	struct hisi_djtag_client *client;
> +
> +	if (!host->of_node)
> +		return;
> +
> +	for_each_available_child_of_node(host->of_node, node) {
> +		if (of_node_test_and_set_flag(node, OF_POPULATED))
> +			continue;
> +		client = hisi_djtag_of_register_device(host, node);
> +		list_add(&client->next, &host->client_list);
> +	}
> +}

Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
list_add is not safe.

> +static int djtag_host_probe(struct platform_device *pdev)
> +{
> +	struct device *dev = &pdev->dev;
> +	struct hisi_djtag_host *host;
> +	const struct of_device_id *of_id;
> +	struct resource *res;
> +	int rc;
> +
> +	of_id = of_match_device(djtag_of_match, dev);
> +	if (!of_id)
> +		return -EINVAL;
> +
> +	host = kzalloc(sizeof(*host), GFP_KERNEL);
> +	if (!host)
> +		return -ENOMEM;
> +
> +	host->of_node = dev->of_node;

	host->of_node = of_node_get(dev->of_node);

> +	host->djtag_readwrite = of_id->data;
> +	spin_lock_init(&host->lock);
> +
> +	INIT_LIST_HEAD(&host->client_list);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	if (!res) {
> +		dev_err(&pdev->dev, "No reg resorces!\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}
> +
> +	if (!resource_size(res)) {
> +		dev_err(&pdev->dev, "Zero reg entry!\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}
> +
> +	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
> +	if (IS_ERR(host->sysctl_reg_map)) {
> +		dev_warn(dev, "Unable to map sysctl registers.\n");
> +		kfree(host);
> +		return -EINVAL;
> +	}

Please have a common error path rather than duplicating this free.

e.g. at the end of the function have:

	err_free:
		kfree(host);
		return err;

... and in cases like this, have:

	if (whatever()) {
		dev_warn(dev, "this failed xxx\n");
		err = -EINVAL;
		goto err_free;
	}

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-03  5:42   ` Anurup M
@ 2016-11-10 18:30     ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 18:30 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm

Hi,

On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
> 	1) Device tree bindings for Hisilicon SoC PMU.
> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
> 
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
> ---
>  .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>  1 file changed, 127 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> new file mode 100644
> index 0000000..e7b35e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> @@ -0,0 +1,127 @@
> +Hisilicon SoC hip05/06/07 ARMv8 PMU
> +===================================
> +
> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR

s/PMU's/PMUs/

> +comtroller. These PMU devices are independent and have hardware logic to

s/comtroller/controller/

> +gather statistics and performance information.
> +
> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die

s/die's/dies/

> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
> +
> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.

s/bindigs/bindings/

> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
> +
> +For uncore PMU devices there are some common required properties as detailed
> +below.
> +
> +Required properties:
> +	- compatible : This field contain two values. The first value is
> +		always "hisilicon" and second value is the Module type as shown
> +		in below examples:

Just say:

 - Compatible: should contain one of:

> +		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
> +			device (Version 1)
> +		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
> +			device (Version 1)
> +		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
> +			device (Version 1)
> +		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
> +
> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
> +		   or IO die in the chip.

What's this needed for?

> +	- num-events : No of events supported by this PMU device.
> +
> +	- num-counters : No of hardware counters available for counting.

This isn't probeable or well-known?

> +
> +L3 cache
> +--------
> +The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
> +for L3 cache for each SCCL. For L3 cache PMU the additional required properties
> +are
> +	- counter-reg : Counter register offset.
> +
> +	- evtype-reg : Event select register offset.
> +
> +	- evctrl-reg : Event counting control(LAUCTRL) register offset.

Surely for a given revision of the chip these offsets are known? i.e.
surely the compatible string implies specific offsets?

> +	- event-en : Event enable value.

Huh?

> +	- module-id : Module ID to input for djtag. This property is an array of
> +		      module_id for each L3 cache banks.
> +
> +	- num-banks : Number of banks or instances of the device.

What's a bank? Surely they have separate instances of the PMU?

What order are these in?

> +	- cfgen-map : Config enable array to select the bank.

Huh?

> +Miscellaneous Node
> +-------------------
> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
> +for each SCCL. For MN PMU the additional required properties are
> +	- counter-reg : Counter register offset.
> +
> +	- evtype-reg : Event select register offset.
> +
> +	- evctrl-reg : Event counting control register offset.

Likewise, surely this is well-known for a given revision of the chip?

> +
> +	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
> +		      this property is a single value.
> +
> +	- cfgen-map : Config enable to select the bank. For MN it is a single value
> +
> +	- event-en : Event enable value.

Same comments as for the L3 cache nodes


[...]

> +DDR controller
> +--------------
> +Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
> +There are separate DT nodes for each DDR channel.
> +For DDRC PMU the additional required properties are
> +
> +	- ch-id : DDRC Channel ID.

Why is this necessary?

Thanks,
Mark.

> +	- reg : Register base address and range for the DDRC channel.
> +
> +Example:
> +	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
> +	pmu_sccl0_ddrc1: pmu_ddrc1@80358000 {
> +		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
> +		 scl-id = <0x02>;
> +		 ch-id = <0x1>;
> +		 num-events = <0x0D>;
> +		 num-counters = <0x04>;
> +		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
> +	 };
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-10 18:30     ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 18:30 UTC (permalink / raw)
  To: linux-arm-kernel

Hi,

On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
> 	1) Device tree bindings for Hisilicon SoC PMU.
> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
> 
> Signed-off-by: Anurup M <anurup.m@huawei.com>
> Signed-off-by: Shaokun Zhang <zhangshaokun@hisilicon.com>
> ---
>  .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>  1 file changed, 127 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> 
> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> new file mode 100644
> index 0000000..e7b35e0
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
> @@ -0,0 +1,127 @@
> +Hisilicon SoC hip05/06/07 ARMv8 PMU
> +===================================
> +
> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR

s/PMU's/PMUs/

> +comtroller. These PMU devices are independent and have hardware logic to

s/comtroller/controller/

> +gather statistics and performance information.
> +
> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die

s/die's/dies/

> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
> +
> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.

s/bindigs/bindings/

> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
> +
> +For uncore PMU devices there are some common required properties as detailed
> +below.
> +
> +Required properties:
> +	- compatible : This field contain two values. The first value is
> +		always "hisilicon" and second value is the Module type as shown
> +		in below examples:

Just say:

 - Compatible: should contain one of:

> +		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
> +			device (Version 1)
> +		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
> +			device (Version 1)
> +		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
> +			device (Version 1)
> +		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
> +
> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
> +		   or IO die in the chip.

What's this needed for?

> +	- num-events : No of events supported by this PMU device.
> +
> +	- num-counters : No of hardware counters available for counting.

This isn't probeable or well-known?

> +
> +L3 cache
> +--------
> +The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
> +for L3 cache for each SCCL. For L3 cache PMU the additional required properties
> +are
> +	- counter-reg : Counter register offset.
> +
> +	- evtype-reg : Event select register offset.
> +
> +	- evctrl-reg : Event counting control(LAUCTRL) register offset.

Surely for a given revision of the chip these offsets are known? i.e.
surely the compatible string implies specific offsets?

> +	- event-en : Event enable value.

Huh?

> +	- module-id : Module ID to input for djtag. This property is an array of
> +		      module_id for each L3 cache banks.
> +
> +	- num-banks : Number of banks or instances of the device.

What's a bank? Surely they have separate instances of the PMU?

What order are these in?

> +	- cfgen-map : Config enable array to select the bank.

Huh?

> +Miscellaneous Node
> +-------------------
> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
> +for each SCCL. For MN PMU the additional required properties are
> +	- counter-reg : Counter register offset.
> +
> +	- evtype-reg : Event select register offset.
> +
> +	- evctrl-reg : Event counting control register offset.

Likewise, surely this is well-known for a given revision of the chip?

> +
> +	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
> +		      this property is a single value.
> +
> +	- cfgen-map : Config enable to select the bank. For MN it is a single value
> +
> +	- event-en : Event enable value.

Same comments as for the L3 cache nodes


[...]

> +DDR controller
> +--------------
> +Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
> +There are separate DT nodes for each DDR channel.
> +For DDRC PMU the additional required properties are
> +
> +	- ch-id : DDRC Channel ID.

Why is this necessary?

Thanks,
Mark.

> +	- reg : Register base address and range for the DDRC channel.
> +
> +Example:
> +	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
> +	pmu_sccl0_ddrc1: pmu_ddrc1 at 80358000 {
> +		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
> +		 scl-id = <0x02>;
> +		 ch-id = <0x1>;
> +		 num-events = <0x0D>;
> +		 num-counters = <0x04>;
> +		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
> +	 };
> -- 
> 2.1.4
> 

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
  2016-11-03  5:42   ` Anurup M
@ 2016-11-10 19:10     ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 19:10 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm

On Thu, Nov 03, 2016 at 01:42:03AM -0400, Anurup M wrote:
> +	do {
> +		/* Get count from individual L3C banks and sum them up */
> +		for (i = 0; i < num_banks; i++) {
> +			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
> +									idx, i);
> +		}
> +		prev_raw_count = local64_read(&hwc->prev_count);
> +
> +		/*
> +		 * As prev_raw_count is updated with average value of
> +		 * L3 cache banks, we multiply it by no of banks and
> +		 * compute the delta
> +		 */
> +		delta = (total_raw_count - (prev_raw_count * num_banks)) &
> +								HISI_MAX_PERIOD;
> +
> +		local64_add(delta, &event->count);
> +
> +		/*
> +		 * Divide by num of banks to get average count and
> +		 * update prev_count with this value
> +		 */
> +		avg_raw_count = total_raw_count / num_banks;
> +	} while (local64_cmpxchg(
> +			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
> +							 prev_raw_count);

Please don't aggregate like this; expose separate PMUs instead.

This is racy, and by averaging and multiplying we're making up and/or
throwing away data.

[...]

> +	event_value = (val -
> +			HISI_HWEVENT_L3C_READ_ALLOCATE);
> +
> +	/* Select the appropriate Event select register */
> +	if (idx > 3)
> +		reg_offset += 4;
> +
> +	/* Value to write to event type register */
> +	val = event_value << (8 * idx);
> +

Please add helpers for these, and explain *why* the transformations are
necessary.

> +	/* Find the djtag Identifier of the Unit */
> +	client = l3c_hwmod_data->client;
> +
> +	/*
> +	 * Set the event in L3C_EVENT_TYPEx Register
> +	 * for all L3C banks
> +	 */

As above, it seems like you should expose a separate PMU per bank
instead. That applies for all the other instances where you iterate over
banks.

[...]

> +	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
> +		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
> +		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
> +		ret = hisi_djtag_writereg(module_id,
> +					cfg_en,
> +					reg_offset,
> +					value,
> +					client);
> +		if (!ret)
> +			ret = value;
> +	}

This is impossible to read. Please factor this into helpers such that
you don't need this amount of indentation.

Please do similarly elsewhere when you see this indentation pattern.

[...]

> +static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
> +{
> +	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
> +	int event_idx;
> +
> +	event_idx =
> +		find_first_zero_bit(
> +			l3c_hwmod_data->hisi_l3c_event_used_mask,
> +					 pl3c_pmu->num_counters);
> +
> +	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
> +		return -EAGAIN;
> +
> +	__set_bit(event_idx,
> +		l3c_hwmod_data->hisi_l3c_event_used_mask);
> +
> +	return event_idx;
> +}

Please get rid of the weird hungarian notation (i.e. don't use 'p' as a
prefix for pointers), and use temporary variables consistently, e.g.

static int hisi_l3c_get_event_idx(struct hisi_pmu *l3c_pmu)
{
	struct hisi_l3c_data *l3c_hwmod_data = l3c_pmu->hwmod_data;
	unsigned long *used_mask = l3c_hwmod_data->hisi_l3c_event_used_mask;
	int num_counters = pl3c_pmu->num_counters
	int idx;

	idx = find_first_zero_bit(used_mask, num_counters);
	if (idx == num_counters)
		return -EAGAIN;

	set_bit(idx, used_mask);

	return idx;
}

[...]

> +	if (of_property_read_u32(node, "counter-reg",
> +				     &pl3c_hwcfg->counter_reg0_off)) {
> +		dev_err(dev, "DT:Couldnot read counter-reg!\n");
> +		return -EINVAL;
> +	}

Please use spaces in these messages.

Otherwise, my comments on the binding apply here.

[...]

> +static int init_hisi_l3c_data(struct device *dev,
> +					struct hisi_pmu *pl3c_pmu,
> +					struct hisi_djtag_client *client)
> +{
> +	struct hisi_l3c_data *l3c_hwmod_data = NULL;
> +	int ret;
> +
> +	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
> +							GFP_KERNEL);

Use:

	l3c_hwmod_data = kzalloc(sizeof(*l3c_hwmod_data, GFP_KERNEL):

[...]

> +static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
> +{
> +	struct hisi_pmu *pl3c_pmu = NULL;
> +	struct device *dev = &client->dev;
> +	int ret;
> +
> +	pl3c_pmu = hisi_pmu_alloc(dev);
> +	if (IS_ERR(pl3c_pmu))
> +		return PTR_ERR(pl3c_pmu);

Why use error pointers for this?

hisi_pmu_alloc() only ever returns ERR_PTR(-ENOMEM) if it failed to
allocate.

It's far simpler to have it pass on NULL there, and here do:

	pl3c_pmu = hisi_pmu_alloc(dev);
	if (!pl3c_pmu)
		return -ENOMEM;

Please also s/pl3c_pmu/l3c_pmu/ here, and elsewhere throughout the
driver. The 'p' only serves to make this harder to read.

[...]

> +	/* Register with perf PMU */
> +	pl3c_pmu->pmu = (struct pmu) {
> +		.name = pl3c_pmu->name,
> +		.task_ctx_nr = perf_invalid_context,
> +		.event_init = hisi_uncore_pmu_event_init,
> +		.add = hisi_uncore_pmu_add,
> +		.del = hisi_uncore_pmu_del,
> +		.start = hisi_uncore_pmu_start,
> +		.stop = hisi_uncore_pmu_stop,
> +		.read = hisi_uncore_pmu_read,
> +	};

Please remove the comment above this.

[...]

> +int hisi_uncore_pmu_event_init(struct perf_event *event)
> +{
> +	int err;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);

This is undefined behaviour. This must be done *after* we check the
event->pmu->type.

> +
> +	if (event->attr.type != event->pmu->type)
> +		return -ENOENT;
> +
> +	/* we do not support sampling as the counters are all
> +	 * shared by all CPU cores in a CPU die(SCCL). Also we
> +	 * donot support attach to a task(per-process mode)
> +	 */
> +	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
> +		return -EOPNOTSUPP;
> +
> +	/* counters do not have these bits */
> +	if (event->attr.exclude_user	||
> +	    event->attr.exclude_kernel	||
> +	    event->attr.exclude_host	||
> +	    event->attr.exclude_guest	||
> +	    event->attr.exclude_hv	||
> +	    event->attr.exclude_idle)
> +		return -EINVAL;
> +
> +	if (event->cpu < 0)
> +		return -EINVAL;
> +
> +	event->cpu = cpumask_first(&phisi_pmu->cpu);

You should also check the event grouping.

Take a look at what we do in arch/arm/mm/cache-l2x0-pmu.c.

[...]

> +/*
> + * Enable counter and set the counter to count
> + * the event that we're interested in.
> + */
> +void hisi_uncore_pmu_enable_event(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	/* Disable the hardware event counting */
> +	if (phisi_pmu->ops->disable_counter)
> +		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

Why isn't the counter already disabled?

> +	/*
> +	 * Set event (if destined for Hisilicon SoC counters).
> +	 */
> +	if (phisi_pmu->ops->set_evtype)
> +		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
> +							hwc->config_base);

Why isn't this done in the pmu::event_add callback?

> +
> +	/* Enable the hardware event counting */
> +	if (phisi_pmu->ops->enable_counter)
> +		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

This should be the only necessary part of this function.

> +}
> +
> +void hisi_pmu_set_event_period(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	/*
> +	 * The Hisilicon PMU counters have a period of 2^32. To account for the
> +	 * possiblity of extreme interrupt latency we program for a period of
> +	 * half that. Hopefully we can handle the interrupt before another 2^31
> +	 * events occur and the counter overtakes its previous value.
> +	 */
> +	u64 val = 1ULL << 31;
> +
> +	local64_set(&hwc->prev_count, val);
> +
> +	/* Write to the hardware event counter */
> +	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
> +}
> +
> +void hisi_uncore_pmu_start(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +	struct hisi_pmu_hw_events *hw_events;
> +
> +	hw_events = &phisi_pmu->hw_events;
> +
> +	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
> +		return;
> +
> +	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
> +	hwc->state = 0;
> +
> +	if (phisi_pmu->ops->set_event_period)
> +		phisi_pmu->ops->set_event_period(event);

When will this differ from hisi_pmu_set_event_period() above?

> +	if (flags & PERF_EF_RELOAD) {
> +		u64 prev_raw_count =  local64_read(&hwc->prev_count);
> +
> +		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
> +						(u32)prev_raw_count);
> +	}

If we always go through hisi_pmu_set_event_period(), this looks
redundant.

> +
> +	hisi_uncore_pmu_enable_event(event);

There's no matching disable_event() call in this function, so this looks
suspicious.

> +	perf_event_update_userpage(event);
> +}
> +
> +void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	if (hwc->state & PERF_HES_UPTODATE)
> +		return;

Why?

[...]

> +int hisi_uncore_common_fwprop_read(struct device *dev,
> +					struct hisi_pmu *phisi_pmu)
> +{
> +	if (device_property_read_u32(dev, "num-events",
> +					&phisi_pmu->num_events)) {
> +		dev_err(dev, "Cant read num-events from DT!\n");
> +		return -EINVAL;
> +	}

For consistency with the rest of the driver, and given there is no ACPI
support, please use the of_property_* API here.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
@ 2016-11-10 19:10     ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-10 19:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Nov 03, 2016 at 01:42:03AM -0400, Anurup M wrote:
> +	do {
> +		/* Get count from individual L3C banks and sum them up */
> +		for (i = 0; i < num_banks; i++) {
> +			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
> +									idx, i);
> +		}
> +		prev_raw_count = local64_read(&hwc->prev_count);
> +
> +		/*
> +		 * As prev_raw_count is updated with average value of
> +		 * L3 cache banks, we multiply it by no of banks and
> +		 * compute the delta
> +		 */
> +		delta = (total_raw_count - (prev_raw_count * num_banks)) &
> +								HISI_MAX_PERIOD;
> +
> +		local64_add(delta, &event->count);
> +
> +		/*
> +		 * Divide by num of banks to get average count and
> +		 * update prev_count with this value
> +		 */
> +		avg_raw_count = total_raw_count / num_banks;
> +	} while (local64_cmpxchg(
> +			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
> +							 prev_raw_count);

Please don't aggregate like this; expose separate PMUs instead.

This is racy, and by averaging and multiplying we're making up and/or
throwing away data.

[...]

> +	event_value = (val -
> +			HISI_HWEVENT_L3C_READ_ALLOCATE);
> +
> +	/* Select the appropriate Event select register */
> +	if (idx > 3)
> +		reg_offset += 4;
> +
> +	/* Value to write to event type register */
> +	val = event_value << (8 * idx);
> +

Please add helpers for these, and explain *why* the transformations are
necessary.

> +	/* Find the djtag Identifier of the Unit */
> +	client = l3c_hwmod_data->client;
> +
> +	/*
> +	 * Set the event in L3C_EVENT_TYPEx Register
> +	 * for all L3C banks
> +	 */

As above, it seems like you should expose a separate PMU per bank
instead. That applies for all the other instances where you iterate over
banks.

[...]

> +	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
> +		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
> +		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
> +		ret = hisi_djtag_writereg(module_id,
> +					cfg_en,
> +					reg_offset,
> +					value,
> +					client);
> +		if (!ret)
> +			ret = value;
> +	}

This is impossible to read. Please factor this into helpers such that
you don't need this amount of indentation.

Please do similarly elsewhere when you see this indentation pattern.

[...]

> +static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
> +{
> +	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
> +	int event_idx;
> +
> +	event_idx =
> +		find_first_zero_bit(
> +			l3c_hwmod_data->hisi_l3c_event_used_mask,
> +					 pl3c_pmu->num_counters);
> +
> +	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
> +		return -EAGAIN;
> +
> +	__set_bit(event_idx,
> +		l3c_hwmod_data->hisi_l3c_event_used_mask);
> +
> +	return event_idx;
> +}

Please get rid of the weird hungarian notation (i.e. don't use 'p' as a
prefix for pointers), and use temporary variables consistently, e.g.

static int hisi_l3c_get_event_idx(struct hisi_pmu *l3c_pmu)
{
	struct hisi_l3c_data *l3c_hwmod_data = l3c_pmu->hwmod_data;
	unsigned long *used_mask = l3c_hwmod_data->hisi_l3c_event_used_mask;
	int num_counters = pl3c_pmu->num_counters
	int idx;

	idx = find_first_zero_bit(used_mask, num_counters);
	if (idx == num_counters)
		return -EAGAIN;

	set_bit(idx, used_mask);

	return idx;
}

[...]

> +	if (of_property_read_u32(node, "counter-reg",
> +				     &pl3c_hwcfg->counter_reg0_off)) {
> +		dev_err(dev, "DT:Couldnot read counter-reg!\n");
> +		return -EINVAL;
> +	}

Please use spaces in these messages.

Otherwise, my comments on the binding apply here.

[...]

> +static int init_hisi_l3c_data(struct device *dev,
> +					struct hisi_pmu *pl3c_pmu,
> +					struct hisi_djtag_client *client)
> +{
> +	struct hisi_l3c_data *l3c_hwmod_data = NULL;
> +	int ret;
> +
> +	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
> +							GFP_KERNEL);

Use:

	l3c_hwmod_data = kzalloc(sizeof(*l3c_hwmod_data, GFP_KERNEL):

[...]

> +static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
> +{
> +	struct hisi_pmu *pl3c_pmu = NULL;
> +	struct device *dev = &client->dev;
> +	int ret;
> +
> +	pl3c_pmu = hisi_pmu_alloc(dev);
> +	if (IS_ERR(pl3c_pmu))
> +		return PTR_ERR(pl3c_pmu);

Why use error pointers for this?

hisi_pmu_alloc() only ever returns ERR_PTR(-ENOMEM) if it failed to
allocate.

It's far simpler to have it pass on NULL there, and here do:

	pl3c_pmu = hisi_pmu_alloc(dev);
	if (!pl3c_pmu)
		return -ENOMEM;

Please also s/pl3c_pmu/l3c_pmu/ here, and elsewhere throughout the
driver. The 'p' only serves to make this harder to read.

[...]

> +	/* Register with perf PMU */
> +	pl3c_pmu->pmu = (struct pmu) {
> +		.name = pl3c_pmu->name,
> +		.task_ctx_nr = perf_invalid_context,
> +		.event_init = hisi_uncore_pmu_event_init,
> +		.add = hisi_uncore_pmu_add,
> +		.del = hisi_uncore_pmu_del,
> +		.start = hisi_uncore_pmu_start,
> +		.stop = hisi_uncore_pmu_stop,
> +		.read = hisi_uncore_pmu_read,
> +	};

Please remove the comment above this.

[...]

> +int hisi_uncore_pmu_event_init(struct perf_event *event)
> +{
> +	int err;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);

This is undefined behaviour. This must be done *after* we check the
event->pmu->type.

> +
> +	if (event->attr.type != event->pmu->type)
> +		return -ENOENT;
> +
> +	/* we do not support sampling as the counters are all
> +	 * shared by all CPU cores in a CPU die(SCCL). Also we
> +	 * donot support attach to a task(per-process mode)
> +	 */
> +	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
> +		return -EOPNOTSUPP;
> +
> +	/* counters do not have these bits */
> +	if (event->attr.exclude_user	||
> +	    event->attr.exclude_kernel	||
> +	    event->attr.exclude_host	||
> +	    event->attr.exclude_guest	||
> +	    event->attr.exclude_hv	||
> +	    event->attr.exclude_idle)
> +		return -EINVAL;
> +
> +	if (event->cpu < 0)
> +		return -EINVAL;
> +
> +	event->cpu = cpumask_first(&phisi_pmu->cpu);

You should also check the event grouping.

Take a look at what we do in arch/arm/mm/cache-l2x0-pmu.c.

[...]

> +/*
> + * Enable counter and set the counter to count
> + * the event that we're interested in.
> + */
> +void hisi_uncore_pmu_enable_event(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	/* Disable the hardware event counting */
> +	if (phisi_pmu->ops->disable_counter)
> +		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

Why isn't the counter already disabled?

> +	/*
> +	 * Set event (if destined for Hisilicon SoC counters).
> +	 */
> +	if (phisi_pmu->ops->set_evtype)
> +		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
> +							hwc->config_base);

Why isn't this done in the pmu::event_add callback?

> +
> +	/* Enable the hardware event counting */
> +	if (phisi_pmu->ops->enable_counter)
> +		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));

This should be the only necessary part of this function.

> +}
> +
> +void hisi_pmu_set_event_period(struct perf_event *event)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	/*
> +	 * The Hisilicon PMU counters have a period of 2^32. To account for the
> +	 * possiblity of extreme interrupt latency we program for a period of
> +	 * half that. Hopefully we can handle the interrupt before another 2^31
> +	 * events occur and the counter overtakes its previous value.
> +	 */
> +	u64 val = 1ULL << 31;
> +
> +	local64_set(&hwc->prev_count, val);
> +
> +	/* Write to the hardware event counter */
> +	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
> +}
> +
> +void hisi_uncore_pmu_start(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +	struct hisi_pmu_hw_events *hw_events;
> +
> +	hw_events = &phisi_pmu->hw_events;
> +
> +	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
> +		return;
> +
> +	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
> +	hwc->state = 0;
> +
> +	if (phisi_pmu->ops->set_event_period)
> +		phisi_pmu->ops->set_event_period(event);

When will this differ from hisi_pmu_set_event_period() above?

> +	if (flags & PERF_EF_RELOAD) {
> +		u64 prev_raw_count =  local64_read(&hwc->prev_count);
> +
> +		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
> +						(u32)prev_raw_count);
> +	}

If we always go through hisi_pmu_set_event_period(), this looks
redundant.

> +
> +	hisi_uncore_pmu_enable_event(event);

There's no matching disable_event() call in this function, so this looks
suspicious.

> +	perf_event_update_userpage(event);
> +}
> +
> +void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
> +{
> +	struct hw_perf_event *hwc = &event->hw;
> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> +
> +	if (hwc->state & PERF_HES_UPTODATE)
> +		return;

Why?

[...]

> +int hisi_uncore_common_fwprop_read(struct device *dev,
> +					struct hisi_pmu *phisi_pmu)
> +{
> +	if (device_property_read_u32(dev, "num-events",
> +					&phisi_pmu->num_events)) {
> +		dev_err(dev, "Cant read num-events from DT!\n");
> +		return -EINVAL;
> +	}

For consistency with the rest of the driver, and given there is no ACPI
support, please use the of_property_* API here.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
  2016-11-10 17:23       ` Mark Rutland
@ 2016-11-11 11:19         ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-11 11:19 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
	corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA



On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
> Hi,
>
> On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>>
>> 	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
>> 	   bindings.
>> 	2) Add Hisilicon Djtag dts binding.
>>
>> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> ---
>>   .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
>>   1 file changed, 82 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> index 7df79a7..341cbb9 100644
>> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> @@ -270,3 +270,85 @@ Required Properties:
>>     [1]: bootwrapper size
>>     [2]: relocation physical address
>>     [3]: relocation size
>
>> +-----------------------------------------------------------------------
>> +The Hisilicon Djtag in CPU die is an independent component which connects with
>> +some other components in the SoC by Debug Bus. This driver can be configured
>> +to access the registers of connecting components (like L3 cache, l3 cache PMU
>> + etc.) during real time debugging by sysctrl. These components appear as child
>> +nodes of djtag.
> Please put the djtag binding in a new file.
>
> It's clearly unrelated to many other things in this file, which should
> also be split out.
Ok. Shall move the djtag bindings to hisilicon/djtag.txt.

>> +The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
>> +components (such as clock, reset, soft reset, secure debugger, etc.).
>> +The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
>> +by djtag driver for use by connecting components.
> The djtag driver is irrelvant here.
>
> If there's a realtionship between the two, please define that in the
> binding rather than implicitly assuming it in the driver.
Ok. I shall remove "djtag driver" and shall modify as

+The Hisilicon Djtag in CPU die is an independent component which connects with
+some other components in the SoC by Debug Bus. The djtag will use the system controller
+registers to access the connecting components in CPU die (like L3 cache, l3 cache PMU
+etc.) during real time debugging. These components appear as child nodes of djtag.

>> +
>> +Required properties:
>> +  - compatible : "hisilicon,hip05-cpu-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP06 djtag for CPU sysctrl
>> +Required properties:
>> +- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
> This looks messy. Why is this syscon and a simple-mfd?
>
> We should kill off / deprecate the syscon binding. It's completely
> meaningless.
My Apologies. The syscon is not used now. Mistake in file version used 
for patch creation.
The compatible filed will be "hisilicon,hisi-cpu-djtag-v1"
>> +- reg : Register address and size
>> +- djtag :
>> +  - compatible : "hisilicon,hip06-cpu-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP07 djtag for CPU sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip07-cpu-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Example:
>> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
>> +	djtag0: djtag@80010000 {
>> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>> +
>> +		/* For L3 cache PMU */
>> +		pmul3c0 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			scl-id = <0x02>;
>> +			num-events = <0x16>;
>> +			num-counters = <0x08>;
>> +			module-id = <0x04>;
>> +			num-banks = <0x04>;
>> +			cfgen-map = <0x02 0x04 0x01 0x08>;
>> +			counter-reg = <0x170>;
>> +			evctrl-reg = <0x04>;
>> +			event-en = <0x1000000>;
>> +			evtype-reg = <0x140>;
>> +		};
> This sub-node needs a binding document.
>
> These properties are completely opaque
The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
Is it OK that I document here(hisilicon/djtag.txt), a reference to the 
PMU bindings doc ?

>> +	};
>> +
>> +-----------------------------------------------------------------------
>> +The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
>> +of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
>> +controller in CPU die and it manage different components, like RSA, etc.
>> +The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
>> +the sysctrl registers for use by connecting components.
>> +All connecting components shall appear as child nodes of djtag.
> I don't follow the above. This describes an ALGSUB system controllerm
> but the documentation below is all about djtag.
The ALGSUB is the name of the sysctrl of IO die. I shall remove ALGSUB 
to avoid confusion.

I would also add the below details in the introduction.

The Hisilicon djtag will use the system controller registers to control 
access to connecting modules
of CPU and IO die in the chip. There are separate system controller 
registers for CPU and IO die  in the chip.

And the section will be modified as

+Hisilicon HiP05 djtag for IO sysctrl
+Required properties:
+  - compatible : "hisilicon,hisi-io-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06/07 djtag for IO sysctrl
+Required properties:
+  - compatible : "hisilicon,hip06-io-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for IO sysctrl */
+	djtag0: djtag@d0000000 {
+	       compatible = "hisilicon,hisi-io-djtag-v1";
+		reg = <0x0 0xd0000000 0x0 0x10000>;
+	};

Thanks,
Anurup

> Thanks,
> Mark.
>
>> +Hisilicon HiP05 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip05-io-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP06 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip06-io-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP07 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip07-io-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Example:
>> +	/* for Hisilicon HiP05 djtag for alg sysctrl */
>> +	djtag0: djtag@d0000000 {
>> +	       compatible = "hisilicon,hip05-io-djtag-v1";
>> +		reg = <0x0 0xd0000000 0x0 0x10000>;
>> +	};
>> -- 
>> 2.1.4
>>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
@ 2016-11-11 11:19         ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-11 11:19 UTC (permalink / raw)
  To: linux-arm-kernel



On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
> Hi,
>
> On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun@huawei.com>
>>
>> 	1) Add Hisilicon HiP05/06/07 CPU and ALGSUB system controller dts
>> 	   bindings.
>> 	2) Add Hisilicon Djtag dts binding.
>>
>> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> ---
>>   .../bindings/arm/hisilicon/hisilicon.txt           | 82 ++++++++++++++++++++++
>>   1 file changed, 82 insertions(+)
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> index 7df79a7..341cbb9 100644
>> --- a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>> @@ -270,3 +270,85 @@ Required Properties:
>>     [1]: bootwrapper size
>>     [2]: relocation physical address
>>     [3]: relocation size
>
>> +-----------------------------------------------------------------------
>> +The Hisilicon Djtag in CPU die is an independent component which connects with
>> +some other components in the SoC by Debug Bus. This driver can be configured
>> +to access the registers of connecting components (like L3 cache, l3 cache PMU
>> + etc.) during real time debugging by sysctrl. These components appear as child
>> +nodes of djtag.
> Please put the djtag binding in a new file.
>
> It's clearly unrelated to many other things in this file, which should
> also be split out.
Ok. Shall move the djtag bindings to hisilicon/djtag.txt.

>> +The Hip05/06/07 CPU system controller(sysctrl) support to manage some important
>> +components (such as clock, reset, soft reset, secure debugger, etc.).
>> +The CPU sysctrl registers in hip05/06/07 doesnot use syscon but will be mapped
>> +by djtag driver for use by connecting components.
> The djtag driver is irrelvant here.
>
> If there's a realtionship between the two, please define that in the
> binding rather than implicitly assuming it in the driver.
Ok. I shall remove "djtag driver" and shall modify as

+The Hisilicon Djtag in CPU die is an independent component which connects with
+some other components in the SoC by Debug Bus. The djtag will use the system controller
+registers to access the connecting components in CPU die (like L3 cache, l3 cache PMU
+etc.) during real time debugging. These components appear as child nodes of djtag.

>> +
>> +Required properties:
>> +  - compatible : "hisilicon,hip05-cpu-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP06 djtag for CPU sysctrl
>> +Required properties:
>> +- compatible : "hisilicon,hip06-sysctrl", "syscon", "simple-mfd";
> This looks messy. Why is this syscon and a simple-mfd?
>
> We should kill off / deprecate the syscon binding. It's completely
> meaningless.
My Apologies. The syscon is not used now. Mistake in file version used 
for patch creation.
The compatible filed will be "hisilicon,hisi-cpu-djtag-v1"
>> +- reg : Register address and size
>> +- djtag :
>> +  - compatible : "hisilicon,hip06-cpu-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP07 djtag for CPU sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip07-cpu-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Example:
>> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
>> +	djtag0: djtag at 80010000 {
>> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>> +
>> +		/* For L3 cache PMU */
>> +		pmul3c0 {
>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>> +			scl-id = <0x02>;
>> +			num-events = <0x16>;
>> +			num-counters = <0x08>;
>> +			module-id = <0x04>;
>> +			num-banks = <0x04>;
>> +			cfgen-map = <0x02 0x04 0x01 0x08>;
>> +			counter-reg = <0x170>;
>> +			evctrl-reg = <0x04>;
>> +			event-en = <0x1000000>;
>> +			evtype-reg = <0x140>;
>> +		};
> This sub-node needs a binding document.
>
> These properties are completely opaque
The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
Is it OK that I document here(hisilicon/djtag.txt), a reference to the 
PMU bindings doc ?

>> +	};
>> +
>> +-----------------------------------------------------------------------
>> +The Hisilicon HiP05/06/07 ALGSUB system controller(sysctrl) is in IO die
>> +of SoC. It has a similar function as the Hisilicon HiP05/06/07 CPU system
>> +controller in CPU die and it manage different components, like RSA, etc.
>> +The Hisilicon Djtag in IO die has a similar function as in CPU die and maps
>> +the sysctrl registers for use by connecting components.
>> +All connecting components shall appear as child nodes of djtag.
> I don't follow the above. This describes an ALGSUB system controllerm
> but the documentation below is all about djtag.
The ALGSUB is the name of the sysctrl of IO die. I shall remove ALGSUB 
to avoid confusion.

I would also add the below details in the introduction.

The Hisilicon djtag will use the system controller registers to control 
access to connecting modules
of CPU and IO die in the chip. There are separate system controller 
registers for CPU and IO die  in the chip.

And the section will be modified as

+Hisilicon HiP05 djtag for IO sysctrl
+Required properties:
+  - compatible : "hisilicon,hisi-io-djtag-v1"
+  - reg : Register address and size
+
+Hisilicon HiP06/07 djtag for IO sysctrl
+Required properties:
+  - compatible : "hisilicon,hip06-io-djtag-v2"
+  - reg : Register address and size
+
+Example:
+	/* for Hisilicon HiP05 djtag for IO sysctrl */
+	djtag0: djtag at d0000000 {
+	       compatible = "hisilicon,hisi-io-djtag-v1";
+		reg = <0x0 0xd0000000 0x0 0x10000>;
+	};

Thanks,
Anurup

> Thanks,
> Mark.
>
>> +Hisilicon HiP05 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip05-io-djtag-v1"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP06 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip06-io-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Hisilicon HiP07 djtag for ALGSUB sysctrl
>> +Required properties:
>> +  - compatible : "hisilicon,hip07-io-djtag-v2"
>> +  - reg : Register address and size
>> +
>> +Example:
>> +	/* for Hisilicon HiP05 djtag for alg sysctrl */
>> +	djtag0: djtag at d0000000 {
>> +	       compatible = "hisilicon,hip05-io-djtag-v1";
>> +		reg = <0x0 0xd0000000 0x0 0x10000>;
>> +	};
>> -- 
>> 2.1.4
>>

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
  2016-11-11 11:19         ` Anurup M
@ 2016-11-11 11:53           ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-11 11:53 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm

On Fri, Nov 11, 2016 at 04:49:03PM +0530, Anurup M wrote:
> On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
> >On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:

> >>diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt

> >>+Example:
> >>+	/* for Hisilicon HiP05 djtag for CPU sysctrl */
> >>+	djtag0: djtag@80010000 {
> >>+		compatible = "hisilicon,hip05-cpu-djtag-v1";
> >>+		reg = <0x0 0x80010000 0x0 0x10000>;
> >>+
> >>+		/* For L3 cache PMU */
> >>+		pmul3c0 {
> >>+			compatible = "hisilicon,hisi-pmu-l3c-v1";
> >>+			scl-id = <0x02>;
> >>+			num-events = <0x16>;
> >>+			num-counters = <0x08>;
> >>+			module-id = <0x04>;
> >>+			num-banks = <0x04>;
> >>+			cfgen-map = <0x02 0x04 0x01 0x08>;
> >>+			counter-reg = <0x170>;
> >>+			evctrl-reg = <0x04>;
> >>+			event-en = <0x1000000>;
> >>+			evtype-reg = <0x140>;
> >>+		};
> >This sub-node needs a binding document.
> >
> >These properties are completely opaque
> The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
> Is it OK that I document here(hisilicon/djtag.txt), a reference to
> the PMU bindings doc ?

At this point in the series, that file does not exist yet, and this is
an undocumented beinding.

Please introduce this sub-node long with the PMU bindings, later in the
series.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
@ 2016-11-11 11:53           ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-11 11:53 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Nov 11, 2016 at 04:49:03PM +0530, Anurup M wrote:
> On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
> >On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:

> >>diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt

> >>+Example:
> >>+	/* for Hisilicon HiP05 djtag for CPU sysctrl */
> >>+	djtag0: djtag at 80010000 {
> >>+		compatible = "hisilicon,hip05-cpu-djtag-v1";
> >>+		reg = <0x0 0x80010000 0x0 0x10000>;
> >>+
> >>+		/* For L3 cache PMU */
> >>+		pmul3c0 {
> >>+			compatible = "hisilicon,hisi-pmu-l3c-v1";
> >>+			scl-id = <0x02>;
> >>+			num-events = <0x16>;
> >>+			num-counters = <0x08>;
> >>+			module-id = <0x04>;
> >>+			num-banks = <0x04>;
> >>+			cfgen-map = <0x02 0x04 0x01 0x08>;
> >>+			counter-reg = <0x170>;
> >>+			evctrl-reg = <0x04>;
> >>+			event-en = <0x1000000>;
> >>+			evtype-reg = <0x140>;
> >>+		};
> >This sub-node needs a binding document.
> >
> >These properties are completely opaque
> The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
> Is it OK that I document here(hisilicon/djtag.txt), a reference to
> the PMU bindings doc ?

At this point in the series, that file does not exist yet, and this is
an undocumented beinding.

Please introduce this sub-node long with the PMU bindings, later in the
series.

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
  2016-11-11 11:53           ` Mark Rutland
@ 2016-11-11 11:59             ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-11 11:59 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
	corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA



On Friday 11 November 2016 05:23 PM, Mark Rutland wrote:
> On Fri, Nov 11, 2016 at 04:49:03PM +0530, Anurup M wrote:
>> On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
>>> On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
>>>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>>>> +Example:
>>>> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
>>>> +	djtag0: djtag@80010000 {
>>>> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
>>>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>>>> +
>>>> +		/* For L3 cache PMU */
>>>> +		pmul3c0 {
>>>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>>>> +			scl-id = <0x02>;
>>>> +			num-events = <0x16>;
>>>> +			num-counters = <0x08>;
>>>> +			module-id = <0x04>;
>>>> +			num-banks = <0x04>;
>>>> +			cfgen-map = <0x02 0x04 0x01 0x08>;
>>>> +			counter-reg = <0x170>;
>>>> +			evctrl-reg = <0x04>;
>>>> +			event-en = <0x1000000>;
>>>> +			evtype-reg = <0x140>;
>>>> +		};
>>> This sub-node needs a binding document.
>>>
>>> These properties are completely opaque
>> The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
>> Is it OK that I document here(hisilicon/djtag.txt), a reference to
>> the PMU bindings doc ?
> At this point in the series, that file does not exist yet, and this is
> an undocumented beinding.
>
> Please introduce this sub-node long with the PMU bindings, later in the
> series.
Thanks, I got your suggestion. Will add this later in series.
> Thanks,
> Mark.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings
@ 2016-11-11 11:59             ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-11 11:59 UTC (permalink / raw)
  To: linux-arm-kernel



On Friday 11 November 2016 05:23 PM, Mark Rutland wrote:
> On Fri, Nov 11, 2016 at 04:49:03PM +0530, Anurup M wrote:
>> On Thursday 10 November 2016 10:53 PM, Mark Rutland wrote:
>>> On Thu, Nov 03, 2016 at 01:41:58AM -0400, Anurup M wrote:
>>>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt b/Documentation/devicetree/bindings/arm/hisilicon/hisilicon.txt
>>>> +Example:
>>>> +	/* for Hisilicon HiP05 djtag for CPU sysctrl */
>>>> +	djtag0: djtag at 80010000 {
>>>> +		compatible = "hisilicon,hip05-cpu-djtag-v1";
>>>> +		reg = <0x0 0x80010000 0x0 0x10000>;
>>>> +
>>>> +		/* For L3 cache PMU */
>>>> +		pmul3c0 {
>>>> +			compatible = "hisilicon,hisi-pmu-l3c-v1";
>>>> +			scl-id = <0x02>;
>>>> +			num-events = <0x16>;
>>>> +			num-counters = <0x08>;
>>>> +			module-id = <0x04>;
>>>> +			num-banks = <0x04>;
>>>> +			cfgen-map = <0x02 0x04 0x01 0x08>;
>>>> +			counter-reg = <0x170>;
>>>> +			evctrl-reg = <0x04>;
>>>> +			event-en = <0x1000000>;
>>>> +			evtype-reg = <0x140>;
>>>> +		};
>>> This sub-node needs a binding document.
>>>
>>> These properties are completely opaque
>> The L3 cache PMU bindings are defined @bindings/arm/hisilicon/pmu.txt.
>> Is it OK that I document here(hisilicon/djtag.txt), a reference to
>> the PMU bindings doc ?
> At this point in the series, that file does not exist yet, and this is
> an undocumented beinding.
>
> Please introduce this sub-node long with the PMU bindings, later in the
> series.
Thanks, I got your suggestion. Will add this later in series.
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-10 18:30     ` Mark Rutland
@ 2016-11-14  0:06       ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-14  0:06 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
	corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA



On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
> Hi,
>
> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>> 	1) Device tree bindings for Hisilicon SoC PMU.
>> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
>>
>> Signed-off-by: Anurup M<anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Shaokun Zhang<zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q@public.gmane.org>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>>   1 file changed, 127 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..e7b35e0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,127 @@
>> +Hisilicon SoC hip05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
>> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
> s/PMU's/PMUs/
OK.
>> +comtroller. These PMU devices are independent and have hardware logic to
> s/comtroller/controller/
>
>> +gather statistics and performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
> s/die's/dies/
OK.
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
>> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +
>> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
> s/bindigs/bindings/
OK. Thanks. I shall make sure with spell checker before sending v2.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
>> +
>> +For uncore PMU devices there are some common required properties as detailed
>> +below.
>> +
>> +Required properties:
>> +	- compatible : This field contain two values. The first value is
>> +		always "hisilicon" and second value is the Module type as shown
>> +		in below examples:
> Just say:
>
>   - Compatible: should contain one of:
OK.
>> +		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
>> +			device (Version 1)
>> +		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
>> +			device (Version 1)
>> +		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
>> +			device (Version 1)
>> +		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
>> +
>> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
>> +		   or IO die in the chip.
> What's this needed for?
This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2 - 
for scl-id = 2).
This is to identify the pmu correspond to which CPU die in the socket.
>> +	- num-events : No of events supported by this PMU device.
>> +
>> +	- num-counters : No of hardware counters available for counting.
> This isn't probeable or well-known?
My idea is to have the common properties of SoC PMU added here.
The num-events, num-counters etc. So that handling can be made common in 
the driver.
Is it not recommended? Please share your comments.
>> +
>> +L3 cache
>> +--------
>> +The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
>> +for L3 cache for each SCCL. For L3 cache PMU the additional required properties
>> +are
>> +	- counter-reg : Counter register offset.
>> +
>> +	- evtype-reg : Event select register offset.
>> +
>> +	- evctrl-reg : Event counting control(LAUCTRL) register offset.
> Surely for a given revision of the chip these offsets are known? i.e.
> surely the compatible string implies specific offsets?
>
>> +	- event-en : Event enable value.
> Huh?
As for the hip05/06 and 07 chips, the above four properties are same, I 
shall
move them to the driver.

The below two properties (module-id, cfgen-map) differs between chips 
hip05/06 and hip07.
There were moved here so as to have minimal changes in driver across 
chips hip05/06/07.

OR whether it is more recommended to have the of_device_id .data set 
accordingly for handling
different chip versions?

Please suggest.
>> +	- module-id : Module ID to input for djtag. This property is an array of
>> +		      module_id for each L3 cache banks.
>> +
>> +	- num-banks : Number of banks or instances of the device.
> What's a bank? Surely they have separate instances of the PMU?
Yes each bank is a separate instance of PMU.
If it is recommended to have each L3 cache bank registered as separate 
PMU with perf, then this property will be removed.
> What order are these in?
The bank number will start from "1" till "4" for L3 cache as there are 
four banks in hip05/06/07 chips.
>> +	- cfgen-map : Config enable array to select the bank.
> Huh?
>
>> +Miscellaneous Node
>> +-------------------
>> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
>> +for each SCCL. For MN PMU the additional required properties are
>> +	- counter-reg : Counter register offset.
>> +
>> +	- evtype-reg : Event select register offset.
>> +
>> +	- evctrl-reg : Event counting control register offset.
> Likewise, surely this is well-known for a given revision of the chip?
>
>> +
>> +	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
>> +		      this property is a single value.
>> +
>> +	- cfgen-map : Config enable to select the bank. For MN it is a single value
>> +
>> +	- event-en : Event enable value.
> Same comments as for the L3 cache nodes
>
>
> [...]
>
>> +DDR controller
>> +--------------
>> +Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
>> +There are separate DT nodes for each DDR channel.
>> +For DDRC PMU the additional required properties are
>> +
>> +	- ch-id : DDRC Channel ID.
> Why is this necessary?
This is used as suffix to the PMU name. hisi_ddrc<scl-id>_<ch-id>. 
(hisi_ddrc2_0 - for scl-id = 2, ddrc channel = 0).
This is to identify the pmu correspond to which DDRC channel.

Thanks,
Anurup
> Thanks,
> Mark.
>
>> +	- reg : Register base address and range for the DDRC channel.
>> +
>> +Example:
>> +	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
>> +	pmu_sccl0_ddrc1: pmu_ddrc1@80358000 {
>> +		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
>> +		 scl-id = <0x02>;
>> +		 ch-id = <0x1>;
>> +		 num-events = <0x0D>;
>> +		 num-counters = <0x04>;
>> +		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
>> +	 };
>> -- 
>> 2.1.4
>>

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-14  0:06       ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-14  0:06 UTC (permalink / raw)
  To: linux-arm-kernel



On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
> Hi,
>
> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>> 	1) Device tree bindings for Hisilicon SoC PMU.
>> 	2) Add example for Hisilicon L3 cache, MN and DDRC PMU.
>>
>> Signed-off-by: Anurup M<anurup.m@huawei.com>
>> Signed-off-by: Shaokun Zhang<zhangshaokun@hisilicon.com>
>> ---
>>   .../devicetree/bindings/arm/hisilicon/pmu.txt      | 127 +++++++++++++++++++++
>>   1 file changed, 127 insertions(+)
>>   create mode 100644 Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>>
>> diff --git a/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> new file mode 100644
>> index 0000000..e7b35e0
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/hisilicon/pmu.txt
>> @@ -0,0 +1,127 @@
>> +Hisilicon SoC hip05/06/07 ARMv8 PMU
>> +===================================
>> +
>> +The Hisilicon SoC chips like hip05/06/07 etc. consist of varous independent
>> +system device PMU's such as L3 cache (L3C), Miscellaneous Nodes(MN) and DDR
> s/PMU's/PMUs/
OK.
>> +comtroller. These PMU devices are independent and have hardware logic to
> s/comtroller/controller/
>
>> +gather statistics and performance information.
>> +
>> +HiSilicon SoC chip is encapsulated by multiple CPU and IO die's. The CPU die
> s/die's/dies/
OK.
>> +is called as Super CPU cluster (SCCL) which includes 16 cpu-cores. Every SCCL
>> +is further grouped as CPU clusters (CCL) which includes 4 cpu-cores each.
>> +e.g. In the case of hip05/06/07, each SCCL has 1 L3 cache and 1 MN PMU device.
>> +
>> +The Hisilicon SoC PMU DT node bindigs for uncore PMU devices are as below.
> s/bindigs/bindings/
OK. Thanks. I shall make sure with spell checker before sending v2.
>> +For PMU devices like L3 cache. MN etc. which are accessed using the djtag,
>> +the parent node will be the djtag node of the corresponding CPU die(SCCL).
>> +
>> +For uncore PMU devices there are some common required properties as detailed
>> +below.
>> +
>> +Required properties:
>> +	- compatible : This field contain two values. The first value is
>> +		always "hisilicon" and second value is the Module type as shown
>> +		in below examples:
> Just say:
>
>   - Compatible: should contain one of:
OK.
>> +		(a) "hisilicon,hisi-pmu-l3c-v1" for Hisilicon SoC L3C PMU
>> +			device (Version 1)
>> +		(b) "hisilicon,hisi-pmu-mn-v1" for Hisilicon SoC MN PMU
>> +			device (Version 1)
>> +		(c) "hisilicon,hisi-pmu-ddrc-v1" for Hisilicon SoC DDRC PMU
>> +			device (Version 1)
>> +		The hip05/06/07 chips have v1 hardware for L3C, MN and DDRC.
>> +
>> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
>> +		   or IO die in the chip.
> What's this needed for?
This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2 - 
for scl-id = 2).
This is to identify the pmu correspond to which CPU die in the socket.
>> +	- num-events : No of events supported by this PMU device.
>> +
>> +	- num-counters : No of hardware counters available for counting.
> This isn't probeable or well-known?
My idea is to have the common properties of SoC PMU added here.
The num-events, num-counters etc. So that handling can be made common in 
the driver.
Is it not recommended? Please share your comments.
>> +
>> +L3 cache
>> +--------
>> +The L3 cache is dedicated for each SCCL and hence there are separate DT nodes
>> +for L3 cache for each SCCL. For L3 cache PMU the additional required properties
>> +are
>> +	- counter-reg : Counter register offset.
>> +
>> +	- evtype-reg : Event select register offset.
>> +
>> +	- evctrl-reg : Event counting control(LAUCTRL) register offset.
> Surely for a given revision of the chip these offsets are known? i.e.
> surely the compatible string implies specific offsets?
>
>> +	- event-en : Event enable value.
> Huh?
As for the hip05/06 and 07 chips, the above four properties are same, I 
shall
move them to the driver.

The below two properties (module-id, cfgen-map) differs between chips 
hip05/06 and hip07.
There were moved here so as to have minimal changes in driver across 
chips hip05/06/07.

OR whether it is more recommended to have the of_device_id .data set 
accordingly for handling
different chip versions?

Please suggest.
>> +	- module-id : Module ID to input for djtag. This property is an array of
>> +		      module_id for each L3 cache banks.
>> +
>> +	- num-banks : Number of banks or instances of the device.
> What's a bank? Surely they have separate instances of the PMU?
Yes each bank is a separate instance of PMU.
If it is recommended to have each L3 cache bank registered as separate 
PMU with perf, then this property will be removed.
> What order are these in?
The bank number will start from "1" till "4" for L3 cache as there are 
four banks in hip05/06/07 chips.
>> +	- cfgen-map : Config enable array to select the bank.
> Huh?
>
>> +Miscellaneous Node
>> +-------------------
>> +The MN is dedicated for each SCCL and hence there are separate DT nodes for MN
>> +for each SCCL. For MN PMU the additional required properties are
>> +	- counter-reg : Counter register offset.
>> +
>> +	- evtype-reg : Event select register offset.
>> +
>> +	- evctrl-reg : Event counting control register offset.
> Likewise, surely this is well-known for a given revision of the chip?
>
>> +
>> +	- module-id : Module ID to input for djtag. As MN doesnot have multiple banks
>> +		      this property is a single value.
>> +
>> +	- cfgen-map : Config enable to select the bank. For MN it is a single value
>> +
>> +	- event-en : Event enable value.
> Same comments as for the L3 cache nodes
>
>
> [...]
>
>> +DDR controller
>> +--------------
>> +Each SCCL in Hip05/06/07 chips have 2 DDR channels and hence 2 DDR controllers.
>> +There are separate DT nodes for each DDR channel.
>> +For DDRC PMU the additional required properties are
>> +
>> +	- ch-id : DDRC Channel ID.
> Why is this necessary?
This is used as suffix to the PMU name. hisi_ddrc<scl-id>_<ch-id>. 
(hisi_ddrc2_0 - for scl-id = 2, ddrc channel = 0).
This is to identify the pmu correspond to which DDRC channel.

Thanks,
Anurup
> Thanks,
> Mark.
>
>> +	- reg : Register base address and range for the DDRC channel.
>> +
>> +Example:
>> +	/* DDRC for CPU die scl #2 Channel #1 for hip05 */
>> +	pmu_sccl0_ddrc1: pmu_ddrc1 at 80358000 {
>> +		 compatible = "hisilicon,hisi-pmu-ddrc-v1";
>> +		 scl-id = <0x02>;
>> +		 ch-id = <0x1>;
>> +		 num-events = <0x0D>;
>> +		 num-counters = <0x04>;
>> +		 reg = <0x80358000 0x10000>; /* TOTEMC DDRC1 */
>> +	 };
>> -- 
>> 2.1.4
>>

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
  2016-11-10 19:10     ` Mark Rutland
@ 2016-11-14  8:11       ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-14  8:11 UTC (permalink / raw)
  To: Mark Rutland
  Cc: wangkefeng.wang, gabriele.paoloni, linux-doc, catalin.marinas,
	will.deacon, linuxarm, xuwei5, guohanjun, tanxiaojun, f.fainelli,
	corbet, krzk, rmk+kernel, devicetree, arnd, john.garry, shyju.pv,
	robh+dt, anurup.m, linux-arm-kernel, zhangshaokun, sanil.kumar,
	shiju.jose



On Friday 11 November 2016 12:40 AM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:42:03AM -0400, Anurup M wrote:
>> +	do {
>> +		/* Get count from individual L3C banks and sum them up */
>> +		for (i = 0; i < num_banks; i++) {
>> +			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
>> +									idx, i);
>> +		}
>> +		prev_raw_count = local64_read(&hwc->prev_count);
>> +
>> +		/*
>> +		 * As prev_raw_count is updated with average value of
>> +		 * L3 cache banks, we multiply it by no of banks and
>> +		 * compute the delta
>> +		 */
>> +		delta = (total_raw_count - (prev_raw_count * num_banks)) &
>> +								HISI_MAX_PERIOD;
>> +
>> +		local64_add(delta, &event->count);
>> +
>> +		/*
>> +		 * Divide by num of banks to get average count and
>> +		 * update prev_count with this value
>> +		 */
>> +		avg_raw_count = total_raw_count / num_banks;
>> +	} while (local64_cmpxchg(
>> +			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
>> +							 prev_raw_count);
> Please don't aggregate like this; expose separate PMUs instead.
>
> This is racy, and by averaging and multiplying we're making up and/or
> throwing away data.
>
> [...]
I have some concerns or doubts regarding registering each L3 cache bank 
as a separate PMU.

1) Each L3 cache PMU has total 22 statistics events. So if registered as 
a separate PMU, will it not
create multiple entries (with same event names) in event listing for 
multiple PMU's. Is there a way
I can avoid this? or this is acceptable?

Or is it acceptable to register as a single PMU and add a config 
parameter in the event listing to
identify the L3 cache bank. ex:  event name will appear as 
"hisi_l3c2/read_allocate,bank=?/".
And user can choose count from bank 0x01 as -e 
"hisi_l3c2/read_allocate,bank=0x01/"

2) The individual count from each L3 cache bank is not meaningful. Only 
aggregate count is useful.
Which is the accepted way to handle such counters?
a) Register each PMU instance as in hardware and handle the aggregation 
in user space.
b) Register as single PMU, handle aggregation in driver.

>> +	event_value = (val -
>> +			HISI_HWEVENT_L3C_READ_ALLOCATE);
>> +
>> +	/* Select the appropriate Event select register */
>> +	if (idx > 3)
>> +		reg_offset += 4;
>> +
>> +	/* Value to write to event type register */
>> +	val = event_value << (8 * idx);
>> +
> Please add helpers for these, and explain *why* the transformations are
> necessary.
Ok. shall add helpers.
>> +	/* Find the djtag Identifier of the Unit */
>> +	client = l3c_hwmod_data->client;
>> +
>> +	/*
>> +	 * Set the event in L3C_EVENT_TYPEx Register
>> +	 * for all L3C banks
>> +	 */
> As above, it seems like you should expose a separate PMU per bank
> instead. That applies for all the other instances where you iterate over
> banks.
>
> [...]
>
>> +	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
>> +		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
>> +		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
>> +		ret = hisi_djtag_writereg(module_id,
>> +					cfg_en,
>> +					reg_offset,
>> +					value,
>> +					client);
>> +		if (!ret)
>> +			ret = value;
>> +	}
> This is impossible to read. Please factor this into helpers such that
> you don't need this amount of indentation.
>
> Please do similarly elsewhere when you see this indentation pattern.
OK.
> [...]
>
>> +static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
>> +{
>> +	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
>> +	int event_idx;
>> +
>> +	event_idx =
>> +		find_first_zero_bit(
>> +			l3c_hwmod_data->hisi_l3c_event_used_mask,
>> +					 pl3c_pmu->num_counters);
>> +
>> +	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
>> +		return -EAGAIN;
>> +
>> +	__set_bit(event_idx,
>> +		l3c_hwmod_data->hisi_l3c_event_used_mask);
>> +
>> +	return event_idx;
>> +}
> Please get rid of the weird hungarian notation (i.e. don't use 'p' as a
> prefix for pointers), and use temporary variables consistently, e.g.
Ok.
> static int hisi_l3c_get_event_idx(struct hisi_pmu *l3c_pmu)
> {
> 	struct hisi_l3c_data *l3c_hwmod_data = l3c_pmu->hwmod_data;
> 	unsigned long *used_mask = l3c_hwmod_data->hisi_l3c_event_used_mask;
> 	int num_counters = pl3c_pmu->num_counters
> 	int idx;
>
> 	idx = find_first_zero_bit(used_mask, num_counters);
> 	if (idx == num_counters)
> 		return -EAGAIN;
>
> 	set_bit(idx, used_mask);
>
> 	return idx;
> }
>
> [...]
>
>> +	if (of_property_read_u32(node, "counter-reg",
>> +				     &pl3c_hwcfg->counter_reg0_off)) {
>> +		dev_err(dev, "DT:Couldnot read counter-reg!\n");
>> +		return -EINVAL;
>> +	}
> Please use spaces in these messages.
>
> Otherwise, my comments on the binding apply here.
>
> [...]
>
>> +static int init_hisi_l3c_data(struct device *dev,
>> +					struct hisi_pmu *pl3c_pmu,
>> +					struct hisi_djtag_client *client)
>> +{
>> +	struct hisi_l3c_data *l3c_hwmod_data = NULL;
>> +	int ret;
>> +
>> +	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
>> +							GFP_KERNEL);
> Use:
>
> 	l3c_hwmod_data = kzalloc(sizeof(*l3c_hwmod_data, GFP_KERNEL):
>
> [...]
Ok.
>> +static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
>> +{
>> +	struct hisi_pmu *pl3c_pmu = NULL;
>> +	struct device *dev = &client->dev;
>> +	int ret;
>> +
>> +	pl3c_pmu = hisi_pmu_alloc(dev);
>> +	if (IS_ERR(pl3c_pmu))
>> +		return PTR_ERR(pl3c_pmu);
> Why use error pointers for this?
>
> hisi_pmu_alloc() only ever returns ERR_PTR(-ENOMEM) if it failed to
> allocate.
>
> It's far simpler to have it pass on NULL there, and here do:
>
> 	pl3c_pmu = hisi_pmu_alloc(dev);
> 	if (!pl3c_pmu)
> 		return -ENOMEM;
>
> Please also s/pl3c_pmu/l3c_pmu/ here, and elsewhere throughout the
> driver. The 'p' only serves to make this harder to read.
>
> [...]
Ok.
>> +	/* Register with perf PMU */
>> +	pl3c_pmu->pmu = (struct pmu) {
>> +		.name = pl3c_pmu->name,
>> +		.task_ctx_nr = perf_invalid_context,
>> +		.event_init = hisi_uncore_pmu_event_init,
>> +		.add = hisi_uncore_pmu_add,
>> +		.del = hisi_uncore_pmu_del,
>> +		.start = hisi_uncore_pmu_start,
>> +		.stop = hisi_uncore_pmu_stop,
>> +		.read = hisi_uncore_pmu_read,
>> +	};
> Please remove the comment above this.
>
> [...]
Ok.
>> +int hisi_uncore_pmu_event_init(struct perf_event *event)
>> +{
>> +	int err;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> This is undefined behaviour. This must be done *after* we check the
> event->pmu->type.
Ok. shall move it after the check.
>> +
>> +	if (event->attr.type != event->pmu->type)
>> +		return -ENOENT;
>> +
>> +	/* we do not support sampling as the counters are all
>> +	 * shared by all CPU cores in a CPU die(SCCL). Also we
>> +	 * donot support attach to a task(per-process mode)
>> +	 */
>> +	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
>> +		return -EOPNOTSUPP;
>> +
>> +	/* counters do not have these bits */
>> +	if (event->attr.exclude_user	||
>> +	    event->attr.exclude_kernel	||
>> +	    event->attr.exclude_host	||
>> +	    event->attr.exclude_guest	||
>> +	    event->attr.exclude_hv	||
>> +	    event->attr.exclude_idle)
>> +		return -EINVAL;
>> +
>> +	if (event->cpu < 0)
>> +		return -EINVAL;
>> +
>> +	event->cpu = cpumask_first(&phisi_pmu->cpu);
> You should also check the event grouping.
>
> Take a look at what we do in arch/arm/mm/cache-l2x0-pmu.c.
>
> [...]
>
Ok. I got it. Thanks for the reference.
As you had suggested earlier I am thinking to put all these checks to a 
common lib function
and every uncore driver can call it.
Where can it be placed in source tree? Please suggest. shall add it.

>> +/*
>> + * Enable counter and set the counter to count
>> + * the event that we're interested in.
>> + */
>> +void hisi_uncore_pmu_enable_event(struct perf_event *event)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	/* Disable the hardware event counting */
>> +	if (phisi_pmu->ops->disable_counter)
>> +		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
> Why isn't the counter already disabled?
>
In Hisilicon SoC PMU for L3C, MN etc, the hip05/06/07 chips don't have 
support for
stop/start individual counters.
The steps to start counting are
1) Stop counting(global control) in L3 cache AUCTRL register.
2) set event type in the event type register (Event code written to
3) Start counting (global control) in L3 cache AUCTRL register.

>> +	/*
>> +	 * Set event (if destined for Hisilicon SoC counters).
>> +	 */
>> +	if (phisi_pmu->ops->set_evtype)
>> +		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
>> +							hwc->config_base);
> Why isn't this done in the pmu::event_add callback?
hisi_uncore_pmu_enable_event is called by pmu::event_add.
The Call flow is  pmu::event_add (hisi_uncore_pmu_add) --> 
hisi_uncore_pmu_start --> hisi_uncore_pmu_enable_event
>> +
>> +	/* Enable the hardware event counting */
>> +	if (phisi_pmu->ops->enable_counter)
>> +		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
> This should be the only necessary part of this function.
>
>> +}
>> +
>> +void hisi_pmu_set_event_period(struct perf_event *event)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	/*
>> +	 * The Hisilicon PMU counters have a period of 2^32. To account for the
>> +	 * possiblity of extreme interrupt latency we program for a period of
>> +	 * half that. Hopefully we can handle the interrupt before another 2^31
>> +	 * events occur and the counter overtakes its previous value.
>> +	 */
>> +	u64 val = 1ULL << 31;
>> +
>> +	local64_set(&hwc->prev_count, val);
>> +
>> +	/* Write to the hardware event counter */
>> +	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
>> +}
>> +
>> +void hisi_uncore_pmu_start(struct perf_event *event, int flags)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +	struct hisi_pmu_hw_events *hw_events;
>> +
>> +	hw_events = &phisi_pmu->hw_events;
>> +
>> +	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
>> +		return;
>> +
>> +	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
>> +	hwc->state = 0;
>> +
>> +	if (phisi_pmu->ops->set_event_period)
>> +		phisi_pmu->ops->set_event_period(event);
> When will this differ from hisi_pmu_set_event_period() above?
On some chips like hip05 and 06, the counter overflow handling with IRQ 
cannot
be supported as the Hisilicon Mbigen V1 driver cannot be upstream. So I 
am thinking
to have the counters start with value 0 and handle overflow with a poll 
method from
a hrtimer. So this method (hisi_pmu::set_event_period) could be changed 
in hisi_uncore(l3c/mn/ddrc).c.

Also for the DDRC PMU, the method (hisi_pmu::set_event_period) override 
to handle continous counting.
In DDRC PMU, the initial counter value start from 0.

Please share your comments.

>> +	if (flags & PERF_EF_RELOAD) {
>> +		u64 prev_raw_count =  local64_read(&hwc->prev_count);
>> +
>> +		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
>> +						(u32)prev_raw_count);
>> +	}
> If we always go through hisi_pmu_set_event_period(), this looks
> redundant.
>
>> +
>> +	hisi_uncore_pmu_enable_event(event);
> There's no matching disable_event() call in this function, so this looks
> suspicious.
>
The hisi_uncore_pmu_enable_event includes
1) disable counting
2) set event type
3) enable counting

Sorry. I think this lead to confusion.

I shall split then to separate functions
a) disable_counter
b) set_event_type
c) enable_counter
>> +	perf_event_update_userpage(event);
>> +}
>> +
>> +void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	if (hwc->state & PERF_HES_UPTODATE)
>> +		return;
> Why?
A mistake. Will remove it.
> [...]
>
>> +int hisi_uncore_common_fwprop_read(struct device *dev,
>> +					struct hisi_pmu *phisi_pmu)
>> +{
>> +	if (device_property_read_u32(dev, "num-events",
>> +					&phisi_pmu->num_events)) {
>> +		dev_err(dev, "Cant read num-events from DT!\n");
>> +		return -EINVAL;
>> +	}
> For consistency with the rest of the driver, and given there is no ACPI
> support, please use the of_property_* API here.
Ok.

Thanks,
Anurup
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters
@ 2016-11-14  8:11       ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-14  8:11 UTC (permalink / raw)
  To: linux-arm-kernel



On Friday 11 November 2016 12:40 AM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:42:03AM -0400, Anurup M wrote:
>> +	do {
>> +		/* Get count from individual L3C banks and sum them up */
>> +		for (i = 0; i < num_banks; i++) {
>> +			total_raw_count += hisi_read_l3c_counter(l3c_hwmod_data,
>> +									idx, i);
>> +		}
>> +		prev_raw_count = local64_read(&hwc->prev_count);
>> +
>> +		/*
>> +		 * As prev_raw_count is updated with average value of
>> +		 * L3 cache banks, we multiply it by no of banks and
>> +		 * compute the delta
>> +		 */
>> +		delta = (total_raw_count - (prev_raw_count * num_banks)) &
>> +								HISI_MAX_PERIOD;
>> +
>> +		local64_add(delta, &event->count);
>> +
>> +		/*
>> +		 * Divide by num of banks to get average count and
>> +		 * update prev_count with this value
>> +		 */
>> +		avg_raw_count = total_raw_count / num_banks;
>> +	} while (local64_cmpxchg(
>> +			 &hwc->prev_count, prev_raw_count, avg_raw_count) !=
>> +							 prev_raw_count);
> Please don't aggregate like this; expose separate PMUs instead.
>
> This is racy, and by averaging and multiplying we're making up and/or
> throwing away data.
>
> [...]
I have some concerns or doubts regarding registering each L3 cache bank 
as a separate PMU.

1) Each L3 cache PMU has total 22 statistics events. So if registered as 
a separate PMU, will it not
create multiple entries (with same event names) in event listing for 
multiple PMU's. Is there a way
I can avoid this? or this is acceptable?

Or is it acceptable to register as a single PMU and add a config 
parameter in the event listing to
identify the L3 cache bank. ex:  event name will appear as 
"hisi_l3c2/read_allocate,bank=?/".
And user can choose count from bank 0x01 as -e 
"hisi_l3c2/read_allocate,bank=0x01/"

2) The individual count from each L3 cache bank is not meaningful. Only 
aggregate count is useful.
Which is the accepted way to handle such counters?
a) Register each PMU instance as in hardware and handle the aggregation 
in user space.
b) Register as single PMU, handle aggregation in driver.

>> +	event_value = (val -
>> +			HISI_HWEVENT_L3C_READ_ALLOCATE);
>> +
>> +	/* Select the appropriate Event select register */
>> +	if (idx > 3)
>> +		reg_offset += 4;
>> +
>> +	/* Value to write to event type register */
>> +	val = event_value << (8 * idx);
>> +
> Please add helpers for these, and explain *why* the transformations are
> necessary.
Ok. shall add helpers.
>> +	/* Find the djtag Identifier of the Unit */
>> +	client = l3c_hwmod_data->client;
>> +
>> +	/*
>> +	 * Set the event in L3C_EVENT_TYPEx Register
>> +	 * for all L3C banks
>> +	 */
> As above, it seems like you should expose a separate PMU per bank
> instead. That applies for all the other instances where you iterate over
> banks.
>
> [...]
>
>> +	for (i = 0; i < l3c_hwmod_data->l3c_hwcfg.num_banks; i++) {
>> +		module_id = l3c_hwmod_data->l3c_hwcfg.module_id[i];
>> +		cfg_en = l3c_hwmod_data->l3c_hwcfg.bank_cfgen[i];
>> +		ret = hisi_djtag_writereg(module_id,
>> +					cfg_en,
>> +					reg_offset,
>> +					value,
>> +					client);
>> +		if (!ret)
>> +			ret = value;
>> +	}
> This is impossible to read. Please factor this into helpers such that
> you don't need this amount of indentation.
>
> Please do similarly elsewhere when you see this indentation pattern.
OK.
> [...]
>
>> +static int hisi_l3c_get_event_idx(struct hisi_pmu *pl3c_pmu)
>> +{
>> +	struct hisi_l3c_data *l3c_hwmod_data = pl3c_pmu->hwmod_data;
>> +	int event_idx;
>> +
>> +	event_idx =
>> +		find_first_zero_bit(
>> +			l3c_hwmod_data->hisi_l3c_event_used_mask,
>> +					 pl3c_pmu->num_counters);
>> +
>> +	if (event_idx == HISI_MAX_CFG_L3C_CNTR)
>> +		return -EAGAIN;
>> +
>> +	__set_bit(event_idx,
>> +		l3c_hwmod_data->hisi_l3c_event_used_mask);
>> +
>> +	return event_idx;
>> +}
> Please get rid of the weird hungarian notation (i.e. don't use 'p' as a
> prefix for pointers), and use temporary variables consistently, e.g.
Ok.
> static int hisi_l3c_get_event_idx(struct hisi_pmu *l3c_pmu)
> {
> 	struct hisi_l3c_data *l3c_hwmod_data = l3c_pmu->hwmod_data;
> 	unsigned long *used_mask = l3c_hwmod_data->hisi_l3c_event_used_mask;
> 	int num_counters = pl3c_pmu->num_counters
> 	int idx;
>
> 	idx = find_first_zero_bit(used_mask, num_counters);
> 	if (idx == num_counters)
> 		return -EAGAIN;
>
> 	set_bit(idx, used_mask);
>
> 	return idx;
> }
>
> [...]
>
>> +	if (of_property_read_u32(node, "counter-reg",
>> +				     &pl3c_hwcfg->counter_reg0_off)) {
>> +		dev_err(dev, "DT:Couldnot read counter-reg!\n");
>> +		return -EINVAL;
>> +	}
> Please use spaces in these messages.
>
> Otherwise, my comments on the binding apply here.
>
> [...]
>
>> +static int init_hisi_l3c_data(struct device *dev,
>> +					struct hisi_pmu *pl3c_pmu,
>> +					struct hisi_djtag_client *client)
>> +{
>> +	struct hisi_l3c_data *l3c_hwmod_data = NULL;
>> +	int ret;
>> +
>> +	l3c_hwmod_data = kzalloc(sizeof(struct hisi_l3c_data),
>> +							GFP_KERNEL);
> Use:
>
> 	l3c_hwmod_data = kzalloc(sizeof(*l3c_hwmod_data, GFP_KERNEL):
>
> [...]
Ok.
>> +static int hisi_pmu_l3c_dev_probe(struct hisi_djtag_client *client)
>> +{
>> +	struct hisi_pmu *pl3c_pmu = NULL;
>> +	struct device *dev = &client->dev;
>> +	int ret;
>> +
>> +	pl3c_pmu = hisi_pmu_alloc(dev);
>> +	if (IS_ERR(pl3c_pmu))
>> +		return PTR_ERR(pl3c_pmu);
> Why use error pointers for this?
>
> hisi_pmu_alloc() only ever returns ERR_PTR(-ENOMEM) if it failed to
> allocate.
>
> It's far simpler to have it pass on NULL there, and here do:
>
> 	pl3c_pmu = hisi_pmu_alloc(dev);
> 	if (!pl3c_pmu)
> 		return -ENOMEM;
>
> Please also s/pl3c_pmu/l3c_pmu/ here, and elsewhere throughout the
> driver. The 'p' only serves to make this harder to read.
>
> [...]
Ok.
>> +	/* Register with perf PMU */
>> +	pl3c_pmu->pmu = (struct pmu) {
>> +		.name = pl3c_pmu->name,
>> +		.task_ctx_nr = perf_invalid_context,
>> +		.event_init = hisi_uncore_pmu_event_init,
>> +		.add = hisi_uncore_pmu_add,
>> +		.del = hisi_uncore_pmu_del,
>> +		.start = hisi_uncore_pmu_start,
>> +		.stop = hisi_uncore_pmu_stop,
>> +		.read = hisi_uncore_pmu_read,
>> +	};
> Please remove the comment above this.
>
> [...]
Ok.
>> +int hisi_uncore_pmu_event_init(struct perf_event *event)
>> +{
>> +	int err;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
> This is undefined behaviour. This must be done *after* we check the
> event->pmu->type.
Ok. shall move it after the check.
>> +
>> +	if (event->attr.type != event->pmu->type)
>> +		return -ENOENT;
>> +
>> +	/* we do not support sampling as the counters are all
>> +	 * shared by all CPU cores in a CPU die(SCCL). Also we
>> +	 * donot support attach to a task(per-process mode)
>> +	 */
>> +	if (is_sampling_event(event) || event->attach_state & PERF_ATTACH_TASK)
>> +		return -EOPNOTSUPP;
>> +
>> +	/* counters do not have these bits */
>> +	if (event->attr.exclude_user	||
>> +	    event->attr.exclude_kernel	||
>> +	    event->attr.exclude_host	||
>> +	    event->attr.exclude_guest	||
>> +	    event->attr.exclude_hv	||
>> +	    event->attr.exclude_idle)
>> +		return -EINVAL;
>> +
>> +	if (event->cpu < 0)
>> +		return -EINVAL;
>> +
>> +	event->cpu = cpumask_first(&phisi_pmu->cpu);
> You should also check the event grouping.
>
> Take a look at what we do in arch/arm/mm/cache-l2x0-pmu.c.
>
> [...]
>
Ok. I got it. Thanks for the reference.
As you had suggested earlier I am thinking to put all these checks to a 
common lib function
and every uncore driver can call it.
Where can it be placed in source tree? Please suggest. shall add it.

>> +/*
>> + * Enable counter and set the counter to count
>> + * the event that we're interested in.
>> + */
>> +void hisi_uncore_pmu_enable_event(struct perf_event *event)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	/* Disable the hardware event counting */
>> +	if (phisi_pmu->ops->disable_counter)
>> +		phisi_pmu->ops->disable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
> Why isn't the counter already disabled?
>
In Hisilicon SoC PMU for L3C, MN etc, the hip05/06/07 chips don't have 
support for
stop/start individual counters.
The steps to start counting are
1) Stop counting(global control) in L3 cache AUCTRL register.
2) set event type in the event type register (Event code written to
3) Start counting (global control) in L3 cache AUCTRL register.

>> +	/*
>> +	 * Set event (if destined for Hisilicon SoC counters).
>> +	 */
>> +	if (phisi_pmu->ops->set_evtype)
>> +		phisi_pmu->ops->set_evtype(phisi_pmu, GET_CNTR_IDX(hwc),
>> +							hwc->config_base);
> Why isn't this done in the pmu::event_add callback?
hisi_uncore_pmu_enable_event is called by pmu::event_add.
The Call flow is  pmu::event_add (hisi_uncore_pmu_add) --> 
hisi_uncore_pmu_start --> hisi_uncore_pmu_enable_event
>> +
>> +	/* Enable the hardware event counting */
>> +	if (phisi_pmu->ops->enable_counter)
>> +		phisi_pmu->ops->enable_counter(phisi_pmu, GET_CNTR_IDX(hwc));
> This should be the only necessary part of this function.
>
>> +}
>> +
>> +void hisi_pmu_set_event_period(struct perf_event *event)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	/*
>> +	 * The Hisilicon PMU counters have a period of 2^32. To account for the
>> +	 * possiblity of extreme interrupt latency we program for a period of
>> +	 * half that. Hopefully we can handle the interrupt before another 2^31
>> +	 * events occur and the counter overtakes its previous value.
>> +	 */
>> +	u64 val = 1ULL << 31;
>> +
>> +	local64_set(&hwc->prev_count, val);
>> +
>> +	/* Write to the hardware event counter */
>> +	phisi_pmu->ops->write_counter(phisi_pmu, hwc, val);
>> +}
>> +
>> +void hisi_uncore_pmu_start(struct perf_event *event, int flags)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +	struct hisi_pmu_hw_events *hw_events;
>> +
>> +	hw_events = &phisi_pmu->hw_events;
>> +
>> +	if (WARN_ON_ONCE(!(hwc->state & PERF_HES_STOPPED)))
>> +		return;
>> +
>> +	WARN_ON_ONCE(!(hwc->state & PERF_HES_UPTODATE));
>> +	hwc->state = 0;
>> +
>> +	if (phisi_pmu->ops->set_event_period)
>> +		phisi_pmu->ops->set_event_period(event);
> When will this differ from hisi_pmu_set_event_period() above?
On some chips like hip05 and 06, the counter overflow handling with IRQ 
cannot
be supported as the Hisilicon Mbigen V1 driver cannot be upstream. So I 
am thinking
to have the counters start with value 0 and handle overflow with a poll 
method from
a hrtimer. So this method (hisi_pmu::set_event_period) could be changed 
in hisi_uncore(l3c/mn/ddrc).c.

Also for the DDRC PMU, the method (hisi_pmu::set_event_period) override 
to handle continous counting.
In DDRC PMU, the initial counter value start from 0.

Please share your comments.

>> +	if (flags & PERF_EF_RELOAD) {
>> +		u64 prev_raw_count =  local64_read(&hwc->prev_count);
>> +
>> +		phisi_pmu->ops->write_counter(phisi_pmu, hwc,
>> +						(u32)prev_raw_count);
>> +	}
> If we always go through hisi_pmu_set_event_period(), this looks
> redundant.
>
>> +
>> +	hisi_uncore_pmu_enable_event(event);
> There's no matching disable_event() call in this function, so this looks
> suspicious.
>
The hisi_uncore_pmu_enable_event includes
1) disable counting
2) set event type
3) enable counting

Sorry. I think this lead to confusion.

I shall split then to separate functions
a) disable_counter
b) set_event_type
c) enable_counter
>> +	perf_event_update_userpage(event);
>> +}
>> +
>> +void hisi_uncore_pmu_stop(struct perf_event *event, int flags)
>> +{
>> +	struct hw_perf_event *hwc = &event->hw;
>> +	struct hisi_pmu *phisi_pmu = to_hisi_pmu(event->pmu);
>> +
>> +	if (hwc->state & PERF_HES_UPTODATE)
>> +		return;
> Why?
A mistake. Will remove it.
> [...]
>
>> +int hisi_uncore_common_fwprop_read(struct device *dev,
>> +					struct hisi_pmu *phisi_pmu)
>> +{
>> +	if (device_property_read_u32(dev, "num-events",
>> +					&phisi_pmu->num_events)) {
>> +		dev_err(dev, "Cant read num-events from DT!\n");
>> +		return -EINVAL;
>> +	}
> For consistency with the rest of the driver, and given there is no ACPI
> support, please use the of_property_* API here.
Ok.

Thanks,
Anurup
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-14  0:06       ` Anurup M
@ 2016-11-15  9:51         ` Mark Rutland
  -1 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-15  9:51 UTC (permalink / raw)
  To: Anurup M
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm

On Mon, Nov 14, 2016 at 05:36:44AM +0530, Anurup M wrote:
> On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
> >On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:

> >>+	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
> >>+		   or IO die in the chip.
> >What's this needed for?
> This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2
> - for scl-id = 2).
> This is to identify the pmu correspond to which CPU die in the socket.
> >>+	- num-events : No of events supported by this PMU device.
> >>+
> >>+	- num-counters : No of hardware counters available for counting.
> >This isn't probeable or well-known?
> My idea is to have the common properties of SoC PMU added here.
> The num-events, num-counters etc. So that handling can be made
> common in the driver.
> Is it not recommended? Please share your comments.

This feels like something that should be well-known for the programming
model of the device. If the number of events and/or counters shange, I'd
expect other things to also change such that the device is no longer
compatible with previous versions.

[...]

> The below two properties (module-id, cfgen-map) differs between
> chips hip05/06 and hip07.

The module-id property sounds like a HW description, but it's not
entirely clear to me what cfgen-map is; more comments on that below.

> Please suggest.
> >>+	- module-id : Module ID to input for djtag. This property is an array of
> >>+		      module_id for each L3 cache banks.
> >>+
> >>+	- num-banks : Number of banks or instances of the device.
> >What's a bank? Surely they have separate instances of the PMU?
> Yes each bank is a separate instance of PMU.
> If it is recommended to have each L3 cache bank registered as
> separate PMU with perf, then this property will be removed.

Generally, I think that separate instances are preferable. 

> >What order are these in?
> The bank number will start from "1" till "4" for L3 cache as there
> are four banks in hip05/06/07 chips.
> >>+	- cfgen-map : Config enable array to select the bank.
> >Huh?

As above, it's not clear to me what this property represents. Could you
please clarify?

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-15  9:51         ` Mark Rutland
  0 siblings, 0 replies; 52+ messages in thread
From: Mark Rutland @ 2016-11-15  9:51 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Nov 14, 2016 at 05:36:44AM +0530, Anurup M wrote:
> On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
> >On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:

> >>+	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
> >>+		   or IO die in the chip.
> >What's this needed for?
> This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2
> - for scl-id = 2).
> This is to identify the pmu correspond to which CPU die in the socket.
> >>+	- num-events : No of events supported by this PMU device.
> >>+
> >>+	- num-counters : No of hardware counters available for counting.
> >This isn't probeable or well-known?
> My idea is to have the common properties of SoC PMU added here.
> The num-events, num-counters etc. So that handling can be made
> common in the driver.
> Is it not recommended? Please share your comments.

This feels like something that should be well-known for the programming
model of the device. If the number of events and/or counters shange, I'd
expect other things to also change such that the device is no longer
compatible with previous versions.

[...]

> The below two properties (module-id, cfgen-map) differs between
> chips hip05/06 and hip07.

The module-id property sounds like a HW description, but it's not
entirely clear to me what cfgen-map is; more comments on that below.

> Please suggest.
> >>+	- module-id : Module ID to input for djtag. This property is an array of
> >>+		      module_id for each L3 cache banks.
> >>+
> >>+	- num-banks : Number of banks or instances of the device.
> >What's a bank? Surely they have separate instances of the PMU?
> Yes each bank is a separate instance of PMU.
> If it is recommended to have each L3 cache bank registered as
> separate PMU with perf, then this property will be removed.

Generally, I think that separate instances are preferable. 

> >What order are these in?
> The bank number will start from "1" till "4" for L3 cache as there
> are four banks in hip05/06/07 chips.
> >>+	- cfgen-map : Config enable array to select the bank.
> >Huh?

As above, it's not clear to me what this property represents. Could you
please clarify?

Thanks,
Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
  2016-11-10 17:55       ` Mark Rutland
@ 2016-11-15 10:15         ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-15 10:15 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	linux-doc-u79uwXL29TY76Z2rM5mHXA, will.deacon-5wv7dgnIgG8,
	corbet-T1hC0tSOHrs, catalin.marinas-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, arnd-r2nGTMty4D4,
	f.fainelli-Re5JQEeQqe8AvxtiuMwx3w,
	rmk+kernel-lFZ/pmaqli7XmaaqVzeoHQ, krzk-DgEjT+Ai2ygdnm+yROfE0A,
	anurup.m-hv44wF8Li93QT0dZR+AlfA,
	zhangshaokun-C8/M+/jPZTeaMJb+Lgu22Q,
	tanxiaojun-hv44wF8Li93QT0dZR+AlfA, xuwei5-C8/M+/jPZTeaMJb+Lgu22Q,
	sanil.kumar-C8/M+/jPZTeaMJb+Lgu22Q,
	john.garry-hv44wF8Li93QT0dZR+AlfA,
	gabriele.paoloni-hv44wF8Li93QT0dZR+AlfA,
	shiju.jose-hv44wF8Li93QT0dZR+AlfA,
	wangkefeng.wang-hv44wF8Li93QT0dZR+AlfA,
	guohanjun-hv44wF8Li93QT0dZR+AlfA,
	shyju.pv-hv44wF8Li93QT0dZR+AlfA, linuxarm-hv44wF8Li93QT0dZR+AlfA



On Thursday 10 November 2016 11:25 PM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>>
>> 	The Hisilicon Djtag is an independent component which connects
>> 	with some other components in the SoC by Debug Bus. This driver
>> 	can be configured to access the registers of connecting components
>> 	(like L3 cache) during real time debugging.
>>
> Just to check, is this likely to be used in multi-socket hardware, and
> if so, are instances always-on?
Yes, It could be used in multi-socket hardware also.
The sockets are always enabled  after bootup. Sorry I didn't get the
>> Signed-off-by: Tan Xiaojun <tanxiaojun-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: John Garry <john.garry-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> Signed-off-by: Anurup M <anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
>> ---
>>   drivers/soc/Kconfig                 |   1 +
>>   drivers/soc/Makefile                |   1 +
>>   drivers/soc/hisilicon/Kconfig       |  12 +
>>   drivers/soc/hisilicon/Makefile      |   1 +
>>   drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
>>   include/linux/soc/hisilicon/djtag.h |  38 +++
>>   6 files changed, 692 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/djtag.c
>>   create mode 100644 include/linux/soc/hisilicon/djtag.h
> Other than the PMU driver(s), what is going to use this?
>
> If you don't have something in particular, please also place this under
> drivers/perf/hisilicon, along with the PMU driver(s).
>
> We can always move it later if necessary.
>
> [...]
Arnd also suggested the same. Currently as there are no other users I 
shall move it to
drivers/perf/hisilicon.
>> +#define SC_DJTAG_TIMEOUT		100000	/* 100ms */
> This would be better as:
>
> #define SC_DJTAG_TIMEOUT_US	(100 * USEC_PER_MSEC)
>
> (you'll need to include <linux/time64.h>)
>
> [...]
OK.
>> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
>> +{
>> +	void __iomem *reg_addr = regs_base + off;
>> +
>> +	*value = readl_relaxed(reg_addr);
>> +}
>> +
>> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
>> +{
>> +	void __iomem *reg_addr = regs_base + off;
>> +
>> +	writel(val, reg_addr);
>> +}
> I think these make the driver harder to read, especially given the read
> function is void and takes an output pointer.
>
> In either case you can call readl/writel directly with base + off for
> the __iomem ptr. Please do that.
Ok.
>> +
>> +/*
>> + * djtag_readwrite_v1/v2: djtag read/write interface
>> + * @reg_base:	djtag register base address
>> + * @offset:	register's offset
>> + * @mod_sel:	module selection
>> + * @mod_mask:	mask to select specific modules for write
>> + * @is_w:	write -> true, read -> false
>> + * @wval:	value to register for write
>> + * @chain_id:	which sub module for read
>> + * @rval:	value in register for read
>> + *
>> + * Return non-zero if error, else return 0.
>> + */
>> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
>> +		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
>> +{
>> +	u32 rd;
>> +	int timeout = SC_DJTAG_TIMEOUT;
>> +
>> +	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
>> +		pr_warn("djtag: do nothing.\n");
>> +		return 0;
>> +	}
>> +
>> +	/* djtag mster enable & accelerate R,W */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
>> +			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
>> +
>> +	/* select module */
>> +	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
>> +	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
>> +				mod_mask & CHAIN_UNIT_CFG_EN);
>> +
>> +	if (is_w) {
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
>> +	} else
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
>> +
>> +	/* address offset */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
>> +
>> +	/* start to write to djtag register */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
>> +
>> +	/* ensure the djtag operation is done */
>> +	do {
>> +		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
>> +		if (!(rd & DJTAG_MSTR_EN))
>> +			break;
>> +
>> +		udelay(1);
>> +	} while (timeout--);
>> +
>> +	if (timeout < 0) {
>> +		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
>> +		return -EBUSY;
>> +	}
>> +
>> +	if (!is_w)
>> +		djtag_read32_relaxed(regs_base,
>> +			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
>> +
>> +	return 0;
>> +}
> Please factor out the common bits into helpers and have separate
> read/write functions. It's incredibly difficult to follow the code with
> read/write hidden behind a boolean parameter.
Ok. Shall change it.
>> +static const struct of_device_id djtag_of_match[] = {
>> +	/* for hip05(D02) cpu die */
>> +	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip05(D02) io die */
>> +	{ .compatible = "hisilicon,hip05-io-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip06(D03) cpu die */
>> +	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip06(D03) io die */
>> +	{ .compatible = "hisilicon,hip06-io-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	/* for hip07(D05) cpu die */
>> +	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	/* for hip07(D05) io die */
>> +	{ .compatible = "hisilicon,hip07-io-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	{},
>> +};
> Binding documentation for all of these should be added *before* this
> patch, per Documentation/devicetree/bindings/submitting-patches.txt.
> Please move any relevant binding documentation earlier in the series.
The binding documentation added in "[RESEND PATCH v1 02/11] dt-bindings: 
hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings]".

>> +MODULE_DEVICE_TABLE(of, djtag_of_match);
>> +
>> +static ssize_t
>> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
>> +{
>> +	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
>> +
>> +	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
>> +}
>> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
>> +
>> +static struct attribute *hisi_djtag_dev_attrs[] = {
>> +	NULL,
>> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
>> +	&dev_attr_modalias.attr,
>> +	NULL
>> +};
>> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
> Why do we need to expose this under sysfs?
This is to know the djtag clients registered with the bus.
>> +struct bus_type hisi_djtag_bus = {
>> +	.name		= "hisi-djtag",
>> +	.match		= hisi_djtag_device_match,
>> +	.probe		= hisi_djtag_device_probe,
>> +	.remove		= hisi_djtag_device_remove,
>> +};
> I take it the core bus code handles reference-counting users of the bus?
The bus_register registers with kobject. Did It answer the question?
>> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
>> +						struct device_node *node)
>> +{
>> +	struct hisi_djtag_client *client;
>> +	int status;
>> +
>> +	client = kzalloc(sizeof(*client), GFP_KERNEL);
>> +	if (!client)
>> +		return NULL;
>> +
>> +	client->host = host;
>> +
>> +	client->dev.parent = &client->host->dev;
>> +	client->dev.bus = &hisi_djtag_bus;
>> +	client->dev.type = &hisi_djtag_client_type;
>> +	client->dev.of_node = node;
> I suspect that we should do:
>
> 	client->dev.of_node = of_node_get(node);
>
> ... so that it's guaranteed we retain a reference.
Ok.
>> +	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
>> +					DJTAG_PREFIX, node->name);
>> +	dev_set_name(&client->dev, "%s", client->name);
>> +
>> +	status = device_register(&client->dev);
>> +	if (status < 0) {
>> +		pr_err("error adding new device, status=%d\n", status);
>> +		kfree(client);
>> +		return NULL;
>> +	}
>> +
>> +	return client;
>> +}
>> +
>> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
>> +						struct hisi_djtag_host *host,
>> +						struct device_node *node)
>> +{
>> +	struct hisi_djtag_client *client;
>> +
>> +	client = hisi_djtag_new_device(host, node);
>> +	if (client == NULL) {
>> +		dev_err(&host->dev, "error registering device %s\n",
>> +			node->full_name);
>> +		of_node_put(node);
> I don't think this should be here, given what djtag_register_devices()
> does.
Will move this to djtag_register_devices.
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	return client;
>> +}
>> +
>> +static void djtag_register_devices(struct hisi_djtag_host *host)
>> +{
>> +	struct device_node *node;
>> +	struct hisi_djtag_client *client;
>> +
>> +	if (!host->of_node)
>> +		return;
>> +
>> +	for_each_available_child_of_node(host->of_node, node) {
>> +		if (of_node_test_and_set_flag(node, OF_POPULATED))
>> +			continue;
>> +		client = hisi_djtag_of_register_device(host, node);
>> +		list_add(&client->next, &host->client_list);
>> +	}
>> +}
> Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
> list_add is not safe.
Shall add the check and handle accordingly.
>> +static int djtag_host_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct hisi_djtag_host *host;
>> +	const struct of_device_id *of_id;
>> +	struct resource *res;
>> +	int rc;
>> +
>> +	of_id = of_match_device(djtag_of_match, dev);
>> +	if (!of_id)
>> +		return -EINVAL;
>> +
>> +	host = kzalloc(sizeof(*host), GFP_KERNEL);
>> +	if (!host)
>> +		return -ENOMEM;
>> +
>> +	host->of_node = dev->of_node;
> 	host->of_node = of_node_get(dev->of_node);
>
>> +	host->djtag_readwrite = of_id->data;
>> +	spin_lock_init(&host->lock);
>> +
>> +	INIT_LIST_HEAD(&host->client_list);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No reg resorces!\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!resource_size(res)) {
>> +		dev_err(&pdev->dev, "Zero reg entry!\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
>> +
>> +	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(host->sysctl_reg_map)) {
>> +		dev_warn(dev, "Unable to map sysctl registers.\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
> Please have a common error path rather than duplicating this free.
>
> e.g. at the end of the function have:
>
> 	err_free:
> 		kfree(host);
> 		return err;
>
> ... and in cases like this, have:
>
> 	if (whatever()) {
> 		dev_warn(dev, "this failed xxx\n");
> 		err = -EINVAL;
> 		goto err_free;
> 	}
Ok. thanks. will change it.

Thanks,
Anurup
> Thanks,
> Mark.

--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver
@ 2016-11-15 10:15         ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-15 10:15 UTC (permalink / raw)
  To: linux-arm-kernel



On Thursday 10 November 2016 11:25 PM, Mark Rutland wrote:
> On Thu, Nov 03, 2016 at 01:41:59AM -0400, Anurup M wrote:
>> From: Tan Xiaojun <tanxiaojun@huawei.com>
>>
>> 	The Hisilicon Djtag is an independent component which connects
>> 	with some other components in the SoC by Debug Bus. This driver
>> 	can be configured to access the registers of connecting components
>> 	(like L3 cache) during real time debugging.
>>
> Just to check, is this likely to be used in multi-socket hardware, and
> if so, are instances always-on?
Yes, It could be used in multi-socket hardware also.
The sockets are always enabled  after bootup. Sorry I didn't get the
>> Signed-off-by: Tan Xiaojun <tanxiaojun@huawei.com>
>> Signed-off-by: John Garry <john.garry@huawei.com>
>> Signed-off-by: Anurup M <anurup.m@huawei.com>
>> ---
>>   drivers/soc/Kconfig                 |   1 +
>>   drivers/soc/Makefile                |   1 +
>>   drivers/soc/hisilicon/Kconfig       |  12 +
>>   drivers/soc/hisilicon/Makefile      |   1 +
>>   drivers/soc/hisilicon/djtag.c       | 639 ++++++++++++++++++++++++++++++++++++
>>   include/linux/soc/hisilicon/djtag.h |  38 +++
>>   6 files changed, 692 insertions(+)
>>   create mode 100644 drivers/soc/hisilicon/Kconfig
>>   create mode 100644 drivers/soc/hisilicon/Makefile
>>   create mode 100644 drivers/soc/hisilicon/djtag.c
>>   create mode 100644 include/linux/soc/hisilicon/djtag.h
> Other than the PMU driver(s), what is going to use this?
>
> If you don't have something in particular, please also place this under
> drivers/perf/hisilicon, along with the PMU driver(s).
>
> We can always move it later if necessary.
>
> [...]
Arnd also suggested the same. Currently as there are no other users I 
shall move it to
drivers/perf/hisilicon.
>> +#define SC_DJTAG_TIMEOUT		100000	/* 100ms */
> This would be better as:
>
> #define SC_DJTAG_TIMEOUT_US	(100 * USEC_PER_MSEC)
>
> (you'll need to include <linux/time64.h>)
>
> [...]
OK.
>> +static void djtag_read32_relaxed(void __iomem *regs_base, u32 off, u32 *value)
>> +{
>> +	void __iomem *reg_addr = regs_base + off;
>> +
>> +	*value = readl_relaxed(reg_addr);
>> +}
>> +
>> +static void djtag_write32(void __iomem *regs_base, u32 off, u32 val)
>> +{
>> +	void __iomem *reg_addr = regs_base + off;
>> +
>> +	writel(val, reg_addr);
>> +}
> I think these make the driver harder to read, especially given the read
> function is void and takes an output pointer.
>
> In either case you can call readl/writel directly with base + off for
> the __iomem ptr. Please do that.
Ok.
>> +
>> +/*
>> + * djtag_readwrite_v1/v2: djtag read/write interface
>> + * @reg_base:	djtag register base address
>> + * @offset:	register's offset
>> + * @mod_sel:	module selection
>> + * @mod_mask:	mask to select specific modules for write
>> + * @is_w:	write -> true, read -> false
>> + * @wval:	value to register for write
>> + * @chain_id:	which sub module for read
>> + * @rval:	value in register for read
>> + *
>> + * Return non-zero if error, else return 0.
>> + */
>> +static int djtag_readwrite_v1(void __iomem *regs_base, u32 offset, u32 mod_sel,
>> +		u32 mod_mask, bool is_w, u32 wval, int chain_id, u32 *rval)
>> +{
>> +	u32 rd;
>> +	int timeout = SC_DJTAG_TIMEOUT;
>> +
>> +	if (!(mod_mask & CHAIN_UNIT_CFG_EN)) {
>> +		pr_warn("djtag: do nothing.\n");
>> +		return 0;
>> +	}
>> +
>> +	/* djtag mster enable & accelerate R,W */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_EN,
>> +			DJTAG_NOR_CFG | DJTAG_MSTR_EN);
>> +
>> +	/* select module */
>> +	djtag_write32(regs_base, SC_DJTAG_DEBUG_MODULE_SEL, mod_sel);
>> +	djtag_write32(regs_base, SC_DJTAG_CHAIN_UNIT_CFG_EN,
>> +				mod_mask & CHAIN_UNIT_CFG_EN);
>> +
>> +	if (is_w) {
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_W);
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_DATA, wval);
>> +	} else
>> +		djtag_write32(regs_base, SC_DJTAG_MSTR_WR, DJTAG_MSTR_R);
>> +
>> +	/* address offset */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_ADDR, offset);
>> +
>> +	/* start to write to djtag register */
>> +	djtag_write32(regs_base, SC_DJTAG_MSTR_START_EN, DJTAG_MSTR_START_EN);
>> +
>> +	/* ensure the djtag operation is done */
>> +	do {
>> +		djtag_read32_relaxed(regs_base, SC_DJTAG_MSTR_START_EN, &rd);
>> +		if (!(rd & DJTAG_MSTR_EN))
>> +			break;
>> +
>> +		udelay(1);
>> +	} while (timeout--);
>> +
>> +	if (timeout < 0) {
>> +		pr_err("djtag: %s timeout!\n", is_w ? "write" : "read");
>> +		return -EBUSY;
>> +	}
>> +
>> +	if (!is_w)
>> +		djtag_read32_relaxed(regs_base,
>> +			SC_DJTAG_RD_DATA_BASE + chain_id * 0x4, rval);
>> +
>> +	return 0;
>> +}
> Please factor out the common bits into helpers and have separate
> read/write functions. It's incredibly difficult to follow the code with
> read/write hidden behind a boolean parameter.
Ok. Shall change it.
>> +static const struct of_device_id djtag_of_match[] = {
>> +	/* for hip05(D02) cpu die */
>> +	{ .compatible = "hisilicon,hip05-cpu-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip05(D02) io die */
>> +	{ .compatible = "hisilicon,hip05-io-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip06(D03) cpu die */
>> +	{ .compatible = "hisilicon,hip06-cpu-djtag-v1",
>> +		.data = (void *)djtag_readwrite_v1 },
>> +	/* for hip06(D03) io die */
>> +	{ .compatible = "hisilicon,hip06-io-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	/* for hip07(D05) cpu die */
>> +	{ .compatible = "hisilicon,hip07-cpu-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	/* for hip07(D05) io die */
>> +	{ .compatible = "hisilicon,hip07-io-djtag-v2",
>> +		.data = (void *)djtag_readwrite_v2 },
>> +	{},
>> +};
> Binding documentation for all of these should be added *before* this
> patch, per Documentation/devicetree/bindings/submitting-patches.txt.
> Please move any relevant binding documentation earlier in the series.
The binding documentation added in "[RESEND PATCH v1 02/11] dt-bindings: 
hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings]".

>> +MODULE_DEVICE_TABLE(of, djtag_of_match);
>> +
>> +static ssize_t
>> +show_modalias(struct device *dev, struct device_attribute *attr, char *buf)
>> +{
>> +	struct hisi_djtag_client *client = to_hisi_djtag_client(dev);
>> +
>> +	return sprintf(buf, "%s%s\n", MODULE_PREFIX, client->name);
>> +}
>> +static DEVICE_ATTR(modalias, 0444, show_modalias, NULL);
>> +
>> +static struct attribute *hisi_djtag_dev_attrs[] = {
>> +	NULL,
>> +	/* modalias helps coldplug:  modprobe $(cat .../modalias) */
>> +	&dev_attr_modalias.attr,
>> +	NULL
>> +};
>> +ATTRIBUTE_GROUPS(hisi_djtag_dev);
> Why do we need to expose this under sysfs?
This is to know the djtag clients registered with the bus.
>> +struct bus_type hisi_djtag_bus = {
>> +	.name		= "hisi-djtag",
>> +	.match		= hisi_djtag_device_match,
>> +	.probe		= hisi_djtag_device_probe,
>> +	.remove		= hisi_djtag_device_remove,
>> +};
> I take it the core bus code handles reference-counting users of the bus?
The bus_register registers with kobject. Did It answer the question?
>> +struct hisi_djtag_client *hisi_djtag_new_device(struct hisi_djtag_host *host,
>> +						struct device_node *node)
>> +{
>> +	struct hisi_djtag_client *client;
>> +	int status;
>> +
>> +	client = kzalloc(sizeof(*client), GFP_KERNEL);
>> +	if (!client)
>> +		return NULL;
>> +
>> +	client->host = host;
>> +
>> +	client->dev.parent = &client->host->dev;
>> +	client->dev.bus = &hisi_djtag_bus;
>> +	client->dev.type = &hisi_djtag_client_type;
>> +	client->dev.of_node = node;
> I suspect that we should do:
>
> 	client->dev.of_node = of_node_get(node);
>
> ... so that it's guaranteed we retain a reference.
Ok.
>> +	snprintf(client->name, DJTAG_CLIENT_NAME_LEN, "%s%s",
>> +					DJTAG_PREFIX, node->name);
>> +	dev_set_name(&client->dev, "%s", client->name);
>> +
>> +	status = device_register(&client->dev);
>> +	if (status < 0) {
>> +		pr_err("error adding new device, status=%d\n", status);
>> +		kfree(client);
>> +		return NULL;
>> +	}
>> +
>> +	return client;
>> +}
>> +
>> +static struct hisi_djtag_client *hisi_djtag_of_register_device(
>> +						struct hisi_djtag_host *host,
>> +						struct device_node *node)
>> +{
>> +	struct hisi_djtag_client *client;
>> +
>> +	client = hisi_djtag_new_device(host, node);
>> +	if (client == NULL) {
>> +		dev_err(&host->dev, "error registering device %s\n",
>> +			node->full_name);
>> +		of_node_put(node);
> I don't think this should be here, given what djtag_register_devices()
> does.
Will move this to djtag_register_devices.
>> +		return ERR_PTR(-EINVAL);
>> +	}
>> +
>> +	return client;
>> +}
>> +
>> +static void djtag_register_devices(struct hisi_djtag_host *host)
>> +{
>> +	struct device_node *node;
>> +	struct hisi_djtag_client *client;
>> +
>> +	if (!host->of_node)
>> +		return;
>> +
>> +	for_each_available_child_of_node(host->of_node, node) {
>> +		if (of_node_test_and_set_flag(node, OF_POPULATED))
>> +			continue;
>> +		client = hisi_djtag_of_register_device(host, node);
>> +		list_add(&client->next, &host->client_list);
>> +	}
>> +}
> Given hisi_djtag_of_register_device() can return ERR_PTR(-EINVAL), the
> list_add is not safe.
Shall add the check and handle accordingly.
>> +static int djtag_host_probe(struct platform_device *pdev)
>> +{
>> +	struct device *dev = &pdev->dev;
>> +	struct hisi_djtag_host *host;
>> +	const struct of_device_id *of_id;
>> +	struct resource *res;
>> +	int rc;
>> +
>> +	of_id = of_match_device(djtag_of_match, dev);
>> +	if (!of_id)
>> +		return -EINVAL;
>> +
>> +	host = kzalloc(sizeof(*host), GFP_KERNEL);
>> +	if (!host)
>> +		return -ENOMEM;
>> +
>> +	host->of_node = dev->of_node;
> 	host->of_node = of_node_get(dev->of_node);
>
>> +	host->djtag_readwrite = of_id->data;
>> +	spin_lock_init(&host->lock);
>> +
>> +	INIT_LIST_HEAD(&host->client_list);
>> +
>> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +	if (!res) {
>> +		dev_err(&pdev->dev, "No reg resorces!\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
>> +
>> +	if (!resource_size(res)) {
>> +		dev_err(&pdev->dev, "Zero reg entry!\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
>> +
>> +	host->sysctl_reg_map = devm_ioremap_resource(dev, res);
>> +	if (IS_ERR(host->sysctl_reg_map)) {
>> +		dev_warn(dev, "Unable to map sysctl registers.\n");
>> +		kfree(host);
>> +		return -EINVAL;
>> +	}
> Please have a common error path rather than duplicating this free.
>
> e.g. at the end of the function have:
>
> 	err_free:
> 		kfree(host);
> 		return err;
>
> ... and in cases like this, have:
>
> 	if (whatever()) {
> 		dev_warn(dev, "this failed xxx\n");
> 		err = -EINVAL;
> 		goto err_free;
> 	}
Ok. thanks. will change it.

Thanks,
Anurup
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

* Re: [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
  2016-11-15  9:51         ` Mark Rutland
@ 2016-11-16  5:54           ` Anurup M
  -1 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-16  5:54 UTC (permalink / raw)
  To: Mark Rutland
  Cc: devicetree, linux-arm-kernel, linux-doc, will.deacon, corbet,
	catalin.marinas, robh+dt, arnd, f.fainelli, rmk+kernel, krzk,
	anurup.m, zhangshaokun, tanxiaojun, xuwei5, sanil.kumar,
	john.garry, gabriele.paoloni, shiju.jose, wangkefeng.wang,
	guohanjun, shyju.pv, linuxarm



On Tuesday 15 November 2016 03:21 PM, Mark Rutland wrote:
> On Mon, Nov 14, 2016 at 05:36:44AM +0530, Anurup M wrote:
>> On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
>>> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>>>> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
>>>> +		   or IO die in the chip.
>>> What's this needed for?
>> This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2
>> - for scl-id = 2).
>> This is to identify the pmu correspond to which CPU die in the socket.
>>>> +	- num-events : No of events supported by this PMU device.
>>>> +
>>>> +	- num-counters : No of hardware counters available for counting.
>>> This isn't probeable or well-known?
>> My idea is to have the common properties of SoC PMU added here.
>> The num-events, num-counters etc. So that handling can be made
>> common in the driver.
>> Is it not recommended? Please share your comments.
> This feels like something that should be well-known for the programming
> model of the device. If the number of events and/or counters shange, I'd
> expect other things to also change such that the device is no longer
> compatible with previous versions.
>
> [...]
>
Agreed, it is possible that the versions can be still incompatible. 
Shall move it to
driver in v2.

>> The below two properties (module-id, cfgen-map) differs between
>> chips hip05/06 and hip07.
> The module-id property sounds like a HW description, but it's not
> entirely clear to me what cfgen-map is; more comments on that below.
>
>> Please suggest.
>>>> +	- module-id : Module ID to input for djtag. This property is an array of
>>>> +		      module_id for each L3 cache banks.
>>>> +
>>>> +	- num-banks : Number of banks or instances of the device.
>>> What's a bank? Surely they have separate instances of the PMU?
>> Yes each bank is a separate instance of PMU.
>> If it is recommended to have each L3 cache bank registered as
>> separate PMU with perf, then this property will be removed.
> Generally, I think that separate instances are preferable.
>
>>> What order are these in?
>> The bank number will start from "1" till "4" for L3 cache as there
>> are four banks in hip05/06/07 chips.
>>>> +	- cfgen-map : Config enable array to select the bank.
>>> Huh?
> As above, it's not clear to me what this property represents. Could you
> please clarify?
This property is used to select the bank. The naming lead to confusion.
I shall change it to bank_select.
If it is recommended to register each L3 cache bank as separate PMU then
this property will be moved to driver.

Thanks
Anurup
>
> Thanks,
> Mark.


^ permalink raw reply	[flat|nested] 52+ messages in thread

* [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU
@ 2016-11-16  5:54           ` Anurup M
  0 siblings, 0 replies; 52+ messages in thread
From: Anurup M @ 2016-11-16  5:54 UTC (permalink / raw)
  To: linux-arm-kernel



On Tuesday 15 November 2016 03:21 PM, Mark Rutland wrote:
> On Mon, Nov 14, 2016 at 05:36:44AM +0530, Anurup M wrote:
>> On Friday 11 November 2016 12:00 AM, Mark Rutland wrote:
>>> On Thu, Nov 03, 2016 at 01:42:01AM -0400, Anurup M wrote:
>>>> +	- scl-id : The Super Cluster ID. This can be the ID of the CPU die
>>>> +		   or IO die in the chip.
>>> What's this needed for?
>> This is used as suffix to the PMU name. hisi_l3c<scl-id>. (hisi_l3c2
>> - for scl-id = 2).
>> This is to identify the pmu correspond to which CPU die in the socket.
>>>> +	- num-events : No of events supported by this PMU device.
>>>> +
>>>> +	- num-counters : No of hardware counters available for counting.
>>> This isn't probeable or well-known?
>> My idea is to have the common properties of SoC PMU added here.
>> The num-events, num-counters etc. So that handling can be made
>> common in the driver.
>> Is it not recommended? Please share your comments.
> This feels like something that should be well-known for the programming
> model of the device. If the number of events and/or counters shange, I'd
> expect other things to also change such that the device is no longer
> compatible with previous versions.
>
> [...]
>
Agreed, it is possible that the versions can be still incompatible. 
Shall move it to
driver in v2.

>> The below two properties (module-id, cfgen-map) differs between
>> chips hip05/06 and hip07.
> The module-id property sounds like a HW description, but it's not
> entirely clear to me what cfgen-map is; more comments on that below.
>
>> Please suggest.
>>>> +	- module-id : Module ID to input for djtag. This property is an array of
>>>> +		      module_id for each L3 cache banks.
>>>> +
>>>> +	- num-banks : Number of banks or instances of the device.
>>> What's a bank? Surely they have separate instances of the PMU?
>> Yes each bank is a separate instance of PMU.
>> If it is recommended to have each L3 cache bank registered as
>> separate PMU with perf, then this property will be removed.
> Generally, I think that separate instances are preferable.
>
>>> What order are these in?
>> The bank number will start from "1" till "4" for L3 cache as there
>> are four banks in hip05/06/07 chips.
>>>> +	- cfgen-map : Config enable array to select the bank.
>>> Huh?
> As above, it's not clear to me what this property represents. Could you
> please clarify?
This property is used to select the bank. The naming lead to confusion.
I shall change it to bank_select.
If it is recommended to register each L3 cache bank as separate PMU then
this property will be moved to driver.

Thanks
Anurup
>
> Thanks,
> Mark.

^ permalink raw reply	[flat|nested] 52+ messages in thread

end of thread, other threads:[~2016-11-16  5:54 UTC | newest]

Thread overview: 52+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-11-03  5:41 [RESEND PATCH v1 00/11] perf: arm64: Support for Hisilicon SoC Hardware event counters Anurup M
2016-11-03  5:41 ` Anurup M
2016-11-03  5:41 ` [RESEND PATCH v1 03/11] drivers: soc: hisi: Add support for Hisilicon Djtag driver Anurup M
2016-11-03  5:41   ` Anurup M
     [not found]   ` <1478151727-20250-4-git-send-email-anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2016-11-10 17:55     ` Mark Rutland
2016-11-10 17:55       ` Mark Rutland
2016-11-15 10:15       ` Anurup M
2016-11-15 10:15         ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 04/11] Documentation: perf: hisi: Documentation for HIP05/06/07 PMU event counting Anurup M
2016-11-03  5:42   ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 05/11] dt-bindings: perf: hisi: Add Devicetree bindings for Hisilicon SoC PMU Anurup M
2016-11-03  5:42   ` Anurup M
2016-11-03 18:26   ` Krzysztof Kozlowski
2016-11-03 18:26     ` Krzysztof Kozlowski
2016-11-04  5:06     ` Anurup M
2016-11-04  5:06       ` Anurup M
2016-11-10 18:30   ` Mark Rutland
2016-11-10 18:30     ` Mark Rutland
2016-11-14  0:06     ` Anurup M
2016-11-14  0:06       ` Anurup M
2016-11-15  9:51       ` Mark Rutland
2016-11-15  9:51         ` Mark Rutland
2016-11-16  5:54         ` Anurup M
2016-11-16  5:54           ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 06/11] perf: hisi: Update Kconfig for Hisilicon PMU support Anurup M
2016-11-03  5:42   ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 07/11] perf: hisi: Add support for Hisilicon SoC event counters Anurup M
2016-11-03  5:42   ` Anurup M
2016-11-10 19:10   ` Mark Rutland
2016-11-10 19:10     ` Mark Rutland
2016-11-14  8:11     ` Anurup M
2016-11-14  8:11       ` Anurup M
     [not found] ` <1478151727-20250-1-git-send-email-anurup.m-hv44wF8Li93QT0dZR+AlfA@public.gmane.org>
2016-11-03  5:41   ` [RESEND PATCH v1 01/11] arm64: MAINTAINERS: hisi: Add hisilicon SoC PMU support Anurup M
2016-11-03  5:41     ` Anurup M
2016-11-03  5:41   ` [RESEND PATCH v1 02/11] dt-bindings: hisi: Add Hisilicon HiP05/06/07 Sysctrl and Djtag dts bindings Anurup M
2016-11-03  5:41     ` Anurup M
2016-11-10 17:23     ` Mark Rutland
2016-11-10 17:23       ` Mark Rutland
2016-11-11 11:19       ` Anurup M
2016-11-11 11:19         ` Anurup M
2016-11-11 11:53         ` Mark Rutland
2016-11-11 11:53           ` Mark Rutland
2016-11-11 11:59           ` Anurup M
2016-11-11 11:59             ` Anurup M
2016-11-03  5:42   ` [RESEND PATCH v1 08/11] perf: hisi: Add sysfs attributes for L3 cache(L3C) PMU Anurup M
2016-11-03  5:42     ` Anurup M
2016-11-03  5:42   ` [RESEND PATCH v1 10/11] perf: hisi: Support for Hisilicon DDRC PMU Anurup M
2016-11-03  5:42     ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 09/11] perf: hisi: Miscellanous node(MN) event counting in perf Anurup M
2016-11-03  5:42   ` Anurup M
2016-11-03  5:42 ` [RESEND PATCH v1 11/11] dts: arm64: hip06: Add Hisilicon SoC PMU support Anurup M
2016-11-03  5:42   ` Anurup M

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.