All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v3 00/12] Initial Tegra186 support
@ 2016-08-19 17:32 ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Hi everyone,

This is a set of patches to add initial support for Tegra186. It is
based on Joseph's patches but I rewrote some of the drivers to be a
little easier to comprehend and maintain (hopefully). I've also
included clock and reset drivers as a proof of concept.

Thierry

Joseph Lo (7):
  dt-bindings: mailbox: Add Tegra HSP binding
  dt-bindings: firmware: Add bindings for Tegra BPMP
  soc/tegra: Add Tegra186 support
  arm64: defconfig: Enable Tegra186 SoC
  arm64: dts: tegra: Add Tegra186 support
  arm64: dts: tegra: Add NVIDIA P3310 main board support
  arm64: dts: tegra: Add NVIDIA P2771 board support

Thierry Reding (5):
  mailbox: Add Tegra HSP driver
  firmware: tegra: Add IVC library
  firmware: tegra: Add BPMP support
  clk: tegra: Add BPMP clock driver
  reset: Add Tegra BPMP reset driver

 .../bindings/firmware/nvidia,tegra186-bpmp.txt     |   77 +
 .../bindings/mailbox/nvidia,tegra186-hsp.txt       |   52 +
 arch/arm64/boot/dts/nvidia/Makefile                |    1 +
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts |    8 +
 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi     |   34 +
 arch/arm64/boot/dts/nvidia/tegra186.dtsi           |   86 ++
 arch/arm64/configs/defconfig                       |    1 +
 drivers/clk/tegra/Makefile                         |    1 +
 drivers/clk/tegra/clk-bpmp.c                       |  565 +++++++
 drivers/firmware/Kconfig                           |    1 +
 drivers/firmware/Makefile                          |    1 +
 drivers/firmware/tegra/Kconfig                     |   25 +
 drivers/firmware/tegra/Makefile                    |    2 +
 drivers/firmware/tegra/bpmp.c                      |  880 +++++++++++
 drivers/firmware/tegra/ivc.c                       |  683 +++++++++
 drivers/mailbox/Kconfig                            |    9 +
 drivers/mailbox/Makefile                           |    2 +
 drivers/mailbox/tegra-hsp.c                        |  561 +++++++
 drivers/reset/Makefile                             |    1 +
 drivers/reset/tegra/Makefile                       |    1 +
 drivers/reset/tegra/reset-bpmp.c                   |   63 +
 drivers/soc/tegra/Kconfig                          |   14 +
 include/dt-bindings/clock/tegra186-clock.h         |  940 ++++++++++++
 include/dt-bindings/mailbox/tegra186-hsp.h         |   24 +
 include/dt-bindings/reset/tegra186-reset.h         |  217 +++
 include/soc/tegra/bpmp-abi.h                       | 1601 ++++++++++++++++++++
 include/soc/tegra/bpmp.h                           |  122 ++
 include/soc/tegra/ivc.h                            |  109 ++
 28 files changed, 6081 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
 create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186.dtsi
 create mode 100644 drivers/clk/tegra/clk-bpmp.c
 create mode 100644 drivers/firmware/tegra/Kconfig
 create mode 100644 drivers/firmware/tegra/Makefile
 create mode 100644 drivers/firmware/tegra/bpmp.c
 create mode 100644 drivers/firmware/tegra/ivc.c
 create mode 100644 drivers/mailbox/tegra-hsp.c
 create mode 100644 drivers/reset/tegra/Makefile
 create mode 100644 drivers/reset/tegra/reset-bpmp.c
 create mode 100644 include/dt-bindings/clock/tegra186-clock.h
 create mode 100644 include/dt-bindings/mailbox/tegra186-hsp.h
 create mode 100644 include/dt-bindings/reset/tegra186-reset.h
 create mode 100644 include/soc/tegra/bpmp-abi.h
 create mode 100644 include/soc/tegra/bpmp.h
 create mode 100644 include/soc/tegra/ivc.h

-- 
2.9.0

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

* [PATCH v3 00/12] Initial Tegra186 support
@ 2016-08-19 17:32 ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

Hi everyone,

This is a set of patches to add initial support for Tegra186. It is
based on Joseph's patches but I rewrote some of the drivers to be a
little easier to comprehend and maintain (hopefully). I've also
included clock and reset drivers as a proof of concept.

Thierry

Joseph Lo (7):
  dt-bindings: mailbox: Add Tegra HSP binding
  dt-bindings: firmware: Add bindings for Tegra BPMP
  soc/tegra: Add Tegra186 support
  arm64: defconfig: Enable Tegra186 SoC
  arm64: dts: tegra: Add Tegra186 support
  arm64: dts: tegra: Add NVIDIA P3310 main board support
  arm64: dts: tegra: Add NVIDIA P2771 board support

Thierry Reding (5):
  mailbox: Add Tegra HSP driver
  firmware: tegra: Add IVC library
  firmware: tegra: Add BPMP support
  clk: tegra: Add BPMP clock driver
  reset: Add Tegra BPMP reset driver

 .../bindings/firmware/nvidia,tegra186-bpmp.txt     |   77 +
 .../bindings/mailbox/nvidia,tegra186-hsp.txt       |   52 +
 arch/arm64/boot/dts/nvidia/Makefile                |    1 +
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts |    8 +
 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi     |   34 +
 arch/arm64/boot/dts/nvidia/tegra186.dtsi           |   86 ++
 arch/arm64/configs/defconfig                       |    1 +
 drivers/clk/tegra/Makefile                         |    1 +
 drivers/clk/tegra/clk-bpmp.c                       |  565 +++++++
 drivers/firmware/Kconfig                           |    1 +
 drivers/firmware/Makefile                          |    1 +
 drivers/firmware/tegra/Kconfig                     |   25 +
 drivers/firmware/tegra/Makefile                    |    2 +
 drivers/firmware/tegra/bpmp.c                      |  880 +++++++++++
 drivers/firmware/tegra/ivc.c                       |  683 +++++++++
 drivers/mailbox/Kconfig                            |    9 +
 drivers/mailbox/Makefile                           |    2 +
 drivers/mailbox/tegra-hsp.c                        |  561 +++++++
 drivers/reset/Makefile                             |    1 +
 drivers/reset/tegra/Makefile                       |    1 +
 drivers/reset/tegra/reset-bpmp.c                   |   63 +
 drivers/soc/tegra/Kconfig                          |   14 +
 include/dt-bindings/clock/tegra186-clock.h         |  940 ++++++++++++
 include/dt-bindings/mailbox/tegra186-hsp.h         |   24 +
 include/dt-bindings/reset/tegra186-reset.h         |  217 +++
 include/soc/tegra/bpmp-abi.h                       | 1601 ++++++++++++++++++++
 include/soc/tegra/bpmp.h                           |  122 ++
 include/soc/tegra/ivc.h                            |  109 ++
 28 files changed, 6081 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
 create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186.dtsi
 create mode 100644 drivers/clk/tegra/clk-bpmp.c
 create mode 100644 drivers/firmware/tegra/Kconfig
 create mode 100644 drivers/firmware/tegra/Makefile
 create mode 100644 drivers/firmware/tegra/bpmp.c
 create mode 100644 drivers/firmware/tegra/ivc.c
 create mode 100644 drivers/mailbox/tegra-hsp.c
 create mode 100644 drivers/reset/tegra/Makefile
 create mode 100644 drivers/reset/tegra/reset-bpmp.c
 create mode 100644 include/dt-bindings/clock/tegra186-clock.h
 create mode 100644 include/dt-bindings/mailbox/tegra186-hsp.h
 create mode 100644 include/dt-bindings/reset/tegra186-reset.h
 create mode 100644 include/soc/tegra/bpmp-abi.h
 create mode 100644 include/soc/tegra/bpmp.h
 create mode 100644 include/soc/tegra/ivc.h

-- 
2.9.0

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

* [PATCH v3 01/12] dt-bindings: mailbox: Add Tegra HSP binding
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Add DT binding for the Hardware Synchronization Primitives (HSP). The
HSP is designed for the processors to share resources and communicate
with one another. A set of hardware synchronization primitives for
interprocessor communication (IPC) is provided. IPC protocols can use
use these hardware synchronization primitives when operating between
processors in an AMP configuration.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Jon Hunter <jonathanh-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 .../bindings/mailbox/nvidia,tegra186-hsp.txt       | 52 ++++++++++++++++++++++
 include/dt-bindings/mailbox/tegra186-hsp.h         | 24 ++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
 create mode 100644 include/dt-bindings/mailbox/tegra186-hsp.h

diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
new file mode 100644
index 000000000000..b99d25fc2f26
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
@@ -0,0 +1,52 @@
+NVIDIA Tegra Hardware Synchronization Primitives (HSP)
+
+The HSP modules are used for the processors to share resources and communicate
+together. It provides a set of hardware synchronization primitives for
+interprocessor communication. So the interprocessor communication (IPC)
+protocols can use hardware synchronization primitives, when operating between
+two processors not in an SMP relationship.
+
+The features that HSP supported are shared mailboxes, shared semaphores,
+arbitrated semaphores and doorbells.
+
+Required properties:
+- name : Should be hsp
+- compatible
+    Array of strings.
+    one of:
+    - "nvidia,tegra186-hsp"
+- reg : Offset and length of the register set for the device.
+- interrupt-names
+    Array of strings.
+    Contains a list of names for the interrupts described by the interrupt
+    property. May contain the following entries, in any order:
+    - "doorbell"
+    Users of this binding MUST look up entries in the interrupt property
+    by name, using this interrupt-names property to do so.
+- interrupts
+    Array of interrupt specifiers.
+    Must contain one entry per entry in the interrupt-names property,
+    in a matching order.
+- #mbox-cells : Should be 2.
+
+The mbox specifier of the "mboxes" property in the client node should
+contain two data. The first one should be the HSP type and the second
+one should be the ID that the client is going to use. Those information
+can be found in the following file.
+
+- <dt-bindings/mailbox/tegra186-hsp.h>.
+
+Example:
+
+hsp_top0: hsp@3c00000 {
+	compatible = "nvidia,tegra186-hsp";
+	reg = <0x0 0x03c00000 0x0 0xa0000>;
+	interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "doorbell";
+	#mbox-cells = <2>;
+};
+
+client {
+	...
+	mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_XXX>;
+};
diff --git a/include/dt-bindings/mailbox/tegra186-hsp.h b/include/dt-bindings/mailbox/tegra186-hsp.h
new file mode 100644
index 000000000000..f5d66e5f5f10
--- /dev/null
+++ b/include/dt-bindings/mailbox/tegra186-hsp.h
@@ -0,0 +1,24 @@
+/*
+ * This header provides constants for binding nvidia,tegra186-hsp.
+ */
+
+#ifndef _DT_BINDINGS_MAILBOX_TEGRA186_HSP_H
+#define _DT_BINDINGS_MAILBOX_TEGRA186_HSP_H
+
+/*
+ * These define the type of mailbox that is to be used (doorbell, shared
+ * mailbox, shared semaphore or arbitrated semaphore).
+ */
+#define TEGRA_HSP_MBOX_TYPE_DB 0x0
+#define TEGRA_HSP_MBOX_TYPE_SM 0x1
+#define TEGRA_HSP_MBOX_TYPE_SS 0x2
+#define TEGRA_HSP_MBOX_TYPE_AS 0x3
+
+/*
+ * These defines represent the bit associated with the given master ID in the
+ * doorbell registers.
+ */
+#define TEGRA_HSP_DB_MASTER_CCPLEX 17
+#define TEGRA_HSP_DB_MASTER_BPMP 19
+
+#endif
-- 
2.9.0

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

* [PATCH v3 01/12] dt-bindings: mailbox: Add Tegra HSP binding
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

Add DT binding for the Hardware Synchronization Primitives (HSP). The
HSP is designed for the processors to share resources and communicate
with one another. A set of hardware synchronization primitives for
interprocessor communication (IPC) is provided. IPC protocols can use
use these hardware synchronization primitives when operating between
processors in an AMP configuration.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 .../bindings/mailbox/nvidia,tegra186-hsp.txt       | 52 ++++++++++++++++++++++
 include/dt-bindings/mailbox/tegra186-hsp.h         | 24 ++++++++++
 2 files changed, 76 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
 create mode 100644 include/dt-bindings/mailbox/tegra186-hsp.h

diff --git a/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
new file mode 100644
index 000000000000..b99d25fc2f26
--- /dev/null
+++ b/Documentation/devicetree/bindings/mailbox/nvidia,tegra186-hsp.txt
@@ -0,0 +1,52 @@
+NVIDIA Tegra Hardware Synchronization Primitives (HSP)
+
+The HSP modules are used for the processors to share resources and communicate
+together. It provides a set of hardware synchronization primitives for
+interprocessor communication. So the interprocessor communication (IPC)
+protocols can use hardware synchronization primitives, when operating between
+two processors not in an SMP relationship.
+
+The features that HSP supported are shared mailboxes, shared semaphores,
+arbitrated semaphores and doorbells.
+
+Required properties:
+- name : Should be hsp
+- compatible
+    Array of strings.
+    one of:
+    - "nvidia,tegra186-hsp"
+- reg : Offset and length of the register set for the device.
+- interrupt-names
+    Array of strings.
+    Contains a list of names for the interrupts described by the interrupt
+    property. May contain the following entries, in any order:
+    - "doorbell"
+    Users of this binding MUST look up entries in the interrupt property
+    by name, using this interrupt-names property to do so.
+- interrupts
+    Array of interrupt specifiers.
+    Must contain one entry per entry in the interrupt-names property,
+    in a matching order.
+- #mbox-cells : Should be 2.
+
+The mbox specifier of the "mboxes" property in the client node should
+contain two data. The first one should be the HSP type and the second
+one should be the ID that the client is going to use. Those information
+can be found in the following file.
+
+- <dt-bindings/mailbox/tegra186-hsp.h>.
+
+Example:
+
+hsp_top0: hsp at 3c00000 {
+	compatible = "nvidia,tegra186-hsp";
+	reg = <0x0 0x03c00000 0x0 0xa0000>;
+	interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+	interrupt-names = "doorbell";
+	#mbox-cells = <2>;
+};
+
+client {
+	...
+	mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_XXX>;
+};
diff --git a/include/dt-bindings/mailbox/tegra186-hsp.h b/include/dt-bindings/mailbox/tegra186-hsp.h
new file mode 100644
index 000000000000..f5d66e5f5f10
--- /dev/null
+++ b/include/dt-bindings/mailbox/tegra186-hsp.h
@@ -0,0 +1,24 @@
+/*
+ * This header provides constants for binding nvidia,tegra186-hsp.
+ */
+
+#ifndef _DT_BINDINGS_MAILBOX_TEGRA186_HSP_H
+#define _DT_BINDINGS_MAILBOX_TEGRA186_HSP_H
+
+/*
+ * These define the type of mailbox that is to be used (doorbell, shared
+ * mailbox, shared semaphore or arbitrated semaphore).
+ */
+#define TEGRA_HSP_MBOX_TYPE_DB 0x0
+#define TEGRA_HSP_MBOX_TYPE_SM 0x1
+#define TEGRA_HSP_MBOX_TYPE_SS 0x2
+#define TEGRA_HSP_MBOX_TYPE_AS 0x3
+
+/*
+ * These defines represent the bit associated with the given master ID in the
+ * doorbell registers.
+ */
+#define TEGRA_HSP_DB_MASTER_CCPLEX 17
+#define TEGRA_HSP_DB_MASTER_BPMP 19
+
+#endif
-- 
2.9.0

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

This driver exposes a mailbox interface for interprocessor communication
using the Hardware Synchronization Primitives (HSP) module's doorbell
mechanism. There are multiple HSP instances and they provide additional
features such as shared mailboxes, shared and arbitrated semaphores.

A driver for a remote processor can use the mailbox client provided by
the HSP driver and build an IPC protocol on top of this synchronization
mechanism.

Based on work by Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Changes in v3:
- use a more object oriented design
- fix some locking issues

 drivers/mailbox/Kconfig     |   9 +
 drivers/mailbox/Makefile    |   2 +
 drivers/mailbox/tegra-hsp.c | 561 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 572 insertions(+)
 create mode 100644 drivers/mailbox/tegra-hsp.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 97c372908e78..90e7b94a1199 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -114,6 +114,15 @@ config MAILBOX_TEST
 	  Test client to help with testing new Controller driver
 	  implementations.
 
+config TEGRA_HSP_MBOX
+	bool "Tegra HSP(Hardware Synchronization Primitives) Driver"
+	depends on ARCH_TEGRA_186_SOC
+	help
+	  The Tegra HSP driver is used for the interprocessor communication
+	  between different remote processors and host processors on Tegra186
+	  and later SoCs. Say Y here if you want to have this support.
+	  If unsure say N.
+
 config XGENE_SLIMPRO_MBOX
 	tristate "APM SoC X-Gene SLIMpro Mailbox Controller"
 	depends on ARCH_XGENE
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 66c38e300dfc..3fa01673f288 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -27,3 +27,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
 obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
 
 obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
+
+obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
new file mode 100644
index 000000000000..8770ba9b58fe
--- /dev/null
+++ b/drivers/mailbox/tegra-hsp.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#define DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_controller.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/mailbox/tegra186-hsp.h>
+
+#define HSP_INT_DIMENSIONING	0x380
+#define HSP_nSM_SHIFT		0
+#define HSP_nSS_SHIFT		4
+#define HSP_nAS_SHIFT		8
+#define HSP_nDB_SHIFT		12
+#define HSP_nSI_SHIFT		16
+#define HSP_nINT_MASK		0xf
+
+#define HSP_DB_TRIGGER	0x0
+#define HSP_DB_ENABLE	0x4
+#define HSP_DB_RAW	0x8
+#define HSP_DB_PENDING	0xc
+
+#define HSP_DB_CCPLEX		1
+#define HSP_DB_BPMP		3
+#define HSP_DB_MAX		7
+
+struct tegra_hsp_channel;
+struct tegra_hsp;
+
+struct tegra_hsp_channel_ops {
+	int (*send_data)(struct tegra_hsp_channel *channel, void *data);
+	int (*startup)(struct tegra_hsp_channel *channel);
+	void (*shutdown)(struct tegra_hsp_channel *channel);
+	bool (*last_tx_done)(struct tegra_hsp_channel *channel);
+};
+
+struct tegra_hsp_channel {
+	struct tegra_hsp *hsp;
+	const struct tegra_hsp_channel_ops *ops;
+	struct mbox_chan *chan;
+	void __iomem *regs;
+};
+
+static struct tegra_hsp_channel *to_tegra_hsp_channel(struct mbox_chan *chan)
+{
+	return chan->con_priv;
+}
+
+struct tegra_hsp_doorbell {
+	struct tegra_hsp_channel channel;
+	struct list_head list;
+	const char *name;
+	unsigned int master;
+	unsigned int index;
+};
+
+static struct tegra_hsp_doorbell *
+to_tegra_hsp_doorbell(struct tegra_hsp_channel *channel)
+{
+	if (!channel)
+		return NULL;
+
+	return container_of(channel, struct tegra_hsp_doorbell, channel);
+}
+
+struct tegra_hsp_db_map {
+	const char *name;
+	unsigned int master;
+	unsigned int index;
+};
+
+struct tegra_hsp_soc {
+	const struct tegra_hsp_db_map *map;
+};
+
+struct tegra_hsp {
+	const struct tegra_hsp_soc *soc;
+	struct mbox_controller mbox;
+	void __iomem *regs;
+	unsigned int irq;
+	unsigned int num_sm;
+	unsigned int num_as;
+	unsigned int num_ss;
+	unsigned int num_db;
+	unsigned int num_si;
+	spinlock_t lock;
+
+	struct list_head doorbells;
+};
+
+static inline struct tegra_hsp *
+to_tegra_hsp(struct mbox_controller *mbox)
+{
+	return container_of(mbox, struct tegra_hsp, mbox);
+}
+
+static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
+{
+	return readl(hsp->regs + offset);
+}
+
+static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
+				    unsigned int offset)
+{
+	writel(value, hsp->regs + offset);
+	(void)readl(hsp->regs + offset);
+}
+
+static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
+					  unsigned int offset)
+{
+	return readl(channel->regs + offset);
+}
+
+static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
+					    u32 value, unsigned int offset)
+{
+	writel(value, channel->regs + offset);
+	(void)readl(channel->regs + offset);
+}
+
+static int tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
+{
+	u32 value;
+
+	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
+
+	return !!(value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX));
+}
+
+static struct tegra_hsp_doorbell *
+__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
+{
+	struct tegra_hsp_doorbell *db = NULL, *entry;
+
+	list_for_each_entry(entry, &hsp->doorbells, list)
+		if (entry->master == master) {
+			db = entry;
+			break;
+		}
+
+	return db;
+}
+
+static struct tegra_hsp_doorbell *
+tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+	db = __tegra_hsp_doorbell_get(hsp, master);
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	return db;
+}
+
+static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
+{
+	struct tegra_hsp *hsp = data;
+	struct tegra_hsp_doorbell *db;
+	unsigned long master, value;
+
+	db = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!db)
+		return IRQ_NONE;
+
+	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_PENDING);
+	tegra_hsp_channel_writel(&db->channel, value, HSP_DB_PENDING);
+
+	spin_lock(&hsp->lock);
+
+	for_each_set_bit(master, &value, hsp->mbox.num_chans) {
+		struct tegra_hsp_doorbell *db;
+
+		db = __tegra_hsp_doorbell_get(hsp, master);
+		if (db)
+			mbox_chan_received_data(db->channel.chan, NULL);
+	}
+
+	spin_unlock(&hsp->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
+					void *data)
+{
+	tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
+
+	return 0;
+}
+
+static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
+{
+	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
+	struct tegra_hsp *hsp = channel->hsp;
+	struct tegra_hsp_doorbell *ccplex;
+	unsigned long flags;
+	u32 value;
+
+	dev_dbg(hsp->mbox.dev, "> %s(channel=%p)\n", __func__, channel);
+	dev_dbg(hsp->mbox.dev, "  regs: %p\n", channel->regs);
+
+	if (db->master >= hsp->mbox.num_chans) {
+		dev_err(hsp->mbox.dev,
+			"invalid master ID %u for HSP channel\n",
+			db->master);
+		return -EINVAL;
+	}
+
+	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!ccplex)
+		return -ENODEV;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
+	value |= BIT(db->master);
+	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	if (!tegra_hsp_doorbell_can_ring(db))
+		return -ENODEV;
+
+	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_channel *channel)
+{
+	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
+	struct tegra_hsp *hsp = channel->hsp;
+	struct tegra_hsp_doorbell *ccplex;
+	unsigned long flags;
+	u32 value;
+
+	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!ccplex)
+		return;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
+	value &= ~BIT(db->master);
+	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+}
+
+static bool tegra_hsp_doorbell_last_tx_done(struct tegra_hsp_channel *channel)
+{
+	return true;
+}
+
+static const struct tegra_hsp_channel_ops tegra_hsp_doorbell_ops = {
+	.send_data = tegra_hsp_doorbell_send_data,
+	.startup = tegra_hsp_doorbell_startup,
+	.shutdown = tegra_hsp_doorbell_shutdown,
+	.last_tx_done = tegra_hsp_doorbell_last_tx_done,
+};
+
+static struct tegra_hsp_channel *
+tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
+			  unsigned int master, unsigned int index)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned int offset;
+	unsigned long flags;
+
+	db = kzalloc(sizeof(*db), GFP_KERNEL);
+	if (!db)
+		return ERR_PTR(-ENOMEM);
+
+	offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
+	offset += index * 0x100;
+
+	db->channel.ops = &tegra_hsp_doorbell_ops;
+	db->channel.regs = hsp->regs + offset;
+	db->channel.hsp = hsp;
+
+	db->name = kstrdup_const(name, GFP_KERNEL);
+	db->master = master;
+	db->index = index;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+	list_add_tail(&db->list, &hsp->doorbells);
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	return &db->channel;
+}
+
+static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
+{
+	list_del(&db->list);
+	kfree_const(db->name);
+	kfree(db);
+}
+
+static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->send_data(channel, data);
+}
+
+static int tegra_hsp_startup(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->startup(channel);
+}
+
+static void tegra_hsp_shutdown(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->shutdown(channel);
+}
+
+static bool tegra_hsp_last_tx_done(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->last_tx_done(channel);
+}
+
+static const struct mbox_chan_ops tegra_hsp_ops = {
+	.send_data = tegra_hsp_send_data,
+	.startup = tegra_hsp_startup,
+	.shutdown = tegra_hsp_shutdown,
+	.last_tx_done = tegra_hsp_last_tx_done,
+};
+
+static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
+					    const struct of_phandle_args *args)
+{
+	struct tegra_hsp *hsp = to_tegra_hsp(mbox);
+	struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
+	unsigned int type = args->args[0];
+	unsigned int master = args->args[1];
+	struct tegra_hsp_doorbell *db;
+	struct mbox_chan *chan;
+	unsigned long flags;
+	unsigned int i;
+
+	dev_dbg(mbox->dev, "> %s(mbox=%p, args=%p)\n", __func__, mbox, args);
+	dev_dbg(mbox->dev, "  type: %x, master: %x\n", type, master);
+
+	switch (type) {
+	case TEGRA_HSP_MBOX_TYPE_DB:
+		db = tegra_hsp_doorbell_get(hsp, master);
+		dev_dbg(mbox->dev, "  doorbell: %p\n", db);
+		if (db)
+			channel = &db->channel;
+
+		break;
+
+	default:
+		break;
+	}
+
+	if (IS_ERR(channel))
+		return ERR_CAST(channel);
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	for (i = 0; i < hsp->mbox.num_chans; i++) {
+		chan = &hsp->mbox.chans[i];
+		if (!chan->con_priv) {
+			chan->con_priv = channel;
+			channel->chan = chan;
+			break;
+		}
+
+		chan = NULL;
+	}
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	dev_dbg(mbox->dev, "< %s() = %p\n", __func__, chan);
+	return chan ?: ERR_PTR(-EBUSY);
+}
+
+static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	list_for_each_entry(db, &hsp->doorbells, list)
+		__tegra_hsp_doorbell_destroy(db);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+}
+
+static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
+{
+	const struct tegra_hsp_db_map *map = hsp->soc->map;
+	struct tegra_hsp_channel *channel;
+
+	dev_dbg(hsp->mbox.dev, "> %s(hsp=%p)\n", __func__, hsp);
+
+	while (map->name) {
+		dev_dbg(hsp->mbox.dev, "  adding doorbell for master %u (%s), index %u\n", map->master, map->name, map->index);
+
+		channel = tegra_hsp_doorbell_create(hsp, map->name,
+						    map->master, map->index);
+		if (IS_ERR(channel)) {
+			tegra_hsp_remove_doorbells(hsp);
+			return PTR_ERR(channel);
+		}
+
+		map++;
+	}
+
+	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static int tegra_hsp_probe(struct platform_device *pdev)
+{
+	struct tegra_hsp *hsp;
+	struct resource *res;
+	u32 value;
+	int err;
+
+	dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+	hsp = devm_kzalloc(&pdev->dev, sizeof(*hsp), GFP_KERNEL);
+	if (!hsp)
+		return -ENOMEM;
+
+	dev_dbg(&pdev->dev, "  hsp: %p\n", hsp);
+
+	hsp->soc = of_device_get_match_data(&pdev->dev);
+	INIT_LIST_HEAD(&hsp->doorbells);
+	spin_lock_init(&hsp->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hsp->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(hsp->regs))
+		return PTR_ERR(hsp->regs);
+
+	value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
+	hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
+	hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
+	hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
+	hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
+	hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
+
+	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);
+	dev_dbg(&pdev->dev, "sm: %u ss:%u as: %u db: %u, si: %u\n",
+		hsp->num_sm, hsp->num_ss, hsp->num_as, hsp->num_db,
+		hsp->num_si);
+
+	err = platform_get_irq_byname(pdev, "doorbell");
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
+		return err;
+	}
+
+	hsp->irq = err;
+
+	err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
+			       IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
+			hsp->irq, err);
+		return err;
+	}
+
+	hsp->mbox.of_xlate = of_tegra_hsp_xlate;
+	hsp->mbox.num_chans = 32;
+	hsp->mbox.dev = &pdev->dev;
+	hsp->mbox.txdone_irq = false;
+	hsp->mbox.txdone_poll = false;
+	hsp->mbox.ops = &tegra_hsp_ops;
+
+	hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
+					sizeof(*hsp->mbox.chans),
+					GFP_KERNEL);
+	if (!hsp->mbox.chans)
+		return -ENOMEM;
+
+	err = tegra_hsp_add_doorbells(hsp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, hsp);
+
+	err = mbox_controller_register(&hsp->mbox);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
+		tegra_hsp_remove_doorbells(hsp);
+		return err;
+	}
+
+	dev_dbg(&pdev->dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static int tegra_hsp_remove(struct platform_device *pdev)
+{
+	struct tegra_hsp *hsp = platform_get_drvdata(pdev);
+
+	mbox_controller_unregister(&hsp->mbox);
+	tegra_hsp_remove_doorbells(hsp);
+
+	return 0;
+}
+
+static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
+	{ "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX },
+	{ "bpmp",   TEGRA_HSP_DB_MASTER_BPMP,   HSP_DB_BPMP   },
+	{ /* sentinel */ }
+};
+
+static const struct tegra_hsp_soc tegra186_hsp_soc = {
+	.map = tegra186_hsp_db_map,
+};
+
+static const struct of_device_id tegra_hsp_match[] = {
+	{ .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
+	{ }
+};
+
+static struct platform_driver tegra_hsp_driver = {
+	.driver = {
+		.name = "tegra-hsp",
+		.of_match_table = tegra_hsp_match,
+	},
+	.probe = tegra_hsp_probe,
+	.remove = tegra_hsp_remove,
+};
+
+static int __init tegra_hsp_init(void)
+{
+	return platform_driver_register(&tegra_hsp_driver);
+}
+core_initcall(tegra_hsp_init);
-- 
2.9.0

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

This driver exposes a mailbox interface for interprocessor communication
using the Hardware Synchronization Primitives (HSP) module's doorbell
mechanism. There are multiple HSP instances and they provide additional
features such as shared mailboxes, shared and arbitrated semaphores.

A driver for a remote processor can use the mailbox client provided by
the HSP driver and build an IPC protocol on top of this synchronization
mechanism.

Based on work by Joseph Lo <josephl@nvidia.com>.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- use a more object oriented design
- fix some locking issues

 drivers/mailbox/Kconfig     |   9 +
 drivers/mailbox/Makefile    |   2 +
 drivers/mailbox/tegra-hsp.c | 561 ++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 572 insertions(+)
 create mode 100644 drivers/mailbox/tegra-hsp.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index 97c372908e78..90e7b94a1199 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -114,6 +114,15 @@ config MAILBOX_TEST
 	  Test client to help with testing new Controller driver
 	  implementations.
 
+config TEGRA_HSP_MBOX
+	bool "Tegra HSP(Hardware Synchronization Primitives) Driver"
+	depends on ARCH_TEGRA_186_SOC
+	help
+	  The Tegra HSP driver is used for the interprocessor communication
+	  between different remote processors and host processors on Tegra186
+	  and later SoCs. Say Y here if you want to have this support.
+	  If unsure say N.
+
 config XGENE_SLIMPRO_MBOX
 	tristate "APM SoC X-Gene SLIMpro Mailbox Controller"
 	depends on ARCH_XGENE
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 66c38e300dfc..3fa01673f288 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -27,3 +27,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
 obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
 
 obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
+
+obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
new file mode 100644
index 000000000000..8770ba9b58fe
--- /dev/null
+++ b/drivers/mailbox/tegra-hsp.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#define DEBUG
+
+#include <linux/interrupt.h>
+#include <linux/io.h>
+#include <linux/mailbox_controller.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+
+#include <dt-bindings/mailbox/tegra186-hsp.h>
+
+#define HSP_INT_DIMENSIONING	0x380
+#define HSP_nSM_SHIFT		0
+#define HSP_nSS_SHIFT		4
+#define HSP_nAS_SHIFT		8
+#define HSP_nDB_SHIFT		12
+#define HSP_nSI_SHIFT		16
+#define HSP_nINT_MASK		0xf
+
+#define HSP_DB_TRIGGER	0x0
+#define HSP_DB_ENABLE	0x4
+#define HSP_DB_RAW	0x8
+#define HSP_DB_PENDING	0xc
+
+#define HSP_DB_CCPLEX		1
+#define HSP_DB_BPMP		3
+#define HSP_DB_MAX		7
+
+struct tegra_hsp_channel;
+struct tegra_hsp;
+
+struct tegra_hsp_channel_ops {
+	int (*send_data)(struct tegra_hsp_channel *channel, void *data);
+	int (*startup)(struct tegra_hsp_channel *channel);
+	void (*shutdown)(struct tegra_hsp_channel *channel);
+	bool (*last_tx_done)(struct tegra_hsp_channel *channel);
+};
+
+struct tegra_hsp_channel {
+	struct tegra_hsp *hsp;
+	const struct tegra_hsp_channel_ops *ops;
+	struct mbox_chan *chan;
+	void __iomem *regs;
+};
+
+static struct tegra_hsp_channel *to_tegra_hsp_channel(struct mbox_chan *chan)
+{
+	return chan->con_priv;
+}
+
+struct tegra_hsp_doorbell {
+	struct tegra_hsp_channel channel;
+	struct list_head list;
+	const char *name;
+	unsigned int master;
+	unsigned int index;
+};
+
+static struct tegra_hsp_doorbell *
+to_tegra_hsp_doorbell(struct tegra_hsp_channel *channel)
+{
+	if (!channel)
+		return NULL;
+
+	return container_of(channel, struct tegra_hsp_doorbell, channel);
+}
+
+struct tegra_hsp_db_map {
+	const char *name;
+	unsigned int master;
+	unsigned int index;
+};
+
+struct tegra_hsp_soc {
+	const struct tegra_hsp_db_map *map;
+};
+
+struct tegra_hsp {
+	const struct tegra_hsp_soc *soc;
+	struct mbox_controller mbox;
+	void __iomem *regs;
+	unsigned int irq;
+	unsigned int num_sm;
+	unsigned int num_as;
+	unsigned int num_ss;
+	unsigned int num_db;
+	unsigned int num_si;
+	spinlock_t lock;
+
+	struct list_head doorbells;
+};
+
+static inline struct tegra_hsp *
+to_tegra_hsp(struct mbox_controller *mbox)
+{
+	return container_of(mbox, struct tegra_hsp, mbox);
+}
+
+static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
+{
+	return readl(hsp->regs + offset);
+}
+
+static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
+				    unsigned int offset)
+{
+	writel(value, hsp->regs + offset);
+	(void)readl(hsp->regs + offset);
+}
+
+static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
+					  unsigned int offset)
+{
+	return readl(channel->regs + offset);
+}
+
+static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
+					    u32 value, unsigned int offset)
+{
+	writel(value, channel->regs + offset);
+	(void)readl(channel->regs + offset);
+}
+
+static int tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
+{
+	u32 value;
+
+	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
+
+	return !!(value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX));
+}
+
+static struct tegra_hsp_doorbell *
+__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
+{
+	struct tegra_hsp_doorbell *db = NULL, *entry;
+
+	list_for_each_entry(entry, &hsp->doorbells, list)
+		if (entry->master == master) {
+			db = entry;
+			break;
+		}
+
+	return db;
+}
+
+static struct tegra_hsp_doorbell *
+tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+	db = __tegra_hsp_doorbell_get(hsp, master);
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	return db;
+}
+
+static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
+{
+	struct tegra_hsp *hsp = data;
+	struct tegra_hsp_doorbell *db;
+	unsigned long master, value;
+
+	db = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!db)
+		return IRQ_NONE;
+
+	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_PENDING);
+	tegra_hsp_channel_writel(&db->channel, value, HSP_DB_PENDING);
+
+	spin_lock(&hsp->lock);
+
+	for_each_set_bit(master, &value, hsp->mbox.num_chans) {
+		struct tegra_hsp_doorbell *db;
+
+		db = __tegra_hsp_doorbell_get(hsp, master);
+		if (db)
+			mbox_chan_received_data(db->channel.chan, NULL);
+	}
+
+	spin_unlock(&hsp->lock);
+
+	return IRQ_HANDLED;
+}
+
+static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
+					void *data)
+{
+	tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
+
+	return 0;
+}
+
+static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
+{
+	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
+	struct tegra_hsp *hsp = channel->hsp;
+	struct tegra_hsp_doorbell *ccplex;
+	unsigned long flags;
+	u32 value;
+
+	dev_dbg(hsp->mbox.dev, "> %s(channel=%p)\n", __func__, channel);
+	dev_dbg(hsp->mbox.dev, "  regs: %p\n", channel->regs);
+
+	if (db->master >= hsp->mbox.num_chans) {
+		dev_err(hsp->mbox.dev,
+			"invalid master ID %u for HSP channel\n",
+			db->master);
+		return -EINVAL;
+	}
+
+	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!ccplex)
+		return -ENODEV;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
+	value |= BIT(db->master);
+	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	if (!tegra_hsp_doorbell_can_ring(db))
+		return -ENODEV;
+
+	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_channel *channel)
+{
+	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
+	struct tegra_hsp *hsp = channel->hsp;
+	struct tegra_hsp_doorbell *ccplex;
+	unsigned long flags;
+	u32 value;
+
+	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
+	if (!ccplex)
+		return;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
+	value &= ~BIT(db->master);
+	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+}
+
+static bool tegra_hsp_doorbell_last_tx_done(struct tegra_hsp_channel *channel)
+{
+	return true;
+}
+
+static const struct tegra_hsp_channel_ops tegra_hsp_doorbell_ops = {
+	.send_data = tegra_hsp_doorbell_send_data,
+	.startup = tegra_hsp_doorbell_startup,
+	.shutdown = tegra_hsp_doorbell_shutdown,
+	.last_tx_done = tegra_hsp_doorbell_last_tx_done,
+};
+
+static struct tegra_hsp_channel *
+tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
+			  unsigned int master, unsigned int index)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned int offset;
+	unsigned long flags;
+
+	db = kzalloc(sizeof(*db), GFP_KERNEL);
+	if (!db)
+		return ERR_PTR(-ENOMEM);
+
+	offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
+	offset += index * 0x100;
+
+	db->channel.ops = &tegra_hsp_doorbell_ops;
+	db->channel.regs = hsp->regs + offset;
+	db->channel.hsp = hsp;
+
+	db->name = kstrdup_const(name, GFP_KERNEL);
+	db->master = master;
+	db->index = index;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+	list_add_tail(&db->list, &hsp->doorbells);
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	return &db->channel;
+}
+
+static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
+{
+	list_del(&db->list);
+	kfree_const(db->name);
+	kfree(db);
+}
+
+static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->send_data(channel, data);
+}
+
+static int tegra_hsp_startup(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->startup(channel);
+}
+
+static void tegra_hsp_shutdown(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->shutdown(channel);
+}
+
+static bool tegra_hsp_last_tx_done(struct mbox_chan *chan)
+{
+	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
+
+	return channel->ops->last_tx_done(channel);
+}
+
+static const struct mbox_chan_ops tegra_hsp_ops = {
+	.send_data = tegra_hsp_send_data,
+	.startup = tegra_hsp_startup,
+	.shutdown = tegra_hsp_shutdown,
+	.last_tx_done = tegra_hsp_last_tx_done,
+};
+
+static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
+					    const struct of_phandle_args *args)
+{
+	struct tegra_hsp *hsp = to_tegra_hsp(mbox);
+	struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
+	unsigned int type = args->args[0];
+	unsigned int master = args->args[1];
+	struct tegra_hsp_doorbell *db;
+	struct mbox_chan *chan;
+	unsigned long flags;
+	unsigned int i;
+
+	dev_dbg(mbox->dev, "> %s(mbox=%p, args=%p)\n", __func__, mbox, args);
+	dev_dbg(mbox->dev, "  type: %x, master: %x\n", type, master);
+
+	switch (type) {
+	case TEGRA_HSP_MBOX_TYPE_DB:
+		db = tegra_hsp_doorbell_get(hsp, master);
+		dev_dbg(mbox->dev, "  doorbell: %p\n", db);
+		if (db)
+			channel = &db->channel;
+
+		break;
+
+	default:
+		break;
+	}
+
+	if (IS_ERR(channel))
+		return ERR_CAST(channel);
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	for (i = 0; i < hsp->mbox.num_chans; i++) {
+		chan = &hsp->mbox.chans[i];
+		if (!chan->con_priv) {
+			chan->con_priv = channel;
+			channel->chan = chan;
+			break;
+		}
+
+		chan = NULL;
+	}
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+
+	dev_dbg(mbox->dev, "< %s() = %p\n", __func__, chan);
+	return chan ?: ERR_PTR(-EBUSY);
+}
+
+static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
+{
+	struct tegra_hsp_doorbell *db;
+	unsigned long flags;
+
+	spin_lock_irqsave(&hsp->lock, flags);
+
+	list_for_each_entry(db, &hsp->doorbells, list)
+		__tegra_hsp_doorbell_destroy(db);
+
+	spin_unlock_irqrestore(&hsp->lock, flags);
+}
+
+static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
+{
+	const struct tegra_hsp_db_map *map = hsp->soc->map;
+	struct tegra_hsp_channel *channel;
+
+	dev_dbg(hsp->mbox.dev, "> %s(hsp=%p)\n", __func__, hsp);
+
+	while (map->name) {
+		dev_dbg(hsp->mbox.dev, "  adding doorbell for master %u (%s), index %u\n", map->master, map->name, map->index);
+
+		channel = tegra_hsp_doorbell_create(hsp, map->name,
+						    map->master, map->index);
+		if (IS_ERR(channel)) {
+			tegra_hsp_remove_doorbells(hsp);
+			return PTR_ERR(channel);
+		}
+
+		map++;
+	}
+
+	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static int tegra_hsp_probe(struct platform_device *pdev)
+{
+	struct tegra_hsp *hsp;
+	struct resource *res;
+	u32 value;
+	int err;
+
+	dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
+
+	hsp = devm_kzalloc(&pdev->dev, sizeof(*hsp), GFP_KERNEL);
+	if (!hsp)
+		return -ENOMEM;
+
+	dev_dbg(&pdev->dev, "  hsp: %p\n", hsp);
+
+	hsp->soc = of_device_get_match_data(&pdev->dev);
+	INIT_LIST_HEAD(&hsp->doorbells);
+	spin_lock_init(&hsp->lock);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	hsp->regs = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(hsp->regs))
+		return PTR_ERR(hsp->regs);
+
+	value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
+	hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
+	hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
+	hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
+	hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
+	hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
+
+	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);
+	dev_dbg(&pdev->dev, "sm: %u ss:%u as: %u db: %u, si: %u\n",
+		hsp->num_sm, hsp->num_ss, hsp->num_as, hsp->num_db,
+		hsp->num_si);
+
+	err = platform_get_irq_byname(pdev, "doorbell");
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
+		return err;
+	}
+
+	hsp->irq = err;
+
+	err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
+			       IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
+			hsp->irq, err);
+		return err;
+	}
+
+	hsp->mbox.of_xlate = of_tegra_hsp_xlate;
+	hsp->mbox.num_chans = 32;
+	hsp->mbox.dev = &pdev->dev;
+	hsp->mbox.txdone_irq = false;
+	hsp->mbox.txdone_poll = false;
+	hsp->mbox.ops = &tegra_hsp_ops;
+
+	hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
+					sizeof(*hsp->mbox.chans),
+					GFP_KERNEL);
+	if (!hsp->mbox.chans)
+		return -ENOMEM;
+
+	err = tegra_hsp_add_doorbells(hsp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
+		return err;
+	}
+
+	platform_set_drvdata(pdev, hsp);
+
+	err = mbox_controller_register(&hsp->mbox);
+	if (err) {
+		dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
+		tegra_hsp_remove_doorbells(hsp);
+		return err;
+	}
+
+	dev_dbg(&pdev->dev, "< %s()\n", __func__);
+	return 0;
+}
+
+static int tegra_hsp_remove(struct platform_device *pdev)
+{
+	struct tegra_hsp *hsp = platform_get_drvdata(pdev);
+
+	mbox_controller_unregister(&hsp->mbox);
+	tegra_hsp_remove_doorbells(hsp);
+
+	return 0;
+}
+
+static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
+	{ "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX },
+	{ "bpmp",   TEGRA_HSP_DB_MASTER_BPMP,   HSP_DB_BPMP   },
+	{ /* sentinel */ }
+};
+
+static const struct tegra_hsp_soc tegra186_hsp_soc = {
+	.map = tegra186_hsp_db_map,
+};
+
+static const struct of_device_id tegra_hsp_match[] = {
+	{ .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
+	{ }
+};
+
+static struct platform_driver tegra_hsp_driver = {
+	.driver = {
+		.name = "tegra-hsp",
+		.of_match_table = tegra_hsp_match,
+	},
+	.probe = tegra_hsp_probe,
+	.remove = tegra_hsp_remove,
+};
+
+static int __init tegra_hsp_init(void)
+{
+	return platform_driver_register(&tegra_hsp_driver);
+}
+core_initcall(tegra_hsp_init);
-- 
2.9.0

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

* [PATCH v3 03/12] dt-bindings: firmware: Add bindings for Tegra BPMP
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The Boot and Power Management Processor (BPMP) is a co-processor found
in Tegra SoCs. It is designed to handle the early stages of the boot
process as well as to offload power management tasks (such as clocks,
resets, powergates, ...).

The binding document defines the resources that are used by the BPMP
firmware, which implements the interprocessor communication (IPC)
between the CPU and the BPMP.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>
Acked-by: Jon Hunter <jonathanh-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 .../bindings/firmware/nvidia,tegra186-bpmp.txt     |  77 ++
 include/dt-bindings/clock/tegra186-clock.h         | 940 +++++++++++++++++++++
 include/dt-bindings/reset/tegra186-reset.h         | 217 +++++
 3 files changed, 1234 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
 create mode 100644 include/dt-bindings/clock/tegra186-clock.h
 create mode 100644 include/dt-bindings/reset/tegra186-reset.h

diff --git a/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
new file mode 100644
index 000000000000..14d7fd035562
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
@@ -0,0 +1,77 @@
+NVIDIA Tegra Boot and Power Management Processor (BPMP)
+
+The BPMP is a specific processor in Tegra chip, which is designed for
+booting process handling and offloading the power management, clock
+management, and reset control tasks from the CPU. The binding document
+defines the resources that would be used by the BPMP firmware driver,
+which can create the interprocessor communication (IPC) between the CPU
+and BPMP.
+
+Required properties:
+- name : Should be bpmp
+- compatible
+    Array of strings
+    One of:
+    - "nvidia,tegra186-bpmp"
+- mboxes : The phandle of mailbox controller and the mailbox specifier.
+- shmem : List of the phandle of the TX and RX shared memory area that
+	  the IPC between CPU and BPMP is based on.
+- #clock-cells : Should be 1.
+- #reset-cells : Should be 1.
+
+This node is a mailbox consumer. See the following files for details of
+the mailbox subsystem, and the specifiers implemented by the relevant
+provider(s):
+
+- .../mailbox/mailbox.txt
+- .../mailbox/nvidia,tegra186-hsp.txt
+
+This node is a clock and reset provider. See the following files for
+general documentation of those features, and the specifiers implemented
+by this node:
+
+- .../clock/clock-bindings.txt
+- <dt-bindings/clock/tegra186-clock.h>
+- .../reset/reset.txt
+- <dt-bindings/reset/tegra186-reset.h>
+
+The shared memory bindings for BPMP
+-----------------------------------
+
+The shared memory area for the IPC TX and RX between CPU and BPMP are
+predefined and work on top of sysram, which is an SRAM inside the chip.
+
+See ".../sram/sram.txt" for the bindings.
+
+Example:
+
+hsp_top0: hsp@03c00000 {
+	...
+	#mbox-cells = <2>;
+};
+
+sysram@30000000 {
+	compatible = "nvidia,tegra186-sysram", "mmio-sram";
+	reg = <0x0 0x30000000 0x0 0x50000>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+	ranges = <0 0x0 0x0 0x30000000 0x0 0x50000>;
+
+	cpu_bpmp_tx: bpmp_shmem@4e000 {
+		compatible = "nvidia,tegra186-bpmp-shmem";
+		reg = <0x0 0x4e000 0x0 0x1000>;
+	};
+
+	cpu_bpmp_rx: bpmp_shmem@4f000 {
+		compatible = "nvidia,tegra186-bpmp-shmem";
+		reg = <0x0 0x4f000 0x0 0x1000>;
+	};
+};
+
+bpmp {
+	compatible = "nvidia,tegra186-bpmp";
+	mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_BPMP>;
+	shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
diff --git a/include/dt-bindings/clock/tegra186-clock.h b/include/dt-bindings/clock/tegra186-clock.h
new file mode 100644
index 000000000000..f73d32098f99
--- /dev/null
+++ b/include/dt-bindings/clock/tegra186-clock.h
@@ -0,0 +1,940 @@
+/** @file */
+
+#ifndef _MACH_T186_CLK_T186_H
+#define _MACH_T186_CLK_T186_H
+
+/**
+ * @defgroup clock_ids Clock Identifiers
+ * @{
+ *   @defgroup extern_input external input clocks
+ *   @{
+ *     @def TEGRA186_CLK_OSC
+ *     @def TEGRA186_CLK_CLK_32K
+ *     @def TEGRA186_CLK_DTV_INPUT
+ *     @def TEGRA186_CLK_SOR0_PAD_CLKOUT
+ *     @def TEGRA186_CLK_SOR1_PAD_CLKOUT
+ *     @def TEGRA186_CLK_I2S1_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S2_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S3_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S4_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S5_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S6_SYNC_INPUT
+ *     @def TEGRA186_CLK_SPDIFIN_SYNC_INPUT
+ *   @}
+ *
+ *   @defgroup extern_output external output clocks
+ *   @{
+ *     @def TEGRA186_CLK_EXTPERIPH1
+ *     @def TEGRA186_CLK_EXTPERIPH2
+ *     @def TEGRA186_CLK_EXTPERIPH3
+ *     @def TEGRA186_CLK_EXTPERIPH4
+ *   @}
+ *
+ *   @defgroup display_clks display related clocks
+ *   @{
+ *     @def TEGRA186_CLK_CEC
+ *     @def TEGRA186_CLK_DSIC
+ *     @def TEGRA186_CLK_DSIC_LP
+ *     @def TEGRA186_CLK_DSID
+ *     @def TEGRA186_CLK_DSID_LP
+ *     @def TEGRA186_CLK_DPAUX1
+ *     @def TEGRA186_CLK_DPAUX
+ *     @def TEGRA186_CLK_HDA2HDMICODEC
+ *     @def TEGRA186_CLK_NVDISPLAY_DISP
+ *     @def TEGRA186_CLK_NVDISPLAY_DSC
+ *     @def TEGRA186_CLK_NVDISPLAY_P0
+ *     @def TEGRA186_CLK_NVDISPLAY_P1
+ *     @def TEGRA186_CLK_NVDISPLAY_P2
+ *     @def TEGRA186_CLK_NVDISPLAYHUB
+ *     @def TEGRA186_CLK_SOR_SAFE
+ *     @def TEGRA186_CLK_SOR0
+ *     @def TEGRA186_CLK_SOR0_OUT
+ *     @def TEGRA186_CLK_SOR1
+ *     @def TEGRA186_CLK_SOR1_OUT
+ *     @def TEGRA186_CLK_DSI
+ *     @def TEGRA186_CLK_MIPI_CAL
+ *     @def TEGRA186_CLK_DSIA_LP
+ *     @def TEGRA186_CLK_DSIB
+ *     @def TEGRA186_CLK_DSIB_LP
+ *   @}
+ *
+ *   @defgroup camera_clks camera related clocks
+ *   @{
+ *     @def TEGRA186_CLK_NVCSI
+ *     @def TEGRA186_CLK_NVCSILP
+ *     @def TEGRA186_CLK_VI
+ *   @}
+ *
+ *   @defgroup audio_clks audio related clocks
+ *   @{
+ *     @def TEGRA186_CLK_ACLK
+ *     @def TEGRA186_CLK_ADSP
+ *     @def TEGRA186_CLK_ADSPNEON
+ *     @def TEGRA186_CLK_AHUB
+ *     @def TEGRA186_CLK_APE
+ *     @def TEGRA186_CLK_APB2APE
+ *     @def TEGRA186_CLK_AUD_MCLK
+ *     @def TEGRA186_CLK_DMIC1
+ *     @def TEGRA186_CLK_DMIC2
+ *     @def TEGRA186_CLK_DMIC3
+ *     @def TEGRA186_CLK_DMIC4
+ *     @def TEGRA186_CLK_DSPK1
+ *     @def TEGRA186_CLK_DSPK2
+ *     @def TEGRA186_CLK_HDA
+ *     @def TEGRA186_CLK_HDA2CODEC_2X
+ *     @def TEGRA186_CLK_I2S1
+ *     @def TEGRA186_CLK_I2S2
+ *     @def TEGRA186_CLK_I2S3
+ *     @def TEGRA186_CLK_I2S4
+ *     @def TEGRA186_CLK_I2S5
+ *     @def TEGRA186_CLK_I2S6
+ *     @def TEGRA186_CLK_MAUD
+ *     @def TEGRA186_CLK_PLL_A_OUT0
+ *     @def TEGRA186_CLK_SPDIF_DOUBLER
+ *     @def TEGRA186_CLK_SPDIF_IN
+ *     @def TEGRA186_CLK_SPDIF_OUT
+ *     @def TEGRA186_CLK_SYNC_DMIC1
+ *     @def TEGRA186_CLK_SYNC_DMIC2
+ *     @def TEGRA186_CLK_SYNC_DMIC3
+ *     @def TEGRA186_CLK_SYNC_DMIC4
+ *     @def TEGRA186_CLK_SYNC_DMIC5
+ *     @def TEGRA186_CLK_SYNC_DSPK1
+ *     @def TEGRA186_CLK_SYNC_DSPK2
+ *     @def TEGRA186_CLK_SYNC_I2S1
+ *     @def TEGRA186_CLK_SYNC_I2S2
+ *     @def TEGRA186_CLK_SYNC_I2S3
+ *     @def TEGRA186_CLK_SYNC_I2S4
+ *     @def TEGRA186_CLK_SYNC_I2S5
+ *     @def TEGRA186_CLK_SYNC_I2S6
+ *     @def TEGRA186_CLK_SYNC_SPDIF
+ *   @}
+ *
+ *   @defgroup uart_clks UART clocks
+ *   @{
+ *     @def TEGRA186_CLK_AON_UART_FST_MIPI_CAL
+ *     @def TEGRA186_CLK_UARTA
+ *     @def TEGRA186_CLK_UARTB
+ *     @def TEGRA186_CLK_UARTC
+ *     @def TEGRA186_CLK_UARTD
+ *     @def TEGRA186_CLK_UARTE
+ *     @def TEGRA186_CLK_UARTF
+ *     @def TEGRA186_CLK_UARTG
+ *     @def TEGRA186_CLK_UART_FST_MIPI_CAL
+ *   @}
+ *
+ *   @defgroup i2c_clks I2C clocks
+ *   @{
+ *     @def TEGRA186_CLK_AON_I2C_SLOW
+ *     @def TEGRA186_CLK_I2C1
+ *     @def TEGRA186_CLK_I2C2
+ *     @def TEGRA186_CLK_I2C3
+ *     @def TEGRA186_CLK_I2C4
+ *     @def TEGRA186_CLK_I2C5
+ *     @def TEGRA186_CLK_I2C6
+ *     @def TEGRA186_CLK_I2C8
+ *     @def TEGRA186_CLK_I2C9
+ *     @def TEGRA186_CLK_I2C1
+ *     @def TEGRA186_CLK_I2C12
+ *     @def TEGRA186_CLK_I2C13
+ *     @def TEGRA186_CLK_I2C14
+ *     @def TEGRA186_CLK_I2C_SLOW
+ *     @def TEGRA186_CLK_VI_I2C
+ *   @}
+ *
+ *   @defgroup spi_clks SPI clocks
+ *   @{
+ *     @def TEGRA186_CLK_SPI1
+ *     @def TEGRA186_CLK_SPI2
+ *     @def TEGRA186_CLK_SPI3
+ *     @def TEGRA186_CLK_SPI4
+ *   @}
+ *
+ *   @defgroup storage storage related clocks
+ *   @{
+ *     @def TEGRA186_CLK_SATA
+ *     @def TEGRA186_CLK_SATA_OOB
+ *     @def TEGRA186_CLK_SATA_IOBIST
+ *     @def TEGRA186_CLK_SDMMC_LEGACY_TM
+ *     @def TEGRA186_CLK_SDMMC1
+ *     @def TEGRA186_CLK_SDMMC2
+ *     @def TEGRA186_CLK_SDMMC3
+ *     @def TEGRA186_CLK_SDMMC4
+ *     @def TEGRA186_CLK_QSPI
+ *     @def TEGRA186_CLK_QSPI_OUT
+ *     @def TEGRA186_CLK_UFSDEV_REF
+ *     @def TEGRA186_CLK_UFSHC
+ *   @}
+ *
+ *   @defgroup pwm_clks PWM clocks
+ *   @{
+ *     @def TEGRA186_CLK_PWM1
+ *     @def TEGRA186_CLK_PWM2
+ *     @def TEGRA186_CLK_PWM3
+ *     @def TEGRA186_CLK_PWM4
+ *     @def TEGRA186_CLK_PWM5
+ *     @def TEGRA186_CLK_PWM6
+ *     @def TEGRA186_CLK_PWM7
+ *     @def TEGRA186_CLK_PWM8
+ *   @}
+ *
+ *   @defgroup plls PLLs and related clocks
+ *   @{
+ *     @def TEGRA186_CLK_PLLREFE_OUT_GATED
+ *     @def TEGRA186_CLK_PLLREFE_OUT1
+ *     @def TEGRA186_CLK_PLLD_OUT1
+ *     @def TEGRA186_CLK_PLLP_OUT0
+ *     @def TEGRA186_CLK_PLLP_OUT5
+ *     @def TEGRA186_CLK_PLLA
+ *     @def TEGRA186_CLK_PLLE_PWRSEQ
+ *     @def TEGRA186_CLK_PLLA_OUT1
+ *     @def TEGRA186_CLK_PLLREFE_REF
+ *     @def TEGRA186_CLK_UPHY_PLL0_PWRSEQ
+ *     @def TEGRA186_CLK_UPHY_PLL1_PWRSEQ
+ *     @def TEGRA186_CLK_PLLREFE_PLLE_PASSTHROUGH
+ *     @def TEGRA186_CLK_PLLREFE_PEX
+ *     @def TEGRA186_CLK_PLLREFE_IDDQ
+ *     @def TEGRA186_CLK_PLLC_OUT_AON
+ *     @def TEGRA186_CLK_PLLC_OUT_ISP
+ *     @def TEGRA186_CLK_PLLC_OUT_VE
+ *     @def TEGRA186_CLK_PLLC4_OUT
+ *     @def TEGRA186_CLK_PLLREFE_OUT
+ *     @def TEGRA186_CLK_PLLREFE_PLL_REF
+ *     @def TEGRA186_CLK_PLLE
+ *     @def TEGRA186_CLK_PLLC
+ *     @def TEGRA186_CLK_PLLP
+ *     @def TEGRA186_CLK_PLLD
+ *     @def TEGRA186_CLK_PLLD2
+ *     @def TEGRA186_CLK_PLLREFE_VCO
+ *     @def TEGRA186_CLK_PLLC2
+ *     @def TEGRA186_CLK_PLLC3
+ *     @def TEGRA186_CLK_PLLDP
+ *     @def TEGRA186_CLK_PLLC4_VCO
+ *     @def TEGRA186_CLK_PLLA1
+ *     @def TEGRA186_CLK_PLLNVCSI
+ *     @def TEGRA186_CLK_PLLDISPHUB
+ *     @def TEGRA186_CLK_PLLD3
+ *     @def TEGRA186_CLK_PLLBPMPCAM
+ *     @def TEGRA186_CLK_PLLAON
+ *     @def TEGRA186_CLK_PLLU
+ *     @def TEGRA186_CLK_PLLC4_VCO_DIV2
+ *     @def TEGRA186_CLK_PLL_REF
+ *     @def TEGRA186_CLK_PLLREFE_OUT1_DIV5
+ *     @def TEGRA186_CLK_UTMIP_PLL_PWRSEQ
+ *     @def TEGRA186_CLK_PLL_U_48M
+ *     @def TEGRA186_CLK_PLL_U_480M
+ *     @def TEGRA186_CLK_PLLC4_OUT0
+ *     @def TEGRA186_CLK_PLLC4_OUT1
+ *     @def TEGRA186_CLK_PLLC4_OUT2
+ *     @def TEGRA186_CLK_PLLC4_OUT_MUX
+ *     @def TEGRA186_CLK_DFLLDISP_DIV
+ *     @def TEGRA186_CLK_PLLDISPHUB_DIV
+ *     @def TEGRA186_CLK_PLLP_DIV8
+ *   @}
+ *
+ *   @defgroup nafll_clks NAFLL clock sources
+ *   @{
+ *     @def TEGRA186_CLK_NAFLL_AXI_CBB
+ *     @def TEGRA186_CLK_NAFLL_BCPU
+ *     @def TEGRA186_CLK_NAFLL_BPMP
+ *     @def TEGRA186_CLK_NAFLL_DISP
+ *     @def TEGRA186_CLK_NAFLL_GPU
+ *     @def TEGRA186_CLK_NAFLL_ISP
+ *     @def TEGRA186_CLK_NAFLL_MCPU
+ *     @def TEGRA186_CLK_NAFLL_NVDEC
+ *     @def TEGRA186_CLK_NAFLL_NVENC
+ *     @def TEGRA186_CLK_NAFLL_NVJPG
+ *     @def TEGRA186_CLK_NAFLL_SCE
+ *     @def TEGRA186_CLK_NAFLL_SE
+ *     @def TEGRA186_CLK_NAFLL_TSEC
+ *     @def TEGRA186_CLK_NAFLL_TSECB
+ *     @def TEGRA186_CLK_NAFLL_VI
+ *     @def TEGRA186_CLK_NAFLL_VIC
+ *   @}
+ *
+ *   @defgroup mphy MPHY related clocks
+ *   @{
+ *     @def TEGRA186_CLK_MPHY_L0_RX_SYMB
+ *     @def TEGRA186_CLK_MPHY_L0_RX_LS_BIT
+ *     @def TEGRA186_CLK_MPHY_L0_TX_SYMB
+ *     @def TEGRA186_CLK_MPHY_L0_TX_LS_3XBIT
+ *     @def TEGRA186_CLK_MPHY_L0_RX_ANA
+ *     @def TEGRA186_CLK_MPHY_L1_RX_ANA
+ *     @def TEGRA186_CLK_MPHY_IOBIST
+ *     @def TEGRA186_CLK_MPHY_TX_1MHZ_REF
+ *     @def TEGRA186_CLK_MPHY_CORE_PLL_FIXED
+ *   @}
+ *
+ *   @defgroup eavb EAVB related clocks
+ *   @{
+ *     @def TEGRA186_CLK_EQOS_AXI
+ *     @def TEGRA186_CLK_EQOS_PTP_REF
+ *     @def TEGRA186_CLK_EQOS_RX
+ *     @def TEGRA186_CLK_EQOS_RX_INPUT
+ *     @def TEGRA186_CLK_EQOS_TX
+ *   @}
+ *
+ *   @defgroup usb USB related clocks
+ *   @{
+ *     @def TEGRA186_CLK_PEX_USB_PAD0_MGMT
+ *     @def TEGRA186_CLK_PEX_USB_PAD1_MGMT
+ *     @def TEGRA186_CLK_HSIC_TRK
+ *     @def TEGRA186_CLK_USB2_TRK
+ *     @def TEGRA186_CLK_USB2_HSIC_TRK
+ *     @def TEGRA186_CLK_XUSB_CORE_SS
+ *     @def TEGRA186_CLK_XUSB_CORE_DEV
+ *     @def TEGRA186_CLK_XUSB_FALCON
+ *     @def TEGRA186_CLK_XUSB_FS
+ *     @def TEGRA186_CLK_XUSB
+ *     @def TEGRA186_CLK_XUSB_DEV
+ *     @def TEGRA186_CLK_XUSB_HOST
+ *     @def TEGRA186_CLK_XUSB_SS
+ *   @}
+ *
+ *   @defgroup bigblock compute block related clocks
+ *   @{
+ *     @def TEGRA186_CLK_GPCCLK
+ *     @def TEGRA186_CLK_GPC2CLK
+ *     @def TEGRA186_CLK_GPU
+ *     @def TEGRA186_CLK_HOST1X
+ *     @def TEGRA186_CLK_ISP
+ *     @def TEGRA186_CLK_NVDEC
+ *     @def TEGRA186_CLK_NVENC
+ *     @def TEGRA186_CLK_NVJPG
+ *     @def TEGRA186_CLK_SE
+ *     @def TEGRA186_CLK_TSEC
+ *     @def TEGRA186_CLK_TSECB
+ *     @def TEGRA186_CLK_VIC
+ *   @}
+ *
+ *   @defgroup can CAN bus related clocks
+ *   @{
+ *     @def TEGRA186_CLK_CAN1
+ *     @def TEGRA186_CLK_CAN1_HOST
+ *     @def TEGRA186_CLK_CAN2
+ *     @def TEGRA186_CLK_CAN2_HOST
+ *   @}
+ *
+ *   @defgroup system basic system clocks
+ *   @{
+ *     @def TEGRA186_CLK_ACTMON
+ *     @def TEGRA186_CLK_AON_APB
+ *     @def TEGRA186_CLK_AON_CPU_NIC
+ *     @def TEGRA186_CLK_AON_NIC
+ *     @def TEGRA186_CLK_AXI_CBB
+ *     @def TEGRA186_CLK_BPMP_APB
+ *     @def TEGRA186_CLK_BPMP_CPU_NIC
+ *     @def TEGRA186_CLK_BPMP_NIC_RATE
+ *     @def TEGRA186_CLK_CLK_M
+ *     @def TEGRA186_CLK_EMC
+ *     @def TEGRA186_CLK_MSS_ENCRYPT
+ *     @def TEGRA186_CLK_SCE_APB
+ *     @def TEGRA186_CLK_SCE_CPU_NIC
+ *     @def TEGRA186_CLK_SCE_NIC
+ *     @def TEGRA186_CLK_TSC
+ *   @}
+ *
+ *   @defgroup pcie_clks PCIe related clocks
+ *   @{
+ *     @def TEGRA186_CLK_AFI
+ *     @def TEGRA186_CLK_PCIE
+ *     @def TEGRA186_CLK_PCIE2_IOBIST
+ *     @def TEGRA186_CLK_PCIERX0
+ *     @def TEGRA186_CLK_PCIERX1
+ *     @def TEGRA186_CLK_PCIERX2
+ *     @def TEGRA186_CLK_PCIERX3
+ *     @def TEGRA186_CLK_PCIERX4
+ *   @}
+ */
+
+/** @brief output of gate CLK_ENB_FUSE */
+#define TEGRA186_CLK_FUSE 0
+/**
+ * @brief It's not what you think
+ * @details output of gate CLK_ENB_GPU. This output connects to the GPU
+ * pwrclk. @warning: This is almost certainly not the clock you think
+ * it is. If you're looking for the clock of the graphics engine, see
+ * TEGRA186_GPCCLK
+ */
+#define TEGRA186_CLK_GPU 1
+/** @brief output of gate CLK_ENB_PCIE */
+#define TEGRA186_CLK_PCIE 3
+/** @brief output of the divider IPFS_CLK_DIVISOR */
+#define TEGRA186_CLK_AFI 4
+/** @brief output of gate CLK_ENB_PCIE2_IOBIST */
+#define TEGRA186_CLK_PCIE2_IOBIST 5
+/** @brief output of gate CLK_ENB_PCIERX0*/
+#define TEGRA186_CLK_PCIERX0 6
+/** @brief output of gate CLK_ENB_PCIERX1*/
+#define TEGRA186_CLK_PCIERX1 7
+/** @brief output of gate CLK_ENB_PCIERX2*/
+#define TEGRA186_CLK_PCIERX2 8
+/** @brief output of gate CLK_ENB_PCIERX3*/
+#define TEGRA186_CLK_PCIERX3 9
+/** @brief output of gate CLK_ENB_PCIERX4*/
+#define TEGRA186_CLK_PCIERX4 10
+/** @brief output branch of PLL_C for ISP, controlled by gate CLK_ENB_PLLC_OUT_ISP */
+#define TEGRA186_CLK_PLLC_OUT_ISP 11
+/** @brief output branch of PLL_C for VI, controlled by gate CLK_ENB_PLLC_OUT_VE */
+#define TEGRA186_CLK_PLLC_OUT_VE 12
+/** @brief output branch of PLL_C for AON domain, controlled by gate CLK_ENB_PLLC_OUT_AON */
+#define TEGRA186_CLK_PLLC_OUT_AON 13
+/** @brief output of gate CLK_ENB_SOR_SAFE */
+#define TEGRA186_CLK_SOR_SAFE 39
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S2 */
+#define TEGRA186_CLK_I2S2 42
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S3 */
+#define TEGRA186_CLK_I2S3 43
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPDF_IN */
+#define TEGRA186_CLK_SPDIF_IN 44
+/** @brief output of gate CLK_ENB_SPDIF_DOUBLER */
+#define TEGRA186_CLK_SPDIF_DOUBLER 45
+/**  @clkdesc{spi_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_SPI3} */
+#define TEGRA186_CLK_SPI3 46
+/** @clkdesc{i2c_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_I2C1} */
+#define TEGRA186_CLK_I2C1 47
+/** @clkdesc{i2c_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5} */
+#define TEGRA186_CLK_I2C5 48
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI1 */
+#define TEGRA186_CLK_SPI1 49
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_ISP */
+#define TEGRA186_CLK_ISP 50
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VI */
+#define TEGRA186_CLK_VI 51
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 */
+#define TEGRA186_CLK_SDMMC1 52
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 */
+#define TEGRA186_CLK_SDMMC2 53
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 */
+#define TEGRA186_CLK_SDMMC4 54
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTA */
+#define TEGRA186_CLK_UARTA 55
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTB */
+#define TEGRA186_CLK_UARTB 56
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X */
+#define TEGRA186_CLK_HOST1X 57
+/**
+ * @brief controls the EMC clock frequency.
+ * @details Doing a clk_set_rate on this clock will select the
+ * appropriate clock source, program the source rate and execute a
+ * specific sequence to switch to the new clock source for both memory
+ * controllers. This can be used to control the balance between memory
+ * throughput and memory controller power.
+ */
+#define TEGRA186_CLK_EMC 58
+/* @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH4 */
+#define TEGRA186_CLK_EXTPERIPH4 73
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI4 */
+#define TEGRA186_CLK_SPI4 74
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 */
+#define TEGRA186_CLK_I2C3 75
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 */
+#define TEGRA186_CLK_SDMMC3 76
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTD */
+#define TEGRA186_CLK_UARTD 77
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S1 */
+#define TEGRA186_CLK_I2S1 79
+/** output of gate CLK_ENB_DTV */
+#define TEGRA186_CLK_DTV 80
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSEC */
+#define TEGRA186_CLK_TSEC 81
+/** @brief output of gate CLK_ENB_DP2 */
+#define TEGRA186_CLK_DP2 82
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S4 */
+#define TEGRA186_CLK_I2S4 84
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S5 */
+#define TEGRA186_CLK_I2S5 85
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C4 */
+#define TEGRA186_CLK_I2C4 86
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AHUB */
+#define TEGRA186_CLK_AHUB 87
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HDA2CODEC_2X */
+#define TEGRA186_CLK_HDA2CODEC_2X 88
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH1 */
+#define TEGRA186_CLK_EXTPERIPH1 89
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH2 */
+#define TEGRA186_CLK_EXTPERIPH2 90
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH3 */
+#define TEGRA186_CLK_EXTPERIPH3 91
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C_SLOW */
+#define TEGRA186_CLK_I2C_SLOW 92
+/** @brief output of the SOR1_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 */
+#define TEGRA186_CLK_SOR1 93
+/** @brief output of gate CLK_ENB_CEC */
+#define TEGRA186_CLK_CEC 94
+/** @brief output of gate CLK_ENB_DPAUX1 */
+#define TEGRA186_CLK_DPAUX1 95
+/** @brief output of gate CLK_ENB_DPAUX */
+#define TEGRA186_CLK_DPAUX 96
+/** @brief output of the SOR0_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0 */
+#define TEGRA186_CLK_SOR0 97
+/** @brief output of gate CLK_ENB_HDA2HDMICODEC */
+#define TEGRA186_CLK_HDA2HDMICODEC 98
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SATA */
+#define TEGRA186_CLK_SATA 99
+/** @brief output of gate CLK_ENB_SATA_OOB */
+#define TEGRA186_CLK_SATA_OOB 100
+/** @brief output of gate CLK_ENB_SATA_IOBIST */
+#define TEGRA186_CLK_SATA_IOBIST 101
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HDA */
+#define TEGRA186_CLK_HDA 102
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SE */
+#define TEGRA186_CLK_SE 103
+/** @brief output of gate CLK_ENB_APB2APE */
+#define TEGRA186_CLK_APB2APE 104
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_APE */
+#define TEGRA186_CLK_APE 105
+/** @brief output of gate CLK_ENB_IQC1 */
+#define TEGRA186_CLK_IQC1 106
+/** @brief output of gate CLK_ENB_IQC2 */
+#define TEGRA186_CLK_IQC2 107
+/** divide by 2 version of TEGRA186_CLK_PLLREFE_VCO */
+#define TEGRA186_CLK_PLLREFE_OUT 108
+/** @brief output of gate CLK_ENB_PLLREFE_PLL_REF */
+#define TEGRA186_CLK_PLLREFE_PLL_REF 109
+/** @brief output of gate CLK_ENB_PLLC4_OUT */
+#define TEGRA186_CLK_PLLC4_OUT 110
+/** @brief output of mux xusb_core_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB 111
+/** controls xusb_dev_ce signal on page 66 and 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_DEV 112
+/** controls xusb_host_ce signal on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_HOST 113
+/** controls xusb_ss_ce signal on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_SS 114
+/** @brief output of gate CLK_ENB_DSI */
+#define TEGRA186_CLK_DSI 115
+/** @brief output of gate CLK_ENB_MIPI_CAL */
+#define TEGRA186_CLK_MIPI_CAL 116
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP */
+#define TEGRA186_CLK_DSIA_LP 117
+/** @brief output of gate CLK_ENB_DSIB */
+#define TEGRA186_CLK_DSIB 118
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIB_LP */
+#define TEGRA186_CLK_DSIB_LP 119
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC1 */
+#define TEGRA186_CLK_DMIC1 122
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC2 */
+#define TEGRA186_CLK_DMIC2 123
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AUD_MCLK */
+#define TEGRA186_CLK_AUD_MCLK 124
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 */
+#define TEGRA186_CLK_I2C6 125
+/**output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL */
+#define TEGRA186_CLK_UART_FST_MIPI_CAL 126
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VIC */
+#define TEGRA186_CLK_VIC 127
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM */
+#define TEGRA186_CLK_SDMMC_LEGACY_TM 128
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDEC */
+#define TEGRA186_CLK_NVDEC 129
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVJPG */
+#define TEGRA186_CLK_NVJPG 130
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVENC */
+#define TEGRA186_CLK_NVENC 131
+/** @brief output of the QSPI_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_QSPI */
+#define TEGRA186_CLK_QSPI 132
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VI_I2C */
+#define TEGRA186_CLK_VI_I2C 133
+/** @brief output of gate CLK_ENB_HSIC_TRK */
+#define TEGRA186_CLK_HSIC_TRK 134
+/** @brief output of gate CLK_ENB_USB2_TRK */
+#define TEGRA186_CLK_USB2_TRK 135
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_MAUD */
+#define TEGRA186_CLK_MAUD 136
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSECB */
+#define TEGRA186_CLK_TSECB 137
+/** @brief output of gate CLK_ENB_ADSP */
+#define TEGRA186_CLK_ADSP 138
+/** @brief output of gate CLK_ENB_ADSPNEON */
+#define TEGRA186_CLK_ADSPNEON 139
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_L0_RX_LS_SYMB */
+#define TEGRA186_CLK_MPHY_L0_RX_SYMB 140
+/** @brief output of gate CLK_ENB_MPHY_L0_RX_LS_BIT */
+#define TEGRA186_CLK_MPHY_L0_RX_LS_BIT 141
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_L0_TX_LS_SYMB */
+#define TEGRA186_CLK_MPHY_L0_TX_SYMB 142
+/** @brief output of gate CLK_ENB_MPHY_L0_TX_LS_3XBIT */
+#define TEGRA186_CLK_MPHY_L0_TX_LS_3XBIT 143
+/** @brief output of gate CLK_ENB_MPHY_L0_RX_ANA */
+#define TEGRA186_CLK_MPHY_L0_RX_ANA 144
+/** @brief output of gate CLK_ENB_MPHY_L1_RX_ANA */
+#define TEGRA186_CLK_MPHY_L1_RX_ANA 145
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_IOBIST */
+#define TEGRA186_CLK_MPHY_IOBIST 146
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_TX_1MHZ_REF */
+#define TEGRA186_CLK_MPHY_TX_1MHZ_REF 147
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_CORE_PLL_FIXED */
+#define TEGRA186_CLK_MPHY_CORE_PLL_FIXED 148
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AXI_CBB */
+#define TEGRA186_CLK_AXI_CBB 149
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC3 */
+#define TEGRA186_CLK_DMIC3 150
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC4 */
+#define TEGRA186_CLK_DMIC4 151
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSPK1 */
+#define TEGRA186_CLK_DSPK1 152
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSPK2 */
+#define TEGRA186_CLK_DSPK2 153
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 */
+#define TEGRA186_CLK_I2S6 154
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P0 */
+#define TEGRA186_CLK_NVDISPLAY_P0 155
+/** @brief output of the NVDISPLAY_DISP_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP */
+#define TEGRA186_CLK_NVDISPLAY_DISP 156
+/** @brief output of gate CLK_ENB_NVDISPLAY_DSC */
+#define TEGRA186_CLK_NVDISPLAY_DSC 157
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAYHUB */
+#define TEGRA186_CLK_NVDISPLAYHUB 158
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P1 */
+#define TEGRA186_CLK_NVDISPLAY_P1 159
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P2 */
+#define TEGRA186_CLK_NVDISPLAY_P2 160
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TACH */
+#define TEGRA186_CLK_TACH 166
+/** @brief output of gate CLK_ENB_EQOS */
+#define TEGRA186_CLK_EQOS_AXI 167
+/** @brief output of gate CLK_ENB_EQOS_RX */
+#define TEGRA186_CLK_EQOS_RX 168
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UFSHC_CG_SYS */
+#define TEGRA186_CLK_UFSHC 178
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UFSDEV_REF */
+#define TEGRA186_CLK_UFSDEV_REF 179
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVCSI */
+#define TEGRA186_CLK_NVCSI 180
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVCSILP */
+#define TEGRA186_CLK_NVCSILP 181
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C7 */
+#define TEGRA186_CLK_I2C7 182
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C9 */
+#define TEGRA186_CLK_I2C9 183
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C12 */
+#define TEGRA186_CLK_I2C12 184
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C13 */
+#define TEGRA186_CLK_I2C13 185
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C14 */
+#define TEGRA186_CLK_I2C14 186
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM1 */
+#define TEGRA186_CLK_PWM1 187
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM2 */
+#define TEGRA186_CLK_PWM2 188
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM3 */
+#define TEGRA186_CLK_PWM3 189
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM5 */
+#define TEGRA186_CLK_PWM5 190
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM6 */
+#define TEGRA186_CLK_PWM6 191
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM7 */
+#define TEGRA186_CLK_PWM7 192
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM8 */
+#define TEGRA186_CLK_PWM8 193
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTE */
+#define TEGRA186_CLK_UARTE 194
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTF */
+#define TEGRA186_CLK_UARTF 195
+/** @deprecated */
+#define TEGRA186_CLK_DBGAPB 196
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_BPMP_CPU_NIC */
+#define TEGRA186_CLK_BPMP_CPU_NIC 197
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_BPMP_APB */
+#define TEGRA186_CLK_BPMP_APB 199
+/** @brief output of mux controlled by TEGRA186_CLK_SOC_ACTMON */
+#define TEGRA186_CLK_ACTMON 201
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_CPU_NIC */
+#define TEGRA186_CLK_AON_CPU_NIC 208
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_CAN1 */
+#define TEGRA186_CLK_CAN1 210
+/** @brief output of gate CLK_ENB_CAN1_HOST */
+#define TEGRA186_CLK_CAN1_HOST 211
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_CAN2 */
+#define TEGRA186_CLK_CAN2 212
+/** @brief output of gate CLK_ENB_CAN2_HOST */
+#define TEGRA186_CLK_CAN2_HOST 213
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_APB */
+#define TEGRA186_CLK_AON_APB 214
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTC */
+#define TEGRA186_CLK_UARTC 215
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTG */
+#define TEGRA186_CLK_UARTG 216
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_UART_FST_MIPI_CAL */
+#define TEGRA186_CLK_AON_UART_FST_MIPI_CAL 217
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C2 */
+#define TEGRA186_CLK_I2C2 218
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C8 */
+#define TEGRA186_CLK_I2C8 219
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C10 */
+#define TEGRA186_CLK_I2C10 220
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_I2C_SLOW */
+#define TEGRA186_CLK_AON_I2C_SLOW 221
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI2 */
+#define TEGRA186_CLK_SPI2 222
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC5 */
+#define TEGRA186_CLK_DMIC5 223
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_TOUCH */
+#define TEGRA186_CLK_AON_TOUCH 224
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM4 */
+#define TEGRA186_CLK_PWM4 225
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSC. This clock object is read only and is used for all timers in the system. */
+#define TEGRA186_CLK_TSC 226
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_MSS_ENCRYPT */
+#define TEGRA186_CLK_MSS_ENCRYPT 227
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SCE_CPU_NIC */
+#define TEGRA186_CLK_SCE_CPU_NIC 228
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SCE_APB */
+#define TEGRA186_CLK_SCE_APB 230
+/** @brief output of gate CLK_ENB_DSIC */
+#define TEGRA186_CLK_DSIC 231
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIC_LP */
+#define TEGRA186_CLK_DSIC_LP 232
+/** @brief output of gate CLK_ENB_DSID */
+#define TEGRA186_CLK_DSID 233
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSID_LP */
+#define TEGRA186_CLK_DSID_LP 234
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_SATA_USB_RX_BYP */
+#define TEGRA186_CLK_PEX_SATA_USB_RX_BYP 236
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_OUT */
+#define TEGRA186_CLK_SPDIF_OUT 238
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_EQOS_PTP_REF_CLK_0 */
+#define TEGRA186_CLK_EQOS_PTP_REF 239
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_EQOS_TX_CLK */
+#define TEGRA186_CLK_EQOS_TX 240
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK */
+#define TEGRA186_CLK_USB2_HSIC_TRK 241
+/** @brief output of mux xusb_ss_clk_switch on page 66 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_CORE_SS 242
+/** @brief output of mux xusb_core_dev_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_CORE_DEV 243
+/** @brief output of mux xusb_core_falcon_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_FALCON 244
+/** @brief output of mux xusb_fs_clk_switch on page 66 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_FS 245
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLA_OUT */
+#define TEGRA186_CLK_PLL_A_OUT0 246
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S1 */
+#define TEGRA186_CLK_SYNC_I2S1 247
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S2 */
+#define TEGRA186_CLK_SYNC_I2S2 248
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S3 */
+#define TEGRA186_CLK_SYNC_I2S3 249
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S4 */
+#define TEGRA186_CLK_SYNC_I2S4 250
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S5 */
+#define TEGRA186_CLK_SYNC_I2S5 251
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S6 */
+#define TEGRA186_CLK_SYNC_I2S6 252
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DSPK1 */
+#define TEGRA186_CLK_SYNC_DSPK1 253
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DSPK2 */
+#define TEGRA186_CLK_SYNC_DSPK2 254
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC1 */
+#define TEGRA186_CLK_SYNC_DMIC1 255
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC2 */
+#define TEGRA186_CLK_SYNC_DMIC2 256
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC3 */
+#define TEGRA186_CLK_SYNC_DMIC3 257
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC4 */
+#define TEGRA186_CLK_SYNC_DMIC4 259
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_SPDIF */
+#define TEGRA186_CLK_SYNC_SPDIF 260
+/** @brief output of gate CLK_ENB_PLLREFE_OUT */
+#define TEGRA186_CLK_PLLREFE_OUT_GATED 261
+/** @brief output of the divider PLLREFE_DIVP in CLK_RST_CONTROLLER_PLLREFE_BASE. PLLREFE has 2 outputs:
+  *      * VCO/pdiv defined by this clock object
+  *      * VCO/2 defined by TEGRA186_CLK_PLLREFE_OUT
+  */
+#define TEGRA186_CLK_PLLREFE_OUT1 262
+#define TEGRA186_CLK_PLLD_OUT1 267
+/** @brief output of the divider PLLP_DIVP in CLK_RST_CONTROLLER_PLLP_BASE */
+#define TEGRA186_CLK_PLLP_OUT0 269
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLP_OUTC */
+#define TEGRA186_CLK_PLLP_OUT5 270
+/** PLL controlled by CLK_RST_CONTROLLER_PLLA_BASE for use by audio clocks */
+#define TEGRA186_CLK_PLLA 271
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_ACLK_BURST_POLICY divided by the divider controlled by ACLK_CLK_DIVISOR in CLK_RST_CONTROLLER_SUPER_ACLK_DIVIDER */
+#define TEGRA186_CLK_ACLK 273
+/** fixed 48MHz clock divided down from TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLL_U_48M 274
+/** fixed 480MHz clock divided down from TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLL_U_480M 275
+/** @brief output of the divider PLLC4_DIVP in CLK_RST_CONTROLLER_PLLC4_BASE. Output frequency is TEGRA186_CLK_PLLC4_VCO/PLLC4_DIVP */
+#define TEGRA186_CLK_PLLC4_OUT0 276
+/** fixed /3 divider. Output frequency of this clock is TEGRA186_CLK_PLLC4_VCO/3 */
+#define TEGRA186_CLK_PLLC4_OUT1 277
+/** fixed /5 divider. Output frequency of this clock is TEGRA186_CLK_PLLC4_VCO/5 */
+#define TEGRA186_CLK_PLLC4_OUT2 278
+/** @brief output of mux controlled by PLLC4_CLK_SEL in CLK_RST_CONTROLLER_PLLC4_MISC1 */
+#define TEGRA186_CLK_PLLC4_OUT_MUX 279
+/** @brief output of divider NVDISPLAY_DISP_CLK_DIVISOR in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP when DFLLDISP_DIV is selected in NVDISPLAY_DISP_CLK_SRC */
+#define TEGRA186_CLK_DFLLDISP_DIV 284
+/** @brief output of divider NVDISPLAY_DISP_CLK_DIVISOR in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP when PLLDISPHUB_DIV is selected in NVDISPLAY_DISP_CLK_SRC */
+#define TEGRA186_CLK_PLLDISPHUB_DIV 285
+/** fixed /8 divider which is used as the input for TEGRA186_CLK_SOR_SAFE */
+#define TEGRA186_CLK_PLLP_DIV8 286
+/** @brief output of divider CLK_RST_CONTROLLER_BPMP_NIC_RATE */
+#define TEGRA186_CLK_BPMP_NIC 287
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLA1_OUT1 */
+#define TEGRA186_CLK_PLL_A_OUT1 288
+/** @deprecated */
+#define TEGRA186_CLK_GPC2CLK 289
+/** A fake clock which must be enabled during KFUSE read operations to ensure adequate VDD_CORE voltage. */
+#define TEGRA186_CLK_KFUSE 293
+/**
+ * @brief controls the PLLE hardware sequencer.
+ * @details This clock only has enable and disable methods. When the
+ * PLLE hw sequencer is enabled, PLLE, will be enabled or disabled by
+ * hw based on the control signals from the PCIe, SATA and XUSB
+ * clocks. When the PLLE hw sequencer is disabled, the state of PLLE
+ * is controlled by sw using clk_enable/clk_disable on
+ * TEGRA186_CLK_PLLE.
+ */
+#define TEGRA186_CLK_PLLE_PWRSEQ 294
+/** fixed 60MHz clock divided down from, TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLLREFE_REF 295
+/** @brief output of mux controlled by SOR0_CLK_SEL0 and SOR0_CLK_SEL1 in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0 */
+#define TEGRA186_CLK_SOR0_OUT 296
+/** @brief output of mux controlled by SOR1_CLK_SEL0 and SOR1_CLK_SEL1 in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 */
+#define TEGRA186_CLK_SOR1_OUT 297
+/** @brief fixed /5 divider.  Output frequency of this clock is TEGRA186_CLK_PLLREFE_OUT1/5. Used as input for TEGRA186_CLK_EQOS_AXI */
+#define TEGRA186_CLK_PLLREFE_OUT1_DIV5 298
+/** @brief controls the UTMIP_PLL (aka PLLU) hardware sqeuencer */
+#define TEGRA186_CLK_UTMIP_PLL_PWRSEQ 301
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_USB_PAD_PLL0_MGMT */
+#define TEGRA186_CLK_PEX_USB_PAD0_MGMT 302
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_USB_PAD_PLL1_MGMT */
+#define TEGRA186_CLK_PEX_USB_PAD1_MGMT 303
+/** @brief controls the UPHY_PLL0 hardware sqeuencer */
+#define TEGRA186_CLK_UPHY_PLL0_PWRSEQ 304
+/** @brief controls the UPHY_PLL1 hardware sqeuencer */
+#define TEGRA186_CLK_UPHY_PLL1_PWRSEQ 305
+/** @brief control for PLLREFE_IDDQ in CLK_RST_CONTROLLER_PLLREFE_MISC so the bypass output even be used when the PLL is disabled */
+#define TEGRA186_CLK_PLLREFE_PLLE_PASSTHROUGH 306
+/** @brief output of the mux controlled by PLLREFE_SEL_CLKIN_PEX in CLK_RST_CONTROLLER_PLLREFE_MISC */
+#define TEGRA186_CLK_PLLREFE_PEX 307
+/** @brief control for PLLREFE_IDDQ in CLK_RST_CONTROLLER_PLLREFE_MISC to turn on the PLL when enabled */
+#define TEGRA186_CLK_PLLREFE_IDDQ 308
+/** @brief output of the divider QSPI_CLK_DIV2_SEL in CLK_RST_CONTROLLER_CLK_SOURCE_QSPI */
+#define TEGRA186_CLK_QSPI_OUT 309
+/**
+ * @brief GPC2CLK-div-2
+ * @details fixed /2 divider. Output frequency is
+ * TEGRA186_CLK_GPC2CLK/2. The frequency of this clock is the
+ * frequency at which the GPU graphics engine runs. */
+#define TEGRA186_CLK_GPCCLK 310
+/** @brief output of divider CLK_RST_CONTROLLER_AON_NIC_RATE */
+#define TEGRA186_CLK_AON_NIC 450
+/** @brief output of divider CLK_RST_CONTROLLER_SCE_NIC_RATE */
+#define TEGRA186_CLK_SCE_NIC 451
+/** Fixed 100MHz PLL for PCIe, SATA and superspeed USB */
+#define TEGRA186_CLK_PLLE 512
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC_BASE */
+#define TEGRA186_CLK_PLLC 513
+/** Fixed 408MHz PLL for use by peripheral clocks */
+#define TEGRA186_CLK_PLLP 516
+/** @deprecated */
+#define TEGRA186_CLK_PLL_P TEGRA186_CLK_PLLP
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD_BASE for use by DSI */
+#define TEGRA186_CLK_PLLD 518
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD2_BASE for use by HDMI or DP */
+#define TEGRA186_CLK_PLLD2 519
+/**
+ * @brief PLL controlled by CLK_RST_CONTROLLER_PLLREFE_BASE.
+ * @details Note that this clock only controls the VCO output, before
+ * the post-divider. See TEGRA186_CLK_PLLREFE_OUT1 for more
+ * information.
+ */
+#define TEGRA186_CLK_PLLREFE_VCO 520
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC2_BASE */
+#define TEGRA186_CLK_PLLC2 521
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC3_BASE */
+#define TEGRA186_CLK_PLLC3 522
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLDP_BASE for use as the DP link clock */
+#define TEGRA186_CLK_PLLDP 523
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC4_BASE */
+#define TEGRA186_CLK_PLLC4_VCO 524
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLA1_BASE for use by audio clocks */
+#define TEGRA186_CLK_PLLA1 525
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLNVCSI_BASE */
+#define TEGRA186_CLK_PLLNVCSI 526
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLDISPHUB_BASE */
+#define TEGRA186_CLK_PLLDISPHUB 527
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD3_BASE for use by HDMI or DP */
+#define TEGRA186_CLK_PLLD3 528
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLBPMPCAM_BASE */
+#define TEGRA186_CLK_PLLBPMPCAM 531
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLAON_BASE for use by IP blocks in the AON domain */
+#define TEGRA186_CLK_PLLAON 532
+/** Fixed frequency 960MHz PLL for USB and EAVB */
+#define TEGRA186_CLK_PLLU 533
+/** fixed /2 divider. Output frequency is TEGRA186_CLK_PLLC4_VCO/2 */
+#define TEGRA186_CLK_PLLC4_VCO_DIV2 535
+/** @brief NAFLL clock source for AXI_CBB */
+#define TEGRA186_CLK_NAFLL_AXI_CBB 564
+/** @brief NAFLL clock source for BPMP */
+#define TEGRA186_CLK_NAFLL_BPMP 565
+/** @brief NAFLL clock source for ISP */
+#define TEGRA186_CLK_NAFLL_ISP 566
+/** @brief NAFLL clock source for NVDEC */
+#define TEGRA186_CLK_NAFLL_NVDEC 567
+/** @brief NAFLL clock source for NVENC */
+#define TEGRA186_CLK_NAFLL_NVENC 568
+/** @brief NAFLL clock source for NVJPG */
+#define TEGRA186_CLK_NAFLL_NVJPG 569
+/** @brief NAFLL clock source for SCE */
+#define TEGRA186_CLK_NAFLL_SCE 570
+/** @brief NAFLL clock source for SE */
+#define TEGRA186_CLK_NAFLL_SE 571
+/** @brief NAFLL clock source for TSEC */
+#define TEGRA186_CLK_NAFLL_TSEC 572
+/** @brief NAFLL clock source for TSECB */
+#define TEGRA186_CLK_NAFLL_TSECB 573
+/** @brief NAFLL clock source for VI */
+#define TEGRA186_CLK_NAFLL_VI 574
+/** @brief NAFLL clock source for VIC */
+#define TEGRA186_CLK_NAFLL_VIC 575
+/** @brief NAFLL clock source for DISP */
+#define TEGRA186_CLK_NAFLL_DISP 576
+/** @brief NAFLL clock source for GPU */
+#define TEGRA186_CLK_NAFLL_GPU 577
+/** @brief NAFLL clock source for M-CPU cluster */
+#define TEGRA186_CLK_NAFLL_MCPU 578
+/** @brief NAFLL clock source for B-CPU cluster */
+#define TEGRA186_CLK_NAFLL_BCPU 579
+/** @brief input from Tegra's CLK_32K_IN pad */
+#define TEGRA186_CLK_CLK_32K 608
+/** @brief output of divider CLK_RST_CONTROLLER_CLK_M_DIVIDE */
+#define TEGRA186_CLK_CLK_M 609
+/** @brief output of divider PLL_REF_DIV in CLK_RST_CONTROLLER_OSC_CTRL */
+#define TEGRA186_CLK_PLL_REF 610
+/** @brief input from Tegra's XTAL_IN */
+#define TEGRA186_CLK_OSC 612
+/** @brief clock recovered from EAVB input */
+#define TEGRA186_CLK_EQOS_RX_INPUT 613
+/** @brief clock recovered from DTV input */
+#define TEGRA186_CLK_DTV_INPUT 614
+/** @brief SOR0 brick output which feeds into SOR0_CLK_SEL mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0*/
+#define TEGRA186_CLK_SOR0_PAD_CLKOUT 615
+/** @brief SOR1 brick output which feeds into SOR1_CLK_SEL mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1*/
+#define TEGRA186_CLK_SOR1_PAD_CLKOUT 616
+/** @brief clock recovered from I2S1 input */
+#define TEGRA186_CLK_I2S1_SYNC_INPUT 617
+/** @brief clock recovered from I2S2 input */
+#define TEGRA186_CLK_I2S2_SYNC_INPUT 618
+/** @brief clock recovered from I2S3 input */
+#define TEGRA186_CLK_I2S3_SYNC_INPUT 619
+/** @brief clock recovered from I2S4 input */
+#define TEGRA186_CLK_I2S4_SYNC_INPUT 620
+/** @brief clock recovered from I2S5 input */
+#define TEGRA186_CLK_I2S5_SYNC_INPUT 621
+/** @brief clock recovered from I2S6 input */
+#define TEGRA186_CLK_I2S6_SYNC_INPUT 622
+/** @brief clock recovered from SPDIFIN input */
+#define TEGRA186_CLK_SPDIFIN_SYNC_INPUT 623
+
+/**
+ * @brief subject to change
+ * @details maximum clock identifier value plus one.
+ */
+#define TEGRA186_CLK_CLK_MAX 624
+
+/** @} */
+
+#endif
diff --git a/include/dt-bindings/reset/tegra186-reset.h b/include/dt-bindings/reset/tegra186-reset.h
new file mode 100644
index 000000000000..8a184e357955
--- /dev/null
+++ b/include/dt-bindings/reset/tegra186-reset.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 _ABI_MACH_T186_RESET_T186_H_
+#define _ABI_MACH_T186_RESET_T186_H_
+
+
+#define TEGRA186_RESET_ACTMON			0
+#define TEGRA186_RESET_AFI			1
+#define TEGRA186_RESET_CEC			2
+#define TEGRA186_RESET_CSITE			3
+#define TEGRA186_RESET_DP2			4
+#define TEGRA186_RESET_DPAUX			5
+#define TEGRA186_RESET_DSI			6
+#define TEGRA186_RESET_DSIB			7
+#define TEGRA186_RESET_DTV			8
+#define TEGRA186_RESET_DVFS			9
+#define TEGRA186_RESET_ENTROPY			10
+#define TEGRA186_RESET_EXTPERIPH1		11
+#define TEGRA186_RESET_EXTPERIPH2		12
+#define TEGRA186_RESET_EXTPERIPH3		13
+#define TEGRA186_RESET_GPU			14
+#define TEGRA186_RESET_HDA			15
+#define TEGRA186_RESET_HDA2CODEC_2X		16
+#define TEGRA186_RESET_HDA2HDMICODEC		17
+#define TEGRA186_RESET_HOST1X			18
+#define TEGRA186_RESET_I2C1			19
+#define TEGRA186_RESET_I2C2			20
+#define TEGRA186_RESET_I2C3			21
+#define TEGRA186_RESET_I2C4			22
+#define TEGRA186_RESET_I2C5			23
+#define TEGRA186_RESET_I2C6			24
+#define TEGRA186_RESET_ISP			25
+#define TEGRA186_RESET_KFUSE			26
+#define TEGRA186_RESET_LA			27
+#define TEGRA186_RESET_MIPI_CAL			28
+#define TEGRA186_RESET_PCIE			29
+#define TEGRA186_RESET_PCIEXCLK			30
+#define TEGRA186_RESET_SATA			31
+#define TEGRA186_RESET_SATACOLD			32
+#define TEGRA186_RESET_SDMMC1			33
+#define TEGRA186_RESET_SDMMC2			34
+#define TEGRA186_RESET_SDMMC3			35
+#define TEGRA186_RESET_SDMMC4			36
+#define TEGRA186_RESET_SE			37
+#define TEGRA186_RESET_SOC_THERM		38
+#define TEGRA186_RESET_SOR0			39
+#define TEGRA186_RESET_SPI1			40
+#define TEGRA186_RESET_SPI2			41
+#define TEGRA186_RESET_SPI3			42
+#define TEGRA186_RESET_SPI4			43
+#define TEGRA186_RESET_TMR			44
+#define TEGRA186_RESET_TRIG_SYS			45
+#define TEGRA186_RESET_TSEC			46
+#define TEGRA186_RESET_UARTA			47
+#define TEGRA186_RESET_UARTB			48
+#define TEGRA186_RESET_UARTC			49
+#define TEGRA186_RESET_UARTD			50
+#define TEGRA186_RESET_VI			51
+#define TEGRA186_RESET_VIC			52
+#define TEGRA186_RESET_XUSB_DEV			53
+#define TEGRA186_RESET_XUSB_HOST		54
+#define TEGRA186_RESET_XUSB_PADCTL		55
+#define TEGRA186_RESET_XUSB_SS			56
+#define TEGRA186_RESET_AON_APB			57
+#define TEGRA186_RESET_AXI_CBB			58
+#define TEGRA186_RESET_BPMP_APB			59
+#define TEGRA186_RESET_CAN1			60
+#define TEGRA186_RESET_CAN2			61
+#define TEGRA186_RESET_DMIC5			62
+#define TEGRA186_RESET_DSIC			63
+#define TEGRA186_RESET_DSID			64
+#define TEGRA186_RESET_EMC_EMC			65
+#define TEGRA186_RESET_EMC_MEM			66
+#define TEGRA186_RESET_EMCSB_EMC		67
+#define TEGRA186_RESET_EMCSB_MEM		68
+#define TEGRA186_RESET_EQOS			69
+#define TEGRA186_RESET_GPCDMA			70
+#define TEGRA186_RESET_GPIO_CTL0		71
+#define TEGRA186_RESET_GPIO_CTL1		72
+#define TEGRA186_RESET_GPIO_CTL2		73
+#define TEGRA186_RESET_GPIO_CTL3		74
+#define TEGRA186_RESET_GPIO_CTL4		75
+#define TEGRA186_RESET_GPIO_CTL5		76
+#define TEGRA186_RESET_I2C10			77
+#define TEGRA186_RESET_I2C12			78
+#define TEGRA186_RESET_I2C13			79
+#define TEGRA186_RESET_I2C14			80
+#define TEGRA186_RESET_I2C7			81
+#define TEGRA186_RESET_I2C8			82
+#define TEGRA186_RESET_I2C9			83
+#define TEGRA186_RESET_JTAG2AXI			84
+#define TEGRA186_RESET_MPHY_IOBIST		85
+#define TEGRA186_RESET_MPHY_L0_RX		86
+#define TEGRA186_RESET_MPHY_L0_TX		87
+#define TEGRA186_RESET_NVCSI			88
+#define TEGRA186_RESET_NVDISPLAY0_HEAD0		89
+#define TEGRA186_RESET_NVDISPLAY0_HEAD1		90
+#define TEGRA186_RESET_NVDISPLAY0_HEAD2		91
+#define TEGRA186_RESET_NVDISPLAY0_MISC		92
+#define TEGRA186_RESET_NVDISPLAY0_WGRP0		93
+#define TEGRA186_RESET_NVDISPLAY0_WGRP1		94
+#define TEGRA186_RESET_NVDISPLAY0_WGRP2		95
+#define TEGRA186_RESET_NVDISPLAY0_WGRP3		96
+#define TEGRA186_RESET_NVDISPLAY0_WGRP4		97
+#define TEGRA186_RESET_NVDISPLAY0_WGRP5		98
+#define TEGRA186_RESET_PWM1			99
+#define TEGRA186_RESET_PWM2			100
+#define TEGRA186_RESET_PWM3			101
+#define TEGRA186_RESET_PWM4			102
+#define TEGRA186_RESET_PWM5			103
+#define TEGRA186_RESET_PWM6			104
+#define TEGRA186_RESET_PWM7			105
+#define TEGRA186_RESET_PWM8			106
+#define TEGRA186_RESET_SCE_APB			107
+#define TEGRA186_RESET_SOR1			108
+#define TEGRA186_RESET_TACH			109
+#define TEGRA186_RESET_TSC			110
+#define TEGRA186_RESET_UARTF			111
+#define TEGRA186_RESET_UARTG			112
+#define TEGRA186_RESET_UFSHC			113
+#define TEGRA186_RESET_UFSHC_AXI_M		114
+#define TEGRA186_RESET_UPHY			115
+#define TEGRA186_RESET_ADSP			116
+#define TEGRA186_RESET_ADSPDBG			117
+#define TEGRA186_RESET_ADSPINTF			118
+#define TEGRA186_RESET_ADSPNEON			119
+#define TEGRA186_RESET_ADSPPERIPH		120
+#define TEGRA186_RESET_ADSPSCU			121
+#define TEGRA186_RESET_ADSPWDT			122
+#define TEGRA186_RESET_APE			123
+#define TEGRA186_RESET_DPAUX1			124
+#define TEGRA186_RESET_NVDEC			125
+#define TEGRA186_RESET_NVENC			126
+#define TEGRA186_RESET_NVJPG			127
+#define TEGRA186_RESET_PEX_USB_UPHY		128
+#define TEGRA186_RESET_QSPI			129
+#define TEGRA186_RESET_TSECB			130
+#define TEGRA186_RESET_VI_I2C			131
+#define TEGRA186_RESET_UARTE			132
+#define TEGRA186_RESET_TOP_GTE			133
+#define TEGRA186_RESET_SHSP			134
+#define TEGRA186_RESET_PEX_USB_UPHY_L5		135
+#define TEGRA186_RESET_PEX_USB_UPHY_L4		136
+#define TEGRA186_RESET_PEX_USB_UPHY_L3		137
+#define TEGRA186_RESET_PEX_USB_UPHY_L2		138
+#define TEGRA186_RESET_PEX_USB_UPHY_L1		139
+#define TEGRA186_RESET_PEX_USB_UPHY_L0		140
+#define TEGRA186_RESET_PEX_USB_UPHY_PLL1	141
+#define TEGRA186_RESET_PEX_USB_UPHY_PLL0	142
+#define TEGRA186_RESET_TSCTNVI			143
+#define TEGRA186_RESET_EXTPERIPH4		144
+#define TEGRA186_RESET_DSIPADCTL		145
+#define TEGRA186_RESET_AUD_MCLK			146
+#define TEGRA186_RESET_MPHY_CLK_CTL		147
+#define TEGRA186_RESET_MPHY_L1_RX		148
+#define TEGRA186_RESET_MPHY_L1_TX		149
+#define TEGRA186_RESET_UFSHC_LP			150
+#define TEGRA186_RESET_BPMP_NIC			151
+#define TEGRA186_RESET_BPMP_NSYSPORESET		152
+#define TEGRA186_RESET_BPMP_NRESET		153
+#define TEGRA186_RESET_BPMP_DBGRESETN		154
+#define TEGRA186_RESET_BPMP_PRESETDBGN		155
+#define TEGRA186_RESET_BPMP_PM			156
+#define TEGRA186_RESET_BPMP_CVC			157
+#define TEGRA186_RESET_BPMP_DMA			158
+#define TEGRA186_RESET_BPMP_HSP			159
+#define TEGRA186_RESET_TSCTNBPMP		160
+#define TEGRA186_RESET_BPMP_TKE			161
+#define TEGRA186_RESET_BPMP_GTE			162
+#define TEGRA186_RESET_BPMP_PM_ACTMON		163
+#define TEGRA186_RESET_AON_NIC			164
+#define TEGRA186_RESET_AON_NSYSPORESET		165
+#define TEGRA186_RESET_AON_NRESET		166
+#define TEGRA186_RESET_AON_DBGRESETN		167
+#define TEGRA186_RESET_AON_PRESETDBGN		168
+#define TEGRA186_RESET_AON_ACTMON		169
+#define TEGRA186_RESET_AOPM			170
+#define TEGRA186_RESET_AOVC			171
+#define TEGRA186_RESET_AON_DMA			172
+#define TEGRA186_RESET_AON_GPIO			173
+#define TEGRA186_RESET_AON_HSP			174
+#define TEGRA186_RESET_TSCTNAON			175
+#define TEGRA186_RESET_AON_TKE			176
+#define TEGRA186_RESET_AON_GTE			177
+#define TEGRA186_RESET_SCE_NIC			178
+#define TEGRA186_RESET_SCE_NSYSPORESET		179
+#define TEGRA186_RESET_SCE_NRESET		180
+#define TEGRA186_RESET_SCE_DBGRESETN		181
+#define TEGRA186_RESET_SCE_PRESETDBGN		182
+#define TEGRA186_RESET_SCE_ACTMON		183
+#define TEGRA186_RESET_SCE_PM			184
+#define TEGRA186_RESET_SCE_DMA			185
+#define TEGRA186_RESET_SCE_HSP			186
+#define TEGRA186_RESET_TSCTNSCE			187
+#define TEGRA186_RESET_SCE_TKE			188
+#define TEGRA186_RESET_SCE_GTE			189
+#define TEGRA186_RESET_SCE_CFG			190
+#define TEGRA186_RESET_ADSP_ALL			191
+/** @brief controls the power up/down sequence of UFSHC PSW partition. Controls LP_PWR_READY, LP_ISOL_EN, and LP_RESET_N signals */
+#define TEGRA186_RESET_UFSHC_LP_SEQ		192
+#define TEGRA186_RESET_SIZE			193
+
+#endif
-- 
2.9.0

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

* [PATCH v3 03/12] dt-bindings: firmware: Add bindings for Tegra BPMP
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

The Boot and Power Management Processor (BPMP) is a co-processor found
in Tegra SoCs. It is designed to handle the early stages of the boot
process as well as to offload power management tasks (such as clocks,
resets, powergates, ...).

The binding document defines the resources that are used by the BPMP
firmware, which implements the interprocessor communication (IPC)
between the CPU and the BPMP.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Acked-by: Stephen Warren <swarren@nvidia.com>
Acked-by: Rob Herring <robh@kernel.org>
Acked-by: Jon Hunter <jonathanh@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 .../bindings/firmware/nvidia,tegra186-bpmp.txt     |  77 ++
 include/dt-bindings/clock/tegra186-clock.h         | 940 +++++++++++++++++++++
 include/dt-bindings/reset/tegra186-reset.h         | 217 +++++
 3 files changed, 1234 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
 create mode 100644 include/dt-bindings/clock/tegra186-clock.h
 create mode 100644 include/dt-bindings/reset/tegra186-reset.h

diff --git a/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
new file mode 100644
index 000000000000..14d7fd035562
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/nvidia,tegra186-bpmp.txt
@@ -0,0 +1,77 @@
+NVIDIA Tegra Boot and Power Management Processor (BPMP)
+
+The BPMP is a specific processor in Tegra chip, which is designed for
+booting process handling and offloading the power management, clock
+management, and reset control tasks from the CPU. The binding document
+defines the resources that would be used by the BPMP firmware driver,
+which can create the interprocessor communication (IPC) between the CPU
+and BPMP.
+
+Required properties:
+- name : Should be bpmp
+- compatible
+    Array of strings
+    One of:
+    - "nvidia,tegra186-bpmp"
+- mboxes : The phandle of mailbox controller and the mailbox specifier.
+- shmem : List of the phandle of the TX and RX shared memory area that
+	  the IPC between CPU and BPMP is based on.
+- #clock-cells : Should be 1.
+- #reset-cells : Should be 1.
+
+This node is a mailbox consumer. See the following files for details of
+the mailbox subsystem, and the specifiers implemented by the relevant
+provider(s):
+
+- .../mailbox/mailbox.txt
+- .../mailbox/nvidia,tegra186-hsp.txt
+
+This node is a clock and reset provider. See the following files for
+general documentation of those features, and the specifiers implemented
+by this node:
+
+- .../clock/clock-bindings.txt
+- <dt-bindings/clock/tegra186-clock.h>
+- .../reset/reset.txt
+- <dt-bindings/reset/tegra186-reset.h>
+
+The shared memory bindings for BPMP
+-----------------------------------
+
+The shared memory area for the IPC TX and RX between CPU and BPMP are
+predefined and work on top of sysram, which is an SRAM inside the chip.
+
+See ".../sram/sram.txt" for the bindings.
+
+Example:
+
+hsp_top0: hsp at 03c00000 {
+	...
+	#mbox-cells = <2>;
+};
+
+sysram at 30000000 {
+	compatible = "nvidia,tegra186-sysram", "mmio-sram";
+	reg = <0x0 0x30000000 0x0 0x50000>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+	ranges = <0 0x0 0x0 0x30000000 0x0 0x50000>;
+
+	cpu_bpmp_tx: bpmp_shmem at 4e000 {
+		compatible = "nvidia,tegra186-bpmp-shmem";
+		reg = <0x0 0x4e000 0x0 0x1000>;
+	};
+
+	cpu_bpmp_rx: bpmp_shmem at 4f000 {
+		compatible = "nvidia,tegra186-bpmp-shmem";
+		reg = <0x0 0x4f000 0x0 0x1000>;
+	};
+};
+
+bpmp {
+	compatible = "nvidia,tegra186-bpmp";
+	mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB TEGRA_HSP_DB_MASTER_BPMP>;
+	shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
+	#clock-cells = <1>;
+	#reset-cells = <1>;
+};
diff --git a/include/dt-bindings/clock/tegra186-clock.h b/include/dt-bindings/clock/tegra186-clock.h
new file mode 100644
index 000000000000..f73d32098f99
--- /dev/null
+++ b/include/dt-bindings/clock/tegra186-clock.h
@@ -0,0 +1,940 @@
+/** @file */
+
+#ifndef _MACH_T186_CLK_T186_H
+#define _MACH_T186_CLK_T186_H
+
+/**
+ * @defgroup clock_ids Clock Identifiers
+ * @{
+ *   @defgroup extern_input external input clocks
+ *   @{
+ *     @def TEGRA186_CLK_OSC
+ *     @def TEGRA186_CLK_CLK_32K
+ *     @def TEGRA186_CLK_DTV_INPUT
+ *     @def TEGRA186_CLK_SOR0_PAD_CLKOUT
+ *     @def TEGRA186_CLK_SOR1_PAD_CLKOUT
+ *     @def TEGRA186_CLK_I2S1_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S2_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S3_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S4_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S5_SYNC_INPUT
+ *     @def TEGRA186_CLK_I2S6_SYNC_INPUT
+ *     @def TEGRA186_CLK_SPDIFIN_SYNC_INPUT
+ *   @}
+ *
+ *   @defgroup extern_output external output clocks
+ *   @{
+ *     @def TEGRA186_CLK_EXTPERIPH1
+ *     @def TEGRA186_CLK_EXTPERIPH2
+ *     @def TEGRA186_CLK_EXTPERIPH3
+ *     @def TEGRA186_CLK_EXTPERIPH4
+ *   @}
+ *
+ *   @defgroup display_clks display related clocks
+ *   @{
+ *     @def TEGRA186_CLK_CEC
+ *     @def TEGRA186_CLK_DSIC
+ *     @def TEGRA186_CLK_DSIC_LP
+ *     @def TEGRA186_CLK_DSID
+ *     @def TEGRA186_CLK_DSID_LP
+ *     @def TEGRA186_CLK_DPAUX1
+ *     @def TEGRA186_CLK_DPAUX
+ *     @def TEGRA186_CLK_HDA2HDMICODEC
+ *     @def TEGRA186_CLK_NVDISPLAY_DISP
+ *     @def TEGRA186_CLK_NVDISPLAY_DSC
+ *     @def TEGRA186_CLK_NVDISPLAY_P0
+ *     @def TEGRA186_CLK_NVDISPLAY_P1
+ *     @def TEGRA186_CLK_NVDISPLAY_P2
+ *     @def TEGRA186_CLK_NVDISPLAYHUB
+ *     @def TEGRA186_CLK_SOR_SAFE
+ *     @def TEGRA186_CLK_SOR0
+ *     @def TEGRA186_CLK_SOR0_OUT
+ *     @def TEGRA186_CLK_SOR1
+ *     @def TEGRA186_CLK_SOR1_OUT
+ *     @def TEGRA186_CLK_DSI
+ *     @def TEGRA186_CLK_MIPI_CAL
+ *     @def TEGRA186_CLK_DSIA_LP
+ *     @def TEGRA186_CLK_DSIB
+ *     @def TEGRA186_CLK_DSIB_LP
+ *   @}
+ *
+ *   @defgroup camera_clks camera related clocks
+ *   @{
+ *     @def TEGRA186_CLK_NVCSI
+ *     @def TEGRA186_CLK_NVCSILP
+ *     @def TEGRA186_CLK_VI
+ *   @}
+ *
+ *   @defgroup audio_clks audio related clocks
+ *   @{
+ *     @def TEGRA186_CLK_ACLK
+ *     @def TEGRA186_CLK_ADSP
+ *     @def TEGRA186_CLK_ADSPNEON
+ *     @def TEGRA186_CLK_AHUB
+ *     @def TEGRA186_CLK_APE
+ *     @def TEGRA186_CLK_APB2APE
+ *     @def TEGRA186_CLK_AUD_MCLK
+ *     @def TEGRA186_CLK_DMIC1
+ *     @def TEGRA186_CLK_DMIC2
+ *     @def TEGRA186_CLK_DMIC3
+ *     @def TEGRA186_CLK_DMIC4
+ *     @def TEGRA186_CLK_DSPK1
+ *     @def TEGRA186_CLK_DSPK2
+ *     @def TEGRA186_CLK_HDA
+ *     @def TEGRA186_CLK_HDA2CODEC_2X
+ *     @def TEGRA186_CLK_I2S1
+ *     @def TEGRA186_CLK_I2S2
+ *     @def TEGRA186_CLK_I2S3
+ *     @def TEGRA186_CLK_I2S4
+ *     @def TEGRA186_CLK_I2S5
+ *     @def TEGRA186_CLK_I2S6
+ *     @def TEGRA186_CLK_MAUD
+ *     @def TEGRA186_CLK_PLL_A_OUT0
+ *     @def TEGRA186_CLK_SPDIF_DOUBLER
+ *     @def TEGRA186_CLK_SPDIF_IN
+ *     @def TEGRA186_CLK_SPDIF_OUT
+ *     @def TEGRA186_CLK_SYNC_DMIC1
+ *     @def TEGRA186_CLK_SYNC_DMIC2
+ *     @def TEGRA186_CLK_SYNC_DMIC3
+ *     @def TEGRA186_CLK_SYNC_DMIC4
+ *     @def TEGRA186_CLK_SYNC_DMIC5
+ *     @def TEGRA186_CLK_SYNC_DSPK1
+ *     @def TEGRA186_CLK_SYNC_DSPK2
+ *     @def TEGRA186_CLK_SYNC_I2S1
+ *     @def TEGRA186_CLK_SYNC_I2S2
+ *     @def TEGRA186_CLK_SYNC_I2S3
+ *     @def TEGRA186_CLK_SYNC_I2S4
+ *     @def TEGRA186_CLK_SYNC_I2S5
+ *     @def TEGRA186_CLK_SYNC_I2S6
+ *     @def TEGRA186_CLK_SYNC_SPDIF
+ *   @}
+ *
+ *   @defgroup uart_clks UART clocks
+ *   @{
+ *     @def TEGRA186_CLK_AON_UART_FST_MIPI_CAL
+ *     @def TEGRA186_CLK_UARTA
+ *     @def TEGRA186_CLK_UARTB
+ *     @def TEGRA186_CLK_UARTC
+ *     @def TEGRA186_CLK_UARTD
+ *     @def TEGRA186_CLK_UARTE
+ *     @def TEGRA186_CLK_UARTF
+ *     @def TEGRA186_CLK_UARTG
+ *     @def TEGRA186_CLK_UART_FST_MIPI_CAL
+ *   @}
+ *
+ *   @defgroup i2c_clks I2C clocks
+ *   @{
+ *     @def TEGRA186_CLK_AON_I2C_SLOW
+ *     @def TEGRA186_CLK_I2C1
+ *     @def TEGRA186_CLK_I2C2
+ *     @def TEGRA186_CLK_I2C3
+ *     @def TEGRA186_CLK_I2C4
+ *     @def TEGRA186_CLK_I2C5
+ *     @def TEGRA186_CLK_I2C6
+ *     @def TEGRA186_CLK_I2C8
+ *     @def TEGRA186_CLK_I2C9
+ *     @def TEGRA186_CLK_I2C1
+ *     @def TEGRA186_CLK_I2C12
+ *     @def TEGRA186_CLK_I2C13
+ *     @def TEGRA186_CLK_I2C14
+ *     @def TEGRA186_CLK_I2C_SLOW
+ *     @def TEGRA186_CLK_VI_I2C
+ *   @}
+ *
+ *   @defgroup spi_clks SPI clocks
+ *   @{
+ *     @def TEGRA186_CLK_SPI1
+ *     @def TEGRA186_CLK_SPI2
+ *     @def TEGRA186_CLK_SPI3
+ *     @def TEGRA186_CLK_SPI4
+ *   @}
+ *
+ *   @defgroup storage storage related clocks
+ *   @{
+ *     @def TEGRA186_CLK_SATA
+ *     @def TEGRA186_CLK_SATA_OOB
+ *     @def TEGRA186_CLK_SATA_IOBIST
+ *     @def TEGRA186_CLK_SDMMC_LEGACY_TM
+ *     @def TEGRA186_CLK_SDMMC1
+ *     @def TEGRA186_CLK_SDMMC2
+ *     @def TEGRA186_CLK_SDMMC3
+ *     @def TEGRA186_CLK_SDMMC4
+ *     @def TEGRA186_CLK_QSPI
+ *     @def TEGRA186_CLK_QSPI_OUT
+ *     @def TEGRA186_CLK_UFSDEV_REF
+ *     @def TEGRA186_CLK_UFSHC
+ *   @}
+ *
+ *   @defgroup pwm_clks PWM clocks
+ *   @{
+ *     @def TEGRA186_CLK_PWM1
+ *     @def TEGRA186_CLK_PWM2
+ *     @def TEGRA186_CLK_PWM3
+ *     @def TEGRA186_CLK_PWM4
+ *     @def TEGRA186_CLK_PWM5
+ *     @def TEGRA186_CLK_PWM6
+ *     @def TEGRA186_CLK_PWM7
+ *     @def TEGRA186_CLK_PWM8
+ *   @}
+ *
+ *   @defgroup plls PLLs and related clocks
+ *   @{
+ *     @def TEGRA186_CLK_PLLREFE_OUT_GATED
+ *     @def TEGRA186_CLK_PLLREFE_OUT1
+ *     @def TEGRA186_CLK_PLLD_OUT1
+ *     @def TEGRA186_CLK_PLLP_OUT0
+ *     @def TEGRA186_CLK_PLLP_OUT5
+ *     @def TEGRA186_CLK_PLLA
+ *     @def TEGRA186_CLK_PLLE_PWRSEQ
+ *     @def TEGRA186_CLK_PLLA_OUT1
+ *     @def TEGRA186_CLK_PLLREFE_REF
+ *     @def TEGRA186_CLK_UPHY_PLL0_PWRSEQ
+ *     @def TEGRA186_CLK_UPHY_PLL1_PWRSEQ
+ *     @def TEGRA186_CLK_PLLREFE_PLLE_PASSTHROUGH
+ *     @def TEGRA186_CLK_PLLREFE_PEX
+ *     @def TEGRA186_CLK_PLLREFE_IDDQ
+ *     @def TEGRA186_CLK_PLLC_OUT_AON
+ *     @def TEGRA186_CLK_PLLC_OUT_ISP
+ *     @def TEGRA186_CLK_PLLC_OUT_VE
+ *     @def TEGRA186_CLK_PLLC4_OUT
+ *     @def TEGRA186_CLK_PLLREFE_OUT
+ *     @def TEGRA186_CLK_PLLREFE_PLL_REF
+ *     @def TEGRA186_CLK_PLLE
+ *     @def TEGRA186_CLK_PLLC
+ *     @def TEGRA186_CLK_PLLP
+ *     @def TEGRA186_CLK_PLLD
+ *     @def TEGRA186_CLK_PLLD2
+ *     @def TEGRA186_CLK_PLLREFE_VCO
+ *     @def TEGRA186_CLK_PLLC2
+ *     @def TEGRA186_CLK_PLLC3
+ *     @def TEGRA186_CLK_PLLDP
+ *     @def TEGRA186_CLK_PLLC4_VCO
+ *     @def TEGRA186_CLK_PLLA1
+ *     @def TEGRA186_CLK_PLLNVCSI
+ *     @def TEGRA186_CLK_PLLDISPHUB
+ *     @def TEGRA186_CLK_PLLD3
+ *     @def TEGRA186_CLK_PLLBPMPCAM
+ *     @def TEGRA186_CLK_PLLAON
+ *     @def TEGRA186_CLK_PLLU
+ *     @def TEGRA186_CLK_PLLC4_VCO_DIV2
+ *     @def TEGRA186_CLK_PLL_REF
+ *     @def TEGRA186_CLK_PLLREFE_OUT1_DIV5
+ *     @def TEGRA186_CLK_UTMIP_PLL_PWRSEQ
+ *     @def TEGRA186_CLK_PLL_U_48M
+ *     @def TEGRA186_CLK_PLL_U_480M
+ *     @def TEGRA186_CLK_PLLC4_OUT0
+ *     @def TEGRA186_CLK_PLLC4_OUT1
+ *     @def TEGRA186_CLK_PLLC4_OUT2
+ *     @def TEGRA186_CLK_PLLC4_OUT_MUX
+ *     @def TEGRA186_CLK_DFLLDISP_DIV
+ *     @def TEGRA186_CLK_PLLDISPHUB_DIV
+ *     @def TEGRA186_CLK_PLLP_DIV8
+ *   @}
+ *
+ *   @defgroup nafll_clks NAFLL clock sources
+ *   @{
+ *     @def TEGRA186_CLK_NAFLL_AXI_CBB
+ *     @def TEGRA186_CLK_NAFLL_BCPU
+ *     @def TEGRA186_CLK_NAFLL_BPMP
+ *     @def TEGRA186_CLK_NAFLL_DISP
+ *     @def TEGRA186_CLK_NAFLL_GPU
+ *     @def TEGRA186_CLK_NAFLL_ISP
+ *     @def TEGRA186_CLK_NAFLL_MCPU
+ *     @def TEGRA186_CLK_NAFLL_NVDEC
+ *     @def TEGRA186_CLK_NAFLL_NVENC
+ *     @def TEGRA186_CLK_NAFLL_NVJPG
+ *     @def TEGRA186_CLK_NAFLL_SCE
+ *     @def TEGRA186_CLK_NAFLL_SE
+ *     @def TEGRA186_CLK_NAFLL_TSEC
+ *     @def TEGRA186_CLK_NAFLL_TSECB
+ *     @def TEGRA186_CLK_NAFLL_VI
+ *     @def TEGRA186_CLK_NAFLL_VIC
+ *   @}
+ *
+ *   @defgroup mphy MPHY related clocks
+ *   @{
+ *     @def TEGRA186_CLK_MPHY_L0_RX_SYMB
+ *     @def TEGRA186_CLK_MPHY_L0_RX_LS_BIT
+ *     @def TEGRA186_CLK_MPHY_L0_TX_SYMB
+ *     @def TEGRA186_CLK_MPHY_L0_TX_LS_3XBIT
+ *     @def TEGRA186_CLK_MPHY_L0_RX_ANA
+ *     @def TEGRA186_CLK_MPHY_L1_RX_ANA
+ *     @def TEGRA186_CLK_MPHY_IOBIST
+ *     @def TEGRA186_CLK_MPHY_TX_1MHZ_REF
+ *     @def TEGRA186_CLK_MPHY_CORE_PLL_FIXED
+ *   @}
+ *
+ *   @defgroup eavb EAVB related clocks
+ *   @{
+ *     @def TEGRA186_CLK_EQOS_AXI
+ *     @def TEGRA186_CLK_EQOS_PTP_REF
+ *     @def TEGRA186_CLK_EQOS_RX
+ *     @def TEGRA186_CLK_EQOS_RX_INPUT
+ *     @def TEGRA186_CLK_EQOS_TX
+ *   @}
+ *
+ *   @defgroup usb USB related clocks
+ *   @{
+ *     @def TEGRA186_CLK_PEX_USB_PAD0_MGMT
+ *     @def TEGRA186_CLK_PEX_USB_PAD1_MGMT
+ *     @def TEGRA186_CLK_HSIC_TRK
+ *     @def TEGRA186_CLK_USB2_TRK
+ *     @def TEGRA186_CLK_USB2_HSIC_TRK
+ *     @def TEGRA186_CLK_XUSB_CORE_SS
+ *     @def TEGRA186_CLK_XUSB_CORE_DEV
+ *     @def TEGRA186_CLK_XUSB_FALCON
+ *     @def TEGRA186_CLK_XUSB_FS
+ *     @def TEGRA186_CLK_XUSB
+ *     @def TEGRA186_CLK_XUSB_DEV
+ *     @def TEGRA186_CLK_XUSB_HOST
+ *     @def TEGRA186_CLK_XUSB_SS
+ *   @}
+ *
+ *   @defgroup bigblock compute block related clocks
+ *   @{
+ *     @def TEGRA186_CLK_GPCCLK
+ *     @def TEGRA186_CLK_GPC2CLK
+ *     @def TEGRA186_CLK_GPU
+ *     @def TEGRA186_CLK_HOST1X
+ *     @def TEGRA186_CLK_ISP
+ *     @def TEGRA186_CLK_NVDEC
+ *     @def TEGRA186_CLK_NVENC
+ *     @def TEGRA186_CLK_NVJPG
+ *     @def TEGRA186_CLK_SE
+ *     @def TEGRA186_CLK_TSEC
+ *     @def TEGRA186_CLK_TSECB
+ *     @def TEGRA186_CLK_VIC
+ *   @}
+ *
+ *   @defgroup can CAN bus related clocks
+ *   @{
+ *     @def TEGRA186_CLK_CAN1
+ *     @def TEGRA186_CLK_CAN1_HOST
+ *     @def TEGRA186_CLK_CAN2
+ *     @def TEGRA186_CLK_CAN2_HOST
+ *   @}
+ *
+ *   @defgroup system basic system clocks
+ *   @{
+ *     @def TEGRA186_CLK_ACTMON
+ *     @def TEGRA186_CLK_AON_APB
+ *     @def TEGRA186_CLK_AON_CPU_NIC
+ *     @def TEGRA186_CLK_AON_NIC
+ *     @def TEGRA186_CLK_AXI_CBB
+ *     @def TEGRA186_CLK_BPMP_APB
+ *     @def TEGRA186_CLK_BPMP_CPU_NIC
+ *     @def TEGRA186_CLK_BPMP_NIC_RATE
+ *     @def TEGRA186_CLK_CLK_M
+ *     @def TEGRA186_CLK_EMC
+ *     @def TEGRA186_CLK_MSS_ENCRYPT
+ *     @def TEGRA186_CLK_SCE_APB
+ *     @def TEGRA186_CLK_SCE_CPU_NIC
+ *     @def TEGRA186_CLK_SCE_NIC
+ *     @def TEGRA186_CLK_TSC
+ *   @}
+ *
+ *   @defgroup pcie_clks PCIe related clocks
+ *   @{
+ *     @def TEGRA186_CLK_AFI
+ *     @def TEGRA186_CLK_PCIE
+ *     @def TEGRA186_CLK_PCIE2_IOBIST
+ *     @def TEGRA186_CLK_PCIERX0
+ *     @def TEGRA186_CLK_PCIERX1
+ *     @def TEGRA186_CLK_PCIERX2
+ *     @def TEGRA186_CLK_PCIERX3
+ *     @def TEGRA186_CLK_PCIERX4
+ *   @}
+ */
+
+/** @brief output of gate CLK_ENB_FUSE */
+#define TEGRA186_CLK_FUSE 0
+/**
+ * @brief It's not what you think
+ * @details output of gate CLK_ENB_GPU. This output connects to the GPU
+ * pwrclk. @warning: This is almost certainly not the clock you think
+ * it is. If you're looking for the clock of the graphics engine, see
+ * TEGRA186_GPCCLK
+ */
+#define TEGRA186_CLK_GPU 1
+/** @brief output of gate CLK_ENB_PCIE */
+#define TEGRA186_CLK_PCIE 3
+/** @brief output of the divider IPFS_CLK_DIVISOR */
+#define TEGRA186_CLK_AFI 4
+/** @brief output of gate CLK_ENB_PCIE2_IOBIST */
+#define TEGRA186_CLK_PCIE2_IOBIST 5
+/** @brief output of gate CLK_ENB_PCIERX0*/
+#define TEGRA186_CLK_PCIERX0 6
+/** @brief output of gate CLK_ENB_PCIERX1*/
+#define TEGRA186_CLK_PCIERX1 7
+/** @brief output of gate CLK_ENB_PCIERX2*/
+#define TEGRA186_CLK_PCIERX2 8
+/** @brief output of gate CLK_ENB_PCIERX3*/
+#define TEGRA186_CLK_PCIERX3 9
+/** @brief output of gate CLK_ENB_PCIERX4*/
+#define TEGRA186_CLK_PCIERX4 10
+/** @brief output branch of PLL_C for ISP, controlled by gate CLK_ENB_PLLC_OUT_ISP */
+#define TEGRA186_CLK_PLLC_OUT_ISP 11
+/** @brief output branch of PLL_C for VI, controlled by gate CLK_ENB_PLLC_OUT_VE */
+#define TEGRA186_CLK_PLLC_OUT_VE 12
+/** @brief output branch of PLL_C for AON domain, controlled by gate CLK_ENB_PLLC_OUT_AON */
+#define TEGRA186_CLK_PLLC_OUT_AON 13
+/** @brief output of gate CLK_ENB_SOR_SAFE */
+#define TEGRA186_CLK_SOR_SAFE 39
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S2 */
+#define TEGRA186_CLK_I2S2 42
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S3 */
+#define TEGRA186_CLK_I2S3 43
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPDF_IN */
+#define TEGRA186_CLK_SPDIF_IN 44
+/** @brief output of gate CLK_ENB_SPDIF_DOUBLER */
+#define TEGRA186_CLK_SPDIF_DOUBLER 45
+/**  @clkdesc{spi_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_SPI3} */
+#define TEGRA186_CLK_SPI3 46
+/** @clkdesc{i2c_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_I2C1} */
+#define TEGRA186_CLK_I2C1 47
+/** @clkdesc{i2c_clks, out, mux, CLK_RST_CONTROLLER_CLK_SOURCE_I2C5} */
+#define TEGRA186_CLK_I2C5 48
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI1 */
+#define TEGRA186_CLK_SPI1 49
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_ISP */
+#define TEGRA186_CLK_ISP 50
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VI */
+#define TEGRA186_CLK_VI 51
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC1 */
+#define TEGRA186_CLK_SDMMC1 52
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC2 */
+#define TEGRA186_CLK_SDMMC2 53
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC4 */
+#define TEGRA186_CLK_SDMMC4 54
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTA */
+#define TEGRA186_CLK_UARTA 55
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTB */
+#define TEGRA186_CLK_UARTB 56
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HOST1X */
+#define TEGRA186_CLK_HOST1X 57
+/**
+ * @brief controls the EMC clock frequency.
+ * @details Doing a clk_set_rate on this clock will select the
+ * appropriate clock source, program the source rate and execute a
+ * specific sequence to switch to the new clock source for both memory
+ * controllers. This can be used to control the balance between memory
+ * throughput and memory controller power.
+ */
+#define TEGRA186_CLK_EMC 58
+/* @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH4 */
+#define TEGRA186_CLK_EXTPERIPH4 73
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI4 */
+#define TEGRA186_CLK_SPI4 74
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C3 */
+#define TEGRA186_CLK_I2C3 75
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC3 */
+#define TEGRA186_CLK_SDMMC3 76
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTD */
+#define TEGRA186_CLK_UARTD 77
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S1 */
+#define TEGRA186_CLK_I2S1 79
+/** output of gate CLK_ENB_DTV */
+#define TEGRA186_CLK_DTV 80
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSEC */
+#define TEGRA186_CLK_TSEC 81
+/** @brief output of gate CLK_ENB_DP2 */
+#define TEGRA186_CLK_DP2 82
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S4 */
+#define TEGRA186_CLK_I2S4 84
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2S5 */
+#define TEGRA186_CLK_I2S5 85
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C4 */
+#define TEGRA186_CLK_I2C4 86
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AHUB */
+#define TEGRA186_CLK_AHUB 87
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HDA2CODEC_2X */
+#define TEGRA186_CLK_HDA2CODEC_2X 88
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH1 */
+#define TEGRA186_CLK_EXTPERIPH1 89
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH2 */
+#define TEGRA186_CLK_EXTPERIPH2 90
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_EXTPERIPH3 */
+#define TEGRA186_CLK_EXTPERIPH3 91
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C_SLOW */
+#define TEGRA186_CLK_I2C_SLOW 92
+/** @brief output of the SOR1_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 */
+#define TEGRA186_CLK_SOR1 93
+/** @brief output of gate CLK_ENB_CEC */
+#define TEGRA186_CLK_CEC 94
+/** @brief output of gate CLK_ENB_DPAUX1 */
+#define TEGRA186_CLK_DPAUX1 95
+/** @brief output of gate CLK_ENB_DPAUX */
+#define TEGRA186_CLK_DPAUX 96
+/** @brief output of the SOR0_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0 */
+#define TEGRA186_CLK_SOR0 97
+/** @brief output of gate CLK_ENB_HDA2HDMICODEC */
+#define TEGRA186_CLK_HDA2HDMICODEC 98
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SATA */
+#define TEGRA186_CLK_SATA 99
+/** @brief output of gate CLK_ENB_SATA_OOB */
+#define TEGRA186_CLK_SATA_OOB 100
+/** @brief output of gate CLK_ENB_SATA_IOBIST */
+#define TEGRA186_CLK_SATA_IOBIST 101
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_HDA */
+#define TEGRA186_CLK_HDA 102
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SE */
+#define TEGRA186_CLK_SE 103
+/** @brief output of gate CLK_ENB_APB2APE */
+#define TEGRA186_CLK_APB2APE 104
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_APE */
+#define TEGRA186_CLK_APE 105
+/** @brief output of gate CLK_ENB_IQC1 */
+#define TEGRA186_CLK_IQC1 106
+/** @brief output of gate CLK_ENB_IQC2 */
+#define TEGRA186_CLK_IQC2 107
+/** divide by 2 version of TEGRA186_CLK_PLLREFE_VCO */
+#define TEGRA186_CLK_PLLREFE_OUT 108
+/** @brief output of gate CLK_ENB_PLLREFE_PLL_REF */
+#define TEGRA186_CLK_PLLREFE_PLL_REF 109
+/** @brief output of gate CLK_ENB_PLLC4_OUT */
+#define TEGRA186_CLK_PLLC4_OUT 110
+/** @brief output of mux xusb_core_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB 111
+/** controls xusb_dev_ce signal on page 66 and 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_DEV 112
+/** controls xusb_host_ce signal on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_HOST 113
+/** controls xusb_ss_ce signal on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_SS 114
+/** @brief output of gate CLK_ENB_DSI */
+#define TEGRA186_CLK_DSI 115
+/** @brief output of gate CLK_ENB_MIPI_CAL */
+#define TEGRA186_CLK_MIPI_CAL 116
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIA_LP */
+#define TEGRA186_CLK_DSIA_LP 117
+/** @brief output of gate CLK_ENB_DSIB */
+#define TEGRA186_CLK_DSIB 118
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIB_LP */
+#define TEGRA186_CLK_DSIB_LP 119
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC1 */
+#define TEGRA186_CLK_DMIC1 122
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC2 */
+#define TEGRA186_CLK_DMIC2 123
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AUD_MCLK */
+#define TEGRA186_CLK_AUD_MCLK 124
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 */
+#define TEGRA186_CLK_I2C6 125
+/**output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UART_FST_MIPI_CAL */
+#define TEGRA186_CLK_UART_FST_MIPI_CAL 126
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VIC */
+#define TEGRA186_CLK_VIC 127
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SDMMC_LEGACY_TM */
+#define TEGRA186_CLK_SDMMC_LEGACY_TM 128
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDEC */
+#define TEGRA186_CLK_NVDEC 129
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVJPG */
+#define TEGRA186_CLK_NVJPG 130
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVENC */
+#define TEGRA186_CLK_NVENC 131
+/** @brief output of the QSPI_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_QSPI */
+#define TEGRA186_CLK_QSPI 132
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_VI_I2C */
+#define TEGRA186_CLK_VI_I2C 133
+/** @brief output of gate CLK_ENB_HSIC_TRK */
+#define TEGRA186_CLK_HSIC_TRK 134
+/** @brief output of gate CLK_ENB_USB2_TRK */
+#define TEGRA186_CLK_USB2_TRK 135
+/** output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_MAUD */
+#define TEGRA186_CLK_MAUD 136
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSECB */
+#define TEGRA186_CLK_TSECB 137
+/** @brief output of gate CLK_ENB_ADSP */
+#define TEGRA186_CLK_ADSP 138
+/** @brief output of gate CLK_ENB_ADSPNEON */
+#define TEGRA186_CLK_ADSPNEON 139
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_L0_RX_LS_SYMB */
+#define TEGRA186_CLK_MPHY_L0_RX_SYMB 140
+/** @brief output of gate CLK_ENB_MPHY_L0_RX_LS_BIT */
+#define TEGRA186_CLK_MPHY_L0_RX_LS_BIT 141
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_L0_TX_LS_SYMB */
+#define TEGRA186_CLK_MPHY_L0_TX_SYMB 142
+/** @brief output of gate CLK_ENB_MPHY_L0_TX_LS_3XBIT */
+#define TEGRA186_CLK_MPHY_L0_TX_LS_3XBIT 143
+/** @brief output of gate CLK_ENB_MPHY_L0_RX_ANA */
+#define TEGRA186_CLK_MPHY_L0_RX_ANA 144
+/** @brief output of gate CLK_ENB_MPHY_L1_RX_ANA */
+#define TEGRA186_CLK_MPHY_L1_RX_ANA 145
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_IOBIST */
+#define TEGRA186_CLK_MPHY_IOBIST 146
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_TX_1MHZ_REF */
+#define TEGRA186_CLK_MPHY_TX_1MHZ_REF 147
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_MPHY_CORE_PLL_FIXED */
+#define TEGRA186_CLK_MPHY_CORE_PLL_FIXED 148
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AXI_CBB */
+#define TEGRA186_CLK_AXI_CBB 149
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC3 */
+#define TEGRA186_CLK_DMIC3 150
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC4 */
+#define TEGRA186_CLK_DMIC4 151
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSPK1 */
+#define TEGRA186_CLK_DSPK1 152
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSPK2 */
+#define TEGRA186_CLK_DSPK2 153
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C6 */
+#define TEGRA186_CLK_I2S6 154
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P0 */
+#define TEGRA186_CLK_NVDISPLAY_P0 155
+/** @brief output of the NVDISPLAY_DISP_CLK_SRC mux in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP */
+#define TEGRA186_CLK_NVDISPLAY_DISP 156
+/** @brief output of gate CLK_ENB_NVDISPLAY_DSC */
+#define TEGRA186_CLK_NVDISPLAY_DSC 157
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAYHUB */
+#define TEGRA186_CLK_NVDISPLAYHUB 158
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P1 */
+#define TEGRA186_CLK_NVDISPLAY_P1 159
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_P2 */
+#define TEGRA186_CLK_NVDISPLAY_P2 160
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TACH */
+#define TEGRA186_CLK_TACH 166
+/** @brief output of gate CLK_ENB_EQOS */
+#define TEGRA186_CLK_EQOS_AXI 167
+/** @brief output of gate CLK_ENB_EQOS_RX */
+#define TEGRA186_CLK_EQOS_RX 168
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UFSHC_CG_SYS */
+#define TEGRA186_CLK_UFSHC 178
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UFSDEV_REF */
+#define TEGRA186_CLK_UFSDEV_REF 179
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVCSI */
+#define TEGRA186_CLK_NVCSI 180
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_NVCSILP */
+#define TEGRA186_CLK_NVCSILP 181
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C7 */
+#define TEGRA186_CLK_I2C7 182
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C9 */
+#define TEGRA186_CLK_I2C9 183
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C12 */
+#define TEGRA186_CLK_I2C12 184
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C13 */
+#define TEGRA186_CLK_I2C13 185
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C14 */
+#define TEGRA186_CLK_I2C14 186
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM1 */
+#define TEGRA186_CLK_PWM1 187
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM2 */
+#define TEGRA186_CLK_PWM2 188
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM3 */
+#define TEGRA186_CLK_PWM3 189
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM5 */
+#define TEGRA186_CLK_PWM5 190
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM6 */
+#define TEGRA186_CLK_PWM6 191
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM7 */
+#define TEGRA186_CLK_PWM7 192
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM8 */
+#define TEGRA186_CLK_PWM8 193
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTE */
+#define TEGRA186_CLK_UARTE 194
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTF */
+#define TEGRA186_CLK_UARTF 195
+/** @deprecated */
+#define TEGRA186_CLK_DBGAPB 196
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_BPMP_CPU_NIC */
+#define TEGRA186_CLK_BPMP_CPU_NIC 197
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_BPMP_APB */
+#define TEGRA186_CLK_BPMP_APB 199
+/** @brief output of mux controlled by TEGRA186_CLK_SOC_ACTMON */
+#define TEGRA186_CLK_ACTMON 201
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_CPU_NIC */
+#define TEGRA186_CLK_AON_CPU_NIC 208
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_CAN1 */
+#define TEGRA186_CLK_CAN1 210
+/** @brief output of gate CLK_ENB_CAN1_HOST */
+#define TEGRA186_CLK_CAN1_HOST 211
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_CAN2 */
+#define TEGRA186_CLK_CAN2 212
+/** @brief output of gate CLK_ENB_CAN2_HOST */
+#define TEGRA186_CLK_CAN2_HOST 213
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_APB */
+#define TEGRA186_CLK_AON_APB 214
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTC */
+#define TEGRA186_CLK_UARTC 215
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_UARTG */
+#define TEGRA186_CLK_UARTG 216
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_UART_FST_MIPI_CAL */
+#define TEGRA186_CLK_AON_UART_FST_MIPI_CAL 217
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C2 */
+#define TEGRA186_CLK_I2C2 218
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C8 */
+#define TEGRA186_CLK_I2C8 219
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_I2C10 */
+#define TEGRA186_CLK_I2C10 220
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_I2C_SLOW */
+#define TEGRA186_CLK_AON_I2C_SLOW 221
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPI2 */
+#define TEGRA186_CLK_SPI2 222
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DMIC5 */
+#define TEGRA186_CLK_DMIC5 223
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_AON_TOUCH */
+#define TEGRA186_CLK_AON_TOUCH 224
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_PWM4 */
+#define TEGRA186_CLK_PWM4 225
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_TSC. This clock object is read only and is used for all timers in the system. */
+#define TEGRA186_CLK_TSC 226
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_MSS_ENCRYPT */
+#define TEGRA186_CLK_MSS_ENCRYPT 227
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SCE_CPU_NIC */
+#define TEGRA186_CLK_SCE_CPU_NIC 228
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SCE_APB */
+#define TEGRA186_CLK_SCE_APB 230
+/** @brief output of gate CLK_ENB_DSIC */
+#define TEGRA186_CLK_DSIC 231
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSIC_LP */
+#define TEGRA186_CLK_DSIC_LP 232
+/** @brief output of gate CLK_ENB_DSID */
+#define TEGRA186_CLK_DSID 233
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_DSID_LP */
+#define TEGRA186_CLK_DSID_LP 234
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_SATA_USB_RX_BYP */
+#define TEGRA186_CLK_PEX_SATA_USB_RX_BYP 236
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_CLK_SOURCE_SPDIF_OUT */
+#define TEGRA186_CLK_SPDIF_OUT 238
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_EQOS_PTP_REF_CLK_0 */
+#define TEGRA186_CLK_EQOS_PTP_REF 239
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_EQOS_TX_CLK */
+#define TEGRA186_CLK_EQOS_TX 240
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_USB2_HSIC_TRK */
+#define TEGRA186_CLK_USB2_HSIC_TRK 241
+/** @brief output of mux xusb_ss_clk_switch on page 66 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_CORE_SS 242
+/** @brief output of mux xusb_core_dev_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_CORE_DEV 243
+/** @brief output of mux xusb_core_falcon_clk_switch on page 67 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_FALCON 244
+/** @brief output of mux xusb_fs_clk_switch on page 66 of T186_Clocks_IAS.doc */
+#define TEGRA186_CLK_XUSB_FS 245
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLA_OUT */
+#define TEGRA186_CLK_PLL_A_OUT0 246
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S1 */
+#define TEGRA186_CLK_SYNC_I2S1 247
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S2 */
+#define TEGRA186_CLK_SYNC_I2S2 248
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S3 */
+#define TEGRA186_CLK_SYNC_I2S3 249
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S4 */
+#define TEGRA186_CLK_SYNC_I2S4 250
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S5 */
+#define TEGRA186_CLK_SYNC_I2S5 251
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_I2S6 */
+#define TEGRA186_CLK_SYNC_I2S6 252
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DSPK1 */
+#define TEGRA186_CLK_SYNC_DSPK1 253
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DSPK2 */
+#define TEGRA186_CLK_SYNC_DSPK2 254
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC1 */
+#define TEGRA186_CLK_SYNC_DMIC1 255
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC2 */
+#define TEGRA186_CLK_SYNC_DMIC2 256
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC3 */
+#define TEGRA186_CLK_SYNC_DMIC3 257
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_DMIC4 */
+#define TEGRA186_CLK_SYNC_DMIC4 259
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_AUDIO_SYNC_CLK_SPDIF */
+#define TEGRA186_CLK_SYNC_SPDIF 260
+/** @brief output of gate CLK_ENB_PLLREFE_OUT */
+#define TEGRA186_CLK_PLLREFE_OUT_GATED 261
+/** @brief output of the divider PLLREFE_DIVP in CLK_RST_CONTROLLER_PLLREFE_BASE. PLLREFE has 2 outputs:
+  *      * VCO/pdiv defined by this clock object
+  *      * VCO/2 defined by TEGRA186_CLK_PLLREFE_OUT
+  */
+#define TEGRA186_CLK_PLLREFE_OUT1 262
+#define TEGRA186_CLK_PLLD_OUT1 267
+/** @brief output of the divider PLLP_DIVP in CLK_RST_CONTROLLER_PLLP_BASE */
+#define TEGRA186_CLK_PLLP_OUT0 269
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLP_OUTC */
+#define TEGRA186_CLK_PLLP_OUT5 270
+/** PLL controlled by CLK_RST_CONTROLLER_PLLA_BASE for use by audio clocks */
+#define TEGRA186_CLK_PLLA 271
+/** @brief output of mux controlled by CLK_RST_CONTROLLER_ACLK_BURST_POLICY divided by the divider controlled by ACLK_CLK_DIVISOR in CLK_RST_CONTROLLER_SUPER_ACLK_DIVIDER */
+#define TEGRA186_CLK_ACLK 273
+/** fixed 48MHz clock divided down from TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLL_U_48M 274
+/** fixed 480MHz clock divided down from TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLL_U_480M 275
+/** @brief output of the divider PLLC4_DIVP in CLK_RST_CONTROLLER_PLLC4_BASE. Output frequency is TEGRA186_CLK_PLLC4_VCO/PLLC4_DIVP */
+#define TEGRA186_CLK_PLLC4_OUT0 276
+/** fixed /3 divider. Output frequency of this clock is TEGRA186_CLK_PLLC4_VCO/3 */
+#define TEGRA186_CLK_PLLC4_OUT1 277
+/** fixed /5 divider. Output frequency of this clock is TEGRA186_CLK_PLLC4_VCO/5 */
+#define TEGRA186_CLK_PLLC4_OUT2 278
+/** @brief output of mux controlled by PLLC4_CLK_SEL in CLK_RST_CONTROLLER_PLLC4_MISC1 */
+#define TEGRA186_CLK_PLLC4_OUT_MUX 279
+/** @brief output of divider NVDISPLAY_DISP_CLK_DIVISOR in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP when DFLLDISP_DIV is selected in NVDISPLAY_DISP_CLK_SRC */
+#define TEGRA186_CLK_DFLLDISP_DIV 284
+/** @brief output of divider NVDISPLAY_DISP_CLK_DIVISOR in CLK_RST_CONTROLLER_CLK_SOURCE_NVDISPLAY_DISP when PLLDISPHUB_DIV is selected in NVDISPLAY_DISP_CLK_SRC */
+#define TEGRA186_CLK_PLLDISPHUB_DIV 285
+/** fixed /8 divider which is used as the input for TEGRA186_CLK_SOR_SAFE */
+#define TEGRA186_CLK_PLLP_DIV8 286
+/** @brief output of divider CLK_RST_CONTROLLER_BPMP_NIC_RATE */
+#define TEGRA186_CLK_BPMP_NIC 287
+/** @brief output of the divider CLK_RST_CONTROLLER_PLLA1_OUT1 */
+#define TEGRA186_CLK_PLL_A_OUT1 288
+/** @deprecated */
+#define TEGRA186_CLK_GPC2CLK 289
+/** A fake clock which must be enabled during KFUSE read operations to ensure adequate VDD_CORE voltage. */
+#define TEGRA186_CLK_KFUSE 293
+/**
+ * @brief controls the PLLE hardware sequencer.
+ * @details This clock only has enable and disable methods. When the
+ * PLLE hw sequencer is enabled, PLLE, will be enabled or disabled by
+ * hw based on the control signals from the PCIe, SATA and XUSB
+ * clocks. When the PLLE hw sequencer is disabled, the state of PLLE
+ * is controlled by sw using clk_enable/clk_disable on
+ * TEGRA186_CLK_PLLE.
+ */
+#define TEGRA186_CLK_PLLE_PWRSEQ 294
+/** fixed 60MHz clock divided down from, TEGRA186_CLK_PLL_U */
+#define TEGRA186_CLK_PLLREFE_REF 295
+/** @brief output of mux controlled by SOR0_CLK_SEL0 and SOR0_CLK_SEL1 in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0 */
+#define TEGRA186_CLK_SOR0_OUT 296
+/** @brief output of mux controlled by SOR1_CLK_SEL0 and SOR1_CLK_SEL1 in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1 */
+#define TEGRA186_CLK_SOR1_OUT 297
+/** @brief fixed /5 divider.  Output frequency of this clock is TEGRA186_CLK_PLLREFE_OUT1/5. Used as input for TEGRA186_CLK_EQOS_AXI */
+#define TEGRA186_CLK_PLLREFE_OUT1_DIV5 298
+/** @brief controls the UTMIP_PLL (aka PLLU) hardware sqeuencer */
+#define TEGRA186_CLK_UTMIP_PLL_PWRSEQ 301
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_USB_PAD_PLL0_MGMT */
+#define TEGRA186_CLK_PEX_USB_PAD0_MGMT 302
+/** @brief output of the divider CLK_RST_CONTROLLER_CLK_SOURCE_PEX_USB_PAD_PLL1_MGMT */
+#define TEGRA186_CLK_PEX_USB_PAD1_MGMT 303
+/** @brief controls the UPHY_PLL0 hardware sqeuencer */
+#define TEGRA186_CLK_UPHY_PLL0_PWRSEQ 304
+/** @brief controls the UPHY_PLL1 hardware sqeuencer */
+#define TEGRA186_CLK_UPHY_PLL1_PWRSEQ 305
+/** @brief control for PLLREFE_IDDQ in CLK_RST_CONTROLLER_PLLREFE_MISC so the bypass output even be used when the PLL is disabled */
+#define TEGRA186_CLK_PLLREFE_PLLE_PASSTHROUGH 306
+/** @brief output of the mux controlled by PLLREFE_SEL_CLKIN_PEX in CLK_RST_CONTROLLER_PLLREFE_MISC */
+#define TEGRA186_CLK_PLLREFE_PEX 307
+/** @brief control for PLLREFE_IDDQ in CLK_RST_CONTROLLER_PLLREFE_MISC to turn on the PLL when enabled */
+#define TEGRA186_CLK_PLLREFE_IDDQ 308
+/** @brief output of the divider QSPI_CLK_DIV2_SEL in CLK_RST_CONTROLLER_CLK_SOURCE_QSPI */
+#define TEGRA186_CLK_QSPI_OUT 309
+/**
+ * @brief GPC2CLK-div-2
+ * @details fixed /2 divider. Output frequency is
+ * TEGRA186_CLK_GPC2CLK/2. The frequency of this clock is the
+ * frequency at which the GPU graphics engine runs. */
+#define TEGRA186_CLK_GPCCLK 310
+/** @brief output of divider CLK_RST_CONTROLLER_AON_NIC_RATE */
+#define TEGRA186_CLK_AON_NIC 450
+/** @brief output of divider CLK_RST_CONTROLLER_SCE_NIC_RATE */
+#define TEGRA186_CLK_SCE_NIC 451
+/** Fixed 100MHz PLL for PCIe, SATA and superspeed USB */
+#define TEGRA186_CLK_PLLE 512
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC_BASE */
+#define TEGRA186_CLK_PLLC 513
+/** Fixed 408MHz PLL for use by peripheral clocks */
+#define TEGRA186_CLK_PLLP 516
+/** @deprecated */
+#define TEGRA186_CLK_PLL_P TEGRA186_CLK_PLLP
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD_BASE for use by DSI */
+#define TEGRA186_CLK_PLLD 518
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD2_BASE for use by HDMI or DP */
+#define TEGRA186_CLK_PLLD2 519
+/**
+ * @brief PLL controlled by CLK_RST_CONTROLLER_PLLREFE_BASE.
+ * @details Note that this clock only controls the VCO output, before
+ * the post-divider. See TEGRA186_CLK_PLLREFE_OUT1 for more
+ * information.
+ */
+#define TEGRA186_CLK_PLLREFE_VCO 520
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC2_BASE */
+#define TEGRA186_CLK_PLLC2 521
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC3_BASE */
+#define TEGRA186_CLK_PLLC3 522
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLDP_BASE for use as the DP link clock */
+#define TEGRA186_CLK_PLLDP 523
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLC4_BASE */
+#define TEGRA186_CLK_PLLC4_VCO 524
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLA1_BASE for use by audio clocks */
+#define TEGRA186_CLK_PLLA1 525
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLNVCSI_BASE */
+#define TEGRA186_CLK_PLLNVCSI 526
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLDISPHUB_BASE */
+#define TEGRA186_CLK_PLLDISPHUB 527
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLD3_BASE for use by HDMI or DP */
+#define TEGRA186_CLK_PLLD3 528
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLBPMPCAM_BASE */
+#define TEGRA186_CLK_PLLBPMPCAM 531
+/** @brief PLL controlled by CLK_RST_CONTROLLER_PLLAON_BASE for use by IP blocks in the AON domain */
+#define TEGRA186_CLK_PLLAON 532
+/** Fixed frequency 960MHz PLL for USB and EAVB */
+#define TEGRA186_CLK_PLLU 533
+/** fixed /2 divider. Output frequency is TEGRA186_CLK_PLLC4_VCO/2 */
+#define TEGRA186_CLK_PLLC4_VCO_DIV2 535
+/** @brief NAFLL clock source for AXI_CBB */
+#define TEGRA186_CLK_NAFLL_AXI_CBB 564
+/** @brief NAFLL clock source for BPMP */
+#define TEGRA186_CLK_NAFLL_BPMP 565
+/** @brief NAFLL clock source for ISP */
+#define TEGRA186_CLK_NAFLL_ISP 566
+/** @brief NAFLL clock source for NVDEC */
+#define TEGRA186_CLK_NAFLL_NVDEC 567
+/** @brief NAFLL clock source for NVENC */
+#define TEGRA186_CLK_NAFLL_NVENC 568
+/** @brief NAFLL clock source for NVJPG */
+#define TEGRA186_CLK_NAFLL_NVJPG 569
+/** @brief NAFLL clock source for SCE */
+#define TEGRA186_CLK_NAFLL_SCE 570
+/** @brief NAFLL clock source for SE */
+#define TEGRA186_CLK_NAFLL_SE 571
+/** @brief NAFLL clock source for TSEC */
+#define TEGRA186_CLK_NAFLL_TSEC 572
+/** @brief NAFLL clock source for TSECB */
+#define TEGRA186_CLK_NAFLL_TSECB 573
+/** @brief NAFLL clock source for VI */
+#define TEGRA186_CLK_NAFLL_VI 574
+/** @brief NAFLL clock source for VIC */
+#define TEGRA186_CLK_NAFLL_VIC 575
+/** @brief NAFLL clock source for DISP */
+#define TEGRA186_CLK_NAFLL_DISP 576
+/** @brief NAFLL clock source for GPU */
+#define TEGRA186_CLK_NAFLL_GPU 577
+/** @brief NAFLL clock source for M-CPU cluster */
+#define TEGRA186_CLK_NAFLL_MCPU 578
+/** @brief NAFLL clock source for B-CPU cluster */
+#define TEGRA186_CLK_NAFLL_BCPU 579
+/** @brief input from Tegra's CLK_32K_IN pad */
+#define TEGRA186_CLK_CLK_32K 608
+/** @brief output of divider CLK_RST_CONTROLLER_CLK_M_DIVIDE */
+#define TEGRA186_CLK_CLK_M 609
+/** @brief output of divider PLL_REF_DIV in CLK_RST_CONTROLLER_OSC_CTRL */
+#define TEGRA186_CLK_PLL_REF 610
+/** @brief input from Tegra's XTAL_IN */
+#define TEGRA186_CLK_OSC 612
+/** @brief clock recovered from EAVB input */
+#define TEGRA186_CLK_EQOS_RX_INPUT 613
+/** @brief clock recovered from DTV input */
+#define TEGRA186_CLK_DTV_INPUT 614
+/** @brief SOR0 brick output which feeds into SOR0_CLK_SEL mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR0*/
+#define TEGRA186_CLK_SOR0_PAD_CLKOUT 615
+/** @brief SOR1 brick output which feeds into SOR1_CLK_SEL mux in CLK_RST_CONTROLLER_CLK_SOURCE_SOR1*/
+#define TEGRA186_CLK_SOR1_PAD_CLKOUT 616
+/** @brief clock recovered from I2S1 input */
+#define TEGRA186_CLK_I2S1_SYNC_INPUT 617
+/** @brief clock recovered from I2S2 input */
+#define TEGRA186_CLK_I2S2_SYNC_INPUT 618
+/** @brief clock recovered from I2S3 input */
+#define TEGRA186_CLK_I2S3_SYNC_INPUT 619
+/** @brief clock recovered from I2S4 input */
+#define TEGRA186_CLK_I2S4_SYNC_INPUT 620
+/** @brief clock recovered from I2S5 input */
+#define TEGRA186_CLK_I2S5_SYNC_INPUT 621
+/** @brief clock recovered from I2S6 input */
+#define TEGRA186_CLK_I2S6_SYNC_INPUT 622
+/** @brief clock recovered from SPDIFIN input */
+#define TEGRA186_CLK_SPDIFIN_SYNC_INPUT 623
+
+/**
+ * @brief subject to change
+ * @details maximum clock identifier value plus one.
+ */
+#define TEGRA186_CLK_CLK_MAX 624
+
+/** @} */
+
+#endif
diff --git a/include/dt-bindings/reset/tegra186-reset.h b/include/dt-bindings/reset/tegra186-reset.h
new file mode 100644
index 000000000000..8a184e357955
--- /dev/null
+++ b/include/dt-bindings/reset/tegra186-reset.h
@@ -0,0 +1,217 @@
+/*
+ * Copyright (c) 2015, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 _ABI_MACH_T186_RESET_T186_H_
+#define _ABI_MACH_T186_RESET_T186_H_
+
+
+#define TEGRA186_RESET_ACTMON			0
+#define TEGRA186_RESET_AFI			1
+#define TEGRA186_RESET_CEC			2
+#define TEGRA186_RESET_CSITE			3
+#define TEGRA186_RESET_DP2			4
+#define TEGRA186_RESET_DPAUX			5
+#define TEGRA186_RESET_DSI			6
+#define TEGRA186_RESET_DSIB			7
+#define TEGRA186_RESET_DTV			8
+#define TEGRA186_RESET_DVFS			9
+#define TEGRA186_RESET_ENTROPY			10
+#define TEGRA186_RESET_EXTPERIPH1		11
+#define TEGRA186_RESET_EXTPERIPH2		12
+#define TEGRA186_RESET_EXTPERIPH3		13
+#define TEGRA186_RESET_GPU			14
+#define TEGRA186_RESET_HDA			15
+#define TEGRA186_RESET_HDA2CODEC_2X		16
+#define TEGRA186_RESET_HDA2HDMICODEC		17
+#define TEGRA186_RESET_HOST1X			18
+#define TEGRA186_RESET_I2C1			19
+#define TEGRA186_RESET_I2C2			20
+#define TEGRA186_RESET_I2C3			21
+#define TEGRA186_RESET_I2C4			22
+#define TEGRA186_RESET_I2C5			23
+#define TEGRA186_RESET_I2C6			24
+#define TEGRA186_RESET_ISP			25
+#define TEGRA186_RESET_KFUSE			26
+#define TEGRA186_RESET_LA			27
+#define TEGRA186_RESET_MIPI_CAL			28
+#define TEGRA186_RESET_PCIE			29
+#define TEGRA186_RESET_PCIEXCLK			30
+#define TEGRA186_RESET_SATA			31
+#define TEGRA186_RESET_SATACOLD			32
+#define TEGRA186_RESET_SDMMC1			33
+#define TEGRA186_RESET_SDMMC2			34
+#define TEGRA186_RESET_SDMMC3			35
+#define TEGRA186_RESET_SDMMC4			36
+#define TEGRA186_RESET_SE			37
+#define TEGRA186_RESET_SOC_THERM		38
+#define TEGRA186_RESET_SOR0			39
+#define TEGRA186_RESET_SPI1			40
+#define TEGRA186_RESET_SPI2			41
+#define TEGRA186_RESET_SPI3			42
+#define TEGRA186_RESET_SPI4			43
+#define TEGRA186_RESET_TMR			44
+#define TEGRA186_RESET_TRIG_SYS			45
+#define TEGRA186_RESET_TSEC			46
+#define TEGRA186_RESET_UARTA			47
+#define TEGRA186_RESET_UARTB			48
+#define TEGRA186_RESET_UARTC			49
+#define TEGRA186_RESET_UARTD			50
+#define TEGRA186_RESET_VI			51
+#define TEGRA186_RESET_VIC			52
+#define TEGRA186_RESET_XUSB_DEV			53
+#define TEGRA186_RESET_XUSB_HOST		54
+#define TEGRA186_RESET_XUSB_PADCTL		55
+#define TEGRA186_RESET_XUSB_SS			56
+#define TEGRA186_RESET_AON_APB			57
+#define TEGRA186_RESET_AXI_CBB			58
+#define TEGRA186_RESET_BPMP_APB			59
+#define TEGRA186_RESET_CAN1			60
+#define TEGRA186_RESET_CAN2			61
+#define TEGRA186_RESET_DMIC5			62
+#define TEGRA186_RESET_DSIC			63
+#define TEGRA186_RESET_DSID			64
+#define TEGRA186_RESET_EMC_EMC			65
+#define TEGRA186_RESET_EMC_MEM			66
+#define TEGRA186_RESET_EMCSB_EMC		67
+#define TEGRA186_RESET_EMCSB_MEM		68
+#define TEGRA186_RESET_EQOS			69
+#define TEGRA186_RESET_GPCDMA			70
+#define TEGRA186_RESET_GPIO_CTL0		71
+#define TEGRA186_RESET_GPIO_CTL1		72
+#define TEGRA186_RESET_GPIO_CTL2		73
+#define TEGRA186_RESET_GPIO_CTL3		74
+#define TEGRA186_RESET_GPIO_CTL4		75
+#define TEGRA186_RESET_GPIO_CTL5		76
+#define TEGRA186_RESET_I2C10			77
+#define TEGRA186_RESET_I2C12			78
+#define TEGRA186_RESET_I2C13			79
+#define TEGRA186_RESET_I2C14			80
+#define TEGRA186_RESET_I2C7			81
+#define TEGRA186_RESET_I2C8			82
+#define TEGRA186_RESET_I2C9			83
+#define TEGRA186_RESET_JTAG2AXI			84
+#define TEGRA186_RESET_MPHY_IOBIST		85
+#define TEGRA186_RESET_MPHY_L0_RX		86
+#define TEGRA186_RESET_MPHY_L0_TX		87
+#define TEGRA186_RESET_NVCSI			88
+#define TEGRA186_RESET_NVDISPLAY0_HEAD0		89
+#define TEGRA186_RESET_NVDISPLAY0_HEAD1		90
+#define TEGRA186_RESET_NVDISPLAY0_HEAD2		91
+#define TEGRA186_RESET_NVDISPLAY0_MISC		92
+#define TEGRA186_RESET_NVDISPLAY0_WGRP0		93
+#define TEGRA186_RESET_NVDISPLAY0_WGRP1		94
+#define TEGRA186_RESET_NVDISPLAY0_WGRP2		95
+#define TEGRA186_RESET_NVDISPLAY0_WGRP3		96
+#define TEGRA186_RESET_NVDISPLAY0_WGRP4		97
+#define TEGRA186_RESET_NVDISPLAY0_WGRP5		98
+#define TEGRA186_RESET_PWM1			99
+#define TEGRA186_RESET_PWM2			100
+#define TEGRA186_RESET_PWM3			101
+#define TEGRA186_RESET_PWM4			102
+#define TEGRA186_RESET_PWM5			103
+#define TEGRA186_RESET_PWM6			104
+#define TEGRA186_RESET_PWM7			105
+#define TEGRA186_RESET_PWM8			106
+#define TEGRA186_RESET_SCE_APB			107
+#define TEGRA186_RESET_SOR1			108
+#define TEGRA186_RESET_TACH			109
+#define TEGRA186_RESET_TSC			110
+#define TEGRA186_RESET_UARTF			111
+#define TEGRA186_RESET_UARTG			112
+#define TEGRA186_RESET_UFSHC			113
+#define TEGRA186_RESET_UFSHC_AXI_M		114
+#define TEGRA186_RESET_UPHY			115
+#define TEGRA186_RESET_ADSP			116
+#define TEGRA186_RESET_ADSPDBG			117
+#define TEGRA186_RESET_ADSPINTF			118
+#define TEGRA186_RESET_ADSPNEON			119
+#define TEGRA186_RESET_ADSPPERIPH		120
+#define TEGRA186_RESET_ADSPSCU			121
+#define TEGRA186_RESET_ADSPWDT			122
+#define TEGRA186_RESET_APE			123
+#define TEGRA186_RESET_DPAUX1			124
+#define TEGRA186_RESET_NVDEC			125
+#define TEGRA186_RESET_NVENC			126
+#define TEGRA186_RESET_NVJPG			127
+#define TEGRA186_RESET_PEX_USB_UPHY		128
+#define TEGRA186_RESET_QSPI			129
+#define TEGRA186_RESET_TSECB			130
+#define TEGRA186_RESET_VI_I2C			131
+#define TEGRA186_RESET_UARTE			132
+#define TEGRA186_RESET_TOP_GTE			133
+#define TEGRA186_RESET_SHSP			134
+#define TEGRA186_RESET_PEX_USB_UPHY_L5		135
+#define TEGRA186_RESET_PEX_USB_UPHY_L4		136
+#define TEGRA186_RESET_PEX_USB_UPHY_L3		137
+#define TEGRA186_RESET_PEX_USB_UPHY_L2		138
+#define TEGRA186_RESET_PEX_USB_UPHY_L1		139
+#define TEGRA186_RESET_PEX_USB_UPHY_L0		140
+#define TEGRA186_RESET_PEX_USB_UPHY_PLL1	141
+#define TEGRA186_RESET_PEX_USB_UPHY_PLL0	142
+#define TEGRA186_RESET_TSCTNVI			143
+#define TEGRA186_RESET_EXTPERIPH4		144
+#define TEGRA186_RESET_DSIPADCTL		145
+#define TEGRA186_RESET_AUD_MCLK			146
+#define TEGRA186_RESET_MPHY_CLK_CTL		147
+#define TEGRA186_RESET_MPHY_L1_RX		148
+#define TEGRA186_RESET_MPHY_L1_TX		149
+#define TEGRA186_RESET_UFSHC_LP			150
+#define TEGRA186_RESET_BPMP_NIC			151
+#define TEGRA186_RESET_BPMP_NSYSPORESET		152
+#define TEGRA186_RESET_BPMP_NRESET		153
+#define TEGRA186_RESET_BPMP_DBGRESETN		154
+#define TEGRA186_RESET_BPMP_PRESETDBGN		155
+#define TEGRA186_RESET_BPMP_PM			156
+#define TEGRA186_RESET_BPMP_CVC			157
+#define TEGRA186_RESET_BPMP_DMA			158
+#define TEGRA186_RESET_BPMP_HSP			159
+#define TEGRA186_RESET_TSCTNBPMP		160
+#define TEGRA186_RESET_BPMP_TKE			161
+#define TEGRA186_RESET_BPMP_GTE			162
+#define TEGRA186_RESET_BPMP_PM_ACTMON		163
+#define TEGRA186_RESET_AON_NIC			164
+#define TEGRA186_RESET_AON_NSYSPORESET		165
+#define TEGRA186_RESET_AON_NRESET		166
+#define TEGRA186_RESET_AON_DBGRESETN		167
+#define TEGRA186_RESET_AON_PRESETDBGN		168
+#define TEGRA186_RESET_AON_ACTMON		169
+#define TEGRA186_RESET_AOPM			170
+#define TEGRA186_RESET_AOVC			171
+#define TEGRA186_RESET_AON_DMA			172
+#define TEGRA186_RESET_AON_GPIO			173
+#define TEGRA186_RESET_AON_HSP			174
+#define TEGRA186_RESET_TSCTNAON			175
+#define TEGRA186_RESET_AON_TKE			176
+#define TEGRA186_RESET_AON_GTE			177
+#define TEGRA186_RESET_SCE_NIC			178
+#define TEGRA186_RESET_SCE_NSYSPORESET		179
+#define TEGRA186_RESET_SCE_NRESET		180
+#define TEGRA186_RESET_SCE_DBGRESETN		181
+#define TEGRA186_RESET_SCE_PRESETDBGN		182
+#define TEGRA186_RESET_SCE_ACTMON		183
+#define TEGRA186_RESET_SCE_PM			184
+#define TEGRA186_RESET_SCE_DMA			185
+#define TEGRA186_RESET_SCE_HSP			186
+#define TEGRA186_RESET_TSCTNSCE			187
+#define TEGRA186_RESET_SCE_TKE			188
+#define TEGRA186_RESET_SCE_GTE			189
+#define TEGRA186_RESET_SCE_CFG			190
+#define TEGRA186_RESET_ADSP_ALL			191
+/** @brief controls the power up/down sequence of UFSHC PSW partition. Controls LP_PWR_READY, LP_ISOL_EN, and LP_RESET_N signals */
+#define TEGRA186_RESET_UFSHC_LP_SEQ		192
+#define TEGRA186_RESET_SIZE			193
+
+#endif
-- 
2.9.0

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The Inter-VM communication (IVC) is a communication protocol which is
designed for interprocessor communication (IPC) or the communication
between the hypervisor and the virtual machine with a guest OS.

Message channels are used to communicate between processors. They are
backed by DRAM or SRAM, so care must be taken to maintain coherence of
data.

The IVC library maintains memory-based descriptors for the transmission
and reception channels as well as the data coherence of the counter and
payload. Clients, such as the driver for the BPMP firmware, can use the
library to exchange messages with remote processors.

Based on work by Peter Newman <pnewman-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
<josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Changes in v3:
- use a more object oriented design

 drivers/firmware/Kconfig        |   1 +
 drivers/firmware/Makefile       |   1 +
 drivers/firmware/tegra/Kconfig  |  13 +
 drivers/firmware/tegra/Makefile |   1 +
 drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/ivc.h         | 109 +++++++
 6 files changed, 808 insertions(+)
 create mode 100644 drivers/firmware/tegra/Kconfig
 create mode 100644 drivers/firmware/tegra/Makefile
 create mode 100644 drivers/firmware/tegra/ivc.c
 create mode 100644 include/soc/tegra/ivc.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 0e22f241403b..e8339c86f52a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -209,5 +209,6 @@ config HAVE_ARM_SMCCC
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
+source "drivers/firmware/tegra/Kconfig"
 
 endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 44a59dcfc398..78a06d5513ff 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -25,3 +25,4 @@ obj-y				+= broadcom/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-$(CONFIG_EFI)		+= efi/
 obj-$(CONFIG_UEFI_CPER)		+= efi/
+obj-y				+= tegra/
diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
new file mode 100644
index 000000000000..1fa3e4e136a5
--- /dev/null
+++ b/drivers/firmware/tegra/Kconfig
@@ -0,0 +1,13 @@
+menu "Tegra firmware driver"
+
+config TEGRA_IVC
+	bool "Tegra IVC protocol"
+	depends on ARCH_TEGRA
+	help
+	  IVC (Inter-VM Communication) protocol is part of the IPC
+	  (Inter Processor Communication) framework on Tegra. It maintains the
+	  data and the different commuication channels in SysRAM or RAM and
+	  keeps the content is synchronization between host CPU and remote
+	  processors.
+
+endmenu
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
new file mode 100644
index 000000000000..92e2153e8173
--- /dev/null
+++ b/drivers/firmware/tegra/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c
new file mode 100644
index 000000000000..aab92bf8d0e1
--- /dev/null
+++ b/drivers/firmware/tegra/ivc.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#include <linux/module.h>
+
+#include <soc/tegra/ivc.h>
+
+#define TEGRA_IVC_ALIGN 64
+
+/*
+ * IVC channel reset protocol.
+ *
+ * Each end uses its tx_channel.state to indicate its synchronization state.
+ */
+enum tegra_ivc_state {
+	/*
+	 * This value is zero for backwards compatibility with services that
+	 * assume channels to be initially zeroed. Such channels are in an
+	 * initially valid state, but cannot be asynchronously reset, and must
+	 * maintain a valid state at all times.
+	 *
+	 * The transmitting end can enter the established state from the sync or
+	 * ack state when it observes the receiving endpoint in the ack or
+	 * established state, indicating that has cleared the counters in our
+	 * rx_channel.
+	 */
+	TEGRA_IVC_STATE_ESTABLISHED = 0,
+
+	/*
+	 * If an endpoint is observed in the sync state, the remote endpoint is
+	 * allowed to clear the counters it owns asynchronously with respect to
+	 * the current endpoint. Therefore, the current endpoint is no longer
+	 * allowed to communicate.
+	 */
+	TEGRA_IVC_STATE_SYNC,
+
+	/*
+	 * When the transmitting end observes the receiving end in the sync
+	 * state, it can clear the w_count and r_count and transition to the ack
+	 * state. If the remote endpoint observes us in the ack state, it can
+	 * return to the established state once it has cleared its counters.
+	 */
+	TEGRA_IVC_STATE_ACK
+};
+
+/*
+ * This structure is divided into two-cache aligned parts, the first is only
+ * written through the tx.channel pointer, while the second is only written
+ * through the rx.channel pointer. This delineates ownership of the cache
+ * lines, which is critical to performance and necessary in non-cache coherent
+ * implementations.
+ */
+struct tegra_ivc_header {
+	union {
+		struct {
+			/* fields owned by the transmitting end */
+			u32 count;
+			u32 state;
+		};
+
+		u8 pad[TEGRA_IVC_ALIGN];
+	} tx;
+
+	union {
+		/* fields owned by the receiving end */
+		u32 count;
+		u8 pad[TEGRA_IVC_ALIGN];
+	} rx;
+};
+
+static inline void tegra_ivc_invalidate(struct tegra_ivc *ivc, dma_addr_t phys)
+{
+	if (!ivc->peer)
+		return;
+
+	dma_sync_single_for_cpu(ivc->peer, phys, TEGRA_IVC_ALIGN,
+				DMA_FROM_DEVICE);
+}
+
+static inline void tegra_ivc_flush(struct tegra_ivc *ivc, dma_addr_t phys)
+{
+	if (!ivc->peer)
+		return;
+
+	dma_sync_single_for_device(ivc->peer, phys, TEGRA_IVC_ALIGN,
+				   DMA_TO_DEVICE);
+}
+
+static inline bool tegra_ivc_empty(struct tegra_ivc *ivc,
+				   struct tegra_ivc_header *header)
+{
+	/*
+	 * This function performs multiple checks on the same values with
+	 * security implications, so create snapshots with ACCESS_ONCE() to
+	 * ensure that these checks use the same values.
+	 */
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * Perform an over-full check to prevent denial of service attacks
+	 * where a server could be easily fooled into believing that there's
+	 * an extremely large number of frames ready, since receivers are not
+	 * expected to check for full or over-full conditions.
+	 *
+	 * Although the channel isn't empty, this is an invalid case caused by
+	 * a potentially malicious peer, so returning empty is safer, because
+	 * it gives the impression that the channel has gone silent.
+	 */
+	if (tx - rx > ivc->num_frames)
+		return true;
+
+	return tx == rx;
+}
+
+static inline bool tegra_ivc_full(struct tegra_ivc *ivc,
+				  struct tegra_ivc_header *header)
+{
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * Invalid cases where the counters indicate that the queue is over
+	 * capacity also appear full.
+	 */
+	return tx - rx >= ivc->num_frames;
+}
+
+static inline u32 tegra_ivc_available(struct tegra_ivc *ivc,
+				      struct tegra_ivc_header *header)
+{
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * This function isn't expected to be used in scenarios where an
+	 * over-full situation can lead to denial of service attacks. See the
+	 * comment in tegra_ivc_empty() for an explanation about special
+	 * over-full considerations.
+	 */
+	return tx - rx;
+}
+
+static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
+{
+	ACCESS_ONCE(ivc->tx.channel->tx.count) =
+		ACCESS_ONCE(ivc->tx.channel->tx.count) + 1;
+
+	if (ivc->tx.position == ivc->num_frames - 1)
+		ivc->tx.position = 0;
+	else
+		ivc->tx.position++;
+}
+
+static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
+{
+	ACCESS_ONCE(ivc->rx.channel->rx.count) =
+		ACCESS_ONCE(ivc->rx.channel->rx.count) + 1;
+
+	if (ivc->rx.position == ivc->num_frames - 1)
+		ivc->rx.position = 0;
+	else
+		ivc->rx.position++;
+}
+
+static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+
+	/*
+	 * tx.channel->state is set locally, so it is not synchronized with
+	 * state from the remote peer. The remote peer cannot reset its
+	 * transmit counters until we've acknowledged its synchronization
+	 * request, so no additional synchronization is required because an
+	 * asynchronous transition of rx.channel->state to
+	 * TEGRA_IVC_STATE_ACK is not allowed.
+	 */
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -ECONNRESET;
+
+	/*
+	 * Avoid unnecessary invalidations when performing repeated accesses
+	 * to an IVC channel by checking the old queue pointers first.
+	 *
+	 * Synchronization is only necessary when these pointers indicate
+	 * empty or full.
+	 */
+	if (!tegra_ivc_empty(ivc, ivc->rx.channel))
+		return 0;
+
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
+
+	if (tegra_ivc_empty(ivc, ivc->rx.channel))
+		return -ENOSPC;
+
+	return 0;
+}
+
+static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, rx.count);
+
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -ECONNRESET;
+
+	if (!tegra_ivc_full(ivc, ivc->tx.channel))
+		return 0;
+
+	tegra_ivc_invalidate(ivc, ivc->tx.phys + offset);
+
+	if (tegra_ivc_full(ivc, ivc->tx.channel))
+		return -ENOSPC;
+
+	return 0;
+}
+
+static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
+				  struct tegra_ivc_header *header,
+				  unsigned int frame)
+{
+	BUG_ON(frame >= ivc->num_frames);
+
+	return (void *)(header + 1) + ivc->frame_size * frame;
+}
+
+static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
+					      dma_addr_t phys,
+					      unsigned int frame)
+{
+	unsigned long offset;
+
+	BUG_ON(!ivc->peer);
+	BUG_ON(frame >= ivc->num_frames);
+
+	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
+
+	return phys + offset;
+}
+
+static inline void tegra_ivc_invalidate_frame(struct tegra_ivc *ivc,
+					      dma_addr_t phys,
+					      unsigned int frame,
+					      unsigned int offset,
+					      size_t size)
+{
+	if (!ivc->peer)
+		return;
+
+	phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
+
+	dma_sync_single_for_cpu(ivc->peer, phys, size, DMA_FROM_DEVICE);
+}
+
+static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
+					 dma_addr_t phys,
+					 unsigned int frame,
+					 unsigned int offset,
+					 size_t size)
+{
+	if (!ivc->peer)
+		return;
+
+	phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
+
+	dma_sync_single_for_device(ivc->peer, phys, size, DMA_TO_DEVICE);
+}
+
+/* directly peek at the next frame rx'ed */
+void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc)
+{
+	int err;
+
+	err = tegra_ivc_check_read(ivc);
+	if (err)
+		return ERR_PTR(err);
+
+	/*
+	 * Order observation of w_pos potentially indicating new data before
+	 * data read.
+	 */
+	smp_rmb();
+
+	tegra_ivc_invalidate_frame(ivc, ivc->rx.phys, ivc->rx.position, 0,
+				   ivc->frame_size);
+
+	return tegra_ivc_frame_virt(ivc, ivc->rx.channel, ivc->rx.position);
+}
+EXPORT_SYMBOL(tegra_ivc_read_get_next_frame);
+
+int tegra_ivc_read_advance(struct tegra_ivc *ivc)
+{
+	unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
+	unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
+	int err;
+
+	/*
+	 * No read barriers or synchronization here: the caller is expected to
+	 * have already observed the channel non-empty. This check is just to
+	 * catch programming errors.
+	 */
+	err = tegra_ivc_check_read(ivc);
+	if (err)
+		return err;
+
+	tegra_ivc_advance_rx(ivc);
+
+	tegra_ivc_flush(ivc, ivc->rx.phys + rx);
+
+	/*
+	 * Ensure our write to r_pos occurs before our read from w_pos.
+	 */
+	smp_mb();
+
+	/*
+	 * Notify only upon transition from full to non-full.
+	 * The available count can only asynchronously increase, so the
+	 * worst possible side-effect will be a spurious notification.
+	 */
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + tx);
+
+	if (tegra_ivc_available(ivc, ivc->rx.channel) == ivc->num_frames - 1)
+		ivc->notify(ivc, ivc->notify_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_read_advance);
+
+/* directly poke at the next frame to be tx'ed */
+void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc)
+{
+	int err;
+
+	err = tegra_ivc_check_write(ivc);
+	if (err)
+		return ERR_PTR(err);
+
+	return tegra_ivc_frame_virt(ivc, ivc->tx.channel, ivc->tx.position);
+}
+EXPORT_SYMBOL(tegra_ivc_write_get_next_frame);
+
+/* advance the tx buffer */
+int tegra_ivc_write_advance(struct tegra_ivc *ivc)
+{
+	unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
+	unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
+	int err;
+
+	err = tegra_ivc_check_write(ivc);
+	if (err)
+		return err;
+
+	tegra_ivc_flush_frame(ivc, ivc->tx.phys, ivc->tx.position, 0,
+			      ivc->frame_size);
+
+	/*
+	 * Order any possible stores to the frame before update of w_pos.
+	 */
+	smp_wmb();
+
+	tegra_ivc_advance_tx(ivc);
+	tegra_ivc_flush(ivc, ivc->tx.phys + tx);
+
+	/*
+	 * Ensure our write to w_pos occurs before our read from r_pos.
+	 */
+	smp_mb();
+
+	/*
+	 * Notify only upon transition from empty to non-empty.
+	 * The available count can only asynchronously decrease, so the
+	 * worst possible side-effect will be a spurious notification.
+	 */
+	tegra_ivc_invalidate(ivc, ivc->tx.phys + rx);
+
+	if (tegra_ivc_available(ivc, ivc->tx.channel) == 1)
+		ivc->notify(ivc, ivc->notify_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_write_advance);
+
+void tegra_ivc_reset(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+
+	ivc->tx.channel->tx.state = TEGRA_IVC_STATE_SYNC;
+	tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+	ivc->notify(ivc, ivc->notify_data);
+}
+EXPORT_SYMBOL(tegra_ivc_reset);
+
+/*
+ * =======================================================
+ *  IVC State Transition Table - see tegra_ivc_notified()
+ * =======================================================
+ *
+ *	local	remote	action
+ *	-----	------	-----------------------------------
+ *	SYNC	EST	<none>
+ *	SYNC	ACK	reset counters; move to EST; notify
+ *	SYNC	SYNC	reset counters; move to ACK; notify
+ *	ACK	EST	move to EST; notify
+ *	ACK	ACK	move to EST; notify
+ *	ACK	SYNC	reset counters; move to ACK; notify
+ *	EST	EST	<none>
+ *	EST	ACK	<none>
+ *	EST	SYNC	reset counters; move to ACK; notify
+ *
+ * ===============================================================
+ */
+
+int tegra_ivc_notified(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+	enum tegra_ivc_state state;
+
+	/* Copy the receiver's state out of shared memory. */
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
+	state = ACCESS_ONCE(ivc->rx.channel->tx.state);
+
+	if (state == TEGRA_IVC_STATE_SYNC) {
+		offset =  offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * Order observation of TEGRA_IVC_STATE_SYNC before stores
+		 * clearing tx.channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Reset tx.channel counters. The remote end is in the SYNC
+		 * state and won't make progress until we change our state,
+		 * so the counters are not in use at this time.
+		 */
+		ivc->tx.channel->tx.count = 0;
+		ivc->rx.channel->rx.count = 0;
+
+		ivc->tx.position = 0;
+		ivc->rx.position = 0;
+
+		/*
+		 * Ensure that counters appear cleared before new state can be
+		 * observed.
+		 */
+		smp_wmb();
+
+		/*
+		 * Move to ACK state. We have just cleared our counters, so it
+		 * is now safe for the remote end to start using these values.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ACK;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_SYNC &&
+		   state == TEGRA_IVC_STATE_ACK) {
+		offset = offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * Order observation of ivc_state_sync before stores clearing
+		 * tx_channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Reset tx.channel counters. The remote end is in the ACK
+		 * state and won't make progress until we change our state,
+		 * so the counters are not in use at this time.
+		 */
+		ivc->tx.channel->tx.count = 0;
+		ivc->rx.channel->rx.count = 0;
+
+		ivc->tx.position = 0;
+		ivc->rx.position = 0;
+
+		/*
+		 * Ensure that counters appear cleared before new state can be
+		 * observed.
+		 */
+		smp_wmb();
+
+		/*
+		 * Move to ESTABLISHED state. We know that the remote end has
+		 * already cleared its counters, so it is safe to start
+		 * writing/reading on this channel.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_ACK) {
+		offset = offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * At this point, we have observed the peer to be in either
+		 * the ACK or ESTABLISHED state. Next, order observation of
+		 * peer state before storing to tx.channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Move to ESTABLISHED state. We know that we have previously
+		 * cleared our counters, and we know that the remote end has
+		 * cleared its counters, so it is safe to start writing/reading
+		 * on this channel.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else {
+		/*
+		 * There is no need to handle any further action. Either the
+		 * channel is already fully established, or we are waiting for
+		 * the remote end to catch up with our current state. Refer
+		 * to the diagram in "IVC State Transition Table" above.
+		 */
+	}
+
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -EAGAIN;
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_notified);
+
+size_t tegra_ivc_align(size_t size)
+{
+	return (size + (TEGRA_IVC_ALIGN - 1)) & ~(TEGRA_IVC_ALIGN - 1);
+}
+EXPORT_SYMBOL(tegra_ivc_align);
+
+unsigned tegra_ivc_total_queue_size(unsigned queue_size)
+{
+	if (queue_size & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("%s: queue_size (%u) must be %u-byte aligned\n",
+		       __func__, queue_size, TEGRA_IVC_ALIGN);
+		return 0;
+	}
+
+	return queue_size + sizeof(struct tegra_ivc_header);
+}
+EXPORT_SYMBOL(tegra_ivc_total_queue_size);
+
+static int check_ivc_params(unsigned long base1, unsigned long base2,
+			    unsigned int num_frames, size_t frame_size)
+{
+	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
+	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
+	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
+
+	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
+		pr_err("num_frames * frame_size overflows\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * The headers must at least be aligned enough for counters
+	 * to be accessed atomically.
+	 */
+	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("IVC channel start not aligned: %lx\n", base1);
+		return -EINVAL;
+	}
+
+	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("IVC channel start not aligned: %lx\n", base2);
+		return -EINVAL;
+	}
+
+	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("frame size not adequately aligned: %zu\n", frame_size);
+		return -EINVAL;
+	}
+
+	if (base1 < base2) {
+		if (base1 + frame_size * num_frames > base2) {
+			pr_err("queue regions overlap: %lx + %zx, %zx\n",
+			       base1, frame_size, frame_size * num_frames);
+			return -EINVAL;
+		}
+	} else {
+		if (base2 + frame_size * num_frames > base1) {
+			pr_err("queue regions overlap: %lx + %zx, %zx\n",
+			       base2, frame_size, frame_size * num_frames);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
+		   void __iomem *rx_virt, dma_addr_t rx_phys,
+		   void __iomem *tx_virt, dma_addr_t tx_phys,
+		   unsigned int num_frames, size_t frame_size,
+		   void (*notify)(struct tegra_ivc *ivc, void *data),
+		   void *data)
+{
+	size_t queue_size;
+	int err;
+
+	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
+			       num_frames, frame_size);
+	if (err < 0)
+		return err;
+
+	BUG_ON(!ivc);
+	BUG_ON(!notify);
+
+	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
+
+	/*
+	 * All sizes that can be returned by communication functions should
+	 * fit in an int.
+	 */
+	if (frame_size > INT_MAX)
+		return -E2BIG;
+
+	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
+	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;
+
+	if (peer) {
+		if (rx_phys != DMA_ERROR_CODE) {
+			ivc->rx.phys = rx_phys;
+			ivc->tx.phys = tx_phys;
+		} else {
+			ivc->rx.phys = dma_map_single(peer, ivc->rx.channel,
+						      queue_size,
+						      DMA_BIDIRECTIONAL);
+			if (ivc->rx.phys == DMA_ERROR_CODE)
+				return -ENOMEM;
+
+			ivc->tx.phys = dma_map_single(peer, ivc->tx.channel,
+						      queue_size,
+						      DMA_BIDIRECTIONAL);
+			if (ivc->tx.phys == DMA_ERROR_CODE) {
+				dma_unmap_single(peer, ivc->rx.phys,
+						 queue_size,
+						 DMA_BIDIRECTIONAL);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	ivc->peer = peer;
+	ivc->notify = notify;
+	ivc->notify_data = data;
+	ivc->frame_size = frame_size;
+	ivc->num_frames = num_frames;
+
+	/*
+	 * These values aren't necessarily correct until the channel has been
+	 * reset.
+	 */
+	ivc->tx.position = 0;
+	ivc->rx.position = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_init);
diff --git a/include/soc/tegra/ivc.h b/include/soc/tegra/ivc.h
new file mode 100644
index 000000000000..af9a54a54e45
--- /dev/null
+++ b/include/soc/tegra/ivc.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __TEGRA_IVC_H
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/types.h>
+
+struct tegra_ivc_header;
+
+struct tegra_ivc {
+	struct device *peer;
+
+	struct {
+		struct tegra_ivc_header *channel;
+		dma_addr_t phys;
+		u32 position;
+	} rx, tx;
+
+	void (*notify)(struct tegra_ivc *ivc, void *data);
+	void *notify_data;
+
+	unsigned int num_frames;
+	size_t frame_size;
+};
+
+/**
+ * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
+ * @ivc		pointer of the IVC channel
+ *
+ * Peek at the next frame to be received, without removing it from
+ * the queue.
+ *
+ * Returns a pointer to the frame, or an error encoded pointer.
+ */
+void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_read_advance - Advance the read queue
+ * @ivc		pointer of the IVC channel
+ *
+ * Advance the read queue
+ *
+ * Returns 0, or a negative error value if failed.
+ */
+int tegra_ivc_read_advance(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_write_get_next_frame - Poke at the next frame to transmit
+ * @ivc		pointer of the IVC channel
+ *
+ * Get access to the next frame.
+ *
+ * Returns a pointer to the frame, or an error encoded pointer.
+ */
+void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_write_advance - Advance the write queue
+ * @ivc		pointer of the IVC channel
+ *
+ * Advance the write queue
+ *
+ * Returns 0, or a negative error value if failed.
+ */
+int tegra_ivc_write_advance(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_notified - handle internal messages
+ * @ivc		pointer of the IVC channel
+ *
+ * This function must be called following every notification.
+ *
+ * Returns 0 if the channel is ready for communication, or -EAGAIN if a channel
+ * reset is in progress.
+ */
+int tegra_ivc_notified(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_reset - initiates a reset of the shared memory state
+ * @ivc		pointer of the IVC channel
+ *
+ * This function must be called after a channel is reserved before it is used
+ * for communication. The channel will be ready for use when a subsequent call
+ * to notify the remote of the channel reset.
+ */
+void tegra_ivc_reset(struct tegra_ivc *ivc);
+
+size_t tegra_ivc_align(size_t size);
+unsigned tegra_ivc_total_queue_size(unsigned queue_size);
+int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
+		   void __iomem *rx_virt, dma_addr_t rx_phys,
+		   void __iomem *tx_virt, dma_addr_t tx_phys,
+		   unsigned int num_frames, size_t frame_size,
+		   void (*notify)(struct tegra_ivc *ivc, void *data),
+		   void *data);
+
+#endif /* __TEGRA_IVC_H */
-- 
2.9.0

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

The Inter-VM communication (IVC) is a communication protocol which is
designed for interprocessor communication (IPC) or the communication
between the hypervisor and the virtual machine with a guest OS.

Message channels are used to communicate between processors. They are
backed by DRAM or SRAM, so care must be taken to maintain coherence of
data.

The IVC library maintains memory-based descriptors for the transmission
and reception channels as well as the data coherence of the counter and
payload. Clients, such as the driver for the BPMP firmware, can use the
library to exchange messages with remote processors.

Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
<josephl@nvidia.com>.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- use a more object oriented design

 drivers/firmware/Kconfig        |   1 +
 drivers/firmware/Makefile       |   1 +
 drivers/firmware/tegra/Kconfig  |  13 +
 drivers/firmware/tegra/Makefile |   1 +
 drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/ivc.h         | 109 +++++++
 6 files changed, 808 insertions(+)
 create mode 100644 drivers/firmware/tegra/Kconfig
 create mode 100644 drivers/firmware/tegra/Makefile
 create mode 100644 drivers/firmware/tegra/ivc.c
 create mode 100644 include/soc/tegra/ivc.h

diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index 0e22f241403b..e8339c86f52a 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -209,5 +209,6 @@ config HAVE_ARM_SMCCC
 source "drivers/firmware/broadcom/Kconfig"
 source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
+source "drivers/firmware/tegra/Kconfig"
 
 endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index 44a59dcfc398..78a06d5513ff 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -25,3 +25,4 @@ obj-y				+= broadcom/
 obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-$(CONFIG_EFI)		+= efi/
 obj-$(CONFIG_UEFI_CPER)		+= efi/
+obj-y				+= tegra/
diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
new file mode 100644
index 000000000000..1fa3e4e136a5
--- /dev/null
+++ b/drivers/firmware/tegra/Kconfig
@@ -0,0 +1,13 @@
+menu "Tegra firmware driver"
+
+config TEGRA_IVC
+	bool "Tegra IVC protocol"
+	depends on ARCH_TEGRA
+	help
+	  IVC (Inter-VM Communication) protocol is part of the IPC
+	  (Inter Processor Communication) framework on Tegra. It maintains the
+	  data and the different commuication channels in SysRAM or RAM and
+	  keeps the content is synchronization between host CPU and remote
+	  processors.
+
+endmenu
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
new file mode 100644
index 000000000000..92e2153e8173
--- /dev/null
+++ b/drivers/firmware/tegra/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
diff --git a/drivers/firmware/tegra/ivc.c b/drivers/firmware/tegra/ivc.c
new file mode 100644
index 000000000000..aab92bf8d0e1
--- /dev/null
+++ b/drivers/firmware/tegra/ivc.c
@@ -0,0 +1,683 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#include <linux/module.h>
+
+#include <soc/tegra/ivc.h>
+
+#define TEGRA_IVC_ALIGN 64
+
+/*
+ * IVC channel reset protocol.
+ *
+ * Each end uses its tx_channel.state to indicate its synchronization state.
+ */
+enum tegra_ivc_state {
+	/*
+	 * This value is zero for backwards compatibility with services that
+	 * assume channels to be initially zeroed. Such channels are in an
+	 * initially valid state, but cannot be asynchronously reset, and must
+	 * maintain a valid state at all times.
+	 *
+	 * The transmitting end can enter the established state from the sync or
+	 * ack state when it observes the receiving endpoint in the ack or
+	 * established state, indicating that has cleared the counters in our
+	 * rx_channel.
+	 */
+	TEGRA_IVC_STATE_ESTABLISHED = 0,
+
+	/*
+	 * If an endpoint is observed in the sync state, the remote endpoint is
+	 * allowed to clear the counters it owns asynchronously with respect to
+	 * the current endpoint. Therefore, the current endpoint is no longer
+	 * allowed to communicate.
+	 */
+	TEGRA_IVC_STATE_SYNC,
+
+	/*
+	 * When the transmitting end observes the receiving end in the sync
+	 * state, it can clear the w_count and r_count and transition to the ack
+	 * state. If the remote endpoint observes us in the ack state, it can
+	 * return to the established state once it has cleared its counters.
+	 */
+	TEGRA_IVC_STATE_ACK
+};
+
+/*
+ * This structure is divided into two-cache aligned parts, the first is only
+ * written through the tx.channel pointer, while the second is only written
+ * through the rx.channel pointer. This delineates ownership of the cache
+ * lines, which is critical to performance and necessary in non-cache coherent
+ * implementations.
+ */
+struct tegra_ivc_header {
+	union {
+		struct {
+			/* fields owned by the transmitting end */
+			u32 count;
+			u32 state;
+		};
+
+		u8 pad[TEGRA_IVC_ALIGN];
+	} tx;
+
+	union {
+		/* fields owned by the receiving end */
+		u32 count;
+		u8 pad[TEGRA_IVC_ALIGN];
+	} rx;
+};
+
+static inline void tegra_ivc_invalidate(struct tegra_ivc *ivc, dma_addr_t phys)
+{
+	if (!ivc->peer)
+		return;
+
+	dma_sync_single_for_cpu(ivc->peer, phys, TEGRA_IVC_ALIGN,
+				DMA_FROM_DEVICE);
+}
+
+static inline void tegra_ivc_flush(struct tegra_ivc *ivc, dma_addr_t phys)
+{
+	if (!ivc->peer)
+		return;
+
+	dma_sync_single_for_device(ivc->peer, phys, TEGRA_IVC_ALIGN,
+				   DMA_TO_DEVICE);
+}
+
+static inline bool tegra_ivc_empty(struct tegra_ivc *ivc,
+				   struct tegra_ivc_header *header)
+{
+	/*
+	 * This function performs multiple checks on the same values with
+	 * security implications, so create snapshots with ACCESS_ONCE() to
+	 * ensure that these checks use the same values.
+	 */
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * Perform an over-full check to prevent denial of service attacks
+	 * where a server could be easily fooled into believing that there's
+	 * an extremely large number of frames ready, since receivers are not
+	 * expected to check for full or over-full conditions.
+	 *
+	 * Although the channel isn't empty, this is an invalid case caused by
+	 * a potentially malicious peer, so returning empty is safer, because
+	 * it gives the impression that the channel has gone silent.
+	 */
+	if (tx - rx > ivc->num_frames)
+		return true;
+
+	return tx == rx;
+}
+
+static inline bool tegra_ivc_full(struct tegra_ivc *ivc,
+				  struct tegra_ivc_header *header)
+{
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * Invalid cases where the counters indicate that the queue is over
+	 * capacity also appear full.
+	 */
+	return tx - rx >= ivc->num_frames;
+}
+
+static inline u32 tegra_ivc_available(struct tegra_ivc *ivc,
+				      struct tegra_ivc_header *header)
+{
+	u32 tx = ACCESS_ONCE(header->tx.count);
+	u32 rx = ACCESS_ONCE(header->rx.count);
+
+	/*
+	 * This function isn't expected to be used in scenarios where an
+	 * over-full situation can lead to denial of service attacks. See the
+	 * comment in tegra_ivc_empty() for an explanation about special
+	 * over-full considerations.
+	 */
+	return tx - rx;
+}
+
+static inline void tegra_ivc_advance_tx(struct tegra_ivc *ivc)
+{
+	ACCESS_ONCE(ivc->tx.channel->tx.count) =
+		ACCESS_ONCE(ivc->tx.channel->tx.count) + 1;
+
+	if (ivc->tx.position == ivc->num_frames - 1)
+		ivc->tx.position = 0;
+	else
+		ivc->tx.position++;
+}
+
+static inline void tegra_ivc_advance_rx(struct tegra_ivc *ivc)
+{
+	ACCESS_ONCE(ivc->rx.channel->rx.count) =
+		ACCESS_ONCE(ivc->rx.channel->rx.count) + 1;
+
+	if (ivc->rx.position == ivc->num_frames - 1)
+		ivc->rx.position = 0;
+	else
+		ivc->rx.position++;
+}
+
+static inline int tegra_ivc_check_read(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+
+	/*
+	 * tx.channel->state is set locally, so it is not synchronized with
+	 * state from the remote peer. The remote peer cannot reset its
+	 * transmit counters until we've acknowledged its synchronization
+	 * request, so no additional synchronization is required because an
+	 * asynchronous transition of rx.channel->state to
+	 * TEGRA_IVC_STATE_ACK is not allowed.
+	 */
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -ECONNRESET;
+
+	/*
+	 * Avoid unnecessary invalidations when performing repeated accesses
+	 * to an IVC channel by checking the old queue pointers first.
+	 *
+	 * Synchronization is only necessary when these pointers indicate
+	 * empty or full.
+	 */
+	if (!tegra_ivc_empty(ivc, ivc->rx.channel))
+		return 0;
+
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
+
+	if (tegra_ivc_empty(ivc, ivc->rx.channel))
+		return -ENOSPC;
+
+	return 0;
+}
+
+static inline int tegra_ivc_check_write(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, rx.count);
+
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -ECONNRESET;
+
+	if (!tegra_ivc_full(ivc, ivc->tx.channel))
+		return 0;
+
+	tegra_ivc_invalidate(ivc, ivc->tx.phys + offset);
+
+	if (tegra_ivc_full(ivc, ivc->tx.channel))
+		return -ENOSPC;
+
+	return 0;
+}
+
+static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
+				  struct tegra_ivc_header *header,
+				  unsigned int frame)
+{
+	BUG_ON(frame >= ivc->num_frames);
+
+	return (void *)(header + 1) + ivc->frame_size * frame;
+}
+
+static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
+					      dma_addr_t phys,
+					      unsigned int frame)
+{
+	unsigned long offset;
+
+	BUG_ON(!ivc->peer);
+	BUG_ON(frame >= ivc->num_frames);
+
+	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
+
+	return phys + offset;
+}
+
+static inline void tegra_ivc_invalidate_frame(struct tegra_ivc *ivc,
+					      dma_addr_t phys,
+					      unsigned int frame,
+					      unsigned int offset,
+					      size_t size)
+{
+	if (!ivc->peer)
+		return;
+
+	phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
+
+	dma_sync_single_for_cpu(ivc->peer, phys, size, DMA_FROM_DEVICE);
+}
+
+static inline void tegra_ivc_flush_frame(struct tegra_ivc *ivc,
+					 dma_addr_t phys,
+					 unsigned int frame,
+					 unsigned int offset,
+					 size_t size)
+{
+	if (!ivc->peer)
+		return;
+
+	phys = tegra_ivc_frame_phys(ivc, phys, frame) + offset;
+
+	dma_sync_single_for_device(ivc->peer, phys, size, DMA_TO_DEVICE);
+}
+
+/* directly peek at the next frame rx'ed */
+void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc)
+{
+	int err;
+
+	err = tegra_ivc_check_read(ivc);
+	if (err)
+		return ERR_PTR(err);
+
+	/*
+	 * Order observation of w_pos potentially indicating new data before
+	 * data read.
+	 */
+	smp_rmb();
+
+	tegra_ivc_invalidate_frame(ivc, ivc->rx.phys, ivc->rx.position, 0,
+				   ivc->frame_size);
+
+	return tegra_ivc_frame_virt(ivc, ivc->rx.channel, ivc->rx.position);
+}
+EXPORT_SYMBOL(tegra_ivc_read_get_next_frame);
+
+int tegra_ivc_read_advance(struct tegra_ivc *ivc)
+{
+	unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
+	unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
+	int err;
+
+	/*
+	 * No read barriers or synchronization here: the caller is expected to
+	 * have already observed the channel non-empty. This check is just to
+	 * catch programming errors.
+	 */
+	err = tegra_ivc_check_read(ivc);
+	if (err)
+		return err;
+
+	tegra_ivc_advance_rx(ivc);
+
+	tegra_ivc_flush(ivc, ivc->rx.phys + rx);
+
+	/*
+	 * Ensure our write to r_pos occurs before our read from w_pos.
+	 */
+	smp_mb();
+
+	/*
+	 * Notify only upon transition from full to non-full.
+	 * The available count can only asynchronously increase, so the
+	 * worst possible side-effect will be a spurious notification.
+	 */
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + tx);
+
+	if (tegra_ivc_available(ivc, ivc->rx.channel) == ivc->num_frames - 1)
+		ivc->notify(ivc, ivc->notify_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_read_advance);
+
+/* directly poke at the next frame to be tx'ed */
+void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc)
+{
+	int err;
+
+	err = tegra_ivc_check_write(ivc);
+	if (err)
+		return ERR_PTR(err);
+
+	return tegra_ivc_frame_virt(ivc, ivc->tx.channel, ivc->tx.position);
+}
+EXPORT_SYMBOL(tegra_ivc_write_get_next_frame);
+
+/* advance the tx buffer */
+int tegra_ivc_write_advance(struct tegra_ivc *ivc)
+{
+	unsigned int tx = offsetof(struct tegra_ivc_header, tx.count);
+	unsigned int rx = offsetof(struct tegra_ivc_header, rx.count);
+	int err;
+
+	err = tegra_ivc_check_write(ivc);
+	if (err)
+		return err;
+
+	tegra_ivc_flush_frame(ivc, ivc->tx.phys, ivc->tx.position, 0,
+			      ivc->frame_size);
+
+	/*
+	 * Order any possible stores to the frame before update of w_pos.
+	 */
+	smp_wmb();
+
+	tegra_ivc_advance_tx(ivc);
+	tegra_ivc_flush(ivc, ivc->tx.phys + tx);
+
+	/*
+	 * Ensure our write to w_pos occurs before our read from r_pos.
+	 */
+	smp_mb();
+
+	/*
+	 * Notify only upon transition from empty to non-empty.
+	 * The available count can only asynchronously decrease, so the
+	 * worst possible side-effect will be a spurious notification.
+	 */
+	tegra_ivc_invalidate(ivc, ivc->tx.phys + rx);
+
+	if (tegra_ivc_available(ivc, ivc->tx.channel) == 1)
+		ivc->notify(ivc, ivc->notify_data);
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_write_advance);
+
+void tegra_ivc_reset(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+
+	ivc->tx.channel->tx.state = TEGRA_IVC_STATE_SYNC;
+	tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+	ivc->notify(ivc, ivc->notify_data);
+}
+EXPORT_SYMBOL(tegra_ivc_reset);
+
+/*
+ * =======================================================
+ *  IVC State Transition Table - see tegra_ivc_notified()
+ * =======================================================
+ *
+ *	local	remote	action
+ *	-----	------	-----------------------------------
+ *	SYNC	EST	<none>
+ *	SYNC	ACK	reset counters; move to EST; notify
+ *	SYNC	SYNC	reset counters; move to ACK; notify
+ *	ACK	EST	move to EST; notify
+ *	ACK	ACK	move to EST; notify
+ *	ACK	SYNC	reset counters; move to ACK; notify
+ *	EST	EST	<none>
+ *	EST	ACK	<none>
+ *	EST	SYNC	reset counters; move to ACK; notify
+ *
+ * ===============================================================
+ */
+
+int tegra_ivc_notified(struct tegra_ivc *ivc)
+{
+	unsigned int offset = offsetof(struct tegra_ivc_header, tx.count);
+	enum tegra_ivc_state state;
+
+	/* Copy the receiver's state out of shared memory. */
+	tegra_ivc_invalidate(ivc, ivc->rx.phys + offset);
+	state = ACCESS_ONCE(ivc->rx.channel->tx.state);
+
+	if (state == TEGRA_IVC_STATE_SYNC) {
+		offset =  offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * Order observation of TEGRA_IVC_STATE_SYNC before stores
+		 * clearing tx.channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Reset tx.channel counters. The remote end is in the SYNC
+		 * state and won't make progress until we change our state,
+		 * so the counters are not in use at this time.
+		 */
+		ivc->tx.channel->tx.count = 0;
+		ivc->rx.channel->rx.count = 0;
+
+		ivc->tx.position = 0;
+		ivc->rx.position = 0;
+
+		/*
+		 * Ensure that counters appear cleared before new state can be
+		 * observed.
+		 */
+		smp_wmb();
+
+		/*
+		 * Move to ACK state. We have just cleared our counters, so it
+		 * is now safe for the remote end to start using these values.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ACK;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_SYNC &&
+		   state == TEGRA_IVC_STATE_ACK) {
+		offset = offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * Order observation of ivc_state_sync before stores clearing
+		 * tx_channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Reset tx.channel counters. The remote end is in the ACK
+		 * state and won't make progress until we change our state,
+		 * so the counters are not in use at this time.
+		 */
+		ivc->tx.channel->tx.count = 0;
+		ivc->rx.channel->rx.count = 0;
+
+		ivc->tx.position = 0;
+		ivc->rx.position = 0;
+
+		/*
+		 * Ensure that counters appear cleared before new state can be
+		 * observed.
+		 */
+		smp_wmb();
+
+		/*
+		 * Move to ESTABLISHED state. We know that the remote end has
+		 * already cleared its counters, so it is safe to start
+		 * writing/reading on this channel.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else if (ivc->tx.channel->tx.state == TEGRA_IVC_STATE_ACK) {
+		offset = offsetof(struct tegra_ivc_header, tx.count);
+
+		/*
+		 * At this point, we have observed the peer to be in either
+		 * the ACK or ESTABLISHED state. Next, order observation of
+		 * peer state before storing to tx.channel.
+		 */
+		smp_rmb();
+
+		/*
+		 * Move to ESTABLISHED state. We know that we have previously
+		 * cleared our counters, and we know that the remote end has
+		 * cleared its counters, so it is safe to start writing/reading
+		 * on this channel.
+		 */
+		ivc->tx.channel->tx.state = TEGRA_IVC_STATE_ESTABLISHED;
+		tegra_ivc_flush(ivc, ivc->tx.phys + offset);
+
+		/*
+		 * Notify remote end to observe state transition.
+		 */
+		ivc->notify(ivc, ivc->notify_data);
+
+	} else {
+		/*
+		 * There is no need to handle any further action. Either the
+		 * channel is already fully established, or we are waiting for
+		 * the remote end to catch up with our current state. Refer
+		 * to the diagram in "IVC State Transition Table" above.
+		 */
+	}
+
+	if (ivc->tx.channel->tx.state != TEGRA_IVC_STATE_ESTABLISHED)
+		return -EAGAIN;
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_notified);
+
+size_t tegra_ivc_align(size_t size)
+{
+	return (size + (TEGRA_IVC_ALIGN - 1)) & ~(TEGRA_IVC_ALIGN - 1);
+}
+EXPORT_SYMBOL(tegra_ivc_align);
+
+unsigned tegra_ivc_total_queue_size(unsigned queue_size)
+{
+	if (queue_size & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("%s: queue_size (%u) must be %u-byte aligned\n",
+		       __func__, queue_size, TEGRA_IVC_ALIGN);
+		return 0;
+	}
+
+	return queue_size + sizeof(struct tegra_ivc_header);
+}
+EXPORT_SYMBOL(tegra_ivc_total_queue_size);
+
+static int check_ivc_params(unsigned long base1, unsigned long base2,
+			    unsigned int num_frames, size_t frame_size)
+{
+	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
+	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
+	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
+
+	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
+		pr_err("num_frames * frame_size overflows\n");
+		return -EINVAL;
+	}
+
+	/*
+	 * The headers must at least be aligned enough for counters
+	 * to be accessed atomically.
+	 */
+	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("IVC channel start not aligned: %lx\n", base1);
+		return -EINVAL;
+	}
+
+	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("IVC channel start not aligned: %lx\n", base2);
+		return -EINVAL;
+	}
+
+	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
+		pr_err("frame size not adequately aligned: %zu\n", frame_size);
+		return -EINVAL;
+	}
+
+	if (base1 < base2) {
+		if (base1 + frame_size * num_frames > base2) {
+			pr_err("queue regions overlap: %lx + %zx, %zx\n",
+			       base1, frame_size, frame_size * num_frames);
+			return -EINVAL;
+		}
+	} else {
+		if (base2 + frame_size * num_frames > base1) {
+			pr_err("queue regions overlap: %lx + %zx, %zx\n",
+			       base2, frame_size, frame_size * num_frames);
+			return -EINVAL;
+		}
+	}
+
+	return 0;
+}
+
+int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
+		   void __iomem *rx_virt, dma_addr_t rx_phys,
+		   void __iomem *tx_virt, dma_addr_t tx_phys,
+		   unsigned int num_frames, size_t frame_size,
+		   void (*notify)(struct tegra_ivc *ivc, void *data),
+		   void *data)
+{
+	size_t queue_size;
+	int err;
+
+	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
+			       num_frames, frame_size);
+	if (err < 0)
+		return err;
+
+	BUG_ON(!ivc);
+	BUG_ON(!notify);
+
+	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
+
+	/*
+	 * All sizes that can be returned by communication functions should
+	 * fit in an int.
+	 */
+	if (frame_size > INT_MAX)
+		return -E2BIG;
+
+	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
+	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;
+
+	if (peer) {
+		if (rx_phys != DMA_ERROR_CODE) {
+			ivc->rx.phys = rx_phys;
+			ivc->tx.phys = tx_phys;
+		} else {
+			ivc->rx.phys = dma_map_single(peer, ivc->rx.channel,
+						      queue_size,
+						      DMA_BIDIRECTIONAL);
+			if (ivc->rx.phys == DMA_ERROR_CODE)
+				return -ENOMEM;
+
+			ivc->tx.phys = dma_map_single(peer, ivc->tx.channel,
+						      queue_size,
+						      DMA_BIDIRECTIONAL);
+			if (ivc->tx.phys == DMA_ERROR_CODE) {
+				dma_unmap_single(peer, ivc->rx.phys,
+						 queue_size,
+						 DMA_BIDIRECTIONAL);
+				return -ENOMEM;
+			}
+		}
+	}
+
+	ivc->peer = peer;
+	ivc->notify = notify;
+	ivc->notify_data = data;
+	ivc->frame_size = frame_size;
+	ivc->num_frames = num_frames;
+
+	/*
+	 * These values aren't necessarily correct until the channel has been
+	 * reset.
+	 */
+	ivc->tx.position = 0;
+	ivc->rx.position = 0;
+
+	return 0;
+}
+EXPORT_SYMBOL(tegra_ivc_init);
diff --git a/include/soc/tegra/ivc.h b/include/soc/tegra/ivc.h
new file mode 100644
index 000000000000..af9a54a54e45
--- /dev/null
+++ b/include/soc/tegra/ivc.h
@@ -0,0 +1,109 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __TEGRA_IVC_H
+
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/types.h>
+
+struct tegra_ivc_header;
+
+struct tegra_ivc {
+	struct device *peer;
+
+	struct {
+		struct tegra_ivc_header *channel;
+		dma_addr_t phys;
+		u32 position;
+	} rx, tx;
+
+	void (*notify)(struct tegra_ivc *ivc, void *data);
+	void *notify_data;
+
+	unsigned int num_frames;
+	size_t frame_size;
+};
+
+/**
+ * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
+ * @ivc		pointer of the IVC channel
+ *
+ * Peek at the next frame to be received, without removing it from
+ * the queue.
+ *
+ * Returns a pointer to the frame, or an error encoded pointer.
+ */
+void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_read_advance - Advance the read queue
+ * @ivc		pointer of the IVC channel
+ *
+ * Advance the read queue
+ *
+ * Returns 0, or a negative error value if failed.
+ */
+int tegra_ivc_read_advance(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_write_get_next_frame - Poke at the next frame to transmit
+ * @ivc		pointer of the IVC channel
+ *
+ * Get access to the next frame.
+ *
+ * Returns a pointer to the frame, or an error encoded pointer.
+ */
+void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_write_advance - Advance the write queue
+ * @ivc		pointer of the IVC channel
+ *
+ * Advance the write queue
+ *
+ * Returns 0, or a negative error value if failed.
+ */
+int tegra_ivc_write_advance(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_notified - handle internal messages
+ * @ivc		pointer of the IVC channel
+ *
+ * This function must be called following every notification.
+ *
+ * Returns 0 if the channel is ready for communication, or -EAGAIN if a channel
+ * reset is in progress.
+ */
+int tegra_ivc_notified(struct tegra_ivc *ivc);
+
+/**
+ * tegra_ivc_reset - initiates a reset of the shared memory state
+ * @ivc		pointer of the IVC channel
+ *
+ * This function must be called after a channel is reserved before it is used
+ * for communication. The channel will be ready for use when a subsequent call
+ * to notify the remote of the channel reset.
+ */
+void tegra_ivc_reset(struct tegra_ivc *ivc);
+
+size_t tegra_ivc_align(size_t size);
+unsigned tegra_ivc_total_queue_size(unsigned queue_size);
+int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
+		   void __iomem *rx_virt, dma_addr_t rx_phys,
+		   void __iomem *tx_virt, dma_addr_t tx_phys,
+		   unsigned int num_frames, size_t frame_size,
+		   void (*notify)(struct tegra_ivc *ivc, void *data),
+		   void *data);
+
+#endif /* __TEGRA_IVC_H */
-- 
2.9.0

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The Boot and Power Management Processor (BPMP) is a co-processor found
on Tegra SoCs. It is designed to handle the early stages of the boot
process and offload power management tasks (such as clocks, resets,
powergates, ...) as well as system control services.

Compared to the ARM SCPI, the services provided by BPMP are message-
based rather than method-based. The BPMP firmware driver provides the
services to transmit data to and receive data from the BPMP. Users can
also register an MRQ, for which a service routine will be run when a
corresponding event is received from the firmware.

A set of messages, called the BPMP ABI, are specified for a number of
different services provided by the BPMP (such as clocks or resets).

Based on work by Sivaram Nair <sivaramn-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
<josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Changes in v3:
- use a message structure for transfers to avoid long function
  prototypes
- restructure driver for easier maintainability
- rename bpmp_abi.h to bpmp-abi.h for consistency

 drivers/firmware/tegra/Kconfig  |   12 +
 drivers/firmware/tegra/Makefile |    1 +
 drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
 include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/bpmp.h        |  122 +++
 5 files changed, 2616 insertions(+)
 create mode 100644 drivers/firmware/tegra/bpmp.c
 create mode 100644 include/soc/tegra/bpmp-abi.h
 create mode 100644 include/soc/tegra/bpmp.h

diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
index 1fa3e4e136a5..ff2730d5c468 100644
--- a/drivers/firmware/tegra/Kconfig
+++ b/drivers/firmware/tegra/Kconfig
@@ -10,4 +10,16 @@ config TEGRA_IVC
 	  keeps the content is synchronization between host CPU and remote
 	  processors.
 
+config TEGRA_BPMP
+	bool "Tegra BPMP driver"
+	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
+	help
+	  BPMP (Boot and Power Management Processor) is designed to off-loading
+	  the PM functions which include clock/DVFS/thermal/power from the CPU.
+	  It needs HSP as the HW synchronization and notification module and
+	  IVC module as the message communication protocol.
+
+	  This driver manages the IPC interface between host CPU and the
+	  firmware running on BPMP.
+
 endmenu
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
index 92e2153e8173..e34a2f79e1ad 100644
--- a/drivers/firmware/tegra/Makefile
+++ b/drivers/firmware/tegra/Makefile
@@ -1 +1,2 @@
+obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
 obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
new file mode 100644
index 000000000000..a09043b1be05
--- /dev/null
+++ b/drivers/firmware/tegra/bpmp.c
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#define DEBUG
+
+#include <linux/clk/tegra.h>
+#include <linux/mailbox_client.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+#include <soc/tegra/ivc.h>
+
+#define MSG_ACK		BIT(0)
+#define MSG_RING	BIT(1)
+
+static inline struct tegra_bpmp *
+mbox_client_to_bpmp(struct mbox_client *client)
+{
+	return container_of(client, struct tegra_bpmp, mbox.client);
+}
+
+struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct tegra_bpmp *bpmp;
+	struct device_node *np;
+
+	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
+	if (!np)
+		return ERR_PTR(-ENOENT);
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev) {
+		bpmp = ERR_PTR(-ENODEV);
+		goto put;
+	}
+
+	bpmp = platform_get_drvdata(pdev);
+	if (!bpmp) {
+		bpmp = ERR_PTR(-EPROBE_DEFER);
+		put_device(&pdev->dev);
+		goto put;
+	}
+
+put:
+	of_node_put(np);
+	return bpmp;
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_get);
+
+void tegra_bpmp_put(struct tegra_bpmp *bpmp)
+{
+	if (bpmp)
+		put_device(bpmp->dev);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_put);
+
+static int tegra_bpmp_channel_get_index(struct tegra_bpmp_channel *channel)
+{
+	return channel - channel->bpmp->channels;
+}
+
+static int
+tegra_bpmp_channel_get_thread_index(struct tegra_bpmp_channel *channel)
+{
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	unsigned int offset, count;
+	int index;
+
+	offset = bpmp->soc->channels.thread.offset;
+	count = bpmp->soc->channels.thread.count;
+
+	index = tegra_bpmp_channel_get_index(channel);
+	if (index < 0)
+		return index;
+
+	if (index < offset || index >= offset + count)
+		return -EINVAL;
+
+	return index - offset;
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_thread(struct tegra_bpmp *bpmp, unsigned int index)
+{
+	unsigned int offset = bpmp->soc->channels.thread.offset;
+	unsigned int count = bpmp->soc->channels.thread.count;
+
+	if (index >= count)
+		return NULL;
+
+	return &bpmp->channels[offset + index];
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_tx(struct tegra_bpmp *bpmp)
+{
+	unsigned int offset = bpmp->soc->channels.cpu_tx.offset;
+
+	return &bpmp->channels[offset + smp_processor_id()];
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_rx(struct tegra_bpmp *bpmp)
+{
+	unsigned int offset = bpmp->soc->channels.cpu_rx.offset;
+
+	return &bpmp->channels[offset];
+}
+
+static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg)
+{
+	return (msg->tx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
+	       (msg->rx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
+	       (msg->tx.size == 0 || msg->tx.data) &&
+	       (msg->rx.size == 0 || msg->rx.data);
+}
+
+static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
+{
+	void *frame;
+
+	frame = tegra_ivc_read_get_next_frame(channel->ivc);
+	if (IS_ERR_OR_NULL(frame)) {
+		channel->ib = NULL;
+		return false;
+	}
+
+	channel->ib = frame;
+
+	return true;
+}
+
+static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
+{
+	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
+	ktime_t start, now;
+
+	start = ns_to_ktime(local_clock());
+
+	do {
+		if (tegra_bpmp_master_acked(channel))
+			return 0;
+
+		now = ns_to_ktime(local_clock());
+	} while (ktime_us_delta(now, start) < timeout);
+
+	return -ETIMEDOUT;
+}
+
+static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
+{
+	void *frame;
+
+	frame = tegra_ivc_write_get_next_frame(channel->ivc);
+	if (IS_ERR_OR_NULL(frame)) {
+		channel->ob = NULL;
+		return false;
+	}
+
+	channel->ob = frame;
+
+	return true;
+}
+
+static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
+{
+	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
+	ktime_t start, now;
+
+	start = ns_to_ktime(local_clock());
+
+	do {
+		if (tegra_bpmp_master_free(channel))
+			return 0;
+
+		now = ns_to_ktime(local_clock());
+	} while (ktime_us_delta(now, start) < timeout);
+
+	return -ETIMEDOUT;
+}
+
+static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
+					 void *data, size_t size)
+{
+	if (data && size > 0)
+		memcpy_fromio(data, channel->ib->data, size);
+
+	return tegra_ivc_read_advance(channel->ivc);
+}
+
+static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
+				       void *data, size_t size)
+{
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	unsigned long flags;
+	ssize_t err;
+	int index;
+
+	index = tegra_bpmp_channel_get_thread_index(channel);
+	if (index < 0)
+		return index;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+	err = __tegra_bpmp_channel_read(channel, data, size);
+	clear_bit(index, bpmp->threaded.allocated);
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+
+	up(&bpmp->threaded.lock);
+
+	return err;
+}
+
+static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
+					  unsigned int mrq, unsigned long flags,
+					  const void *data, size_t size)
+{
+	channel->ob->code = mrq;
+	channel->ob->flags = flags;
+
+	if (data && size > 0)
+		memcpy_toio(channel->ob->data, data, size);
+
+	return tegra_ivc_write_advance(channel->ivc);
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
+			  const void *data, size_t size)
+{
+	unsigned long timeout = bpmp->soc->channels.thread.timeout;
+	unsigned int count = bpmp->soc->channels.thread.count;
+	struct tegra_bpmp_channel *channel;
+	unsigned long flags;
+	unsigned int index;
+	int err;
+
+	err = down_timeout(&bpmp->threaded.lock, usecs_to_jiffies(timeout));
+	if (err < 0)
+		return ERR_PTR(err);
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	index = find_first_zero_bit(bpmp->threaded.allocated, count);
+	if (index == count) {
+		channel = ERR_PTR(-EBUSY);
+		goto unlock;
+	}
+
+	channel = tegra_bpmp_channel_get_thread(bpmp, index);
+	if (!channel) {
+		channel = ERR_PTR(-EINVAL);
+		goto unlock;
+	}
+
+	if (!tegra_bpmp_master_free(channel)) {
+		channel = ERR_PTR(-EBUSY);
+		goto unlock;
+	}
+
+	set_bit(index, bpmp->threaded.allocated);
+
+	err = __tegra_bpmp_channel_write(channel, mrq, MSG_ACK | MSG_RING,
+					 data, size);
+	if (err < 0) {
+		clear_bit(index, bpmp->threaded.allocated);
+		goto unlock;
+	}
+
+	set_bit(index, bpmp->threaded.busy);
+
+unlock:
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+	return channel;
+}
+
+static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
+					unsigned int mrq, unsigned long flags,
+					const void *data, size_t size)
+{
+	int err;
+
+	err = tegra_bpmp_wait_master_free(channel);
+	if (err < 0)
+		return err;
+
+	return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
+}
+
+int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
+			       struct tegra_bpmp_message *msg)
+{
+	struct tegra_bpmp_channel *channel;
+	int err;
+
+	if (WARN_ON(!irqs_disabled()))
+		return -EPERM;
+
+	if (!tegra_bpmp_message_valid(msg))
+		return -EINVAL;
+
+	channel = tegra_bpmp_channel_get_tx(bpmp);
+
+	err = tegra_bpmp_channel_write(channel, msg->mrq, MSG_ACK,
+				       msg->tx.data, msg->tx.size);
+	if (err < 0)
+		return err;
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return err;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+
+	err = tegra_bpmp_wait_ack(channel);
+	if (err)
+		return err;
+
+	return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
+
+int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
+			struct tegra_bpmp_message *msg)
+{
+	struct tegra_bpmp_channel *channel;
+	unsigned long timeout;
+	int err;
+
+	if (WARN_ON(irqs_disabled()))
+		return -EPERM;
+
+	if (!tegra_bpmp_message_valid(msg))
+		return -EINVAL;
+
+	channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
+					    msg->tx.size);
+	if (IS_ERR(channel))
+		return PTR_ERR(channel);
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return err;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+
+	timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout);
+
+	err = wait_for_completion_timeout(&channel->completion, timeout);
+	if (err <= 0) {
+		if (err == 0)
+			err = -ETIMEDOUT;
+
+		return err;
+	}
+
+	return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
+
+static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
+						  unsigned int mrq)
+{
+	struct tegra_bpmp_mrq *entry;
+
+	list_for_each_entry(entry, &bpmp->mrqs, list)
+		if (entry->mrq == mrq)
+			return entry;
+
+	return NULL;
+}
+
+static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
+				  int code, const void *data, size_t size)
+{
+	unsigned long flags = channel->ib->flags;
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	struct tegra_bpmp_mb_data *frame;
+	int err;
+
+	if (WARN_ON(size > TEGRA_BPMP_MSG_DATA_SIZE))
+		return;
+
+	err = tegra_ivc_read_advance(channel->ivc);
+	if (WARN_ON(err < 0))
+		return;
+
+	if ((flags & MSG_ACK) == 0)
+		return;
+
+	frame = tegra_ivc_write_get_next_frame(channel->ivc);
+	if (WARN_ON(IS_ERR_OR_NULL(frame)))
+		return;
+
+	frame->code = code;
+
+	if (data && size > 0)
+		memcpy_toio(frame->data, data, size);
+
+	err = tegra_ivc_write_advance(channel->ivc);
+	if (WARN_ON(err < 0))
+		return;
+
+	if (flags & MSG_RING) {
+		err = mbox_send_message(bpmp->mbox.channel, NULL);
+		if (WARN_ON(err < 0))
+			return;
+
+		mbox_client_txdone(bpmp->mbox.channel, 0);
+	}
+}
+
+static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
+				  unsigned int mrq,
+				  struct tegra_bpmp_channel *channel)
+{
+	struct tegra_bpmp_mrq *entry;
+	u32 zero = 0;
+
+	spin_lock(&bpmp->lock);
+
+	entry = tegra_bpmp_find_mrq(bpmp, mrq);
+	if (!entry) {
+		spin_unlock(&bpmp->lock);
+		tegra_bpmp_mrq_return(channel, -EINVAL, &zero, sizeof(zero));
+		return;
+	}
+
+	entry->handler(mrq, channel, entry->data);
+
+	spin_unlock(&bpmp->lock);
+}
+
+int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			   tegra_bpmp_mrq_handler_t handler, void *data)
+{
+	struct tegra_bpmp_mrq *entry;
+	unsigned long flags;
+
+	if (!handler)
+		return -EINVAL;
+
+	entry = devm_kzalloc(bpmp->dev, sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	entry->mrq = mrq;
+	entry->handler = handler;
+	entry->data = data;
+	list_add(&entry->list, &bpmp->mrqs);
+
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_request_mrq);
+
+void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data)
+{
+	struct tegra_bpmp_mrq *entry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	entry = tegra_bpmp_find_mrq(bpmp, mrq);
+	if (!entry)
+		goto unlock;
+
+	list_del(&entry->list);
+	devm_kfree(bpmp->dev, entry);
+
+unlock:
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq);
+
+static void tegra_bpmp_mrq_handle_ping(unsigned int mrq,
+				       struct tegra_bpmp_channel *channel,
+				       void *data)
+{
+	struct mrq_ping_request *request;
+	struct mrq_ping_response response;
+
+	request = (struct mrq_ping_request *)channel->ib->data;
+
+	memset(&response, 0, sizeof(response));
+	response.reply = request->challenge << (smp_processor_id() + 1);
+
+	tegra_bpmp_mrq_return(channel, 0, &response, sizeof(response));
+}
+
+static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
+{
+	struct mrq_ping_response response;
+	struct mrq_ping_request request;
+	struct tegra_bpmp_message msg;
+	ktime_t start, delta;
+	unsigned long flags;
+	int err;
+
+	memset(&request, 0, sizeof(request));
+	request.challenge = 1;
+
+	memset(&response, 0, sizeof(response));
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_PING;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	start = ktime_get();
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+	local_irq_restore(flags);
+
+	delta = ktime_sub(ktime_get(), start);
+
+	if (!err)
+		dev_info(bpmp->dev,
+			 "ping ok: challenge: %u, response: %u, time: %lld\n",
+			 request.challenge, response.reply,
+			 ktime_to_us(delta));
+
+	return err;
+}
+
+static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
+				       size_t size)
+{
+	struct mrq_query_tag_request request;
+	struct tegra_bpmp_message msg;
+	unsigned long flags;
+	dma_addr_t phys;
+	void *virt;
+	int err;
+
+	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
+				  GFP_KERNEL | GFP_DMA32);
+	if (!virt)
+		return -ENOMEM;
+
+	memset(&request, 0, sizeof(request));
+	request.addr = phys;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_QUERY_TAG;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+	local_irq_restore(flags);
+
+	if (err == 0)
+		strlcpy(tag, virt, size);
+
+	dma_free_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, virt, phys);
+
+	return err;
+}
+
+static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
+{
+	unsigned long flags = channel->ob->flags;
+
+	if ((flags & MSG_RING) == 0)
+		return;
+
+	complete(&channel->completion);
+}
+
+static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
+{
+	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
+	struct tegra_bpmp_channel *channel;
+	unsigned int i, count;
+	unsigned long *busy;
+
+	channel = tegra_bpmp_channel_get_rx(bpmp);
+	count = bpmp->soc->channels.thread.count;
+	busy = bpmp->threaded.busy;
+
+	if (tegra_bpmp_master_acked(channel))
+		tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel);
+
+	spin_lock(&bpmp->lock);
+
+	for_each_set_bit(i, busy, count) {
+		struct tegra_bpmp_channel *channel;
+
+		channel = tegra_bpmp_channel_get_thread(bpmp, i);
+		if (!channel)
+			continue;
+
+		if (tegra_bpmp_master_acked(channel)) {
+			tegra_bpmp_channel_signal(channel);
+			clear_bit(i, busy);
+		}
+	}
+
+	spin_unlock(&bpmp->lock);
+}
+
+static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
+{
+	struct tegra_bpmp *bpmp = data;
+	int err;
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+}
+
+static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
+				   struct tegra_bpmp *bpmp,
+				   unsigned int index)
+{
+	size_t message, queue;
+	void __iomem *rx_base;
+	void __iomem *tx_base;
+	int err;
+
+	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
+				    GFP_KERNEL);
+	if (!channel->ivc)
+		return -ENOMEM;
+
+	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
+	queue = tegra_ivc_total_queue_size(message);
+
+	rx_base = bpmp->rx_base + queue * index;
+	tx_base = bpmp->tx_base + queue * index;
+
+	err = tegra_ivc_init(channel->ivc, bpmp->dev, rx_base, DMA_ERROR_CODE,
+			     tx_base, DMA_ERROR_CODE, 1, message,
+			     tegra_bpmp_ivc_notify, bpmp);
+	if (err < 0) {
+		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
+			index, err);
+		return err;
+	}
+
+	/* reset the channel state */
+	tegra_ivc_reset(channel->ivc);
+
+	/* sync the channel state with BPMP */
+	while (tegra_ivc_notified(channel->ivc))
+		;
+
+	init_completion(&channel->completion);
+	channel->bpmp = bpmp;
+
+	return 0;
+}
+
+static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
+{
+	struct mrq_pg_read_state_response response;
+	struct mrq_pg_read_state_request request;
+	struct tegra_bpmp_message msg;
+	unsigned int i;
+	int err;
+
+	dev_dbg(bpmp->dev, "powergates:\n");
+
+	for (i = 0; i < 32; i++) {
+		memset(&request, 0, sizeof(request));
+		request.partition_id = i;
+
+		memset(&response, 0, sizeof(response));
+
+		memset(&msg, 0, sizeof(msg));
+		msg.mrq = MRQ_PG_READ_STATE;
+		msg.tx.data = &request;
+		msg.tx.size = sizeof(request);
+		msg.rx.data = &response;
+		msg.rx.size = sizeof(response);
+
+		err = tegra_bpmp_transfer(bpmp, &msg);
+		if (err < 0) {
+			dev_err(bpmp->dev, "failed to transfer message: %d\n", err);
+			continue;
+		}
+
+		dev_dbg(bpmp->dev, "  %u: %x (%x)\n", i, response.logic_state,
+			response.sram_state);
+	}
+
+	return 0;
+}
+
+static int tegra_bpmp_probe(struct platform_device *pdev)
+{
+	struct tegra_bpmp_channel *channel;
+	struct tegra_bpmp *bpmp;
+	struct device_node *np;
+	struct resource res;
+	unsigned int i;
+	char tag[32];
+	size_t size;
+	int err;
+
+	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
+	if (!bpmp)
+		return -ENOMEM;
+
+	bpmp->soc = of_device_get_match_data(&pdev->dev);
+	bpmp->dev = &pdev->dev;
+
+	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
+	if (!np)
+		return -ENOENT;
+
+	of_address_to_resource(np, 0, &res);
+	of_node_put(np);
+
+	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
+	if (IS_ERR(bpmp->tx_base))
+		return PTR_ERR(bpmp->tx_base);
+
+	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
+	if (!np)
+		return -ENOENT;
+
+	of_address_to_resource(np, 0, &res);
+	of_node_put(np);
+
+	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
+	if (IS_ERR(bpmp->rx_base))
+		return PTR_ERR(bpmp->rx_base);
+
+	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
+			     bpmp->soc->channels.thread.count +
+			     bpmp->soc->channels.cpu_rx.count;
+
+	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
+				      sizeof(*channel), GFP_KERNEL);
+	if (!bpmp->channels)
+		return -ENOMEM;
+
+	/* mbox registration */
+	bpmp->mbox.client.dev = &pdev->dev;
+	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
+	bpmp->mbox.client.tx_block = false;
+	bpmp->mbox.client.knows_txdone = false;
+
+	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
+	if (IS_ERR(bpmp->mbox.channel)) {
+		err = PTR_ERR(bpmp->mbox.channel);
+		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
+		return err;
+	}
+
+	/* message channel initialization */
+	for (i = 0; i < bpmp->num_channels; i++) {
+		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
+
+		err = tegra_bpmp_channel_init(channel, bpmp, i);
+		if (err)
+			return err;
+	}
+
+	bpmp->threaded.count = bpmp->soc->channels.thread.count;
+	size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long);
+
+	bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!bpmp->threaded.allocated)
+		return -ENOMEM;
+
+	bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!bpmp->threaded.busy)
+		return -ENOMEM;
+
+	sema_init(&bpmp->threaded.lock, bpmp->threaded.count);
+
+	INIT_LIST_HEAD(&bpmp->mrqs);
+	spin_lock_init(&bpmp->lock);
+
+	err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
+				     tegra_bpmp_mrq_handle_ping, bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_ping(bpmp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to ping BPMP: %d\n", err);
+		goto free_mrq;
+	}
+
+	err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1);
+	if (err) {
+		dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
+		goto free_mrq;
+	}
+
+	dev_info(&pdev->dev, "firmware: %s\n", tag);
+
+	err = tegra_bpmp_init_clocks(bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_init_resets(bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_init_powergates(bpmp);
+	if (err < 0)
+		return err;
+
+	platform_set_drvdata(pdev, bpmp);
+
+	return 0;
+
+free_mrq:
+	tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
+
+	return err;
+}
+
+static const struct tegra_bpmp_soc tegra186_soc = {
+	.channels = {
+		.cpu_tx = {
+			.offset = 0,
+			.count = 6,
+			.timeout = 60 * USEC_PER_SEC,
+		},
+		.thread = {
+			.offset = 6,
+			.count = 7,
+			.timeout = 600 * USEC_PER_SEC,
+		},
+		.cpu_rx = {
+			.offset = 13,
+			.count = 1,
+			.timeout = 0,
+		},
+	},
+	.num_resets = 193,
+};
+
+static const struct of_device_id tegra_bpmp_match[] = {
+	{ .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc },
+	{ }
+};
+
+static struct platform_driver tegra_bpmp_driver = {
+	.driver = {
+		.name = "tegra-bpmp",
+		.of_match_table = tegra_bpmp_match,
+	},
+	.probe = tegra_bpmp_probe,
+};
+
+static int __init tegra_bpmp_init(void)
+{
+	return platform_driver_register(&tegra_bpmp_driver);
+}
+core_initcall(tegra_bpmp_init);
diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
new file mode 100644
index 000000000000..0aaef5960e29
--- /dev/null
+++ b/include/soc/tegra/bpmp-abi.h
@@ -0,0 +1,1601 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 _ABI_BPMP_ABI_H_
+#define _ABI_BPMP_ABI_H_
+
+#ifdef LK
+#include <stdint.h>
+#endif
+
+#ifndef __ABI_PACKED
+#define __ABI_PACKED __attribute__((packed))
+#endif
+
+#ifdef NO_GCC_EXTENSIONS
+#define EMPTY char empty;
+#define EMPTY_ARRAY 1
+#else
+#define EMPTY
+#define EMPTY_ARRAY 0
+#endif
+
+#ifndef __UNION_ANON
+#define __UNION_ANON
+#endif
+/**
+ * @file
+ */
+
+
+/**
+ * @defgroup MRQ MRQ Messages
+ * @brief Messages sent to/from BPMP via IPC
+ * @{
+ *   @defgroup MRQ_Format Message Format
+ *   @defgroup MRQ_Codes Message Request (MRQ) Codes
+ *   @defgroup MRQ_Payloads Message Payloads
+ *   @defgroup Error_Codes Error Codes
+ * @}
+ */
+
+/**
+ * @addtogroup MRQ_Format Message Format
+ * @{
+ * The CPU requests the BPMP to perform a particular service by
+ * sending it an IVC frame containing a single MRQ message. An MRQ
+ * message consists of a @ref mrq_request followed by a payload whose
+ * format depends on mrq_request::mrq.
+ *
+ * The BPMP processes the data and replies with an IVC frame (on the
+ * same IVC channel) containing and MRQ response. An MRQ response
+ * consists of a @ref mrq_response followed by a payload whose format
+ * depends on the associated mrq_request::mrq.
+ *
+ * A well-defined subset of the MRQ messages that the CPU sends to the
+ * BPMP can lead to BPMP eventually sending an MRQ message to the
+ * CPU. For example, when the CPU uses an #MRQ_THERMAL message to set
+ * a thermal trip point, the BPMP may eventually send a single
+ * #MRQ_THERMAL message of its own to the CPU indicating that the trip
+ * point has been crossed.
+ * @}
+ */
+
+/**
+ * @ingroup MRQ_Format
+ * @brief header for an MRQ message
+ *
+ * Provides the MRQ number for the MRQ message: #mrq. The remainder of
+ * the MRQ message is a payload (immediately following the
+ * mrq_request) whose format depends on mrq.
+ *
+ * @todo document the flags
+ */
+struct mrq_request {
+	/** @brief MRQ number of the request */
+	uint32_t mrq;
+	/** @brief flags for the request */
+	uint32_t flags;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Format
+ * @brief header for an MRQ response
+ *
+ *  Provides an error code for the associated MRQ message. The
+ *  remainder of the MRQ response is a payload (immediately following
+ *  the mrq_response) whose format depends on the associated
+ *  mrq_request::mrq
+ *
+ * @todo document the flags
+ */
+struct mrq_response {
+	/** @brief error code for the MRQ request itself */
+	int32_t err;
+	/** @brief flags for the response */
+	uint32_t flags;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Format
+ * Minimum needed size for an IPC message buffer
+ */
+#define MSG_MIN_SZ	128
+/**
+ * @ingroup MRQ_Format
+ *  Minimum size guaranteed for data in an IPC message buffer
+ */
+#define MSG_DATA_MIN_SZ	120
+
+/**
+ * @ingroup MRQ_Codes
+ * @name Legal MRQ codes
+ * These are the legal values for mrq_request::mrq
+ * @{
+ */
+
+#define MRQ_PING		0
+#define MRQ_QUERY_TAG		1
+#define MRQ_MODULE_LOAD		4
+#define MRQ_MODULE_UNLOAD	5
+#define MRQ_TRACE_MODIFY	7
+#define MRQ_WRITE_TRACE		8
+#define MRQ_THREADED_PING	9
+#define MRQ_MODULE_MAIL		11
+#define MRQ_DEBUGFS		19
+#define MRQ_RESET		20
+#define MRQ_I2C			21
+#define MRQ_CLK			22
+#define MRQ_QUERY_ABI		23
+#define MRQ_PG_READ_STATE	25
+#define MRQ_PG_UPDATE_STATE	26
+#define MRQ_THERMAL		27
+#define MRQ_CPU_VHINT		28
+#define MRQ_ABI_RATCHET		29
+#define MRQ_EMC_DVFS_LATENCY	31
+#define MRQ_TRACE_ITER		64
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @brief Maximum MRQ code to be sent by CPU software to
+ * BPMP. Subject to change in future
+ */
+#define MAX_CPU_MRQ_ID		64
+
+/**
+ * @addtogroup MRQ_Payloads Message Payloads
+ * @{
+ *   @defgroup Ping
+ *   @defgroup Query_Tag Query Tag
+ *   @defgroup Module Loadable Modules
+ *   @defgroup Trace
+ *   @defgroup Debugfs
+ *   @defgroup Reset
+ *   @defgroup I2C
+ *   @defgroup Clocks
+ *   @defgroup ABI_info ABI Info
+ *   @defgroup MC_Flush MC Flush
+ *   @defgroup Powergating
+ *   @defgroup Thermal
+ *   @defgroup Vhint CPU Voltage hint
+ *   @defgroup MRQ_Deprecated Deprecated MRQ messages
+ *   @defgroup EMC
+ * @}
+ */
+
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PING
+ * @brief A simple ping
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: @ref mrq_ping_request
+ * * Response Payload: @ref mrq_ping_response
+ *
+ * @ingroup MRQ_Codes
+ * @def MRQ_THREADED_PING
+ * @brief A deeper ping
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_ping_request
+ * * Response Payload: @ref mrq_ping_response
+ *
+ * Behavior is equivalent to a simple #MRQ_PING except that BPMP
+ * responds from a thread context (providing a slightly more robust
+ * sign of life).
+ *
+ */
+
+/**
+ * @ingroup Ping
+ * @brief request with #MRQ_PING
+ *
+ * Used by the sender of an #MRQ_PING message to request a pong from
+ * recipient. The response from the recipient is computed based on
+ * #challenge.
+ */
+struct mrq_ping_request {
+/** @brief arbitrarily chosen value */
+	uint32_t challenge;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Ping
+ * @brief response to #MRQ_PING
+ *
+ * Sent in response to an #MRQ_PING message. #reply should be the
+ * mrq_ping_request challenge left shifted by 1 with the carry-bit
+ * dropped.
+ *
+ */
+struct mrq_ping_response {
+	/** @brief response to the MRQ_PING challege */
+	uint32_t reply;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_QUERY_TAG
+ * @brief Query BPMP firmware's tag (i.e. version information)
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_query_tag_request
+ * * Response Payload: N/A
+ *
+ */
+
+/**
+ * @ingroup Query_Tag
+ * @brief request with #MRQ_QUERY_TAG
+ *
+ * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory
+ * pointed by #addr with BPMP firmware header.
+ *
+ * The sender is reponsible for ensuring that #addr is mapped in to
+ * the recipient's address map.
+ */
+struct mrq_query_tag_request {
+  /** @brief base address to store the firmware header */
+	uint32_t addr;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_LOAD
+ * @brief dynamically load a BPMP code module
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_load_request
+ * * Response Payload: @ref mrq_module_load_response
+ *
+ * @note This MRQ is disabled on production systems
+ *
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_LOAD
+ *
+ * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically
+ * load the code located at #phys_addr and having size #size
+ * bytes. #phys_addr is treated as a void pointer.
+ *
+ * The recipient copies the code from #phys_addr to locally allocated
+ * memory prior to responding to this message.
+ *
+ * @todo document the module header format
+ *
+ * The sender is responsible for ensuring that the code is mapped in
+ * the recipient's address map.
+ *
+ */
+struct mrq_module_load_request {
+	/** @brief base address of the code to load. Treated as (void *) */
+	uint32_t phys_addr; /* (void *) */
+	/** @brief size in bytes of code to load */
+	uint32_t size;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Module
+ * @brief response to #MRQ_MODULE_LOAD
+ *
+ * @todo document mrq_response::err
+ */
+struct mrq_module_load_response {
+	/** @brief handle to the loaded module */
+	uint32_t base;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_UNLOAD
+ * @brief unload a previously loaded code module
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_unload_request
+ * * Response Payload: N/A
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_UNLOAD
+ *
+ * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded
+ * module be unloaded.
+ */
+struct mrq_module_unload_request {
+	/** @brief handle of the module to unload */
+	uint32_t base;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_TRACE_MODIFY
+ * @brief modify the set of enabled trace events
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_trace_modify_request
+ * * Response Payload: @ref mrq_trace_modify_response
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Trace
+ * @brief request with #MRQ_TRACE_MODIFY
+ *
+ * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace
+ * events.  #set takes precedence for any bit set in both #set and
+ * #clr.
+ */
+struct mrq_trace_modify_request {
+	/** @brief bit mask of trace events to disable */
+	uint32_t clr;
+	/** @brief bit mask of trace events to enable */
+	uint32_t set;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Trace
+ * @brief response to #MRQ_TRACE_MODIFY
+ *
+ * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the
+ * state of which events are enabled after the recipient acted on the
+ * message.
+ *
+ */
+struct mrq_trace_modify_response {
+	/** @brief bit mask of trace event enable states */
+	uint32_t mask;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_WRITE_TRACE
+ * @brief Write trace data to a buffer
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_write_trace_request
+ * * Response Payload: @ref mrq_write_trace_response
+ *
+ * mrq_response::err depends on the @ref mrq_write_trace_request field
+ * values. err is -#BPMP_EINVAL if size is zero or area is NULL or
+ * area is in an illegal range. A positive value for err indicates the
+ * number of bytes written to area.
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Trace
+ * @brief request with #MRQ_WRITE_TRACE
+ *
+ * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace
+ * data from the recipient's local buffer to the output buffer. #area
+ * is treated as a byte-aligned pointer in the recipient's address
+ * space.
+ *
+ * The sender is responsible for ensuring that the output
+ * buffer is mapped in the recipient's address map. The recipient is
+ * responsible for protecting its own code and data from accidental
+ * overwrites.
+ */
+struct mrq_write_trace_request {
+	/** @brief base address of output buffer */
+	uint32_t area;
+	/** @brief size in bytes of the output buffer */
+	uint32_t size;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Trace
+ * @brief response to #MRQ_WRITE_TRACE
+ *
+ * Once this response is sent, the respondent will not access the
+ * output buffer further.
+ */
+struct mrq_write_trace_response {
+	/**
+	 * @brief flag whether more data remains in local buffer
+	 *
+	 * Value is 1 if the entire local trace buffer has been
+	 * drained to the outputbuffer. Value is 0 otherwise.
+	 */
+	uint32_t eof;
+} __ABI_PACKED;
+
+/** @private */
+struct mrq_threaded_ping_request {
+	uint32_t challenge;
+} __ABI_PACKED;
+
+/** @private */
+struct mrq_threaded_ping_response {
+	uint32_t reply;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_MAIL
+ * @brief send a message to a loadable module
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_mail_request
+ * * Response Payload: @ref mrq_module_mail_response
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_MAIL
+ */
+struct mrq_module_mail_request {
+	/** @brief handle to the previously loaded module */
+	uint32_t base;
+	/** @brief module-specific mail payload
+	 *
+	 * The length of data[ ] is unknown to the BPMP core firmware
+	 * but it is limited to the size of an IPC message.
+	 */
+	uint8_t data[EMPTY_ARRAY];
+} __ABI_PACKED;
+
+/**
+ * @ingroup Module
+ * @brief response to #MRQ_MODULE_MAIL
+ */
+struct mrq_module_mail_response {
+	/** @brief module-specific mail payload
+	 *
+	 * The length of data[ ] is unknown to the BPMP core firmware
+	 * but it is limited to the size of an IPC message.
+	 */
+	uint8_t data[EMPTY_ARRAY];
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_DEBUGFS
+ * @brief Interact with BPMP's debugfs file nodes
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_debugfs_request
+ * * Response Payload: @ref mrq_debugfs_response
+ */
+
+/**
+ * @addtogroup Debugfs
+ * @{
+ *
+ * The BPMP firmware implements a pseudo-filesystem called
+ * debugfs. Any driver within the firmware may register with debugfs
+ * to expose an arbitrary set of "files" in the filesystem. When
+ * software on the CPU writes to a debugfs file, debugfs passes the
+ * written data to a callback provided by the driver. When software on
+ * the CPU reads a debugfs file, debugfs queries the driver for the
+ * data to return to the CPU. The intention of the debugfs filesystem
+ * is to provide information useful for debugging the system at
+ * runtime.
+ *
+ * @note The files exposed via debugfs are not part of the
+ * BPMP firmware's ABI. debugfs files may be added or removed in any
+ * given version of the firmware. Typically the semantics of a debugfs
+ * file are consistent from version to version but even that is not
+ * guaranteed.
+ *
+ * @}
+ */
+/** @ingroup Debugfs */
+enum mrq_debugfs_commands {
+	CMD_DEBUGFS_READ = 1,
+	CMD_DEBUGFS_WRITE = 2,
+	CMD_DEBUGFS_DUMPDIR = 3,
+	CMD_DEBUGFS_MAX
+};
+
+/**
+ * @ingroup Debugfs
+ * @brief parameters for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_fileop_request {
+	/** @brief physical address pointing at filename */
+	uint32_t fnameaddr;
+	/** @brief length in bytes of filename buffer */
+	uint32_t fnamelen;
+	/** @brief physical address pointing to data buffer */
+	uint32_t dataaddr;
+	/** @brief length in bytes of data buffer */
+	uint32_t datalen;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief parameters for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_dumpdir_request {
+	/** @brief physical address pointing to data buffer */
+	uint32_t dataaddr;
+	/** @brief length in bytes of data buffer */
+	uint32_t datalen;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief response data for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_fileop_response {
+	/** @brief always 0 */
+	uint32_t reserved;
+	/** @brief number of bytes read from or written to data buffer */
+	uint32_t nbytes;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief response data for CMD_DEBUGFS_DUMPDIR command
+ */
+struct cmd_debugfs_dumpdir_response {
+	/** @brief always 0 */
+	uint32_t reserved;
+	/** @brief number of bytes read from or written to data buffer */
+	uint32_t nbytes;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief request with #MRQ_DEBUGFS.
+ *
+ * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs
+ * command to execute. Legal commands are the values of @ref
+ * mrq_debugfs_commands. Each command requires a specific additional
+ * payload of data.
+ *
+ * |command            |payload|
+ * |-------------------|-------|
+ * |CMD_DEBUGFS_READ   |fop    |
+ * |CMD_DEBUGFS_WRITE  |fop    |
+ * |CMD_DEBUGFS_DUMPDIR|dumpdir|
+ */
+struct mrq_debugfs_request {
+	uint32_t cmd;
+	union {
+		struct cmd_debugfs_fileop_request fop;
+		struct cmd_debugfs_dumpdir_request dumpdir;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ */
+struct mrq_debugfs_response {
+	/** @brief always 0 */
+	int32_t reserved;
+	union {
+		/** @brief response data for CMD_DEBUGFS_READ OR
+		 * CMD_DEBUGFS_WRITE command
+		 */
+		struct cmd_debugfs_fileop_response fop;
+		/** @brief response data for CMD_DEBUGFS_DUMPDIR command */
+		struct cmd_debugfs_dumpdir_response dumpdir;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @addtogroup Debugfs
+ * @{
+ */
+#define DEBUGFS_S_ISDIR	(1 << 9)
+#define DEBUGFS_S_IRUSR	(1 << 8)
+#define DEBUGFS_S_IWUSR	(1 << 7)
+/** @} */
+
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_RESET
+ * @brief reset an IP block
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_reset_request
+ * * Response Payload: N/A
+ */
+
+/**
+ * @ingroup Reset
+ */
+enum mrq_reset_commands {
+	CMD_RESET_ASSERT = 1,
+	CMD_RESET_DEASSERT = 2,
+	CMD_RESET_MODULE = 3,
+	CMD_RESET_MAX, /* not part of ABI and subject to change */
+};
+
+/**
+ * @ingroup Reset
+ * @brief request with MRQ_RESET
+ *
+ * Used by the sender of an #MRQ_RESET message to request BPMP to
+ * assert or or deassert a given reset line.
+ */
+struct mrq_reset_request {
+	/** @brief reset action to perform (@enum mrq_reset_commands) */
+	uint32_t cmd;
+	/** @brief id of the reset to affected */
+	uint32_t reset_id;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_I2C
+ * @brief issue an i2c transaction
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_i2c_request
+ * * Response Payload: @ref mrq_i2c_response
+ */
+
+/**
+ * @addtogroup I2C
+ * @{
+ */
+#define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE	(MSG_DATA_MIN_SZ - 12)
+#define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE	(MSG_DATA_MIN_SZ - 4)
+/** @} */
+
+/**
+ * @ingroup I2C
+ * @name Serial I2C flags
+ * Use these flags with serial_i2c_request::flags
+ * @{
+ */
+#define SERIALI2C_TEN           0x0010
+#define SERIALI2C_RD            0x0001
+#define SERIALI2C_STOP          0x8000
+#define SERIALI2C_NOSTART       0x4000
+#define SERIALI2C_REV_DIR_ADDR  0x2000
+#define SERIALI2C_IGNORE_NAK    0x1000
+#define SERIALI2C_NO_RD_ACK     0x0800
+#define SERIALI2C_RECV_LEN      0x0400
+/** @} */
+/** @ingroup I2C */
+enum {
+	CMD_I2C_XFER = 1
+};
+
+/**
+ * @ingroup I2C
+ * @brief serializable i2c request
+ *
+ * Instances of this structure are packed (little-endian) into
+ * cmd_i2c_xfer_request::data_buf. Each instance represents a single
+ * transaction (or a portion of a transaction with repeated starts) on
+ * an i2c bus.
+ *
+ * Because these structures are packed, some instances are likely to
+ * be misaligned. Additionally because #data is variable length, it is
+ * not possible to iterate through a serialized list of these
+ * structures without inspecting #len in each instance.  It may be
+ * easier to serialize or deserialize cmd_i2c_xfer_request::data_buf
+ * manually rather than using this structure definition.
+*/
+struct serial_i2c_request {
+	/** @brief I2C slave address */
+	uint16_t addr;
+	/** @brief bitmask of SERIALI2C_ flags */
+	uint16_t flags;
+	/** @brief length of I2C transaction in bytes */
+	uint16_t len;
+	/** @brief for write transactions only, #len bytes of data */
+	uint8_t data[];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief trigger one or more i2c transactions
+ */
+struct cmd_i2c_xfer_request {
+	/** @brief valid bus number from mach-t186/i2c-t186.h*/
+	uint32_t bus_id;
+
+	/** @brief count of valid bytes in #data_buf*/
+	uint32_t data_size;
+
+	/** @brief serialized packed instances of @ref serial_i2c_request*/
+	uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief container for data read from the i2c bus
+ *
+ * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute
+ * zero or more I2C reads. The data read from the bus is serialized
+ * into #data_buf.
+ */
+struct cmd_i2c_xfer_response {
+	/** @brief count of valid bytes in #data_buf*/
+	uint32_t data_size;
+	/** @brief i2c read data */
+	uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief request with #MRQ_I2C
+ */
+struct mrq_i2c_request {
+	/** @brief always CMD_I2C_XFER (i.e. 1) */
+	uint32_t cmd;
+	/** @brief parameters of the transfer request */
+	struct cmd_i2c_xfer_request xfer;
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief response to #MRQ_I2C
+ */
+struct mrq_i2c_response {
+	struct cmd_i2c_xfer_response xfer;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_CLK
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_clk_request
+ * * Response Payload: @ref mrq_clk_response
+ * @addtogroup Clocks
+ * @{
+ */
+
+/**
+ * @name MRQ_CLK sub-commands
+ * @{
+ */
+enum {
+	CMD_CLK_GET_RATE = 1,
+	CMD_CLK_SET_RATE = 2,
+	CMD_CLK_ROUND_RATE = 3,
+	CMD_CLK_GET_PARENT = 4,
+	CMD_CLK_SET_PARENT = 5,
+	CMD_CLK_IS_ENABLED = 6,
+	CMD_CLK_ENABLE = 7,
+	CMD_CLK_DISABLE = 8,
+	CMD_CLK_GET_ALL_INFO = 14,
+	CMD_CLK_GET_MAX_CLK_ID = 15,
+	CMD_CLK_MAX,
+};
+/** @} */
+
+#define MRQ_CLK_NAME_MAXLEN	40
+#define MRQ_CLK_MAX_PARENTS	16
+
+/** @private */
+struct cmd_clk_get_rate_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_set_rate_request {
+	int32_t unused;
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_set_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_round_rate_request {
+	int32_t unused;
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_round_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_parent_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_parent_response {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+struct cmd_clk_set_parent_request {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+struct cmd_clk_set_parent_response {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_is_enabled_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_is_enabled_response {
+	int32_t state;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_enable_request {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_enable_response {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_disable_request {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_disable_response {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_all_info_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_all_info_response {
+	uint32_t flags;
+	uint32_t parent;
+	uint32_t parents[MRQ_CLK_MAX_PARENTS];
+	uint8_t num_parents;
+	uint8_t name[MRQ_CLK_NAME_MAXLEN];
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_max_clk_id_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_max_clk_id_response {
+	uint32_t max_id;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup Clocks
+ * @brief request with #MRQ_CLK
+ *
+ * Used by the sender of an #MRQ_CLK message to control clocks. The
+ * clk_request is split into several sub-commands. Some sub-commands
+ * require no additional data. Others have a sub-command specific
+ * payload
+ *
+ * |sub-command                 |payload                |
+ * |----------------------------|-----------------------|
+ * |CMD_CLK_GET_RATE            |-                      |
+ * |CMD_CLK_SET_RATE            |clk_set_rate           |
+ * |CMD_CLK_ROUND_RATE          |clk_round_rate         |
+ * |CMD_CLK_GET_PARENT          |-                      |
+ * |CMD_CLK_SET_PARENT          |clk_set_parent         |
+ * |CMD_CLK_IS_ENABLED          |-                      |
+ * |CMD_CLK_ENABLE              |-                      |
+ * |CMD_CLK_DISABLE             |-                      |
+ * |CMD_CLK_GET_ALL_INFO        |-                      |
+ * |CMD_CLK_GET_MAX_CLK_ID      |-                      |
+ *
+ */
+
+struct mrq_clk_request {
+	/** @brief sub-command and clock id concatenated to 32-bit word.
+	 * - bits[31..24] is the sub-cmd.
+	 * - bits[23..0] is the clock id
+	 */
+	uint32_t cmd_and_id;
+
+	union {
+		/** @private */
+		struct cmd_clk_get_rate_request clk_get_rate;
+		struct cmd_clk_set_rate_request clk_set_rate;
+		struct cmd_clk_round_rate_request clk_round_rate;
+		/** @private */
+		struct cmd_clk_get_parent_request clk_get_parent;
+		struct cmd_clk_set_parent_request clk_set_parent;
+		/** @private */
+		struct cmd_clk_enable_request clk_enable;
+		/** @private */
+		struct cmd_clk_disable_request clk_disable;
+		/** @private */
+		struct cmd_clk_is_enabled_request clk_is_enabled;
+		/** @private */
+		struct cmd_clk_get_all_info_request clk_get_all_info;
+		/** @private */
+		struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Clocks
+ * @brief response to MRQ_CLK
+ *
+ * Each sub-command supported by @ref mrq_clk_request may return
+ * sub-command-specific data. Some do and some do not as indicated in
+ * the following table
+ *
+ * |sub-command                 |payload                 |
+ * |----------------------------|------------------------|
+ * |CMD_CLK_GET_RATE            |clk_get_rate            |
+ * |CMD_CLK_SET_RATE            |clk_set_rate            |
+ * |CMD_CLK_ROUND_RATE          |clk_round_rate          |
+ * |CMD_CLK_GET_PARENT          |clk_get_parent          |
+ * |CMD_CLK_SET_PARENT          |clk_set_parent          |
+ * |CMD_CLK_IS_ENABLED          |clk_is_enabled          |
+ * |CMD_CLK_ENABLE              |-                       |
+ * |CMD_CLK_DISABLE             |-                       |
+ * |CMD_CLK_GET_ALL_INFO        |clk_get_all_info        |
+ * |CMD_CLK_GET_MAX_CLK_ID      |clk_get_max_id          |
+ *
+ */
+
+struct mrq_clk_response {
+	union {
+		struct cmd_clk_get_rate_response clk_get_rate;
+		struct cmd_clk_set_rate_response clk_set_rate;
+		struct cmd_clk_round_rate_response clk_round_rate;
+		struct cmd_clk_get_parent_response clk_get_parent;
+		struct cmd_clk_set_parent_response clk_set_parent;
+		/** @private */
+		struct cmd_clk_enable_response clk_enable;
+		/** @private */
+		struct cmd_clk_disable_response clk_disable;
+		struct cmd_clk_is_enabled_response clk_is_enabled;
+		struct cmd_clk_get_all_info_response clk_get_all_info;
+		struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_QUERY_ABI
+ * @brief check if an MRQ is implemented
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: @ref mrq_query_abi_request
+ * * Response Payload: @ref mrq_query_abi_response
+ */
+
+/**
+ * @ingroup ABI_info
+ * @brief request with MRQ_QUERY_ABI
+ *
+ * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported
+ * by the recipient.
+ */
+struct mrq_query_abi_request {
+	/** @brief MRQ code to query */
+	uint32_t mrq;
+} __ABI_PACKED;
+
+/**
+ * @ingroup ABI_info
+ * @brief response to MRQ_QUERY_ABI
+ */
+struct mrq_query_abi_response {
+	/** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */
+	int32_t status;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PG_READ_STATE
+ * @brief read the power-gating state of a partition
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_pg_read_state_request
+ * * Response Payload: @ref mrq_pg_read_state_response
+ * @addtogroup Powergating
+ * @{
+ */
+
+/**
+ * @brief request with #MRQ_PG_READ_STATE
+ *
+ * Used by MRQ_PG_READ_STATE call to read the current state of a
+ * partition.
+ */
+struct mrq_pg_read_state_request {
+	/** @brief ID of partition */
+	uint32_t partition_id;
+} __ABI_PACKED;
+
+/**
+ * @brief response to MRQ_PG_READ_STATE
+ * @todo define possible errors.
+ */
+struct mrq_pg_read_state_response {
+	/** @brief read as don't care */
+	uint32_t sram_state;
+	/** @brief state of power partition
+	 * * 0 : off
+	 * * 1 : on
+	 */
+	uint32_t logic_state;
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PG_UPDATE_STATE
+ * @brief modify the power-gating state of a partition
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_pg_update_state_request
+ * * Response Payload: N/A
+ * @addtogroup Powergating
+ * @{
+ */
+
+/**
+ * @brief request with mrq_pg_update_state_request
+ *
+ * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the
+ * state of a power partition #partition_id.
+ */
+struct mrq_pg_update_state_request {
+	/** @brief ID of partition */
+	uint32_t partition_id;
+	/** @brief secondary control of power partition
+	 *  @details Ignored by many versions of the BPMP
+	 *  firmware. For maximum compatibility, set the value
+	 *  according to @logic_state
+	 * *  0x1: power ON partition (@ref logic_state == 0x3)
+	 * *  0x3: power OFF partition (@ref logic_state == 0x1)
+	 */
+	uint32_t sram_state;
+	/** @brief controls state of power partition, legal values are
+	 * *  0x1 : power OFF partition
+	 * *  0x3 : power ON partition
+	 */
+	uint32_t logic_state;
+	/** @brief change state of clocks of the power partition, legal values
+	 * *  0x0 : do not change clock state
+	 * *  0x1 : disable partition clocks (only applicable when
+	 *          @ref logic_state == 0x1)
+	 * *  0x3 : enable partition clocks (only applicable when
+	 *          @ref logic_state == 0x3)
+	 */
+	uint32_t clock_state;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_THERMAL
+ * @brief interact with BPMP thermal framework
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: TODO
+ * * Response Payload: TODO
+ *
+ * @addtogroup Thermal
+ *
+ * The BPMP firmware includes a thermal framework. Drivers within the
+ * bpmp firmware register with the framework to provide thermal
+ * zones. Each thermal zone corresponds to an entity whose temperature
+ * can be measured. The framework also has a notion of trip points. A
+ * trip point consists of a thermal zone id, a temperature, and a
+ * callback routine. The framework invokes the callback when the zone
+ * hits the indicated temperature. The BPMP firmware uses this thermal
+ * framework interally to implement various temperature-dependent
+ * functions.
+ *
+ * Software on the CPU can use #MRQ_THERMAL (with payload @ref
+ * mrq_thermal_host_to_bpmp_request) to interact with the BPMP thermal
+ * framework. The CPU must It can query the number of supported zones,
+ * query zone temperatures, and set trip points.
+ *
+ * When a trip point set by the CPU gets crossed, BPMP firmware issues
+ * an IPC to the CPU having mrq_request::mrq = #MRQ_THERMAL and a
+ * payload of @ref mrq_thermal_bpmp_to_host_request.
+ * @{
+ */
+enum mrq_thermal_host_to_bpmp_cmd {
+	/**
+	 * @brief Check whether the BPMP driver supports the specified
+	 * request type.
+	 *
+	 * Host needs to supply request parameters.
+	 *
+	 * mrq_response::err is 0 if the specified request is
+	 * supported and -#BPMP_ENODEV otherwise.
+	 */
+	CMD_THERMAL_QUERY_ABI = 0,
+
+	/**
+	 * @brief Get the current temperature of the specified zone.
+	 *
+	 * Host needs to supply request parameters.
+	 *
+	 * mrq_response::err is
+	 * *  0: Temperature query succeeded.
+	 * *  -#BPMP_EINVAL: Invalid request parameters.
+	 * *  -#BPMP_ENOENT: No driver registered for thermal zone..
+	 * *  -#BPMP_EFAULT: Problem reading temperature measurement.
+	 */
+	CMD_THERMAL_GET_TEMP = 1,
+
+	/**
+	 * @brief Enable or disable and set the lower and upper
+	 *   thermal limits for a thermal trip point. Each zone has
+	 *   one trip point.
+	 *
+	 * Host needs to supply request parameters. Once the
+	 * temperature hits a trip point, the BPMP will send a message
+	 * to the CPU having MRQ=MRQ_THERMAL and
+	 * type=CMD_THERMAL_HOST_TRIP_REACHED
+	 *
+	 * mrq_response::err is
+	 * *  0: Trip successfully set.
+	 * *  -#BPMP_EINVAL: Invalid request parameters.
+	 * *  -#BPMP_ENOENT: No driver registered for thermal zone.
+	 * *  -#BPMP_EFAULT: Problem setting trip point.
+	 */
+	CMD_THERMAL_SET_TRIP = 2,
+
+	/**
+	 * @brief Get the number of supported thermal zones.
+	 *
+	 * No request parameters required.
+	 *
+	 * mrq_response::err is always 0, indicating success.
+	 */
+	CMD_THERMAL_GET_NUM_ZONES = 3,
+
+	/** @brief: number of supported host-to-bpmp commands. May
+	 * increase in future
+	 */
+	CMD_THERMAL_HOST_TO_BPMP_NUM
+};
+
+enum mrq_thermal_bpmp_to_host_cmd {
+	/**
+	 * @brief Indication that the temperature for a zone has
+	 *   exceeded the range indicated in the thermal trip point
+	 *   for the zone.
+	 *
+	 * BPMP needs to supply request parameters. Host only needs to
+	 * acknowledge.
+	 */
+	CMD_THERMAL_HOST_TRIP_REACHED = 100,
+
+	/** @brief: number of supported bpmp-to-host commands. May
+	 * increase in future
+	 */
+	CMD_THERMAL_BPMP_TO_HOST_NUM
+};
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI
+ *
+ * zone: Request type for which to check existence.
+ */
+struct cmd_thermal_query_abi_request {
+	uint32_t type;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP
+ *
+ * zone: Number of thermal zone.
+ */
+struct cmd_thermal_get_temp_request {
+	uint32_t zone;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP
+ *
+ * error: 0 if request succeeded.
+ *	-BPMP_EINVAL if request parameters were invalid.
+ *      -BPMP_ENOENT if no driver was registered for the specified thermal zone.
+ *      -BPMP_EFAULT for other thermal zone driver errors.
+ * temp: Current temperature in millicelsius.
+ */
+struct cmd_thermal_get_temp_response {
+	int32_t temp;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP
+ *
+ * zone: Number of thermal zone.
+ * low: Temperature of lower trip point in millicelsius
+ * high: Temperature of upper trip point in millicelsius
+ * enabled: 1 to enable trip point, 0 to disable trip point
+ */
+struct cmd_thermal_set_trip_request {
+	uint32_t zone;
+	int32_t low;
+	int32_t high;
+	uint32_t enabled;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED
+ *
+ * zone: Number of thermal zone where trip point was reached.
+ */
+struct cmd_thermal_host_trip_reached_request {
+	uint32_t zone;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES
+ *
+ * num: Number of supported thermal zones. The thermal zones are indexed
+ *      starting from zero.
+ */
+struct cmd_thermal_get_num_zones_response {
+	uint32_t num;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data.
+ *
+ * Reply type is union mrq_thermal_bpmp_to_host_response.
+ *
+ * type: Type of request. Values listed in enum mrq_thermal_type.
+ * data: Request type specific parameters.
+ */
+struct mrq_thermal_host_to_bpmp_request {
+	uint32_t type;
+	union {
+		struct cmd_thermal_query_abi_request query_abi;
+		struct cmd_thermal_get_temp_request get_temp;
+		struct cmd_thermal_set_trip_request set_trip;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host request data.
+ *
+ * type: Type of request. Values listed in enum mrq_thermal_type.
+ * data: Request type specific parameters.
+ */
+struct mrq_thermal_bpmp_to_host_request {
+	uint32_t type;
+	union {
+		struct cmd_thermal_host_trip_reached_request host_trip_reached;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/*
+ * Data in reply to a Host->BPMP request.
+ */
+union mrq_thermal_bpmp_to_host_response {
+	struct cmd_thermal_get_temp_response get_temp;
+	struct cmd_thermal_get_num_zones_response get_num_zones;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_CPU_VHINT
+ * @brief Query CPU voltage hint data
+ *
+ * * Platforms: T186
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_cpu_vhint_request
+ * * Response Payload: N/A
+ *
+ * @addtogroup Vhint CPU Voltage hint
+ * @{
+ */
+
+/**
+ * @brief request with #MRQ_CPU_VHINT
+ *
+ * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data
+ * from BPMP to memory space pointed by #addr. CCPLEX is responsible
+ * to allocate sizeof(cpu_vhint_data) sized block of memory and
+ * appropriately map it for BPMP before sending the request.
+ */
+struct mrq_cpu_vhint_request {
+	/** @brief IOVA address for the #cpu_vhint_data */
+	uint32_t addr; /* struct cpu_vhint_data * */
+	/** @brief ID of the cluster whose data is requested */
+	uint32_t cluster_id; /* enum cluster_id */
+} __ABI_PACKED;
+
+/**
+ * @brief description of the CPU v/f relation
+ *
+ * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of
+ * struct mrq_cpu_vhint_request
+ */
+struct cpu_vhint_data {
+	uint32_t ref_clk_hz; /**< reference frequency in Hz */
+	uint16_t pdiv; /**< post divider value */
+	uint16_t mdiv; /**< input divider value */
+	uint16_t ndiv_max; /**< fMAX expressed with max NDIV value */
+	/** table of ndiv values as a function of vINDEX (voltage index) */
+	uint16_t ndiv[80];
+	/** minimum allowed NDIV value */
+	uint16_t ndiv_min;
+	/** minimum allowed voltage hint value (as in vINDEX) */
+	uint16_t vfloor;
+	/** maximum allowed voltage hint value (as in vINDEX) */
+	uint16_t vceil;
+	/** post-multiplier for vindex value */
+	uint16_t vindex_mult;
+	/** post-divider for vindex value */
+	uint16_t vindex_div;
+	/** reserved for future use */
+	uint16_t reserved[328];
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_ABI_RATCHET
+ * @brief ABI ratchet value query
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_abi_ratchet_request
+ * * Response Payload: @ref mrq_abi_ratchet_response
+ * @addtogroup ABI_info
+ * @{
+ */
+
+/**
+ * @brief an ABI compatibility mechanism
+ *
+ * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future
+ * revision of this header file.
+ * 1. That future revision deprecates some MRQ
+ * 2. That future revision introduces a breaking change to an existing
+ *    MRQ or
+ * 3. A bug is discovered in an existing implementation of the BPMP-FW
+ *    (or possibly one of its clients) which warrants deprecating that
+ *    implementation.
+ */
+#define BPMP_ABI_RATCHET_VALUE 3
+
+/**
+ * @brief request with #MRQ_ABI_RATCHET.
+ *
+ * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header
+ * against which the requester was compiled.
+ *
+ * If ratchet is less than BPMP's #BPMP_ABI_RATCHET_VALUE, BPMP may
+ * reply with mrq_response::err = -#BPMP_ERANGE to indicate that
+ * BPMP-FW cannot interoperate correctly with the requester. Requester
+ * should cease further communication with BPMP.
+ *
+ * Otherwise, err shall be 0.
+ */
+struct mrq_abi_ratchet_request {
+	/** @brief requester's ratchet value */
+	uint16_t ratchet;
+};
+
+/**
+ * @brief response to #MRQ_ABI_RATCHET
+ *
+ * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header
+ * against which BPMP firwmare was compiled.
+ *
+ * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE,
+ * the requster must either interoperate with BPMP according to an ABI
+ * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease
+ * communication with BPMP.
+ *
+ * If mrq_response::err is 0 and ratchet is greater than or equal to the
+ * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue
+ * normal operation.
+ */
+struct mrq_abi_ratchet_response {
+	/** @brief BPMP's ratchet value */
+	uint16_t ratchet;
+};
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_EMC_DVFS_LATENCY
+ * @brief query frequency dependent EMC DVFS latency
+ *
+ * * Platforms: T186
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: N/A
+ * * Response Payload: @ref mrq_emc_dvfs_latency_response
+ * @addtogroup EMC
+ * @{
+ */
+
+/**
+ * @brief used by @ref mrq_emc_dvfs_latency_response
+ */
+struct emc_dvfs_latency {
+	/** @brief EMC frequency in kHz */
+	uint32_t freq;
+	/** @brief EMC DVFS latency in nanoseconds */
+	uint32_t latency;
+} __ABI_PACKED;
+
+#define EMC_DVFS_LATENCY_MAX_SIZE	14
+/**
+ * @brief response to #MRQ_EMC_DVFS_LATENCY
+ */
+struct mrq_emc_dvfs_latency_response {
+	/** @brief the number valid entries in #pairs */
+	uint32_t num_pairs;
+	/** @brief EMC <frequency, latency> information */
+	struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE];
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_TRACE_ITER
+ * @brief manage the trace iterator
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: N/A
+ * * Response Payload: @ref mrq_trace_iter_request
+ * @addtogroup Trace
+ * @{
+ */
+enum {
+	/** @brief (re)start the tracing now. Ignore older events */
+	TRACE_ITER_INIT = 0,
+	/** @brief clobber all events in the trace buffer */
+	TRACE_ITER_CLEAN = 1
+};
+
+/**
+ * @brief request with #MRQ_TRACE_ITER
+ */
+struct mrq_trace_iter_request {
+	/** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */
+	uint32_t cmd;
+} __ABI_PACKED;
+
+/** @} */
+
+/*
+ *  4. Enumerations
+ */
+
+/*
+ *   4.1 CPU enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ *
+ *   4.2 CPU Cluster enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ *
+ *   4.3 System low power state enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ */
+
+/*
+ *   4.4 Clock enumerations
+ *
+ * For clock enumerations, see <mach-t186/clk-t186.h>
+ */
+
+/*
+ *   4.5 Reset enumerations
+ *
+ * For reset enumerations, see <mach-t186/reset-t186.h>
+ */
+
+/*
+ *   4.6 Thermal sensor enumerations
+ *
+ * For thermal sensor enumerations, see <mach-t186/thermal-t186.h>
+ */
+
+/**
+ * @defgroup Error_Codes
+ * Negative values for mrq_response::err generally indicate some
+ * error. The ABI defines the following error codes. Negating these
+ * defines is an exercise left to the user.
+ * @{
+ */
+/** @brief No such file or directory */
+#define BPMP_ENOENT	2
+/** @brief No MRQ handler */
+#define BPMP_ENOHANDLER	3
+/** @brief I/O error */
+#define BPMP_EIO	5
+/** @brief Bad sub-MRQ command */
+#define BPMP_EBADCMD	6
+/** @brief Not enough memory */
+#define BPMP_ENOMEM	12
+/** @brief Permission denied */
+#define BPMP_EACCES	13
+/** @brief Bad address */
+#define BPMP_EFAULT	14
+/** @brief No such device */
+#define BPMP_ENODEV	19
+/** @brief Argument is a directory */
+#define BPMP_EISDIR	21
+/** @brief Invalid argument */
+#define BPMP_EINVAL	22
+/** @brief Timeout during operation */
+#define BPMP_ETIMEDOUT  23
+/** @brief Out of range */
+#define BPMP_ERANGE	34
+/** @} */
+/** @} */
+#endif
diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
new file mode 100644
index 000000000000..e17d091e8a9a
--- /dev/null
+++ b/include/soc/tegra/bpmp.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __SOC_TEGRA_BPMP_H
+#define __SOC_TEGRA_BPMP_H
+
+#include <linux/mailbox_client.h>
+#include <linux/reset-controller.h>
+#include <linux/semaphore.h>
+#include <linux/types.h>
+
+#define TEGRA_BPMP_MSG_SIZE 128
+#define TEGRA_BPMP_MSG_DATA_SIZE 120
+
+struct tegra_bpmp_soc {
+	struct {
+		struct {
+			unsigned int offset;
+			unsigned int count;
+			unsigned int timeout;
+		} cpu_tx, thread, cpu_rx;
+	} channels;
+	unsigned int num_resets;
+};
+
+struct tegra_bpmp_mb_data {
+	u32 code;
+	u32 flags;
+	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
+} __packed;
+
+struct tegra_bpmp_channel {
+	struct tegra_bpmp *bpmp;
+	struct tegra_bpmp_mb_data *ib;
+	struct tegra_bpmp_mb_data *ob;
+	struct completion completion;
+	struct tegra_ivc *ivc;
+};
+
+typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq,
+					 struct tegra_bpmp_channel *channel,
+					 void *data);
+
+struct tegra_bpmp_mrq {
+	struct list_head list;
+	unsigned int mrq;
+	tegra_bpmp_mrq_handler_t handler;
+	void *data;
+};
+
+struct tegra_bpmp {
+	const struct tegra_bpmp_soc *soc;
+	struct device *dev;
+
+	void __iomem *tx_base;
+	void __iomem *rx_base;
+
+	struct {
+		struct mbox_client client;
+		struct mbox_chan *channel;
+	} mbox;
+
+	struct tegra_bpmp_channel *channels;
+	unsigned int num_channels;
+
+	struct {
+		unsigned long *allocated;
+		unsigned long *busy;
+		unsigned int count;
+		struct semaphore lock;
+	} threaded;
+
+	struct list_head mrqs;
+	spinlock_t lock;
+
+	unsigned int num_clocks;
+	struct clk_hw **clocks;
+
+	struct reset_controller_dev rstc;
+};
+
+struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
+void tegra_bpmp_put(struct tegra_bpmp *bpmp);
+
+struct tegra_bpmp_message {
+	unsigned int mrq;
+
+	struct {
+		const void *data;
+		size_t size;
+	} tx;
+
+	struct {
+		void *data;
+		size_t size;
+	} rx;
+};
+
+int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
+			       struct tegra_bpmp_message *msg);
+int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
+			struct tegra_bpmp_message *msg);
+
+int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			   tegra_bpmp_mrq_handler_t handler, void *data);
+void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			 void *data);
+
+int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
+int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);
+
+#endif /* __SOC_TEGRA_BPMP_H */
-- 
2.9.0

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

The Boot and Power Management Processor (BPMP) is a co-processor found
on Tegra SoCs. It is designed to handle the early stages of the boot
process and offload power management tasks (such as clocks, resets,
powergates, ...) as well as system control services.

Compared to the ARM SCPI, the services provided by BPMP are message-
based rather than method-based. The BPMP firmware driver provides the
services to transmit data to and receive data from the BPMP. Users can
also register an MRQ, for which a service routine will be run when a
corresponding event is received from the firmware.

A set of messages, called the BPMP ABI, are specified for a number of
different services provided by the BPMP (such as clocks or resets).

Based on work by Sivaram Nair <sivaramn@nvidia.com> and Joseph Lo
<josephl@nvidia.com>.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- use a message structure for transfers to avoid long function
  prototypes
- restructure driver for easier maintainability
- rename bpmp_abi.h to bpmp-abi.h for consistency

 drivers/firmware/tegra/Kconfig  |   12 +
 drivers/firmware/tegra/Makefile |    1 +
 drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
 include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
 include/soc/tegra/bpmp.h        |  122 +++
 5 files changed, 2616 insertions(+)
 create mode 100644 drivers/firmware/tegra/bpmp.c
 create mode 100644 include/soc/tegra/bpmp-abi.h
 create mode 100644 include/soc/tegra/bpmp.h

diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
index 1fa3e4e136a5..ff2730d5c468 100644
--- a/drivers/firmware/tegra/Kconfig
+++ b/drivers/firmware/tegra/Kconfig
@@ -10,4 +10,16 @@ config TEGRA_IVC
 	  keeps the content is synchronization between host CPU and remote
 	  processors.
 
+config TEGRA_BPMP
+	bool "Tegra BPMP driver"
+	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
+	help
+	  BPMP (Boot and Power Management Processor) is designed to off-loading
+	  the PM functions which include clock/DVFS/thermal/power from the CPU.
+	  It needs HSP as the HW synchronization and notification module and
+	  IVC module as the message communication protocol.
+
+	  This driver manages the IPC interface between host CPU and the
+	  firmware running on BPMP.
+
 endmenu
diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
index 92e2153e8173..e34a2f79e1ad 100644
--- a/drivers/firmware/tegra/Makefile
+++ b/drivers/firmware/tegra/Makefile
@@ -1 +1,2 @@
+obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
 obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
new file mode 100644
index 000000000000..a09043b1be05
--- /dev/null
+++ b/drivers/firmware/tegra/bpmp.c
@@ -0,0 +1,880 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#define DEBUG
+
+#include <linux/clk/tegra.h>
+#include <linux/mailbox_client.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/semaphore.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+#include <soc/tegra/ivc.h>
+
+#define MSG_ACK		BIT(0)
+#define MSG_RING	BIT(1)
+
+static inline struct tegra_bpmp *
+mbox_client_to_bpmp(struct mbox_client *client)
+{
+	return container_of(client, struct tegra_bpmp, mbox.client);
+}
+
+struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
+{
+	struct platform_device *pdev;
+	struct tegra_bpmp *bpmp;
+	struct device_node *np;
+
+	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
+	if (!np)
+		return ERR_PTR(-ENOENT);
+
+	pdev = of_find_device_by_node(np);
+	if (!pdev) {
+		bpmp = ERR_PTR(-ENODEV);
+		goto put;
+	}
+
+	bpmp = platform_get_drvdata(pdev);
+	if (!bpmp) {
+		bpmp = ERR_PTR(-EPROBE_DEFER);
+		put_device(&pdev->dev);
+		goto put;
+	}
+
+put:
+	of_node_put(np);
+	return bpmp;
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_get);
+
+void tegra_bpmp_put(struct tegra_bpmp *bpmp)
+{
+	if (bpmp)
+		put_device(bpmp->dev);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_put);
+
+static int tegra_bpmp_channel_get_index(struct tegra_bpmp_channel *channel)
+{
+	return channel - channel->bpmp->channels;
+}
+
+static int
+tegra_bpmp_channel_get_thread_index(struct tegra_bpmp_channel *channel)
+{
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	unsigned int offset, count;
+	int index;
+
+	offset = bpmp->soc->channels.thread.offset;
+	count = bpmp->soc->channels.thread.count;
+
+	index = tegra_bpmp_channel_get_index(channel);
+	if (index < 0)
+		return index;
+
+	if (index < offset || index >= offset + count)
+		return -EINVAL;
+
+	return index - offset;
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_thread(struct tegra_bpmp *bpmp, unsigned int index)
+{
+	unsigned int offset = bpmp->soc->channels.thread.offset;
+	unsigned int count = bpmp->soc->channels.thread.count;
+
+	if (index >= count)
+		return NULL;
+
+	return &bpmp->channels[offset + index];
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_tx(struct tegra_bpmp *bpmp)
+{
+	unsigned int offset = bpmp->soc->channels.cpu_tx.offset;
+
+	return &bpmp->channels[offset + smp_processor_id()];
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_channel_get_rx(struct tegra_bpmp *bpmp)
+{
+	unsigned int offset = bpmp->soc->channels.cpu_rx.offset;
+
+	return &bpmp->channels[offset];
+}
+
+static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg)
+{
+	return (msg->tx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
+	       (msg->rx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
+	       (msg->tx.size == 0 || msg->tx.data) &&
+	       (msg->rx.size == 0 || msg->rx.data);
+}
+
+static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
+{
+	void *frame;
+
+	frame = tegra_ivc_read_get_next_frame(channel->ivc);
+	if (IS_ERR_OR_NULL(frame)) {
+		channel->ib = NULL;
+		return false;
+	}
+
+	channel->ib = frame;
+
+	return true;
+}
+
+static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
+{
+	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
+	ktime_t start, now;
+
+	start = ns_to_ktime(local_clock());
+
+	do {
+		if (tegra_bpmp_master_acked(channel))
+			return 0;
+
+		now = ns_to_ktime(local_clock());
+	} while (ktime_us_delta(now, start) < timeout);
+
+	return -ETIMEDOUT;
+}
+
+static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
+{
+	void *frame;
+
+	frame = tegra_ivc_write_get_next_frame(channel->ivc);
+	if (IS_ERR_OR_NULL(frame)) {
+		channel->ob = NULL;
+		return false;
+	}
+
+	channel->ob = frame;
+
+	return true;
+}
+
+static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
+{
+	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
+	ktime_t start, now;
+
+	start = ns_to_ktime(local_clock());
+
+	do {
+		if (tegra_bpmp_master_free(channel))
+			return 0;
+
+		now = ns_to_ktime(local_clock());
+	} while (ktime_us_delta(now, start) < timeout);
+
+	return -ETIMEDOUT;
+}
+
+static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
+					 void *data, size_t size)
+{
+	if (data && size > 0)
+		memcpy_fromio(data, channel->ib->data, size);
+
+	return tegra_ivc_read_advance(channel->ivc);
+}
+
+static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
+				       void *data, size_t size)
+{
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	unsigned long flags;
+	ssize_t err;
+	int index;
+
+	index = tegra_bpmp_channel_get_thread_index(channel);
+	if (index < 0)
+		return index;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+	err = __tegra_bpmp_channel_read(channel, data, size);
+	clear_bit(index, bpmp->threaded.allocated);
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+
+	up(&bpmp->threaded.lock);
+
+	return err;
+}
+
+static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
+					  unsigned int mrq, unsigned long flags,
+					  const void *data, size_t size)
+{
+	channel->ob->code = mrq;
+	channel->ob->flags = flags;
+
+	if (data && size > 0)
+		memcpy_toio(channel->ob->data, data, size);
+
+	return tegra_ivc_write_advance(channel->ivc);
+}
+
+static struct tegra_bpmp_channel *
+tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
+			  const void *data, size_t size)
+{
+	unsigned long timeout = bpmp->soc->channels.thread.timeout;
+	unsigned int count = bpmp->soc->channels.thread.count;
+	struct tegra_bpmp_channel *channel;
+	unsigned long flags;
+	unsigned int index;
+	int err;
+
+	err = down_timeout(&bpmp->threaded.lock, usecs_to_jiffies(timeout));
+	if (err < 0)
+		return ERR_PTR(err);
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	index = find_first_zero_bit(bpmp->threaded.allocated, count);
+	if (index == count) {
+		channel = ERR_PTR(-EBUSY);
+		goto unlock;
+	}
+
+	channel = tegra_bpmp_channel_get_thread(bpmp, index);
+	if (!channel) {
+		channel = ERR_PTR(-EINVAL);
+		goto unlock;
+	}
+
+	if (!tegra_bpmp_master_free(channel)) {
+		channel = ERR_PTR(-EBUSY);
+		goto unlock;
+	}
+
+	set_bit(index, bpmp->threaded.allocated);
+
+	err = __tegra_bpmp_channel_write(channel, mrq, MSG_ACK | MSG_RING,
+					 data, size);
+	if (err < 0) {
+		clear_bit(index, bpmp->threaded.allocated);
+		goto unlock;
+	}
+
+	set_bit(index, bpmp->threaded.busy);
+
+unlock:
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+	return channel;
+}
+
+static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
+					unsigned int mrq, unsigned long flags,
+					const void *data, size_t size)
+{
+	int err;
+
+	err = tegra_bpmp_wait_master_free(channel);
+	if (err < 0)
+		return err;
+
+	return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
+}
+
+int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
+			       struct tegra_bpmp_message *msg)
+{
+	struct tegra_bpmp_channel *channel;
+	int err;
+
+	if (WARN_ON(!irqs_disabled()))
+		return -EPERM;
+
+	if (!tegra_bpmp_message_valid(msg))
+		return -EINVAL;
+
+	channel = tegra_bpmp_channel_get_tx(bpmp);
+
+	err = tegra_bpmp_channel_write(channel, msg->mrq, MSG_ACK,
+				       msg->tx.data, msg->tx.size);
+	if (err < 0)
+		return err;
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return err;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+
+	err = tegra_bpmp_wait_ack(channel);
+	if (err)
+		return err;
+
+	return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
+
+int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
+			struct tegra_bpmp_message *msg)
+{
+	struct tegra_bpmp_channel *channel;
+	unsigned long timeout;
+	int err;
+
+	if (WARN_ON(irqs_disabled()))
+		return -EPERM;
+
+	if (!tegra_bpmp_message_valid(msg))
+		return -EINVAL;
+
+	channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
+					    msg->tx.size);
+	if (IS_ERR(channel))
+		return PTR_ERR(channel);
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return err;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+
+	timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout);
+
+	err = wait_for_completion_timeout(&channel->completion, timeout);
+	if (err <= 0) {
+		if (err == 0)
+			err = -ETIMEDOUT;
+
+		return err;
+	}
+
+	return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
+
+static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
+						  unsigned int mrq)
+{
+	struct tegra_bpmp_mrq *entry;
+
+	list_for_each_entry(entry, &bpmp->mrqs, list)
+		if (entry->mrq == mrq)
+			return entry;
+
+	return NULL;
+}
+
+static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
+				  int code, const void *data, size_t size)
+{
+	unsigned long flags = channel->ib->flags;
+	struct tegra_bpmp *bpmp = channel->bpmp;
+	struct tegra_bpmp_mb_data *frame;
+	int err;
+
+	if (WARN_ON(size > TEGRA_BPMP_MSG_DATA_SIZE))
+		return;
+
+	err = tegra_ivc_read_advance(channel->ivc);
+	if (WARN_ON(err < 0))
+		return;
+
+	if ((flags & MSG_ACK) == 0)
+		return;
+
+	frame = tegra_ivc_write_get_next_frame(channel->ivc);
+	if (WARN_ON(IS_ERR_OR_NULL(frame)))
+		return;
+
+	frame->code = code;
+
+	if (data && size > 0)
+		memcpy_toio(frame->data, data, size);
+
+	err = tegra_ivc_write_advance(channel->ivc);
+	if (WARN_ON(err < 0))
+		return;
+
+	if (flags & MSG_RING) {
+		err = mbox_send_message(bpmp->mbox.channel, NULL);
+		if (WARN_ON(err < 0))
+			return;
+
+		mbox_client_txdone(bpmp->mbox.channel, 0);
+	}
+}
+
+static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
+				  unsigned int mrq,
+				  struct tegra_bpmp_channel *channel)
+{
+	struct tegra_bpmp_mrq *entry;
+	u32 zero = 0;
+
+	spin_lock(&bpmp->lock);
+
+	entry = tegra_bpmp_find_mrq(bpmp, mrq);
+	if (!entry) {
+		spin_unlock(&bpmp->lock);
+		tegra_bpmp_mrq_return(channel, -EINVAL, &zero, sizeof(zero));
+		return;
+	}
+
+	entry->handler(mrq, channel, entry->data);
+
+	spin_unlock(&bpmp->lock);
+}
+
+int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			   tegra_bpmp_mrq_handler_t handler, void *data)
+{
+	struct tegra_bpmp_mrq *entry;
+	unsigned long flags;
+
+	if (!handler)
+		return -EINVAL;
+
+	entry = devm_kzalloc(bpmp->dev, sizeof(*entry), GFP_KERNEL);
+	if (!entry)
+		return -ENOMEM;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	entry->mrq = mrq;
+	entry->handler = handler;
+	entry->data = data;
+	list_add(&entry->list, &bpmp->mrqs);
+
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_request_mrq);
+
+void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data)
+{
+	struct tegra_bpmp_mrq *entry;
+	unsigned long flags;
+
+	spin_lock_irqsave(&bpmp->lock, flags);
+
+	entry = tegra_bpmp_find_mrq(bpmp, mrq);
+	if (!entry)
+		goto unlock;
+
+	list_del(&entry->list);
+	devm_kfree(bpmp->dev, entry);
+
+unlock:
+	spin_unlock_irqrestore(&bpmp->lock, flags);
+}
+EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq);
+
+static void tegra_bpmp_mrq_handle_ping(unsigned int mrq,
+				       struct tegra_bpmp_channel *channel,
+				       void *data)
+{
+	struct mrq_ping_request *request;
+	struct mrq_ping_response response;
+
+	request = (struct mrq_ping_request *)channel->ib->data;
+
+	memset(&response, 0, sizeof(response));
+	response.reply = request->challenge << (smp_processor_id() + 1);
+
+	tegra_bpmp_mrq_return(channel, 0, &response, sizeof(response));
+}
+
+static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
+{
+	struct mrq_ping_response response;
+	struct mrq_ping_request request;
+	struct tegra_bpmp_message msg;
+	ktime_t start, delta;
+	unsigned long flags;
+	int err;
+
+	memset(&request, 0, sizeof(request));
+	request.challenge = 1;
+
+	memset(&response, 0, sizeof(response));
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_PING;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	start = ktime_get();
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+	local_irq_restore(flags);
+
+	delta = ktime_sub(ktime_get(), start);
+
+	if (!err)
+		dev_info(bpmp->dev,
+			 "ping ok: challenge: %u, response: %u, time: %lld\n",
+			 request.challenge, response.reply,
+			 ktime_to_us(delta));
+
+	return err;
+}
+
+static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
+				       size_t size)
+{
+	struct mrq_query_tag_request request;
+	struct tegra_bpmp_message msg;
+	unsigned long flags;
+	dma_addr_t phys;
+	void *virt;
+	int err;
+
+	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
+				  GFP_KERNEL | GFP_DMA32);
+	if (!virt)
+		return -ENOMEM;
+
+	memset(&request, 0, sizeof(request));
+	request.addr = phys;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_QUERY_TAG;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
+	local_irq_restore(flags);
+
+	if (err == 0)
+		strlcpy(tag, virt, size);
+
+	dma_free_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, virt, phys);
+
+	return err;
+}
+
+static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
+{
+	unsigned long flags = channel->ob->flags;
+
+	if ((flags & MSG_RING) == 0)
+		return;
+
+	complete(&channel->completion);
+}
+
+static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
+{
+	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
+	struct tegra_bpmp_channel *channel;
+	unsigned int i, count;
+	unsigned long *busy;
+
+	channel = tegra_bpmp_channel_get_rx(bpmp);
+	count = bpmp->soc->channels.thread.count;
+	busy = bpmp->threaded.busy;
+
+	if (tegra_bpmp_master_acked(channel))
+		tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel);
+
+	spin_lock(&bpmp->lock);
+
+	for_each_set_bit(i, busy, count) {
+		struct tegra_bpmp_channel *channel;
+
+		channel = tegra_bpmp_channel_get_thread(bpmp, i);
+		if (!channel)
+			continue;
+
+		if (tegra_bpmp_master_acked(channel)) {
+			tegra_bpmp_channel_signal(channel);
+			clear_bit(i, busy);
+		}
+	}
+
+	spin_unlock(&bpmp->lock);
+}
+
+static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
+{
+	struct tegra_bpmp *bpmp = data;
+	int err;
+
+	err = mbox_send_message(bpmp->mbox.channel, NULL);
+	if (err < 0)
+		return;
+
+	mbox_client_txdone(bpmp->mbox.channel, 0);
+}
+
+static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
+				   struct tegra_bpmp *bpmp,
+				   unsigned int index)
+{
+	size_t message, queue;
+	void __iomem *rx_base;
+	void __iomem *tx_base;
+	int err;
+
+	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
+				    GFP_KERNEL);
+	if (!channel->ivc)
+		return -ENOMEM;
+
+	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
+	queue = tegra_ivc_total_queue_size(message);
+
+	rx_base = bpmp->rx_base + queue * index;
+	tx_base = bpmp->tx_base + queue * index;
+
+	err = tegra_ivc_init(channel->ivc, bpmp->dev, rx_base, DMA_ERROR_CODE,
+			     tx_base, DMA_ERROR_CODE, 1, message,
+			     tegra_bpmp_ivc_notify, bpmp);
+	if (err < 0) {
+		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
+			index, err);
+		return err;
+	}
+
+	/* reset the channel state */
+	tegra_ivc_reset(channel->ivc);
+
+	/* sync the channel state with BPMP */
+	while (tegra_ivc_notified(channel->ivc))
+		;
+
+	init_completion(&channel->completion);
+	channel->bpmp = bpmp;
+
+	return 0;
+}
+
+static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
+{
+	struct mrq_pg_read_state_response response;
+	struct mrq_pg_read_state_request request;
+	struct tegra_bpmp_message msg;
+	unsigned int i;
+	int err;
+
+	dev_dbg(bpmp->dev, "powergates:\n");
+
+	for (i = 0; i < 32; i++) {
+		memset(&request, 0, sizeof(request));
+		request.partition_id = i;
+
+		memset(&response, 0, sizeof(response));
+
+		memset(&msg, 0, sizeof(msg));
+		msg.mrq = MRQ_PG_READ_STATE;
+		msg.tx.data = &request;
+		msg.tx.size = sizeof(request);
+		msg.rx.data = &response;
+		msg.rx.size = sizeof(response);
+
+		err = tegra_bpmp_transfer(bpmp, &msg);
+		if (err < 0) {
+			dev_err(bpmp->dev, "failed to transfer message: %d\n", err);
+			continue;
+		}
+
+		dev_dbg(bpmp->dev, "  %u: %x (%x)\n", i, response.logic_state,
+			response.sram_state);
+	}
+
+	return 0;
+}
+
+static int tegra_bpmp_probe(struct platform_device *pdev)
+{
+	struct tegra_bpmp_channel *channel;
+	struct tegra_bpmp *bpmp;
+	struct device_node *np;
+	struct resource res;
+	unsigned int i;
+	char tag[32];
+	size_t size;
+	int err;
+
+	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
+	if (!bpmp)
+		return -ENOMEM;
+
+	bpmp->soc = of_device_get_match_data(&pdev->dev);
+	bpmp->dev = &pdev->dev;
+
+	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
+	if (!np)
+		return -ENOENT;
+
+	of_address_to_resource(np, 0, &res);
+	of_node_put(np);
+
+	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
+	if (IS_ERR(bpmp->tx_base))
+		return PTR_ERR(bpmp->tx_base);
+
+	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
+	if (!np)
+		return -ENOENT;
+
+	of_address_to_resource(np, 0, &res);
+	of_node_put(np);
+
+	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
+	if (IS_ERR(bpmp->rx_base))
+		return PTR_ERR(bpmp->rx_base);
+
+	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
+			     bpmp->soc->channels.thread.count +
+			     bpmp->soc->channels.cpu_rx.count;
+
+	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
+				      sizeof(*channel), GFP_KERNEL);
+	if (!bpmp->channels)
+		return -ENOMEM;
+
+	/* mbox registration */
+	bpmp->mbox.client.dev = &pdev->dev;
+	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
+	bpmp->mbox.client.tx_block = false;
+	bpmp->mbox.client.knows_txdone = false;
+
+	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
+	if (IS_ERR(bpmp->mbox.channel)) {
+		err = PTR_ERR(bpmp->mbox.channel);
+		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
+		return err;
+	}
+
+	/* message channel initialization */
+	for (i = 0; i < bpmp->num_channels; i++) {
+		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
+
+		err = tegra_bpmp_channel_init(channel, bpmp, i);
+		if (err)
+			return err;
+	}
+
+	bpmp->threaded.count = bpmp->soc->channels.thread.count;
+	size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long);
+
+	bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!bpmp->threaded.allocated)
+		return -ENOMEM;
+
+	bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+	if (!bpmp->threaded.busy)
+		return -ENOMEM;
+
+	sema_init(&bpmp->threaded.lock, bpmp->threaded.count);
+
+	INIT_LIST_HEAD(&bpmp->mrqs);
+	spin_lock_init(&bpmp->lock);
+
+	err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
+				     tegra_bpmp_mrq_handle_ping, bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_ping(bpmp);
+	if (err < 0) {
+		dev_err(&pdev->dev, "failed to ping BPMP: %d\n", err);
+		goto free_mrq;
+	}
+
+	err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1);
+	if (err) {
+		dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
+		goto free_mrq;
+	}
+
+	dev_info(&pdev->dev, "firmware: %s\n", tag);
+
+	err = tegra_bpmp_init_clocks(bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_init_resets(bpmp);
+	if (err < 0)
+		return err;
+
+	err = tegra_bpmp_init_powergates(bpmp);
+	if (err < 0)
+		return err;
+
+	platform_set_drvdata(pdev, bpmp);
+
+	return 0;
+
+free_mrq:
+	tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
+
+	return err;
+}
+
+static const struct tegra_bpmp_soc tegra186_soc = {
+	.channels = {
+		.cpu_tx = {
+			.offset = 0,
+			.count = 6,
+			.timeout = 60 * USEC_PER_SEC,
+		},
+		.thread = {
+			.offset = 6,
+			.count = 7,
+			.timeout = 600 * USEC_PER_SEC,
+		},
+		.cpu_rx = {
+			.offset = 13,
+			.count = 1,
+			.timeout = 0,
+		},
+	},
+	.num_resets = 193,
+};
+
+static const struct of_device_id tegra_bpmp_match[] = {
+	{ .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc },
+	{ }
+};
+
+static struct platform_driver tegra_bpmp_driver = {
+	.driver = {
+		.name = "tegra-bpmp",
+		.of_match_table = tegra_bpmp_match,
+	},
+	.probe = tegra_bpmp_probe,
+};
+
+static int __init tegra_bpmp_init(void)
+{
+	return platform_driver_register(&tegra_bpmp_driver);
+}
+core_initcall(tegra_bpmp_init);
diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
new file mode 100644
index 000000000000..0aaef5960e29
--- /dev/null
+++ b/include/soc/tegra/bpmp-abi.h
@@ -0,0 +1,1601 @@
+/*
+ * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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 _ABI_BPMP_ABI_H_
+#define _ABI_BPMP_ABI_H_
+
+#ifdef LK
+#include <stdint.h>
+#endif
+
+#ifndef __ABI_PACKED
+#define __ABI_PACKED __attribute__((packed))
+#endif
+
+#ifdef NO_GCC_EXTENSIONS
+#define EMPTY char empty;
+#define EMPTY_ARRAY 1
+#else
+#define EMPTY
+#define EMPTY_ARRAY 0
+#endif
+
+#ifndef __UNION_ANON
+#define __UNION_ANON
+#endif
+/**
+ * @file
+ */
+
+
+/**
+ * @defgroup MRQ MRQ Messages
+ * @brief Messages sent to/from BPMP via IPC
+ * @{
+ *   @defgroup MRQ_Format Message Format
+ *   @defgroup MRQ_Codes Message Request (MRQ) Codes
+ *   @defgroup MRQ_Payloads Message Payloads
+ *   @defgroup Error_Codes Error Codes
+ * @}
+ */
+
+/**
+ * @addtogroup MRQ_Format Message Format
+ * @{
+ * The CPU requests the BPMP to perform a particular service by
+ * sending it an IVC frame containing a single MRQ message. An MRQ
+ * message consists of a @ref mrq_request followed by a payload whose
+ * format depends on mrq_request::mrq.
+ *
+ * The BPMP processes the data and replies with an IVC frame (on the
+ * same IVC channel) containing and MRQ response. An MRQ response
+ * consists of a @ref mrq_response followed by a payload whose format
+ * depends on the associated mrq_request::mrq.
+ *
+ * A well-defined subset of the MRQ messages that the CPU sends to the
+ * BPMP can lead to BPMP eventually sending an MRQ message to the
+ * CPU. For example, when the CPU uses an #MRQ_THERMAL message to set
+ * a thermal trip point, the BPMP may eventually send a single
+ * #MRQ_THERMAL message of its own to the CPU indicating that the trip
+ * point has been crossed.
+ * @}
+ */
+
+/**
+ * @ingroup MRQ_Format
+ * @brief header for an MRQ message
+ *
+ * Provides the MRQ number for the MRQ message: #mrq. The remainder of
+ * the MRQ message is a payload (immediately following the
+ * mrq_request) whose format depends on mrq.
+ *
+ * @todo document the flags
+ */
+struct mrq_request {
+	/** @brief MRQ number of the request */
+	uint32_t mrq;
+	/** @brief flags for the request */
+	uint32_t flags;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Format
+ * @brief header for an MRQ response
+ *
+ *  Provides an error code for the associated MRQ message. The
+ *  remainder of the MRQ response is a payload (immediately following
+ *  the mrq_response) whose format depends on the associated
+ *  mrq_request::mrq
+ *
+ * @todo document the flags
+ */
+struct mrq_response {
+	/** @brief error code for the MRQ request itself */
+	int32_t err;
+	/** @brief flags for the response */
+	uint32_t flags;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Format
+ * Minimum needed size for an IPC message buffer
+ */
+#define MSG_MIN_SZ	128
+/**
+ * @ingroup MRQ_Format
+ *  Minimum size guaranteed for data in an IPC message buffer
+ */
+#define MSG_DATA_MIN_SZ	120
+
+/**
+ * @ingroup MRQ_Codes
+ * @name Legal MRQ codes
+ * These are the legal values for mrq_request::mrq
+ * @{
+ */
+
+#define MRQ_PING		0
+#define MRQ_QUERY_TAG		1
+#define MRQ_MODULE_LOAD		4
+#define MRQ_MODULE_UNLOAD	5
+#define MRQ_TRACE_MODIFY	7
+#define MRQ_WRITE_TRACE		8
+#define MRQ_THREADED_PING	9
+#define MRQ_MODULE_MAIL		11
+#define MRQ_DEBUGFS		19
+#define MRQ_RESET		20
+#define MRQ_I2C			21
+#define MRQ_CLK			22
+#define MRQ_QUERY_ABI		23
+#define MRQ_PG_READ_STATE	25
+#define MRQ_PG_UPDATE_STATE	26
+#define MRQ_THERMAL		27
+#define MRQ_CPU_VHINT		28
+#define MRQ_ABI_RATCHET		29
+#define MRQ_EMC_DVFS_LATENCY	31
+#define MRQ_TRACE_ITER		64
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @brief Maximum MRQ code to be sent by CPU software to
+ * BPMP. Subject to change in future
+ */
+#define MAX_CPU_MRQ_ID		64
+
+/**
+ * @addtogroup MRQ_Payloads Message Payloads
+ * @{
+ *   @defgroup Ping
+ *   @defgroup Query_Tag Query Tag
+ *   @defgroup Module Loadable Modules
+ *   @defgroup Trace
+ *   @defgroup Debugfs
+ *   @defgroup Reset
+ *   @defgroup I2C
+ *   @defgroup Clocks
+ *   @defgroup ABI_info ABI Info
+ *   @defgroup MC_Flush MC Flush
+ *   @defgroup Powergating
+ *   @defgroup Thermal
+ *   @defgroup Vhint CPU Voltage hint
+ *   @defgroup MRQ_Deprecated Deprecated MRQ messages
+ *   @defgroup EMC
+ * @}
+ */
+
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PING
+ * @brief A simple ping
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: @ref mrq_ping_request
+ * * Response Payload: @ref mrq_ping_response
+ *
+ * @ingroup MRQ_Codes
+ * @def MRQ_THREADED_PING
+ * @brief A deeper ping
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_ping_request
+ * * Response Payload: @ref mrq_ping_response
+ *
+ * Behavior is equivalent to a simple #MRQ_PING except that BPMP
+ * responds from a thread context (providing a slightly more robust
+ * sign of life).
+ *
+ */
+
+/**
+ * @ingroup Ping
+ * @brief request with #MRQ_PING
+ *
+ * Used by the sender of an #MRQ_PING message to request a pong from
+ * recipient. The response from the recipient is computed based on
+ * #challenge.
+ */
+struct mrq_ping_request {
+/** @brief arbitrarily chosen value */
+	uint32_t challenge;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Ping
+ * @brief response to #MRQ_PING
+ *
+ * Sent in response to an #MRQ_PING message. #reply should be the
+ * mrq_ping_request challenge left shifted by 1 with the carry-bit
+ * dropped.
+ *
+ */
+struct mrq_ping_response {
+	/** @brief response to the MRQ_PING challege */
+	uint32_t reply;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_QUERY_TAG
+ * @brief Query BPMP firmware's tag (i.e. version information)
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_query_tag_request
+ * * Response Payload: N/A
+ *
+ */
+
+/**
+ * @ingroup Query_Tag
+ * @brief request with #MRQ_QUERY_TAG
+ *
+ * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory
+ * pointed by #addr with BPMP firmware header.
+ *
+ * The sender is reponsible for ensuring that #addr is mapped in to
+ * the recipient's address map.
+ */
+struct mrq_query_tag_request {
+  /** @brief base address to store the firmware header */
+	uint32_t addr;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_LOAD
+ * @brief dynamically load a BPMP code module
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_load_request
+ * * Response Payload: @ref mrq_module_load_response
+ *
+ * @note This MRQ is disabled on production systems
+ *
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_LOAD
+ *
+ * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically
+ * load the code located at #phys_addr and having size #size
+ * bytes. #phys_addr is treated as a void pointer.
+ *
+ * The recipient copies the code from #phys_addr to locally allocated
+ * memory prior to responding to this message.
+ *
+ * @todo document the module header format
+ *
+ * The sender is responsible for ensuring that the code is mapped in
+ * the recipient's address map.
+ *
+ */
+struct mrq_module_load_request {
+	/** @brief base address of the code to load. Treated as (void *) */
+	uint32_t phys_addr; /* (void *) */
+	/** @brief size in bytes of code to load */
+	uint32_t size;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Module
+ * @brief response to #MRQ_MODULE_LOAD
+ *
+ * @todo document mrq_response::err
+ */
+struct mrq_module_load_response {
+	/** @brief handle to the loaded module */
+	uint32_t base;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_UNLOAD
+ * @brief unload a previously loaded code module
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_unload_request
+ * * Response Payload: N/A
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_UNLOAD
+ *
+ * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded
+ * module be unloaded.
+ */
+struct mrq_module_unload_request {
+	/** @brief handle of the module to unload */
+	uint32_t base;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_TRACE_MODIFY
+ * @brief modify the set of enabled trace events
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_trace_modify_request
+ * * Response Payload: @ref mrq_trace_modify_response
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Trace
+ * @brief request with #MRQ_TRACE_MODIFY
+ *
+ * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace
+ * events.  #set takes precedence for any bit set in both #set and
+ * #clr.
+ */
+struct mrq_trace_modify_request {
+	/** @brief bit mask of trace events to disable */
+	uint32_t clr;
+	/** @brief bit mask of trace events to enable */
+	uint32_t set;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Trace
+ * @brief response to #MRQ_TRACE_MODIFY
+ *
+ * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the
+ * state of which events are enabled after the recipient acted on the
+ * message.
+ *
+ */
+struct mrq_trace_modify_response {
+	/** @brief bit mask of trace event enable states */
+	uint32_t mask;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_WRITE_TRACE
+ * @brief Write trace data to a buffer
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_write_trace_request
+ * * Response Payload: @ref mrq_write_trace_response
+ *
+ * mrq_response::err depends on the @ref mrq_write_trace_request field
+ * values. err is -#BPMP_EINVAL if size is zero or area is NULL or
+ * area is in an illegal range. A positive value for err indicates the
+ * number of bytes written to area.
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Trace
+ * @brief request with #MRQ_WRITE_TRACE
+ *
+ * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace
+ * data from the recipient's local buffer to the output buffer. #area
+ * is treated as a byte-aligned pointer in the recipient's address
+ * space.
+ *
+ * The sender is responsible for ensuring that the output
+ * buffer is mapped in the recipient's address map. The recipient is
+ * responsible for protecting its own code and data from accidental
+ * overwrites.
+ */
+struct mrq_write_trace_request {
+	/** @brief base address of output buffer */
+	uint32_t area;
+	/** @brief size in bytes of the output buffer */
+	uint32_t size;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Trace
+ * @brief response to #MRQ_WRITE_TRACE
+ *
+ * Once this response is sent, the respondent will not access the
+ * output buffer further.
+ */
+struct mrq_write_trace_response {
+	/**
+	 * @brief flag whether more data remains in local buffer
+	 *
+	 * Value is 1 if the entire local trace buffer has been
+	 * drained to the outputbuffer. Value is 0 otherwise.
+	 */
+	uint32_t eof;
+} __ABI_PACKED;
+
+/** @private */
+struct mrq_threaded_ping_request {
+	uint32_t challenge;
+} __ABI_PACKED;
+
+/** @private */
+struct mrq_threaded_ping_response {
+	uint32_t reply;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_MODULE_MAIL
+ * @brief send a message to a loadable module
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_module_mail_request
+ * * Response Payload: @ref mrq_module_mail_response
+ *
+ * @note This MRQ is disabled on production systems
+ */
+
+/**
+ * @ingroup Module
+ * @brief request with #MRQ_MODULE_MAIL
+ */
+struct mrq_module_mail_request {
+	/** @brief handle to the previously loaded module */
+	uint32_t base;
+	/** @brief module-specific mail payload
+	 *
+	 * The length of data[ ] is unknown to the BPMP core firmware
+	 * but it is limited to the size of an IPC message.
+	 */
+	uint8_t data[EMPTY_ARRAY];
+} __ABI_PACKED;
+
+/**
+ * @ingroup Module
+ * @brief response to #MRQ_MODULE_MAIL
+ */
+struct mrq_module_mail_response {
+	/** @brief module-specific mail payload
+	 *
+	 * The length of data[ ] is unknown to the BPMP core firmware
+	 * but it is limited to the size of an IPC message.
+	 */
+	uint8_t data[EMPTY_ARRAY];
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_DEBUGFS
+ * @brief Interact with BPMP's debugfs file nodes
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_debugfs_request
+ * * Response Payload: @ref mrq_debugfs_response
+ */
+
+/**
+ * @addtogroup Debugfs
+ * @{
+ *
+ * The BPMP firmware implements a pseudo-filesystem called
+ * debugfs. Any driver within the firmware may register with debugfs
+ * to expose an arbitrary set of "files" in the filesystem. When
+ * software on the CPU writes to a debugfs file, debugfs passes the
+ * written data to a callback provided by the driver. When software on
+ * the CPU reads a debugfs file, debugfs queries the driver for the
+ * data to return to the CPU. The intention of the debugfs filesystem
+ * is to provide information useful for debugging the system at
+ * runtime.
+ *
+ * @note The files exposed via debugfs are not part of the
+ * BPMP firmware's ABI. debugfs files may be added or removed in any
+ * given version of the firmware. Typically the semantics of a debugfs
+ * file are consistent from version to version but even that is not
+ * guaranteed.
+ *
+ * @}
+ */
+/** @ingroup Debugfs */
+enum mrq_debugfs_commands {
+	CMD_DEBUGFS_READ = 1,
+	CMD_DEBUGFS_WRITE = 2,
+	CMD_DEBUGFS_DUMPDIR = 3,
+	CMD_DEBUGFS_MAX
+};
+
+/**
+ * @ingroup Debugfs
+ * @brief parameters for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_fileop_request {
+	/** @brief physical address pointing at filename */
+	uint32_t fnameaddr;
+	/** @brief length in bytes of filename buffer */
+	uint32_t fnamelen;
+	/** @brief physical address pointing to data buffer */
+	uint32_t dataaddr;
+	/** @brief length in bytes of data buffer */
+	uint32_t datalen;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief parameters for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_dumpdir_request {
+	/** @brief physical address pointing to data buffer */
+	uint32_t dataaddr;
+	/** @brief length in bytes of data buffer */
+	uint32_t datalen;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief response data for CMD_DEBUGFS_READ/WRITE command
+ */
+struct cmd_debugfs_fileop_response {
+	/** @brief always 0 */
+	uint32_t reserved;
+	/** @brief number of bytes read from or written to data buffer */
+	uint32_t nbytes;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief response data for CMD_DEBUGFS_DUMPDIR command
+ */
+struct cmd_debugfs_dumpdir_response {
+	/** @brief always 0 */
+	uint32_t reserved;
+	/** @brief number of bytes read from or written to data buffer */
+	uint32_t nbytes;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ * @brief request with #MRQ_DEBUGFS.
+ *
+ * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs
+ * command to execute. Legal commands are the values of @ref
+ * mrq_debugfs_commands. Each command requires a specific additional
+ * payload of data.
+ *
+ * |command            |payload|
+ * |-------------------|-------|
+ * |CMD_DEBUGFS_READ   |fop    |
+ * |CMD_DEBUGFS_WRITE  |fop    |
+ * |CMD_DEBUGFS_DUMPDIR|dumpdir|
+ */
+struct mrq_debugfs_request {
+	uint32_t cmd;
+	union {
+		struct cmd_debugfs_fileop_request fop;
+		struct cmd_debugfs_dumpdir_request dumpdir;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Debugfs
+ */
+struct mrq_debugfs_response {
+	/** @brief always 0 */
+	int32_t reserved;
+	union {
+		/** @brief response data for CMD_DEBUGFS_READ OR
+		 * CMD_DEBUGFS_WRITE command
+		 */
+		struct cmd_debugfs_fileop_response fop;
+		/** @brief response data for CMD_DEBUGFS_DUMPDIR command */
+		struct cmd_debugfs_dumpdir_response dumpdir;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @addtogroup Debugfs
+ * @{
+ */
+#define DEBUGFS_S_ISDIR	(1 << 9)
+#define DEBUGFS_S_IRUSR	(1 << 8)
+#define DEBUGFS_S_IWUSR	(1 << 7)
+/** @} */
+
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_RESET
+ * @brief reset an IP block
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_reset_request
+ * * Response Payload: N/A
+ */
+
+/**
+ * @ingroup Reset
+ */
+enum mrq_reset_commands {
+	CMD_RESET_ASSERT = 1,
+	CMD_RESET_DEASSERT = 2,
+	CMD_RESET_MODULE = 3,
+	CMD_RESET_MAX, /* not part of ABI and subject to change */
+};
+
+/**
+ * @ingroup Reset
+ * @brief request with MRQ_RESET
+ *
+ * Used by the sender of an #MRQ_RESET message to request BPMP to
+ * assert or or deassert a given reset line.
+ */
+struct mrq_reset_request {
+	/** @brief reset action to perform (@enum mrq_reset_commands) */
+	uint32_t cmd;
+	/** @brief id of the reset to affected */
+	uint32_t reset_id;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_I2C
+ * @brief issue an i2c transaction
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_i2c_request
+ * * Response Payload: @ref mrq_i2c_response
+ */
+
+/**
+ * @addtogroup I2C
+ * @{
+ */
+#define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE	(MSG_DATA_MIN_SZ - 12)
+#define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE	(MSG_DATA_MIN_SZ - 4)
+/** @} */
+
+/**
+ * @ingroup I2C
+ * @name Serial I2C flags
+ * Use these flags with serial_i2c_request::flags
+ * @{
+ */
+#define SERIALI2C_TEN           0x0010
+#define SERIALI2C_RD            0x0001
+#define SERIALI2C_STOP          0x8000
+#define SERIALI2C_NOSTART       0x4000
+#define SERIALI2C_REV_DIR_ADDR  0x2000
+#define SERIALI2C_IGNORE_NAK    0x1000
+#define SERIALI2C_NO_RD_ACK     0x0800
+#define SERIALI2C_RECV_LEN      0x0400
+/** @} */
+/** @ingroup I2C */
+enum {
+	CMD_I2C_XFER = 1
+};
+
+/**
+ * @ingroup I2C
+ * @brief serializable i2c request
+ *
+ * Instances of this structure are packed (little-endian) into
+ * cmd_i2c_xfer_request::data_buf. Each instance represents a single
+ * transaction (or a portion of a transaction with repeated starts) on
+ * an i2c bus.
+ *
+ * Because these structures are packed, some instances are likely to
+ * be misaligned. Additionally because #data is variable length, it is
+ * not possible to iterate through a serialized list of these
+ * structures without inspecting #len in each instance.  It may be
+ * easier to serialize or deserialize cmd_i2c_xfer_request::data_buf
+ * manually rather than using this structure definition.
+*/
+struct serial_i2c_request {
+	/** @brief I2C slave address */
+	uint16_t addr;
+	/** @brief bitmask of SERIALI2C_ flags */
+	uint16_t flags;
+	/** @brief length of I2C transaction in bytes */
+	uint16_t len;
+	/** @brief for write transactions only, #len bytes of data */
+	uint8_t data[];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief trigger one or more i2c transactions
+ */
+struct cmd_i2c_xfer_request {
+	/** @brief valid bus number from mach-t186/i2c-t186.h*/
+	uint32_t bus_id;
+
+	/** @brief count of valid bytes in #data_buf*/
+	uint32_t data_size;
+
+	/** @brief serialized packed instances of @ref serial_i2c_request*/
+	uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief container for data read from the i2c bus
+ *
+ * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute
+ * zero or more I2C reads. The data read from the bus is serialized
+ * into #data_buf.
+ */
+struct cmd_i2c_xfer_response {
+	/** @brief count of valid bytes in #data_buf*/
+	uint32_t data_size;
+	/** @brief i2c read data */
+	uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE];
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief request with #MRQ_I2C
+ */
+struct mrq_i2c_request {
+	/** @brief always CMD_I2C_XFER (i.e. 1) */
+	uint32_t cmd;
+	/** @brief parameters of the transfer request */
+	struct cmd_i2c_xfer_request xfer;
+} __ABI_PACKED;
+
+/**
+ * @ingroup I2C
+ * @brief response to #MRQ_I2C
+ */
+struct mrq_i2c_response {
+	struct cmd_i2c_xfer_response xfer;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_CLK
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_clk_request
+ * * Response Payload: @ref mrq_clk_response
+ * @addtogroup Clocks
+ * @{
+ */
+
+/**
+ * @name MRQ_CLK sub-commands
+ * @{
+ */
+enum {
+	CMD_CLK_GET_RATE = 1,
+	CMD_CLK_SET_RATE = 2,
+	CMD_CLK_ROUND_RATE = 3,
+	CMD_CLK_GET_PARENT = 4,
+	CMD_CLK_SET_PARENT = 5,
+	CMD_CLK_IS_ENABLED = 6,
+	CMD_CLK_ENABLE = 7,
+	CMD_CLK_DISABLE = 8,
+	CMD_CLK_GET_ALL_INFO = 14,
+	CMD_CLK_GET_MAX_CLK_ID = 15,
+	CMD_CLK_MAX,
+};
+/** @} */
+
+#define MRQ_CLK_NAME_MAXLEN	40
+#define MRQ_CLK_MAX_PARENTS	16
+
+/** @private */
+struct cmd_clk_get_rate_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_set_rate_request {
+	int32_t unused;
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_set_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_round_rate_request {
+	int32_t unused;
+	int64_t rate;
+} __ABI_PACKED;
+
+struct cmd_clk_round_rate_response {
+	int64_t rate;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_parent_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_parent_response {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+struct cmd_clk_set_parent_request {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+struct cmd_clk_set_parent_response {
+	uint32_t parent_id;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_is_enabled_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_is_enabled_response {
+	int32_t state;
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_enable_request {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_enable_response {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_disable_request {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_disable_response {
+	EMPTY
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_all_info_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_all_info_response {
+	uint32_t flags;
+	uint32_t parent;
+	uint32_t parents[MRQ_CLK_MAX_PARENTS];
+	uint8_t num_parents;
+	uint8_t name[MRQ_CLK_NAME_MAXLEN];
+} __ABI_PACKED;
+
+/** @private */
+struct cmd_clk_get_max_clk_id_request {
+	EMPTY
+} __ABI_PACKED;
+
+struct cmd_clk_get_max_clk_id_response {
+	uint32_t max_id;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup Clocks
+ * @brief request with #MRQ_CLK
+ *
+ * Used by the sender of an #MRQ_CLK message to control clocks. The
+ * clk_request is split into several sub-commands. Some sub-commands
+ * require no additional data. Others have a sub-command specific
+ * payload
+ *
+ * |sub-command                 |payload                |
+ * |----------------------------|-----------------------|
+ * |CMD_CLK_GET_RATE            |-                      |
+ * |CMD_CLK_SET_RATE            |clk_set_rate           |
+ * |CMD_CLK_ROUND_RATE          |clk_round_rate         |
+ * |CMD_CLK_GET_PARENT          |-                      |
+ * |CMD_CLK_SET_PARENT          |clk_set_parent         |
+ * |CMD_CLK_IS_ENABLED          |-                      |
+ * |CMD_CLK_ENABLE              |-                      |
+ * |CMD_CLK_DISABLE             |-                      |
+ * |CMD_CLK_GET_ALL_INFO        |-                      |
+ * |CMD_CLK_GET_MAX_CLK_ID      |-                      |
+ *
+ */
+
+struct mrq_clk_request {
+	/** @brief sub-command and clock id concatenated to 32-bit word.
+	 * - bits[31..24] is the sub-cmd.
+	 * - bits[23..0] is the clock id
+	 */
+	uint32_t cmd_and_id;
+
+	union {
+		/** @private */
+		struct cmd_clk_get_rate_request clk_get_rate;
+		struct cmd_clk_set_rate_request clk_set_rate;
+		struct cmd_clk_round_rate_request clk_round_rate;
+		/** @private */
+		struct cmd_clk_get_parent_request clk_get_parent;
+		struct cmd_clk_set_parent_request clk_set_parent;
+		/** @private */
+		struct cmd_clk_enable_request clk_enable;
+		/** @private */
+		struct cmd_clk_disable_request clk_disable;
+		/** @private */
+		struct cmd_clk_is_enabled_request clk_is_enabled;
+		/** @private */
+		struct cmd_clk_get_all_info_request clk_get_all_info;
+		/** @private */
+		struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup Clocks
+ * @brief response to MRQ_CLK
+ *
+ * Each sub-command supported by @ref mrq_clk_request may return
+ * sub-command-specific data. Some do and some do not as indicated in
+ * the following table
+ *
+ * |sub-command                 |payload                 |
+ * |----------------------------|------------------------|
+ * |CMD_CLK_GET_RATE            |clk_get_rate            |
+ * |CMD_CLK_SET_RATE            |clk_set_rate            |
+ * |CMD_CLK_ROUND_RATE          |clk_round_rate          |
+ * |CMD_CLK_GET_PARENT          |clk_get_parent          |
+ * |CMD_CLK_SET_PARENT          |clk_set_parent          |
+ * |CMD_CLK_IS_ENABLED          |clk_is_enabled          |
+ * |CMD_CLK_ENABLE              |-                       |
+ * |CMD_CLK_DISABLE             |-                       |
+ * |CMD_CLK_GET_ALL_INFO        |clk_get_all_info        |
+ * |CMD_CLK_GET_MAX_CLK_ID      |clk_get_max_id          |
+ *
+ */
+
+struct mrq_clk_response {
+	union {
+		struct cmd_clk_get_rate_response clk_get_rate;
+		struct cmd_clk_set_rate_response clk_set_rate;
+		struct cmd_clk_round_rate_response clk_round_rate;
+		struct cmd_clk_get_parent_response clk_get_parent;
+		struct cmd_clk_set_parent_response clk_set_parent;
+		/** @private */
+		struct cmd_clk_enable_response clk_enable;
+		/** @private */
+		struct cmd_clk_disable_response clk_disable;
+		struct cmd_clk_is_enabled_response clk_is_enabled;
+		struct cmd_clk_get_all_info_response clk_get_all_info;
+		struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_QUERY_ABI
+ * @brief check if an MRQ is implemented
+ *
+ * * Platforms: All
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: @ref mrq_query_abi_request
+ * * Response Payload: @ref mrq_query_abi_response
+ */
+
+/**
+ * @ingroup ABI_info
+ * @brief request with MRQ_QUERY_ABI
+ *
+ * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported
+ * by the recipient.
+ */
+struct mrq_query_abi_request {
+	/** @brief MRQ code to query */
+	uint32_t mrq;
+} __ABI_PACKED;
+
+/**
+ * @ingroup ABI_info
+ * @brief response to MRQ_QUERY_ABI
+ */
+struct mrq_query_abi_response {
+	/** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */
+	int32_t status;
+} __ABI_PACKED;
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PG_READ_STATE
+ * @brief read the power-gating state of a partition
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_pg_read_state_request
+ * * Response Payload: @ref mrq_pg_read_state_response
+ * @addtogroup Powergating
+ * @{
+ */
+
+/**
+ * @brief request with #MRQ_PG_READ_STATE
+ *
+ * Used by MRQ_PG_READ_STATE call to read the current state of a
+ * partition.
+ */
+struct mrq_pg_read_state_request {
+	/** @brief ID of partition */
+	uint32_t partition_id;
+} __ABI_PACKED;
+
+/**
+ * @brief response to MRQ_PG_READ_STATE
+ * @todo define possible errors.
+ */
+struct mrq_pg_read_state_response {
+	/** @brief read as don't care */
+	uint32_t sram_state;
+	/** @brief state of power partition
+	 * * 0 : off
+	 * * 1 : on
+	 */
+	uint32_t logic_state;
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_PG_UPDATE_STATE
+ * @brief modify the power-gating state of a partition
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_pg_update_state_request
+ * * Response Payload: N/A
+ * @addtogroup Powergating
+ * @{
+ */
+
+/**
+ * @brief request with mrq_pg_update_state_request
+ *
+ * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the
+ * state of a power partition #partition_id.
+ */
+struct mrq_pg_update_state_request {
+	/** @brief ID of partition */
+	uint32_t partition_id;
+	/** @brief secondary control of power partition
+	 *  @details Ignored by many versions of the BPMP
+	 *  firmware. For maximum compatibility, set the value
+	 *  according to @logic_state
+	 * *  0x1: power ON partition (@ref logic_state == 0x3)
+	 * *  0x3: power OFF partition (@ref logic_state == 0x1)
+	 */
+	uint32_t sram_state;
+	/** @brief controls state of power partition, legal values are
+	 * *  0x1 : power OFF partition
+	 * *  0x3 : power ON partition
+	 */
+	uint32_t logic_state;
+	/** @brief change state of clocks of the power partition, legal values
+	 * *  0x0 : do not change clock state
+	 * *  0x1 : disable partition clocks (only applicable when
+	 *          @ref logic_state == 0x1)
+	 * *  0x3 : enable partition clocks (only applicable when
+	 *          @ref logic_state == 0x3)
+	 */
+	uint32_t clock_state;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_THERMAL
+ * @brief interact with BPMP thermal framework
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: Any
+ * * Request Payload: TODO
+ * * Response Payload: TODO
+ *
+ * @addtogroup Thermal
+ *
+ * The BPMP firmware includes a thermal framework. Drivers within the
+ * bpmp firmware register with the framework to provide thermal
+ * zones. Each thermal zone corresponds to an entity whose temperature
+ * can be measured. The framework also has a notion of trip points. A
+ * trip point consists of a thermal zone id, a temperature, and a
+ * callback routine. The framework invokes the callback when the zone
+ * hits the indicated temperature. The BPMP firmware uses this thermal
+ * framework interally to implement various temperature-dependent
+ * functions.
+ *
+ * Software on the CPU can use #MRQ_THERMAL (with payload @ref
+ * mrq_thermal_host_to_bpmp_request) to interact with the BPMP thermal
+ * framework. The CPU must It can query the number of supported zones,
+ * query zone temperatures, and set trip points.
+ *
+ * When a trip point set by the CPU gets crossed, BPMP firmware issues
+ * an IPC to the CPU having mrq_request::mrq = #MRQ_THERMAL and a
+ * payload of @ref mrq_thermal_bpmp_to_host_request.
+ * @{
+ */
+enum mrq_thermal_host_to_bpmp_cmd {
+	/**
+	 * @brief Check whether the BPMP driver supports the specified
+	 * request type.
+	 *
+	 * Host needs to supply request parameters.
+	 *
+	 * mrq_response::err is 0 if the specified request is
+	 * supported and -#BPMP_ENODEV otherwise.
+	 */
+	CMD_THERMAL_QUERY_ABI = 0,
+
+	/**
+	 * @brief Get the current temperature of the specified zone.
+	 *
+	 * Host needs to supply request parameters.
+	 *
+	 * mrq_response::err is
+	 * *  0: Temperature query succeeded.
+	 * *  -#BPMP_EINVAL: Invalid request parameters.
+	 * *  -#BPMP_ENOENT: No driver registered for thermal zone..
+	 * *  -#BPMP_EFAULT: Problem reading temperature measurement.
+	 */
+	CMD_THERMAL_GET_TEMP = 1,
+
+	/**
+	 * @brief Enable or disable and set the lower and upper
+	 *   thermal limits for a thermal trip point. Each zone has
+	 *   one trip point.
+	 *
+	 * Host needs to supply request parameters. Once the
+	 * temperature hits a trip point, the BPMP will send a message
+	 * to the CPU having MRQ=MRQ_THERMAL and
+	 * type=CMD_THERMAL_HOST_TRIP_REACHED
+	 *
+	 * mrq_response::err is
+	 * *  0: Trip successfully set.
+	 * *  -#BPMP_EINVAL: Invalid request parameters.
+	 * *  -#BPMP_ENOENT: No driver registered for thermal zone.
+	 * *  -#BPMP_EFAULT: Problem setting trip point.
+	 */
+	CMD_THERMAL_SET_TRIP = 2,
+
+	/**
+	 * @brief Get the number of supported thermal zones.
+	 *
+	 * No request parameters required.
+	 *
+	 * mrq_response::err is always 0, indicating success.
+	 */
+	CMD_THERMAL_GET_NUM_ZONES = 3,
+
+	/** @brief: number of supported host-to-bpmp commands. May
+	 * increase in future
+	 */
+	CMD_THERMAL_HOST_TO_BPMP_NUM
+};
+
+enum mrq_thermal_bpmp_to_host_cmd {
+	/**
+	 * @brief Indication that the temperature for a zone has
+	 *   exceeded the range indicated in the thermal trip point
+	 *   for the zone.
+	 *
+	 * BPMP needs to supply request parameters. Host only needs to
+	 * acknowledge.
+	 */
+	CMD_THERMAL_HOST_TRIP_REACHED = 100,
+
+	/** @brief: number of supported bpmp-to-host commands. May
+	 * increase in future
+	 */
+	CMD_THERMAL_BPMP_TO_HOST_NUM
+};
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI
+ *
+ * zone: Request type for which to check existence.
+ */
+struct cmd_thermal_query_abi_request {
+	uint32_t type;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP
+ *
+ * zone: Number of thermal zone.
+ */
+struct cmd_thermal_get_temp_request {
+	uint32_t zone;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP
+ *
+ * error: 0 if request succeeded.
+ *	-BPMP_EINVAL if request parameters were invalid.
+ *      -BPMP_ENOENT if no driver was registered for the specified thermal zone.
+ *      -BPMP_EFAULT for other thermal zone driver errors.
+ * temp: Current temperature in millicelsius.
+ */
+struct cmd_thermal_get_temp_response {
+	int32_t temp;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP
+ *
+ * zone: Number of thermal zone.
+ * low: Temperature of lower trip point in millicelsius
+ * high: Temperature of upper trip point in millicelsius
+ * enabled: 1 to enable trip point, 0 to disable trip point
+ */
+struct cmd_thermal_set_trip_request {
+	uint32_t zone;
+	int32_t low;
+	int32_t high;
+	uint32_t enabled;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED
+ *
+ * zone: Number of thermal zone where trip point was reached.
+ */
+struct cmd_thermal_host_trip_reached_request {
+	uint32_t zone;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES
+ *
+ * num: Number of supported thermal zones. The thermal zones are indexed
+ *      starting from zero.
+ */
+struct cmd_thermal_get_num_zones_response {
+	uint32_t num;
+} __ABI_PACKED;
+
+/*
+ * Host->BPMP request data.
+ *
+ * Reply type is union mrq_thermal_bpmp_to_host_response.
+ *
+ * type: Type of request. Values listed in enum mrq_thermal_type.
+ * data: Request type specific parameters.
+ */
+struct mrq_thermal_host_to_bpmp_request {
+	uint32_t type;
+	union {
+		struct cmd_thermal_query_abi_request query_abi;
+		struct cmd_thermal_get_temp_request get_temp;
+		struct cmd_thermal_set_trip_request set_trip;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/*
+ * BPMP->Host request data.
+ *
+ * type: Type of request. Values listed in enum mrq_thermal_type.
+ * data: Request type specific parameters.
+ */
+struct mrq_thermal_bpmp_to_host_request {
+	uint32_t type;
+	union {
+		struct cmd_thermal_host_trip_reached_request host_trip_reached;
+	} __UNION_ANON;
+} __ABI_PACKED;
+
+/*
+ * Data in reply to a Host->BPMP request.
+ */
+union mrq_thermal_bpmp_to_host_response {
+	struct cmd_thermal_get_temp_response get_temp;
+	struct cmd_thermal_get_num_zones_response get_num_zones;
+} __ABI_PACKED;
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_CPU_VHINT
+ * @brief Query CPU voltage hint data
+ *
+ * * Platforms: T186
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_cpu_vhint_request
+ * * Response Payload: N/A
+ *
+ * @addtogroup Vhint CPU Voltage hint
+ * @{
+ */
+
+/**
+ * @brief request with #MRQ_CPU_VHINT
+ *
+ * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data
+ * from BPMP to memory space pointed by #addr. CCPLEX is responsible
+ * to allocate sizeof(cpu_vhint_data) sized block of memory and
+ * appropriately map it for BPMP before sending the request.
+ */
+struct mrq_cpu_vhint_request {
+	/** @brief IOVA address for the #cpu_vhint_data */
+	uint32_t addr; /* struct cpu_vhint_data * */
+	/** @brief ID of the cluster whose data is requested */
+	uint32_t cluster_id; /* enum cluster_id */
+} __ABI_PACKED;
+
+/**
+ * @brief description of the CPU v/f relation
+ *
+ * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of
+ * struct mrq_cpu_vhint_request
+ */
+struct cpu_vhint_data {
+	uint32_t ref_clk_hz; /**< reference frequency in Hz */
+	uint16_t pdiv; /**< post divider value */
+	uint16_t mdiv; /**< input divider value */
+	uint16_t ndiv_max; /**< fMAX expressed with max NDIV value */
+	/** table of ndiv values as a function of vINDEX (voltage index) */
+	uint16_t ndiv[80];
+	/** minimum allowed NDIV value */
+	uint16_t ndiv_min;
+	/** minimum allowed voltage hint value (as in vINDEX) */
+	uint16_t vfloor;
+	/** maximum allowed voltage hint value (as in vINDEX) */
+	uint16_t vceil;
+	/** post-multiplier for vindex value */
+	uint16_t vindex_mult;
+	/** post-divider for vindex value */
+	uint16_t vindex_div;
+	/** reserved for future use */
+	uint16_t reserved[328];
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_ABI_RATCHET
+ * @brief ABI ratchet value query
+ *
+ * * Platforms: T186
+ * * Initiators: Any
+ * * Targets: BPMP
+ * * Request Payload: @ref mrq_abi_ratchet_request
+ * * Response Payload: @ref mrq_abi_ratchet_response
+ * @addtogroup ABI_info
+ * @{
+ */
+
+/**
+ * @brief an ABI compatibility mechanism
+ *
+ * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future
+ * revision of this header file.
+ * 1. That future revision deprecates some MRQ
+ * 2. That future revision introduces a breaking change to an existing
+ *    MRQ or
+ * 3. A bug is discovered in an existing implementation of the BPMP-FW
+ *    (or possibly one of its clients) which warrants deprecating that
+ *    implementation.
+ */
+#define BPMP_ABI_RATCHET_VALUE 3
+
+/**
+ * @brief request with #MRQ_ABI_RATCHET.
+ *
+ * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header
+ * against which the requester was compiled.
+ *
+ * If ratchet is less than BPMP's #BPMP_ABI_RATCHET_VALUE, BPMP may
+ * reply with mrq_response::err = -#BPMP_ERANGE to indicate that
+ * BPMP-FW cannot interoperate correctly with the requester. Requester
+ * should cease further communication with BPMP.
+ *
+ * Otherwise, err shall be 0.
+ */
+struct mrq_abi_ratchet_request {
+	/** @brief requester's ratchet value */
+	uint16_t ratchet;
+};
+
+/**
+ * @brief response to #MRQ_ABI_RATCHET
+ *
+ * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header
+ * against which BPMP firwmare was compiled.
+ *
+ * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE,
+ * the requster must either interoperate with BPMP according to an ABI
+ * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease
+ * communication with BPMP.
+ *
+ * If mrq_response::err is 0 and ratchet is greater than or equal to the
+ * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue
+ * normal operation.
+ */
+struct mrq_abi_ratchet_response {
+	/** @brief BPMP's ratchet value */
+	uint16_t ratchet;
+};
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_EMC_DVFS_LATENCY
+ * @brief query frequency dependent EMC DVFS latency
+ *
+ * * Platforms: T186
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: N/A
+ * * Response Payload: @ref mrq_emc_dvfs_latency_response
+ * @addtogroup EMC
+ * @{
+ */
+
+/**
+ * @brief used by @ref mrq_emc_dvfs_latency_response
+ */
+struct emc_dvfs_latency {
+	/** @brief EMC frequency in kHz */
+	uint32_t freq;
+	/** @brief EMC DVFS latency in nanoseconds */
+	uint32_t latency;
+} __ABI_PACKED;
+
+#define EMC_DVFS_LATENCY_MAX_SIZE	14
+/**
+ * @brief response to #MRQ_EMC_DVFS_LATENCY
+ */
+struct mrq_emc_dvfs_latency_response {
+	/** @brief the number valid entries in #pairs */
+	uint32_t num_pairs;
+	/** @brief EMC <frequency, latency> information */
+	struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE];
+} __ABI_PACKED;
+
+/** @} */
+
+/**
+ * @ingroup MRQ_Codes
+ * @def MRQ_TRACE_ITER
+ * @brief manage the trace iterator
+ *
+ * * Platforms: All
+ * * Initiators: CCPLEX
+ * * Targets: BPMP
+ * * Request Payload: N/A
+ * * Response Payload: @ref mrq_trace_iter_request
+ * @addtogroup Trace
+ * @{
+ */
+enum {
+	/** @brief (re)start the tracing now. Ignore older events */
+	TRACE_ITER_INIT = 0,
+	/** @brief clobber all events in the trace buffer */
+	TRACE_ITER_CLEAN = 1
+};
+
+/**
+ * @brief request with #MRQ_TRACE_ITER
+ */
+struct mrq_trace_iter_request {
+	/** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */
+	uint32_t cmd;
+} __ABI_PACKED;
+
+/** @} */
+
+/*
+ *  4. Enumerations
+ */
+
+/*
+ *   4.1 CPU enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ *
+ *   4.2 CPU Cluster enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ *
+ *   4.3 System low power state enumerations
+ *
+ * See <mach-t186/system-t186.h>
+ */
+
+/*
+ *   4.4 Clock enumerations
+ *
+ * For clock enumerations, see <mach-t186/clk-t186.h>
+ */
+
+/*
+ *   4.5 Reset enumerations
+ *
+ * For reset enumerations, see <mach-t186/reset-t186.h>
+ */
+
+/*
+ *   4.6 Thermal sensor enumerations
+ *
+ * For thermal sensor enumerations, see <mach-t186/thermal-t186.h>
+ */
+
+/**
+ * @defgroup Error_Codes
+ * Negative values for mrq_response::err generally indicate some
+ * error. The ABI defines the following error codes. Negating these
+ * defines is an exercise left to the user.
+ * @{
+ */
+/** @brief No such file or directory */
+#define BPMP_ENOENT	2
+/** @brief No MRQ handler */
+#define BPMP_ENOHANDLER	3
+/** @brief I/O error */
+#define BPMP_EIO	5
+/** @brief Bad sub-MRQ command */
+#define BPMP_EBADCMD	6
+/** @brief Not enough memory */
+#define BPMP_ENOMEM	12
+/** @brief Permission denied */
+#define BPMP_EACCES	13
+/** @brief Bad address */
+#define BPMP_EFAULT	14
+/** @brief No such device */
+#define BPMP_ENODEV	19
+/** @brief Argument is a directory */
+#define BPMP_EISDIR	21
+/** @brief Invalid argument */
+#define BPMP_EINVAL	22
+/** @brief Timeout during operation */
+#define BPMP_ETIMEDOUT  23
+/** @brief Out of range */
+#define BPMP_ERANGE	34
+/** @} */
+/** @} */
+#endif
diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
new file mode 100644
index 000000000000..e17d091e8a9a
--- /dev/null
+++ b/include/soc/tegra/bpmp.h
@@ -0,0 +1,122 @@
+/*
+ * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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.
+ */
+
+#ifndef __SOC_TEGRA_BPMP_H
+#define __SOC_TEGRA_BPMP_H
+
+#include <linux/mailbox_client.h>
+#include <linux/reset-controller.h>
+#include <linux/semaphore.h>
+#include <linux/types.h>
+
+#define TEGRA_BPMP_MSG_SIZE 128
+#define TEGRA_BPMP_MSG_DATA_SIZE 120
+
+struct tegra_bpmp_soc {
+	struct {
+		struct {
+			unsigned int offset;
+			unsigned int count;
+			unsigned int timeout;
+		} cpu_tx, thread, cpu_rx;
+	} channels;
+	unsigned int num_resets;
+};
+
+struct tegra_bpmp_mb_data {
+	u32 code;
+	u32 flags;
+	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
+} __packed;
+
+struct tegra_bpmp_channel {
+	struct tegra_bpmp *bpmp;
+	struct tegra_bpmp_mb_data *ib;
+	struct tegra_bpmp_mb_data *ob;
+	struct completion completion;
+	struct tegra_ivc *ivc;
+};
+
+typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq,
+					 struct tegra_bpmp_channel *channel,
+					 void *data);
+
+struct tegra_bpmp_mrq {
+	struct list_head list;
+	unsigned int mrq;
+	tegra_bpmp_mrq_handler_t handler;
+	void *data;
+};
+
+struct tegra_bpmp {
+	const struct tegra_bpmp_soc *soc;
+	struct device *dev;
+
+	void __iomem *tx_base;
+	void __iomem *rx_base;
+
+	struct {
+		struct mbox_client client;
+		struct mbox_chan *channel;
+	} mbox;
+
+	struct tegra_bpmp_channel *channels;
+	unsigned int num_channels;
+
+	struct {
+		unsigned long *allocated;
+		unsigned long *busy;
+		unsigned int count;
+		struct semaphore lock;
+	} threaded;
+
+	struct list_head mrqs;
+	spinlock_t lock;
+
+	unsigned int num_clocks;
+	struct clk_hw **clocks;
+
+	struct reset_controller_dev rstc;
+};
+
+struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
+void tegra_bpmp_put(struct tegra_bpmp *bpmp);
+
+struct tegra_bpmp_message {
+	unsigned int mrq;
+
+	struct {
+		const void *data;
+		size_t size;
+	} tx;
+
+	struct {
+		void *data;
+		size_t size;
+	} rx;
+};
+
+int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
+			       struct tegra_bpmp_message *msg);
+int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
+			struct tegra_bpmp_message *msg);
+
+int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			   tegra_bpmp_mrq_handler_t handler, void *data);
+void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
+			 void *data);
+
+int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
+int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);
+
+#endif /* __SOC_TEGRA_BPMP_H */
-- 
2.9.0

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

* [PATCH v3 06/12] soc/tegra: Add Tegra186 support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
GPUs with Pascal architecture on it. It features with ADSP with
Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
multi-format support, ISP for image capture processing and BPMP for the
power managements.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/soc/tegra/Kconfig | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index 03089ad2fc65..88a71dfd466c 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -61,6 +61,20 @@ config ARCH_TEGRA_132_SOC
 	  but contains an NVIDIA Denver CPU complex in place of
 	  Tegra124's "4+1" Cortex-A15 CPU complex.
 
+config ARCH_TEGRA_186_SOC
+	bool "NVIDIA Tegra186 SoC"
+	select MAILBOX
+	select TEGRA_BPMP
+	select TEGRA_HSP_MBOX
+	select TEGRA_IVC
+	help
+	  Enable support for the NVIDIA Tegar186 SoC. The Tegra186 has a
+	  combination of Denver and Cortex-A57 CPU cores and GPUs with Pascal
+	  architecture on it. It features with ADSP with Cortex-A9 CPU for
+	  audio processing, hardware video encoder/decoder with multi-format
+	  support, ISP for image capture processing and BPMP for the power
+	  managements.
+
 config ARCH_TEGRA_210_SOC
 	bool "NVIDIA Tegra210 SoC"
 	select PINCTRL_TEGRA210
-- 
2.9.0

--
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] 104+ messages in thread

* [PATCH v3 06/12] soc/tegra: Add Tegra186 support
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
GPUs with Pascal architecture on it. It features with ADSP with
Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
multi-format support, ISP for image capture processing and BPMP for the
power managements.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/soc/tegra/Kconfig | 14 ++++++++++++++
 1 file changed, 14 insertions(+)

diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
index 03089ad2fc65..88a71dfd466c 100644
--- a/drivers/soc/tegra/Kconfig
+++ b/drivers/soc/tegra/Kconfig
@@ -61,6 +61,20 @@ config ARCH_TEGRA_132_SOC
 	  but contains an NVIDIA Denver CPU complex in place of
 	  Tegra124's "4+1" Cortex-A15 CPU complex.
 
+config ARCH_TEGRA_186_SOC
+	bool "NVIDIA Tegra186 SoC"
+	select MAILBOX
+	select TEGRA_BPMP
+	select TEGRA_HSP_MBOX
+	select TEGRA_IVC
+	help
+	  Enable support for the NVIDIA Tegar186 SoC. The Tegra186 has a
+	  combination of Denver and Cortex-A57 CPU cores and GPUs with Pascal
+	  architecture on it. It features with ADSP with Cortex-A9 CPU for
+	  audio processing, hardware video encoder/decoder with multi-format
+	  support, ISP for image capture processing and BPMP for the power
+	  managements.
+
 config ARCH_TEGRA_210_SOC
 	bool "NVIDIA Tegra210 SoC"
 	select PINCTRL_TEGRA210
-- 
2.9.0

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

* [PATCH v3 07/12] arm64: defconfig: Enable Tegra186 SoC
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Enable Tegra186 SoC.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 76bfaa7eee57..ba67b4e67608 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -375,6 +375,7 @@ CONFIG_QCOM_SMEM=y
 CONFIG_QCOM_SMD=y
 CONFIG_QCOM_SMD_RPM=y
 CONFIG_ARCH_TEGRA_132_SOC=y
+CONFIG_ARCH_TEGRA_186_SOC=y
 CONFIG_ARCH_TEGRA_210_SOC=y
 CONFIG_EXTCON_USB_GPIO=y
 CONFIG_PWM=y
-- 
2.9.0

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

* [PATCH v3 07/12] arm64: defconfig: Enable Tegra186 SoC
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

Enable Tegra186 SoC.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 arch/arm64/configs/defconfig | 1 +
 1 file changed, 1 insertion(+)

diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig
index 76bfaa7eee57..ba67b4e67608 100644
--- a/arch/arm64/configs/defconfig
+++ b/arch/arm64/configs/defconfig
@@ -375,6 +375,7 @@ CONFIG_QCOM_SMEM=y
 CONFIG_QCOM_SMD=y
 CONFIG_QCOM_SMD_RPM=y
 CONFIG_ARCH_TEGRA_132_SOC=y
+CONFIG_ARCH_TEGRA_186_SOC=y
 CONFIG_ARCH_TEGRA_210_SOC=y
 CONFIG_EXTCON_USB_GPIO=y
 CONFIG_PWM=y
-- 
2.9.0

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

* [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

This adds the initial support of Tegra186 SoC. It provides enough to
enable the serial console and boot from an initial ramdisk.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 86 ++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186.dtsi

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
new file mode 100644
index 000000000000..53b89b1c1e1f
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -0,0 +1,86 @@
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/mailbox/tegra186-hsp.h>
+
+/ {
+	compatible = "nvidia,tegra186";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	uarta: serial@03100000 {
+		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
+		reg = <0x0 0x03100000 0x0 0x40>;
+		reg-shift = <2>;
+		interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	gic: interrupt-controller@03881000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0x0 0x03881000 0x0 0x1000>,
+		      <0x0 0x03882000 0x0 0x2000>;
+		interrupts = <GIC_PPI 9
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+		interrupt-parent = <&gic>;
+	};
+
+	hsp_top0: hsp@03c00000 {
+		compatible = "nvidia,tegra186-hsp";
+		reg = <0x0 0x03c00000 0x0 0xa0000>;
+		interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "doorbell";
+		#mbox-cells = <2>;
+		status = "disabled";
+	};
+
+	sysram@30000000 {
+		compatible = "nvidia,tegra186-sysram", "mmio-ram";
+		reg = <0x0 0x30000000 0x0 0x4ffff>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges = <0 0x0 0x0 0x30000000 0x0 0x4ffff>;
+
+		cpu_bpmp_tx: bpmp_shmem@4e000 {
+			compatible = "nvidia,tegra186-bpmp-shmem";
+			reg = <0x0 0x4e000 0x0 0x1000>;
+		};
+
+		cpu_bpmp_rx: bpmp_shmem@4f000 {
+			compatible = "nvidia,tegra186-bpmp-shmem";
+			reg = <0x0 0x4f000 0x0 0x1000>;
+		};
+	};
+
+	bpmp: bpmp {
+		compatible = "nvidia,tegra186-bpmp";
+		mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB
+				    TEGRA_HSP_DB_MASTER_BPMP>;
+		shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+
+		bpmp_i2c: i2c {
+			compatible = "nvidia,tegra186-bpmp-i2c";
+			nvidia,bpmp = <&bpmp>;
+			nvidia,bpmp-bus-id = <5>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
+		interrupt-parent = <&gic>;
+	};
+};
-- 
2.9.0

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

* [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

This adds the initial support of Tegra186 SoC. It provides enough to
enable the serial console and boot from an initial ramdisk.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/tegra186.dtsi | 86 ++++++++++++++++++++++++++++++++
 1 file changed, 86 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186.dtsi

diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
new file mode 100644
index 000000000000..53b89b1c1e1f
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186.dtsi
@@ -0,0 +1,86 @@
+#include <dt-bindings/interrupt-controller/arm-gic.h>
+#include <dt-bindings/mailbox/tegra186-hsp.h>
+
+/ {
+	compatible = "nvidia,tegra186";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <2>;
+
+	uarta: serial at 03100000 {
+		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
+		reg = <0x0 0x03100000 0x0 0x40>;
+		reg-shift = <2>;
+		interrupts = <GIC_SPI 112 IRQ_TYPE_LEVEL_HIGH>;
+		status = "disabled";
+	};
+
+	gic: interrupt-controller at 03881000 {
+		compatible = "arm,gic-400";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0x0 0x03881000 0x0 0x1000>,
+		      <0x0 0x03882000 0x0 0x2000>;
+		interrupts = <GIC_PPI 9
+			(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_HIGH)>;
+		interrupt-parent = <&gic>;
+	};
+
+	hsp_top0: hsp at 03c00000 {
+		compatible = "nvidia,tegra186-hsp";
+		reg = <0x0 0x03c00000 0x0 0xa0000>;
+		interrupts = <GIC_SPI 176 IRQ_TYPE_LEVEL_HIGH>;
+		interrupt-names = "doorbell";
+		#mbox-cells = <2>;
+		status = "disabled";
+	};
+
+	sysram at 30000000 {
+		compatible = "nvidia,tegra186-sysram", "mmio-ram";
+		reg = <0x0 0x30000000 0x0 0x4ffff>;
+		#address-cells = <2>;
+		#size-cells = <2>;
+		ranges = <0 0x0 0x0 0x30000000 0x0 0x4ffff>;
+
+		cpu_bpmp_tx: bpmp_shmem at 4e000 {
+			compatible = "nvidia,tegra186-bpmp-shmem";
+			reg = <0x0 0x4e000 0x0 0x1000>;
+		};
+
+		cpu_bpmp_rx: bpmp_shmem at 4f000 {
+			compatible = "nvidia,tegra186-bpmp-shmem";
+			reg = <0x0 0x4f000 0x0 0x1000>;
+		};
+	};
+
+	bpmp: bpmp {
+		compatible = "nvidia,tegra186-bpmp";
+		mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB
+				    TEGRA_HSP_DB_MASTER_BPMP>;
+		shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
+		#clock-cells = <1>;
+		#reset-cells = <1>;
+
+		bpmp_i2c: i2c {
+			compatible = "nvidia,tegra186-bpmp-i2c";
+			nvidia,bpmp = <&bpmp>;
+			nvidia,bpmp-bus-id = <5>;
+			#address-cells = <1>;
+			#size-cells = <0>;
+			status = "disabled";
+		};
+	};
+
+	timer {
+		compatible = "arm,armv8-timer";
+		interrupts = <GIC_PPI 13
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 14
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 11
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>,
+			     <GIC_PPI 10
+				(GIC_CPU_MASK_SIMPLE(4) | IRQ_TYPE_LEVEL_LOW)>;
+		interrupt-parent = <&gic>;
+	};
+};
-- 
2.9.0

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

* [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

The NVIDIA P3310 is a processor module used in several reference designs
that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
essentials such as ethernet, WiFi and a PMIC. It is typically connected
to an I/O board (such as the P2597) that provides the connecters needed
to hook it up to the outside world.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
Changes in v3:
- fix compatible string

 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi | 34 ++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi

diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
new file mode 100644
index 000000000000..dc07ea618378
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
@@ -0,0 +1,34 @@
+#include "tegra186.dtsi"
+
+/ {
+	model = "NVIDIA Tegra186 P3310 main Board";
+	compatible = "nvidia,p3310", "nvidia,tegra186";
+
+	aliases {
+		serial0 = &uarta;
+	};
+
+	chosen {
+		bootargs = "earlycon console=ttyS0,115200n8";
+		stdout-path = "serial0:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x80000000 0x2 0x00000000>;
+	};
+
+	serial@03100000 {
+		// HACK: before clk driver ready
+		clock-frequency = <408000000>;
+		status = "okay";
+	};
+
+	hsp@03c00000 {
+		status = "okay";
+	};
+
+	bpmp {
+		status = "okay";
+	};
+};
-- 
2.9.0

--
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] 104+ messages in thread

* [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

The NVIDIA P3310 is a processor module used in several reference designs
that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
essentials such as ethernet, WiFi and a PMIC. It is typically connected
to an I/O board (such as the P2597) that provides the connecters needed
to hook it up to the outside world.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
Changes in v3:
- fix compatible string

 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi | 34 ++++++++++++++++++++++++++
 1 file changed, 34 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi

diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
new file mode 100644
index 000000000000..dc07ea618378
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
@@ -0,0 +1,34 @@
+#include "tegra186.dtsi"
+
+/ {
+	model = "NVIDIA Tegra186 P3310 main Board";
+	compatible = "nvidia,p3310", "nvidia,tegra186";
+
+	aliases {
+		serial0 = &uarta;
+	};
+
+	chosen {
+		bootargs = "earlycon console=ttyS0,115200n8";
+		stdout-path = "serial0:115200n8";
+	};
+
+	memory {
+		device_type = "memory";
+		reg = <0x0 0x80000000 0x2 0x00000000>;
+	};
+
+	serial at 03100000 {
+		// HACK: before clk driver ready
+		clock-frequency = <408000000>;
+		status = "okay";
+	};
+
+	hsp at 03c00000 {
+		status = "okay";
+	};
+
+	bpmp {
+		status = "okay";
+	};
+};
-- 
2.9.0

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

* [PATCH v3 10/12] arm64: dts: tegra: Add NVIDIA P2771 board support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

Add NVIDIA P2771 is composed of a P3310 processor module that connects
to the P2597 I/O board. It comes with a 1200x1920 MIPI DSI panel
connected via the P2597's display connector and has several connectors
such as HDMI, USB 3.0, PCIe and ethernet.

Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 arch/arm64/boot/dts/nvidia/Makefile                | 1 +
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 8 ++++++++
 2 files changed, 9 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts

diff --git a/arch/arm64/boot/dts/nvidia/Makefile b/arch/arm64/boot/dts/nvidia/Makefile
index 0f7cdf3e05c1..67234f3dc795 100644
--- a/arch/arm64/boot/dts/nvidia/Makefile
+++ b/arch/arm64/boot/dts/nvidia/Makefile
@@ -1,4 +1,5 @@
 dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb
+dtb-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-p2771-0000.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-2180.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2571.dtb
diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
new file mode 100644
index 000000000000..66b936389fa7
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+#include "tegra186-p3310.dtsi"
+
+/ {
+	model = "NVIDIA Tegra186 P2771-0000 Board";
+	compatible = "nvidia,p2771-0000", "nvidia,tegra186";
+};
-- 
2.9.0

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

* [PATCH v3 10/12] arm64: dts: tegra: Add NVIDIA P2771 board support
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Joseph Lo <josephl@nvidia.com>

Add NVIDIA P2771 is composed of a P3310 processor module that connects
to the P2597 I/O board. It comes with a 1200x1920 MIPI DSI panel
connected via the P2597's display connector and has several connectors
such as HDMI, USB 3.0, PCIe and ethernet.

Signed-off-by: Joseph Lo <josephl@nvidia.com>
Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 arch/arm64/boot/dts/nvidia/Makefile                | 1 +
 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 8 ++++++++
 2 files changed, 9 insertions(+)
 create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts

diff --git a/arch/arm64/boot/dts/nvidia/Makefile b/arch/arm64/boot/dts/nvidia/Makefile
index 0f7cdf3e05c1..67234f3dc795 100644
--- a/arch/arm64/boot/dts/nvidia/Makefile
+++ b/arch/arm64/boot/dts/nvidia/Makefile
@@ -1,4 +1,5 @@
 dtb-$(CONFIG_ARCH_TEGRA_132_SOC) += tegra132-norrin.dtb
+dtb-$(CONFIG_ARCH_TEGRA_186_SOC) += tegra186-p2771-0000.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-0000.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2371-2180.dtb
 dtb-$(CONFIG_ARCH_TEGRA_210_SOC) += tegra210-p2571.dtb
diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
new file mode 100644
index 000000000000..66b936389fa7
--- /dev/null
+++ b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
@@ -0,0 +1,8 @@
+/dts-v1/;
+
+#include "tegra186-p3310.dtsi"
+
+/ {
+	model = "NVIDIA Tegra186 P2771-0000 Board";
+	compatible = "nvidia,p2771-0000", "nvidia,tegra186";
+};
-- 
2.9.0

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

This driver uses the services provided by the BPMP firmware driver to
implement a clock driver based on the MRQ_CLK request. This part of the
BPMP ABI provides a means to enumerate and control clocks and should
allow the driver to work on any chip that supports this ABI.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/clk/tegra/Makefile   |   1 +
 drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 566 insertions(+)
 create mode 100644 drivers/clk/tegra/clk-bpmp.c

diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 33fd0938d79e..130df5685d21 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
 obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
 obj-y					+= cvb.o
 obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
+obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
new file mode 100644
index 000000000000..96cd6becf73e
--- /dev/null
+++ b/drivers/clk/tegra/clk-bpmp.c
@@ -0,0 +1,565 @@
+#define DEBUG
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/seq_buf.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
+#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
+#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)
+
+struct tegra_bpmp_clk_info {
+	unsigned int id;
+	char name[MRQ_CLK_NAME_MAXLEN];
+	unsigned int parents[MRQ_CLK_MAX_PARENTS];
+	unsigned int num_parents;
+	unsigned long flags;
+};
+
+struct tegra_bpmp_clk {
+	struct clk_hw hw;
+
+	struct tegra_bpmp *bpmp;
+	unsigned int id;
+
+	unsigned int num_parents;
+	unsigned int *parents;
+};
+
+static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct tegra_bpmp_clk, hw);
+}
+
+struct tegra_bpmp_clk_message {
+	unsigned int cmd;
+	unsigned int clk;
+
+	struct {
+		const void *data;
+		size_t size;
+	} tx;
+
+	struct {
+		void *data;
+		size_t size;
+	} rx;
+};
+
+static int
+__tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
+				 struct tegra_bpmp_message *msg)
+{
+	unsigned long flags;
+	int err;
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, msg);
+	local_irq_restore(flags);
+
+	return err;
+}
+
+static int
+tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
+			       const struct tegra_bpmp_clk_message *clk)
+{
+	struct mrq_clk_request request;
+	struct tegra_bpmp_message msg;
+	void *req = (void *)&request;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
+	memcpy(req + 4, clk->tx.data, clk->tx.size);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_CLK;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = clk->rx.data;
+	msg.rx.size = clk->rx.size;
+
+	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);;
+}
+
+static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
+				   const struct tegra_bpmp_clk_message *clk)
+{
+	struct mrq_clk_request request;
+	struct tegra_bpmp_message msg;
+	void *req = (void *)&request;
+	int err;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
+	memcpy(req + 4, clk->tx.data, clk->tx.size);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_CLK;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = clk->rx.data;
+	msg.rx.size = clk->rx.size;
+
+	err = tegra_bpmp_transfer(bpmp, &msg);
+	if (err != -EAGAIN)
+		return err;
+
+	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
+}
+
+static int tegra_bpmp_clk_enable(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	dev_dbg(clk->bpmp->dev, "> %s(hw=%p)\n", __func__, hw);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_ENABLE;
+	msg.clk = clk->id;
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+
+	dev_dbg(clk->bpmp->dev, "< %s() = %d\n", __func__, err);
+	return err;
+}
+
+static void tegra_bpmp_clk_disable(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_DISABLE;
+	msg.clk = clk->id;
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",
+			clk_hw_get_name(hw), err);
+}
+
+static int tegra_bpmp_clk_is_enabled(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_is_enabled_response response;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_IS_ENABLED;
+	msg.clk = clk->id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer_atomic(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	return response.state;
+}
+
+static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_get_parent_response response;
+	struct tegra_bpmp_clk_message msg;
+	unsigned int i;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_PARENT;
+	msg.clk = clk->id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(&response);
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < clk->num_parents; i++)
+		if (clk->parents[i] == response.parent_id)
+			return i;
+
+	return U8_MAX;
+}
+
+static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_set_parent_response response;
+	struct cmd_clk_set_parent_request request;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	if (index >= clk->num_parents)
+		return -EINVAL;
+
+	memset(&request, 0, sizeof(request));
+	request.parent_id = clk->parents[index];
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_SET_PARENT;
+	msg.clk = clk->id;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	/* XXX check parent ID in response */
+
+	return 0;
+}
+
+static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	return 0;
+}
+
+static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *parent_rate)
+{
+	return 0;
+}
+
+static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	return 0;
+}
+
+static const struct clk_ops tegra_bpmp_clk_gate_ops = {
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_ops = {
+	.get_parent = tegra_bpmp_clk_get_parent,
+	.set_parent = tegra_bpmp_clk_set_parent,
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+};
+
+static const struct clk_ops tegra_bpmp_clk_rate_ops = {
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+	.set_rate = tegra_bpmp_clk_set_rate,
+	.round_rate = tegra_bpmp_clk_round_rate,
+	.recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
+	.get_parent = tegra_bpmp_clk_get_parent,
+	.set_parent = tegra_bpmp_clk_set_parent,
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+	.set_rate = tegra_bpmp_clk_set_rate,
+	.round_rate = tegra_bpmp_clk_round_rate,
+	.recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
+{
+	struct cmd_clk_get_max_clk_id_response response;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	return response.max_id;
+}
+
+static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
+				   struct tegra_bpmp_clk_info *info)
+{
+	struct cmd_clk_get_all_info_response response;
+	struct tegra_bpmp_clk_message msg;
+	unsigned int i;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_ALL_INFO;
+	msg.clk = id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
+	info->num_parents = response.num_parents;
+
+	for (i = 0; i < info->num_parents; i++)
+		info->parents[i] = response.parents[i];
+
+	info->flags = response.flags;
+
+	return 0;
+}
+
+static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
+				   struct tegra_bpmp_clk_info **clocksp)
+{
+	struct tegra_bpmp_clk_info *clocks;
+	unsigned int max_id, id, count = 0;
+	int err;
+
+	err = tegra_bpmp_clk_get_max_id(bpmp);
+	if (err < 0)
+		return err;
+
+	max_id = err;
+
+	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
+
+	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
+	if (!clocks)
+		return -ENOMEM;
+
+	for (id = 0; id <= max_id; id++) {
+		struct tegra_bpmp_clk_info *info = &clocks[count];
+#if 0
+		const char *prefix = "";
+		struct seq_buf buf;
+		unsigned int i;
+		char flags[64];
+#endif
+
+		err = tegra_bpmp_clk_get_info(bpmp, id, info);
+		if (err < 0) {
+			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
+				id, err);
+			continue;
+		}
+
+#if 0
+		seq_buf_init(&buf, flags, sizeof(flags));
+
+		if (info->flags)
+			seq_buf_printf(&buf, "(");
+
+		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+			seq_buf_printf(&buf, "%smux", prefix);
+			prefix = ", ";
+		}
+
+		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
+			seq_buf_printf(&buf, "%sfixed", prefix);
+			prefix = ", ";
+		}
+
+		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
+			seq_buf_printf(&buf, "%sroot", prefix);
+			prefix = ", ";
+		}
+
+		if (info->flags)
+			seq_buf_printf(&buf, ")");
+
+		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
+		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
+		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
+
+		for (i = 0; i < info->num_parents; i++)
+			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
+#endif
+
+		/* clock not exposed by BPMP */
+		if (info->name[0] == '\0')
+			continue;
+
+		info->id = id;
+		count++;
+	}
+
+	*clocksp = clocks;
+
+	return count;
+}
+
+static struct clk_hw *
+tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
+			const struct tegra_bpmp_clk_info *info,
+			const struct tegra_bpmp_clk_info *clocks,
+			unsigned int num_clocks)
+{
+	struct tegra_bpmp_clk *priv;
+	struct clk_init_data init;
+	const char **parents;
+	unsigned int i, j;
+	struct clk *clk;
+
+	dev_dbg(bpmp->dev, "registering clock %u (%s)\n", info->id, info->name);
+
+	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	priv->bpmp = bpmp;
+	priv->id = info->id;
+
+	priv->parents = devm_kcalloc(bpmp->dev, info->num_parents,
+				     sizeof(*priv->parents), GFP_KERNEL);
+	if (!priv->parents)
+		return ERR_PTR(-ENOMEM);
+
+	priv->num_parents = info->num_parents;
+
+	/* hardware clock initialization */
+	priv->hw.init = &init;
+	init.name = info->name;
+
+	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+			init.ops = &tegra_bpmp_clk_mux_rate_ops;
+		else
+			init.ops = &tegra_bpmp_clk_mux_ops;
+	} else {
+		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+			init.ops = &tegra_bpmp_clk_rate_ops;
+		else
+			init.ops = &tegra_bpmp_clk_gate_ops;
+	}
+
+	init.num_parents = info->num_parents;
+
+	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
+	if (!parents)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < info->num_parents; i++) {
+		/* keep a private copy of the ID to parent index map */
+		priv->parents[i] = info->parents[i];
+
+		for (j = 0; j < num_clocks; j++) {
+			const struct tegra_bpmp_clk_info *parent = &clocks[j];
+
+			if (parent->id == info->parents[i]) {
+				parents[i] = parent->name;
+				break;
+			}
+		}
+
+		if (!parents[i])
+			dev_err(bpmp->dev, "no parent %u found for %u\n",
+				info->parents[i], info->id);
+	}
+
+	init.parent_names = parents;
+
+	clk = clk_register(bpmp->dev, &priv->hw);
+
+	kfree(parents);
+
+	return &priv->hw;
+}
+
+static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
+				      struct tegra_bpmp_clk_info *clocks,
+				      unsigned int count)
+{
+	struct clk_hw *hw;
+	unsigned int i;
+
+	bpmp->num_clocks = count;
+
+	bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(hw), GFP_KERNEL);
+	if (!bpmp->clocks)
+		return -ENOMEM;
+
+	for (i = 0; i < count; i++) {
+		struct tegra_bpmp_clk_info *info = &clocks[i];
+
+		hw = tegra_bpmp_clk_register(bpmp, info, clocks, count);
+		if (IS_ERR(hw)) {
+			dev_err(bpmp->dev,
+				"failed to register clock %u (%s): %ld\n",
+				info->id, info->name, PTR_ERR(hw));
+			continue;
+		}
+
+		bpmp->clocks[i] = hw;
+	}
+
+	return 0;
+}
+
+static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
+					      void *data)
+{
+	unsigned int id = clkspec->args[0], i;
+	struct tegra_bpmp *bpmp = data;
+	struct clk_hw *hw = NULL;
+
+	dev_dbg(bpmp->dev, "> %s(clkspec=%p, data=%p)\n", __func__, clkspec,
+		data);
+
+	for (i = 0; i < bpmp->num_clocks; i++) {
+		struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(bpmp->clocks[i]);
+
+		if (clk->id == id) {
+			hw = bpmp->clocks[i];
+			dev_dbg(bpmp->dev, "  found %u: %s\n", clk->id, clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	dev_dbg(bpmp->dev, "< %s() = %p\n", __func__, hw);
+
+	return hw;
+}
+
+int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
+{
+	struct tegra_bpmp_clk_info *clocks;
+	unsigned int count;
+	int err;
+
+	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
+
+	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
+	if (err < 0)
+		return err;
+
+	count = err;
+
+	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
+
+	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
+	if (err < 0) {
+		kfree(clocks);
+		return err;
+	}
+
+	kfree(clocks);
+
+	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
+			       bpmp);
+
+	dev_dbg(bpmp->dev, "< %s()\n", __func__);
+	return 0;
+}
-- 
2.9.0

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

This driver uses the services provided by the BPMP firmware driver to
implement a clock driver based on the MRQ_CLK request. This part of the
BPMP ABI provides a means to enumerate and control clocks and should
allow the driver to work on any chip that supports this ABI.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/clk/tegra/Makefile   |   1 +
 drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 566 insertions(+)
 create mode 100644 drivers/clk/tegra/clk-bpmp.c

diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
index 33fd0938d79e..130df5685d21 100644
--- a/drivers/clk/tegra/Makefile
+++ b/drivers/clk/tegra/Makefile
@@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
 obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
 obj-y					+= cvb.o
 obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
+obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
new file mode 100644
index 000000000000..96cd6becf73e
--- /dev/null
+++ b/drivers/clk/tegra/clk-bpmp.c
@@ -0,0 +1,565 @@
+#define DEBUG
+
+#include <linux/clk-provider.h>
+#include <linux/device.h>
+#include <linux/seq_buf.h>
+#include <linux/slab.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
+#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
+#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)
+
+struct tegra_bpmp_clk_info {
+	unsigned int id;
+	char name[MRQ_CLK_NAME_MAXLEN];
+	unsigned int parents[MRQ_CLK_MAX_PARENTS];
+	unsigned int num_parents;
+	unsigned long flags;
+};
+
+struct tegra_bpmp_clk {
+	struct clk_hw hw;
+
+	struct tegra_bpmp *bpmp;
+	unsigned int id;
+
+	unsigned int num_parents;
+	unsigned int *parents;
+};
+
+static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)
+{
+	return container_of(hw, struct tegra_bpmp_clk, hw);
+}
+
+struct tegra_bpmp_clk_message {
+	unsigned int cmd;
+	unsigned int clk;
+
+	struct {
+		const void *data;
+		size_t size;
+	} tx;
+
+	struct {
+		void *data;
+		size_t size;
+	} rx;
+};
+
+static int
+__tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
+				 struct tegra_bpmp_message *msg)
+{
+	unsigned long flags;
+	int err;
+
+	local_irq_save(flags);
+	err = tegra_bpmp_transfer_atomic(bpmp, msg);
+	local_irq_restore(flags);
+
+	return err;
+}
+
+static int
+tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
+			       const struct tegra_bpmp_clk_message *clk)
+{
+	struct mrq_clk_request request;
+	struct tegra_bpmp_message msg;
+	void *req = (void *)&request;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
+	memcpy(req + 4, clk->tx.data, clk->tx.size);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_CLK;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = clk->rx.data;
+	msg.rx.size = clk->rx.size;
+
+	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);;
+}
+
+static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
+				   const struct tegra_bpmp_clk_message *clk)
+{
+	struct mrq_clk_request request;
+	struct tegra_bpmp_message msg;
+	void *req = (void *)&request;
+	int err;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
+	memcpy(req + 4, clk->tx.data, clk->tx.size);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_CLK;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = clk->rx.data;
+	msg.rx.size = clk->rx.size;
+
+	err = tegra_bpmp_transfer(bpmp, &msg);
+	if (err != -EAGAIN)
+		return err;
+
+	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
+}
+
+static int tegra_bpmp_clk_enable(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	dev_dbg(clk->bpmp->dev, "> %s(hw=%p)\n", __func__, hw);
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_ENABLE;
+	msg.clk = clk->id;
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+
+	dev_dbg(clk->bpmp->dev, "< %s() = %d\n", __func__, err);
+	return err;
+}
+
+static void tegra_bpmp_clk_disable(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_DISABLE;
+	msg.clk = clk->id;
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",
+			clk_hw_get_name(hw), err);
+}
+
+static int tegra_bpmp_clk_is_enabled(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_is_enabled_response response;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_IS_ENABLED;
+	msg.clk = clk->id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer_atomic(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	return response.state;
+}
+
+static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_get_parent_response response;
+	struct tegra_bpmp_clk_message msg;
+	unsigned int i;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_PARENT;
+	msg.clk = clk->id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(&response);
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	for (i = 0; i < clk->num_parents; i++)
+		if (clk->parents[i] == response.parent_id)
+			return i;
+
+	return U8_MAX;
+}
+
+static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
+	struct cmd_clk_set_parent_response response;
+	struct cmd_clk_set_parent_request request;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	if (index >= clk->num_parents)
+		return -EINVAL;
+
+	memset(&request, 0, sizeof(request));
+	request.parent_id = clk->parents[index];
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_SET_PARENT;
+	msg.clk = clk->id;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	/* XXX check parent ID in response */
+
+	return 0;
+}
+
+static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	return 0;
+}
+
+static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *parent_rate)
+{
+	return 0;
+}
+
+static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	return 0;
+}
+
+static const struct clk_ops tegra_bpmp_clk_gate_ops = {
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_ops = {
+	.get_parent = tegra_bpmp_clk_get_parent,
+	.set_parent = tegra_bpmp_clk_set_parent,
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+};
+
+static const struct clk_ops tegra_bpmp_clk_rate_ops = {
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+	.set_rate = tegra_bpmp_clk_set_rate,
+	.round_rate = tegra_bpmp_clk_round_rate,
+	.recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
+	.get_parent = tegra_bpmp_clk_get_parent,
+	.set_parent = tegra_bpmp_clk_set_parent,
+	.is_enabled = tegra_bpmp_clk_is_enabled,
+	.prepare = tegra_bpmp_clk_enable,
+	.unprepare = tegra_bpmp_clk_disable,
+	.set_rate = tegra_bpmp_clk_set_rate,
+	.round_rate = tegra_bpmp_clk_round_rate,
+	.recalc_rate = tegra_bpmp_clk_recalc_rate,
+};
+
+static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
+{
+	struct cmd_clk_get_max_clk_id_response response;
+	struct tegra_bpmp_clk_message msg;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	return response.max_id;
+}
+
+static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
+				   struct tegra_bpmp_clk_info *info)
+{
+	struct cmd_clk_get_all_info_response response;
+	struct tegra_bpmp_clk_message msg;
+	unsigned int i;
+	int err;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.cmd = CMD_CLK_GET_ALL_INFO;
+	msg.clk = id;
+	msg.rx.data = &response;
+	msg.rx.size = sizeof(response);
+
+	err = tegra_bpmp_clk_transfer(bpmp, &msg);
+	if (err < 0)
+		return err;
+
+	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
+	info->num_parents = response.num_parents;
+
+	for (i = 0; i < info->num_parents; i++)
+		info->parents[i] = response.parents[i];
+
+	info->flags = response.flags;
+
+	return 0;
+}
+
+static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
+				   struct tegra_bpmp_clk_info **clocksp)
+{
+	struct tegra_bpmp_clk_info *clocks;
+	unsigned int max_id, id, count = 0;
+	int err;
+
+	err = tegra_bpmp_clk_get_max_id(bpmp);
+	if (err < 0)
+		return err;
+
+	max_id = err;
+
+	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
+
+	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
+	if (!clocks)
+		return -ENOMEM;
+
+	for (id = 0; id <= max_id; id++) {
+		struct tegra_bpmp_clk_info *info = &clocks[count];
+#if 0
+		const char *prefix = "";
+		struct seq_buf buf;
+		unsigned int i;
+		char flags[64];
+#endif
+
+		err = tegra_bpmp_clk_get_info(bpmp, id, info);
+		if (err < 0) {
+			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
+				id, err);
+			continue;
+		}
+
+#if 0
+		seq_buf_init(&buf, flags, sizeof(flags));
+
+		if (info->flags)
+			seq_buf_printf(&buf, "(");
+
+		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+			seq_buf_printf(&buf, "%smux", prefix);
+			prefix = ", ";
+		}
+
+		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
+			seq_buf_printf(&buf, "%sfixed", prefix);
+			prefix = ", ";
+		}
+
+		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
+			seq_buf_printf(&buf, "%sroot", prefix);
+			prefix = ", ";
+		}
+
+		if (info->flags)
+			seq_buf_printf(&buf, ")");
+
+		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
+		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
+		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
+
+		for (i = 0; i < info->num_parents; i++)
+			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
+#endif
+
+		/* clock not exposed by BPMP */
+		if (info->name[0] == '\0')
+			continue;
+
+		info->id = id;
+		count++;
+	}
+
+	*clocksp = clocks;
+
+	return count;
+}
+
+static struct clk_hw *
+tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
+			const struct tegra_bpmp_clk_info *info,
+			const struct tegra_bpmp_clk_info *clocks,
+			unsigned int num_clocks)
+{
+	struct tegra_bpmp_clk *priv;
+	struct clk_init_data init;
+	const char **parents;
+	unsigned int i, j;
+	struct clk *clk;
+
+	dev_dbg(bpmp->dev, "registering clock %u (%s)\n", info->id, info->name);
+
+	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
+	if (!priv)
+		return ERR_PTR(-ENOMEM);
+
+	priv->bpmp = bpmp;
+	priv->id = info->id;
+
+	priv->parents = devm_kcalloc(bpmp->dev, info->num_parents,
+				     sizeof(*priv->parents), GFP_KERNEL);
+	if (!priv->parents)
+		return ERR_PTR(-ENOMEM);
+
+	priv->num_parents = info->num_parents;
+
+	/* hardware clock initialization */
+	priv->hw.init = &init;
+	init.name = info->name;
+
+	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
+		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+			init.ops = &tegra_bpmp_clk_mux_rate_ops;
+		else
+			init.ops = &tegra_bpmp_clk_mux_ops;
+	} else {
+		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
+			init.ops = &tegra_bpmp_clk_rate_ops;
+		else
+			init.ops = &tegra_bpmp_clk_gate_ops;
+	}
+
+	init.num_parents = info->num_parents;
+
+	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
+	if (!parents)
+		return ERR_PTR(-ENOMEM);
+
+	for (i = 0; i < info->num_parents; i++) {
+		/* keep a private copy of the ID to parent index map */
+		priv->parents[i] = info->parents[i];
+
+		for (j = 0; j < num_clocks; j++) {
+			const struct tegra_bpmp_clk_info *parent = &clocks[j];
+
+			if (parent->id == info->parents[i]) {
+				parents[i] = parent->name;
+				break;
+			}
+		}
+
+		if (!parents[i])
+			dev_err(bpmp->dev, "no parent %u found for %u\n",
+				info->parents[i], info->id);
+	}
+
+	init.parent_names = parents;
+
+	clk = clk_register(bpmp->dev, &priv->hw);
+
+	kfree(parents);
+
+	return &priv->hw;
+}
+
+static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
+				      struct tegra_bpmp_clk_info *clocks,
+				      unsigned int count)
+{
+	struct clk_hw *hw;
+	unsigned int i;
+
+	bpmp->num_clocks = count;
+
+	bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(hw), GFP_KERNEL);
+	if (!bpmp->clocks)
+		return -ENOMEM;
+
+	for (i = 0; i < count; i++) {
+		struct tegra_bpmp_clk_info *info = &clocks[i];
+
+		hw = tegra_bpmp_clk_register(bpmp, info, clocks, count);
+		if (IS_ERR(hw)) {
+			dev_err(bpmp->dev,
+				"failed to register clock %u (%s): %ld\n",
+				info->id, info->name, PTR_ERR(hw));
+			continue;
+		}
+
+		bpmp->clocks[i] = hw;
+	}
+
+	return 0;
+}
+
+static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
+					      void *data)
+{
+	unsigned int id = clkspec->args[0], i;
+	struct tegra_bpmp *bpmp = data;
+	struct clk_hw *hw = NULL;
+
+	dev_dbg(bpmp->dev, "> %s(clkspec=%p, data=%p)\n", __func__, clkspec,
+		data);
+
+	for (i = 0; i < bpmp->num_clocks; i++) {
+		struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(bpmp->clocks[i]);
+
+		if (clk->id == id) {
+			hw = bpmp->clocks[i];
+			dev_dbg(bpmp->dev, "  found %u: %s\n", clk->id, clk_hw_get_name(hw));
+			break;
+		}
+	}
+
+	dev_dbg(bpmp->dev, "< %s() = %p\n", __func__, hw);
+
+	return hw;
+}
+
+int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
+{
+	struct tegra_bpmp_clk_info *clocks;
+	unsigned int count;
+	int err;
+
+	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
+
+	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
+	if (err < 0)
+		return err;
+
+	count = err;
+
+	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
+
+	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
+	if (err < 0) {
+		kfree(clocks);
+		return err;
+	}
+
+	kfree(clocks);
+
+	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
+			       bpmp);
+
+	dev_dbg(bpmp->dev, "< %s()\n", __func__);
+	return 0;
+}
-- 
2.9.0

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

* [PATCH v3 12/12] reset: Add Tegra BPMP reset driver
  2016-08-19 17:32 ` Thierry Reding
@ 2016-08-19 17:32     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

This driver uses the services provided by the BPMP firmware driver to
implement a reset driver based on the MRQ_RESET request.

Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
---
 drivers/reset/Makefile           |  1 +
 drivers/reset/tegra/Makefile     |  1 +
 drivers/reset/tegra/reset-bpmp.c | 63 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+)
 create mode 100644 drivers/reset/tegra/Makefile
 create mode 100644 drivers/reset/tegra/reset-bpmp.c

diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 5d65a93d3c43..523f2d705d24 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o
 obj-$(CONFIG_ARCH_MESON) += reset-meson.o
 obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_ARCH_STI) += sti/
+obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_ARCH_HISI) += hisilicon/
 obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
 obj-$(CONFIG_ATH79) += reset-ath79.o
diff --git a/drivers/reset/tegra/Makefile b/drivers/reset/tegra/Makefile
new file mode 100644
index 000000000000..fd943b1ae029
--- /dev/null
+++ b/drivers/reset/tegra/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= reset-bpmp.o
diff --git a/drivers/reset/tegra/reset-bpmp.c b/drivers/reset/tegra/reset-bpmp.c
new file mode 100644
index 000000000000..a06e45dc3f30
--- /dev/null
+++ b/drivers/reset/tegra/reset-bpmp.c
@@ -0,0 +1,63 @@
+#include <linux/reset-controller.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+static struct tegra_bpmp *to_tegra_bpmp(struct reset_controller_dev *rstc)
+{
+	return container_of(rstc, struct tegra_bpmp, rstc);
+}
+
+static int tegra_bpmp_reset_common(struct reset_controller_dev *rstc,
+				   enum mrq_reset_commands command,
+				   unsigned int id)
+{
+	struct tegra_bpmp *bpmp = to_tegra_bpmp(rstc);
+	struct mrq_reset_request request;
+	struct tegra_bpmp_message msg;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd = command;
+	request.reset_id = id;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_RESET;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+
+	return tegra_bpmp_transfer(bpmp, &msg);
+}
+
+static int tegra_bpmp_reset_module(struct reset_controller_dev *rstc,
+				   unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_MODULE, id);
+}
+
+static int tegra_bpmp_reset_assert(struct reset_controller_dev *rstc,
+				   unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_ASSERT, id);
+}
+
+static int tegra_bpmp_reset_deassert(struct reset_controller_dev *rstc,
+				     unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_DEASSERT, id);
+}
+
+static const struct reset_control_ops tegra_bpmp_reset_ops = {
+	.reset = tegra_bpmp_reset_module,
+	.assert = tegra_bpmp_reset_assert,
+	.deassert = tegra_bpmp_reset_deassert,
+};
+
+int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
+{
+	bpmp->rstc.ops = &tegra_bpmp_reset_ops;
+	bpmp->rstc.owner = THIS_MODULE;
+	bpmp->rstc.of_node = bpmp->dev->of_node;
+	bpmp->rstc.nr_resets = bpmp->soc->num_resets;
+
+	return devm_reset_controller_register(bpmp->dev, &bpmp->rstc);
+}
-- 
2.9.0

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

* [PATCH v3 12/12] reset: Add Tegra BPMP reset driver
@ 2016-08-19 17:32     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-19 17:32 UTC (permalink / raw)
  To: linux-arm-kernel

From: Thierry Reding <treding@nvidia.com>

This driver uses the services provided by the BPMP firmware driver to
implement a reset driver based on the MRQ_RESET request.

Signed-off-by: Thierry Reding <treding@nvidia.com>
---
 drivers/reset/Makefile           |  1 +
 drivers/reset/tegra/Makefile     |  1 +
 drivers/reset/tegra/reset-bpmp.c | 63 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 65 insertions(+)
 create mode 100644 drivers/reset/tegra/Makefile
 create mode 100644 drivers/reset/tegra/reset-bpmp.c

diff --git a/drivers/reset/Makefile b/drivers/reset/Makefile
index 5d65a93d3c43..523f2d705d24 100644
--- a/drivers/reset/Makefile
+++ b/drivers/reset/Makefile
@@ -6,6 +6,7 @@ obj-$(CONFIG_MACH_PISTACHIO) += reset-pistachio.o
 obj-$(CONFIG_ARCH_MESON) += reset-meson.o
 obj-$(CONFIG_ARCH_SUNXI) += reset-sunxi.o
 obj-$(CONFIG_ARCH_STI) += sti/
+obj-$(CONFIG_ARCH_TEGRA) += tegra/
 obj-$(CONFIG_ARCH_HISI) += hisilicon/
 obj-$(CONFIG_ARCH_ZYNQ) += reset-zynq.o
 obj-$(CONFIG_ATH79) += reset-ath79.o
diff --git a/drivers/reset/tegra/Makefile b/drivers/reset/tegra/Makefile
new file mode 100644
index 000000000000..fd943b1ae029
--- /dev/null
+++ b/drivers/reset/tegra/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= reset-bpmp.o
diff --git a/drivers/reset/tegra/reset-bpmp.c b/drivers/reset/tegra/reset-bpmp.c
new file mode 100644
index 000000000000..a06e45dc3f30
--- /dev/null
+++ b/drivers/reset/tegra/reset-bpmp.c
@@ -0,0 +1,63 @@
+#include <linux/reset-controller.h>
+
+#include <soc/tegra/bpmp.h>
+#include <soc/tegra/bpmp-abi.h>
+
+static struct tegra_bpmp *to_tegra_bpmp(struct reset_controller_dev *rstc)
+{
+	return container_of(rstc, struct tegra_bpmp, rstc);
+}
+
+static int tegra_bpmp_reset_common(struct reset_controller_dev *rstc,
+				   enum mrq_reset_commands command,
+				   unsigned int id)
+{
+	struct tegra_bpmp *bpmp = to_tegra_bpmp(rstc);
+	struct mrq_reset_request request;
+	struct tegra_bpmp_message msg;
+
+	memset(&request, 0, sizeof(request));
+	request.cmd = command;
+	request.reset_id = id;
+
+	memset(&msg, 0, sizeof(msg));
+	msg.mrq = MRQ_RESET;
+	msg.tx.data = &request;
+	msg.tx.size = sizeof(request);
+
+	return tegra_bpmp_transfer(bpmp, &msg);
+}
+
+static int tegra_bpmp_reset_module(struct reset_controller_dev *rstc,
+				   unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_MODULE, id);
+}
+
+static int tegra_bpmp_reset_assert(struct reset_controller_dev *rstc,
+				   unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_ASSERT, id);
+}
+
+static int tegra_bpmp_reset_deassert(struct reset_controller_dev *rstc,
+				     unsigned long id)
+{
+	return tegra_bpmp_reset_common(rstc, CMD_RESET_DEASSERT, id);
+}
+
+static const struct reset_control_ops tegra_bpmp_reset_ops = {
+	.reset = tegra_bpmp_reset_module,
+	.assert = tegra_bpmp_reset_assert,
+	.deassert = tegra_bpmp_reset_deassert,
+};
+
+int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
+{
+	bpmp->rstc.ops = &tegra_bpmp_reset_ops;
+	bpmp->rstc.owner = THIS_MODULE;
+	bpmp->rstc.of_node = bpmp->dev->of_node;
+	bpmp->rstc.nr_resets = bpmp->soc->num_resets;
+
+	return devm_reset_controller_register(bpmp->dev, &bpmp->rstc);
+}
-- 
2.9.0

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22  9:26         ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22  9:26 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
> 
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.

MRQ?

> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
> 
> Based on work by Sivaram Nair <sivaramn-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
> <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.
> 
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v3:
> - use a message structure for transfers to avoid long function
>   prototypes
> - restructure driver for easier maintainability
> - rename bpmp_abi.h to bpmp-abi.h for consistency
> 
>  drivers/firmware/tegra/Kconfig  |   12 +
>  drivers/firmware/tegra/Makefile |    1 +
>  drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
>  include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/bpmp.h        |  122 +++
>  5 files changed, 2616 insertions(+)
>  create mode 100644 drivers/firmware/tegra/bpmp.c
>  create mode 100644 include/soc/tegra/bpmp-abi.h
>  create mode 100644 include/soc/tegra/bpmp.h
> 
> diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
> index 1fa3e4e136a5..ff2730d5c468 100644
> --- a/drivers/firmware/tegra/Kconfig
> +++ b/drivers/firmware/tegra/Kconfig
> @@ -10,4 +10,16 @@ config TEGRA_IVC
>  	  keeps the content is synchronization between host CPU and remote
>  	  processors.
>  
> +config TEGRA_BPMP
> +	bool "Tegra BPMP driver"
> +	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
> +	help
> +	  BPMP (Boot and Power Management Processor) is designed to off-loading
> +	  the PM functions which include clock/DVFS/thermal/power from the CPU.
> +	  It needs HSP as the HW synchronization and notification module and
> +	  IVC module as the message communication protocol.
> +
> +	  This driver manages the IPC interface between host CPU and the
> +	  firmware running on BPMP.
> +
>  endmenu
> diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> index 92e2153e8173..e34a2f79e1ad 100644
> --- a/drivers/firmware/tegra/Makefile
> +++ b/drivers/firmware/tegra/Makefile
> @@ -1 +1,2 @@
> +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
>  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> new file mode 100644
> index 000000000000..a09043b1be05
> --- /dev/null
> +++ b/drivers/firmware/tegra/bpmp.c
> @@ -0,0 +1,880 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG

I don't think we want DEBUG by default, right?

> +#include <linux/clk/tegra.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/semaphore.h>

[snip]

> +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_ping_response response;
> +	struct mrq_ping_request request;
> +	struct tegra_bpmp_message msg;
> +	ktime_t start, delta;
> +	unsigned long flags;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.challenge = 1;
> +
> +	memset(&response, 0, sizeof(response));
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_PING;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	start = ktime_get();
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	delta = ktime_sub(ktime_get(), start);
> +
> +	if (!err)
> +		dev_info(bpmp->dev,
> +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> +			 request.challenge, response.reply,
> +			 ktime_to_us(delta));

Should this be a dev_dbg? I guess this only happens on probe.

> +	return err;
> +}

[snip]

> +static int tegra_bpmp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +	struct resource res;
> +	unsigned int i;
> +	char tag[32];
> +	size_t size;
> +	int err;
> +
> +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> +	if (!bpmp)
> +		return -ENOMEM;
> +
> +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> +	bpmp->dev = &pdev->dev;
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->tx_base))
> +		return PTR_ERR(bpmp->tx_base);
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->rx_base))
> +		return PTR_ERR(bpmp->rx_base);
> +
> +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> +			     bpmp->soc->channels.thread.count +
> +			     bpmp->soc->channels.cpu_rx.count;
> +
> +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> +				      sizeof(*channel), GFP_KERNEL);
> +	if (!bpmp->channels)
> +		return -ENOMEM;
> +
> +	/* mbox registration */
> +	bpmp->mbox.client.dev = &pdev->dev;
> +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> +	bpmp->mbox.client.tx_block = false;
> +	bpmp->mbox.client.knows_txdone = false;
> +
> +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> +	if (IS_ERR(bpmp->mbox.channel)) {
> +		err = PTR_ERR(bpmp->mbox.channel);
> +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> +		return err;
> +	}
> +
> +	/* message channel initialization */
> +	for (i = 0; i < bpmp->num_channels; i++) {
> +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> +
> +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> +		if (err)
> +			return err;
> +	}

We should make sure we free the mbox if we fail after requesting it.

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22  9:26         ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22  9:26 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
> 
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.

MRQ?

> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
> 
> Based on work by Sivaram Nair <sivaramn@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a message structure for transfers to avoid long function
>   prototypes
> - restructure driver for easier maintainability
> - rename bpmp_abi.h to bpmp-abi.h for consistency
> 
>  drivers/firmware/tegra/Kconfig  |   12 +
>  drivers/firmware/tegra/Makefile |    1 +
>  drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
>  include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/bpmp.h        |  122 +++
>  5 files changed, 2616 insertions(+)
>  create mode 100644 drivers/firmware/tegra/bpmp.c
>  create mode 100644 include/soc/tegra/bpmp-abi.h
>  create mode 100644 include/soc/tegra/bpmp.h
> 
> diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
> index 1fa3e4e136a5..ff2730d5c468 100644
> --- a/drivers/firmware/tegra/Kconfig
> +++ b/drivers/firmware/tegra/Kconfig
> @@ -10,4 +10,16 @@ config TEGRA_IVC
>  	  keeps the content is synchronization between host CPU and remote
>  	  processors.
>  
> +config TEGRA_BPMP
> +	bool "Tegra BPMP driver"
> +	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
> +	help
> +	  BPMP (Boot and Power Management Processor) is designed to off-loading
> +	  the PM functions which include clock/DVFS/thermal/power from the CPU.
> +	  It needs HSP as the HW synchronization and notification module and
> +	  IVC module as the message communication protocol.
> +
> +	  This driver manages the IPC interface between host CPU and the
> +	  firmware running on BPMP.
> +
>  endmenu
> diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> index 92e2153e8173..e34a2f79e1ad 100644
> --- a/drivers/firmware/tegra/Makefile
> +++ b/drivers/firmware/tegra/Makefile
> @@ -1 +1,2 @@
> +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
>  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> new file mode 100644
> index 000000000000..a09043b1be05
> --- /dev/null
> +++ b/drivers/firmware/tegra/bpmp.c
> @@ -0,0 +1,880 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG

I don't think we want DEBUG by default, right?

> +#include <linux/clk/tegra.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/semaphore.h>

[snip]

> +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_ping_response response;
> +	struct mrq_ping_request request;
> +	struct tegra_bpmp_message msg;
> +	ktime_t start, delta;
> +	unsigned long flags;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.challenge = 1;
> +
> +	memset(&response, 0, sizeof(response));
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_PING;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	start = ktime_get();
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	delta = ktime_sub(ktime_get(), start);
> +
> +	if (!err)
> +		dev_info(bpmp->dev,
> +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> +			 request.challenge, response.reply,
> +			 ktime_to_us(delta));

Should this be a dev_dbg? I guess this only happens on probe.

> +	return err;
> +}

[snip]

> +static int tegra_bpmp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +	struct resource res;
> +	unsigned int i;
> +	char tag[32];
> +	size_t size;
> +	int err;
> +
> +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> +	if (!bpmp)
> +		return -ENOMEM;
> +
> +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> +	bpmp->dev = &pdev->dev;
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->tx_base))
> +		return PTR_ERR(bpmp->tx_base);
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->rx_base))
> +		return PTR_ERR(bpmp->rx_base);
> +
> +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> +			     bpmp->soc->channels.thread.count +
> +			     bpmp->soc->channels.cpu_rx.count;
> +
> +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> +				      sizeof(*channel), GFP_KERNEL);
> +	if (!bpmp->channels)
> +		return -ENOMEM;
> +
> +	/* mbox registration */
> +	bpmp->mbox.client.dev = &pdev->dev;
> +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> +	bpmp->mbox.client.tx_block = false;
> +	bpmp->mbox.client.knows_txdone = false;
> +
> +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> +	if (IS_ERR(bpmp->mbox.channel)) {
> +		err = PTR_ERR(bpmp->mbox.channel);
> +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> +		return err;
> +	}
> +
> +	/* message channel initialization */
> +	for (i = 0; i < bpmp->num_channels; i++) {
> +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> +
> +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> +		if (err)
> +			return err;
> +	}

We should make sure we free the mbox if we fail after requesting it.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 10:11         ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 10:11 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> This driver uses the services provided by the BPMP firmware driver to
> implement a clock driver based on the MRQ_CLK request. This part of the
> BPMP ABI provides a means to enumerate and control clocks and should
> allow the driver to work on any chip that supports this ABI.
> 
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/clk/tegra/Makefile   |   1 +
>  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 566 insertions(+)
>  create mode 100644 drivers/clk/tegra/clk-bpmp.c
> 
> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> index 33fd0938d79e..130df5685d21 100644
> --- a/drivers/clk/tegra/Makefile
> +++ b/drivers/clk/tegra/Makefile
> @@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
>  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
>  obj-y					+= cvb.o
>  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> +obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
> diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
> new file mode 100644
> index 000000000000..96cd6becf73e
> --- /dev/null
> +++ b/drivers/clk/tegra/clk-bpmp.c
> @@ -0,0 +1,565 @@
> +#define DEBUG

Do we want DEBUG by default?

> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/seq_buf.h>
> +#include <linux/slab.h>
> +
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +
> +#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
> +#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
> +#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)
> +
> +struct tegra_bpmp_clk_info {
> +	unsigned int id;
> +	char name[MRQ_CLK_NAME_MAXLEN];
> +	unsigned int parents[MRQ_CLK_MAX_PARENTS];
> +	unsigned int num_parents;
> +	unsigned long flags;
> +};
> +
> +struct tegra_bpmp_clk {
> +	struct clk_hw hw;
> +
> +	struct tegra_bpmp *bpmp;
> +	unsigned int id;
> +
> +	unsigned int num_parents;
> +	unsigned int *parents;
> +};
> +
> +static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct tegra_bpmp_clk, hw);
> +}
> +
> +struct tegra_bpmp_clk_message {
> +	unsigned int cmd;
> +	unsigned int clk;
> +
> +	struct {
> +		const void *data;
> +		size_t size;
> +	} tx;
> +
> +	struct {
> +		void *data;
> +		size_t size;
> +	} rx;
> +};
> +
> +static int
> +__tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +				 struct tegra_bpmp_message *msg)
> +{
> +	unsigned long flags;
> +	int err;
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, msg);
> +	local_irq_restore(flags);
> +
> +	return err;
> +}
> +
> +static int
> +tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_CLK;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = clk->rx.data;
> +	msg.rx.size = clk->rx.size;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);;
> +}
> +
> +static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
> +				   const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_CLK;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = clk->rx.data;
> +	msg.rx.size = clk->rx.size;
> +
> +	err = tegra_bpmp_transfer(bpmp, &msg);
> +	if (err != -EAGAIN)
> +		return err;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
> +}
> +
> +static int tegra_bpmp_clk_enable(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	dev_dbg(clk->bpmp->dev, "> %s(hw=%p)\n", __func__, hw);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_ENABLE;
> +	msg.clk = clk->id;
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +
> +	dev_dbg(clk->bpmp->dev, "< %s() = %d\n", __func__, err);
> +	return err;
> +}
> +
> +static void tegra_bpmp_clk_disable(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_DISABLE;
> +	msg.clk = clk->id;
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",
> +			clk_hw_get_name(hw), err);
> +}
> +
> +static int tegra_bpmp_clk_is_enabled(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_is_enabled_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_IS_ENABLED;
> +	msg.clk = clk->id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer_atomic(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	return response.state;
> +}
> +
> +static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_get_parent_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_PARENT;
> +	msg.clk = clk->id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(&response);
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;

tegra_bpmp_clk_transfer returns an int, but this function returns a u8.

> +
> +	for (i = 0; i < clk->num_parents; i++)
> +		if (clk->parents[i] == response.parent_id)
> +			return i;
> +
> +	return U8_MAX;

Is there any chance the U8_MAX == num_parents? Should we warn here
somewhere?

> +}
> +
> +static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_set_parent_response response;
> +	struct cmd_clk_set_parent_request request;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	if (index >= clk->num_parents)
> +		return -EINVAL;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.parent_id = clk->parents[index];
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_SET_PARENT;
> +	msg.clk = clk->id;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	/* XXX check parent ID in response */
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *parent_rate)
> +{
> +	return 0;
> +}
> +
> +static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static const struct clk_ops tegra_bpmp_clk_gate_ops = {
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_mux_ops = {
> +	.get_parent = tegra_bpmp_clk_get_parent,
> +	.set_parent = tegra_bpmp_clk_set_parent,
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_rate_ops = {
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +	.set_rate = tegra_bpmp_clk_set_rate,
> +	.round_rate = tegra_bpmp_clk_round_rate,
> +	.recalc_rate = tegra_bpmp_clk_recalc_rate,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
> +	.get_parent = tegra_bpmp_clk_get_parent,
> +	.set_parent = tegra_bpmp_clk_set_parent,
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +	.set_rate = tegra_bpmp_clk_set_rate,
> +	.round_rate = tegra_bpmp_clk_round_rate,
> +	.recalc_rate = tegra_bpmp_clk_recalc_rate,
> +};
> +
> +static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
> +{
> +	struct cmd_clk_get_max_clk_id_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	return response.max_id;

response.max_id is a uint32
> +}
> +
> +static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
> +				   struct tegra_bpmp_clk_info *info)
> +{
> +	struct cmd_clk_get_all_info_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_ALL_INFO;
> +	msg.clk = id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
> +	info->num_parents = response.num_parents;
> +
> +	for (i = 0; i < info->num_parents; i++)
> +		info->parents[i] = response.parents[i];
> +
> +	info->flags = response.flags;
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> +				   struct tegra_bpmp_clk_info **clocksp)
> +{
> +	struct tegra_bpmp_clk_info *clocks;
> +	unsigned int max_id, id, count = 0;
> +	int err;
> +
> +	err = tegra_bpmp_clk_get_max_id(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	max_id = err;
> +
> +	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
> +
> +	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
> +	if (!clocks)
> +		return -ENOMEM;
> +
> +	for (id = 0; id <= max_id; id++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[count];
> +#if 0
> +		const char *prefix = "";
> +		struct seq_buf buf;
> +		unsigned int i;
> +		char flags[64];
> +#endif
> +
> +		err = tegra_bpmp_clk_get_info(bpmp, id, info);
> +		if (err < 0) {
> +			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
> +				id, err);
> +			continue;
> +		}
> +
> +#if 0
> +		seq_buf_init(&buf, flags, sizeof(flags));
> +
> +		if (info->flags)
> +			seq_buf_printf(&buf, "(");
> +
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> +			seq_buf_printf(&buf, "%smux", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
> +			seq_buf_printf(&buf, "%sfixed", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
> +			seq_buf_printf(&buf, "%sroot", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if (info->flags)
> +			seq_buf_printf(&buf, ")");
> +
> +		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
> +		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
> +		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
> +
> +		for (i = 0; i < info->num_parents; i++)
> +			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
> +#endif
> +
> +		/* clock not exposed by BPMP */
> +		if (info->name[0] == '\0')
> +			continue;
> +
> +		info->id = id;
> +		count++;
> +	}
> +
> +	*clocksp = clocks;

Nit we could just return the pointer.

> +
> +	return count;

We return unsigned int here and not int. Why do we bother returning
count and just store it directly in the bpmp->num_clocks here (see
tegra_bpmp_register_clocks)?

> +}
> +
> +static struct clk_hw *
> +tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
> +			const struct tegra_bpmp_clk_info *info,
> +			const struct tegra_bpmp_clk_info *clocks,
> +			unsigned int num_clocks)
> +{
> +	struct tegra_bpmp_clk *priv;
> +	struct clk_init_data init;
> +	const char **parents;
> +	unsigned int i, j;
> +	struct clk *clk;
> +
> +	dev_dbg(bpmp->dev, "registering clock %u (%s)\n", info->id, info->name);
> +
> +	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return ERR_PTR(-ENOMEM);
> +
> +	priv->bpmp = bpmp;
> +	priv->id = info->id;
> +
> +	priv->parents = devm_kcalloc(bpmp->dev, info->num_parents,
> +				     sizeof(*priv->parents), GFP_KERNEL);
> +	if (!priv->parents)
> +		return ERR_PTR(-ENOMEM);
> +
> +	priv->num_parents = info->num_parents;
> +
> +	/* hardware clock initialization */
> +	priv->hw.init = &init;
> +	init.name = info->name;
> +
> +	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
> +			init.ops = &tegra_bpmp_clk_mux_rate_ops;
> +		else
> +			init.ops = &tegra_bpmp_clk_mux_ops;
> +	} else {
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
> +			init.ops = &tegra_bpmp_clk_rate_ops;
> +		else
> +			init.ops = &tegra_bpmp_clk_gate_ops;
> +	}
> +
> +	init.num_parents = info->num_parents;
> +
> +	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
> +	if (!parents)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < info->num_parents; i++) {
> +		/* keep a private copy of the ID to parent index map */
> +		priv->parents[i] = info->parents[i];
> +
> +		for (j = 0; j < num_clocks; j++) {
> +			const struct tegra_bpmp_clk_info *parent = &clocks[j];
> +
> +			if (parent->id == info->parents[i]) {
> +				parents[i] = parent->name;
> +				break;
> +			}
> +		}

Is it necessary to loop through all the clocks again here? This function
is called from tegra_bpmp_register_clocks() which is already looping
through all the clocks. So for each clock we loop through all the clocks
again.

> +		if (!parents[i])
> +			dev_err(bpmp->dev, "no parent %u found for %u\n",
> +				info->parents[i], info->id);
> +	}
> +
> +	init.parent_names = parents;
> +
> +	clk = clk_register(bpmp->dev, &priv->hw);

IS_ERR?

> +
> +	kfree(parents);
> +
> +	return &priv->hw;
> +}
> +
> +static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
> +				      struct tegra_bpmp_clk_info *clocks,
> +				      unsigned int count)
> +{
> +	struct clk_hw *hw;
> +	unsigned int i;
> +
> +	bpmp->num_clocks = count;
> +
> +	bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(hw), GFP_KERNEL);
> +	if (!bpmp->clocks)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < count; i++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[i];
> +
> +		hw = tegra_bpmp_clk_register(bpmp, info, clocks, count);
> +		if (IS_ERR(hw)) {
> +			dev_err(bpmp->dev,
> +				"failed to register clock %u (%s): %ld\n",
> +				info->id, info->name, PTR_ERR(hw));
> +			continue;
> +		}
> +
> +		bpmp->clocks[i] = hw;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
> +					      void *data)
> +{
> +	unsigned int id = clkspec->args[0], i;
> +	struct tegra_bpmp *bpmp = data;
> +	struct clk_hw *hw = NULL;
> +
> +	dev_dbg(bpmp->dev, "> %s(clkspec=%p, data=%p)\n", __func__, clkspec,
> +		data);
> +
> +	for (i = 0; i < bpmp->num_clocks; i++) {
> +		struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(bpmp->clocks[i]);
> +
> +		if (clk->id == id) {
> +			hw = bpmp->clocks[i];
> +			dev_dbg(bpmp->dev, "  found %u: %s\n", clk->id, clk_hw_get_name(hw));
> +			break;
> +		}
> +	}
> +
> +	dev_dbg(bpmp->dev, "< %s() = %p\n", __func__, hw);
> +
> +	return hw;
> +}
> +
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
> +{
> +	struct tegra_bpmp_clk_info *clocks;
> +	unsigned int count;
> +	int err;
> +
> +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
> +
> +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
> +	if (err < 0)
> +		return err;
> +
> +	count = err;
> +
> +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
> +
> +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
> +	if (err < 0) {
> +		kfree(clocks);
> +		return err;
> +	}
> +
> +	kfree(clocks);
> +
> +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
> +			       bpmp);

We should check the return code.

> +	dev_dbg(bpmp->dev, "< %s()\n", __func__);
> +	return 0;
> +}
> 

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
@ 2016-08-22 10:11         ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 10:11 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This driver uses the services provided by the BPMP firmware driver to
> implement a clock driver based on the MRQ_CLK request. This part of the
> BPMP ABI provides a means to enumerate and control clocks and should
> allow the driver to work on any chip that supports this ABI.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/clk/tegra/Makefile   |   1 +
>  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 566 insertions(+)
>  create mode 100644 drivers/clk/tegra/clk-bpmp.c
> 
> diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> index 33fd0938d79e..130df5685d21 100644
> --- a/drivers/clk/tegra/Makefile
> +++ b/drivers/clk/tegra/Makefile
> @@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
>  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
>  obj-y					+= cvb.o
>  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> +obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
> diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
> new file mode 100644
> index 000000000000..96cd6becf73e
> --- /dev/null
> +++ b/drivers/clk/tegra/clk-bpmp.c
> @@ -0,0 +1,565 @@
> +#define DEBUG

Do we want DEBUG by default?

> +#include <linux/clk-provider.h>
> +#include <linux/device.h>
> +#include <linux/seq_buf.h>
> +#include <linux/slab.h>
> +
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +
> +#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
> +#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
> +#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)
> +
> +struct tegra_bpmp_clk_info {
> +	unsigned int id;
> +	char name[MRQ_CLK_NAME_MAXLEN];
> +	unsigned int parents[MRQ_CLK_MAX_PARENTS];
> +	unsigned int num_parents;
> +	unsigned long flags;
> +};
> +
> +struct tegra_bpmp_clk {
> +	struct clk_hw hw;
> +
> +	struct tegra_bpmp *bpmp;
> +	unsigned int id;
> +
> +	unsigned int num_parents;
> +	unsigned int *parents;
> +};
> +
> +static inline struct tegra_bpmp_clk *to_tegra_bpmp_clk(struct clk_hw *hw)
> +{
> +	return container_of(hw, struct tegra_bpmp_clk, hw);
> +}
> +
> +struct tegra_bpmp_clk_message {
> +	unsigned int cmd;
> +	unsigned int clk;
> +
> +	struct {
> +		const void *data;
> +		size_t size;
> +	} tx;
> +
> +	struct {
> +		void *data;
> +		size_t size;
> +	} rx;
> +};
> +
> +static int
> +__tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +				 struct tegra_bpmp_message *msg)
> +{
> +	unsigned long flags;
> +	int err;
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, msg);
> +	local_irq_restore(flags);
> +
> +	return err;
> +}
> +
> +static int
> +tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_CLK;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = clk->rx.data;
> +	msg.rx.size = clk->rx.size;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);;
> +}
> +
> +static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
> +				   const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_CLK;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = clk->rx.data;
> +	msg.rx.size = clk->rx.size;
> +
> +	err = tegra_bpmp_transfer(bpmp, &msg);
> +	if (err != -EAGAIN)
> +		return err;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
> +}
> +
> +static int tegra_bpmp_clk_enable(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	dev_dbg(clk->bpmp->dev, "> %s(hw=%p)\n", __func__, hw);
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_ENABLE;
> +	msg.clk = clk->id;
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +
> +	dev_dbg(clk->bpmp->dev, "< %s() = %d\n", __func__, err);
> +	return err;
> +}
> +
> +static void tegra_bpmp_clk_disable(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_DISABLE;
> +	msg.clk = clk->id;
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		dev_err(clk->bpmp->dev, "failed to disable clock %s: %d\n",
> +			clk_hw_get_name(hw), err);
> +}
> +
> +static int tegra_bpmp_clk_is_enabled(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_is_enabled_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_IS_ENABLED;
> +	msg.clk = clk->id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer_atomic(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	return response.state;
> +}
> +
> +static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_get_parent_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_PARENT;
> +	msg.clk = clk->id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(&response);
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;

tegra_bpmp_clk_transfer returns an int, but this function returns a u8.

> +
> +	for (i = 0; i < clk->num_parents; i++)
> +		if (clk->parents[i] == response.parent_id)
> +			return i;
> +
> +	return U8_MAX;

Is there any chance the U8_MAX == num_parents? Should we warn here
somewhere?

> +}
> +
> +static int tegra_bpmp_clk_set_parent(struct clk_hw *hw, u8 index)
> +{
> +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> +	struct cmd_clk_set_parent_response response;
> +	struct cmd_clk_set_parent_request request;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	if (index >= clk->num_parents)
> +		return -EINVAL;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.parent_id = clk->parents[index];
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_SET_PARENT;
> +	msg.clk = clk->id;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	/* XXX check parent ID in response */
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *parent_rate)
> +{
> +	return 0;
> +}
> +
> +static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static const struct clk_ops tegra_bpmp_clk_gate_ops = {
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_mux_ops = {
> +	.get_parent = tegra_bpmp_clk_get_parent,
> +	.set_parent = tegra_bpmp_clk_set_parent,
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_rate_ops = {
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +	.set_rate = tegra_bpmp_clk_set_rate,
> +	.round_rate = tegra_bpmp_clk_round_rate,
> +	.recalc_rate = tegra_bpmp_clk_recalc_rate,
> +};
> +
> +static const struct clk_ops tegra_bpmp_clk_mux_rate_ops = {
> +	.get_parent = tegra_bpmp_clk_get_parent,
> +	.set_parent = tegra_bpmp_clk_set_parent,
> +	.is_enabled = tegra_bpmp_clk_is_enabled,
> +	.prepare = tegra_bpmp_clk_enable,
> +	.unprepare = tegra_bpmp_clk_disable,
> +	.set_rate = tegra_bpmp_clk_set_rate,
> +	.round_rate = tegra_bpmp_clk_round_rate,
> +	.recalc_rate = tegra_bpmp_clk_recalc_rate,
> +};
> +
> +static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
> +{
> +	struct cmd_clk_get_max_clk_id_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	return response.max_id;

response.max_id is a uint32
> +}
> +
> +static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
> +				   struct tegra_bpmp_clk_info *info)
> +{
> +	struct cmd_clk_get_all_info_response response;
> +	struct tegra_bpmp_clk_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.cmd = CMD_CLK_GET_ALL_INFO;
> +	msg.clk = id;
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> +	if (err < 0)
> +		return err;
> +
> +	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
> +	info->num_parents = response.num_parents;
> +
> +	for (i = 0; i < info->num_parents; i++)
> +		info->parents[i] = response.parents[i];
> +
> +	info->flags = response.flags;
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> +				   struct tegra_bpmp_clk_info **clocksp)
> +{
> +	struct tegra_bpmp_clk_info *clocks;
> +	unsigned int max_id, id, count = 0;
> +	int err;
> +
> +	err = tegra_bpmp_clk_get_max_id(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	max_id = err;
> +
> +	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
> +
> +	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
> +	if (!clocks)
> +		return -ENOMEM;
> +
> +	for (id = 0; id <= max_id; id++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[count];
> +#if 0
> +		const char *prefix = "";
> +		struct seq_buf buf;
> +		unsigned int i;
> +		char flags[64];
> +#endif
> +
> +		err = tegra_bpmp_clk_get_info(bpmp, id, info);
> +		if (err < 0) {
> +			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
> +				id, err);
> +			continue;
> +		}
> +
> +#if 0
> +		seq_buf_init(&buf, flags, sizeof(flags));
> +
> +		if (info->flags)
> +			seq_buf_printf(&buf, "(");
> +
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> +			seq_buf_printf(&buf, "%smux", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
> +			seq_buf_printf(&buf, "%sfixed", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
> +			seq_buf_printf(&buf, "%sroot", prefix);
> +			prefix = ", ";
> +		}
> +
> +		if (info->flags)
> +			seq_buf_printf(&buf, ")");
> +
> +		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
> +		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
> +		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
> +
> +		for (i = 0; i < info->num_parents; i++)
> +			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
> +#endif
> +
> +		/* clock not exposed by BPMP */
> +		if (info->name[0] == '\0')
> +			continue;
> +
> +		info->id = id;
> +		count++;
> +	}
> +
> +	*clocksp = clocks;

Nit we could just return the pointer.

> +
> +	return count;

We return unsigned int here and not int. Why do we bother returning
count and just store it directly in the bpmp->num_clocks here (see
tegra_bpmp_register_clocks)?

> +}
> +
> +static struct clk_hw *
> +tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
> +			const struct tegra_bpmp_clk_info *info,
> +			const struct tegra_bpmp_clk_info *clocks,
> +			unsigned int num_clocks)
> +{
> +	struct tegra_bpmp_clk *priv;
> +	struct clk_init_data init;
> +	const char **parents;
> +	unsigned int i, j;
> +	struct clk *clk;
> +
> +	dev_dbg(bpmp->dev, "registering clock %u (%s)\n", info->id, info->name);
> +
> +	priv = devm_kzalloc(bpmp->dev, sizeof(*priv), GFP_KERNEL);
> +	if (!priv)
> +		return ERR_PTR(-ENOMEM);
> +
> +	priv->bpmp = bpmp;
> +	priv->id = info->id;
> +
> +	priv->parents = devm_kcalloc(bpmp->dev, info->num_parents,
> +				     sizeof(*priv->parents), GFP_KERNEL);
> +	if (!priv->parents)
> +		return ERR_PTR(-ENOMEM);
> +
> +	priv->num_parents = info->num_parents;
> +
> +	/* hardware clock initialization */
> +	priv->hw.init = &init;
> +	init.name = info->name;
> +
> +	if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
> +			init.ops = &tegra_bpmp_clk_mux_rate_ops;
> +		else
> +			init.ops = &tegra_bpmp_clk_mux_ops;
> +	} else {
> +		if (info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE)
> +			init.ops = &tegra_bpmp_clk_rate_ops;
> +		else
> +			init.ops = &tegra_bpmp_clk_gate_ops;
> +	}
> +
> +	init.num_parents = info->num_parents;
> +
> +	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
> +	if (!parents)
> +		return ERR_PTR(-ENOMEM);
> +
> +	for (i = 0; i < info->num_parents; i++) {
> +		/* keep a private copy of the ID to parent index map */
> +		priv->parents[i] = info->parents[i];
> +
> +		for (j = 0; j < num_clocks; j++) {
> +			const struct tegra_bpmp_clk_info *parent = &clocks[j];
> +
> +			if (parent->id == info->parents[i]) {
> +				parents[i] = parent->name;
> +				break;
> +			}
> +		}

Is it necessary to loop through all the clocks again here? This function
is called from tegra_bpmp_register_clocks() which is already looping
through all the clocks. So for each clock we loop through all the clocks
again.

> +		if (!parents[i])
> +			dev_err(bpmp->dev, "no parent %u found for %u\n",
> +				info->parents[i], info->id);
> +	}
> +
> +	init.parent_names = parents;
> +
> +	clk = clk_register(bpmp->dev, &priv->hw);

IS_ERR?

> +
> +	kfree(parents);
> +
> +	return &priv->hw;
> +}
> +
> +static int tegra_bpmp_register_clocks(struct tegra_bpmp *bpmp,
> +				      struct tegra_bpmp_clk_info *clocks,
> +				      unsigned int count)
> +{
> +	struct clk_hw *hw;
> +	unsigned int i;
> +
> +	bpmp->num_clocks = count;
> +
> +	bpmp->clocks = devm_kcalloc(bpmp->dev, count, sizeof(hw), GFP_KERNEL);
> +	if (!bpmp->clocks)
> +		return -ENOMEM;
> +
> +	for (i = 0; i < count; i++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[i];
> +
> +		hw = tegra_bpmp_clk_register(bpmp, info, clocks, count);
> +		if (IS_ERR(hw)) {
> +			dev_err(bpmp->dev,
> +				"failed to register clock %u (%s): %ld\n",
> +				info->id, info->name, PTR_ERR(hw));
> +			continue;
> +		}
> +
> +		bpmp->clocks[i] = hw;
> +	}
> +
> +	return 0;
> +}
> +
> +static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
> +					      void *data)
> +{
> +	unsigned int id = clkspec->args[0], i;
> +	struct tegra_bpmp *bpmp = data;
> +	struct clk_hw *hw = NULL;
> +
> +	dev_dbg(bpmp->dev, "> %s(clkspec=%p, data=%p)\n", __func__, clkspec,
> +		data);
> +
> +	for (i = 0; i < bpmp->num_clocks; i++) {
> +		struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(bpmp->clocks[i]);
> +
> +		if (clk->id == id) {
> +			hw = bpmp->clocks[i];
> +			dev_dbg(bpmp->dev, "  found %u: %s\n", clk->id, clk_hw_get_name(hw));
> +			break;
> +		}
> +	}
> +
> +	dev_dbg(bpmp->dev, "< %s() = %p\n", __func__, hw);
> +
> +	return hw;
> +}
> +
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
> +{
> +	struct tegra_bpmp_clk_info *clocks;
> +	unsigned int count;
> +	int err;
> +
> +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
> +
> +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
> +	if (err < 0)
> +		return err;
> +
> +	count = err;
> +
> +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
> +
> +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
> +	if (err < 0) {
> +		kfree(clocks);
> +		return err;
> +	}
> +
> +	kfree(clocks);
> +
> +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
> +			       bpmp);

We should check the return code.

> +	dev_dbg(bpmp->dev, "< %s()\n", __func__);
> +	return 0;
> +}
> 

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 04/12] firmware: tegra: Add IVC library
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 10:46         ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 10:46 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
> 
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
> 
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
> 
> Based on work by Peter Newman <pnewman-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
> <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.
> 
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v3:
> - use a more object oriented design
> 
>  drivers/firmware/Kconfig        |   1 +
>  drivers/firmware/Makefile       |   1 +
>  drivers/firmware/tegra/Kconfig  |  13 +
>  drivers/firmware/tegra/Makefile |   1 +
>  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/ivc.h         | 109 +++++++
>  6 files changed, 808 insertions(+)
>  create mode 100644 drivers/firmware/tegra/Kconfig
>  create mode 100644 drivers/firmware/tegra/Makefile
>  create mode 100644 drivers/firmware/tegra/ivc.c
>  create mode 100644 include/soc/tegra/ivc.h

[snip]

> +static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
> +				  struct tegra_ivc_header *header,
> +				  unsigned int frame)
> +{
> +	BUG_ON(frame >= ivc->num_frames);

WARN_ON and return an error pointer?

> +
> +	return (void *)(header + 1) + ivc->frame_size * frame;
> +}
> +
> +static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
> +					      dma_addr_t phys,
> +					      unsigned int frame)
> +{
> +	unsigned long offset;
> +
> +	BUG_ON(!ivc->peer);
> +	BUG_ON(frame >= ivc->num_frames);

WARN_ON?

> +
> +	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
> +
> +	return phys + offset;
> +}

[snip]

> +static int check_ivc_params(unsigned long base1, unsigned long base2,
> +			    unsigned int num_frames, size_t frame_size)
> +{
> +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));

WARN_ON?

> +	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
> +		pr_err("num_frames * frame_size overflows\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * The headers must at least be aligned enough for counters
> +	 * to be accessed atomically.
> +	 */
> +	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base1);
> +		return -EINVAL;
> +	}
> +
> +	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base2);
> +		return -EINVAL;
> +	}
> +
> +	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("frame size not adequately aligned: %zu\n", frame_size);
> +		return -EINVAL;
> +	}
> +
> +	if (base1 < base2) {
> +		if (base1 + frame_size * num_frames > base2) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base1, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (base2 + frame_size * num_frames > base1) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base2, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> +		   unsigned int num_frames, size_t frame_size,
> +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> +		   void *data)
> +{
> +	size_t queue_size;
> +	int err;
> +
> +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> +			       num_frames, frame_size);
> +	if (err < 0)
> +		return err;
> +
> +	BUG_ON(!ivc);
> +	BUG_ON(!notify);

We should check this first and just return -EINVAL.

> +	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
> +
> +	/*
> +	 * All sizes that can be returned by communication functions should
> +	 * fit in an int.
> +	 */
> +	if (frame_size > INT_MAX)
> +		return -E2BIG;
> +
> +	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
> +	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;
> +
> +	if (peer) {
> +		if (rx_phys != DMA_ERROR_CODE) {
> +			ivc->rx.phys = rx_phys;
> +			ivc->tx.phys = tx_phys;
> +		} else {
> +			ivc->rx.phys = dma_map_single(peer, ivc->rx.channel,
> +						      queue_size,
> +						      DMA_BIDIRECTIONAL);
> +			if (ivc->rx.phys == DMA_ERROR_CODE)
> +				return -ENOMEM;
> +
> +			ivc->tx.phys = dma_map_single(peer, ivc->tx.channel,
> +						      queue_size,
> +						      DMA_BIDIRECTIONAL);
> +			if (ivc->tx.phys == DMA_ERROR_CODE) {
> +				dma_unmap_single(peer, ivc->rx.phys,
> +						 queue_size,
> +						 DMA_BIDIRECTIONAL);
> +				return -ENOMEM;
> +			}
> +		}
> +	}
> +
> +	ivc->peer = peer;
> +	ivc->notify = notify;
> +	ivc->notify_data = data;
> +	ivc->frame_size = frame_size;
> +	ivc->num_frames = num_frames;
> +
> +	/*
> +	 * These values aren't necessarily correct until the channel has been
> +	 * reset.
> +	 */
> +	ivc->tx.position = 0;
> +	ivc->rx.position = 0;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(tegra_ivc_init);
> diff --git a/include/soc/tegra/ivc.h b/include/soc/tegra/ivc.h
> new file mode 100644
> index 000000000000..af9a54a54e45
> --- /dev/null
> +++ b/include/soc/tegra/ivc.h
> @@ -0,0 +1,109 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#ifndef __TEGRA_IVC_H
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/types.h>
> +
> +struct tegra_ivc_header;
> +
> +struct tegra_ivc {
> +	struct device *peer;
> +
> +	struct {
> +		struct tegra_ivc_header *channel;
> +		dma_addr_t phys;
> +		u32 position;
> +	} rx, tx;
> +
> +	void (*notify)(struct tegra_ivc *ivc, void *data);
> +	void *notify_data;
> +
> +	unsigned int num_frames;
> +	size_t frame_size;
> +};
> +
> +/**
> + * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
> + * @ivc		pointer of the IVC channel
> + *
> + * Peek at the next frame to be received, without removing it from
> + * the queue.
> + *
> + * Returns a pointer to the frame, or an error encoded pointer.
> + */
> +void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);

Is it odd to return a void * pointer here and not a pointer to a
specific structure type?

> +/**
> + * tegra_ivc_read_advance - Advance the read queue
> + * @ivc		pointer of the IVC channel
> + *
> + * Advance the read queue
> + *
> + * Returns 0, or a negative error value if failed.
> + */
> +int tegra_ivc_read_advance(struct tegra_ivc *ivc);
> +
> +/**
> + * tegra_ivc_write_get_next_frame - Poke at the next frame to transmit
> + * @ivc		pointer of the IVC channel
> + *
> + * Get access to the next frame.
> + *
> + * Returns a pointer to the frame, or an error encoded pointer.
> + */
> +void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc);

Same here.

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
@ 2016-08-22 10:46         ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 10:46 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
> 
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
> 
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
> 
> Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a more object oriented design
> 
>  drivers/firmware/Kconfig        |   1 +
>  drivers/firmware/Makefile       |   1 +
>  drivers/firmware/tegra/Kconfig  |  13 +
>  drivers/firmware/tegra/Makefile |   1 +
>  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/ivc.h         | 109 +++++++
>  6 files changed, 808 insertions(+)
>  create mode 100644 drivers/firmware/tegra/Kconfig
>  create mode 100644 drivers/firmware/tegra/Makefile
>  create mode 100644 drivers/firmware/tegra/ivc.c
>  create mode 100644 include/soc/tegra/ivc.h

[snip]

> +static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
> +				  struct tegra_ivc_header *header,
> +				  unsigned int frame)
> +{
> +	BUG_ON(frame >= ivc->num_frames);

WARN_ON and return an error pointer?

> +
> +	return (void *)(header + 1) + ivc->frame_size * frame;
> +}
> +
> +static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
> +					      dma_addr_t phys,
> +					      unsigned int frame)
> +{
> +	unsigned long offset;
> +
> +	BUG_ON(!ivc->peer);
> +	BUG_ON(frame >= ivc->num_frames);

WARN_ON?

> +
> +	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
> +
> +	return phys + offset;
> +}

[snip]

> +static int check_ivc_params(unsigned long base1, unsigned long base2,
> +			    unsigned int num_frames, size_t frame_size)
> +{
> +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));

WARN_ON?

> +	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
> +		pr_err("num_frames * frame_size overflows\n");
> +		return -EINVAL;
> +	}
> +
> +	/*
> +	 * The headers must at least be aligned enough for counters
> +	 * to be accessed atomically.
> +	 */
> +	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base1);
> +		return -EINVAL;
> +	}
> +
> +	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base2);
> +		return -EINVAL;
> +	}
> +
> +	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("frame size not adequately aligned: %zu\n", frame_size);
> +		return -EINVAL;
> +	}
> +
> +	if (base1 < base2) {
> +		if (base1 + frame_size * num_frames > base2) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base1, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (base2 + frame_size * num_frames > base1) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base2, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> +		   unsigned int num_frames, size_t frame_size,
> +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> +		   void *data)
> +{
> +	size_t queue_size;
> +	int err;
> +
> +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> +			       num_frames, frame_size);
> +	if (err < 0)
> +		return err;
> +
> +	BUG_ON(!ivc);
> +	BUG_ON(!notify);

We should check this first and just return -EINVAL.

> +	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
> +
> +	/*
> +	 * All sizes that can be returned by communication functions should
> +	 * fit in an int.
> +	 */
> +	if (frame_size > INT_MAX)
> +		return -E2BIG;
> +
> +	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
> +	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;
> +
> +	if (peer) {
> +		if (rx_phys != DMA_ERROR_CODE) {
> +			ivc->rx.phys = rx_phys;
> +			ivc->tx.phys = tx_phys;
> +		} else {
> +			ivc->rx.phys = dma_map_single(peer, ivc->rx.channel,
> +						      queue_size,
> +						      DMA_BIDIRECTIONAL);
> +			if (ivc->rx.phys == DMA_ERROR_CODE)
> +				return -ENOMEM;
> +
> +			ivc->tx.phys = dma_map_single(peer, ivc->tx.channel,
> +						      queue_size,
> +						      DMA_BIDIRECTIONAL);
> +			if (ivc->tx.phys == DMA_ERROR_CODE) {
> +				dma_unmap_single(peer, ivc->rx.phys,
> +						 queue_size,
> +						 DMA_BIDIRECTIONAL);
> +				return -ENOMEM;
> +			}
> +		}
> +	}
> +
> +	ivc->peer = peer;
> +	ivc->notify = notify;
> +	ivc->notify_data = data;
> +	ivc->frame_size = frame_size;
> +	ivc->num_frames = num_frames;
> +
> +	/*
> +	 * These values aren't necessarily correct until the channel has been
> +	 * reset.
> +	 */
> +	ivc->tx.position = 0;
> +	ivc->rx.position = 0;
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL(tegra_ivc_init);
> diff --git a/include/soc/tegra/ivc.h b/include/soc/tegra/ivc.h
> new file mode 100644
> index 000000000000..af9a54a54e45
> --- /dev/null
> +++ b/include/soc/tegra/ivc.h
> @@ -0,0 +1,109 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#ifndef __TEGRA_IVC_H
> +
> +#include <linux/device.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/types.h>
> +
> +struct tegra_ivc_header;
> +
> +struct tegra_ivc {
> +	struct device *peer;
> +
> +	struct {
> +		struct tegra_ivc_header *channel;
> +		dma_addr_t phys;
> +		u32 position;
> +	} rx, tx;
> +
> +	void (*notify)(struct tegra_ivc *ivc, void *data);
> +	void *notify_data;
> +
> +	unsigned int num_frames;
> +	size_t frame_size;
> +};
> +
> +/**
> + * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
> + * @ivc		pointer of the IVC channel
> + *
> + * Peek at the next frame to be received, without removing it from
> + * the queue.
> + *
> + * Returns a pointer to the frame, or an error encoded pointer.
> + */
> +void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);

Is it odd to return a void * pointer here and not a pointer to a
specific structure type?

> +/**
> + * tegra_ivc_read_advance - Advance the read queue
> + * @ivc		pointer of the IVC channel
> + *
> + * Advance the read queue
> + *
> + * Returns 0, or a negative error value if failed.
> + */
> +int tegra_ivc_read_advance(struct tegra_ivc *ivc);
> +
> +/**
> + * tegra_ivc_write_get_next_frame - Poke at the next frame to transmit
> + * @ivc		pointer of the IVC channel
> + *
> + * Get access to the next frame.
> + *
> + * Returns a pointer to the frame, or an error encoded pointer.
> + */
> +void *tegra_ivc_write_get_next_frame(struct tegra_ivc *ivc);

Same here.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 04/12] firmware: tegra: Add IVC library
  2016-08-22 10:46         ` Jon Hunter
@ 2016-08-22 12:40             ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 12:40 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 5188 bytes --]

On Mon, Aug 22, 2016 at 11:46:49AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > 
> > The Inter-VM communication (IVC) is a communication protocol which is
> > designed for interprocessor communication (IPC) or the communication
> > between the hypervisor and the virtual machine with a guest OS.
> > 
> > Message channels are used to communicate between processors. They are
> > backed by DRAM or SRAM, so care must be taken to maintain coherence of
> > data.
> > 
> > The IVC library maintains memory-based descriptors for the transmission
> > and reception channels as well as the data coherence of the counter and
> > payload. Clients, such as the driver for the BPMP firmware, can use the
> > library to exchange messages with remote processors.
> > 
> > Based on work by Peter Newman <pnewman-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
> > <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.
> > 
> > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> > Changes in v3:
> > - use a more object oriented design
> > 
> >  drivers/firmware/Kconfig        |   1 +
> >  drivers/firmware/Makefile       |   1 +
> >  drivers/firmware/tegra/Kconfig  |  13 +
> >  drivers/firmware/tegra/Makefile |   1 +
> >  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
> >  include/soc/tegra/ivc.h         | 109 +++++++
> >  6 files changed, 808 insertions(+)
> >  create mode 100644 drivers/firmware/tegra/Kconfig
> >  create mode 100644 drivers/firmware/tegra/Makefile
> >  create mode 100644 drivers/firmware/tegra/ivc.c
> >  create mode 100644 include/soc/tegra/ivc.h
> 
> [snip]
> 
> > +static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
> > +				  struct tegra_ivc_header *header,
> > +				  unsigned int frame)
> > +{
> > +	BUG_ON(frame >= ivc->num_frames);
> 
> WARN_ON and return an error pointer?

I think I'll actually drop these. Or move them one layer up. The only
parameters passed into as frame are ivc->{rx,tx}.position, and all the
code that modifies these will properly wrap them at ivc->num_frames. I
think the only way that this condition could become true is if someone
were to directly access the structure and modify the position. That's
technically possible, so I guess the checks could stay in for extra
paranoia.

> > +
> > +	return (void *)(header + 1) + ivc->frame_size * frame;
> > +}
> > +
> > +static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
> > +					      dma_addr_t phys,
> > +					      unsigned int frame)
> > +{
> > +	unsigned long offset;
> > +
> > +	BUG_ON(!ivc->peer);
> > +	BUG_ON(frame >= ivc->num_frames);
> 
> WARN_ON?

I've moved this up one layer since it's a little cumbersome to return an
error via dma_addr_t and the !ivc->peer check is present in all callers
of this function anyway.

> > +	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
> > +
> > +	return phys + offset;
> > +}
> 
> [snip]
> 
> > +static int check_ivc_params(unsigned long base1, unsigned long base2,
> > +			    unsigned int num_frames, size_t frame_size)
> > +{
> > +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> > +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> > +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
> 
> WARN_ON?

I've turned all of these into BUILD_BUG_ON() because the parameters are
all statically known at build time. I've also switched to the IS_ALIGNED
macro here and the checks below because it's easier to read.

> > +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> > +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> > +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> > +		   unsigned int num_frames, size_t frame_size,
> > +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> > +		   void *data)
> > +{
> > +	size_t queue_size;
> > +	int err;
> > +
> > +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> > +			       num_frames, frame_size);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	BUG_ON(!ivc);
> > +	BUG_ON(!notify);
> 
> We should check this first and just return -EINVAL.

Yes, done. I've wrapped these in a single WARN_ON() with a -EINVAL
return.

> > +/**
> > + * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
> > + * @ivc		pointer of the IVC channel
> > + *
> > + * Peek at the next frame to be received, without removing it from
> > + * the queue.
> > + *
> > + * Returns a pointer to the frame, or an error encoded pointer.
> > + */
> > +void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);
> 
> Is it odd to return a void * pointer here and not a pointer to a
> specific structure type?

I think that's by design. IVC is a generic library to implement an IPC
mechanism on top. There is no specific structure to return a pointer to
here. The caller determines what type it wants to put into frames.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
@ 2016-08-22 12:40             ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 12:40 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 11:46:49AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > The Inter-VM communication (IVC) is a communication protocol which is
> > designed for interprocessor communication (IPC) or the communication
> > between the hypervisor and the virtual machine with a guest OS.
> > 
> > Message channels are used to communicate between processors. They are
> > backed by DRAM or SRAM, so care must be taken to maintain coherence of
> > data.
> > 
> > The IVC library maintains memory-based descriptors for the transmission
> > and reception channels as well as the data coherence of the counter and
> > payload. Clients, such as the driver for the BPMP firmware, can use the
> > library to exchange messages with remote processors.
> > 
> > Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
> > <josephl@nvidia.com>.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> > Changes in v3:
> > - use a more object oriented design
> > 
> >  drivers/firmware/Kconfig        |   1 +
> >  drivers/firmware/Makefile       |   1 +
> >  drivers/firmware/tegra/Kconfig  |  13 +
> >  drivers/firmware/tegra/Makefile |   1 +
> >  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
> >  include/soc/tegra/ivc.h         | 109 +++++++
> >  6 files changed, 808 insertions(+)
> >  create mode 100644 drivers/firmware/tegra/Kconfig
> >  create mode 100644 drivers/firmware/tegra/Makefile
> >  create mode 100644 drivers/firmware/tegra/ivc.c
> >  create mode 100644 include/soc/tegra/ivc.h
> 
> [snip]
> 
> > +static void *tegra_ivc_frame_virt(struct tegra_ivc *ivc,
> > +				  struct tegra_ivc_header *header,
> > +				  unsigned int frame)
> > +{
> > +	BUG_ON(frame >= ivc->num_frames);
> 
> WARN_ON and return an error pointer?

I think I'll actually drop these. Or move them one layer up. The only
parameters passed into as frame are ivc->{rx,tx}.position, and all the
code that modifies these will properly wrap them at ivc->num_frames. I
think the only way that this condition could become true is if someone
were to directly access the structure and modify the position. That's
technically possible, so I guess the checks could stay in for extra
paranoia.

> > +
> > +	return (void *)(header + 1) + ivc->frame_size * frame;
> > +}
> > +
> > +static inline dma_addr_t tegra_ivc_frame_phys(struct tegra_ivc *ivc,
> > +					      dma_addr_t phys,
> > +					      unsigned int frame)
> > +{
> > +	unsigned long offset;
> > +
> > +	BUG_ON(!ivc->peer);
> > +	BUG_ON(frame >= ivc->num_frames);
> 
> WARN_ON?

I've moved this up one layer since it's a little cumbersome to return an
error via dma_addr_t and the !ivc->peer check is present in all callers
of this function anyway.

> > +	offset = sizeof(struct tegra_ivc_header) + ivc->frame_size * frame;
> > +
> > +	return phys + offset;
> > +}
> 
> [snip]
> 
> > +static int check_ivc_params(unsigned long base1, unsigned long base2,
> > +			    unsigned int num_frames, size_t frame_size)
> > +{
> > +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> > +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> > +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
> 
> WARN_ON?

I've turned all of these into BUILD_BUG_ON() because the parameters are
all statically known at build time. I've also switched to the IS_ALIGNED
macro here and the checks below because it's easier to read.

> > +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> > +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> > +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> > +		   unsigned int num_frames, size_t frame_size,
> > +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> > +		   void *data)
> > +{
> > +	size_t queue_size;
> > +	int err;
> > +
> > +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> > +			       num_frames, frame_size);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	BUG_ON(!ivc);
> > +	BUG_ON(!notify);
> 
> We should check this first and just return -EINVAL.

Yes, done. I've wrapped these in a single WARN_ON() with a -EINVAL
return.

> > +/**
> > + * tegra_ivc_read_get_next_frame - Peek at the next frame to receive
> > + * @ivc		pointer of the IVC channel
> > + *
> > + * Peek at the next frame to be received, without removing it from
> > + * the queue.
> > + *
> > + * Returns a pointer to the frame, or an error encoded pointer.
> > + */
> > +void *tegra_ivc_read_get_next_frame(struct tegra_ivc *ivc);
> 
> Is it odd to return a void * pointer here and not a pointer to a
> specific structure type?

I think that's by design. IVC is a generic library to implement an IPC
mechanism on top. There is no specific structure to return a pointer to
here. The caller determines what type it wants to put into frames.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/663fe482/attachment-0001.sig>

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22  9:26         ` Jon Hunter
@ 2016-08-22 12:54             ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 12:54 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 6048 bytes --]

On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > 
> > The Boot and Power Management Processor (BPMP) is a co-processor found
> > on Tegra SoCs. It is designed to handle the early stages of the boot
> > process and offload power management tasks (such as clocks, resets,
> > powergates, ...) as well as system control services.
> > 
> > Compared to the ARM SCPI, the services provided by BPMP are message-
> > based rather than method-based. The BPMP firmware driver provides the
> > services to transmit data to and receive data from the BPMP. Users can
> > also register an MRQ, for which a service routine will be run when a
> > corresponding event is received from the firmware.
> 
> MRQ?

I think that means "Message ReQuest", which is sort of like an IRQ but
the user will receive a message (with potentially payload) instead. Do
you want me to spell that out in the commit message, or what would you
suggest?

> > diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> > index 92e2153e8173..e34a2f79e1ad 100644
> > --- a/drivers/firmware/tegra/Makefile
> > +++ b/drivers/firmware/tegra/Makefile
> > @@ -1 +1,2 @@
> > +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
> >  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> > diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> > new file mode 100644
> > index 000000000000..a09043b1be05
> > --- /dev/null
> > +++ b/drivers/firmware/tegra/bpmp.c
> > @@ -0,0 +1,880 @@
> > +/*
> > + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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.
> > + */
> > +
> > +#define DEBUG
> 
> I don't think we want DEBUG by default, right?

Yes, that's left-over from debugging.

> > +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> > +{
> > +	struct mrq_ping_response response;
> > +	struct mrq_ping_request request;
> > +	struct tegra_bpmp_message msg;
> > +	ktime_t start, delta;
> > +	unsigned long flags;
> > +	int err;
> > +
> > +	memset(&request, 0, sizeof(request));
> > +	request.challenge = 1;
> > +
> > +	memset(&response, 0, sizeof(response));
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.mrq = MRQ_PING;
> > +	msg.tx.data = &request;
> > +	msg.tx.size = sizeof(request);
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	start = ktime_get();
> > +
> > +	local_irq_save(flags);
> > +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> > +	local_irq_restore(flags);
> > +
> > +	delta = ktime_sub(ktime_get(), start);
> > +
> > +	if (!err)
> > +		dev_info(bpmp->dev,
> > +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> > +			 request.challenge, response.reply,
> > +			 ktime_to_us(delta));
> 
> Should this be a dev_dbg? I guess this only happens on probe.

I suppose you could use this anywhere else, too, just to check that the
BPMP is still responding. But yes, I think making this DEBUG level will
be enough.

> 
> > +	return err;
> > +}
> 
> [snip]
> 
> > +static int tegra_bpmp_probe(struct platform_device *pdev)
> > +{
> > +	struct tegra_bpmp_channel *channel;
> > +	struct tegra_bpmp *bpmp;
> > +	struct device_node *np;
> > +	struct resource res;
> > +	unsigned int i;
> > +	char tag[32];
> > +	size_t size;
> > +	int err;
> > +
> > +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> > +	if (!bpmp)
> > +		return -ENOMEM;
> > +
> > +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> > +	bpmp->dev = &pdev->dev;
> > +
> > +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> > +	if (!np)
> > +		return -ENOENT;
> > +
> > +	of_address_to_resource(np, 0, &res);
> > +	of_node_put(np);
> > +
> > +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> > +	if (IS_ERR(bpmp->tx_base))
> > +		return PTR_ERR(bpmp->tx_base);
> > +
> > +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> > +	if (!np)
> > +		return -ENOENT;
> > +
> > +	of_address_to_resource(np, 0, &res);
> > +	of_node_put(np);
> > +
> > +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> > +	if (IS_ERR(bpmp->rx_base))
> > +		return PTR_ERR(bpmp->rx_base);
> > +
> > +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> > +			     bpmp->soc->channels.thread.count +
> > +			     bpmp->soc->channels.cpu_rx.count;
> > +
> > +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> > +				      sizeof(*channel), GFP_KERNEL);
> > +	if (!bpmp->channels)
> > +		return -ENOMEM;
> > +
> > +	/* mbox registration */
> > +	bpmp->mbox.client.dev = &pdev->dev;
> > +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> > +	bpmp->mbox.client.tx_block = false;
> > +	bpmp->mbox.client.knows_txdone = false;
> > +
> > +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> > +	if (IS_ERR(bpmp->mbox.channel)) {
> > +		err = PTR_ERR(bpmp->mbox.channel);
> > +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> > +		return err;
> > +	}
> > +
> > +	/* message channel initialization */
> > +	for (i = 0; i < bpmp->num_channels; i++) {
> > +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> > +
> > +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> > +		if (err)
> > +			return err;
> > +	}
> 
> We should make sure we free the mbox if we fail after requesting it.

Yes, will do.

Thanks,
Thierry

> 
> Cheers
> Jon
> 
> -- 
> nvpublic

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 12:54             ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 12:54 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > The Boot and Power Management Processor (BPMP) is a co-processor found
> > on Tegra SoCs. It is designed to handle the early stages of the boot
> > process and offload power management tasks (such as clocks, resets,
> > powergates, ...) as well as system control services.
> > 
> > Compared to the ARM SCPI, the services provided by BPMP are message-
> > based rather than method-based. The BPMP firmware driver provides the
> > services to transmit data to and receive data from the BPMP. Users can
> > also register an MRQ, for which a service routine will be run when a
> > corresponding event is received from the firmware.
> 
> MRQ?

I think that means "Message ReQuest", which is sort of like an IRQ but
the user will receive a message (with potentially payload) instead. Do
you want me to spell that out in the commit message, or what would you
suggest?

> > diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> > index 92e2153e8173..e34a2f79e1ad 100644
> > --- a/drivers/firmware/tegra/Makefile
> > +++ b/drivers/firmware/tegra/Makefile
> > @@ -1 +1,2 @@
> > +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
> >  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> > diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> > new file mode 100644
> > index 000000000000..a09043b1be05
> > --- /dev/null
> > +++ b/drivers/firmware/tegra/bpmp.c
> > @@ -0,0 +1,880 @@
> > +/*
> > + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> > + *
> > + * This program is free software; you can redistribute it and/or modify it
> > + * under the terms and conditions of the GNU General Public License,
> > + * version 2, as published by the Free Software Foundation.
> > + *
> > + * This program is distributed in the hope 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.
> > + */
> > +
> > +#define DEBUG
> 
> I don't think we want DEBUG by default, right?

Yes, that's left-over from debugging.

> > +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> > +{
> > +	struct mrq_ping_response response;
> > +	struct mrq_ping_request request;
> > +	struct tegra_bpmp_message msg;
> > +	ktime_t start, delta;
> > +	unsigned long flags;
> > +	int err;
> > +
> > +	memset(&request, 0, sizeof(request));
> > +	request.challenge = 1;
> > +
> > +	memset(&response, 0, sizeof(response));
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.mrq = MRQ_PING;
> > +	msg.tx.data = &request;
> > +	msg.tx.size = sizeof(request);
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	start = ktime_get();
> > +
> > +	local_irq_save(flags);
> > +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> > +	local_irq_restore(flags);
> > +
> > +	delta = ktime_sub(ktime_get(), start);
> > +
> > +	if (!err)
> > +		dev_info(bpmp->dev,
> > +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> > +			 request.challenge, response.reply,
> > +			 ktime_to_us(delta));
> 
> Should this be a dev_dbg? I guess this only happens on probe.

I suppose you could use this anywhere else, too, just to check that the
BPMP is still responding. But yes, I think making this DEBUG level will
be enough.

> 
> > +	return err;
> > +}
> 
> [snip]
> 
> > +static int tegra_bpmp_probe(struct platform_device *pdev)
> > +{
> > +	struct tegra_bpmp_channel *channel;
> > +	struct tegra_bpmp *bpmp;
> > +	struct device_node *np;
> > +	struct resource res;
> > +	unsigned int i;
> > +	char tag[32];
> > +	size_t size;
> > +	int err;
> > +
> > +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> > +	if (!bpmp)
> > +		return -ENOMEM;
> > +
> > +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> > +	bpmp->dev = &pdev->dev;
> > +
> > +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> > +	if (!np)
> > +		return -ENOENT;
> > +
> > +	of_address_to_resource(np, 0, &res);
> > +	of_node_put(np);
> > +
> > +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> > +	if (IS_ERR(bpmp->tx_base))
> > +		return PTR_ERR(bpmp->tx_base);
> > +
> > +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> > +	if (!np)
> > +		return -ENOENT;
> > +
> > +	of_address_to_resource(np, 0, &res);
> > +	of_node_put(np);
> > +
> > +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> > +	if (IS_ERR(bpmp->rx_base))
> > +		return PTR_ERR(bpmp->rx_base);
> > +
> > +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> > +			     bpmp->soc->channels.thread.count +
> > +			     bpmp->soc->channels.cpu_rx.count;
> > +
> > +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> > +				      sizeof(*channel), GFP_KERNEL);
> > +	if (!bpmp->channels)
> > +		return -ENOMEM;
> > +
> > +	/* mbox registration */
> > +	bpmp->mbox.client.dev = &pdev->dev;
> > +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> > +	bpmp->mbox.client.tx_block = false;
> > +	bpmp->mbox.client.knows_txdone = false;
> > +
> > +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> > +	if (IS_ERR(bpmp->mbox.channel)) {
> > +		err = PTR_ERR(bpmp->mbox.channel);
> > +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> > +		return err;
> > +	}
> > +
> > +	/* message channel initialization */
> > +	for (i = 0; i < bpmp->num_channels; i++) {
> > +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> > +
> > +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> > +		if (err)
> > +			return err;
> > +	}
> 
> We should make sure we free the mbox if we fail after requesting it.

Yes, will do.

Thanks,
Thierry

> 
> Cheers
> Jon
> 
> -- 
> nvpublic
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/d8c5926a/attachment.sig>

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

* Re: [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
  2016-08-22 10:11         ` Jon Hunter
@ 2016-08-22 13:28             ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 13:28 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 9423 bytes --]

On Mon, Aug 22, 2016 at 11:11:57AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > 
> > This driver uses the services provided by the BPMP firmware driver to
> > implement a clock driver based on the MRQ_CLK request. This part of the
> > BPMP ABI provides a means to enumerate and control clocks and should
> > allow the driver to work on any chip that supports this ABI.
> > 
> > Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > ---
> >  drivers/clk/tegra/Makefile   |   1 +
> >  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 566 insertions(+)
> >  create mode 100644 drivers/clk/tegra/clk-bpmp.c
> > 
> > diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> > index 33fd0938d79e..130df5685d21 100644
> > --- a/drivers/clk/tegra/Makefile
> > +++ b/drivers/clk/tegra/Makefile
> > @@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
> >  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
> >  obj-y					+= cvb.o
> >  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> > +obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
> > diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
> > new file mode 100644
> > index 000000000000..96cd6becf73e
> > --- /dev/null
> > +++ b/drivers/clk/tegra/clk-bpmp.c
> > @@ -0,0 +1,565 @@
> > +#define DEBUG
> 
> Do we want DEBUG by default?

That was left-over from debugging. I've removed it.

> > +static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
> > +{
> > +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> > +	struct cmd_clk_get_parent_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_PARENT;
> > +	msg.clk = clk->id;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(&response);
> > +
> > +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> 
> tegra_bpmp_clk_transfer returns an int, but this function returns a u8.

I've added an error message containing the error code and made this
return U8_MAX.

> > +
> > +	for (i = 0; i < clk->num_parents; i++)
> > +		if (clk->parents[i] == response.parent_id)
> > +			return i;
> > +
> > +	return U8_MAX;
> 
> Is there any chance the U8_MAX == num_parents? Should we warn here
> somewhere?

I've added a check to the function registration code that will refuse to
add clocks with num_parents >= U8_MAX. This should never happen because
the MRQ_CLK_MAX_PARENTS is the upper bound for this, and it's currently
very small.

> > +static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
> > +{
> > +	struct cmd_clk_get_max_clk_id_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return response.max_id;
> 
> response.max_id is a uint32

I've added a check to make sure this doesn't overflow.

> > +}
> > +
> > +static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
> > +				   struct tegra_bpmp_clk_info *info)
> > +{
> > +	struct cmd_clk_get_all_info_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_ALL_INFO;
> > +	msg.clk = id;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
> > +	info->num_parents = response.num_parents;
> > +
> > +	for (i = 0; i < info->num_parents; i++)
> > +		info->parents[i] = response.parents[i];
> > +
> > +	info->flags = response.flags;
> > +
> > +	return 0;
> > +}
> > +
> > +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> > +				   struct tegra_bpmp_clk_info **clocksp)
> > +{
> > +	struct tegra_bpmp_clk_info *clocks;
> > +	unsigned int max_id, id, count = 0;
> > +	int err;
> > +
> > +	err = tegra_bpmp_clk_get_max_id(bpmp);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	max_id = err;
> > +
> > +	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
> > +
> > +	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
> > +	if (!clocks)
> > +		return -ENOMEM;
> > +
> > +	for (id = 0; id <= max_id; id++) {
> > +		struct tegra_bpmp_clk_info *info = &clocks[count];
> > +#if 0
> > +		const char *prefix = "";
> > +		struct seq_buf buf;
> > +		unsigned int i;
> > +		char flags[64];
> > +#endif
> > +
> > +		err = tegra_bpmp_clk_get_info(bpmp, id, info);
> > +		if (err < 0) {
> > +			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
> > +				id, err);
> > +			continue;
> > +		}
> > +
> > +#if 0
> > +		seq_buf_init(&buf, flags, sizeof(flags));
> > +
> > +		if (info->flags)
> > +			seq_buf_printf(&buf, "(");
> > +
> > +		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> > +			seq_buf_printf(&buf, "%smux", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
> > +			seq_buf_printf(&buf, "%sfixed", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
> > +			seq_buf_printf(&buf, "%sroot", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if (info->flags)
> > +			seq_buf_printf(&buf, ")");
> > +
> > +		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
> > +		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
> > +		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
> > +
> > +		for (i = 0; i < info->num_parents; i++)
> > +			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
> > +#endif
> > +
> > +		/* clock not exposed by BPMP */
> > +		if (info->name[0] == '\0')
> > +			continue;
> > +
> > +		info->id = id;
> > +		count++;
> > +	}
> > +
> > +	*clocksp = clocks;
> 
> Nit we could just return the pointer.
> 
> > +
> > +	return count;
> 
> We return unsigned int here and not int. Why do we bother returning
> count and just store it directly in the bpmp->num_clocks here (see
> tegra_bpmp_register_clocks)?

Hm... I'm not sure but I think I had initially imagined this to be used
in two steps: call tegra_bpmp_probe_clocks() without passing in a clocks
buffer to get a count for how many clocks to allocate, allocate a buffer
with the proper size in the caller and call again, this time passing in
the new buffer. It looks like halfway through the code I changed my mind
and it's no longer consistent with what I had intended originally.

I'll rewrite this so that it (hopefully) makes more sense.

> > +	for (i = 0; i < info->num_parents; i++) {
> > +		/* keep a private copy of the ID to parent index map */
> > +		priv->parents[i] = info->parents[i];
> > +
> > +		for (j = 0; j < num_clocks; j++) {
> > +			const struct tegra_bpmp_clk_info *parent = &clocks[j];
> > +
> > +			if (parent->id == info->parents[i]) {
> > +				parents[i] = parent->name;
> > +				break;
> > +			}
> > +		}
> 
> Is it necessary to loop through all the clocks again here? This function
> is called from tegra_bpmp_register_clocks() which is already looping
> through all the clocks. So for each clock we loop through all the clocks
> again.

Yes, it's unfortunately necessary because clocks aren't topologically
sorted. That is, in the earlier loop we may encounter clocks for which a
parent hasn't been probed yet. That's not a problem for the common clock
framework because it supports orphan clocks and reparenting when their
parent becomes available. However, given that the driver queries all the
clock names from BPMP, we first need to get all names before we can save
the parent names for the CCFs consumption.

> 
> > +		if (!parents[i])
> > +			dev_err(bpmp->dev, "no parent %u found for %u\n",
> > +				info->parents[i], info->id);
> > +	}
> > +
> > +	init.parent_names = parents;
> > +
> > +	clk = clk_register(bpmp->dev, &priv->hw);
> 
> IS_ERR?

Yes, I've added a check.

> > +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
> > +{
> > +	struct tegra_bpmp_clk_info *clocks;
> > +	unsigned int count;
> > +	int err;
> > +
> > +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
> > +
> > +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	count = err;
> > +
> > +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
> > +
> > +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
> > +	if (err < 0) {
> > +		kfree(clocks);
> > +		return err;
> > +	}
> > +
> > +	kfree(clocks);
> > +
> > +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
> > +			       bpmp);
> 
> We should check the return code.

Yes, I suppose we should also make sure to remove all clocks if this
ever fails. I'll see how difficult it is to implement that.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
@ 2016-08-22 13:28             ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 13:28 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 11:11:57AM +0100, Jon Hunter wrote:
> 
> On 19/08/16 18:32, Thierry Reding wrote:
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > This driver uses the services provided by the BPMP firmware driver to
> > implement a clock driver based on the MRQ_CLK request. This part of the
> > BPMP ABI provides a means to enumerate and control clocks and should
> > allow the driver to work on any chip that supports this ABI.
> > 
> > Signed-off-by: Thierry Reding <treding@nvidia.com>
> > ---
> >  drivers/clk/tegra/Makefile   |   1 +
> >  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
> >  2 files changed, 566 insertions(+)
> >  create mode 100644 drivers/clk/tegra/clk-bpmp.c
> > 
> > diff --git a/drivers/clk/tegra/Makefile b/drivers/clk/tegra/Makefile
> > index 33fd0938d79e..130df5685d21 100644
> > --- a/drivers/clk/tegra/Makefile
> > +++ b/drivers/clk/tegra/Makefile
> > @@ -22,3 +22,4 @@ obj-$(CONFIG_ARCH_TEGRA_124_SOC)	+= clk-tegra124-dfll-fcpu.o
> >  obj-$(CONFIG_ARCH_TEGRA_132_SOC)	+= clk-tegra124.o
> >  obj-y					+= cvb.o
> >  obj-$(CONFIG_ARCH_TEGRA_210_SOC)	+= clk-tegra210.o
> > +obj-$(CONFIG_ARCH_TEGRA_186_SOC)	+= clk-bpmp.o
> > diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c
> > new file mode 100644
> > index 000000000000..96cd6becf73e
> > --- /dev/null
> > +++ b/drivers/clk/tegra/clk-bpmp.c
> > @@ -0,0 +1,565 @@
> > +#define DEBUG
> 
> Do we want DEBUG by default?

That was left-over from debugging. I've removed it.

> > +static u8 tegra_bpmp_clk_get_parent(struct clk_hw *hw)
> > +{
> > +	struct tegra_bpmp_clk *clk = to_tegra_bpmp_clk(hw);
> > +	struct cmd_clk_get_parent_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_PARENT;
> > +	msg.clk = clk->id;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(&response);
> > +
> > +	err = tegra_bpmp_clk_transfer(clk->bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> 
> tegra_bpmp_clk_transfer returns an int, but this function returns a u8.

I've added an error message containing the error code and made this
return U8_MAX.

> > +
> > +	for (i = 0; i < clk->num_parents; i++)
> > +		if (clk->parents[i] == response.parent_id)
> > +			return i;
> > +
> > +	return U8_MAX;
> 
> Is there any chance the U8_MAX == num_parents? Should we warn here
> somewhere?

I've added a check to the function registration code that will refuse to
add clocks with num_parents >= U8_MAX. This should never happen because
the MRQ_CLK_MAX_PARENTS is the upper bound for this, and it's currently
very small.

> > +static int tegra_bpmp_clk_get_max_id(struct tegra_bpmp *bpmp)
> > +{
> > +	struct cmd_clk_get_max_clk_id_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_MAX_CLK_ID;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	return response.max_id;
> 
> response.max_id is a uint32

I've added a check to make sure this doesn't overflow.

> > +}
> > +
> > +static int tegra_bpmp_clk_get_info(struct tegra_bpmp *bpmp, unsigned int id,
> > +				   struct tegra_bpmp_clk_info *info)
> > +{
> > +	struct cmd_clk_get_all_info_response response;
> > +	struct tegra_bpmp_clk_message msg;
> > +	unsigned int i;
> > +	int err;
> > +
> > +	memset(&msg, 0, sizeof(msg));
> > +	msg.cmd = CMD_CLK_GET_ALL_INFO;
> > +	msg.clk = id;
> > +	msg.rx.data = &response;
> > +	msg.rx.size = sizeof(response);
> > +
> > +	err = tegra_bpmp_clk_transfer(bpmp, &msg);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	strlcpy(info->name, response.name, MRQ_CLK_NAME_MAXLEN);
> > +	info->num_parents = response.num_parents;
> > +
> > +	for (i = 0; i < info->num_parents; i++)
> > +		info->parents[i] = response.parents[i];
> > +
> > +	info->flags = response.flags;
> > +
> > +	return 0;
> > +}
> > +
> > +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> > +				   struct tegra_bpmp_clk_info **clocksp)
> > +{
> > +	struct tegra_bpmp_clk_info *clocks;
> > +	unsigned int max_id, id, count = 0;
> > +	int err;
> > +
> > +	err = tegra_bpmp_clk_get_max_id(bpmp);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	max_id = err;
> > +
> > +	dev_dbg(bpmp->dev, "maximum clock ID: %u\n", max_id);
> > +
> > +	clocks = kcalloc(max_id + 1, sizeof(*clocks), GFP_KERNEL);
> > +	if (!clocks)
> > +		return -ENOMEM;
> > +
> > +	for (id = 0; id <= max_id; id++) {
> > +		struct tegra_bpmp_clk_info *info = &clocks[count];
> > +#if 0
> > +		const char *prefix = "";
> > +		struct seq_buf buf;
> > +		unsigned int i;
> > +		char flags[64];
> > +#endif
> > +
> > +		err = tegra_bpmp_clk_get_info(bpmp, id, info);
> > +		if (err < 0) {
> > +			dev_err(bpmp->dev, "failed to query clock %u: %d\n",
> > +				id, err);
> > +			continue;
> > +		}
> > +
> > +#if 0
> > +		seq_buf_init(&buf, flags, sizeof(flags));
> > +
> > +		if (info->flags)
> > +			seq_buf_printf(&buf, "(");
> > +
> > +		if (info->flags & TEGRA_BPMP_CLK_HAS_MUX) {
> > +			seq_buf_printf(&buf, "%smux", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if ((info->flags & TEGRA_BPMP_CLK_HAS_SET_RATE) == 0) {
> > +			seq_buf_printf(&buf, "%sfixed", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if (info->flags & TEGRA_BPMP_CLK_IS_ROOT) {
> > +			seq_buf_printf(&buf, "%sroot", prefix);
> > +			prefix = ", ";
> > +		}
> > +
> > +		if (info->flags)
> > +			seq_buf_printf(&buf, ")");
> > +
> > +		dev_dbg(bpmp->dev, "  %03u: %s\n", id, info->name);
> > +		dev_dbg(bpmp->dev, "    flags: %lx %s\n", info->flags, flags);
> > +		dev_dbg(bpmp->dev, "    parents: %u\n", info->num_parents);
> > +
> > +		for (i = 0; i < info->num_parents; i++)
> > +			dev_dbg(bpmp->dev, "      %03u\n", info->parents[i]);
> > +#endif
> > +
> > +		/* clock not exposed by BPMP */
> > +		if (info->name[0] == '\0')
> > +			continue;
> > +
> > +		info->id = id;
> > +		count++;
> > +	}
> > +
> > +	*clocksp = clocks;
> 
> Nit we could just return the pointer.
> 
> > +
> > +	return count;
> 
> We return unsigned int here and not int. Why do we bother returning
> count and just store it directly in the bpmp->num_clocks here (see
> tegra_bpmp_register_clocks)?

Hm... I'm not sure but I think I had initially imagined this to be used
in two steps: call tegra_bpmp_probe_clocks() without passing in a clocks
buffer to get a count for how many clocks to allocate, allocate a buffer
with the proper size in the caller and call again, this time passing in
the new buffer. It looks like halfway through the code I changed my mind
and it's no longer consistent with what I had intended originally.

I'll rewrite this so that it (hopefully) makes more sense.

> > +	for (i = 0; i < info->num_parents; i++) {
> > +		/* keep a private copy of the ID to parent index map */
> > +		priv->parents[i] = info->parents[i];
> > +
> > +		for (j = 0; j < num_clocks; j++) {
> > +			const struct tegra_bpmp_clk_info *parent = &clocks[j];
> > +
> > +			if (parent->id == info->parents[i]) {
> > +				parents[i] = parent->name;
> > +				break;
> > +			}
> > +		}
> 
> Is it necessary to loop through all the clocks again here? This function
> is called from tegra_bpmp_register_clocks() which is already looping
> through all the clocks. So for each clock we loop through all the clocks
> again.

Yes, it's unfortunately necessary because clocks aren't topologically
sorted. That is, in the earlier loop we may encounter clocks for which a
parent hasn't been probed yet. That's not a problem for the common clock
framework because it supports orphan clocks and reparenting when their
parent becomes available. However, given that the driver queries all the
clock names from BPMP, we first need to get all names before we can save
the parent names for the CCFs consumption.

> 
> > +		if (!parents[i])
> > +			dev_err(bpmp->dev, "no parent %u found for %u\n",
> > +				info->parents[i], info->id);
> > +	}
> > +
> > +	init.parent_names = parents;
> > +
> > +	clk = clk_register(bpmp->dev, &priv->hw);
> 
> IS_ERR?

Yes, I've added a check.

> > +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
> > +{
> > +	struct tegra_bpmp_clk_info *clocks;
> > +	unsigned int count;
> > +	int err;
> > +
> > +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
> > +
> > +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
> > +	if (err < 0)
> > +		return err;
> > +
> > +	count = err;
> > +
> > +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
> > +
> > +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
> > +	if (err < 0) {
> > +		kfree(clocks);
> > +		return err;
> > +	}
> > +
> > +	kfree(clocks);
> > +
> > +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
> > +			       bpmp);
> 
> We should check the return code.

Yes, I suppose we should also make sure to remove all clocks if this
ever fails. I'll see how difficult it is to implement that.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/c063c82c/attachment.sig>

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 13:34         ` Arnd Bergmann
  -1 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 13:34 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ib = NULL;
> +		return false;
> +	}
> +
> +	channel->ib = frame;
> +
> +	return true;
> +}

Something is wrong with your API if you need IS_ERR_OR_NULL(). If you
can return NULL, use that for all error. Alternatively make sure
that you never return NULL 

> +
> +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_acked(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}

local_clock() is not guaranteed to be in nanoseconds, why not use
ktime_get() instead?

ktime_us_delta() is a bit slow because of the 64-bit division,
you could multiply timeout by NSEC_PER_USEC instead and do a
straight comparison.


	ktime_t end = ktime_add_us(ktime_get(), timeout);
	do {
		...
	} while (ktime_before(ktime_get(), end);

> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> new file mode 100644
> index 000000000000..0aaef5960e29
> --- /dev/null
> +++ b/include/soc/tegra/bpmp-abi.h
> +#ifndef _ABI_BPMP_ABI_H_
> +#define _ABI_BPMP_ABI_H_
> +
> +#ifdef LK
> +#include <stdint.h>
> +#endif
> +
> +#ifndef __ABI_PACKED
> +#define __ABI_PACKED __attribute__((packed))
> +#endif
> +
> +#ifdef NO_GCC_EXTENSIONS
> +#define EMPTY char empty;
> +#define EMPTY_ARRAY 1
> +#else
> +#define EMPTY
> +#define EMPTY_ARRAY 0
> +#endif
> +
> +#ifndef __UNION_ANON
> +#define __UNION_ANON
> +#endif

Maybe keep these all out of the kernel?

> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ message
> + *
> + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> + * the MRQ message is a payload (immediately following the
> + * mrq_request) whose format depends on mrq.
> + *
> + * @todo document the flags
> + */

What's the deal with the odd documentation format?

> +struct mrq_request {
> +	/** @brief MRQ number of the request */
> +	uint32_t mrq;
> +	/** @brief flags for the request */
> +	uint32_t flags;
> +} __ABI_PACKED;

Marking the structure as packed may result in byte-wise access, depending
on compiler flags. Is that what you intended? The structure is fully
packed already, so you won't avoid any padding here.

> +/**
> + * @addtogroup Debugfs
> + * @{
> + *
> + * The BPMP firmware implements a pseudo-filesystem called
> + * debugfs. Any driver within the firmware may register with debugfs
> + * to expose an arbitrary set of "files" in the filesystem. When
> + * software on the CPU writes to a debugfs file, debugfs passes the
> + * written data to a callback provided by the driver. When software on
> + * the CPU reads a debugfs file, debugfs queries the driver for the
> + * data to return to the CPU. The intention of the debugfs filesystem
> + * is to provide information useful for debugging the system at
> + * runtime.
> + *
> + * @note The files exposed via debugfs are not part of the
> + * BPMP firmware's ABI. debugfs files may be added or removed in any
> + * given version of the firmware. Typically the semantics of a debugfs
> + * file are consistent from version to version but even that is not
> + * guaranteed.
> + *
> + * @}
> + */
> +/** @ingroup Debugfs */
> +enum mrq_debugfs_commands {
> +	CMD_DEBUGFS_READ = 1,
> +	CMD_DEBUGFS_WRITE = 2,
> +	CMD_DEBUGFS_DUMPDIR = 3,
> +	CMD_DEBUGFS_MAX
> +};
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_request {
> +	/** @brief physical address pointing at filename */
> +	uint32_t fnameaddr;
> +	/** @brief length in bytes of filename buffer */
> +	uint32_t fnamelen;
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
>

If the ABI is version specific, maybe add the firmware version name
to the structure definition?

> +struct cmd_clk_set_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;

This structure actually has a non-aligned struct member, but you
can write that as

struct cmd_clk_set_rate_request {
	int32_t unused;
	int64_t rate;
} __attribute__((packed, aligned(4)));

to still use a default four-byte alignment.

	Arnd

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 13:34         ` Arnd Bergmann
  0 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 13:34 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ib = NULL;
> +		return false;
> +	}
> +
> +	channel->ib = frame;
> +
> +	return true;
> +}

Something is wrong with your API if you need IS_ERR_OR_NULL(). If you
can return NULL, use that for all error. Alternatively make sure
that you never return NULL 

> +
> +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_acked(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}

local_clock() is not guaranteed to be in nanoseconds, why not use
ktime_get() instead?

ktime_us_delta() is a bit slow because of the 64-bit division,
you could multiply timeout by NSEC_PER_USEC instead and do a
straight comparison.


	ktime_t end = ktime_add_us(ktime_get(), timeout);
	do {
		...
	} while (ktime_before(ktime_get(), end);

> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> new file mode 100644
> index 000000000000..0aaef5960e29
> --- /dev/null
> +++ b/include/soc/tegra/bpmp-abi.h
> +#ifndef _ABI_BPMP_ABI_H_
> +#define _ABI_BPMP_ABI_H_
> +
> +#ifdef LK
> +#include <stdint.h>
> +#endif
> +
> +#ifndef __ABI_PACKED
> +#define __ABI_PACKED __attribute__((packed))
> +#endif
> +
> +#ifdef NO_GCC_EXTENSIONS
> +#define EMPTY char empty;
> +#define EMPTY_ARRAY 1
> +#else
> +#define EMPTY
> +#define EMPTY_ARRAY 0
> +#endif
> +
> +#ifndef __UNION_ANON
> +#define __UNION_ANON
> +#endif

Maybe keep these all out of the kernel?

> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ message
> + *
> + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> + * the MRQ message is a payload (immediately following the
> + * mrq_request) whose format depends on mrq.
> + *
> + * @todo document the flags
> + */

What's the deal with the odd documentation format?

> +struct mrq_request {
> +	/** @brief MRQ number of the request */
> +	uint32_t mrq;
> +	/** @brief flags for the request */
> +	uint32_t flags;
> +} __ABI_PACKED;

Marking the structure as packed may result in byte-wise access, depending
on compiler flags. Is that what you intended? The structure is fully
packed already, so you won't avoid any padding here.

> +/**
> + * @addtogroup Debugfs
> + * @{
> + *
> + * The BPMP firmware implements a pseudo-filesystem called
> + * debugfs. Any driver within the firmware may register with debugfs
> + * to expose an arbitrary set of "files" in the filesystem. When
> + * software on the CPU writes to a debugfs file, debugfs passes the
> + * written data to a callback provided by the driver. When software on
> + * the CPU reads a debugfs file, debugfs queries the driver for the
> + * data to return to the CPU. The intention of the debugfs filesystem
> + * is to provide information useful for debugging the system at
> + * runtime.
> + *
> + * @note The files exposed via debugfs are not part of the
> + * BPMP firmware's ABI. debugfs files may be added or removed in any
> + * given version of the firmware. Typically the semantics of a debugfs
> + * file are consistent from version to version but even that is not
> + * guaranteed.
> + *
> + * @}
> + */
> +/** @ingroup Debugfs */
> +enum mrq_debugfs_commands {
> +	CMD_DEBUGFS_READ = 1,
> +	CMD_DEBUGFS_WRITE = 2,
> +	CMD_DEBUGFS_DUMPDIR = 3,
> +	CMD_DEBUGFS_MAX
> +};
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_request {
> +	/** @brief physical address pointing at filename */
> +	uint32_t fnameaddr;
> +	/** @brief length in bytes of filename buffer */
> +	uint32_t fnamelen;
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
>

If the ABI is version specific, maybe add the firmware version name
to the structure definition?

> +struct cmd_clk_set_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;

This structure actually has a non-aligned struct member, but you
can write that as

struct cmd_clk_set_rate_request {
	int32_t unused;
	int64_t rate;
} __attribute__((packed, aligned(4)));

to still use a default four-byte alignment.

	Arnd

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 13:43       ` Arnd Bergmann
  -1 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 13:43 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Sivaram Nair, devicetree, Peter De Schrijver, Timo Alho,
	Joseph Lo, linux-tegra, linux-arm-kernel

On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> +                                       void *data)
> +{
> +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> +
> +       return 0;
> +}
> 

It seems very odd that you don't write the data at all but just trigger
the communication here. My interpretation is that you bypass
the mailbox infrastructure for the actual message, and instead reimplement
it on top of the mailbox infrastructure as part of the bpmp driver.

Why not expose the bpmp itself as the mailbox here?

	Arnd

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-22 13:43       ` Arnd Bergmann
  0 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 13:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> +                                       void *data)
> +{
> +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> +
> +       return 0;
> +}
> 

It seems very odd that you don't write the data at all but just trigger
the communication here. My interpretation is that you bypass
the mailbox infrastructure for the actual message, and instead reimplement
it on top of the mailbox infrastructure as part of the bpmp driver.

Why not expose the bpmp itself as the mailbox here?

	Arnd

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 13:34         ` Arnd Bergmann
@ 2016-08-22 14:02           ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 14:02 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 6809 bytes --]

On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> > +{
> > +	void *frame;
> > +
> > +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> > +	if (IS_ERR_OR_NULL(frame)) {
> > +		channel->ib = NULL;
> > +		return false;
> > +	}
> > +
> > +	channel->ib = frame;
> > +
> > +	return true;
> > +}
> 
> Something is wrong with your API if you need IS_ERR_OR_NULL(). If you
> can return NULL, use that for all error. Alternatively make sure
> that you never return NULL 

You're absolutely right, I had missed those on my first pass. I've since
revised all of the error handling based on earlier comments from Jon and
there are IS_ERR_OR_NULL() left.

> > +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> > +{
> > +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> > +	ktime_t start, now;
> > +
> > +	start = ns_to_ktime(local_clock());
> > +
> > +	do {
> > +		if (tegra_bpmp_master_acked(channel))
> > +			return 0;
> > +
> > +		now = ns_to_ktime(local_clock());
> > +	} while (ktime_us_delta(now, start) < timeout);
> > +
> > +	return -ETIMEDOUT;
> > +}
> 
> local_clock() is not guaranteed to be in nanoseconds, why not use
> ktime_get() instead?

We don't need nanosecond resolution anyway, all timeouts are specified
in microseconds.

> ktime_us_delta() is a bit slow because of the 64-bit division,
> you could multiply timeout by NSEC_PER_USEC instead and do a
> straight comparison.
> 
> 
> 	ktime_t end = ktime_add_us(ktime_get(), timeout);
> 	do {
> 		...
> 	} while (ktime_before(ktime_get(), end);

Yes, I think that should work better. Thanks.

> > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > new file mode 100644
> > index 000000000000..0aaef5960e29
> > --- /dev/null
> > +++ b/include/soc/tegra/bpmp-abi.h
> > +#ifndef _ABI_BPMP_ABI_H_
> > +#define _ABI_BPMP_ABI_H_
> > +
> > +#ifdef LK
> > +#include <stdint.h>
> > +#endif
> > +
> > +#ifndef __ABI_PACKED
> > +#define __ABI_PACKED __attribute__((packed))
> > +#endif
> > +
> > +#ifdef NO_GCC_EXTENSIONS
> > +#define EMPTY char empty;
> > +#define EMPTY_ARRAY 1
> > +#else
> > +#define EMPTY
> > +#define EMPTY_ARRAY 0
> > +#endif
> > +
> > +#ifndef __UNION_ANON
> > +#define __UNION_ANON
> > +#endif
> 
> Maybe keep these all out of the kernel?

This was discussed a little in an earlier posting. This header file is
maintained by the BPMP firmware team and using it verbatim means little
to no effort required to update it.

> > +/**
> > + * @ingroup MRQ_Format
> > + * @brief header for an MRQ message
> > + *
> > + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> > + * the MRQ message is a payload (immediately following the
> > + * mrq_request) whose format depends on mrq.
> > + *
> > + * @todo document the flags
> > + */
> 
> What's the deal with the odd documentation format?

This is also a side-effect of this coming straight out of the repository
for the BPMP firmware. Presumably this used to generate documentation
for internal (or possibly public) consumption.

> > +struct mrq_request {
> > +	/** @brief MRQ number of the request */
> > +	uint32_t mrq;
> > +	/** @brief flags for the request */
> > +	uint32_t flags;
> > +} __ABI_PACKED;
> 
> Marking the structure as packed may result in byte-wise access, depending
> on compiler flags. Is that what you intended? The structure is fully
> packed already, so you won't avoid any padding here.

Agreed, the packing seems unnecessary in many places. However this is
defining an ABI that's used across multiple operating systems, so the
packing may still be required on some systems or toolchains to ensure
the exact same format in the transport.

> > +/**
> > + * @addtogroup Debugfs
> > + * @{
> > + *
> > + * The BPMP firmware implements a pseudo-filesystem called
> > + * debugfs. Any driver within the firmware may register with debugfs
> > + * to expose an arbitrary set of "files" in the filesystem. When
> > + * software on the CPU writes to a debugfs file, debugfs passes the
> > + * written data to a callback provided by the driver. When software on
> > + * the CPU reads a debugfs file, debugfs queries the driver for the
> > + * data to return to the CPU. The intention of the debugfs filesystem
> > + * is to provide information useful for debugging the system at
> > + * runtime.
> > + *
> > + * @note The files exposed via debugfs are not part of the
> > + * BPMP firmware's ABI. debugfs files may be added or removed in any
> > + * given version of the firmware. Typically the semantics of a debugfs
> > + * file are consistent from version to version but even that is not
> > + * guaranteed.
> > + *
> > + * @}
> > + */
> > +/** @ingroup Debugfs */
> > +enum mrq_debugfs_commands {
> > +	CMD_DEBUGFS_READ = 1,
> > +	CMD_DEBUGFS_WRITE = 2,
> > +	CMD_DEBUGFS_DUMPDIR = 3,
> > +	CMD_DEBUGFS_MAX
> > +};
> > +
> > +/**
> > + * @ingroup Debugfs
> > + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> > + */
> > +struct cmd_debugfs_fileop_request {
> > +	/** @brief physical address pointing at filename */
> > +	uint32_t fnameaddr;
> > +	/** @brief length in bytes of filename buffer */
> > +	uint32_t fnamelen;
> > +	/** @brief physical address pointing to data buffer */
> > +	uint32_t dataaddr;
> > +	/** @brief length in bytes of data buffer */
> > +	uint32_t datalen;
> > +} __ABI_PACKED;
> >
> 
> If the ABI is version specific, maybe add the firmware version name
> to the structure definition?

I don't think the ABI is version specific. Only the set and contents of
the files in the BPMP's debugfs may change per version. The messages
which are exchanged between the CPU and the BPMP remain the same.

> > +struct cmd_clk_set_rate_request {
> > +	int32_t unused;
> > +	int64_t rate;
> > +} __ABI_PACKED;
> 
> This structure actually has a non-aligned struct member, but you
> can write that as
> 
> struct cmd_clk_set_rate_request {
> 	int32_t unused;
> 	int64_t rate;
> } __attribute__((packed, aligned(4)));
> 
> to still use a default four-byte alignment.

I thought the original would yield something like this in memory:

	[unused]
	[rate  ]
	[rate  ]

because packing makes sure to avoid any padding introduced for natural
alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
exact same layout?

If it's not going to yield the same layout, it's probably too late to
change it now because products are going to ship with this ABI as far
as I understand.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 14:02           ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 14:02 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> > +{
> > +	void *frame;
> > +
> > +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> > +	if (IS_ERR_OR_NULL(frame)) {
> > +		channel->ib = NULL;
> > +		return false;
> > +	}
> > +
> > +	channel->ib = frame;
> > +
> > +	return true;
> > +}
> 
> Something is wrong with your API if you need IS_ERR_OR_NULL(). If you
> can return NULL, use that for all error. Alternatively make sure
> that you never return NULL 

You're absolutely right, I had missed those on my first pass. I've since
revised all of the error handling based on earlier comments from Jon and
there are IS_ERR_OR_NULL() left.

> > +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> > +{
> > +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> > +	ktime_t start, now;
> > +
> > +	start = ns_to_ktime(local_clock());
> > +
> > +	do {
> > +		if (tegra_bpmp_master_acked(channel))
> > +			return 0;
> > +
> > +		now = ns_to_ktime(local_clock());
> > +	} while (ktime_us_delta(now, start) < timeout);
> > +
> > +	return -ETIMEDOUT;
> > +}
> 
> local_clock() is not guaranteed to be in nanoseconds, why not use
> ktime_get() instead?

We don't need nanosecond resolution anyway, all timeouts are specified
in microseconds.

> ktime_us_delta() is a bit slow because of the 64-bit division,
> you could multiply timeout by NSEC_PER_USEC instead and do a
> straight comparison.
> 
> 
> 	ktime_t end = ktime_add_us(ktime_get(), timeout);
> 	do {
> 		...
> 	} while (ktime_before(ktime_get(), end);

Yes, I think that should work better. Thanks.

> > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > new file mode 100644
> > index 000000000000..0aaef5960e29
> > --- /dev/null
> > +++ b/include/soc/tegra/bpmp-abi.h
> > +#ifndef _ABI_BPMP_ABI_H_
> > +#define _ABI_BPMP_ABI_H_
> > +
> > +#ifdef LK
> > +#include <stdint.h>
> > +#endif
> > +
> > +#ifndef __ABI_PACKED
> > +#define __ABI_PACKED __attribute__((packed))
> > +#endif
> > +
> > +#ifdef NO_GCC_EXTENSIONS
> > +#define EMPTY char empty;
> > +#define EMPTY_ARRAY 1
> > +#else
> > +#define EMPTY
> > +#define EMPTY_ARRAY 0
> > +#endif
> > +
> > +#ifndef __UNION_ANON
> > +#define __UNION_ANON
> > +#endif
> 
> Maybe keep these all out of the kernel?

This was discussed a little in an earlier posting. This header file is
maintained by the BPMP firmware team and using it verbatim means little
to no effort required to update it.

> > +/**
> > + * @ingroup MRQ_Format
> > + * @brief header for an MRQ message
> > + *
> > + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> > + * the MRQ message is a payload (immediately following the
> > + * mrq_request) whose format depends on mrq.
> > + *
> > + * @todo document the flags
> > + */
> 
> What's the deal with the odd documentation format?

This is also a side-effect of this coming straight out of the repository
for the BPMP firmware. Presumably this used to generate documentation
for internal (or possibly public) consumption.

> > +struct mrq_request {
> > +	/** @brief MRQ number of the request */
> > +	uint32_t mrq;
> > +	/** @brief flags for the request */
> > +	uint32_t flags;
> > +} __ABI_PACKED;
> 
> Marking the structure as packed may result in byte-wise access, depending
> on compiler flags. Is that what you intended? The structure is fully
> packed already, so you won't avoid any padding here.

Agreed, the packing seems unnecessary in many places. However this is
defining an ABI that's used across multiple operating systems, so the
packing may still be required on some systems or toolchains to ensure
the exact same format in the transport.

> > +/**
> > + * @addtogroup Debugfs
> > + * @{
> > + *
> > + * The BPMP firmware implements a pseudo-filesystem called
> > + * debugfs. Any driver within the firmware may register with debugfs
> > + * to expose an arbitrary set of "files" in the filesystem. When
> > + * software on the CPU writes to a debugfs file, debugfs passes the
> > + * written data to a callback provided by the driver. When software on
> > + * the CPU reads a debugfs file, debugfs queries the driver for the
> > + * data to return to the CPU. The intention of the debugfs filesystem
> > + * is to provide information useful for debugging the system at
> > + * runtime.
> > + *
> > + * @note The files exposed via debugfs are not part of the
> > + * BPMP firmware's ABI. debugfs files may be added or removed in any
> > + * given version of the firmware. Typically the semantics of a debugfs
> > + * file are consistent from version to version but even that is not
> > + * guaranteed.
> > + *
> > + * @}
> > + */
> > +/** @ingroup Debugfs */
> > +enum mrq_debugfs_commands {
> > +	CMD_DEBUGFS_READ = 1,
> > +	CMD_DEBUGFS_WRITE = 2,
> > +	CMD_DEBUGFS_DUMPDIR = 3,
> > +	CMD_DEBUGFS_MAX
> > +};
> > +
> > +/**
> > + * @ingroup Debugfs
> > + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> > + */
> > +struct cmd_debugfs_fileop_request {
> > +	/** @brief physical address pointing at filename */
> > +	uint32_t fnameaddr;
> > +	/** @brief length in bytes of filename buffer */
> > +	uint32_t fnamelen;
> > +	/** @brief physical address pointing to data buffer */
> > +	uint32_t dataaddr;
> > +	/** @brief length in bytes of data buffer */
> > +	uint32_t datalen;
> > +} __ABI_PACKED;
> >
> 
> If the ABI is version specific, maybe add the firmware version name
> to the structure definition?

I don't think the ABI is version specific. Only the set and contents of
the files in the BPMP's debugfs may change per version. The messages
which are exchanged between the CPU and the BPMP remain the same.

> > +struct cmd_clk_set_rate_request {
> > +	int32_t unused;
> > +	int64_t rate;
> > +} __ABI_PACKED;
> 
> This structure actually has a non-aligned struct member, but you
> can write that as
> 
> struct cmd_clk_set_rate_request {
> 	int32_t unused;
> 	int64_t rate;
> } __attribute__((packed, aligned(4)));
> 
> to still use a default four-byte alignment.

I thought the original would yield something like this in memory:

	[unused]
	[rate  ]
	[rate  ]

because packing makes sure to avoid any padding introduced for natural
alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
exact same layout?

If it's not going to yield the same layout, it's probably too late to
change it now because products are going to ship with this ABI as far
as I understand.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/9ac2d1bc/attachment-0001.sig>

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-22 13:43       ` Arnd Bergmann
@ 2016-08-22 14:17         ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 14:17 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 1506 bytes --]

On Mon, Aug 22, 2016 at 03:43:23PM +0200, Arnd Bergmann wrote:
> On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
> > +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> > +                                       void *data)
> > +{
> > +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> > +
> > +       return 0;
> > +}
> > 
> 
> It seems very odd that you don't write the data at all but just trigger
> the communication here. My interpretation is that you bypass
> the mailbox infrastructure for the actual message, and instead reimplement
> it on top of the mailbox infrastructure as part of the bpmp driver.
> 
> Why not expose the bpmp itself as the mailbox here?

My understanding is that doorbell doesn't provide any means to transfer
data. It merely signals the availability of data. It is up to a use-case
specific implementation to arrange for the transport of data.

BPMP implements a shmem-based protocol and uses the doorbell merely as a
mechanism for the BPMP and CPU to signal each other when data is ready.

Exposing the BPMP as the mailbox could possibly be done, but it would
implement a fairly large software layer in the mailbox system which is
targetted primarily at simple mailbox hardware that exchanges small
amounts of data (usually fitting into 32 bits). mailbox-sti is another
driver that uses the framework only to signal, so this doesn't seem to
be a mechanism exclusive to Tegra.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-22 14:17         ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 14:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 03:43:23PM +0200, Arnd Bergmann wrote:
> On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
> > +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> > +                                       void *data)
> > +{
> > +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> > +
> > +       return 0;
> > +}
> > 
> 
> It seems very odd that you don't write the data at all but just trigger
> the communication here. My interpretation is that you bypass
> the mailbox infrastructure for the actual message, and instead reimplement
> it on top of the mailbox infrastructure as part of the bpmp driver.
> 
> Why not expose the bpmp itself as the mailbox here?

My understanding is that doorbell doesn't provide any means to transfer
data. It merely signals the availability of data. It is up to a use-case
specific implementation to arrange for the transport of data.

BPMP implements a shmem-based protocol and uses the doorbell merely as a
mechanism for the BPMP and CPU to signal each other when data is ready.

Exposing the BPMP as the mailbox could possibly be done, but it would
implement a fairly large software layer in the mailbox system which is
targetted primarily at simple mailbox hardware that exchanges small
amounts of data (usually fitting into 32 bits). mailbox-sti is another
driver that uses the framework only to signal, so this doesn't seem to
be a mechanism exclusive to Tegra.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/2bac033a/attachment.sig>

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 12:54             ` Thierry Reding
@ 2016-08-22 14:24                 ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 14:24 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 22/08/16 13:54, Thierry Reding wrote:
> * PGP Signed by an unknown key
> 
> On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>>
>>> The Boot and Power Management Processor (BPMP) is a co-processor found
>>> on Tegra SoCs. It is designed to handle the early stages of the boot
>>> process and offload power management tasks (such as clocks, resets,
>>> powergates, ...) as well as system control services.
>>>
>>> Compared to the ARM SCPI, the services provided by BPMP are message-
>>> based rather than method-based. The BPMP firmware driver provides the
>>> services to transmit data to and receive data from the BPMP. Users can
>>> also register an MRQ, for which a service routine will be run when a
>>> corresponding event is received from the firmware.
>>
>> MRQ?
> 
> I think that means "Message ReQuest", which is sort of like an IRQ but
> the user will receive a message (with potentially payload) instead. Do
> you want me to spell that out in the commit message, or what would you
> suggest?

I think that if we simply say 'Message ReQuest (MRQ)" it becomes clearer
what this is. Otherwise, google returns everything from 'most recent
quarter' to canadian rock bands :-)

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 14:24                 ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-22 14:24 UTC (permalink / raw)
  To: linux-arm-kernel


On 22/08/16 13:54, Thierry Reding wrote:
> * PGP Signed by an unknown key
> 
> On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding@nvidia.com>
>>>
>>> The Boot and Power Management Processor (BPMP) is a co-processor found
>>> on Tegra SoCs. It is designed to handle the early stages of the boot
>>> process and offload power management tasks (such as clocks, resets,
>>> powergates, ...) as well as system control services.
>>>
>>> Compared to the ARM SCPI, the services provided by BPMP are message-
>>> based rather than method-based. The BPMP firmware driver provides the
>>> services to transmit data to and receive data from the BPMP. Users can
>>> also register an MRQ, for which a service routine will be run when a
>>> corresponding event is received from the firmware.
>>
>> MRQ?
> 
> I think that means "Message ReQuest", which is sort of like an IRQ but
> the user will receive a message (with potentially payload) instead. Do
> you want me to spell that out in the commit message, or what would you
> suggest?

I think that if we simply say 'Message ReQuest (MRQ)" it becomes clearer
what this is. Otherwise, google returns everything from 'most recent
quarter' to canadian rock bands :-)

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 14:02           ` Thierry Reding
@ 2016-08-22 14:42               ` Arnd Bergmann
  -1 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Thierry Reding, Sivaram Nair, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Peter De Schrijver, Timo Alho, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> > On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > > new file mode 100644
> > > index 000000000000..0aaef5960e29
> > > --- /dev/null
> > > +++ b/include/soc/tegra/bpmp-abi.h
> > > +#ifndef _ABI_BPMP_ABI_H_
> > > +#define _ABI_BPMP_ABI_H_
> > > +
> > > +#ifdef LK
> > > +#include <stdint.h>
> > > +#endif
> > > +
> > > +#ifndef __ABI_PACKED
> > > +#define __ABI_PACKED __attribute__((packed))
> > > +#endif
> > > +
> > > +#ifdef NO_GCC_EXTENSIONS
> > > +#define EMPTY char empty;
> > > +#define EMPTY_ARRAY 1
> > > +#else
> > > +#define EMPTY
> > > +#define EMPTY_ARRAY 0
> > > +#endif
> > > +
> > > +#ifndef __UNION_ANON
> > > +#define __UNION_ANON
> > > +#endif
> > 
> > Maybe keep these all out of the kernel?
> 
> This was discussed a little in an earlier posting. This header file is
> maintained by the BPMP firmware team and using it verbatim means little
> to no effort required to update it.

The usual recommendation is to just use Linux coding style in shared
files, and possibly add another header that provides the required
definitions. Otherwise you end up with people randomly cleaning up
the file later ;-)


> > > +struct mrq_request {
> > > +	/** @brief MRQ number of the request */
> > > +	uint32_t mrq;
> > > +	/** @brief flags for the request */
> > > +	uint32_t flags;
> > > +} __ABI_PACKED;
> > 
> > Marking the structure as packed may result in byte-wise access, depending
> > on compiler flags. Is that what you intended? The structure is fully
> > packed already, so you won't avoid any padding here.
> 
> Agreed, the packing seems unnecessary in many places. However this is
> defining an ABI that's used across multiple operating systems, so the
> packing may still be required on some systems or toolchains to ensure
> the exact same format in the transport.

However, if __ABI_PACKED is defined to an empty string, it is different
in some cases.

Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
some of the structures, by adding an extra member. If the firmware
has a compiler that is less than 10 years old, I'd suggest using C99
syntax instead, which should avoid those differences and eliminate
all gcc extensions.

> > > +/**
> > > + * @addtogroup Debugfs
> > > + * @{
> > > + *
> > > + * The BPMP firmware implements a pseudo-filesystem called
> > > + * debugfs. Any driver within the firmware may register with debugfs
> > > + * to expose an arbitrary set of "files" in the filesystem. When
> > > + * software on the CPU writes to a debugfs file, debugfs passes the
> > > + * written data to a callback provided by the driver. When software on
> > > + * the CPU reads a debugfs file, debugfs queries the driver for the
> > > + * data to return to the CPU. The intention of the debugfs filesystem
> > > + * is to provide information useful for debugging the system at
> > > + * runtime.
> > > + *
> > > + * @note The files exposed via debugfs are not part of the
> > > + * BPMP firmware's ABI. debugfs files may be added or removed in any
> > > + * given version of the firmware. Typically the semantics of a debugfs
> > > + * file are consistent from version to version but even that is not
> > > + * guaranteed.
> > > + *
> > > + * @}
> > > + */
> > > +/** @ingroup Debugfs */
> > > +enum mrq_debugfs_commands {
> > > +	CMD_DEBUGFS_READ = 1,
> > > +	CMD_DEBUGFS_WRITE = 2,
> > > +	CMD_DEBUGFS_DUMPDIR = 3,
> > > +	CMD_DEBUGFS_MAX
> > > +};
> > > +
> > > +/**
> > > + * @ingroup Debugfs
> > > + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> > > + */
> > > +struct cmd_debugfs_fileop_request {
> > > +	/** @brief physical address pointing at filename */
> > > +	uint32_t fnameaddr;
> > > +	/** @brief length in bytes of filename buffer */
> > > +	uint32_t fnamelen;
> > > +	/** @brief physical address pointing to data buffer */
> > > +	uint32_t dataaddr;
> > > +	/** @brief length in bytes of data buffer */
> > > +	uint32_t datalen;
> > > +} __ABI_PACKED;
> > >
> > 
> > If the ABI is version specific, maybe add the firmware version name
> > to the structure definition?
> 
> I don't think the ABI is version specific. Only the set and contents of
> the files in the BPMP's debugfs may change per version. The messages
> which are exchanged between the CPU and the BPMP remain the same.

Ah, got it.

> > > +struct cmd_clk_set_rate_request {
> > > +	int32_t unused;
> > > +	int64_t rate;
> > > +} __ABI_PACKED;
> > 
> > This structure actually has a non-aligned struct member, but you
> > can write that as
> > 
> > struct cmd_clk_set_rate_request {
> > 	int32_t unused;
> > 	int64_t rate;
> > } __attribute__((packed, aligned(4)));
> > 
> > to still use a default four-byte alignment.
> 
> I thought the original would yield something like this in memory:
> 
> 	[unused]
> 	[rate  ]
> 	[rate  ]
> 
> because packing makes sure to avoid any padding introduced for natural
> alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
> exact same layout?

Yes, only the alignment of the structure itself is changed, not
the contents. The main difference is that accessing 'rate' on
a target machine without unaligned access will use two 32-bit
loads rather than eight 8-bit loads.

Also, embedding this structure in another one is different
(in theory, I don't expect this to actually appear somewhere):

struct example {
	char a;
	struct cmd_clk_set_rate_request b;
};

would currently be 13 bytes long, with the alignment I suggested you
get three bytes of padding after 'a'.

	Arnd

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 14:42               ` Arnd Bergmann
  0 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 14:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> > On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > > new file mode 100644
> > > index 000000000000..0aaef5960e29
> > > --- /dev/null
> > > +++ b/include/soc/tegra/bpmp-abi.h
> > > +#ifndef _ABI_BPMP_ABI_H_
> > > +#define _ABI_BPMP_ABI_H_
> > > +
> > > +#ifdef LK
> > > +#include <stdint.h>
> > > +#endif
> > > +
> > > +#ifndef __ABI_PACKED
> > > +#define __ABI_PACKED __attribute__((packed))
> > > +#endif
> > > +
> > > +#ifdef NO_GCC_EXTENSIONS
> > > +#define EMPTY char empty;
> > > +#define EMPTY_ARRAY 1
> > > +#else
> > > +#define EMPTY
> > > +#define EMPTY_ARRAY 0
> > > +#endif
> > > +
> > > +#ifndef __UNION_ANON
> > > +#define __UNION_ANON
> > > +#endif
> > 
> > Maybe keep these all out of the kernel?
> 
> This was discussed a little in an earlier posting. This header file is
> maintained by the BPMP firmware team and using it verbatim means little
> to no effort required to update it.

The usual recommendation is to just use Linux coding style in shared
files, and possibly add another header that provides the required
definitions. Otherwise you end up with people randomly cleaning up
the file later ;-)


> > > +struct mrq_request {
> > > +	/** @brief MRQ number of the request */
> > > +	uint32_t mrq;
> > > +	/** @brief flags for the request */
> > > +	uint32_t flags;
> > > +} __ABI_PACKED;
> > 
> > Marking the structure as packed may result in byte-wise access, depending
> > on compiler flags. Is that what you intended? The structure is fully
> > packed already, so you won't avoid any padding here.
> 
> Agreed, the packing seems unnecessary in many places. However this is
> defining an ABI that's used across multiple operating systems, so the
> packing may still be required on some systems or toolchains to ensure
> the exact same format in the transport.

However, if __ABI_PACKED is defined to an empty string, it is different
in some cases.

Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
some of the structures, by adding an extra member. If the firmware
has a compiler that is less than 10 years old, I'd suggest using C99
syntax instead, which should avoid those differences and eliminate
all gcc extensions.

> > > +/**
> > > + * @addtogroup Debugfs
> > > + * @{
> > > + *
> > > + * The BPMP firmware implements a pseudo-filesystem called
> > > + * debugfs. Any driver within the firmware may register with debugfs
> > > + * to expose an arbitrary set of "files" in the filesystem. When
> > > + * software on the CPU writes to a debugfs file, debugfs passes the
> > > + * written data to a callback provided by the driver. When software on
> > > + * the CPU reads a debugfs file, debugfs queries the driver for the
> > > + * data to return to the CPU. The intention of the debugfs filesystem
> > > + * is to provide information useful for debugging the system at
> > > + * runtime.
> > > + *
> > > + * @note The files exposed via debugfs are not part of the
> > > + * BPMP firmware's ABI. debugfs files may be added or removed in any
> > > + * given version of the firmware. Typically the semantics of a debugfs
> > > + * file are consistent from version to version but even that is not
> > > + * guaranteed.
> > > + *
> > > + * @}
> > > + */
> > > +/** @ingroup Debugfs */
> > > +enum mrq_debugfs_commands {
> > > +	CMD_DEBUGFS_READ = 1,
> > > +	CMD_DEBUGFS_WRITE = 2,
> > > +	CMD_DEBUGFS_DUMPDIR = 3,
> > > +	CMD_DEBUGFS_MAX
> > > +};
> > > +
> > > +/**
> > > + * @ingroup Debugfs
> > > + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> > > + */
> > > +struct cmd_debugfs_fileop_request {
> > > +	/** @brief physical address pointing at filename */
> > > +	uint32_t fnameaddr;
> > > +	/** @brief length in bytes of filename buffer */
> > > +	uint32_t fnamelen;
> > > +	/** @brief physical address pointing to data buffer */
> > > +	uint32_t dataaddr;
> > > +	/** @brief length in bytes of data buffer */
> > > +	uint32_t datalen;
> > > +} __ABI_PACKED;
> > >
> > 
> > If the ABI is version specific, maybe add the firmware version name
> > to the structure definition?
> 
> I don't think the ABI is version specific. Only the set and contents of
> the files in the BPMP's debugfs may change per version. The messages
> which are exchanged between the CPU and the BPMP remain the same.

Ah, got it.

> > > +struct cmd_clk_set_rate_request {
> > > +	int32_t unused;
> > > +	int64_t rate;
> > > +} __ABI_PACKED;
> > 
> > This structure actually has a non-aligned struct member, but you
> > can write that as
> > 
> > struct cmd_clk_set_rate_request {
> > 	int32_t unused;
> > 	int64_t rate;
> > } __attribute__((packed, aligned(4)));
> > 
> > to still use a default four-byte alignment.
> 
> I thought the original would yield something like this in memory:
> 
> 	[unused]
> 	[rate  ]
> 	[rate  ]
> 
> because packing makes sure to avoid any padding introduced for natural
> alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
> exact same layout?

Yes, only the alignment of the structure itself is changed, not
the contents. The main difference is that accessing 'rate' on
a target machine without unaligned access will use two 32-bit
loads rather than eight 8-bit loads.

Also, embedding this structure in another one is different
(in theory, I don't expect this to actually appear somewhere):

struct example {
	char a;
	struct cmd_clk_set_rate_request b;
};

would currently be 13 bytes long, with the alignment I suggested you
get three bytes of padding after 'a'.

	Arnd

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 14:24                 ` Jon Hunter
@ 2016-08-22 15:00                     ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 15:00 UTC (permalink / raw)
  To: Jon Hunter
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 1497 bytes --]

On Mon, Aug 22, 2016 at 03:24:28PM +0100, Jon Hunter wrote:
> 
> On 22/08/16 13:54, Thierry Reding wrote:
> > * PGP Signed by an unknown key
> > 
> > On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
> >>
> >> On 19/08/16 18:32, Thierry Reding wrote:
> >>> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> >>>
> >>> The Boot and Power Management Processor (BPMP) is a co-processor found
> >>> on Tegra SoCs. It is designed to handle the early stages of the boot
> >>> process and offload power management tasks (such as clocks, resets,
> >>> powergates, ...) as well as system control services.
> >>>
> >>> Compared to the ARM SCPI, the services provided by BPMP are message-
> >>> based rather than method-based. The BPMP firmware driver provides the
> >>> services to transmit data to and receive data from the BPMP. Users can
> >>> also register an MRQ, for which a service routine will be run when a
> >>> corresponding event is received from the firmware.
> >>
> >> MRQ?
> > 
> > I think that means "Message ReQuest", which is sort of like an IRQ but
> > the user will receive a message (with potentially payload) instead. Do
> > you want me to spell that out in the commit message, or what would you
> > suggest?
> 
> I think that if we simply say 'Message ReQuest (MRQ)" it becomes clearer
> what this is. Otherwise, google returns everything from 'most recent
> quarter' to canadian rock bands :-)

Okay, done.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 15:00                     ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 15:00 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 03:24:28PM +0100, Jon Hunter wrote:
> 
> On 22/08/16 13:54, Thierry Reding wrote:
> > * PGP Signed by an unknown key
> > 
> > On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
> >>
> >> On 19/08/16 18:32, Thierry Reding wrote:
> >>> From: Thierry Reding <treding@nvidia.com>
> >>>
> >>> The Boot and Power Management Processor (BPMP) is a co-processor found
> >>> on Tegra SoCs. It is designed to handle the early stages of the boot
> >>> process and offload power management tasks (such as clocks, resets,
> >>> powergates, ...) as well as system control services.
> >>>
> >>> Compared to the ARM SCPI, the services provided by BPMP are message-
> >>> based rather than method-based. The BPMP firmware driver provides the
> >>> services to transmit data to and receive data from the BPMP. Users can
> >>> also register an MRQ, for which a service routine will be run when a
> >>> corresponding event is received from the firmware.
> >>
> >> MRQ?
> > 
> > I think that means "Message ReQuest", which is sort of like an IRQ but
> > the user will receive a message (with potentially payload) instead. Do
> > you want me to spell that out in the commit message, or what would you
> > suggest?
> 
> I think that if we simply say 'Message ReQuest (MRQ)" it becomes clearer
> what this is. Otherwise, google returns everything from 'most recent
> quarter' to canadian rock bands :-)

Okay, done.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/ecdf72bc/attachment.sig>

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 14:42               ` Arnd Bergmann
@ 2016-08-22 15:32                 ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 15:32 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sivaram Nair,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Peter De Schrijver, Timo Alho,
	Joseph Lo, linux-tegra-u79uwXL29TY76Z2rM5mHXA

[-- Attachment #1: Type: text/plain, Size: 5493 bytes --]

On Mon, Aug 22, 2016 at 04:42:32PM +0200, Arnd Bergmann wrote:
> On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> > On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> > > On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > > > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > > > new file mode 100644
> > > > index 000000000000..0aaef5960e29
> > > > --- /dev/null
> > > > +++ b/include/soc/tegra/bpmp-abi.h
> > > > +#ifndef _ABI_BPMP_ABI_H_
> > > > +#define _ABI_BPMP_ABI_H_
> > > > +
> > > > +#ifdef LK
> > > > +#include <stdint.h>
> > > > +#endif
> > > > +
> > > > +#ifndef __ABI_PACKED
> > > > +#define __ABI_PACKED __attribute__((packed))
> > > > +#endif
> > > > +
> > > > +#ifdef NO_GCC_EXTENSIONS
> > > > +#define EMPTY char empty;
> > > > +#define EMPTY_ARRAY 1
> > > > +#else
> > > > +#define EMPTY
> > > > +#define EMPTY_ARRAY 0
> > > > +#endif
> > > > +
> > > > +#ifndef __UNION_ANON
> > > > +#define __UNION_ANON
> > > > +#endif
> > > 
> > > Maybe keep these all out of the kernel?
> > 
> > This was discussed a little in an earlier posting. This header file is
> > maintained by the BPMP firmware team and using it verbatim means little
> > to no effort required to update it.
> 
> The usual recommendation is to just use Linux coding style in shared
> files, and possibly add another header that provides the required
> definitions. Otherwise you end up with people randomly cleaning up
> the file later ;-)

I dug up the discussion from earlier:

	http://patchwork.ozlabs.org/patch/644643/

Some people from our BPMP firmware team objected to this file being
modified in any way because it would make subsequent updates needlessly
difficult.

There's also at least one precedent, albeit with style a lot closer to
Linux:

	include/linux/mfd/cros_ec_commands.h

I don't mind much either way. The file is at least well documented and
consistent in itself. If you or anyone else insists, I can make a pass
and mold this into something that adheres to the kernel coding style.

> > > > +struct mrq_request {
> > > > +	/** @brief MRQ number of the request */
> > > > +	uint32_t mrq;
> > > > +	/** @brief flags for the request */
> > > > +	uint32_t flags;
> > > > +} __ABI_PACKED;
> > > 
> > > Marking the structure as packed may result in byte-wise access, depending
> > > on compiler flags. Is that what you intended? The structure is fully
> > > packed already, so you won't avoid any padding here.
> > 
> > Agreed, the packing seems unnecessary in many places. However this is
> > defining an ABI that's used across multiple operating systems, so the
> > packing may still be required on some systems or toolchains to ensure
> > the exact same format in the transport.
> 
> However, if __ABI_PACKED is defined to an empty string, it is different
> in some cases.
> 
> Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
> some of the structures, by adding an extra member. If the firmware
> has a compiler that is less than 10 years old, I'd suggest using C99
> syntax instead, which should avoid those differences and eliminate
> all gcc extensions.

I think this isn't only about the firmware (which, as far as I can tell,
is always built with a non-ancient version of GCC). The same header file
is used in other operating systems and I have no idea about the
toolchain situation there.

As for the NO_GCC_EXTENSIONS I think that's only used to avoid empty
structures and zero-sized arrays, which I assume not all supported
toolchains can deal with.

Sivaram, Timo: can you shed any light on the scope of operating systems
and toolchains that we need to support? Any ideas, short of manual
editing, that we can try to eliminate some of Arnd's concerns?

> > > > +struct cmd_clk_set_rate_request {
> > > > +	int32_t unused;
> > > > +	int64_t rate;
> > > > +} __ABI_PACKED;
> > > 
> > > This structure actually has a non-aligned struct member, but you
> > > can write that as
> > > 
> > > struct cmd_clk_set_rate_request {
> > > 	int32_t unused;
> > > 	int64_t rate;
> > > } __attribute__((packed, aligned(4)));
> > > 
> > > to still use a default four-byte alignment.
> > 
> > I thought the original would yield something like this in memory:
> > 
> > 	[unused]
> > 	[rate  ]
> > 	[rate  ]
> > 
> > because packing makes sure to avoid any padding introduced for natural
> > alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
> > exact same layout?
> 
> Yes, only the alignment of the structure itself is changed, not
> the contents. The main difference is that accessing 'rate' on
> a target machine without unaligned access will use two 32-bit
> loads rather than eight 8-bit loads.
> 
> Also, embedding this structure in another one is different
> (in theory, I don't expect this to actually appear somewhere):
> 
> struct example {
> 	char a;
> 	struct cmd_clk_set_rate_request b;
> };
> 
> would currently be 13 bytes long, with the alignment I suggested you
> get three bytes of padding after 'a'.

Something that just occurred to me now is that the field that causes the
misalignment is actually unused. I can only imagine that some earlier
version of the ABI must have used it for some purpose and then it was
deprecated but kept for backwards-compatibility.

Sivaram, Timo: any ideas how this came about?

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 819 bytes --]

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 15:32                 ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-08-22 15:32 UTC (permalink / raw)
  To: linux-arm-kernel

On Mon, Aug 22, 2016 at 04:42:32PM +0200, Arnd Bergmann wrote:
> On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> > On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> > > On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> > > > diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> > > > new file mode 100644
> > > > index 000000000000..0aaef5960e29
> > > > --- /dev/null
> > > > +++ b/include/soc/tegra/bpmp-abi.h
> > > > +#ifndef _ABI_BPMP_ABI_H_
> > > > +#define _ABI_BPMP_ABI_H_
> > > > +
> > > > +#ifdef LK
> > > > +#include <stdint.h>
> > > > +#endif
> > > > +
> > > > +#ifndef __ABI_PACKED
> > > > +#define __ABI_PACKED __attribute__((packed))
> > > > +#endif
> > > > +
> > > > +#ifdef NO_GCC_EXTENSIONS
> > > > +#define EMPTY char empty;
> > > > +#define EMPTY_ARRAY 1
> > > > +#else
> > > > +#define EMPTY
> > > > +#define EMPTY_ARRAY 0
> > > > +#endif
> > > > +
> > > > +#ifndef __UNION_ANON
> > > > +#define __UNION_ANON
> > > > +#endif
> > > 
> > > Maybe keep these all out of the kernel?
> > 
> > This was discussed a little in an earlier posting. This header file is
> > maintained by the BPMP firmware team and using it verbatim means little
> > to no effort required to update it.
> 
> The usual recommendation is to just use Linux coding style in shared
> files, and possibly add another header that provides the required
> definitions. Otherwise you end up with people randomly cleaning up
> the file later ;-)

I dug up the discussion from earlier:

	http://patchwork.ozlabs.org/patch/644643/

Some people from our BPMP firmware team objected to this file being
modified in any way because it would make subsequent updates needlessly
difficult.

There's also at least one precedent, albeit with style a lot closer to
Linux:

	include/linux/mfd/cros_ec_commands.h

I don't mind much either way. The file is at least well documented and
consistent in itself. If you or anyone else insists, I can make a pass
and mold this into something that adheres to the kernel coding style.

> > > > +struct mrq_request {
> > > > +	/** @brief MRQ number of the request */
> > > > +	uint32_t mrq;
> > > > +	/** @brief flags for the request */
> > > > +	uint32_t flags;
> > > > +} __ABI_PACKED;
> > > 
> > > Marking the structure as packed may result in byte-wise access, depending
> > > on compiler flags. Is that what you intended? The structure is fully
> > > packed already, so you won't avoid any padding here.
> > 
> > Agreed, the packing seems unnecessary in many places. However this is
> > defining an ABI that's used across multiple operating systems, so the
> > packing may still be required on some systems or toolchains to ensure
> > the exact same format in the transport.
> 
> However, if __ABI_PACKED is defined to an empty string, it is different
> in some cases.
> 
> Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
> some of the structures, by adding an extra member. If the firmware
> has a compiler that is less than 10 years old, I'd suggest using C99
> syntax instead, which should avoid those differences and eliminate
> all gcc extensions.

I think this isn't only about the firmware (which, as far as I can tell,
is always built with a non-ancient version of GCC). The same header file
is used in other operating systems and I have no idea about the
toolchain situation there.

As for the NO_GCC_EXTENSIONS I think that's only used to avoid empty
structures and zero-sized arrays, which I assume not all supported
toolchains can deal with.

Sivaram, Timo: can you shed any light on the scope of operating systems
and toolchains that we need to support? Any ideas, short of manual
editing, that we can try to eliminate some of Arnd's concerns?

> > > > +struct cmd_clk_set_rate_request {
> > > > +	int32_t unused;
> > > > +	int64_t rate;
> > > > +} __ABI_PACKED;
> > > 
> > > This structure actually has a non-aligned struct member, but you
> > > can write that as
> > > 
> > > struct cmd_clk_set_rate_request {
> > > 	int32_t unused;
> > > 	int64_t rate;
> > > } __attribute__((packed, aligned(4)));
> > > 
> > > to still use a default four-byte alignment.
> > 
> > I thought the original would yield something like this in memory:
> > 
> > 	[unused]
> > 	[rate  ]
> > 	[rate  ]
> > 
> > because packing makes sure to avoid any padding introduced for natural
> > alignment. Isn't __attribute__((packd, aligned(4))) going to yield the
> > exact same layout?
> 
> Yes, only the alignment of the structure itself is changed, not
> the contents. The main difference is that accessing 'rate' on
> a target machine without unaligned access will use two 32-bit
> loads rather than eight 8-bit loads.
> 
> Also, embedding this structure in another one is different
> (in theory, I don't expect this to actually appear somewhere):
> 
> struct example {
> 	char a;
> 	struct cmd_clk_set_rate_request b;
> };
> 
> would currently be 13 bytes long, with the alignment I suggested you
> get three bytes of padding after 'a'.

Something that just occurred to me now is that the field that causes the
misalignment is actually unused. I can only imagine that some earlier
version of the ABI must have used it for some purpose and then it was
deprecated but kept for backwards-compatibility.

Sivaram, Timo: any ideas how this came about?

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 819 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20160822/894ce248/attachment.sig>

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 15:32                 ` Thierry Reding
@ 2016-08-22 15:43                     ` Arnd Bergmann
  -1 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 15:43 UTC (permalink / raw)
  To: Thierry Reding
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Sivaram Nair,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Peter De Schrijver, Timo Alho,
	Joseph Lo, linux-tegra-u79uwXL29TY76Z2rM5mHXA

On Monday, August 22, 2016 5:32:58 PM CEST Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 04:42:32PM +0200, Arnd Bergmann wrote:
> > On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> > > > > +struct mrq_request {
> > > > > +	/** @brief MRQ number of the request */
> > > > > +	uint32_t mrq;
> > > > > +	/** @brief flags for the request */
> > > > > +	uint32_t flags;
> > > > > +} __ABI_PACKED;
> > > > 
> > > > Marking the structure as packed may result in byte-wise access, depending
> > > > on compiler flags. Is that what you intended? The structure is fully
> > > > packed already, so you won't avoid any padding here.
> > > 
> > > Agreed, the packing seems unnecessary in many places. However this is
> > > defining an ABI that's used across multiple operating systems, so the
> > > packing may still be required on some systems or toolchains to ensure
> > > the exact same format in the transport.
> > 
> > However, if __ABI_PACKED is defined to an empty string, it is different
> > in some cases.
> > 
> > Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
> > some of the structures, by adding an extra member. If the firmware
> > has a compiler that is less than 10 years old, I'd suggest using C99
> > syntax instead, which should avoid those differences and eliminate
> > all gcc extensions.
> 
> I think this isn't only about the firmware (which, as far as I can tell,
> is always built with a non-ancient version of GCC). The same header file
> is used in other operating systems and I have no idea about the
> toolchain situation there.
> 
> As for the NO_GCC_EXTENSIONS I think that's only used to avoid empty
> structures and zero-sized arrays, which I assume not all supported
> toolchains can deal with.
>
> Sivaram, Timo: can you shed any light on the scope of operating systems
> and toolchains that we need to support? Any ideas, short of manual
> editing, that we can try to eliminate some of Arnd's concerns?

Ok. To clarify, C99 supports this syntax:

struct variable_length_struct {
	int length;
	char data[];
};

struct empty_struct {
	char nothing[];
};

which could be used in place of the gcc specific syntax in
portable code.

	Arnd

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 15:43                     ` Arnd Bergmann
  0 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-22 15:43 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday, August 22, 2016 5:32:58 PM CEST Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 04:42:32PM +0200, Arnd Bergmann wrote:
> > On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> > > > > +struct mrq_request {
> > > > > +	/** @brief MRQ number of the request */
> > > > > +	uint32_t mrq;
> > > > > +	/** @brief flags for the request */
> > > > > +	uint32_t flags;
> > > > > +} __ABI_PACKED;
> > > > 
> > > > Marking the structure as packed may result in byte-wise access, depending
> > > > on compiler flags. Is that what you intended? The structure is fully
> > > > packed already, so you won't avoid any padding here.
> > > 
> > > Agreed, the packing seems unnecessary in many places. However this is
> > > defining an ABI that's used across multiple operating systems, so the
> > > packing may still be required on some systems or toolchains to ensure
> > > the exact same format in the transport.
> > 
> > However, if __ABI_PACKED is defined to an empty string, it is different
> > in some cases.
> > 
> > Also, setting 'NO_GCC_EXTENSIONS' changes the structure layout of
> > some of the structures, by adding an extra member. If the firmware
> > has a compiler that is less than 10 years old, I'd suggest using C99
> > syntax instead, which should avoid those differences and eliminate
> > all gcc extensions.
> 
> I think this isn't only about the firmware (which, as far as I can tell,
> is always built with a non-ancient version of GCC). The same header file
> is used in other operating systems and I have no idea about the
> toolchain situation there.
> 
> As for the NO_GCC_EXTENSIONS I think that's only used to avoid empty
> structures and zero-sized arrays, which I assume not all supported
> toolchains can deal with.
>
> Sivaram, Timo: can you shed any light on the scope of operating systems
> and toolchains that we need to support? Any ideas, short of manual
> editing, that we can try to eliminate some of Arnd's concerns?

Ok. To clarify, C99 supports this syntax:

struct variable_length_struct {
	int length;
	char data[];
};

struct empty_struct {
	char nothing[];
};

which could be used in place of the gcc specific syntax in
portable code.

	Arnd

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-22 14:17         ` Thierry Reding
@ 2016-08-22 16:42             ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 16:42 UTC (permalink / raw)
  To: Thierry Reding, Arnd Bergmann
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/22/2016 08:17 AM, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 03:43:23PM +0200, Arnd Bergmann wrote:
>> On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
>>> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
>>> +                                       void *data)
>>> +{
>>> +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
>>> +
>>> +       return 0;
>>> +}
>>>
>>
>> It seems very odd that you don't write the data at all but just trigger
>> the communication here. My interpretation is that you bypass
>> the mailbox infrastructure for the actual message, and instead reimplement
>> it on top of the mailbox infrastructure as part of the bpmp driver.
>>
>> Why not expose the bpmp itself as the mailbox here?
>
> My understanding is that doorbell doesn't provide any means to transfer
> data. It merely signals the availability of data. It is up to a use-case
> specific implementation to arrange for the transport of data.

Yes, the doorbell doesn't transfer any data.

> BPMP implements a shmem-based protocol and uses the doorbell merely as a
> mechanism for the BPMP and CPU to signal each other when data is ready.
>
> Exposing the BPMP as the mailbox could possibly be done, but it would
> implement a fairly large software layer in the mailbox system which is
> targetted primarily at simple mailbox hardware that exchanges small
> amounts of data (usually fitting into 32 bits). mailbox-sti is another
> driver that uses the framework only to signal, so this doesn't seem to
> be a mechanism exclusive to Tegra.

I also don't think that in-memory IPC mechanisms are what the mailbox 
subsystem is all about; it's about pure HW mechanisms that transfer data.

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-22 16:42             ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 16:42 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/22/2016 08:17 AM, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 03:43:23PM +0200, Arnd Bergmann wrote:
>> On Friday, August 19, 2016 7:32:23 PM CEST Thierry Reding wrote:
>>> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
>>> +                                       void *data)
>>> +{
>>> +       tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
>>> +
>>> +       return 0;
>>> +}
>>>
>>
>> It seems very odd that you don't write the data at all but just trigger
>> the communication here. My interpretation is that you bypass
>> the mailbox infrastructure for the actual message, and instead reimplement
>> it on top of the mailbox infrastructure as part of the bpmp driver.
>>
>> Why not expose the bpmp itself as the mailbox here?
>
> My understanding is that doorbell doesn't provide any means to transfer
> data. It merely signals the availability of data. It is up to a use-case
> specific implementation to arrange for the transport of data.

Yes, the doorbell doesn't transfer any data.

> BPMP implements a shmem-based protocol and uses the doorbell merely as a
> mechanism for the BPMP and CPU to signal each other when data is ready.
>
> Exposing the BPMP as the mailbox could possibly be done, but it would
> implement a fairly large software layer in the mailbox system which is
> targetted primarily at simple mailbox hardware that exchanges small
> amounts of data (usually fitting into 32 bits). mailbox-sti is another
> driver that uses the framework only to signal, so this doesn't seem to
> be a mechanism exclusive to Tegra.

I also don't think that in-memory IPC mechanisms are what the mailbox 
subsystem is all about; it's about pure HW mechanisms that transfer data.

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 16:53         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 16:53 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This driver exposes a mailbox interface for interprocessor communication
> using the Hardware Synchronization Primitives (HSP) module's doorbell
> mechanism. There are multiple HSP instances and they provide additional
> features such as shared mailboxes, shared and arbitrated semaphores.
>
> A driver for a remote processor can use the mailbox client provided by
> the HSP driver and build an IPC protocol on top of this synchronization
> mechanism.

> +static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
...
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value |= BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	if (!tegra_hsp_doorbell_can_ring(db))
> +		return -ENODEV;

Should the error-check happen before enabling the RX path? Otherwise, 
presumably that HSP_DB_ENABLE bit should be cleared on failure.

> +static int tegra_hsp_probe(struct platform_device *pdev)

> +	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);

Line width?

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-22 16:53         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 16:53 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> This driver exposes a mailbox interface for interprocessor communication
> using the Hardware Synchronization Primitives (HSP) module's doorbell
> mechanism. There are multiple HSP instances and they provide additional
> features such as shared mailboxes, shared and arbitrated semaphores.
>
> A driver for a remote processor can use the mailbox client provided by
> the HSP driver and build an IPC protocol on top of this synchronization
> mechanism.

> +static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
...
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value |= BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	if (!tegra_hsp_doorbell_can_ring(db))
> +		return -ENODEV;

Should the error-check happen before enabling the RX path? Otherwise, 
presumably that HSP_DB_ENABLE bit should be cleared on failure.

> +static int tegra_hsp_probe(struct platform_device *pdev)

> +	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);

Line width?

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

* Re: [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 17:11         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 17:11 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This adds the initial support of Tegra186 SoC. It provides enough to
> enable the serial console and boot from an initial ramdisk.

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi

> +	uarta: serial@03100000 {
> +		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
> +		reg = <0x0 0x03100000 0x0 0x40>;

At least in the unit address, I think we want to suppress leading zeroes 
(s/03100000/3100000/). I'd argue we should do the same in the reg values 
too, although that makes no difference to the DTB itself. Same comment 
for other nodes.

> +	sysram@30000000 {
> +		compatible = "nvidia,tegra186-sysram", "mmio-ram";
> +		reg = <0x0 0x30000000 0x0 0x4ffff>;
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges = <0 0x0 0x0 0x30000000 0x0 0x4ffff>;

Shouldn't 0x4ffff be 0x50000 since it's length not max?

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

* [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
@ 2016-08-22 17:11         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 17:11 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> This adds the initial support of Tegra186 SoC. It provides enough to
> enable the serial console and boot from an initial ramdisk.

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi

> +	uarta: serial at 03100000 {
> +		compatible = "nvidia,tegra186-uart", "nvidia,tegra20-uart";
> +		reg = <0x0 0x03100000 0x0 0x40>;

At least in the unit address, I think we want to suppress leading zeroes 
(s/03100000/3100000/). I'd argue we should do the same in the reg values 
too, although that makes no difference to the DTB itself. Same comment 
for other nodes.

> +	sysram at 30000000 {
> +		compatible = "nvidia,tegra186-sysram", "mmio-ram";
> +		reg = <0x0 0x30000000 0x0 0x4ffff>;
> +		#address-cells = <2>;
> +		#size-cells = <2>;
> +		ranges = <0 0x0 0x0 0x30000000 0x0 0x4ffff>;

Shouldn't 0x4ffff be 0x50000 since it's length not max?

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

* Re: [PATCH v3 04/12] firmware: tegra: Add IVC library
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 18:49         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:49 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
>
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
>
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
>
> Based on work by Peter Newman <pnewman-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
> <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.

Reviewed-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

All the renaming/restructuring relative to the internal code made 
comparing them a bit more difficult that I'd like...

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
@ 2016-08-22 18:49         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:49 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
>
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
>
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
>
> Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.

Reviewed-by: Stephen Warren <swarren@nvidia.com>

All the renaming/restructuring relative to the internal code made 
comparing them a bit more difficult that I'd like...

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 12:54             ` Thierry Reding
@ 2016-08-22 18:51                 ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:51 UTC (permalink / raw)
  To: Thierry Reding, Jon Hunter
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/22/2016 06:54 AM, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>>
>>> The Boot and Power Management Processor (BPMP) is a co-processor found
>>> on Tegra SoCs. It is designed to handle the early stages of the boot
>>> process and offload power management tasks (such as clocks, resets,
>>> powergates, ...) as well as system control services.
>>>
>>> Compared to the ARM SCPI, the services provided by BPMP are message-
>>> based rather than method-based. The BPMP firmware driver provides the
>>> services to transmit data to and receive data from the BPMP. Users can
>>> also register an MRQ, for which a service routine will be run when a
>>> corresponding event is received from the firmware.
>>
>> MRQ?
>
> I think that means "Message ReQuest", which is sort of like an IRQ but
> the user will receive a message (with potentially payload) instead. Do
> you want me to spell that out in the commit message, or what would you
> suggest?

I believe MRQ refers to the ID/type/semantics of the message structure 
itself, not so much the notification aspect of having received a message.

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 18:51                 ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:51 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/22/2016 06:54 AM, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 10:26:50AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding@nvidia.com>
>>>
>>> The Boot and Power Management Processor (BPMP) is a co-processor found
>>> on Tegra SoCs. It is designed to handle the early stages of the boot
>>> process and offload power management tasks (such as clocks, resets,
>>> powergates, ...) as well as system control services.
>>>
>>> Compared to the ARM SCPI, the services provided by BPMP are message-
>>> based rather than method-based. The BPMP firmware driver provides the
>>> services to transmit data to and receive data from the BPMP. Users can
>>> also register an MRQ, for which a service routine will be run when a
>>> corresponding event is received from the firmware.
>>
>> MRQ?
>
> I think that means "Message ReQuest", which is sort of like an IRQ but
> the user will receive a message (with potentially payload) instead. Do
> you want me to spell that out in the commit message, or what would you
> suggest?

I believe MRQ refers to the ID/type/semantics of the message structure 
itself, not so much the notification aspect of having received a message.

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 14:42               ` Arnd Bergmann
@ 2016-08-22 18:56                 ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:56 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	Thierry Reding, Sivaram Nair, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Peter De Schrijver, Timo Alho, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA

On 08/22/2016 08:42 AM, Arnd Bergmann wrote:
> On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
>> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
>>> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
>>>> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
>>>> new file mode 100644
>>>> index 000000000000..0aaef5960e29
>>>> --- /dev/null
>>>> +++ b/include/soc/tegra/bpmp-abi.h
>>>> +#ifndef _ABI_BPMP_ABI_H_
>>>> +#define _ABI_BPMP_ABI_H_
>>>> +
>>>> +#ifdef LK
>>>> +#include <stdint.h>
>>>> +#endif
>>>> +
>>>> +#ifndef __ABI_PACKED
>>>> +#define __ABI_PACKED __attribute__((packed))
>>>> +#endif
>>>> +
>>>> +#ifdef NO_GCC_EXTENSIONS
>>>> +#define EMPTY char empty;
>>>> +#define EMPTY_ARRAY 1
>>>> +#else
>>>> +#define EMPTY
>>>> +#define EMPTY_ARRAY 0
>>>> +#endif
>>>> +
>>>> +#ifndef __UNION_ANON
>>>> +#define __UNION_ANON
>>>> +#endif
>>>
>>> Maybe keep these all out of the kernel?
>>
>> This was discussed a little in an earlier posting. This header file is
>> maintained by the BPMP firmware team and using it verbatim means little
>> to no effort required to update it.
>
> The usual recommendation is to just use Linux coding style in shared
> files, and possibly add another header that provides the required
> definitions. Otherwise you end up with people randomly cleaning up
> the file later ;-)

That's just going to push the problem into all other operating systems; 
what happens when BSD, Windows, QNX, FreeRTOS, ... import this file and 
attempt to apply the same argument, but want the FW team to use their 
coding style?

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 18:56                 ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 18:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/22/2016 08:42 AM, Arnd Bergmann wrote:
> On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
>> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
>>> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
>>>> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
>>>> new file mode 100644
>>>> index 000000000000..0aaef5960e29
>>>> --- /dev/null
>>>> +++ b/include/soc/tegra/bpmp-abi.h
>>>> +#ifndef _ABI_BPMP_ABI_H_
>>>> +#define _ABI_BPMP_ABI_H_
>>>> +
>>>> +#ifdef LK
>>>> +#include <stdint.h>
>>>> +#endif
>>>> +
>>>> +#ifndef __ABI_PACKED
>>>> +#define __ABI_PACKED __attribute__((packed))
>>>> +#endif
>>>> +
>>>> +#ifdef NO_GCC_EXTENSIONS
>>>> +#define EMPTY char empty;
>>>> +#define EMPTY_ARRAY 1
>>>> +#else
>>>> +#define EMPTY
>>>> +#define EMPTY_ARRAY 0
>>>> +#endif
>>>> +
>>>> +#ifndef __UNION_ANON
>>>> +#define __UNION_ANON
>>>> +#endif
>>>
>>> Maybe keep these all out of the kernel?
>>
>> This was discussed a little in an earlier posting. This header file is
>> maintained by the BPMP firmware team and using it verbatim means little
>> to no effort required to update it.
>
> The usual recommendation is to just use Linux coding style in shared
> files, and possibly add another header that provides the required
> definitions. Otherwise you end up with people randomly cleaning up
> the file later ;-)

That's just going to push the problem into all other operating systems; 
what happens when BSD, Windows, QNX, FreeRTOS, ... import this file and 
attempt to apply the same argument, but want the FW team to use their 
coding style?

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

* Re: [PATCH v3 06/12] soc/tegra: Add Tegra186 support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:01         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:01 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
> GPUs with Pascal architecture on it. It features with ADSP with
> Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
> multi-format support, ISP for image capture processing and BPMP for the
> power managements.

Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

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

* [PATCH v3 06/12] soc/tegra: Add Tegra186 support
@ 2016-08-22 19:01         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
> GPUs with Pascal architecture on it. It features with ADSP with
> Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
> multi-format support, ISP for image capture processing and BPMP for the
> power managements.

Acked-by: Stephen Warren <swarren@nvidia.com>

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

* Re: [PATCH v3 07/12] arm64: defconfig: Enable Tegra186 SoC
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:01         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:01 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> Enable Tegra186 SoC.

Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

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

* [PATCH v3 07/12] arm64: defconfig: Enable Tegra186 SoC
@ 2016-08-22 19:01         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:01 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> Enable Tegra186 SoC.

Acked-by: Stephen Warren <swarren@nvidia.com>

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

* Re: [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:07         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:07 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This adds the initial support of Tegra186 SoC. It provides enough to
> enable the serial console and boot from an initial ramdisk.

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi

> +	bpmp: bpmp {
> +		compatible = "nvidia,tegra186-bpmp";
> +		mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB
> +				    TEGRA_HSP_DB_MASTER_BPMP>;
> +		shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
> +		#clock-cells = <1>;
> +		#reset-cells = <1>;
> +
> +		bpmp_i2c: i2c {
> +			compatible = "nvidia,tegra186-bpmp-i2c";
> +			nvidia,bpmp = <&bpmp>;
> +			nvidia,bpmp-bus-id = <5>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			status = "disabled";
> +		};
> +	};

Back on 7/20, Rob Herring wrote:

> Just 'i2c' here. With that:
>
> Acked-by: Rob Herring <robh-DgEjT+Ai2ygdnm+yROfE0A@public.gmane.org>

... but this series doesn't yet include the BPMP I2C bindings (they've 
been ack'd so I assume they'll be applied right after this series is). 
Is it better to leave out the I2C sub-node until after the bindings are 
applied, or doesn't the order matter too?

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

* [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support
@ 2016-08-22 19:07         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:07 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> This adds the initial support of Tegra186 SoC. It provides enough to
> enable the serial console and boot from an initial ramdisk.

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186.dtsi b/arch/arm64/boot/dts/nvidia/tegra186.dtsi

> +	bpmp: bpmp {
> +		compatible = "nvidia,tegra186-bpmp";
> +		mboxes = <&hsp_top0 TEGRA_HSP_MBOX_TYPE_DB
> +				    TEGRA_HSP_DB_MASTER_BPMP>;
> +		shmem = <&cpu_bpmp_tx &cpu_bpmp_rx>;
> +		#clock-cells = <1>;
> +		#reset-cells = <1>;
> +
> +		bpmp_i2c: i2c {
> +			compatible = "nvidia,tegra186-bpmp-i2c";
> +			nvidia,bpmp = <&bpmp>;
> +			nvidia,bpmp-bus-id = <5>;
> +			#address-cells = <1>;
> +			#size-cells = <0>;
> +			status = "disabled";
> +		};
> +	};

Back on 7/20, Rob Herring wrote:

> Just 'i2c' here. With that:
>
> Acked-by: Rob Herring <robh@kernel.org>

... but this series doesn't yet include the BPMP I2C bindings (they've 
been ack'd so I assume they'll be applied right after this series is). 
Is it better to leave out the I2C sub-node until after the bindings are 
applied, or doesn't the order matter too?

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

* Re: [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:08         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:08 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> The NVIDIA P3310 is a processor module used in several reference designs
> that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
> essentials such as ethernet, WiFi and a PMIC. It is typically connected
> to an I/O board (such as the P2597) that provides the connecters needed
> to hook it up to the outside world.

Acked-by: Stephen Warren <swarren-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>

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

* [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
@ 2016-08-22 19:08         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:08 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> The NVIDIA P3310 is a processor module used in several reference designs
> that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
> essentials such as ethernet, WiFi and a PMIC. It is typically connected
> to an I/O board (such as the P2597) that provides the connecters needed
> to hook it up to the outside world.

Acked-by: Stephen Warren <swarren@nvidia.com>

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

* Re: [PATCH v3 10/12] arm64: dts: tegra: Add NVIDIA P2771 board support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:11         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:11 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> Add NVIDIA P2771 is composed of a P3310 processor module that connects
> to the P2597 I/O board. It comes with a 1200x1920 MIPI DSI panel
> connected via the P2597's display connector and has several connectors
> such as HDMI, USB 3.0, PCIe and ethernet.

>  arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 8 ++++++++

The DT will be different for the different HW versions. So far, there 
are at least 3 different versions with 3 combinations of 2 different HW 
changes (PCIe lane layout, and SD card GPIO assignments). I suspect 
we'll want this file to be tegra186-p2771-0000-NNN.dts where NNN is the 
version number from the EEPROM (which is 000 for the first version, 300 
for the version that introduces the first change, and 500 for the 
version that introduces the second change).

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts

> +#include "tegra186-p3310.dtsi"
> +
> +/ {
> +	model = "NVIDIA Tegra186 P2771-0000 Board";
> +	compatible = "nvidia,p2771-0000", "nvidia,tegra186";

Do we need to propagate the "nvidia,p3310" entry from the included CVM 
DT? It'd be nice if we could write:

/prepend/ compatible = "nvidia,p2771-0000";

... and have that happen automatically.

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

* [PATCH v3 10/12] arm64: dts: tegra: Add NVIDIA P2771 board support
@ 2016-08-22 19:11         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:11 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
>
> Add NVIDIA P2771 is composed of a P3310 processor module that connects
> to the P2597 I/O board. It comes with a 1200x1920 MIPI DSI panel
> connected via the P2597's display connector and has several connectors
> such as HDMI, USB 3.0, PCIe and ethernet.

>  arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 8 ++++++++

The DT will be different for the different HW versions. So far, there 
are at least 3 different versions with 3 combinations of 2 different HW 
changes (PCIe lane layout, and SD card GPIO assignments). I suspect 
we'll want this file to be tegra186-p2771-0000-NNN.dts where NNN is the 
version number from the EEPROM (which is 000 for the first version, 300 
for the version that introduces the first change, and 500 for the 
version that introduces the second change).

> diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts

> +#include "tegra186-p3310.dtsi"
> +
> +/ {
> +	model = "NVIDIA Tegra186 P2771-0000 Board";
> +	compatible = "nvidia,p2771-0000", "nvidia,tegra186";

Do we need to propagate the "nvidia,p3310" entry from the included CVM 
DT? It'd be nice if we could write:

/prepend/ compatible = "nvidia,p2771-0000";

... and have that happen automatically.

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

* Re: [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:47         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:47 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This driver uses the services provided by the BPMP firmware driver to
> implement a clock driver based on the MRQ_CLK request. This part of the
> BPMP ABI provides a means to enumerate and control clocks and should
> allow the driver to work on any chip that supports this ABI.

> diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c

> +#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
> +#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
> +#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)

Shouldn't those be defined in the BPMP ABI header?

> +static int
> +tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);

So the TX payload gets memcpy()d once here to combine it with the 
request header, and again inside the BPMP driver to copy it to the IVC 
shared memory. Does it make sense to eliminate the copy here, and 
require callers to allocate and fill in the entire TX structure? The 
"(clk->cmd << 24) | clk->clk" could be wrapped in a static inline 
function to avoid any duplication of logic.

> +static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
> +				   const struct tegra_bpmp_clk_message *clk)
...
> +	err = tegra_bpmp_transfer(bpmp, &msg);
> +	if (err != -EAGAIN)
> +		return err;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
> +}

This seems odd; can you add some comments indicating why there's a need 
for a retry, and why it falls back to the _atomic() function rather than 
just retrying?

Nit: Perhaps s/tegra_bpmp_clk/tegra_clk_bpmp/ for all symbols 
implemented in this driver; it can be a little hard to tell which 
function calls are to the Tegra BPMP driver (tegra_bpmp_*), and which 
are to the Tegra clock driver that's implemented using BPMP 
(tegra_bpmp_clk_*).

> +static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *parent_rate)
> +{
> +	return 0;
> +}
> +
> +static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	return 0;
> +}

Aren't those all missing an implementation?

> +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> +				   struct tegra_bpmp_clk_info **clocksp)

> +	for (id = 0; id <= max_id; id++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[count];
> +#if 0
> +		const char *prefix = "";
> +		struct seq_buf buf;
> +		unsigned int i;
> +		char flags[64];
> +#endif

Should the #if 0 be removed? checkpatch would warn about this; was it 
run and if not would it find other things to complain about?

> +static struct clk_hw *
> +tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
> +			const struct tegra_bpmp_clk_info *info,
> +			const struct tegra_bpmp_clk_info *clocks,
> +			unsigned int num_clocks)
> +{
> +	struct tegra_bpmp_clk *priv;
> +	struct clk_init_data init;
...
> +	/* hardware clock initialization */
> +	priv->hw.init = &init;
> +	init.name = info->name;
...
> +	clk = clk_register(bpmp->dev, &priv->hw);

Is priv->hw.init guaranteed to only be used inside the call to 
clk_register()? If not, it's storing a pointer to the stack for longer 
than it's valid.

> +	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
> +	if (!parents)
> +		return ERR_PTR(-ENOMEM);

That needs to free priv->parents which was allocated earlier this function.

> +static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
> +					      void *data)
> +{
> +	unsigned int id = clkspec->args[0], i;

Should this function validate the cell count first? Too small means 
args[0] doesn't contain valid data, and too large means the DT is bogus, 
and we should at least warn the user they've included extra cruft, since 
it could in theory gain additional meaning later on if the binding gets 
extended.

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
@ 2016-08-22 19:47         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:47 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> This driver uses the services provided by the BPMP firmware driver to
> implement a clock driver based on the MRQ_CLK request. This part of the
> BPMP ABI provides a means to enumerate and control clocks and should
> allow the driver to work on any chip that supports this ABI.

> diff --git a/drivers/clk/tegra/clk-bpmp.c b/drivers/clk/tegra/clk-bpmp.c

> +#define TEGRA_BPMP_CLK_HAS_MUX		BIT(0)
> +#define TEGRA_BPMP_CLK_HAS_SET_RATE	BIT(1)
> +#define TEGRA_BPMP_CLK_IS_ROOT		BIT(2)

Shouldn't those be defined in the BPMP ABI header?

> +static int
> +tegra_bpmp_clk_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       const struct tegra_bpmp_clk_message *clk)
> +{
> +	struct mrq_clk_request request;
> +	struct tegra_bpmp_message msg;
> +	void *req = (void *)&request;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.cmd_and_id = (clk->cmd << 24) | clk->clk;
> +	memcpy(req + 4, clk->tx.data, clk->tx.size);

So the TX payload gets memcpy()d once here to combine it with the 
request header, and again inside the BPMP driver to copy it to the IVC 
shared memory. Does it make sense to eliminate the copy here, and 
require callers to allocate and fill in the entire TX structure? The 
"(clk->cmd << 24) | clk->clk" could be wrapped in a static inline 
function to avoid any duplication of logic.

> +static int tegra_bpmp_clk_transfer(struct tegra_bpmp *bpmp,
> +				   const struct tegra_bpmp_clk_message *clk)
...
> +	err = tegra_bpmp_transfer(bpmp, &msg);
> +	if (err != -EAGAIN)
> +		return err;
> +
> +	return __tegra_bpmp_clk_transfer_atomic(bpmp, &msg);
> +}

This seems odd; can you add some comments indicating why there's a need 
for a retry, and why it falls back to the _atomic() function rather than 
just retrying?

Nit: Perhaps s/tegra_bpmp_clk/tegra_clk_bpmp/ for all symbols 
implemented in this driver; it can be a little hard to tell which 
function calls are to the Tegra BPMP driver (tegra_bpmp_*), and which 
are to the Tegra clock driver that's implemented using BPMP 
(tegra_bpmp_clk_*).

> +static int tegra_bpmp_clk_set_rate(struct clk_hw *hw, unsigned long rate,
> +				   unsigned long parent_rate)
> +{
> +	return 0;
> +}
> +
> +static long tegra_bpmp_clk_round_rate(struct clk_hw *hw, unsigned long rate,
> +				      unsigned long *parent_rate)
> +{
> +	return 0;
> +}
> +
> +static unsigned long tegra_bpmp_clk_recalc_rate(struct clk_hw *hw,
> +						unsigned long parent_rate)
> +{
> +	return 0;
> +}

Aren't those all missing an implementation?

> +static int tegra_bpmp_probe_clocks(struct tegra_bpmp *bpmp,
> +				   struct tegra_bpmp_clk_info **clocksp)

> +	for (id = 0; id <= max_id; id++) {
> +		struct tegra_bpmp_clk_info *info = &clocks[count];
> +#if 0
> +		const char *prefix = "";
> +		struct seq_buf buf;
> +		unsigned int i;
> +		char flags[64];
> +#endif

Should the #if 0 be removed? checkpatch would warn about this; was it 
run and if not would it find other things to complain about?

> +static struct clk_hw *
> +tegra_bpmp_clk_register(struct tegra_bpmp *bpmp,
> +			const struct tegra_bpmp_clk_info *info,
> +			const struct tegra_bpmp_clk_info *clocks,
> +			unsigned int num_clocks)
> +{
> +	struct tegra_bpmp_clk *priv;
> +	struct clk_init_data init;
...
> +	/* hardware clock initialization */
> +	priv->hw.init = &init;
> +	init.name = info->name;
...
> +	clk = clk_register(bpmp->dev, &priv->hw);

Is priv->hw.init guaranteed to only be used inside the call to 
clk_register()? If not, it's storing a pointer to the stack for longer 
than it's valid.

> +	parents = kcalloc(info->num_parents, sizeof(*parents), GFP_KERNEL);
> +	if (!parents)
> +		return ERR_PTR(-ENOMEM);

That needs to free priv->parents which was allocated earlier this function.

> +static struct clk_hw *tegra_bpmp_clk_of_xlate(struct of_phandle_args *clkspec,
> +					      void *data)
> +{
> +	unsigned int id = clkspec->args[0], i;

Should this function validate the cell count first? Too small means 
args[0] doesn't contain valid data, and too large means the DT is bogus, 
and we should at least warn the user they've included extra cruft, since 
it could in theory gain additional meaning later on if the binding gets 
extended.

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

* Re: [PATCH v3 12/12] reset: Add Tegra BPMP reset driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 19:56         ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:56 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>
> This driver uses the services provided by the BPMP firmware driver to
> implement a reset driver based on the MRQ_RESET request.

> diff --git a/drivers/reset/tegra/reset-bpmp.c b/drivers/reset/tegra/reset-bpmp.c

> +static struct tegra_bpmp *to_tegra_bpmp(struct reset_controller_dev *rstc)
> +{
> +	return container_of(rstc, struct tegra_bpmp, rstc);
> +}

It seems rather odd for the BPMP to include a struct 
reset_controller_dev inside it. Rather, I'd expect a separate reset 
controller struct to be defined here, which contains a pointer to the 
BPMP. Otherwise, the BPMP structure is going to get all kinds of stuff 
added to it.

Put another way, I'd expect the BPMP driver to be nothing more than an 
IPC provider, with all the clock/reset/power-domain/I2C/... logic 
contained in drivers that simply use that IPC service and nothing more, 
rather than clock/reset/power-domain/I2C/... being entirely in-bed with 
the core BPMP driver.

(I haven't reviewed patch 5/12, the BPMP driver yet BTW).

> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
...
> +	bpmp->rstc.nr_resets = bpmp->soc->num_resets;

Presumably the BPMP already validates reset IDs. Can we leave all the 
validation to the BPMP? That way, we wouldn't need the SoC data to 
include a num_resets field, and hence wouldn't need to update the Linux 
driver in order to support a FW that supported more resets.

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

* [PATCH v3 12/12] reset: Add Tegra BPMP reset driver
@ 2016-08-22 19:56         ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 19:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> This driver uses the services provided by the BPMP firmware driver to
> implement a reset driver based on the MRQ_RESET request.

> diff --git a/drivers/reset/tegra/reset-bpmp.c b/drivers/reset/tegra/reset-bpmp.c

> +static struct tegra_bpmp *to_tegra_bpmp(struct reset_controller_dev *rstc)
> +{
> +	return container_of(rstc, struct tegra_bpmp, rstc);
> +}

It seems rather odd for the BPMP to include a struct 
reset_controller_dev inside it. Rather, I'd expect a separate reset 
controller struct to be defined here, which contains a pointer to the 
BPMP. Otherwise, the BPMP structure is going to get all kinds of stuff 
added to it.

Put another way, I'd expect the BPMP driver to be nothing more than an 
IPC provider, with all the clock/reset/power-domain/I2C/... logic 
contained in drivers that simply use that IPC service and nothing more, 
rather than clock/reset/power-domain/I2C/... being entirely in-bed with 
the core BPMP driver.

(I haven't reviewed patch 5/12, the BPMP driver yet BTW).

> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp)
...
> +	bpmp->rstc.nr_resets = bpmp->soc->num_resets;

Presumably the BPMP already validates reset IDs. Can we leave all the 
validation to the BPMP? That way, we wouldn't need the SoC data to 
include a num_resets field, and hence wouldn't need to update the Linux 
driver in order to support a FW that supported more resets.

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-22 22:23       ` Stephen Warren
  -1 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 22:23 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Sivaram Nair, devicetree, Peter De Schrijver, Timo Alho,
	Joseph Lo, linux-tegra, linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
>
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.
>
> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
>
> Based on work by Sivaram Nair <sivaramn@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.

Overall, this driver could use comments describing how it works. Even a 
few or a few tens of lines describing the general structure of how 
messages pass from the initial client's function call into the IPC 
memory and back up to the return to the original caller would be useful. 
There seem to be many varied paths for this, and it's a bit difficult to 
reverse engineer which are used when and why.

> diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h

> +struct tegra_bpmp_soc {
> +	struct {
> +		struct {
> +			unsigned int offset;
> +			unsigned int count;
> +			unsigned int timeout;
> +		} cpu_tx, thread, cpu_rx;
> +	} channels;
> +	unsigned int num_resets;
> +};

This deserves a comment to describe what the offset/count/timeout 
represent. It took me a while to realize that each CPU had its own 
channel in IVC memory, and there was a pool of channels for threads to 
use. At least, that's what I reversed engineered is going on... (Having 
only used BPMP from U-Boot, where there's only a single CPU/thread 
active ever and no IRQs, hence we always use channel 0).

> +struct tegra_bpmp_mb_data {
> +	u32 code;
> +	u32 flags;
> +	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
> +} __packed;

Shouldn't struct mrq_request from bpmp_abi.h be used instead of 
redefining this?

> +struct tegra_bpmp {
...
> +	unsigned int num_clocks;
> +	struct clk_hw **clocks;
> +
> +	struct reset_controller_dev rstc;
> +};

As I mentioned elsewhere, I'm not totally convinced that the BPMP driver 
should be anything more than a fairly transparent IPC channel; all the 
clock/reset/... stuff could be shuffled entirely into child devices.

(out-of-order quote)
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);

Oh I see; the clock/reset code isn't actually separate drivers (like 
MFD), but just a set of functions that run "in the context of" the main 
BPMP driver. Maybe that's OK, but I was expected more MFD style, hence 
various other comments related to this topic, like just above.

(FWIW, I've been mostly in U-Boot-land recently, and the driver model 
there requires each device to be of a separate class like clock, reset, 
... so the only option is separate MFD style sub-devices. However, the 
Linux subsystems allow a single device to implement various subsystems 
at once. No doubt U-Boot's model has warped my thinking a bit.)

> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp);

Those don't seem to be used; can they be dropped?

> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data);
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			 void *data);

Those are only used inside the BPMP driver itself right now at least 
(i.e. not in the clock/reset drivers in this series). Can they be static 
rather than public?

> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c

> +#define MSG_ACK		BIT(0)
> +#define MSG_RING	BIT(1)

It'd be nice if bpmp_abi.h defined those, but I know it doesn't:-(

> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
> +{
> +	struct platform_device *pdev;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +
> +	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
> +	if (!np)
> +		return ERR_PTR(-ENOENT);

That property name isn't defined by any DT binding. I think it's 
left-over from an old I2C sub-node binding.

> +static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ob = NULL;
> +		return false;
> +	}
> +
> +	channel->ob = frame;
> +
> +	return true;
> +}

I'm confused by the function name; this doesn't seem to be freeing 
anything. Is it really tegra_bpmp_get_next_free_tx_frame() or something 
like that? Same for tegra_bpmp_wait_master_free() below.

> +static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
> +						  unsigned int mrq)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +
> +	list_for_each_entry(entry, &bpmp->mrqs, list)
> +		if (entry->mrq == mrq)
> +			return entry;
> +
> +	return NULL;
> +}

I believe an MRQ is a type of message, not the identity of an individual 
message. As such, there can be multiple instances (message) of a 
particular type/MRQ. Is there code somewhere to ensure that only a 
single request of any given type is outstanding at a time? I'm not sure 
why anything would ever be looked up by MRQ...

> +static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
> +				       size_t size)
...
> +	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
> +				  GFP_KERNEL | GFP_DMA32);
> +	if (!virt)
> +		return -ENOMEM;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.addr = phys;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_QUERY_TAG;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	if (err == 0)
> +		strlcpy(tag, virt, size);

virt seems to be allocated and copied from, but never used in the IPC 
transaction.

> +static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long flags = channel->ob->flags;
> +
> +	if ((flags & MSG_RING) == 0)
> +		return;
> +
> +	complete(&channel->completion);
> +}

Shouldn't that always call complete()? tegra_bpmp_transfer() always call 
wait_for_completion_timeout(), irrespective of the flags value.

> +static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
> +				   struct tegra_bpmp *bpmp,
> +				   unsigned int index)
> +{
> +	size_t message, queue;
...
> +	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
> +	queue = tegra_ivc_total_queue_size(message);

I suspect those should be message_size and message_queue, otherwise the 
variable names sound like they should be pointers to objects, not 
properties of the objects.

...
> +	/* reset the channel state */
> +	tegra_ivc_reset(channel->ivc);
> +
> +	/* sync the channel state with BPMP */
> +	while (tegra_ivc_notified(channel->ivc))
> +		;

I think this synchronously waits for the single channel in question to 
reset, one-by-one. Won't that take rather a long time; wouldn't it be 
better to initialize an start-the-reset-of all channels, then wait for 
the reset to complete across all channels in parallel?

> +static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)

Is this function necessary? It doesn't seem to do anything with the 
response data except pass it to dev_dbg() which probably doesn't do 
anything.

> +static int __init tegra_bpmp_init(void)
> +{
> +	return platform_driver_register(&tegra_bpmp_driver);
> +}
> +core_initcall(tegra_bpmp_init);

Can that boiler-plat be replaced with module_init_driver(); IIRC that's 
appropriate and often used even for non-modular drivers?

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-22 22:23       ` Stephen Warren
  0 siblings, 0 replies; 104+ messages in thread
From: Stephen Warren @ 2016-08-22 22:23 UTC (permalink / raw)
  To: linux-arm-kernel

On 08/19/2016 11:32 AM, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
>
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
>
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.
>
> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
>
> Based on work by Sivaram Nair <sivaramn@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.

Overall, this driver could use comments describing how it works. Even a 
few or a few tens of lines describing the general structure of how 
messages pass from the initial client's function call into the IPC 
memory and back up to the return to the original caller would be useful. 
There seem to be many varied paths for this, and it's a bit difficult to 
reverse engineer which are used when and why.

> diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h

> +struct tegra_bpmp_soc {
> +	struct {
> +		struct {
> +			unsigned int offset;
> +			unsigned int count;
> +			unsigned int timeout;
> +		} cpu_tx, thread, cpu_rx;
> +	} channels;
> +	unsigned int num_resets;
> +};

This deserves a comment to describe what the offset/count/timeout 
represent. It took me a while to realize that each CPU had its own 
channel in IVC memory, and there was a pool of channels for threads to 
use. At least, that's what I reversed engineered is going on... (Having 
only used BPMP from U-Boot, where there's only a single CPU/thread 
active ever and no IRQs, hence we always use channel 0).

> +struct tegra_bpmp_mb_data {
> +	u32 code;
> +	u32 flags;
> +	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
> +} __packed;

Shouldn't struct mrq_request from bpmp_abi.h be used instead of 
redefining this?

> +struct tegra_bpmp {
...
> +	unsigned int num_clocks;
> +	struct clk_hw **clocks;
> +
> +	struct reset_controller_dev rstc;
> +};

As I mentioned elsewhere, I'm not totally convinced that the BPMP driver 
should be anything more than a fairly transparent IPC channel; all the 
clock/reset/... stuff could be shuffled entirely into child devices.

(out-of-order quote)
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);

Oh I see; the clock/reset code isn't actually separate drivers (like 
MFD), but just a set of functions that run "in the context of" the main 
BPMP driver. Maybe that's OK, but I was expected more MFD style, hence 
various other comments related to this topic, like just above.

(FWIW, I've been mostly in U-Boot-land recently, and the driver model 
there requires each device to be of a separate class like clock, reset, 
... so the only option is separate MFD style sub-devices. However, the 
Linux subsystems allow a single device to implement various subsystems 
at once. No doubt U-Boot's model has warped my thinking a bit.)

> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp);

Those don't seem to be used; can they be dropped?

> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data);
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			 void *data);

Those are only used inside the BPMP driver itself right now at least 
(i.e. not in the clock/reset drivers in this series). Can they be static 
rather than public?

> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c

> +#define MSG_ACK		BIT(0)
> +#define MSG_RING	BIT(1)

It'd be nice if bpmp_abi.h defined those, but I know it doesn't:-(

> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
> +{
> +	struct platform_device *pdev;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +
> +	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
> +	if (!np)
> +		return ERR_PTR(-ENOENT);

That property name isn't defined by any DT binding. I think it's 
left-over from an old I2C sub-node binding.

> +static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ob = NULL;
> +		return false;
> +	}
> +
> +	channel->ob = frame;
> +
> +	return true;
> +}

I'm confused by the function name; this doesn't seem to be freeing 
anything. Is it really tegra_bpmp_get_next_free_tx_frame() or something 
like that? Same for tegra_bpmp_wait_master_free() below.

> +static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
> +						  unsigned int mrq)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +
> +	list_for_each_entry(entry, &bpmp->mrqs, list)
> +		if (entry->mrq == mrq)
> +			return entry;
> +
> +	return NULL;
> +}

I believe an MRQ is a type of message, not the identity of an individual 
message. As such, there can be multiple instances (message) of a 
particular type/MRQ. Is there code somewhere to ensure that only a 
single request of any given type is outstanding at a time? I'm not sure 
why anything would ever be looked up by MRQ...

> +static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
> +				       size_t size)
...
> +	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
> +				  GFP_KERNEL | GFP_DMA32);
> +	if (!virt)
> +		return -ENOMEM;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.addr = phys;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_QUERY_TAG;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	if (err == 0)
> +		strlcpy(tag, virt, size);

virt seems to be allocated and copied from, but never used in the IPC 
transaction.

> +static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long flags = channel->ob->flags;
> +
> +	if ((flags & MSG_RING) == 0)
> +		return;
> +
> +	complete(&channel->completion);
> +}

Shouldn't that always call complete()? tegra_bpmp_transfer() always call 
wait_for_completion_timeout(), irrespective of the flags value.

> +static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
> +				   struct tegra_bpmp *bpmp,
> +				   unsigned int index)
> +{
> +	size_t message, queue;
...
> +	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
> +	queue = tegra_ivc_total_queue_size(message);

I suspect those should be message_size and message_queue, otherwise the 
variable names sound like they should be pointers to objects, not 
properties of the objects.

...
> +	/* reset the channel state */
> +	tegra_ivc_reset(channel->ivc);
> +
> +	/* sync the channel state with BPMP */
> +	while (tegra_ivc_notified(channel->ivc))
> +		;

I think this synchronously waits for the single channel in question to 
reset, one-by-one. Won't that take rather a long time; wouldn't it be 
better to initialize an start-the-reset-of all channels, then wait for 
the reset to complete across all channels in parallel?

> +static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)

Is this function necessary? It doesn't seem to do anything with the 
response data except pass it to dev_dbg() which probably doesn't do 
anything.

> +static int __init tegra_bpmp_init(void)
> +{
> +	return platform_driver_register(&tegra_bpmp_driver);
> +}
> +core_initcall(tegra_bpmp_init);

Can that boiler-plat be replaced with module_init_driver(); IIRC that's 
appropriate and often used even for non-modular drivers?

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-23  0:06         ` Sivaram Nair
  -1 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23  0:06 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Fri, Aug 19, 2016 at 07:32:23PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> This driver exposes a mailbox interface for interprocessor communication
> using the Hardware Synchronization Primitives (HSP) module's doorbell
> mechanism. There are multiple HSP instances and they provide additional
> features such as shared mailboxes, shared and arbitrated semaphores.
> 
> A driver for a remote processor can use the mailbox client provided by
> the HSP driver and build an IPC protocol on top of this synchronization
> mechanism.
> 
> Based on work by Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.
> 
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v3:
> - use a more object oriented design
> - fix some locking issues
> 
>  drivers/mailbox/Kconfig     |   9 +
>  drivers/mailbox/Makefile    |   2 +
>  drivers/mailbox/tegra-hsp.c | 561 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 572 insertions(+)
>  create mode 100644 drivers/mailbox/tegra-hsp.c
> 
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index 97c372908e78..90e7b94a1199 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -114,6 +114,15 @@ config MAILBOX_TEST
>  	  Test client to help with testing new Controller driver
>  	  implementations.
>  
> +config TEGRA_HSP_MBOX
> +	bool "Tegra HSP(Hardware Synchronization Primitives) Driver"
> +	depends on ARCH_TEGRA_186_SOC
> +	help
> +	  The Tegra HSP driver is used for the interprocessor communication
> +	  between different remote processors and host processors on Tegra186
> +	  and later SoCs. Say Y here if you want to have this support.
> +	  If unsure say N.
> +
>  config XGENE_SLIMPRO_MBOX
>  	tristate "APM SoC X-Gene SLIMpro Mailbox Controller"
>  	depends on ARCH_XGENE
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 66c38e300dfc..3fa01673f288 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -27,3 +27,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
>  obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
>  
>  obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
> +
> +obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
> diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
> new file mode 100644
> index 000000000000..8770ba9b58fe
> --- /dev/null
> +++ b/drivers/mailbox/tegra-hsp.c
> @@ -0,0 +1,561 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_controller.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/mailbox/tegra186-hsp.h>
> +
> +#define HSP_INT_DIMENSIONING	0x380
> +#define HSP_nSM_SHIFT		0
> +#define HSP_nSS_SHIFT		4
> +#define HSP_nAS_SHIFT		8
> +#define HSP_nDB_SHIFT		12
> +#define HSP_nSI_SHIFT		16
> +#define HSP_nINT_MASK		0xf
> +
> +#define HSP_DB_TRIGGER	0x0
> +#define HSP_DB_ENABLE	0x4
> +#define HSP_DB_RAW	0x8
> +#define HSP_DB_PENDING	0xc
> +
> +#define HSP_DB_CCPLEX		1
> +#define HSP_DB_BPMP		3
> +#define HSP_DB_MAX		7
> +
> +struct tegra_hsp_channel;
> +struct tegra_hsp;
> +
> +struct tegra_hsp_channel_ops {
> +	int (*send_data)(struct tegra_hsp_channel *channel, void *data);
> +	int (*startup)(struct tegra_hsp_channel *channel);
> +	void (*shutdown)(struct tegra_hsp_channel *channel);
> +	bool (*last_tx_done)(struct tegra_hsp_channel *channel);
> +};
> +
> +struct tegra_hsp_channel {
> +	struct tegra_hsp *hsp;
> +	const struct tegra_hsp_channel_ops *ops;
> +	struct mbox_chan *chan;
> +	void __iomem *regs;
> +};
> +
> +static struct tegra_hsp_channel *to_tegra_hsp_channel(struct mbox_chan *chan)
> +{
> +	return chan->con_priv;
> +}
> +
> +struct tegra_hsp_doorbell {
> +	struct tegra_hsp_channel channel;
> +	struct list_head list;
> +	const char *name;
> +	unsigned int master;
> +	unsigned int index;
> +};
> +
> +static struct tegra_hsp_doorbell *
> +to_tegra_hsp_doorbell(struct tegra_hsp_channel *channel)
> +{
> +	if (!channel)
> +		return NULL;
> +
> +	return container_of(channel, struct tegra_hsp_doorbell, channel);
> +}
> +
> +struct tegra_hsp_db_map {
> +	const char *name;
> +	unsigned int master;
> +	unsigned int index;
> +};
> +
> +struct tegra_hsp_soc {
> +	const struct tegra_hsp_db_map *map;
> +};
> +
> +struct tegra_hsp {
> +	const struct tegra_hsp_soc *soc;
> +	struct mbox_controller mbox;
> +	void __iomem *regs;
> +	unsigned int irq;
> +	unsigned int num_sm;
> +	unsigned int num_as;
> +	unsigned int num_ss;
> +	unsigned int num_db;
> +	unsigned int num_si;
> +	spinlock_t lock;
> +
> +	struct list_head doorbells;
> +};
> +
> +static inline struct tegra_hsp *
> +to_tegra_hsp(struct mbox_controller *mbox)
> +{
> +	return container_of(mbox, struct tegra_hsp, mbox);
> +}
> +
> +static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
> +{
> +	return readl(hsp->regs + offset);
> +}
> +
> +static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
> +				    unsigned int offset)
> +{
> +	writel(value, hsp->regs + offset);
> +	(void)readl(hsp->regs + offset);

I am not aware of a need for such barriers (even if the downstream code
has them).

> +}
> +
> +static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
> +					  unsigned int offset)
> +{
> +	return readl(channel->regs + offset);
> +}
> +
> +static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
> +					    u32 value, unsigned int offset)
> +{
> +	writel(value, channel->regs + offset);
> +	(void)readl(channel->regs + offset);

same here.

> +}
> +
> +static int tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
> +{
> +	u32 value;
> +
> +	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
> +
> +	return !!(value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX));
> +}
> +
> +static struct tegra_hsp_doorbell *
> +__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
> +{
> +	struct tegra_hsp_doorbell *db = NULL, *entry;
> +
> +	list_for_each_entry(entry, &hsp->doorbells, list)
> +		if (entry->master == master) {

you might as well say: 'return entry;' and remove 'db' ?

> +			db = entry;
> +			break;
> +		}
> +
> +	return db;
> +}
> +
> +static struct tegra_hsp_doorbell *
> +tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +	db = __tegra_hsp_doorbell_get(hsp, master);
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	return db;
> +}
> +
> +static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
> +{
> +	struct tegra_hsp *hsp = data;
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long master, value;
> +
> +	db = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!db)
> +		return IRQ_NONE;
> +
> +	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_PENDING);
> +	tegra_hsp_channel_writel(&db->channel, value, HSP_DB_PENDING);
> +
> +	spin_lock(&hsp->lock);
> +
> +	for_each_set_bit(master, &value, hsp->mbox.num_chans) {
> +		struct tegra_hsp_doorbell *db;
> +
> +		db = __tegra_hsp_doorbell_get(hsp, master);
> +		if (db)
> +			mbox_chan_received_data(db->channel.chan, NULL);
> +	}
> +
> +	spin_unlock(&hsp->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> +					void *data)
> +{
> +	tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> +
> +	return 0;
> +}
> +
> +static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
> +{
> +	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
> +	struct tegra_hsp *hsp = channel->hsp;
> +	struct tegra_hsp_doorbell *ccplex;
> +	unsigned long flags;
> +	u32 value;
> +
> +	dev_dbg(hsp->mbox.dev, "> %s(channel=%p)\n", __func__, channel);
> +	dev_dbg(hsp->mbox.dev, "  regs: %p\n", channel->regs);
> +
> +	if (db->master >= hsp->mbox.num_chans) {
> +		dev_err(hsp->mbox.dev,
> +			"invalid master ID %u for HSP channel\n",
> +			db->master);
> +		return -EINVAL;
> +	}
> +
> +	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!ccplex)
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value |= BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	if (!tegra_hsp_doorbell_can_ring(db))
> +		return -ENODEV;
> +
> +	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_channel *channel)
> +{
> +	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
> +	struct tegra_hsp *hsp = channel->hsp;
> +	struct tegra_hsp_doorbell *ccplex;
> +	unsigned long flags;
> +	u32 value;
> +
> +	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!ccplex)
> +		return;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value &= ~BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +}
> +
> +static bool tegra_hsp_doorbell_last_tx_done(struct tegra_hsp_channel *channel)
> +{
> +	return true;
> +}
> +
> +static const struct tegra_hsp_channel_ops tegra_hsp_doorbell_ops = {
> +	.send_data = tegra_hsp_doorbell_send_data,
> +	.startup = tegra_hsp_doorbell_startup,
> +	.shutdown = tegra_hsp_doorbell_shutdown,
> +	.last_tx_done = tegra_hsp_doorbell_last_tx_done,
> +};
> +
> +static struct tegra_hsp_channel *
> +tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
> +			  unsigned int master, unsigned int index)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned int offset;
> +	unsigned long flags;
> +
> +	db = kzalloc(sizeof(*db), GFP_KERNEL);
> +	if (!db)
> +		return ERR_PTR(-ENOMEM);
> +
> +	offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
> +	offset += index * 0x100;
> +
> +	db->channel.ops = &tegra_hsp_doorbell_ops;
> +	db->channel.regs = hsp->regs + offset;
> +	db->channel.hsp = hsp;
> +
> +	db->name = kstrdup_const(name, GFP_KERNEL);
> +	db->master = master;
> +	db->index = index;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +	list_add_tail(&db->list, &hsp->doorbells);
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	return &db->channel;
> +}
> +
> +static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
> +{
> +	list_del(&db->list);
> +	kfree_const(db->name);
> +	kfree(db);
> +}
> +
> +static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->send_data(channel, data);
> +}
> +
> +static int tegra_hsp_startup(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->startup(channel);
> +}
> +
> +static void tegra_hsp_shutdown(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->shutdown(channel);
> +}
> +
> +static bool tegra_hsp_last_tx_done(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->last_tx_done(channel);
> +}
> +
> +static const struct mbox_chan_ops tegra_hsp_ops = {
> +	.send_data = tegra_hsp_send_data,
> +	.startup = tegra_hsp_startup,
> +	.shutdown = tegra_hsp_shutdown,
> +	.last_tx_done = tegra_hsp_last_tx_done,
> +};
> +
> +static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
> +					    const struct of_phandle_args *args)
> +{
> +	struct tegra_hsp *hsp = to_tegra_hsp(mbox);
> +	struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
> +	unsigned int type = args->args[0];
> +	unsigned int master = args->args[1];
> +	struct tegra_hsp_doorbell *db;
> +	struct mbox_chan *chan;
> +	unsigned long flags;
> +	unsigned int i;
> +
> +	dev_dbg(mbox->dev, "> %s(mbox=%p, args=%p)\n", __func__, mbox, args);
> +	dev_dbg(mbox->dev, "  type: %x, master: %x\n", type, master);
> +
> +	switch (type) {
> +	case TEGRA_HSP_MBOX_TYPE_DB:
> +		db = tegra_hsp_doorbell_get(hsp, master);
> +		dev_dbg(mbox->dev, "  doorbell: %p\n", db);
> +		if (db)
> +			channel = &db->channel;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	if (IS_ERR(channel))
> +		return ERR_CAST(channel);
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	for (i = 0; i < hsp->mbox.num_chans; i++) {
> +		chan = &hsp->mbox.chans[i];
> +		if (!chan->con_priv) {
> +			chan->con_priv = channel;
> +			channel->chan = chan;
> +			break;
> +		}
> +
> +		chan = NULL;
> +	}
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	dev_dbg(mbox->dev, "< %s() = %p\n", __func__, chan);
> +	return chan ?: ERR_PTR(-EBUSY);
> +}
> +
> +static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	list_for_each_entry(db, &hsp->doorbells, list)
> +		__tegra_hsp_doorbell_destroy(db);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +}
> +
> +static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
> +{
> +	const struct tegra_hsp_db_map *map = hsp->soc->map;
> +	struct tegra_hsp_channel *channel;
> +
> +	dev_dbg(hsp->mbox.dev, "> %s(hsp=%p)\n", __func__, hsp);
> +
> +	while (map->name) {
> +		dev_dbg(hsp->mbox.dev, "  adding doorbell for master %u (%s), index %u\n", map->master, map->name, map->index);
> +
> +		channel = tegra_hsp_doorbell_create(hsp, map->name,
> +						    map->master, map->index);
> +		if (IS_ERR(channel)) {
> +			tegra_hsp_remove_doorbells(hsp);
> +			return PTR_ERR(channel);
> +		}
> +
> +		map++;
> +	}
> +
> +	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static int tegra_hsp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_hsp *hsp;
> +	struct resource *res;
> +	u32 value;
> +	int err;
> +
> +	dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
> +
> +	hsp = devm_kzalloc(&pdev->dev, sizeof(*hsp), GFP_KERNEL);
> +	if (!hsp)
> +		return -ENOMEM;
> +
> +	dev_dbg(&pdev->dev, "  hsp: %p\n", hsp);
> +
> +	hsp->soc = of_device_get_match_data(&pdev->dev);
> +	INIT_LIST_HEAD(&hsp->doorbells);
> +	spin_lock_init(&hsp->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hsp->regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(hsp->regs))
> +		return PTR_ERR(hsp->regs);
> +
> +	value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
> +	hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
> +
> +	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);
> +	dev_dbg(&pdev->dev, "sm: %u ss:%u as: %u db: %u, si: %u\n",
> +		hsp->num_sm, hsp->num_ss, hsp->num_as, hsp->num_db,
> +		hsp->num_si);
> +
> +	err = platform_get_irq_byname(pdev, "doorbell");
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
> +		return err;
> +	}
> +
> +	hsp->irq = err;
> +
> +	err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
> +			       IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
> +			hsp->irq, err);
> +		return err;
> +	}
> +
> +	hsp->mbox.of_xlate = of_tegra_hsp_xlate;
> +	hsp->mbox.num_chans = 32;
> +	hsp->mbox.dev = &pdev->dev;
> +	hsp->mbox.txdone_irq = false;
> +	hsp->mbox.txdone_poll = false;
> +	hsp->mbox.ops = &tegra_hsp_ops;
> +
> +	hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
> +					sizeof(*hsp->mbox.chans),
> +					GFP_KERNEL);
> +	if (!hsp->mbox.chans)
> +		return -ENOMEM;
> +
> +	err = tegra_hsp_add_doorbells(hsp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
> +		return err;
> +	}
> +
> +	platform_set_drvdata(pdev, hsp);
> +
> +	err = mbox_controller_register(&hsp->mbox);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
> +		tegra_hsp_remove_doorbells(hsp);
> +		return err;
> +	}
> +
> +	dev_dbg(&pdev->dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static int tegra_hsp_remove(struct platform_device *pdev)
> +{
> +	struct tegra_hsp *hsp = platform_get_drvdata(pdev);
> +
> +	mbox_controller_unregister(&hsp->mbox);
> +	tegra_hsp_remove_doorbells(hsp);
> +
> +	return 0;
> +}
> +
> +static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
> +	{ "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX },
> +	{ "bpmp",   TEGRA_HSP_DB_MASTER_BPMP,   HSP_DB_BPMP   },
> +	{ /* sentinel */ }
> +};
> +
> +static const struct tegra_hsp_soc tegra186_hsp_soc = {
> +	.map = tegra186_hsp_db_map,
> +};
> +
> +static const struct of_device_id tegra_hsp_match[] = {
> +	{ .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
> +	{ }
> +};
> +
> +static struct platform_driver tegra_hsp_driver = {
> +	.driver = {
> +		.name = "tegra-hsp",
> +		.of_match_table = tegra_hsp_match,
> +	},
> +	.probe = tegra_hsp_probe,
> +	.remove = tegra_hsp_remove,
> +};
> +
> +static int __init tegra_hsp_init(void)
> +{
> +	return platform_driver_register(&tegra_hsp_driver);
> +}
> +core_initcall(tegra_hsp_init);
> -- 
> 2.9.0
> 

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-23  0:06         ` Sivaram Nair
  0 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23  0:06 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 19, 2016 at 07:32:23PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> This driver exposes a mailbox interface for interprocessor communication
> using the Hardware Synchronization Primitives (HSP) module's doorbell
> mechanism. There are multiple HSP instances and they provide additional
> features such as shared mailboxes, shared and arbitrated semaphores.
> 
> A driver for a remote processor can use the mailbox client provided by
> the HSP driver and build an IPC protocol on top of this synchronization
> mechanism.
> 
> Based on work by Joseph Lo <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a more object oriented design
> - fix some locking issues
> 
>  drivers/mailbox/Kconfig     |   9 +
>  drivers/mailbox/Makefile    |   2 +
>  drivers/mailbox/tegra-hsp.c | 561 ++++++++++++++++++++++++++++++++++++++++++++
>  3 files changed, 572 insertions(+)
>  create mode 100644 drivers/mailbox/tegra-hsp.c
> 
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index 97c372908e78..90e7b94a1199 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -114,6 +114,15 @@ config MAILBOX_TEST
>  	  Test client to help with testing new Controller driver
>  	  implementations.
>  
> +config TEGRA_HSP_MBOX
> +	bool "Tegra HSP(Hardware Synchronization Primitives) Driver"
> +	depends on ARCH_TEGRA_186_SOC
> +	help
> +	  The Tegra HSP driver is used for the interprocessor communication
> +	  between different remote processors and host processors on Tegra186
> +	  and later SoCs. Say Y here if you want to have this support.
> +	  If unsure say N.
> +
>  config XGENE_SLIMPRO_MBOX
>  	tristate "APM SoC X-Gene SLIMpro Mailbox Controller"
>  	depends on ARCH_XGENE
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 66c38e300dfc..3fa01673f288 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -27,3 +27,5 @@ obj-$(CONFIG_XGENE_SLIMPRO_MBOX) += mailbox-xgene-slimpro.o
>  obj-$(CONFIG_HI6220_MBOX)	+= hi6220-mailbox.o
>  
>  obj-$(CONFIG_BCM_PDC_MBOX)	+= bcm-pdc-mailbox.o
> +
> +obj-$(CONFIG_TEGRA_HSP_MBOX)	+= tegra-hsp.o
> diff --git a/drivers/mailbox/tegra-hsp.c b/drivers/mailbox/tegra-hsp.c
> new file mode 100644
> index 000000000000..8770ba9b58fe
> --- /dev/null
> +++ b/drivers/mailbox/tegra-hsp.c
> @@ -0,0 +1,561 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG
> +
> +#include <linux/interrupt.h>
> +#include <linux/io.h>
> +#include <linux/mailbox_controller.h>
> +#include <linux/of.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/slab.h>
> +
> +#include <dt-bindings/mailbox/tegra186-hsp.h>
> +
> +#define HSP_INT_DIMENSIONING	0x380
> +#define HSP_nSM_SHIFT		0
> +#define HSP_nSS_SHIFT		4
> +#define HSP_nAS_SHIFT		8
> +#define HSP_nDB_SHIFT		12
> +#define HSP_nSI_SHIFT		16
> +#define HSP_nINT_MASK		0xf
> +
> +#define HSP_DB_TRIGGER	0x0
> +#define HSP_DB_ENABLE	0x4
> +#define HSP_DB_RAW	0x8
> +#define HSP_DB_PENDING	0xc
> +
> +#define HSP_DB_CCPLEX		1
> +#define HSP_DB_BPMP		3
> +#define HSP_DB_MAX		7
> +
> +struct tegra_hsp_channel;
> +struct tegra_hsp;
> +
> +struct tegra_hsp_channel_ops {
> +	int (*send_data)(struct tegra_hsp_channel *channel, void *data);
> +	int (*startup)(struct tegra_hsp_channel *channel);
> +	void (*shutdown)(struct tegra_hsp_channel *channel);
> +	bool (*last_tx_done)(struct tegra_hsp_channel *channel);
> +};
> +
> +struct tegra_hsp_channel {
> +	struct tegra_hsp *hsp;
> +	const struct tegra_hsp_channel_ops *ops;
> +	struct mbox_chan *chan;
> +	void __iomem *regs;
> +};
> +
> +static struct tegra_hsp_channel *to_tegra_hsp_channel(struct mbox_chan *chan)
> +{
> +	return chan->con_priv;
> +}
> +
> +struct tegra_hsp_doorbell {
> +	struct tegra_hsp_channel channel;
> +	struct list_head list;
> +	const char *name;
> +	unsigned int master;
> +	unsigned int index;
> +};
> +
> +static struct tegra_hsp_doorbell *
> +to_tegra_hsp_doorbell(struct tegra_hsp_channel *channel)
> +{
> +	if (!channel)
> +		return NULL;
> +
> +	return container_of(channel, struct tegra_hsp_doorbell, channel);
> +}
> +
> +struct tegra_hsp_db_map {
> +	const char *name;
> +	unsigned int master;
> +	unsigned int index;
> +};
> +
> +struct tegra_hsp_soc {
> +	const struct tegra_hsp_db_map *map;
> +};
> +
> +struct tegra_hsp {
> +	const struct tegra_hsp_soc *soc;
> +	struct mbox_controller mbox;
> +	void __iomem *regs;
> +	unsigned int irq;
> +	unsigned int num_sm;
> +	unsigned int num_as;
> +	unsigned int num_ss;
> +	unsigned int num_db;
> +	unsigned int num_si;
> +	spinlock_t lock;
> +
> +	struct list_head doorbells;
> +};
> +
> +static inline struct tegra_hsp *
> +to_tegra_hsp(struct mbox_controller *mbox)
> +{
> +	return container_of(mbox, struct tegra_hsp, mbox);
> +}
> +
> +static inline u32 tegra_hsp_readl(struct tegra_hsp *hsp, unsigned int offset)
> +{
> +	return readl(hsp->regs + offset);
> +}
> +
> +static inline void tegra_hsp_writel(struct tegra_hsp *hsp, u32 value,
> +				    unsigned int offset)
> +{
> +	writel(value, hsp->regs + offset);
> +	(void)readl(hsp->regs + offset);

I am not aware of a need for such barriers (even if the downstream code
has them).

> +}
> +
> +static inline u32 tegra_hsp_channel_readl(struct tegra_hsp_channel *channel,
> +					  unsigned int offset)
> +{
> +	return readl(channel->regs + offset);
> +}
> +
> +static inline void tegra_hsp_channel_writel(struct tegra_hsp_channel *channel,
> +					    u32 value, unsigned int offset)
> +{
> +	writel(value, channel->regs + offset);
> +	(void)readl(channel->regs + offset);

same here.

> +}
> +
> +static int tegra_hsp_doorbell_can_ring(struct tegra_hsp_doorbell *db)
> +{
> +	u32 value;
> +
> +	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_ENABLE);
> +
> +	return !!(value & BIT(TEGRA_HSP_DB_MASTER_CCPLEX));
> +}
> +
> +static struct tegra_hsp_doorbell *
> +__tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
> +{
> +	struct tegra_hsp_doorbell *db = NULL, *entry;
> +
> +	list_for_each_entry(entry, &hsp->doorbells, list)
> +		if (entry->master == master) {

you might as well say: 'return entry;' and remove 'db' ?

> +			db = entry;
> +			break;
> +		}
> +
> +	return db;
> +}
> +
> +static struct tegra_hsp_doorbell *
> +tegra_hsp_doorbell_get(struct tegra_hsp *hsp, unsigned int master)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +	db = __tegra_hsp_doorbell_get(hsp, master);
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	return db;
> +}
> +
> +static irqreturn_t tegra_hsp_doorbell_irq(int irq, void *data)
> +{
> +	struct tegra_hsp *hsp = data;
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long master, value;
> +
> +	db = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!db)
> +		return IRQ_NONE;
> +
> +	value = tegra_hsp_channel_readl(&db->channel, HSP_DB_PENDING);
> +	tegra_hsp_channel_writel(&db->channel, value, HSP_DB_PENDING);
> +
> +	spin_lock(&hsp->lock);
> +
> +	for_each_set_bit(master, &value, hsp->mbox.num_chans) {
> +		struct tegra_hsp_doorbell *db;
> +
> +		db = __tegra_hsp_doorbell_get(hsp, master);
> +		if (db)
> +			mbox_chan_received_data(db->channel.chan, NULL);
> +	}
> +
> +	spin_unlock(&hsp->lock);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static int tegra_hsp_doorbell_send_data(struct tegra_hsp_channel *channel,
> +					void *data)
> +{
> +	tegra_hsp_channel_writel(channel, 1, HSP_DB_TRIGGER);
> +
> +	return 0;
> +}
> +
> +static int tegra_hsp_doorbell_startup(struct tegra_hsp_channel *channel)
> +{
> +	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
> +	struct tegra_hsp *hsp = channel->hsp;
> +	struct tegra_hsp_doorbell *ccplex;
> +	unsigned long flags;
> +	u32 value;
> +
> +	dev_dbg(hsp->mbox.dev, "> %s(channel=%p)\n", __func__, channel);
> +	dev_dbg(hsp->mbox.dev, "  regs: %p\n", channel->regs);
> +
> +	if (db->master >= hsp->mbox.num_chans) {
> +		dev_err(hsp->mbox.dev,
> +			"invalid master ID %u for HSP channel\n",
> +			db->master);
> +		return -EINVAL;
> +	}
> +
> +	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!ccplex)
> +		return -ENODEV;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value |= BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	if (!tegra_hsp_doorbell_can_ring(db))
> +		return -ENODEV;
> +
> +	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static void tegra_hsp_doorbell_shutdown(struct tegra_hsp_channel *channel)
> +{
> +	struct tegra_hsp_doorbell *db = to_tegra_hsp_doorbell(channel);
> +	struct tegra_hsp *hsp = channel->hsp;
> +	struct tegra_hsp_doorbell *ccplex;
> +	unsigned long flags;
> +	u32 value;
> +
> +	ccplex = tegra_hsp_doorbell_get(hsp, TEGRA_HSP_DB_MASTER_CCPLEX);
> +	if (!ccplex)
> +		return;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	value = tegra_hsp_channel_readl(&ccplex->channel, HSP_DB_ENABLE);
> +	value &= ~BIT(db->master);
> +	tegra_hsp_channel_writel(&ccplex->channel, value, HSP_DB_ENABLE);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +}
> +
> +static bool tegra_hsp_doorbell_last_tx_done(struct tegra_hsp_channel *channel)
> +{
> +	return true;
> +}
> +
> +static const struct tegra_hsp_channel_ops tegra_hsp_doorbell_ops = {
> +	.send_data = tegra_hsp_doorbell_send_data,
> +	.startup = tegra_hsp_doorbell_startup,
> +	.shutdown = tegra_hsp_doorbell_shutdown,
> +	.last_tx_done = tegra_hsp_doorbell_last_tx_done,
> +};
> +
> +static struct tegra_hsp_channel *
> +tegra_hsp_doorbell_create(struct tegra_hsp *hsp, const char *name,
> +			  unsigned int master, unsigned int index)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned int offset;
> +	unsigned long flags;
> +
> +	db = kzalloc(sizeof(*db), GFP_KERNEL);
> +	if (!db)
> +		return ERR_PTR(-ENOMEM);
> +
> +	offset = (1 + (hsp->num_sm / 2) + hsp->num_ss + hsp->num_as) << 16;
> +	offset += index * 0x100;
> +
> +	db->channel.ops = &tegra_hsp_doorbell_ops;
> +	db->channel.regs = hsp->regs + offset;
> +	db->channel.hsp = hsp;
> +
> +	db->name = kstrdup_const(name, GFP_KERNEL);
> +	db->master = master;
> +	db->index = index;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +	list_add_tail(&db->list, &hsp->doorbells);
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	return &db->channel;
> +}
> +
> +static void __tegra_hsp_doorbell_destroy(struct tegra_hsp_doorbell *db)
> +{
> +	list_del(&db->list);
> +	kfree_const(db->name);
> +	kfree(db);
> +}
> +
> +static int tegra_hsp_send_data(struct mbox_chan *chan, void *data)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->send_data(channel, data);
> +}
> +
> +static int tegra_hsp_startup(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->startup(channel);
> +}
> +
> +static void tegra_hsp_shutdown(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->shutdown(channel);
> +}
> +
> +static bool tegra_hsp_last_tx_done(struct mbox_chan *chan)
> +{
> +	struct tegra_hsp_channel *channel = to_tegra_hsp_channel(chan);
> +
> +	return channel->ops->last_tx_done(channel);
> +}
> +
> +static const struct mbox_chan_ops tegra_hsp_ops = {
> +	.send_data = tegra_hsp_send_data,
> +	.startup = tegra_hsp_startup,
> +	.shutdown = tegra_hsp_shutdown,
> +	.last_tx_done = tegra_hsp_last_tx_done,
> +};
> +
> +static struct mbox_chan *of_tegra_hsp_xlate(struct mbox_controller *mbox,
> +					    const struct of_phandle_args *args)
> +{
> +	struct tegra_hsp *hsp = to_tegra_hsp(mbox);
> +	struct tegra_hsp_channel *channel = ERR_PTR(-ENODEV);
> +	unsigned int type = args->args[0];
> +	unsigned int master = args->args[1];
> +	struct tegra_hsp_doorbell *db;
> +	struct mbox_chan *chan;
> +	unsigned long flags;
> +	unsigned int i;
> +
> +	dev_dbg(mbox->dev, "> %s(mbox=%p, args=%p)\n", __func__, mbox, args);
> +	dev_dbg(mbox->dev, "  type: %x, master: %x\n", type, master);
> +
> +	switch (type) {
> +	case TEGRA_HSP_MBOX_TYPE_DB:
> +		db = tegra_hsp_doorbell_get(hsp, master);
> +		dev_dbg(mbox->dev, "  doorbell: %p\n", db);
> +		if (db)
> +			channel = &db->channel;
> +
> +		break;
> +
> +	default:
> +		break;
> +	}
> +
> +	if (IS_ERR(channel))
> +		return ERR_CAST(channel);
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	for (i = 0; i < hsp->mbox.num_chans; i++) {
> +		chan = &hsp->mbox.chans[i];
> +		if (!chan->con_priv) {
> +			chan->con_priv = channel;
> +			channel->chan = chan;
> +			break;
> +		}
> +
> +		chan = NULL;
> +	}
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +
> +	dev_dbg(mbox->dev, "< %s() = %p\n", __func__, chan);
> +	return chan ?: ERR_PTR(-EBUSY);
> +}
> +
> +static void tegra_hsp_remove_doorbells(struct tegra_hsp *hsp)
> +{
> +	struct tegra_hsp_doorbell *db;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&hsp->lock, flags);
> +
> +	list_for_each_entry(db, &hsp->doorbells, list)
> +		__tegra_hsp_doorbell_destroy(db);
> +
> +	spin_unlock_irqrestore(&hsp->lock, flags);
> +}
> +
> +static int tegra_hsp_add_doorbells(struct tegra_hsp *hsp)
> +{
> +	const struct tegra_hsp_db_map *map = hsp->soc->map;
> +	struct tegra_hsp_channel *channel;
> +
> +	dev_dbg(hsp->mbox.dev, "> %s(hsp=%p)\n", __func__, hsp);
> +
> +	while (map->name) {
> +		dev_dbg(hsp->mbox.dev, "  adding doorbell for master %u (%s), index %u\n", map->master, map->name, map->index);
> +
> +		channel = tegra_hsp_doorbell_create(hsp, map->name,
> +						    map->master, map->index);
> +		if (IS_ERR(channel)) {
> +			tegra_hsp_remove_doorbells(hsp);
> +			return PTR_ERR(channel);
> +		}
> +
> +		map++;
> +	}
> +
> +	dev_dbg(hsp->mbox.dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static int tegra_hsp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_hsp *hsp;
> +	struct resource *res;
> +	u32 value;
> +	int err;
> +
> +	dev_dbg(&pdev->dev, "> %s(pdev=%p)\n", __func__, pdev);
> +
> +	hsp = devm_kzalloc(&pdev->dev, sizeof(*hsp), GFP_KERNEL);
> +	if (!hsp)
> +		return -ENOMEM;
> +
> +	dev_dbg(&pdev->dev, "  hsp: %p\n", hsp);
> +
> +	hsp->soc = of_device_get_match_data(&pdev->dev);
> +	INIT_LIST_HEAD(&hsp->doorbells);
> +	spin_lock_init(&hsp->lock);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	hsp->regs = devm_ioremap_resource(&pdev->dev, res);
> +	if (IS_ERR(hsp->regs))
> +		return PTR_ERR(hsp->regs);
> +
> +	value = tegra_hsp_readl(hsp, HSP_INT_DIMENSIONING);
> +	hsp->num_sm = (value >> HSP_nSM_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_ss = (value >> HSP_nSS_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_as = (value >> HSP_nAS_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_db = (value >> HSP_nDB_SHIFT) & HSP_nINT_MASK;
> +	hsp->num_si = (value >> HSP_nSI_SHIFT) & HSP_nINT_MASK;
> +
> +	dev_dbg(&pdev->dev, "regs: %pR (%p-%p)\n", res, hsp->regs, hsp->regs + resource_size(res) - 1);
> +	dev_dbg(&pdev->dev, "sm: %u ss:%u as: %u db: %u, si: %u\n",
> +		hsp->num_sm, hsp->num_ss, hsp->num_as, hsp->num_db,
> +		hsp->num_si);
> +
> +	err = platform_get_irq_byname(pdev, "doorbell");
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to get doorbell IRQ: %d\n", err);
> +		return err;
> +	}
> +
> +	hsp->irq = err;
> +
> +	err = devm_request_irq(&pdev->dev, hsp->irq, tegra_hsp_doorbell_irq,
> +			       IRQF_NO_SUSPEND, dev_name(&pdev->dev), hsp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to request IRQ#%u: %d\n",
> +			hsp->irq, err);
> +		return err;
> +	}
> +
> +	hsp->mbox.of_xlate = of_tegra_hsp_xlate;
> +	hsp->mbox.num_chans = 32;
> +	hsp->mbox.dev = &pdev->dev;
> +	hsp->mbox.txdone_irq = false;
> +	hsp->mbox.txdone_poll = false;
> +	hsp->mbox.ops = &tegra_hsp_ops;
> +
> +	hsp->mbox.chans = devm_kcalloc(&pdev->dev, hsp->mbox.num_chans,
> +					sizeof(*hsp->mbox.chans),
> +					GFP_KERNEL);
> +	if (!hsp->mbox.chans)
> +		return -ENOMEM;
> +
> +	err = tegra_hsp_add_doorbells(hsp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to add doorbells: %d\n", err);
> +		return err;
> +	}
> +
> +	platform_set_drvdata(pdev, hsp);
> +
> +	err = mbox_controller_register(&hsp->mbox);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to register mailbox: %d\n", err);
> +		tegra_hsp_remove_doorbells(hsp);
> +		return err;
> +	}
> +
> +	dev_dbg(&pdev->dev, "< %s()\n", __func__);
> +	return 0;
> +}
> +
> +static int tegra_hsp_remove(struct platform_device *pdev)
> +{
> +	struct tegra_hsp *hsp = platform_get_drvdata(pdev);
> +
> +	mbox_controller_unregister(&hsp->mbox);
> +	tegra_hsp_remove_doorbells(hsp);
> +
> +	return 0;
> +}
> +
> +static const struct tegra_hsp_db_map tegra186_hsp_db_map[] = {
> +	{ "ccplex", TEGRA_HSP_DB_MASTER_CCPLEX, HSP_DB_CCPLEX },
> +	{ "bpmp",   TEGRA_HSP_DB_MASTER_BPMP,   HSP_DB_BPMP   },
> +	{ /* sentinel */ }
> +};
> +
> +static const struct tegra_hsp_soc tegra186_hsp_soc = {
> +	.map = tegra186_hsp_db_map,
> +};
> +
> +static const struct of_device_id tegra_hsp_match[] = {
> +	{ .compatible = "nvidia,tegra186-hsp", .data = &tegra186_hsp_soc },
> +	{ }
> +};
> +
> +static struct platform_driver tegra_hsp_driver = {
> +	.driver = {
> +		.name = "tegra-hsp",
> +		.of_match_table = tegra_hsp_match,
> +	},
> +	.probe = tegra_hsp_probe,
> +	.remove = tegra_hsp_remove,
> +};
> +
> +static int __init tegra_hsp_init(void)
> +{
> +	return platform_driver_register(&tegra_hsp_driver);
> +}
> +core_initcall(tegra_hsp_init);
> -- 
> 2.9.0
> 

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

* Re: [PATCH v3 02/12] mailbox: Add Tegra HSP driver
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-23  0:12         ` Sivaram Nair
  -1 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23  0:12 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Fri, Aug 19, 2016 at 07:32:23PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG

Is this a leftover from your debugging (elsewhere in this series too)?

> +
> +#include <linux/interrupt.h>

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

* [PATCH v3 02/12] mailbox: Add Tegra HSP driver
@ 2016-08-23  0:12         ` Sivaram Nair
  0 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23  0:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 19, 2016 at 07:32:23PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG

Is this a leftover from your debugging (elsewhere in this series too)?

> +
> +#include <linux/interrupt.h>

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

* Re: [PATCH v3 06/12] soc/tegra: Add Tegra186 support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-23 13:44         ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 13:44 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 19/08/16 18:32, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
> GPUs with Pascal architecture on it. It features with ADSP with
> Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
> multi-format support, ISP for image capture processing and BPMP for the
> power managements.
> 
> Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
>  drivers/soc/tegra/Kconfig | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
> index 03089ad2fc65..88a71dfd466c 100644
> --- a/drivers/soc/tegra/Kconfig
> +++ b/drivers/soc/tegra/Kconfig
> @@ -61,6 +61,20 @@ config ARCH_TEGRA_132_SOC
>  	  but contains an NVIDIA Denver CPU complex in place of
>  	  Tegra124's "4+1" Cortex-A15 CPU complex.
>  
> +config ARCH_TEGRA_186_SOC
> +	bool "NVIDIA Tegra186 SoC"
> +	select MAILBOX
> +	select TEGRA_BPMP
> +	select TEGRA_HSP_MBOX
> +	select TEGRA_IVC
> +	help
> +	  Enable support for the NVIDIA Tegar186 SoC. The Tegra186 has a
> +	  combination of Denver and Cortex-A57 CPU cores and GPUs with Pascal
> +	  architecture on it. It features with ADSP with Cortex-A9 CPU for
> +	  audio processing, hardware video encoder/decoder with multi-format
> +	  support, ISP for image capture processing and BPMP for the power
> +	  managements.
> +
>  config ARCH_TEGRA_210_SOC
>  	bool "NVIDIA Tegra210 SoC"
>  	select PINCTRL_TEGRA210
> 

After this patch is applied, if I say Y to enable Tegra186 support then I get ...

drivers/built-in.o: In function `tegra_bpmp_probe':
/home/jonathanh/workdir/tegra/korg-linux-next.git/drivers/firmware/tegra/bpmp.c:820: undefined reference to `tegra_bpmp_init_clocks'
/home/jonathanh/workdir/tegra/korg-linux-next.git/drivers/firmware/tegra/bpmp.c:824: undefined reference to `tegra_bpmp_init_resets'

It seems that patches 11 and 12 should be applied before patch 5 of this series or am I overlooking something? 

Jon

-- 
nvpublic

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

* [PATCH v3 06/12] soc/tegra: Add Tegra186 support
@ 2016-08-23 13:44         ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 13:44 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
> 
> The Tegra186 has a combination of Denver and Cortex-A57 CPU cores and
> GPUs with Pascal architecture on it. It features with ADSP with
> Cortex-A9 CPU for audio processing, hardware video encoder/decoder with
> multi-format support, ISP for image capture processing and BPMP for the
> power managements.
> 
> Signed-off-by: Joseph Lo <josephl@nvidia.com>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
>  drivers/soc/tegra/Kconfig | 14 ++++++++++++++
>  1 file changed, 14 insertions(+)
> 
> diff --git a/drivers/soc/tegra/Kconfig b/drivers/soc/tegra/Kconfig
> index 03089ad2fc65..88a71dfd466c 100644
> --- a/drivers/soc/tegra/Kconfig
> +++ b/drivers/soc/tegra/Kconfig
> @@ -61,6 +61,20 @@ config ARCH_TEGRA_132_SOC
>  	  but contains an NVIDIA Denver CPU complex in place of
>  	  Tegra124's "4+1" Cortex-A15 CPU complex.
>  
> +config ARCH_TEGRA_186_SOC
> +	bool "NVIDIA Tegra186 SoC"
> +	select MAILBOX
> +	select TEGRA_BPMP
> +	select TEGRA_HSP_MBOX
> +	select TEGRA_IVC
> +	help
> +	  Enable support for the NVIDIA Tegar186 SoC. The Tegra186 has a
> +	  combination of Denver and Cortex-A57 CPU cores and GPUs with Pascal
> +	  architecture on it. It features with ADSP with Cortex-A9 CPU for
> +	  audio processing, hardware video encoder/decoder with multi-format
> +	  support, ISP for image capture processing and BPMP for the power
> +	  managements.
> +
>  config ARCH_TEGRA_210_SOC
>  	bool "NVIDIA Tegra210 SoC"
>  	select PINCTRL_TEGRA210
> 

After this patch is applied, if I say Y to enable Tegra186 support then I get ...

drivers/built-in.o: In function `tegra_bpmp_probe':
/home/jonathanh/workdir/tegra/korg-linux-next.git/drivers/firmware/tegra/bpmp.c:820: undefined reference to `tegra_bpmp_init_clocks'
/home/jonathanh/workdir/tegra/korg-linux-next.git/drivers/firmware/tegra/bpmp.c:824: undefined reference to `tegra_bpmp_init_resets'

It seems that patches 11 and 12 should be applied before patch 5 of this series or am I overlooking something? 

Jon

-- 
nvpublic

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

* Re: [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
  2016-08-22 13:28             ` Thierry Reding
@ 2016-08-23 13:49                 ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 13:49 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 22/08/16 14:28, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 11:11:57AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>>
>>> This driver uses the services provided by the BPMP firmware driver to
>>> implement a clock driver based on the MRQ_CLK request. This part of the
>>> BPMP ABI provides a means to enumerate and control clocks and should
>>> allow the driver to work on any chip that supports this ABI.
>>>
>>> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
>>> ---
>>>  drivers/clk/tegra/Makefile   |   1 +
>>>  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 566 insertions(+)
>>>  create mode 100644 drivers/clk/tegra/clk-bpmp.c

[snip]

>>> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
>>> +{
>>> +	struct tegra_bpmp_clk_info *clocks;
>>> +	unsigned int count;
>>> +	int err;
>>> +
>>> +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
>>> +
>>> +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
>>> +	if (err < 0)
>>> +		return err;
>>> +
>>> +	count = err;
>>> +
>>> +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
>>> +
>>> +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
>>> +	if (err < 0) {
>>> +		kfree(clocks);
>>> +		return err;
>>> +	}
>>> +
>>> +	kfree(clocks);
>>> +
>>> +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
>>> +			       bpmp);
>>
>> We should check the return code.
> 
> Yes, I suppose we should also make sure to remove all clocks if this
> ever fails. I'll see how difficult it is to implement that.

In the unlikely event it fails, not much is going to work and so may be
warning or printing an error is enough. It seems that we don't clean-up
in the bpmp firmware driver (that calls this) and so not cleaning up
here will not make much difference.

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 11/12] clk: tegra: Add BPMP clock driver
@ 2016-08-23 13:49                 ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 13:49 UTC (permalink / raw)
  To: linux-arm-kernel


On 22/08/16 14:28, Thierry Reding wrote:
> On Mon, Aug 22, 2016 at 11:11:57AM +0100, Jon Hunter wrote:
>>
>> On 19/08/16 18:32, Thierry Reding wrote:
>>> From: Thierry Reding <treding@nvidia.com>
>>>
>>> This driver uses the services provided by the BPMP firmware driver to
>>> implement a clock driver based on the MRQ_CLK request. This part of the
>>> BPMP ABI provides a means to enumerate and control clocks and should
>>> allow the driver to work on any chip that supports this ABI.
>>>
>>> Signed-off-by: Thierry Reding <treding@nvidia.com>
>>> ---
>>>  drivers/clk/tegra/Makefile   |   1 +
>>>  drivers/clk/tegra/clk-bpmp.c | 565 +++++++++++++++++++++++++++++++++++++++++++
>>>  2 files changed, 566 insertions(+)
>>>  create mode 100644 drivers/clk/tegra/clk-bpmp.c

[snip]

>>> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp)
>>> +{
>>> +	struct tegra_bpmp_clk_info *clocks;
>>> +	unsigned int count;
>>> +	int err;
>>> +
>>> +	dev_dbg(bpmp->dev, "> %s(bpmp=%p)\n", __func__, bpmp);
>>> +
>>> +	err = tegra_bpmp_probe_clocks(bpmp, &clocks);
>>> +	if (err < 0)
>>> +		return err;
>>> +
>>> +	count = err;
>>> +
>>> +	dev_dbg(bpmp->dev, "%u clocks probed\n", count);
>>> +
>>> +	err = tegra_bpmp_register_clocks(bpmp, clocks, count);
>>> +	if (err < 0) {
>>> +		kfree(clocks);
>>> +		return err;
>>> +	}
>>> +
>>> +	kfree(clocks);
>>> +
>>> +	of_clk_add_hw_provider(bpmp->dev->of_node, tegra_bpmp_clk_of_xlate,
>>> +			       bpmp);
>>
>> We should check the return code.
> 
> Yes, I suppose we should also make sure to remove all clocks if this
> ever fails. I'll see how difficult it is to implement that.

In the unlikely event it fails, not much is going to work and so may be
warning or printing an error is enough. It seems that we don't clean-up
in the bpmp firmware driver (that calls this) and so not cleaning up
here will not make much difference.

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-22 18:56                 ` Stephen Warren
@ 2016-08-23 14:58                   ` Arnd Bergmann
  -1 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-23 14:58 UTC (permalink / raw)
  To: Stephen Warren
  Cc: devicetree, Peter De Schrijver, Sivaram Nair, Timo Alho,
	Thierry Reding, Joseph Lo, linux-tegra, linux-arm-kernel

On Monday, August 22, 2016 12:56:52 PM CEST Stephen Warren wrote:
> On 08/22/2016 08:42 AM, Arnd Bergmann wrote:
> > On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> >> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> >>> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> >>>> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> >>>> new file mode 100644
> >>>> index 000000000000..0aaef5960e29
> >>>> --- /dev/null
> >>>> +++ b/include/soc/tegra/bpmp-abi.h
> >>>> +#ifndef _ABI_BPMP_ABI_H_
> >>>> +#define _ABI_BPMP_ABI_H_
> >>>> +
> >>>> +#ifdef LK
> >>>> +#include <stdint.h>
> >>>> +#endif
> >>>> +
> >>>> +#ifndef __ABI_PACKED
> >>>> +#define __ABI_PACKED __attribute__((packed))
> >>>> +#endif
> >>>> +
> >>>> +#ifdef NO_GCC_EXTENSIONS
> >>>> +#define EMPTY char empty;
> >>>> +#define EMPTY_ARRAY 1
> >>>> +#else
> >>>> +#define EMPTY
> >>>> +#define EMPTY_ARRAY 0
> >>>> +#endif
> >>>> +
> >>>> +#ifndef __UNION_ANON
> >>>> +#define __UNION_ANON
> >>>> +#endif
> >>>
> >>> Maybe keep these all out of the kernel?
> >>
> >> This was discussed a little in an earlier posting. This header file is
> >> maintained by the BPMP firmware team and using it verbatim means little
> >> to no effort required to update it.
> >
> > The usual recommendation is to just use Linux coding style in shared
> > files, and possibly add another header that provides the required
> > definitions. Otherwise you end up with people randomly cleaning up
> > the file later 
> 
> That's just going to push the problem into all other operating systems; 
> what happens when BSD, Windows, QNX, FreeRTOS, ... import this file and 
> attempt to apply the same argument, but want the FW team to use their 
> coding style?

That's not how the world works, fortunately. In practice, Linux is going
to be the only OS that cares enough about the coding style of an interface
header for a random device driver. The others either don't include
vendor drivers, or they don't have specific coding style requirements,
or they use a sane style already.

	Arnd

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-23 14:58                   ` Arnd Bergmann
  0 siblings, 0 replies; 104+ messages in thread
From: Arnd Bergmann @ 2016-08-23 14:58 UTC (permalink / raw)
  To: linux-arm-kernel

On Monday, August 22, 2016 12:56:52 PM CEST Stephen Warren wrote:
> On 08/22/2016 08:42 AM, Arnd Bergmann wrote:
> > On Monday, August 22, 2016 4:02:11 PM CEST Thierry Reding wrote:
> >> On Mon, Aug 22, 2016 at 03:34:15PM +0200, Arnd Bergmann wrote:
> >>> On Friday, August 19, 2016 7:32:26 PM CEST Thierry Reding wrote:
> >>>> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> >>>> new file mode 100644
> >>>> index 000000000000..0aaef5960e29
> >>>> --- /dev/null
> >>>> +++ b/include/soc/tegra/bpmp-abi.h
> >>>> +#ifndef _ABI_BPMP_ABI_H_
> >>>> +#define _ABI_BPMP_ABI_H_
> >>>> +
> >>>> +#ifdef LK
> >>>> +#include <stdint.h>
> >>>> +#endif
> >>>> +
> >>>> +#ifndef __ABI_PACKED
> >>>> +#define __ABI_PACKED __attribute__((packed))
> >>>> +#endif
> >>>> +
> >>>> +#ifdef NO_GCC_EXTENSIONS
> >>>> +#define EMPTY char empty;
> >>>> +#define EMPTY_ARRAY 1
> >>>> +#else
> >>>> +#define EMPTY
> >>>> +#define EMPTY_ARRAY 0
> >>>> +#endif
> >>>> +
> >>>> +#ifndef __UNION_ANON
> >>>> +#define __UNION_ANON
> >>>> +#endif
> >>>
> >>> Maybe keep these all out of the kernel?
> >>
> >> This was discussed a little in an earlier posting. This header file is
> >> maintained by the BPMP firmware team and using it verbatim means little
> >> to no effort required to update it.
> >
> > The usual recommendation is to just use Linux coding style in shared
> > files, and possibly add another header that provides the required
> > definitions. Otherwise you end up with people randomly cleaning up
> > the file later 
> 
> That's just going to push the problem into all other operating systems; 
> what happens when BSD, Windows, QNX, FreeRTOS, ... import this file and 
> attempt to apply the same argument, but want the FW team to use their 
> coding style?

That's not how the world works, fortunately. In practice, Linux is going
to be the only OS that cares enough about the coding style of an interface
header for a random device driver. The others either don't include
vendor drivers, or they don't have specific coding style requirements,
or they use a sane style already.

	Arnd

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

* Re: [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-23 17:35         ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 17:35 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Sivaram Nair, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA


On 19/08/16 18:32, Thierry Reding wrote:
> From: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> The NVIDIA P3310 is a processor module used in several reference designs
> that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
> essentials such as ethernet, WiFi and a PMIC. It is typically connected
> to an I/O board (such as the P2597) that provides the connecters needed
> to hook it up to the outside world.
> 
> Signed-off-by: Joseph Lo <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v3:
> - fix compatible string
> 
>  arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi | 34 ++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> new file mode 100644
> index 000000000000..dc07ea618378
> --- /dev/null
> +++ b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> @@ -0,0 +1,34 @@
> +#include "tegra186.dtsi"
> +
> +/ {
> +	model = "NVIDIA Tegra186 P3310 main Board";
> +	compatible = "nvidia,p3310", "nvidia,tegra186";
> +
> +	aliases {
> +		serial0 = &uarta;
> +	};
> +
> +	chosen {
> +		bootargs = "earlycon console=ttyS0,115200n8";

I don't think we need 'console' in the bootargs anymore because
stdout-path is enough (even for early console).

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support
@ 2016-08-23 17:35         ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-23 17:35 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Joseph Lo <josephl@nvidia.com>
> 
> The NVIDIA P3310 is a processor module used in several reference designs
> that features a Tegra186 SoC, 8 GiB of LPDDR4 RAM, 32 GiB eMMC and other
> essentials such as ethernet, WiFi and a PMIC. It is typically connected
> to an I/O board (such as the P2597) that provides the connecters needed
> to hook it up to the outside world.
> 
> Signed-off-by: Joseph Lo <josephl@nvidia.com>
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - fix compatible string
> 
>  arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi | 34 ++++++++++++++++++++++++++
>  1 file changed, 34 insertions(+)
>  create mode 100644 arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> 
> diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> new file mode 100644
> index 000000000000..dc07ea618378
> --- /dev/null
> +++ b/arch/arm64/boot/dts/nvidia/tegra186-p3310.dtsi
> @@ -0,0 +1,34 @@
> +#include "tegra186.dtsi"
> +
> +/ {
> +	model = "NVIDIA Tegra186 P3310 main Board";
> +	compatible = "nvidia,p3310", "nvidia,tegra186";
> +
> +	aliases {
> +		serial0 = &uarta;
> +	};
> +
> +	chosen {
> +		bootargs = "earlycon console=ttyS0,115200n8";

I don't think we need 'console' in the bootargs anymore because
stdout-path is enough (even for early console).

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 05/12] firmware: tegra: Add BPMP support
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-23 23:26         ` Sivaram Nair
  -1 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23 23:26 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Timo Alho, Peter De Schrijver, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA

On Fri, Aug 19, 2016 at 07:32:26PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
> 
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.
> 
> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
> 
> Based on work by Sivaram Nair <sivaramn-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org> and Joseph Lo
> <josephl-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>.
> 
> Signed-off-by: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> ---
> Changes in v3:
> - use a message structure for transfers to avoid long function
>   prototypes
> - restructure driver for easier maintainability
> - rename bpmp_abi.h to bpmp-abi.h for consistency
> 
>  drivers/firmware/tegra/Kconfig  |   12 +
>  drivers/firmware/tegra/Makefile |    1 +
>  drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
>  include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/bpmp.h        |  122 +++
>  5 files changed, 2616 insertions(+)
>  create mode 100644 drivers/firmware/tegra/bpmp.c
>  create mode 100644 include/soc/tegra/bpmp-abi.h
>  create mode 100644 include/soc/tegra/bpmp.h
> 
> diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
> index 1fa3e4e136a5..ff2730d5c468 100644
> --- a/drivers/firmware/tegra/Kconfig
> +++ b/drivers/firmware/tegra/Kconfig
> @@ -10,4 +10,16 @@ config TEGRA_IVC
>  	  keeps the content is synchronization between host CPU and remote
>  	  processors.
>  
> +config TEGRA_BPMP
> +	bool "Tegra BPMP driver"
> +	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
> +	help
> +	  BPMP (Boot and Power Management Processor) is designed to off-loading
> +	  the PM functions which include clock/DVFS/thermal/power from the CPU.
> +	  It needs HSP as the HW synchronization and notification module and
> +	  IVC module as the message communication protocol.
> +
> +	  This driver manages the IPC interface between host CPU and the
> +	  firmware running on BPMP.
> +
>  endmenu
> diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> index 92e2153e8173..e34a2f79e1ad 100644
> --- a/drivers/firmware/tegra/Makefile
> +++ b/drivers/firmware/tegra/Makefile
> @@ -1 +1,2 @@
> +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
>  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> new file mode 100644
> index 000000000000..a09043b1be05
> --- /dev/null
> +++ b/drivers/firmware/tegra/bpmp.c
> @@ -0,0 +1,880 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG
> +
> +#include <linux/clk/tegra.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/semaphore.h>
> +
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +#include <soc/tegra/ivc.h>
> +
> +#define MSG_ACK		BIT(0)
> +#define MSG_RING	BIT(1)
> +
> +static inline struct tegra_bpmp *
> +mbox_client_to_bpmp(struct mbox_client *client)
> +{
> +	return container_of(client, struct tegra_bpmp, mbox.client);
> +}
> +
> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
> +{
> +	struct platform_device *pdev;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +
> +	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
> +	if (!np)
> +		return ERR_PTR(-ENOENT);
> +
> +	pdev = of_find_device_by_node(np);
> +	if (!pdev) {
> +		bpmp = ERR_PTR(-ENODEV);
> +		goto put;
> +	}
> +
> +	bpmp = platform_get_drvdata(pdev);
> +	if (!bpmp) {
> +		bpmp = ERR_PTR(-EPROBE_DEFER);
> +		put_device(&pdev->dev);
> +		goto put;
> +	}
> +
> +put:
> +	of_node_put(np);
> +	return bpmp;
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_get);
> +
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp)
> +{
> +	if (bpmp)
> +		put_device(bpmp->dev);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_put);
> +
> +static int tegra_bpmp_channel_get_index(struct tegra_bpmp_channel *channel)
> +{
> +	return channel - channel->bpmp->channels;
> +}
> +
> +static int
> +tegra_bpmp_channel_get_thread_index(struct tegra_bpmp_channel *channel)
> +{
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	unsigned int offset, count;
> +	int index;
> +
> +	offset = bpmp->soc->channels.thread.offset;
> +	count = bpmp->soc->channels.thread.count;
> +
> +	index = tegra_bpmp_channel_get_index(channel);
> +	if (index < 0)
> +		return index;
> +
> +	if (index < offset || index >= offset + count)
> +		return -EINVAL;
> +
> +	return index - offset;
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_thread(struct tegra_bpmp *bpmp, unsigned int index)
> +{
> +	unsigned int offset = bpmp->soc->channels.thread.offset;
> +	unsigned int count = bpmp->soc->channels.thread.count;
> +
> +	if (index >= count)
> +		return NULL;
> +
> +	return &bpmp->channels[offset + index];
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_tx(struct tegra_bpmp *bpmp)
> +{
> +	unsigned int offset = bpmp->soc->channels.cpu_tx.offset;
> +
> +	return &bpmp->channels[offset + smp_processor_id()];
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_rx(struct tegra_bpmp *bpmp)
> +{
> +	unsigned int offset = bpmp->soc->channels.cpu_rx.offset;
> +
> +	return &bpmp->channels[offset];
> +}
> +
> +static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg)
> +{
> +	return (msg->tx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
> +	       (msg->rx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
> +	       (msg->tx.size == 0 || msg->tx.data) &&
> +	       (msg->rx.size == 0 || msg->rx.data);
> +}
> +
> +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ib = NULL;
> +		return false;
> +	}
> +
> +	channel->ib = frame;
> +
> +	return true;
> +}
> +
> +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_acked(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ob = NULL;
> +		return false;
> +	}
> +
> +	channel->ob = frame;
> +
> +	return true;
> +}
> +
> +static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_free(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
> +					 void *data, size_t size)
> +{
> +	if (data && size > 0)
> +		memcpy_fromio(data, channel->ib->data, size);
> +
> +	return tegra_ivc_read_advance(channel->ivc);
> +}
> +
> +static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
> +				       void *data, size_t size)
> +{
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	unsigned long flags;
> +	ssize_t err;
> +	int index;
> +
> +	index = tegra_bpmp_channel_get_thread_index(channel);
> +	if (index < 0)
> +		return index;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +	err = __tegra_bpmp_channel_read(channel, data, size);
> +	clear_bit(index, bpmp->threaded.allocated);
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +
> +	up(&bpmp->threaded.lock);
> +
> +	return err;
> +}
> +
> +static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
> +					  unsigned int mrq, unsigned long flags,
> +					  const void *data, size_t size)
> +{
> +	channel->ob->code = mrq;
> +	channel->ob->flags = flags;
> +
> +	if (data && size > 0)
> +		memcpy_toio(channel->ob->data, data, size);
> +
> +	return tegra_ivc_write_advance(channel->ivc);
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			  const void *data, size_t size)
> +{
> +	unsigned long timeout = bpmp->soc->channels.thread.timeout;
> +	unsigned int count = bpmp->soc->channels.thread.count;
> +	struct tegra_bpmp_channel *channel;
> +	unsigned long flags;
> +	unsigned int index;
> +	int err;
> +
> +	err = down_timeout(&bpmp->threaded.lock, usecs_to_jiffies(timeout));
> +	if (err < 0)
> +		return ERR_PTR(err);
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	index = find_first_zero_bit(bpmp->threaded.allocated, count);
> +	if (index == count) {
> +		channel = ERR_PTR(-EBUSY);
> +		goto unlock;
> +	}
> +
> +	channel = tegra_bpmp_channel_get_thread(bpmp, index);
> +	if (!channel) {
> +		channel = ERR_PTR(-EINVAL);
> +		goto unlock;
> +	}
> +
> +	if (!tegra_bpmp_master_free(channel)) {
> +		channel = ERR_PTR(-EBUSY);
> +		goto unlock;
> +	}
> +
> +	set_bit(index, bpmp->threaded.allocated);
> +
> +	err = __tegra_bpmp_channel_write(channel, mrq, MSG_ACK | MSG_RING,
> +					 data, size);
> +	if (err < 0) {
> +		clear_bit(index, bpmp->threaded.allocated);
> +		goto unlock;
> +	}
> +
> +	set_bit(index, bpmp->threaded.busy);
> +
> +unlock:
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +	return channel;
> +}
> +
> +static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
> +					unsigned int mrq, unsigned long flags,
> +					const void *data, size_t size)
> +{
> +	int err;
> +
> +	err = tegra_bpmp_wait_master_free(channel);
> +	if (err < 0)
> +		return err;
> +
> +	return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
> +}
> +
> +int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       struct tegra_bpmp_message *msg)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	int err;
> +
> +	if (WARN_ON(!irqs_disabled()))
> +		return -EPERM;
> +
> +	if (!tegra_bpmp_message_valid(msg))
> +		return -EINVAL;
> +
> +	channel = tegra_bpmp_channel_get_tx(bpmp);
> +
> +	err = tegra_bpmp_channel_write(channel, msg->mrq, MSG_ACK,
> +				       msg->tx.data, msg->tx.size);
> +	if (err < 0)
> +		return err;
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +
> +	err = tegra_bpmp_wait_ack(channel);
> +	if (err)
> +		return err;
> +
> +	return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
> +
> +int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
> +			struct tegra_bpmp_message *msg)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	unsigned long timeout;
> +	int err;
> +
> +	if (WARN_ON(irqs_disabled()))
> +		return -EPERM;
> +
> +	if (!tegra_bpmp_message_valid(msg))
> +		return -EINVAL;
> +
> +	channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
> +					    msg->tx.size);
> +	if (IS_ERR(channel))
> +		return PTR_ERR(channel);
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +
> +	timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout);
> +
> +	err = wait_for_completion_timeout(&channel->completion, timeout);
> +	if (err <= 0) {

wait_for_completion_timeout() returns unsigned long - why are we
checking for < 0? The documentation says that zero indicates timeout.

> +		if (err == 0)
> +			err = -ETIMEDOUT;
> +
> +		return err;
> +	}
> +
> +	return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
> +
> +static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
> +						  unsigned int mrq)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +
> +	list_for_each_entry(entry, &bpmp->mrqs, list)
> +		if (entry->mrq == mrq)
> +			return entry;
> +
> +	return NULL;
> +}
> +
> +static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
> +				  int code, const void *data, size_t size)
> +{
> +	unsigned long flags = channel->ib->flags;
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	struct tegra_bpmp_mb_data *frame;
> +	int err;
> +
> +	if (WARN_ON(size > TEGRA_BPMP_MSG_DATA_SIZE))
> +		return;
> +
> +	err = tegra_ivc_read_advance(channel->ivc);
> +	if (WARN_ON(err < 0))
> +		return;
> +
> +	if ((flags & MSG_ACK) == 0)
> +		return;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (WARN_ON(IS_ERR_OR_NULL(frame)))
> +		return;
> +
> +	frame->code = code;
> +
> +	if (data && size > 0)
> +		memcpy_toio(frame->data, data, size);
> +
> +	err = tegra_ivc_write_advance(channel->ivc);
> +	if (WARN_ON(err < 0))
> +		return;
> +
> +	if (flags & MSG_RING) {
> +		err = mbox_send_message(bpmp->mbox.channel, NULL);
> +		if (WARN_ON(err < 0))
> +			return;
> +
> +		mbox_client_txdone(bpmp->mbox.channel, 0);
> +	}
> +}
> +
> +static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
> +				  unsigned int mrq,
> +				  struct tegra_bpmp_channel *channel)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	u32 zero = 0;
> +
> +	spin_lock(&bpmp->lock);
> +
> +	entry = tegra_bpmp_find_mrq(bpmp, mrq);
> +	if (!entry) {
> +		spin_unlock(&bpmp->lock);
> +		tegra_bpmp_mrq_return(channel, -EINVAL, &zero, sizeof(zero));
> +		return;
> +	}
> +
> +	entry->handler(mrq, channel, entry->data);
> +
> +	spin_unlock(&bpmp->lock);
> +}
> +
> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	unsigned long flags;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	entry = devm_kzalloc(bpmp->dev, sizeof(*entry), GFP_KERNEL);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	entry->mrq = mrq;
> +	entry->handler = handler;
> +	entry->data = data;
> +	list_add(&entry->list, &bpmp->mrqs);
> +
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_request_mrq);
> +
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	entry = tegra_bpmp_find_mrq(bpmp, mrq);
> +	if (!entry)
> +		goto unlock;
> +
> +	list_del(&entry->list);
> +	devm_kfree(bpmp->dev, entry);
> +
> +unlock:
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq);
> +
> +static void tegra_bpmp_mrq_handle_ping(unsigned int mrq,
> +				       struct tegra_bpmp_channel *channel,
> +				       void *data)
> +{
> +	struct mrq_ping_request *request;
> +	struct mrq_ping_response response;
> +
> +	request = (struct mrq_ping_request *)channel->ib->data;
> +
> +	memset(&response, 0, sizeof(response));

Why memset() - here and other places - ? Are you preparing for a
future/unforseen change to the ABI structures? If yes, I think we can ignore
that case - the existing structures should never change.

> +	response.reply = request->challenge << (smp_processor_id() + 1);

This does not follow the spec. reply should be challenge << 1
(downstream implementation seems wrong).

> +
> +	tegra_bpmp_mrq_return(channel, 0, &response, sizeof(response));
> +}
> +
> +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_ping_response response;
> +	struct mrq_ping_request request;
> +	struct tegra_bpmp_message msg;
> +	ktime_t start, delta;
> +	unsigned long flags;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.challenge = 1;
> +
> +	memset(&response, 0, sizeof(response));
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_PING;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	start = ktime_get();

The ktime_get()s should be within the local_irq_save/restore() - so we
get a better measure of the round trip delay?

> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	delta = ktime_sub(ktime_get(), start);
> +
> +	if (!err)
> +		dev_info(bpmp->dev,
> +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> +			 request.challenge, response.reply,
> +			 ktime_to_us(delta));
> +
> +	return err;
> +}
> +
> +static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
> +				       size_t size)
> +{
> +	struct mrq_query_tag_request request;
> +	struct tegra_bpmp_message msg;
> +	unsigned long flags;
> +	dma_addr_t phys;
> +	void *virt;
> +	int err;
> +
> +	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
> +				  GFP_KERNEL | GFP_DMA32);
> +	if (!virt)
> +		return -ENOMEM;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.addr = phys;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_QUERY_TAG;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);

I believe the local_irq_save/restore can be dropped - they are not
adding any value.

> +
> +	if (err == 0)
> +		strlcpy(tag, virt, size);
> +
> +	dma_free_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, virt, phys);
> +
> +	return err;
> +}
> +
> +static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long flags = channel->ob->flags;
> +
> +	if ((flags & MSG_RING) == 0)
> +		return;
> +
> +	complete(&channel->completion);
> +}
> +
> +static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
> +{
> +	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
> +	struct tegra_bpmp_channel *channel;
> +	unsigned int i, count;
> +	unsigned long *busy;
> +
> +	channel = tegra_bpmp_channel_get_rx(bpmp);
> +	count = bpmp->soc->channels.thread.count;
> +	busy = bpmp->threaded.busy;
> +
> +	if (tegra_bpmp_master_acked(channel))
> +		tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel);
> +
> +	spin_lock(&bpmp->lock);
> +
> +	for_each_set_bit(i, busy, count) {
> +		struct tegra_bpmp_channel *channel;
> +
> +		channel = tegra_bpmp_channel_get_thread(bpmp, i);
> +		if (!channel)
> +			continue;
> +
> +		if (tegra_bpmp_master_acked(channel)) {
> +			tegra_bpmp_channel_signal(channel);
> +			clear_bit(i, busy);
> +		}
> +	}
> +
> +	spin_unlock(&bpmp->lock);
> +}
> +
> +static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
> +{
> +	struct tegra_bpmp *bpmp = data;
> +	int err;
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +}
> +
> +static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
> +				   struct tegra_bpmp *bpmp,
> +				   unsigned int index)
> +{
> +	size_t message, queue;
> +	void __iomem *rx_base;
> +	void __iomem *tx_base;
> +	int err;
> +
> +	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
> +				    GFP_KERNEL);
> +	if (!channel->ivc)
> +		return -ENOMEM;
> +
> +	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
> +	queue = tegra_ivc_total_queue_size(message);
> +
> +	rx_base = bpmp->rx_base + queue * index;
> +	tx_base = bpmp->tx_base + queue * index;
> +
> +	err = tegra_ivc_init(channel->ivc, bpmp->dev, rx_base, DMA_ERROR_CODE,
> +			     tx_base, DMA_ERROR_CODE, 1, message,
> +			     tegra_bpmp_ivc_notify, bpmp);
> +	if (err < 0) {
> +		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
> +			index, err);
> +		return err;
> +	}
> +
> +	/* reset the channel state */
> +	tegra_ivc_reset(channel->ivc);
> +
> +	/* sync the channel state with BPMP */
> +	while (tegra_ivc_notified(channel->ivc))
> +		;
> +
> +	init_completion(&channel->completion);
> +	channel->bpmp = bpmp;
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_pg_read_state_response response;
> +	struct mrq_pg_read_state_request request;
> +	struct tegra_bpmp_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	dev_dbg(bpmp->dev, "powergates:\n");
> +
> +	for (i = 0; i < 32; i++) {
> +		memset(&request, 0, sizeof(request));
> +		request.partition_id = i;
> +
> +		memset(&response, 0, sizeof(response));
> +
> +		memset(&msg, 0, sizeof(msg));
> +		msg.mrq = MRQ_PG_READ_STATE;
> +		msg.tx.data = &request;
> +		msg.tx.size = sizeof(request);
> +		msg.rx.data = &response;
> +		msg.rx.size = sizeof(response);
> +
> +		err = tegra_bpmp_transfer(bpmp, &msg);
> +		if (err < 0) {
> +			dev_err(bpmp->dev, "failed to transfer message: %d\n", err);
> +			continue;
> +		}
> +
> +		dev_dbg(bpmp->dev, "  %u: %x (%x)\n", i, response.logic_state,
> +			response.sram_state);
> +	}
> +
> +	return 0;
> +}

IMO, this should not be here (in the firmware interface driver). We
should keep the bpmp driver and its users de-coupled. 

> +
> +static int tegra_bpmp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +	struct resource res;
> +	unsigned int i;
> +	char tag[32];
> +	size_t size;
> +	int err;
> +
> +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> +	if (!bpmp)
> +		return -ENOMEM;
> +
> +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> +	bpmp->dev = &pdev->dev;
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->tx_base))
> +		return PTR_ERR(bpmp->tx_base);
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->rx_base))
> +		return PTR_ERR(bpmp->rx_base);
> +
> +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> +			     bpmp->soc->channels.thread.count +
> +			     bpmp->soc->channels.cpu_rx.count;
> +
> +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> +				      sizeof(*channel), GFP_KERNEL);
> +	if (!bpmp->channels)
> +		return -ENOMEM;
> +
> +	/* mbox registration */
> +	bpmp->mbox.client.dev = &pdev->dev;
> +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> +	bpmp->mbox.client.tx_block = false;
> +	bpmp->mbox.client.knows_txdone = false;
> +
> +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> +	if (IS_ERR(bpmp->mbox.channel)) {
> +		err = PTR_ERR(bpmp->mbox.channel);
> +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> +		return err;
> +	}
> +
> +	/* message channel initialization */
> +	for (i = 0; i < bpmp->num_channels; i++) {
> +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> +
> +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> +		if (err)
> +			return err;
> +	}
> +
> +	bpmp->threaded.count = bpmp->soc->channels.thread.count;
> +	size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long);
> +
> +	bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!bpmp->threaded.allocated)
> +		return -ENOMEM;
> +
> +	bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!bpmp->threaded.busy)
> +		return -ENOMEM;
> +
> +	sema_init(&bpmp->threaded.lock, bpmp->threaded.count);
> +
> +	INIT_LIST_HEAD(&bpmp->mrqs);
> +	spin_lock_init(&bpmp->lock);
> +
> +	err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
> +				     tegra_bpmp_mrq_handle_ping, bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_ping(bpmp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to ping BPMP: %d\n", err);
> +		goto free_mrq;
> +	}
> +
> +	err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
> +		goto free_mrq;
> +	}
> +
> +	dev_info(&pdev->dev, "firmware: %s\n", tag);
> +
> +	err = tegra_bpmp_init_clocks(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_init_resets(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_init_powergates(bpmp);
> +	if (err < 0)
> +		return err;

Given a choice, I would want to keep this out of the bpmp driver
altogether.

> +
> +	platform_set_drvdata(pdev, bpmp);
> +
> +	return 0;
> +
> +free_mrq:
> +	tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
> +
> +	return err;
> +}
> +
> +static const struct tegra_bpmp_soc tegra186_soc = {
> +	.channels = {
> +		.cpu_tx = {
> +			.offset = 0,
> +			.count = 6,
> +			.timeout = 60 * USEC_PER_SEC,
> +		},
> +		.thread = {
> +			.offset = 6,
> +			.count = 7,
> +			.timeout = 600 * USEC_PER_SEC,
> +		},
> +		.cpu_rx = {
> +			.offset = 13,
> +			.count = 1,
> +			.timeout = 0,
> +		},
> +	},
> +	.num_resets = 193,
> +};
> +
> +static const struct of_device_id tegra_bpmp_match[] = {
> +	{ .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc },
> +	{ }
> +};
> +
> +static struct platform_driver tegra_bpmp_driver = {
> +	.driver = {
> +		.name = "tegra-bpmp",
> +		.of_match_table = tegra_bpmp_match,
> +	},
> +	.probe = tegra_bpmp_probe,
> +};
> +
> +static int __init tegra_bpmp_init(void)
> +{
> +	return platform_driver_register(&tegra_bpmp_driver);
> +}
> +core_initcall(tegra_bpmp_init);
> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> new file mode 100644
> index 000000000000..0aaef5960e29
> --- /dev/null
> +++ b/include/soc/tegra/bpmp-abi.h
> @@ -0,0 +1,1601 @@
> +/*
> + * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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 _ABI_BPMP_ABI_H_
> +#define _ABI_BPMP_ABI_H_
> +
> +#ifdef LK
> +#include <stdint.h>
> +#endif
> +
> +#ifndef __ABI_PACKED
> +#define __ABI_PACKED __attribute__((packed))
> +#endif
> +
> +#ifdef NO_GCC_EXTENSIONS
> +#define EMPTY char empty;
> +#define EMPTY_ARRAY 1
> +#else
> +#define EMPTY
> +#define EMPTY_ARRAY 0
> +#endif
> +
> +#ifndef __UNION_ANON
> +#define __UNION_ANON
> +#endif
> +/**
> + * @file
> + */
> +
> +
> +/**
> + * @defgroup MRQ MRQ Messages
> + * @brief Messages sent to/from BPMP via IPC
> + * @{
> + *   @defgroup MRQ_Format Message Format
> + *   @defgroup MRQ_Codes Message Request (MRQ) Codes
> + *   @defgroup MRQ_Payloads Message Payloads
> + *   @defgroup Error_Codes Error Codes
> + * @}
> + */
> +
> +/**
> + * @addtogroup MRQ_Format Message Format
> + * @{
> + * The CPU requests the BPMP to perform a particular service by
> + * sending it an IVC frame containing a single MRQ message. An MRQ
> + * message consists of a @ref mrq_request followed by a payload whose
> + * format depends on mrq_request::mrq.
> + *
> + * The BPMP processes the data and replies with an IVC frame (on the
> + * same IVC channel) containing and MRQ response. An MRQ response
> + * consists of a @ref mrq_response followed by a payload whose format
> + * depends on the associated mrq_request::mrq.
> + *
> + * A well-defined subset of the MRQ messages that the CPU sends to the
> + * BPMP can lead to BPMP eventually sending an MRQ message to the
> + * CPU. For example, when the CPU uses an #MRQ_THERMAL message to set
> + * a thermal trip point, the BPMP may eventually send a single
> + * #MRQ_THERMAL message of its own to the CPU indicating that the trip
> + * point has been crossed.
> + * @}
> + */
> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ message
> + *
> + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> + * the MRQ message is a payload (immediately following the
> + * mrq_request) whose format depends on mrq.
> + *
> + * @todo document the flags
> + */
> +struct mrq_request {
> +	/** @brief MRQ number of the request */
> +	uint32_t mrq;
> +	/** @brief flags for the request */
> +	uint32_t flags;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ response
> + *
> + *  Provides an error code for the associated MRQ message. The
> + *  remainder of the MRQ response is a payload (immediately following
> + *  the mrq_response) whose format depends on the associated
> + *  mrq_request::mrq
> + *
> + * @todo document the flags
> + */
> +struct mrq_response {
> +	/** @brief error code for the MRQ request itself */
> +	int32_t err;
> +	/** @brief flags for the response */
> +	uint32_t flags;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Format
> + * Minimum needed size for an IPC message buffer
> + */
> +#define MSG_MIN_SZ	128
> +/**
> + * @ingroup MRQ_Format
> + *  Minimum size guaranteed for data in an IPC message buffer
> + */
> +#define MSG_DATA_MIN_SZ	120
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @name Legal MRQ codes
> + * These are the legal values for mrq_request::mrq
> + * @{
> + */
> +
> +#define MRQ_PING		0
> +#define MRQ_QUERY_TAG		1
> +#define MRQ_MODULE_LOAD		4
> +#define MRQ_MODULE_UNLOAD	5
> +#define MRQ_TRACE_MODIFY	7
> +#define MRQ_WRITE_TRACE		8
> +#define MRQ_THREADED_PING	9
> +#define MRQ_MODULE_MAIL		11
> +#define MRQ_DEBUGFS		19
> +#define MRQ_RESET		20
> +#define MRQ_I2C			21
> +#define MRQ_CLK			22
> +#define MRQ_QUERY_ABI		23
> +#define MRQ_PG_READ_STATE	25
> +#define MRQ_PG_UPDATE_STATE	26
> +#define MRQ_THERMAL		27
> +#define MRQ_CPU_VHINT		28
> +#define MRQ_ABI_RATCHET		29
> +#define MRQ_EMC_DVFS_LATENCY	31
> +#define MRQ_TRACE_ITER		64
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @brief Maximum MRQ code to be sent by CPU software to
> + * BPMP. Subject to change in future
> + */
> +#define MAX_CPU_MRQ_ID		64
> +
> +/**
> + * @addtogroup MRQ_Payloads Message Payloads
> + * @{
> + *   @defgroup Ping
> + *   @defgroup Query_Tag Query Tag
> + *   @defgroup Module Loadable Modules
> + *   @defgroup Trace
> + *   @defgroup Debugfs
> + *   @defgroup Reset
> + *   @defgroup I2C
> + *   @defgroup Clocks
> + *   @defgroup ABI_info ABI Info
> + *   @defgroup MC_Flush MC Flush
> + *   @defgroup Powergating
> + *   @defgroup Thermal
> + *   @defgroup Vhint CPU Voltage hint
> + *   @defgroup MRQ_Deprecated Deprecated MRQ messages
> + *   @defgroup EMC
> + * @}
> + */
> +
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PING
> + * @brief A simple ping
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: @ref mrq_ping_request
> + * * Response Payload: @ref mrq_ping_response
> + *
> + * @ingroup MRQ_Codes
> + * @def MRQ_THREADED_PING
> + * @brief A deeper ping
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_ping_request
> + * * Response Payload: @ref mrq_ping_response
> + *
> + * Behavior is equivalent to a simple #MRQ_PING except that BPMP
> + * responds from a thread context (providing a slightly more robust
> + * sign of life).
> + *
> + */
> +
> +/**
> + * @ingroup Ping
> + * @brief request with #MRQ_PING
> + *
> + * Used by the sender of an #MRQ_PING message to request a pong from
> + * recipient. The response from the recipient is computed based on
> + * #challenge.
> + */
> +struct mrq_ping_request {
> +/** @brief arbitrarily chosen value */
> +	uint32_t challenge;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Ping
> + * @brief response to #MRQ_PING
> + *
> + * Sent in response to an #MRQ_PING message. #reply should be the
> + * mrq_ping_request challenge left shifted by 1 with the carry-bit
> + * dropped.
> + *
> + */
> +struct mrq_ping_response {
> +	/** @brief response to the MRQ_PING challege */
> +	uint32_t reply;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_QUERY_TAG
> + * @brief Query BPMP firmware's tag (i.e. version information)
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_query_tag_request
> + * * Response Payload: N/A
> + *
> + */
> +
> +/**
> + * @ingroup Query_Tag
> + * @brief request with #MRQ_QUERY_TAG
> + *
> + * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory
> + * pointed by #addr with BPMP firmware header.
> + *
> + * The sender is reponsible for ensuring that #addr is mapped in to
> + * the recipient's address map.
> + */
> +struct mrq_query_tag_request {
> +  /** @brief base address to store the firmware header */
> +	uint32_t addr;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_LOAD
> + * @brief dynamically load a BPMP code module
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_load_request
> + * * Response Payload: @ref mrq_module_load_response
> + *
> + * @note This MRQ is disabled on production systems
> + *
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_LOAD
> + *
> + * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically
> + * load the code located at #phys_addr and having size #size
> + * bytes. #phys_addr is treated as a void pointer.
> + *
> + * The recipient copies the code from #phys_addr to locally allocated
> + * memory prior to responding to this message.
> + *
> + * @todo document the module header format
> + *
> + * The sender is responsible for ensuring that the code is mapped in
> + * the recipient's address map.
> + *
> + */
> +struct mrq_module_load_request {
> +	/** @brief base address of the code to load. Treated as (void *) */
> +	uint32_t phys_addr; /* (void *) */
> +	/** @brief size in bytes of code to load */
> +	uint32_t size;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Module
> + * @brief response to #MRQ_MODULE_LOAD
> + *
> + * @todo document mrq_response::err
> + */
> +struct mrq_module_load_response {
> +	/** @brief handle to the loaded module */
> +	uint32_t base;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_UNLOAD
> + * @brief unload a previously loaded code module
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_unload_request
> + * * Response Payload: N/A
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_UNLOAD
> + *
> + * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded
> + * module be unloaded.
> + */
> +struct mrq_module_unload_request {
> +	/** @brief handle of the module to unload */
> +	uint32_t base;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_TRACE_MODIFY
> + * @brief modify the set of enabled trace events
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_trace_modify_request
> + * * Response Payload: @ref mrq_trace_modify_response
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Trace
> + * @brief request with #MRQ_TRACE_MODIFY
> + *
> + * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace
> + * events.  #set takes precedence for any bit set in both #set and
> + * #clr.
> + */
> +struct mrq_trace_modify_request {
> +	/** @brief bit mask of trace events to disable */
> +	uint32_t clr;
> +	/** @brief bit mask of trace events to enable */
> +	uint32_t set;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Trace
> + * @brief response to #MRQ_TRACE_MODIFY
> + *
> + * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the
> + * state of which events are enabled after the recipient acted on the
> + * message.
> + *
> + */
> +struct mrq_trace_modify_response {
> +	/** @brief bit mask of trace event enable states */
> +	uint32_t mask;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_WRITE_TRACE
> + * @brief Write trace data to a buffer
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_write_trace_request
> + * * Response Payload: @ref mrq_write_trace_response
> + *
> + * mrq_response::err depends on the @ref mrq_write_trace_request field
> + * values. err is -#BPMP_EINVAL if size is zero or area is NULL or
> + * area is in an illegal range. A positive value for err indicates the
> + * number of bytes written to area.
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Trace
> + * @brief request with #MRQ_WRITE_TRACE
> + *
> + * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace
> + * data from the recipient's local buffer to the output buffer. #area
> + * is treated as a byte-aligned pointer in the recipient's address
> + * space.
> + *
> + * The sender is responsible for ensuring that the output
> + * buffer is mapped in the recipient's address map. The recipient is
> + * responsible for protecting its own code and data from accidental
> + * overwrites.
> + */
> +struct mrq_write_trace_request {
> +	/** @brief base address of output buffer */
> +	uint32_t area;
> +	/** @brief size in bytes of the output buffer */
> +	uint32_t size;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Trace
> + * @brief response to #MRQ_WRITE_TRACE
> + *
> + * Once this response is sent, the respondent will not access the
> + * output buffer further.
> + */
> +struct mrq_write_trace_response {
> +	/**
> +	 * @brief flag whether more data remains in local buffer
> +	 *
> +	 * Value is 1 if the entire local trace buffer has been
> +	 * drained to the outputbuffer. Value is 0 otherwise.
> +	 */
> +	uint32_t eof;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct mrq_threaded_ping_request {
> +	uint32_t challenge;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct mrq_threaded_ping_response {
> +	uint32_t reply;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_MAIL
> + * @brief send a message to a loadable module
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_mail_request
> + * * Response Payload: @ref mrq_module_mail_response
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_MAIL
> + */
> +struct mrq_module_mail_request {
> +	/** @brief handle to the previously loaded module */
> +	uint32_t base;
> +	/** @brief module-specific mail payload
> +	 *
> +	 * The length of data[ ] is unknown to the BPMP core firmware
> +	 * but it is limited to the size of an IPC message.
> +	 */
> +	uint8_t data[EMPTY_ARRAY];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Module
> + * @brief response to #MRQ_MODULE_MAIL
> + */
> +struct mrq_module_mail_response {
> +	/** @brief module-specific mail payload
> +	 *
> +	 * The length of data[ ] is unknown to the BPMP core firmware
> +	 * but it is limited to the size of an IPC message.
> +	 */
> +	uint8_t data[EMPTY_ARRAY];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_DEBUGFS
> + * @brief Interact with BPMP's debugfs file nodes
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_debugfs_request
> + * * Response Payload: @ref mrq_debugfs_response
> + */
> +
> +/**
> + * @addtogroup Debugfs
> + * @{
> + *
> + * The BPMP firmware implements a pseudo-filesystem called
> + * debugfs. Any driver within the firmware may register with debugfs
> + * to expose an arbitrary set of "files" in the filesystem. When
> + * software on the CPU writes to a debugfs file, debugfs passes the
> + * written data to a callback provided by the driver. When software on
> + * the CPU reads a debugfs file, debugfs queries the driver for the
> + * data to return to the CPU. The intention of the debugfs filesystem
> + * is to provide information useful for debugging the system at
> + * runtime.
> + *
> + * @note The files exposed via debugfs are not part of the
> + * BPMP firmware's ABI. debugfs files may be added or removed in any
> + * given version of the firmware. Typically the semantics of a debugfs
> + * file are consistent from version to version but even that is not
> + * guaranteed.
> + *
> + * @}
> + */
> +/** @ingroup Debugfs */
> +enum mrq_debugfs_commands {
> +	CMD_DEBUGFS_READ = 1,
> +	CMD_DEBUGFS_WRITE = 2,
> +	CMD_DEBUGFS_DUMPDIR = 3,
> +	CMD_DEBUGFS_MAX
> +};
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_request {
> +	/** @brief physical address pointing at filename */
> +	uint32_t fnameaddr;
> +	/** @brief length in bytes of filename buffer */
> +	uint32_t fnamelen;
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_dumpdir_request {
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief response data for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_response {
> +	/** @brief always 0 */
> +	uint32_t reserved;
> +	/** @brief number of bytes read from or written to data buffer */
> +	uint32_t nbytes;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief response data for CMD_DEBUGFS_DUMPDIR command
> + */
> +struct cmd_debugfs_dumpdir_response {
> +	/** @brief always 0 */
> +	uint32_t reserved;
> +	/** @brief number of bytes read from or written to data buffer */
> +	uint32_t nbytes;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief request with #MRQ_DEBUGFS.
> + *
> + * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs
> + * command to execute. Legal commands are the values of @ref
> + * mrq_debugfs_commands. Each command requires a specific additional
> + * payload of data.
> + *
> + * |command            |payload|
> + * |-------------------|-------|
> + * |CMD_DEBUGFS_READ   |fop    |
> + * |CMD_DEBUGFS_WRITE  |fop    |
> + * |CMD_DEBUGFS_DUMPDIR|dumpdir|
> + */
> +struct mrq_debugfs_request {
> +	uint32_t cmd;
> +	union {
> +		struct cmd_debugfs_fileop_request fop;
> +		struct cmd_debugfs_dumpdir_request dumpdir;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + */
> +struct mrq_debugfs_response {
> +	/** @brief always 0 */
> +	int32_t reserved;
> +	union {
> +		/** @brief response data for CMD_DEBUGFS_READ OR
> +		 * CMD_DEBUGFS_WRITE command
> +		 */
> +		struct cmd_debugfs_fileop_response fop;
> +		/** @brief response data for CMD_DEBUGFS_DUMPDIR command */
> +		struct cmd_debugfs_dumpdir_response dumpdir;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @addtogroup Debugfs
> + * @{
> + */
> +#define DEBUGFS_S_ISDIR	(1 << 9)
> +#define DEBUGFS_S_IRUSR	(1 << 8)
> +#define DEBUGFS_S_IWUSR	(1 << 7)
> +/** @} */
> +
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_RESET
> + * @brief reset an IP block
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_reset_request
> + * * Response Payload: N/A
> + */
> +
> +/**
> + * @ingroup Reset
> + */
> +enum mrq_reset_commands {
> +	CMD_RESET_ASSERT = 1,
> +	CMD_RESET_DEASSERT = 2,
> +	CMD_RESET_MODULE = 3,
> +	CMD_RESET_MAX, /* not part of ABI and subject to change */
> +};
> +
> +/**
> + * @ingroup Reset
> + * @brief request with MRQ_RESET
> + *
> + * Used by the sender of an #MRQ_RESET message to request BPMP to
> + * assert or or deassert a given reset line.
> + */
> +struct mrq_reset_request {
> +	/** @brief reset action to perform (@enum mrq_reset_commands) */
> +	uint32_t cmd;
> +	/** @brief id of the reset to affected */
> +	uint32_t reset_id;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_I2C
> + * @brief issue an i2c transaction
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_i2c_request
> + * * Response Payload: @ref mrq_i2c_response
> + */
> +
> +/**
> + * @addtogroup I2C
> + * @{
> + */
> +#define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE	(MSG_DATA_MIN_SZ - 12)
> +#define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE	(MSG_DATA_MIN_SZ - 4)
> +/** @} */
> +
> +/**
> + * @ingroup I2C
> + * @name Serial I2C flags
> + * Use these flags with serial_i2c_request::flags
> + * @{
> + */
> +#define SERIALI2C_TEN           0x0010
> +#define SERIALI2C_RD            0x0001
> +#define SERIALI2C_STOP          0x8000
> +#define SERIALI2C_NOSTART       0x4000
> +#define SERIALI2C_REV_DIR_ADDR  0x2000
> +#define SERIALI2C_IGNORE_NAK    0x1000
> +#define SERIALI2C_NO_RD_ACK     0x0800
> +#define SERIALI2C_RECV_LEN      0x0400
> +/** @} */
> +/** @ingroup I2C */
> +enum {
> +	CMD_I2C_XFER = 1
> +};
> +
> +/**
> + * @ingroup I2C
> + * @brief serializable i2c request
> + *
> + * Instances of this structure are packed (little-endian) into
> + * cmd_i2c_xfer_request::data_buf. Each instance represents a single
> + * transaction (or a portion of a transaction with repeated starts) on
> + * an i2c bus.
> + *
> + * Because these structures are packed, some instances are likely to
> + * be misaligned. Additionally because #data is variable length, it is
> + * not possible to iterate through a serialized list of these
> + * structures without inspecting #len in each instance.  It may be
> + * easier to serialize or deserialize cmd_i2c_xfer_request::data_buf
> + * manually rather than using this structure definition.
> +*/
> +struct serial_i2c_request {
> +	/** @brief I2C slave address */
> +	uint16_t addr;
> +	/** @brief bitmask of SERIALI2C_ flags */
> +	uint16_t flags;
> +	/** @brief length of I2C transaction in bytes */
> +	uint16_t len;
> +	/** @brief for write transactions only, #len bytes of data */
> +	uint8_t data[];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief trigger one or more i2c transactions
> + */
> +struct cmd_i2c_xfer_request {
> +	/** @brief valid bus number from mach-t186/i2c-t186.h*/
> +	uint32_t bus_id;
> +
> +	/** @brief count of valid bytes in #data_buf*/
> +	uint32_t data_size;
> +
> +	/** @brief serialized packed instances of @ref serial_i2c_request*/
> +	uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief container for data read from the i2c bus
> + *
> + * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute
> + * zero or more I2C reads. The data read from the bus is serialized
> + * into #data_buf.
> + */
> +struct cmd_i2c_xfer_response {
> +	/** @brief count of valid bytes in #data_buf*/
> +	uint32_t data_size;
> +	/** @brief i2c read data */
> +	uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief request with #MRQ_I2C
> + */
> +struct mrq_i2c_request {
> +	/** @brief always CMD_I2C_XFER (i.e. 1) */
> +	uint32_t cmd;
> +	/** @brief parameters of the transfer request */
> +	struct cmd_i2c_xfer_request xfer;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief response to #MRQ_I2C
> + */
> +struct mrq_i2c_response {
> +	struct cmd_i2c_xfer_response xfer;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_CLK
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_clk_request
> + * * Response Payload: @ref mrq_clk_response
> + * @addtogroup Clocks
> + * @{
> + */
> +
> +/**
> + * @name MRQ_CLK sub-commands
> + * @{
> + */
> +enum {
> +	CMD_CLK_GET_RATE = 1,
> +	CMD_CLK_SET_RATE = 2,
> +	CMD_CLK_ROUND_RATE = 3,
> +	CMD_CLK_GET_PARENT = 4,
> +	CMD_CLK_SET_PARENT = 5,
> +	CMD_CLK_IS_ENABLED = 6,
> +	CMD_CLK_ENABLE = 7,
> +	CMD_CLK_DISABLE = 8,
> +	CMD_CLK_GET_ALL_INFO = 14,
> +	CMD_CLK_GET_MAX_CLK_ID = 15,
> +	CMD_CLK_MAX,
> +};
> +/** @} */
> +
> +#define MRQ_CLK_NAME_MAXLEN	40
> +#define MRQ_CLK_MAX_PARENTS	16
> +
> +/** @private */
> +struct cmd_clk_get_rate_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_round_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_round_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_parent_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_parent_response {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_parent_request {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_parent_response {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_is_enabled_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_is_enabled_response {
> +	int32_t state;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_enable_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_enable_response {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_disable_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_disable_response {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_all_info_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_all_info_response {
> +	uint32_t flags;
> +	uint32_t parent;
> +	uint32_t parents[MRQ_CLK_MAX_PARENTS];
> +	uint8_t num_parents;
> +	uint8_t name[MRQ_CLK_NAME_MAXLEN];
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_max_clk_id_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_max_clk_id_response {
> +	uint32_t max_id;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup Clocks
> + * @brief request with #MRQ_CLK
> + *
> + * Used by the sender of an #MRQ_CLK message to control clocks. The
> + * clk_request is split into several sub-commands. Some sub-commands
> + * require no additional data. Others have a sub-command specific
> + * payload
> + *
> + * |sub-command                 |payload                |
> + * |----------------------------|-----------------------|
> + * |CMD_CLK_GET_RATE            |-                      |
> + * |CMD_CLK_SET_RATE            |clk_set_rate           |
> + * |CMD_CLK_ROUND_RATE          |clk_round_rate         |
> + * |CMD_CLK_GET_PARENT          |-                      |
> + * |CMD_CLK_SET_PARENT          |clk_set_parent         |
> + * |CMD_CLK_IS_ENABLED          |-                      |
> + * |CMD_CLK_ENABLE              |-                      |
> + * |CMD_CLK_DISABLE             |-                      |
> + * |CMD_CLK_GET_ALL_INFO        |-                      |
> + * |CMD_CLK_GET_MAX_CLK_ID      |-                      |
> + *
> + */
> +
> +struct mrq_clk_request {
> +	/** @brief sub-command and clock id concatenated to 32-bit word.
> +	 * - bits[31..24] is the sub-cmd.
> +	 * - bits[23..0] is the clock id
> +	 */
> +	uint32_t cmd_and_id;
> +
> +	union {
> +		/** @private */
> +		struct cmd_clk_get_rate_request clk_get_rate;
> +		struct cmd_clk_set_rate_request clk_set_rate;
> +		struct cmd_clk_round_rate_request clk_round_rate;
> +		/** @private */
> +		struct cmd_clk_get_parent_request clk_get_parent;
> +		struct cmd_clk_set_parent_request clk_set_parent;
> +		/** @private */
> +		struct cmd_clk_enable_request clk_enable;
> +		/** @private */
> +		struct cmd_clk_disable_request clk_disable;
> +		/** @private */
> +		struct cmd_clk_is_enabled_request clk_is_enabled;
> +		/** @private */
> +		struct cmd_clk_get_all_info_request clk_get_all_info;
> +		/** @private */
> +		struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Clocks
> + * @brief response to MRQ_CLK
> + *
> + * Each sub-command supported by @ref mrq_clk_request may return
> + * sub-command-specific data. Some do and some do not as indicated in
> + * the following table
> + *
> + * |sub-command                 |payload                 |
> + * |----------------------------|------------------------|
> + * |CMD_CLK_GET_RATE            |clk_get_rate            |
> + * |CMD_CLK_SET_RATE            |clk_set_rate            |
> + * |CMD_CLK_ROUND_RATE          |clk_round_rate          |
> + * |CMD_CLK_GET_PARENT          |clk_get_parent          |
> + * |CMD_CLK_SET_PARENT          |clk_set_parent          |
> + * |CMD_CLK_IS_ENABLED          |clk_is_enabled          |
> + * |CMD_CLK_ENABLE              |-                       |
> + * |CMD_CLK_DISABLE             |-                       |
> + * |CMD_CLK_GET_ALL_INFO        |clk_get_all_info        |
> + * |CMD_CLK_GET_MAX_CLK_ID      |clk_get_max_id          |
> + *
> + */
> +
> +struct mrq_clk_response {
> +	union {
> +		struct cmd_clk_get_rate_response clk_get_rate;
> +		struct cmd_clk_set_rate_response clk_set_rate;
> +		struct cmd_clk_round_rate_response clk_round_rate;
> +		struct cmd_clk_get_parent_response clk_get_parent;
> +		struct cmd_clk_set_parent_response clk_set_parent;
> +		/** @private */
> +		struct cmd_clk_enable_response clk_enable;
> +		/** @private */
> +		struct cmd_clk_disable_response clk_disable;
> +		struct cmd_clk_is_enabled_response clk_is_enabled;
> +		struct cmd_clk_get_all_info_response clk_get_all_info;
> +		struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_QUERY_ABI
> + * @brief check if an MRQ is implemented
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: @ref mrq_query_abi_request
> + * * Response Payload: @ref mrq_query_abi_response
> + */
> +
> +/**
> + * @ingroup ABI_info
> + * @brief request with MRQ_QUERY_ABI
> + *
> + * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported
> + * by the recipient.
> + */
> +struct mrq_query_abi_request {
> +	/** @brief MRQ code to query */
> +	uint32_t mrq;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup ABI_info
> + * @brief response to MRQ_QUERY_ABI
> + */
> +struct mrq_query_abi_response {
> +	/** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */
> +	int32_t status;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PG_READ_STATE
> + * @brief read the power-gating state of a partition
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_pg_read_state_request
> + * * Response Payload: @ref mrq_pg_read_state_response
> + * @addtogroup Powergating
> + * @{
> + */
> +
> +/**
> + * @brief request with #MRQ_PG_READ_STATE
> + *
> + * Used by MRQ_PG_READ_STATE call to read the current state of a
> + * partition.
> + */
> +struct mrq_pg_read_state_request {
> +	/** @brief ID of partition */
> +	uint32_t partition_id;
> +} __ABI_PACKED;
> +
> +/**
> + * @brief response to MRQ_PG_READ_STATE
> + * @todo define possible errors.
> + */
> +struct mrq_pg_read_state_response {
> +	/** @brief read as don't care */
> +	uint32_t sram_state;
> +	/** @brief state of power partition
> +	 * * 0 : off
> +	 * * 1 : on
> +	 */
> +	uint32_t logic_state;
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PG_UPDATE_STATE
> + * @brief modify the power-gating state of a partition
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_pg_update_state_request
> + * * Response Payload: N/A
> + * @addtogroup Powergating
> + * @{
> + */
> +
> +/**
> + * @brief request with mrq_pg_update_state_request
> + *
> + * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the
> + * state of a power partition #partition_id.
> + */
> +struct mrq_pg_update_state_request {
> +	/** @brief ID of partition */
> +	uint32_t partition_id;
> +	/** @brief secondary control of power partition
> +	 *  @details Ignored by many versions of the BPMP
> +	 *  firmware. For maximum compatibility, set the value
> +	 *  according to @logic_state
> +	 * *  0x1: power ON partition (@ref logic_state == 0x3)
> +	 * *  0x3: power OFF partition (@ref logic_state == 0x1)
> +	 */
> +	uint32_t sram_state;
> +	/** @brief controls state of power partition, legal values are
> +	 * *  0x1 : power OFF partition
> +	 * *  0x3 : power ON partition
> +	 */
> +	uint32_t logic_state;
> +	/** @brief change state of clocks of the power partition, legal values
> +	 * *  0x0 : do not change clock state
> +	 * *  0x1 : disable partition clocks (only applicable when
> +	 *          @ref logic_state == 0x1)
> +	 * *  0x3 : enable partition clocks (only applicable when
> +	 *          @ref logic_state == 0x3)
> +	 */
> +	uint32_t clock_state;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_THERMAL
> + * @brief interact with BPMP thermal framework
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: TODO
> + * * Response Payload: TODO
> + *
> + * @addtogroup Thermal
> + *
> + * The BPMP firmware includes a thermal framework. Drivers within the
> + * bpmp firmware register with the framework to provide thermal
> + * zones. Each thermal zone corresponds to an entity whose temperature
> + * can be measured. The framework also has a notion of trip points. A
> + * trip point consists of a thermal zone id, a temperature, and a
> + * callback routine. The framework invokes the callback when the zone
> + * hits the indicated temperature. The BPMP firmware uses this thermal
> + * framework interally to implement various temperature-dependent
> + * functions.
> + *
> + * Software on the CPU can use #MRQ_THERMAL (with payload @ref
> + * mrq_thermal_host_to_bpmp_request) to interact with the BPMP thermal
> + * framework. The CPU must It can query the number of supported zones,
> + * query zone temperatures, and set trip points.
> + *
> + * When a trip point set by the CPU gets crossed, BPMP firmware issues
> + * an IPC to the CPU having mrq_request::mrq = #MRQ_THERMAL and a
> + * payload of @ref mrq_thermal_bpmp_to_host_request.
> + * @{
> + */
> +enum mrq_thermal_host_to_bpmp_cmd {
> +	/**
> +	 * @brief Check whether the BPMP driver supports the specified
> +	 * request type.
> +	 *
> +	 * Host needs to supply request parameters.
> +	 *
> +	 * mrq_response::err is 0 if the specified request is
> +	 * supported and -#BPMP_ENODEV otherwise.
> +	 */
> +	CMD_THERMAL_QUERY_ABI = 0,
> +
> +	/**
> +	 * @brief Get the current temperature of the specified zone.
> +	 *
> +	 * Host needs to supply request parameters.
> +	 *
> +	 * mrq_response::err is
> +	 * *  0: Temperature query succeeded.
> +	 * *  -#BPMP_EINVAL: Invalid request parameters.
> +	 * *  -#BPMP_ENOENT: No driver registered for thermal zone..
> +	 * *  -#BPMP_EFAULT: Problem reading temperature measurement.
> +	 */
> +	CMD_THERMAL_GET_TEMP = 1,
> +
> +	/**
> +	 * @brief Enable or disable and set the lower and upper
> +	 *   thermal limits for a thermal trip point. Each zone has
> +	 *   one trip point.
> +	 *
> +	 * Host needs to supply request parameters. Once the
> +	 * temperature hits a trip point, the BPMP will send a message
> +	 * to the CPU having MRQ=MRQ_THERMAL and
> +	 * type=CMD_THERMAL_HOST_TRIP_REACHED
> +	 *
> +	 * mrq_response::err is
> +	 * *  0: Trip successfully set.
> +	 * *  -#BPMP_EINVAL: Invalid request parameters.
> +	 * *  -#BPMP_ENOENT: No driver registered for thermal zone.
> +	 * *  -#BPMP_EFAULT: Problem setting trip point.
> +	 */
> +	CMD_THERMAL_SET_TRIP = 2,
> +
> +	/**
> +	 * @brief Get the number of supported thermal zones.
> +	 *
> +	 * No request parameters required.
> +	 *
> +	 * mrq_response::err is always 0, indicating success.
> +	 */
> +	CMD_THERMAL_GET_NUM_ZONES = 3,
> +
> +	/** @brief: number of supported host-to-bpmp commands. May
> +	 * increase in future
> +	 */
> +	CMD_THERMAL_HOST_TO_BPMP_NUM
> +};
> +
> +enum mrq_thermal_bpmp_to_host_cmd {
> +	/**
> +	 * @brief Indication that the temperature for a zone has
> +	 *   exceeded the range indicated in the thermal trip point
> +	 *   for the zone.
> +	 *
> +	 * BPMP needs to supply request parameters. Host only needs to
> +	 * acknowledge.
> +	 */
> +	CMD_THERMAL_HOST_TRIP_REACHED = 100,
> +
> +	/** @brief: number of supported bpmp-to-host commands. May
> +	 * increase in future
> +	 */
> +	CMD_THERMAL_BPMP_TO_HOST_NUM
> +};
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI
> + *
> + * zone: Request type for which to check existence.
> + */
> +struct cmd_thermal_query_abi_request {
> +	uint32_t type;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP
> + *
> + * zone: Number of thermal zone.
> + */
> +struct cmd_thermal_get_temp_request {
> +	uint32_t zone;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP
> + *
> + * error: 0 if request succeeded.
> + *	-BPMP_EINVAL if request parameters were invalid.
> + *      -BPMP_ENOENT if no driver was registered for the specified thermal zone.
> + *      -BPMP_EFAULT for other thermal zone driver errors.
> + * temp: Current temperature in millicelsius.
> + */
> +struct cmd_thermal_get_temp_response {
> +	int32_t temp;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP
> + *
> + * zone: Number of thermal zone.
> + * low: Temperature of lower trip point in millicelsius
> + * high: Temperature of upper trip point in millicelsius
> + * enabled: 1 to enable trip point, 0 to disable trip point
> + */
> +struct cmd_thermal_set_trip_request {
> +	uint32_t zone;
> +	int32_t low;
> +	int32_t high;
> +	uint32_t enabled;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED
> + *
> + * zone: Number of thermal zone where trip point was reached.
> + */
> +struct cmd_thermal_host_trip_reached_request {
> +	uint32_t zone;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES
> + *
> + * num: Number of supported thermal zones. The thermal zones are indexed
> + *      starting from zero.
> + */
> +struct cmd_thermal_get_num_zones_response {
> +	uint32_t num;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data.
> + *
> + * Reply type is union mrq_thermal_bpmp_to_host_response.
> + *
> + * type: Type of request. Values listed in enum mrq_thermal_type.
> + * data: Request type specific parameters.
> + */
> +struct mrq_thermal_host_to_bpmp_request {
> +	uint32_t type;
> +	union {
> +		struct cmd_thermal_query_abi_request query_abi;
> +		struct cmd_thermal_get_temp_request get_temp;
> +		struct cmd_thermal_set_trip_request set_trip;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host request data.
> + *
> + * type: Type of request. Values listed in enum mrq_thermal_type.
> + * data: Request type specific parameters.
> + */
> +struct mrq_thermal_bpmp_to_host_request {
> +	uint32_t type;
> +	union {
> +		struct cmd_thermal_host_trip_reached_request host_trip_reached;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/*
> + * Data in reply to a Host->BPMP request.
> + */
> +union mrq_thermal_bpmp_to_host_response {
> +	struct cmd_thermal_get_temp_response get_temp;
> +	struct cmd_thermal_get_num_zones_response get_num_zones;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_CPU_VHINT
> + * @brief Query CPU voltage hint data
> + *
> + * * Platforms: T186
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_cpu_vhint_request
> + * * Response Payload: N/A
> + *
> + * @addtogroup Vhint CPU Voltage hint
> + * @{
> + */
> +
> +/**
> + * @brief request with #MRQ_CPU_VHINT
> + *
> + * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data
> + * from BPMP to memory space pointed by #addr. CCPLEX is responsible
> + * to allocate sizeof(cpu_vhint_data) sized block of memory and
> + * appropriately map it for BPMP before sending the request.
> + */
> +struct mrq_cpu_vhint_request {
> +	/** @brief IOVA address for the #cpu_vhint_data */
> +	uint32_t addr; /* struct cpu_vhint_data * */
> +	/** @brief ID of the cluster whose data is requested */
> +	uint32_t cluster_id; /* enum cluster_id */
> +} __ABI_PACKED;
> +
> +/**
> + * @brief description of the CPU v/f relation
> + *
> + * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of
> + * struct mrq_cpu_vhint_request
> + */
> +struct cpu_vhint_data {
> +	uint32_t ref_clk_hz; /**< reference frequency in Hz */
> +	uint16_t pdiv; /**< post divider value */
> +	uint16_t mdiv; /**< input divider value */
> +	uint16_t ndiv_max; /**< fMAX expressed with max NDIV value */
> +	/** table of ndiv values as a function of vINDEX (voltage index) */
> +	uint16_t ndiv[80];
> +	/** minimum allowed NDIV value */
> +	uint16_t ndiv_min;
> +	/** minimum allowed voltage hint value (as in vINDEX) */
> +	uint16_t vfloor;
> +	/** maximum allowed voltage hint value (as in vINDEX) */
> +	uint16_t vceil;
> +	/** post-multiplier for vindex value */
> +	uint16_t vindex_mult;
> +	/** post-divider for vindex value */
> +	uint16_t vindex_div;
> +	/** reserved for future use */
> +	uint16_t reserved[328];
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_ABI_RATCHET
> + * @brief ABI ratchet value query
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_abi_ratchet_request
> + * * Response Payload: @ref mrq_abi_ratchet_response
> + * @addtogroup ABI_info
> + * @{
> + */
> +
> +/**
> + * @brief an ABI compatibility mechanism
> + *
> + * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future
> + * revision of this header file.
> + * 1. That future revision deprecates some MRQ
> + * 2. That future revision introduces a breaking change to an existing
> + *    MRQ or
> + * 3. A bug is discovered in an existing implementation of the BPMP-FW
> + *    (or possibly one of its clients) which warrants deprecating that
> + *    implementation.
> + */
> +#define BPMP_ABI_RATCHET_VALUE 3
> +
> +/**
> + * @brief request with #MRQ_ABI_RATCHET.
> + *
> + * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header
> + * against which the requester was compiled.
> + *
> + * If ratchet is less than BPMP's #BPMP_ABI_RATCHET_VALUE, BPMP may
> + * reply with mrq_response::err = -#BPMP_ERANGE to indicate that
> + * BPMP-FW cannot interoperate correctly with the requester. Requester
> + * should cease further communication with BPMP.
> + *
> + * Otherwise, err shall be 0.
> + */
> +struct mrq_abi_ratchet_request {
> +	/** @brief requester's ratchet value */
> +	uint16_t ratchet;
> +};
> +
> +/**
> + * @brief response to #MRQ_ABI_RATCHET
> + *
> + * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header
> + * against which BPMP firwmare was compiled.
> + *
> + * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE,
> + * the requster must either interoperate with BPMP according to an ABI
> + * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease
> + * communication with BPMP.
> + *
> + * If mrq_response::err is 0 and ratchet is greater than or equal to the
> + * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue
> + * normal operation.
> + */
> +struct mrq_abi_ratchet_response {
> +	/** @brief BPMP's ratchet value */
> +	uint16_t ratchet;
> +};
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_EMC_DVFS_LATENCY
> + * @brief query frequency dependent EMC DVFS latency
> + *
> + * * Platforms: T186
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: N/A
> + * * Response Payload: @ref mrq_emc_dvfs_latency_response
> + * @addtogroup EMC
> + * @{
> + */
> +
> +/**
> + * @brief used by @ref mrq_emc_dvfs_latency_response
> + */
> +struct emc_dvfs_latency {
> +	/** @brief EMC frequency in kHz */
> +	uint32_t freq;
> +	/** @brief EMC DVFS latency in nanoseconds */
> +	uint32_t latency;
> +} __ABI_PACKED;
> +
> +#define EMC_DVFS_LATENCY_MAX_SIZE	14
> +/**
> + * @brief response to #MRQ_EMC_DVFS_LATENCY
> + */
> +struct mrq_emc_dvfs_latency_response {
> +	/** @brief the number valid entries in #pairs */
> +	uint32_t num_pairs;
> +	/** @brief EMC <frequency, latency> information */
> +	struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE];
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_TRACE_ITER
> + * @brief manage the trace iterator
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: N/A
> + * * Response Payload: @ref mrq_trace_iter_request
> + * @addtogroup Trace
> + * @{
> + */
> +enum {
> +	/** @brief (re)start the tracing now. Ignore older events */
> +	TRACE_ITER_INIT = 0,
> +	/** @brief clobber all events in the trace buffer */
> +	TRACE_ITER_CLEAN = 1
> +};
> +
> +/**
> + * @brief request with #MRQ_TRACE_ITER
> + */
> +struct mrq_trace_iter_request {
> +	/** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */
> +	uint32_t cmd;
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/*
> + *  4. Enumerations
> + */
> +
> +/*
> + *   4.1 CPU enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + *
> + *   4.2 CPU Cluster enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + *
> + *   4.3 System low power state enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + */
> +
> +/*
> + *   4.4 Clock enumerations
> + *
> + * For clock enumerations, see <mach-t186/clk-t186.h>
> + */
> +
> +/*
> + *   4.5 Reset enumerations
> + *
> + * For reset enumerations, see <mach-t186/reset-t186.h>
> + */
> +
> +/*
> + *   4.6 Thermal sensor enumerations
> + *
> + * For thermal sensor enumerations, see <mach-t186/thermal-t186.h>
> + */
> +
> +/**
> + * @defgroup Error_Codes
> + * Negative values for mrq_response::err generally indicate some
> + * error. The ABI defines the following error codes. Negating these
> + * defines is an exercise left to the user.
> + * @{
> + */
> +/** @brief No such file or directory */
> +#define BPMP_ENOENT	2
> +/** @brief No MRQ handler */
> +#define BPMP_ENOHANDLER	3
> +/** @brief I/O error */
> +#define BPMP_EIO	5
> +/** @brief Bad sub-MRQ command */
> +#define BPMP_EBADCMD	6
> +/** @brief Not enough memory */
> +#define BPMP_ENOMEM	12
> +/** @brief Permission denied */
> +#define BPMP_EACCES	13
> +/** @brief Bad address */
> +#define BPMP_EFAULT	14
> +/** @brief No such device */
> +#define BPMP_ENODEV	19
> +/** @brief Argument is a directory */
> +#define BPMP_EISDIR	21
> +/** @brief Invalid argument */
> +#define BPMP_EINVAL	22
> +/** @brief Timeout during operation */
> +#define BPMP_ETIMEDOUT  23
> +/** @brief Out of range */
> +#define BPMP_ERANGE	34
> +/** @} */
> +/** @} */
> +#endif
> diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
> new file mode 100644
> index 000000000000..e17d091e8a9a
> --- /dev/null
> +++ b/include/soc/tegra/bpmp.h
> @@ -0,0 +1,122 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#ifndef __SOC_TEGRA_BPMP_H
> +#define __SOC_TEGRA_BPMP_H
> +
> +#include <linux/mailbox_client.h>
> +#include <linux/reset-controller.h>
> +#include <linux/semaphore.h>
> +#include <linux/types.h>
> +
> +#define TEGRA_BPMP_MSG_SIZE 128
> +#define TEGRA_BPMP_MSG_DATA_SIZE 120

Why aren't we using the ABI equivalents?

> +
> +struct tegra_bpmp_soc {
> +	struct {
> +		struct {
> +			unsigned int offset;
> +			unsigned int count;
> +			unsigned int timeout;
> +		} cpu_tx, thread, cpu_rx;
> +	} channels;
> +	unsigned int num_resets;
> +};
> +
> +struct tegra_bpmp_mb_data {
> +	u32 code;
> +	u32 flags;
> +	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
> +} __packed;
> +
> +struct tegra_bpmp_channel {
> +	struct tegra_bpmp *bpmp;
> +	struct tegra_bpmp_mb_data *ib;
> +	struct tegra_bpmp_mb_data *ob;
> +	struct completion completion;
> +	struct tegra_ivc *ivc;
> +};
> +
> +typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq,
> +					 struct tegra_bpmp_channel *channel,
> +					 void *data);
> +
> +struct tegra_bpmp_mrq {
> +	struct list_head list;
> +	unsigned int mrq;
> +	tegra_bpmp_mrq_handler_t handler;
> +	void *data;
> +};
> +
> +struct tegra_bpmp {
> +	const struct tegra_bpmp_soc *soc;
> +	struct device *dev;
> +
> +	void __iomem *tx_base;
> +	void __iomem *rx_base;
> +
> +	struct {
> +		struct mbox_client client;
> +		struct mbox_chan *channel;
> +	} mbox;
> +
> +	struct tegra_bpmp_channel *channels;
> +	unsigned int num_channels;
> +
> +	struct {
> +		unsigned long *allocated;
> +		unsigned long *busy;
> +		unsigned int count;
> +		struct semaphore lock;
> +	} threaded;
> +
> +	struct list_head mrqs;
> +	spinlock_t lock;
> +
> +	unsigned int num_clocks;
> +	struct clk_hw **clocks;
> +
> +	struct reset_controller_dev rstc;
> +};
> +
> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp);
> +
> +struct tegra_bpmp_message {
> +	unsigned int mrq;
> +
> +	struct {
> +		const void *data;
> +		size_t size;
> +	} tx;
> +
> +	struct {
> +		void *data;
> +		size_t size;
> +	} rx;
> +};
> +
> +int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       struct tegra_bpmp_message *msg);
> +int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
> +			struct tegra_bpmp_message *msg);
> +
> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data);
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			 void *data);
> +
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);

I believe this will break bisect (because the functions are defined in a
later patch)? But I like the clock/reset driver to refer to bpmp driver
than in the reverse direction.

> +
> +#endif /* __SOC_TEGRA_BPMP_H */
> -- 
> 2.9.0
> 

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

* [PATCH v3 05/12] firmware: tegra: Add BPMP support
@ 2016-08-23 23:26         ` Sivaram Nair
  0 siblings, 0 replies; 104+ messages in thread
From: Sivaram Nair @ 2016-08-23 23:26 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 19, 2016 at 07:32:26PM +0200, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The Boot and Power Management Processor (BPMP) is a co-processor found
> on Tegra SoCs. It is designed to handle the early stages of the boot
> process and offload power management tasks (such as clocks, resets,
> powergates, ...) as well as system control services.
> 
> Compared to the ARM SCPI, the services provided by BPMP are message-
> based rather than method-based. The BPMP firmware driver provides the
> services to transmit data to and receive data from the BPMP. Users can
> also register an MRQ, for which a service routine will be run when a
> corresponding event is received from the firmware.
> 
> A set of messages, called the BPMP ABI, are specified for a number of
> different services provided by the BPMP (such as clocks or resets).
> 
> Based on work by Sivaram Nair <sivaramn@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a message structure for transfers to avoid long function
>   prototypes
> - restructure driver for easier maintainability
> - rename bpmp_abi.h to bpmp-abi.h for consistency
> 
>  drivers/firmware/tegra/Kconfig  |   12 +
>  drivers/firmware/tegra/Makefile |    1 +
>  drivers/firmware/tegra/bpmp.c   |  880 +++++++++++++++++++++
>  include/soc/tegra/bpmp-abi.h    | 1601 +++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/bpmp.h        |  122 +++
>  5 files changed, 2616 insertions(+)
>  create mode 100644 drivers/firmware/tegra/bpmp.c
>  create mode 100644 include/soc/tegra/bpmp-abi.h
>  create mode 100644 include/soc/tegra/bpmp.h
> 
> diff --git a/drivers/firmware/tegra/Kconfig b/drivers/firmware/tegra/Kconfig
> index 1fa3e4e136a5..ff2730d5c468 100644
> --- a/drivers/firmware/tegra/Kconfig
> +++ b/drivers/firmware/tegra/Kconfig
> @@ -10,4 +10,16 @@ config TEGRA_IVC
>  	  keeps the content is synchronization between host CPU and remote
>  	  processors.
>  
> +config TEGRA_BPMP
> +	bool "Tegra BPMP driver"
> +	depends on ARCH_TEGRA && TEGRA_HSP_MBOX && TEGRA_IVC
> +	help
> +	  BPMP (Boot and Power Management Processor) is designed to off-loading
> +	  the PM functions which include clock/DVFS/thermal/power from the CPU.
> +	  It needs HSP as the HW synchronization and notification module and
> +	  IVC module as the message communication protocol.
> +
> +	  This driver manages the IPC interface between host CPU and the
> +	  firmware running on BPMP.
> +
>  endmenu
> diff --git a/drivers/firmware/tegra/Makefile b/drivers/firmware/tegra/Makefile
> index 92e2153e8173..e34a2f79e1ad 100644
> --- a/drivers/firmware/tegra/Makefile
> +++ b/drivers/firmware/tegra/Makefile
> @@ -1 +1,2 @@
> +obj-$(CONFIG_TEGRA_BPMP)	+= bpmp.o
>  obj-$(CONFIG_TEGRA_IVC)		+= ivc.o
> diff --git a/drivers/firmware/tegra/bpmp.c b/drivers/firmware/tegra/bpmp.c
> new file mode 100644
> index 000000000000..a09043b1be05
> --- /dev/null
> +++ b/drivers/firmware/tegra/bpmp.c
> @@ -0,0 +1,880 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#define DEBUG
> +
> +#include <linux/clk/tegra.h>
> +#include <linux/mailbox_client.h>
> +#include <linux/of.h>
> +#include <linux/of_address.h>
> +#include <linux/of_device.h>
> +#include <linux/platform_device.h>
> +#include <linux/semaphore.h>
> +
> +#include <soc/tegra/bpmp.h>
> +#include <soc/tegra/bpmp-abi.h>
> +#include <soc/tegra/ivc.h>
> +
> +#define MSG_ACK		BIT(0)
> +#define MSG_RING	BIT(1)
> +
> +static inline struct tegra_bpmp *
> +mbox_client_to_bpmp(struct mbox_client *client)
> +{
> +	return container_of(client, struct tegra_bpmp, mbox.client);
> +}
> +
> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev)
> +{
> +	struct platform_device *pdev;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +
> +	np = of_parse_phandle(dev->of_node, "nvidia,bpmp", 0);
> +	if (!np)
> +		return ERR_PTR(-ENOENT);
> +
> +	pdev = of_find_device_by_node(np);
> +	if (!pdev) {
> +		bpmp = ERR_PTR(-ENODEV);
> +		goto put;
> +	}
> +
> +	bpmp = platform_get_drvdata(pdev);
> +	if (!bpmp) {
> +		bpmp = ERR_PTR(-EPROBE_DEFER);
> +		put_device(&pdev->dev);
> +		goto put;
> +	}
> +
> +put:
> +	of_node_put(np);
> +	return bpmp;
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_get);
> +
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp)
> +{
> +	if (bpmp)
> +		put_device(bpmp->dev);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_put);
> +
> +static int tegra_bpmp_channel_get_index(struct tegra_bpmp_channel *channel)
> +{
> +	return channel - channel->bpmp->channels;
> +}
> +
> +static int
> +tegra_bpmp_channel_get_thread_index(struct tegra_bpmp_channel *channel)
> +{
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	unsigned int offset, count;
> +	int index;
> +
> +	offset = bpmp->soc->channels.thread.offset;
> +	count = bpmp->soc->channels.thread.count;
> +
> +	index = tegra_bpmp_channel_get_index(channel);
> +	if (index < 0)
> +		return index;
> +
> +	if (index < offset || index >= offset + count)
> +		return -EINVAL;
> +
> +	return index - offset;
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_thread(struct tegra_bpmp *bpmp, unsigned int index)
> +{
> +	unsigned int offset = bpmp->soc->channels.thread.offset;
> +	unsigned int count = bpmp->soc->channels.thread.count;
> +
> +	if (index >= count)
> +		return NULL;
> +
> +	return &bpmp->channels[offset + index];
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_tx(struct tegra_bpmp *bpmp)
> +{
> +	unsigned int offset = bpmp->soc->channels.cpu_tx.offset;
> +
> +	return &bpmp->channels[offset + smp_processor_id()];
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_channel_get_rx(struct tegra_bpmp *bpmp)
> +{
> +	unsigned int offset = bpmp->soc->channels.cpu_rx.offset;
> +
> +	return &bpmp->channels[offset];
> +}
> +
> +static bool tegra_bpmp_message_valid(const struct tegra_bpmp_message *msg)
> +{
> +	return (msg->tx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
> +	       (msg->rx.size <= TEGRA_BPMP_MSG_DATA_SIZE) &&
> +	       (msg->tx.size == 0 || msg->tx.data) &&
> +	       (msg->rx.size == 0 || msg->rx.data);
> +}
> +
> +static bool tegra_bpmp_master_acked(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_read_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ib = NULL;
> +		return false;
> +	}
> +
> +	channel->ib = frame;
> +
> +	return true;
> +}
> +
> +static int tegra_bpmp_wait_ack(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_acked(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static bool tegra_bpmp_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	void *frame;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (IS_ERR_OR_NULL(frame)) {
> +		channel->ob = NULL;
> +		return false;
> +	}
> +
> +	channel->ob = frame;
> +
> +	return true;
> +}
> +
> +static int tegra_bpmp_wait_master_free(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long timeout = channel->bpmp->soc->channels.cpu_tx.timeout;
> +	ktime_t start, now;
> +
> +	start = ns_to_ktime(local_clock());
> +
> +	do {
> +		if (tegra_bpmp_master_free(channel))
> +			return 0;
> +
> +		now = ns_to_ktime(local_clock());
> +	} while (ktime_us_delta(now, start) < timeout);
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static ssize_t __tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
> +					 void *data, size_t size)
> +{
> +	if (data && size > 0)
> +		memcpy_fromio(data, channel->ib->data, size);
> +
> +	return tegra_ivc_read_advance(channel->ivc);
> +}
> +
> +static ssize_t tegra_bpmp_channel_read(struct tegra_bpmp_channel *channel,
> +				       void *data, size_t size)
> +{
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	unsigned long flags;
> +	ssize_t err;
> +	int index;
> +
> +	index = tegra_bpmp_channel_get_thread_index(channel);
> +	if (index < 0)
> +		return index;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +	err = __tegra_bpmp_channel_read(channel, data, size);
> +	clear_bit(index, bpmp->threaded.allocated);
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +
> +	up(&bpmp->threaded.lock);
> +
> +	return err;
> +}
> +
> +static ssize_t __tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
> +					  unsigned int mrq, unsigned long flags,
> +					  const void *data, size_t size)
> +{
> +	channel->ob->code = mrq;
> +	channel->ob->flags = flags;
> +
> +	if (data && size > 0)
> +		memcpy_toio(channel->ob->data, data, size);
> +
> +	return tegra_ivc_write_advance(channel->ivc);
> +}
> +
> +static struct tegra_bpmp_channel *
> +tegra_bpmp_write_threaded(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			  const void *data, size_t size)
> +{
> +	unsigned long timeout = bpmp->soc->channels.thread.timeout;
> +	unsigned int count = bpmp->soc->channels.thread.count;
> +	struct tegra_bpmp_channel *channel;
> +	unsigned long flags;
> +	unsigned int index;
> +	int err;
> +
> +	err = down_timeout(&bpmp->threaded.lock, usecs_to_jiffies(timeout));
> +	if (err < 0)
> +		return ERR_PTR(err);
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	index = find_first_zero_bit(bpmp->threaded.allocated, count);
> +	if (index == count) {
> +		channel = ERR_PTR(-EBUSY);
> +		goto unlock;
> +	}
> +
> +	channel = tegra_bpmp_channel_get_thread(bpmp, index);
> +	if (!channel) {
> +		channel = ERR_PTR(-EINVAL);
> +		goto unlock;
> +	}
> +
> +	if (!tegra_bpmp_master_free(channel)) {
> +		channel = ERR_PTR(-EBUSY);
> +		goto unlock;
> +	}
> +
> +	set_bit(index, bpmp->threaded.allocated);
> +
> +	err = __tegra_bpmp_channel_write(channel, mrq, MSG_ACK | MSG_RING,
> +					 data, size);
> +	if (err < 0) {
> +		clear_bit(index, bpmp->threaded.allocated);
> +		goto unlock;
> +	}
> +
> +	set_bit(index, bpmp->threaded.busy);
> +
> +unlock:
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +	return channel;
> +}
> +
> +static ssize_t tegra_bpmp_channel_write(struct tegra_bpmp_channel *channel,
> +					unsigned int mrq, unsigned long flags,
> +					const void *data, size_t size)
> +{
> +	int err;
> +
> +	err = tegra_bpmp_wait_master_free(channel);
> +	if (err < 0)
> +		return err;
> +
> +	return __tegra_bpmp_channel_write(channel, mrq, flags, data, size);
> +}
> +
> +int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       struct tegra_bpmp_message *msg)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	int err;
> +
> +	if (WARN_ON(!irqs_disabled()))
> +		return -EPERM;
> +
> +	if (!tegra_bpmp_message_valid(msg))
> +		return -EINVAL;
> +
> +	channel = tegra_bpmp_channel_get_tx(bpmp);
> +
> +	err = tegra_bpmp_channel_write(channel, msg->mrq, MSG_ACK,
> +				       msg->tx.data, msg->tx.size);
> +	if (err < 0)
> +		return err;
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +
> +	err = tegra_bpmp_wait_ack(channel);
> +	if (err)
> +		return err;
> +
> +	return __tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_transfer_atomic);
> +
> +int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
> +			struct tegra_bpmp_message *msg)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	unsigned long timeout;
> +	int err;
> +
> +	if (WARN_ON(irqs_disabled()))
> +		return -EPERM;
> +
> +	if (!tegra_bpmp_message_valid(msg))
> +		return -EINVAL;
> +
> +	channel = tegra_bpmp_write_threaded(bpmp, msg->mrq, msg->tx.data,
> +					    msg->tx.size);
> +	if (IS_ERR(channel))
> +		return PTR_ERR(channel);
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return err;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +
> +	timeout = usecs_to_jiffies(bpmp->soc->channels.thread.timeout);
> +
> +	err = wait_for_completion_timeout(&channel->completion, timeout);
> +	if (err <= 0) {

wait_for_completion_timeout() returns unsigned long - why are we
checking for < 0? The documentation says that zero indicates timeout.

> +		if (err == 0)
> +			err = -ETIMEDOUT;
> +
> +		return err;
> +	}
> +
> +	return tegra_bpmp_channel_read(channel, msg->rx.data, msg->rx.size);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_transfer);
> +
> +static struct tegra_bpmp_mrq *tegra_bpmp_find_mrq(struct tegra_bpmp *bpmp,
> +						  unsigned int mrq)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +
> +	list_for_each_entry(entry, &bpmp->mrqs, list)
> +		if (entry->mrq == mrq)
> +			return entry;
> +
> +	return NULL;
> +}
> +
> +static void tegra_bpmp_mrq_return(struct tegra_bpmp_channel *channel,
> +				  int code, const void *data, size_t size)
> +{
> +	unsigned long flags = channel->ib->flags;
> +	struct tegra_bpmp *bpmp = channel->bpmp;
> +	struct tegra_bpmp_mb_data *frame;
> +	int err;
> +
> +	if (WARN_ON(size > TEGRA_BPMP_MSG_DATA_SIZE))
> +		return;
> +
> +	err = tegra_ivc_read_advance(channel->ivc);
> +	if (WARN_ON(err < 0))
> +		return;
> +
> +	if ((flags & MSG_ACK) == 0)
> +		return;
> +
> +	frame = tegra_ivc_write_get_next_frame(channel->ivc);
> +	if (WARN_ON(IS_ERR_OR_NULL(frame)))
> +		return;
> +
> +	frame->code = code;
> +
> +	if (data && size > 0)
> +		memcpy_toio(frame->data, data, size);
> +
> +	err = tegra_ivc_write_advance(channel->ivc);
> +	if (WARN_ON(err < 0))
> +		return;
> +
> +	if (flags & MSG_RING) {
> +		err = mbox_send_message(bpmp->mbox.channel, NULL);
> +		if (WARN_ON(err < 0))
> +			return;
> +
> +		mbox_client_txdone(bpmp->mbox.channel, 0);
> +	}
> +}
> +
> +static void tegra_bpmp_handle_mrq(struct tegra_bpmp *bpmp,
> +				  unsigned int mrq,
> +				  struct tegra_bpmp_channel *channel)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	u32 zero = 0;
> +
> +	spin_lock(&bpmp->lock);
> +
> +	entry = tegra_bpmp_find_mrq(bpmp, mrq);
> +	if (!entry) {
> +		spin_unlock(&bpmp->lock);
> +		tegra_bpmp_mrq_return(channel, -EINVAL, &zero, sizeof(zero));
> +		return;
> +	}
> +
> +	entry->handler(mrq, channel, entry->data);
> +
> +	spin_unlock(&bpmp->lock);
> +}
> +
> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	unsigned long flags;
> +
> +	if (!handler)
> +		return -EINVAL;
> +
> +	entry = devm_kzalloc(bpmp->dev, sizeof(*entry), GFP_KERNEL);
> +	if (!entry)
> +		return -ENOMEM;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	entry->mrq = mrq;
> +	entry->handler = handler;
> +	entry->data = data;
> +	list_add(&entry->list, &bpmp->mrqs);
> +
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_request_mrq);
> +
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq, void *data)
> +{
> +	struct tegra_bpmp_mrq *entry;
> +	unsigned long flags;
> +
> +	spin_lock_irqsave(&bpmp->lock, flags);
> +
> +	entry = tegra_bpmp_find_mrq(bpmp, mrq);
> +	if (!entry)
> +		goto unlock;
> +
> +	list_del(&entry->list);
> +	devm_kfree(bpmp->dev, entry);
> +
> +unlock:
> +	spin_unlock_irqrestore(&bpmp->lock, flags);
> +}
> +EXPORT_SYMBOL_GPL(tegra_bpmp_free_mrq);
> +
> +static void tegra_bpmp_mrq_handle_ping(unsigned int mrq,
> +				       struct tegra_bpmp_channel *channel,
> +				       void *data)
> +{
> +	struct mrq_ping_request *request;
> +	struct mrq_ping_response response;
> +
> +	request = (struct mrq_ping_request *)channel->ib->data;
> +
> +	memset(&response, 0, sizeof(response));

Why memset() - here and other places - ? Are you preparing for a
future/unforseen change to the ABI structures? If yes, I think we can ignore
that case - the existing structures should never change.

> +	response.reply = request->challenge << (smp_processor_id() + 1);

This does not follow the spec. reply should be challenge << 1
(downstream implementation seems wrong).

> +
> +	tegra_bpmp_mrq_return(channel, 0, &response, sizeof(response));
> +}
> +
> +static int tegra_bpmp_ping(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_ping_response response;
> +	struct mrq_ping_request request;
> +	struct tegra_bpmp_message msg;
> +	ktime_t start, delta;
> +	unsigned long flags;
> +	int err;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.challenge = 1;
> +
> +	memset(&response, 0, sizeof(response));
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_PING;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +	msg.rx.data = &response;
> +	msg.rx.size = sizeof(response);
> +
> +	start = ktime_get();

The ktime_get()s should be within the local_irq_save/restore() - so we
get a better measure of the round trip delay?

> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);
> +
> +	delta = ktime_sub(ktime_get(), start);
> +
> +	if (!err)
> +		dev_info(bpmp->dev,
> +			 "ping ok: challenge: %u, response: %u, time: %lld\n",
> +			 request.challenge, response.reply,
> +			 ktime_to_us(delta));
> +
> +	return err;
> +}
> +
> +static int tegra_bpmp_get_firmware_tag(struct tegra_bpmp *bpmp, char *tag,
> +				       size_t size)
> +{
> +	struct mrq_query_tag_request request;
> +	struct tegra_bpmp_message msg;
> +	unsigned long flags;
> +	dma_addr_t phys;
> +	void *virt;
> +	int err;
> +
> +	virt = dma_alloc_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, &phys,
> +				  GFP_KERNEL | GFP_DMA32);
> +	if (!virt)
> +		return -ENOMEM;
> +
> +	memset(&request, 0, sizeof(request));
> +	request.addr = phys;
> +
> +	memset(&msg, 0, sizeof(msg));
> +	msg.mrq = MRQ_QUERY_TAG;
> +	msg.tx.data = &request;
> +	msg.tx.size = sizeof(request);
> +
> +	local_irq_save(flags);
> +	err = tegra_bpmp_transfer_atomic(bpmp, &msg);
> +	local_irq_restore(flags);

I believe the local_irq_save/restore can be dropped - they are not
adding any value.

> +
> +	if (err == 0)
> +		strlcpy(tag, virt, size);
> +
> +	dma_free_coherent(bpmp->dev, TEGRA_BPMP_MSG_DATA_SIZE, virt, phys);
> +
> +	return err;
> +}
> +
> +static void tegra_bpmp_channel_signal(struct tegra_bpmp_channel *channel)
> +{
> +	unsigned long flags = channel->ob->flags;
> +
> +	if ((flags & MSG_RING) == 0)
> +		return;
> +
> +	complete(&channel->completion);
> +}
> +
> +static void tegra_bpmp_handle_rx(struct mbox_client *client, void *data)
> +{
> +	struct tegra_bpmp *bpmp = mbox_client_to_bpmp(client);
> +	struct tegra_bpmp_channel *channel;
> +	unsigned int i, count;
> +	unsigned long *busy;
> +
> +	channel = tegra_bpmp_channel_get_rx(bpmp);
> +	count = bpmp->soc->channels.thread.count;
> +	busy = bpmp->threaded.busy;
> +
> +	if (tegra_bpmp_master_acked(channel))
> +		tegra_bpmp_handle_mrq(bpmp, channel->ib->code, channel);
> +
> +	spin_lock(&bpmp->lock);
> +
> +	for_each_set_bit(i, busy, count) {
> +		struct tegra_bpmp_channel *channel;
> +
> +		channel = tegra_bpmp_channel_get_thread(bpmp, i);
> +		if (!channel)
> +			continue;
> +
> +		if (tegra_bpmp_master_acked(channel)) {
> +			tegra_bpmp_channel_signal(channel);
> +			clear_bit(i, busy);
> +		}
> +	}
> +
> +	spin_unlock(&bpmp->lock);
> +}
> +
> +static void tegra_bpmp_ivc_notify(struct tegra_ivc *ivc, void *data)
> +{
> +	struct tegra_bpmp *bpmp = data;
> +	int err;
> +
> +	err = mbox_send_message(bpmp->mbox.channel, NULL);
> +	if (err < 0)
> +		return;
> +
> +	mbox_client_txdone(bpmp->mbox.channel, 0);
> +}
> +
> +static int tegra_bpmp_channel_init(struct tegra_bpmp_channel *channel,
> +				   struct tegra_bpmp *bpmp,
> +				   unsigned int index)
> +{
> +	size_t message, queue;
> +	void __iomem *rx_base;
> +	void __iomem *tx_base;
> +	int err;
> +
> +	channel->ivc = devm_kzalloc(bpmp->dev, sizeof(*channel->ivc),
> +				    GFP_KERNEL);
> +	if (!channel->ivc)
> +		return -ENOMEM;
> +
> +	message = tegra_ivc_align(TEGRA_BPMP_MSG_SIZE);
> +	queue = tegra_ivc_total_queue_size(message);
> +
> +	rx_base = bpmp->rx_base + queue * index;
> +	tx_base = bpmp->tx_base + queue * index;
> +
> +	err = tegra_ivc_init(channel->ivc, bpmp->dev, rx_base, DMA_ERROR_CODE,
> +			     tx_base, DMA_ERROR_CODE, 1, message,
> +			     tegra_bpmp_ivc_notify, bpmp);
> +	if (err < 0) {
> +		dev_err(bpmp->dev, "failed to setup IVC for channel %u: %d\n",
> +			index, err);
> +		return err;
> +	}
> +
> +	/* reset the channel state */
> +	tegra_ivc_reset(channel->ivc);
> +
> +	/* sync the channel state with BPMP */
> +	while (tegra_ivc_notified(channel->ivc))
> +		;
> +
> +	init_completion(&channel->completion);
> +	channel->bpmp = bpmp;
> +
> +	return 0;
> +}
> +
> +static int tegra_bpmp_init_powergates(struct tegra_bpmp *bpmp)
> +{
> +	struct mrq_pg_read_state_response response;
> +	struct mrq_pg_read_state_request request;
> +	struct tegra_bpmp_message msg;
> +	unsigned int i;
> +	int err;
> +
> +	dev_dbg(bpmp->dev, "powergates:\n");
> +
> +	for (i = 0; i < 32; i++) {
> +		memset(&request, 0, sizeof(request));
> +		request.partition_id = i;
> +
> +		memset(&response, 0, sizeof(response));
> +
> +		memset(&msg, 0, sizeof(msg));
> +		msg.mrq = MRQ_PG_READ_STATE;
> +		msg.tx.data = &request;
> +		msg.tx.size = sizeof(request);
> +		msg.rx.data = &response;
> +		msg.rx.size = sizeof(response);
> +
> +		err = tegra_bpmp_transfer(bpmp, &msg);
> +		if (err < 0) {
> +			dev_err(bpmp->dev, "failed to transfer message: %d\n", err);
> +			continue;
> +		}
> +
> +		dev_dbg(bpmp->dev, "  %u: %x (%x)\n", i, response.logic_state,
> +			response.sram_state);
> +	}
> +
> +	return 0;
> +}

IMO, this should not be here (in the firmware interface driver). We
should keep the bpmp driver and its users de-coupled. 

> +
> +static int tegra_bpmp_probe(struct platform_device *pdev)
> +{
> +	struct tegra_bpmp_channel *channel;
> +	struct tegra_bpmp *bpmp;
> +	struct device_node *np;
> +	struct resource res;
> +	unsigned int i;
> +	char tag[32];
> +	size_t size;
> +	int err;
> +
> +	bpmp = devm_kzalloc(&pdev->dev, sizeof(*bpmp), GFP_KERNEL);
> +	if (!bpmp)
> +		return -ENOMEM;
> +
> +	bpmp->soc = of_device_get_match_data(&pdev->dev);
> +	bpmp->dev = &pdev->dev;
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 0);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->tx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->tx_base))
> +		return PTR_ERR(bpmp->tx_base);
> +
> +	np = of_parse_phandle(pdev->dev.of_node, "shmem", 1);
> +	if (!np)
> +		return -ENOENT;
> +
> +	of_address_to_resource(np, 0, &res);
> +	of_node_put(np);
> +
> +	bpmp->rx_base = devm_ioremap_resource(&pdev->dev, &res);
> +	if (IS_ERR(bpmp->rx_base))
> +		return PTR_ERR(bpmp->rx_base);
> +
> +	bpmp->num_channels = bpmp->soc->channels.cpu_tx.count +
> +			     bpmp->soc->channels.thread.count +
> +			     bpmp->soc->channels.cpu_rx.count;
> +
> +	bpmp->channels = devm_kcalloc(&pdev->dev, bpmp->num_channels,
> +				      sizeof(*channel), GFP_KERNEL);
> +	if (!bpmp->channels)
> +		return -ENOMEM;
> +
> +	/* mbox registration */
> +	bpmp->mbox.client.dev = &pdev->dev;
> +	bpmp->mbox.client.rx_callback = tegra_bpmp_handle_rx;
> +	bpmp->mbox.client.tx_block = false;
> +	bpmp->mbox.client.knows_txdone = false;
> +
> +	bpmp->mbox.channel = mbox_request_channel(&bpmp->mbox.client, 0);
> +	if (IS_ERR(bpmp->mbox.channel)) {
> +		err = PTR_ERR(bpmp->mbox.channel);
> +		dev_err(&pdev->dev, "failed to get HSP mailbox: %d\n", err);
> +		return err;
> +	}
> +
> +	/* message channel initialization */
> +	for (i = 0; i < bpmp->num_channels; i++) {
> +		struct tegra_bpmp_channel *channel = &bpmp->channels[i];
> +
> +		err = tegra_bpmp_channel_init(channel, bpmp, i);
> +		if (err)
> +			return err;
> +	}
> +
> +	bpmp->threaded.count = bpmp->soc->channels.thread.count;
> +	size = BITS_TO_LONGS(bpmp->threaded.count) * sizeof(long);
> +
> +	bpmp->threaded.allocated = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!bpmp->threaded.allocated)
> +		return -ENOMEM;
> +
> +	bpmp->threaded.busy = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
> +	if (!bpmp->threaded.busy)
> +		return -ENOMEM;
> +
> +	sema_init(&bpmp->threaded.lock, bpmp->threaded.count);
> +
> +	INIT_LIST_HEAD(&bpmp->mrqs);
> +	spin_lock_init(&bpmp->lock);
> +
> +	err = tegra_bpmp_request_mrq(bpmp, MRQ_PING,
> +				     tegra_bpmp_mrq_handle_ping, bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_ping(bpmp);
> +	if (err < 0) {
> +		dev_err(&pdev->dev, "failed to ping BPMP: %d\n", err);
> +		goto free_mrq;
> +	}
> +
> +	err = tegra_bpmp_get_firmware_tag(bpmp, tag, sizeof(tag) - 1);
> +	if (err) {
> +		dev_err(&pdev->dev, "failed to get firmware tag: %d\n", err);
> +		goto free_mrq;
> +	}
> +
> +	dev_info(&pdev->dev, "firmware: %s\n", tag);
> +
> +	err = tegra_bpmp_init_clocks(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_init_resets(bpmp);
> +	if (err < 0)
> +		return err;
> +
> +	err = tegra_bpmp_init_powergates(bpmp);
> +	if (err < 0)
> +		return err;

Given a choice, I would want to keep this out of the bpmp driver
altogether.

> +
> +	platform_set_drvdata(pdev, bpmp);
> +
> +	return 0;
> +
> +free_mrq:
> +	tegra_bpmp_free_mrq(bpmp, MRQ_PING, bpmp);
> +
> +	return err;
> +}
> +
> +static const struct tegra_bpmp_soc tegra186_soc = {
> +	.channels = {
> +		.cpu_tx = {
> +			.offset = 0,
> +			.count = 6,
> +			.timeout = 60 * USEC_PER_SEC,
> +		},
> +		.thread = {
> +			.offset = 6,
> +			.count = 7,
> +			.timeout = 600 * USEC_PER_SEC,
> +		},
> +		.cpu_rx = {
> +			.offset = 13,
> +			.count = 1,
> +			.timeout = 0,
> +		},
> +	},
> +	.num_resets = 193,
> +};
> +
> +static const struct of_device_id tegra_bpmp_match[] = {
> +	{ .compatible = "nvidia,tegra186-bpmp", .data = &tegra186_soc },
> +	{ }
> +};
> +
> +static struct platform_driver tegra_bpmp_driver = {
> +	.driver = {
> +		.name = "tegra-bpmp",
> +		.of_match_table = tegra_bpmp_match,
> +	},
> +	.probe = tegra_bpmp_probe,
> +};
> +
> +static int __init tegra_bpmp_init(void)
> +{
> +	return platform_driver_register(&tegra_bpmp_driver);
> +}
> +core_initcall(tegra_bpmp_init);
> diff --git a/include/soc/tegra/bpmp-abi.h b/include/soc/tegra/bpmp-abi.h
> new file mode 100644
> index 000000000000..0aaef5960e29
> --- /dev/null
> +++ b/include/soc/tegra/bpmp-abi.h
> @@ -0,0 +1,1601 @@
> +/*
> + * Copyright (c) 2014-2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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 _ABI_BPMP_ABI_H_
> +#define _ABI_BPMP_ABI_H_
> +
> +#ifdef LK
> +#include <stdint.h>
> +#endif
> +
> +#ifndef __ABI_PACKED
> +#define __ABI_PACKED __attribute__((packed))
> +#endif
> +
> +#ifdef NO_GCC_EXTENSIONS
> +#define EMPTY char empty;
> +#define EMPTY_ARRAY 1
> +#else
> +#define EMPTY
> +#define EMPTY_ARRAY 0
> +#endif
> +
> +#ifndef __UNION_ANON
> +#define __UNION_ANON
> +#endif
> +/**
> + * @file
> + */
> +
> +
> +/**
> + * @defgroup MRQ MRQ Messages
> + * @brief Messages sent to/from BPMP via IPC
> + * @{
> + *   @defgroup MRQ_Format Message Format
> + *   @defgroup MRQ_Codes Message Request (MRQ) Codes
> + *   @defgroup MRQ_Payloads Message Payloads
> + *   @defgroup Error_Codes Error Codes
> + * @}
> + */
> +
> +/**
> + * @addtogroup MRQ_Format Message Format
> + * @{
> + * The CPU requests the BPMP to perform a particular service by
> + * sending it an IVC frame containing a single MRQ message. An MRQ
> + * message consists of a @ref mrq_request followed by a payload whose
> + * format depends on mrq_request::mrq.
> + *
> + * The BPMP processes the data and replies with an IVC frame (on the
> + * same IVC channel) containing and MRQ response. An MRQ response
> + * consists of a @ref mrq_response followed by a payload whose format
> + * depends on the associated mrq_request::mrq.
> + *
> + * A well-defined subset of the MRQ messages that the CPU sends to the
> + * BPMP can lead to BPMP eventually sending an MRQ message to the
> + * CPU. For example, when the CPU uses an #MRQ_THERMAL message to set
> + * a thermal trip point, the BPMP may eventually send a single
> + * #MRQ_THERMAL message of its own to the CPU indicating that the trip
> + * point has been crossed.
> + * @}
> + */
> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ message
> + *
> + * Provides the MRQ number for the MRQ message: #mrq. The remainder of
> + * the MRQ message is a payload (immediately following the
> + * mrq_request) whose format depends on mrq.
> + *
> + * @todo document the flags
> + */
> +struct mrq_request {
> +	/** @brief MRQ number of the request */
> +	uint32_t mrq;
> +	/** @brief flags for the request */
> +	uint32_t flags;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Format
> + * @brief header for an MRQ response
> + *
> + *  Provides an error code for the associated MRQ message. The
> + *  remainder of the MRQ response is a payload (immediately following
> + *  the mrq_response) whose format depends on the associated
> + *  mrq_request::mrq
> + *
> + * @todo document the flags
> + */
> +struct mrq_response {
> +	/** @brief error code for the MRQ request itself */
> +	int32_t err;
> +	/** @brief flags for the response */
> +	uint32_t flags;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Format
> + * Minimum needed size for an IPC message buffer
> + */
> +#define MSG_MIN_SZ	128
> +/**
> + * @ingroup MRQ_Format
> + *  Minimum size guaranteed for data in an IPC message buffer
> + */
> +#define MSG_DATA_MIN_SZ	120
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @name Legal MRQ codes
> + * These are the legal values for mrq_request::mrq
> + * @{
> + */
> +
> +#define MRQ_PING		0
> +#define MRQ_QUERY_TAG		1
> +#define MRQ_MODULE_LOAD		4
> +#define MRQ_MODULE_UNLOAD	5
> +#define MRQ_TRACE_MODIFY	7
> +#define MRQ_WRITE_TRACE		8
> +#define MRQ_THREADED_PING	9
> +#define MRQ_MODULE_MAIL		11
> +#define MRQ_DEBUGFS		19
> +#define MRQ_RESET		20
> +#define MRQ_I2C			21
> +#define MRQ_CLK			22
> +#define MRQ_QUERY_ABI		23
> +#define MRQ_PG_READ_STATE	25
> +#define MRQ_PG_UPDATE_STATE	26
> +#define MRQ_THERMAL		27
> +#define MRQ_CPU_VHINT		28
> +#define MRQ_ABI_RATCHET		29
> +#define MRQ_EMC_DVFS_LATENCY	31
> +#define MRQ_TRACE_ITER		64
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @brief Maximum MRQ code to be sent by CPU software to
> + * BPMP. Subject to change in future
> + */
> +#define MAX_CPU_MRQ_ID		64
> +
> +/**
> + * @addtogroup MRQ_Payloads Message Payloads
> + * @{
> + *   @defgroup Ping
> + *   @defgroup Query_Tag Query Tag
> + *   @defgroup Module Loadable Modules
> + *   @defgroup Trace
> + *   @defgroup Debugfs
> + *   @defgroup Reset
> + *   @defgroup I2C
> + *   @defgroup Clocks
> + *   @defgroup ABI_info ABI Info
> + *   @defgroup MC_Flush MC Flush
> + *   @defgroup Powergating
> + *   @defgroup Thermal
> + *   @defgroup Vhint CPU Voltage hint
> + *   @defgroup MRQ_Deprecated Deprecated MRQ messages
> + *   @defgroup EMC
> + * @}
> + */
> +
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PING
> + * @brief A simple ping
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: @ref mrq_ping_request
> + * * Response Payload: @ref mrq_ping_response
> + *
> + * @ingroup MRQ_Codes
> + * @def MRQ_THREADED_PING
> + * @brief A deeper ping
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_ping_request
> + * * Response Payload: @ref mrq_ping_response
> + *
> + * Behavior is equivalent to a simple #MRQ_PING except that BPMP
> + * responds from a thread context (providing a slightly more robust
> + * sign of life).
> + *
> + */
> +
> +/**
> + * @ingroup Ping
> + * @brief request with #MRQ_PING
> + *
> + * Used by the sender of an #MRQ_PING message to request a pong from
> + * recipient. The response from the recipient is computed based on
> + * #challenge.
> + */
> +struct mrq_ping_request {
> +/** @brief arbitrarily chosen value */
> +	uint32_t challenge;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Ping
> + * @brief response to #MRQ_PING
> + *
> + * Sent in response to an #MRQ_PING message. #reply should be the
> + * mrq_ping_request challenge left shifted by 1 with the carry-bit
> + * dropped.
> + *
> + */
> +struct mrq_ping_response {
> +	/** @brief response to the MRQ_PING challege */
> +	uint32_t reply;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_QUERY_TAG
> + * @brief Query BPMP firmware's tag (i.e. version information)
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_query_tag_request
> + * * Response Payload: N/A
> + *
> + */
> +
> +/**
> + * @ingroup Query_Tag
> + * @brief request with #MRQ_QUERY_TAG
> + *
> + * Used by #MRQ_QUERY_TAG call to ask BPMP to fill in the memory
> + * pointed by #addr with BPMP firmware header.
> + *
> + * The sender is reponsible for ensuring that #addr is mapped in to
> + * the recipient's address map.
> + */
> +struct mrq_query_tag_request {
> +  /** @brief base address to store the firmware header */
> +	uint32_t addr;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_LOAD
> + * @brief dynamically load a BPMP code module
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_load_request
> + * * Response Payload: @ref mrq_module_load_response
> + *
> + * @note This MRQ is disabled on production systems
> + *
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_LOAD
> + *
> + * Used by #MRQ_MODULE_LOAD calls to ask the recipient to dynamically
> + * load the code located at #phys_addr and having size #size
> + * bytes. #phys_addr is treated as a void pointer.
> + *
> + * The recipient copies the code from #phys_addr to locally allocated
> + * memory prior to responding to this message.
> + *
> + * @todo document the module header format
> + *
> + * The sender is responsible for ensuring that the code is mapped in
> + * the recipient's address map.
> + *
> + */
> +struct mrq_module_load_request {
> +	/** @brief base address of the code to load. Treated as (void *) */
> +	uint32_t phys_addr; /* (void *) */
> +	/** @brief size in bytes of code to load */
> +	uint32_t size;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Module
> + * @brief response to #MRQ_MODULE_LOAD
> + *
> + * @todo document mrq_response::err
> + */
> +struct mrq_module_load_response {
> +	/** @brief handle to the loaded module */
> +	uint32_t base;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_UNLOAD
> + * @brief unload a previously loaded code module
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_unload_request
> + * * Response Payload: N/A
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_UNLOAD
> + *
> + * Used by #MRQ_MODULE_UNLOAD calls to request that a previously loaded
> + * module be unloaded.
> + */
> +struct mrq_module_unload_request {
> +	/** @brief handle of the module to unload */
> +	uint32_t base;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_TRACE_MODIFY
> + * @brief modify the set of enabled trace events
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_trace_modify_request
> + * * Response Payload: @ref mrq_trace_modify_response
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Trace
> + * @brief request with #MRQ_TRACE_MODIFY
> + *
> + * Used by %MRQ_TRACE_MODIFY calls to enable or disable specify trace
> + * events.  #set takes precedence for any bit set in both #set and
> + * #clr.
> + */
> +struct mrq_trace_modify_request {
> +	/** @brief bit mask of trace events to disable */
> +	uint32_t clr;
> +	/** @brief bit mask of trace events to enable */
> +	uint32_t set;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Trace
> + * @brief response to #MRQ_TRACE_MODIFY
> + *
> + * Sent in repsonse to an #MRQ_TRACE_MODIFY message. #mask reflects the
> + * state of which events are enabled after the recipient acted on the
> + * message.
> + *
> + */
> +struct mrq_trace_modify_response {
> +	/** @brief bit mask of trace event enable states */
> +	uint32_t mask;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_WRITE_TRACE
> + * @brief Write trace data to a buffer
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_write_trace_request
> + * * Response Payload: @ref mrq_write_trace_response
> + *
> + * mrq_response::err depends on the @ref mrq_write_trace_request field
> + * values. err is -#BPMP_EINVAL if size is zero or area is NULL or
> + * area is in an illegal range. A positive value for err indicates the
> + * number of bytes written to area.
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Trace
> + * @brief request with #MRQ_WRITE_TRACE
> + *
> + * Used by MRQ_WRITE_TRACE calls to ask the recipient to copy trace
> + * data from the recipient's local buffer to the output buffer. #area
> + * is treated as a byte-aligned pointer in the recipient's address
> + * space.
> + *
> + * The sender is responsible for ensuring that the output
> + * buffer is mapped in the recipient's address map. The recipient is
> + * responsible for protecting its own code and data from accidental
> + * overwrites.
> + */
> +struct mrq_write_trace_request {
> +	/** @brief base address of output buffer */
> +	uint32_t area;
> +	/** @brief size in bytes of the output buffer */
> +	uint32_t size;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Trace
> + * @brief response to #MRQ_WRITE_TRACE
> + *
> + * Once this response is sent, the respondent will not access the
> + * output buffer further.
> + */
> +struct mrq_write_trace_response {
> +	/**
> +	 * @brief flag whether more data remains in local buffer
> +	 *
> +	 * Value is 1 if the entire local trace buffer has been
> +	 * drained to the outputbuffer. Value is 0 otherwise.
> +	 */
> +	uint32_t eof;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct mrq_threaded_ping_request {
> +	uint32_t challenge;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct mrq_threaded_ping_response {
> +	uint32_t reply;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_MODULE_MAIL
> + * @brief send a message to a loadable module
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_module_mail_request
> + * * Response Payload: @ref mrq_module_mail_response
> + *
> + * @note This MRQ is disabled on production systems
> + */
> +
> +/**
> + * @ingroup Module
> + * @brief request with #MRQ_MODULE_MAIL
> + */
> +struct mrq_module_mail_request {
> +	/** @brief handle to the previously loaded module */
> +	uint32_t base;
> +	/** @brief module-specific mail payload
> +	 *
> +	 * The length of data[ ] is unknown to the BPMP core firmware
> +	 * but it is limited to the size of an IPC message.
> +	 */
> +	uint8_t data[EMPTY_ARRAY];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Module
> + * @brief response to #MRQ_MODULE_MAIL
> + */
> +struct mrq_module_mail_response {
> +	/** @brief module-specific mail payload
> +	 *
> +	 * The length of data[ ] is unknown to the BPMP core firmware
> +	 * but it is limited to the size of an IPC message.
> +	 */
> +	uint8_t data[EMPTY_ARRAY];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_DEBUGFS
> + * @brief Interact with BPMP's debugfs file nodes
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_debugfs_request
> + * * Response Payload: @ref mrq_debugfs_response
> + */
> +
> +/**
> + * @addtogroup Debugfs
> + * @{
> + *
> + * The BPMP firmware implements a pseudo-filesystem called
> + * debugfs. Any driver within the firmware may register with debugfs
> + * to expose an arbitrary set of "files" in the filesystem. When
> + * software on the CPU writes to a debugfs file, debugfs passes the
> + * written data to a callback provided by the driver. When software on
> + * the CPU reads a debugfs file, debugfs queries the driver for the
> + * data to return to the CPU. The intention of the debugfs filesystem
> + * is to provide information useful for debugging the system at
> + * runtime.
> + *
> + * @note The files exposed via debugfs are not part of the
> + * BPMP firmware's ABI. debugfs files may be added or removed in any
> + * given version of the firmware. Typically the semantics of a debugfs
> + * file are consistent from version to version but even that is not
> + * guaranteed.
> + *
> + * @}
> + */
> +/** @ingroup Debugfs */
> +enum mrq_debugfs_commands {
> +	CMD_DEBUGFS_READ = 1,
> +	CMD_DEBUGFS_WRITE = 2,
> +	CMD_DEBUGFS_DUMPDIR = 3,
> +	CMD_DEBUGFS_MAX
> +};
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_request {
> +	/** @brief physical address pointing at filename */
> +	uint32_t fnameaddr;
> +	/** @brief length in bytes of filename buffer */
> +	uint32_t fnamelen;
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief parameters for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_dumpdir_request {
> +	/** @brief physical address pointing to data buffer */
> +	uint32_t dataaddr;
> +	/** @brief length in bytes of data buffer */
> +	uint32_t datalen;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief response data for CMD_DEBUGFS_READ/WRITE command
> + */
> +struct cmd_debugfs_fileop_response {
> +	/** @brief always 0 */
> +	uint32_t reserved;
> +	/** @brief number of bytes read from or written to data buffer */
> +	uint32_t nbytes;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief response data for CMD_DEBUGFS_DUMPDIR command
> + */
> +struct cmd_debugfs_dumpdir_response {
> +	/** @brief always 0 */
> +	uint32_t reserved;
> +	/** @brief number of bytes read from or written to data buffer */
> +	uint32_t nbytes;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + * @brief request with #MRQ_DEBUGFS.
> + *
> + * The sender of an MRQ_DEBUGFS message uses #cmd to specify a debugfs
> + * command to execute. Legal commands are the values of @ref
> + * mrq_debugfs_commands. Each command requires a specific additional
> + * payload of data.
> + *
> + * |command            |payload|
> + * |-------------------|-------|
> + * |CMD_DEBUGFS_READ   |fop    |
> + * |CMD_DEBUGFS_WRITE  |fop    |
> + * |CMD_DEBUGFS_DUMPDIR|dumpdir|
> + */
> +struct mrq_debugfs_request {
> +	uint32_t cmd;
> +	union {
> +		struct cmd_debugfs_fileop_request fop;
> +		struct cmd_debugfs_dumpdir_request dumpdir;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Debugfs
> + */
> +struct mrq_debugfs_response {
> +	/** @brief always 0 */
> +	int32_t reserved;
> +	union {
> +		/** @brief response data for CMD_DEBUGFS_READ OR
> +		 * CMD_DEBUGFS_WRITE command
> +		 */
> +		struct cmd_debugfs_fileop_response fop;
> +		/** @brief response data for CMD_DEBUGFS_DUMPDIR command */
> +		struct cmd_debugfs_dumpdir_response dumpdir;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @addtogroup Debugfs
> + * @{
> + */
> +#define DEBUGFS_S_ISDIR	(1 << 9)
> +#define DEBUGFS_S_IRUSR	(1 << 8)
> +#define DEBUGFS_S_IWUSR	(1 << 7)
> +/** @} */
> +
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_RESET
> + * @brief reset an IP block
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_reset_request
> + * * Response Payload: N/A
> + */
> +
> +/**
> + * @ingroup Reset
> + */
> +enum mrq_reset_commands {
> +	CMD_RESET_ASSERT = 1,
> +	CMD_RESET_DEASSERT = 2,
> +	CMD_RESET_MODULE = 3,
> +	CMD_RESET_MAX, /* not part of ABI and subject to change */
> +};
> +
> +/**
> + * @ingroup Reset
> + * @brief request with MRQ_RESET
> + *
> + * Used by the sender of an #MRQ_RESET message to request BPMP to
> + * assert or or deassert a given reset line.
> + */
> +struct mrq_reset_request {
> +	/** @brief reset action to perform (@enum mrq_reset_commands) */
> +	uint32_t cmd;
> +	/** @brief id of the reset to affected */
> +	uint32_t reset_id;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_I2C
> + * @brief issue an i2c transaction
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_i2c_request
> + * * Response Payload: @ref mrq_i2c_response
> + */
> +
> +/**
> + * @addtogroup I2C
> + * @{
> + */
> +#define TEGRA_I2C_IPC_MAX_IN_BUF_SIZE	(MSG_DATA_MIN_SZ - 12)
> +#define TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE	(MSG_DATA_MIN_SZ - 4)
> +/** @} */
> +
> +/**
> + * @ingroup I2C
> + * @name Serial I2C flags
> + * Use these flags with serial_i2c_request::flags
> + * @{
> + */
> +#define SERIALI2C_TEN           0x0010
> +#define SERIALI2C_RD            0x0001
> +#define SERIALI2C_STOP          0x8000
> +#define SERIALI2C_NOSTART       0x4000
> +#define SERIALI2C_REV_DIR_ADDR  0x2000
> +#define SERIALI2C_IGNORE_NAK    0x1000
> +#define SERIALI2C_NO_RD_ACK     0x0800
> +#define SERIALI2C_RECV_LEN      0x0400
> +/** @} */
> +/** @ingroup I2C */
> +enum {
> +	CMD_I2C_XFER = 1
> +};
> +
> +/**
> + * @ingroup I2C
> + * @brief serializable i2c request
> + *
> + * Instances of this structure are packed (little-endian) into
> + * cmd_i2c_xfer_request::data_buf. Each instance represents a single
> + * transaction (or a portion of a transaction with repeated starts) on
> + * an i2c bus.
> + *
> + * Because these structures are packed, some instances are likely to
> + * be misaligned. Additionally because #data is variable length, it is
> + * not possible to iterate through a serialized list of these
> + * structures without inspecting #len in each instance.  It may be
> + * easier to serialize or deserialize cmd_i2c_xfer_request::data_buf
> + * manually rather than using this structure definition.
> +*/
> +struct serial_i2c_request {
> +	/** @brief I2C slave address */
> +	uint16_t addr;
> +	/** @brief bitmask of SERIALI2C_ flags */
> +	uint16_t flags;
> +	/** @brief length of I2C transaction in bytes */
> +	uint16_t len;
> +	/** @brief for write transactions only, #len bytes of data */
> +	uint8_t data[];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief trigger one or more i2c transactions
> + */
> +struct cmd_i2c_xfer_request {
> +	/** @brief valid bus number from mach-t186/i2c-t186.h*/
> +	uint32_t bus_id;
> +
> +	/** @brief count of valid bytes in #data_buf*/
> +	uint32_t data_size;
> +
> +	/** @brief serialized packed instances of @ref serial_i2c_request*/
> +	uint8_t data_buf[TEGRA_I2C_IPC_MAX_IN_BUF_SIZE];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief container for data read from the i2c bus
> + *
> + * Processing an cmd_i2c_xfer_request::data_buf causes BPMP to execute
> + * zero or more I2C reads. The data read from the bus is serialized
> + * into #data_buf.
> + */
> +struct cmd_i2c_xfer_response {
> +	/** @brief count of valid bytes in #data_buf*/
> +	uint32_t data_size;
> +	/** @brief i2c read data */
> +	uint8_t data_buf[TEGRA_I2C_IPC_MAX_OUT_BUF_SIZE];
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief request with #MRQ_I2C
> + */
> +struct mrq_i2c_request {
> +	/** @brief always CMD_I2C_XFER (i.e. 1) */
> +	uint32_t cmd;
> +	/** @brief parameters of the transfer request */
> +	struct cmd_i2c_xfer_request xfer;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup I2C
> + * @brief response to #MRQ_I2C
> + */
> +struct mrq_i2c_response {
> +	struct cmd_i2c_xfer_response xfer;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_CLK
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_clk_request
> + * * Response Payload: @ref mrq_clk_response
> + * @addtogroup Clocks
> + * @{
> + */
> +
> +/**
> + * @name MRQ_CLK sub-commands
> + * @{
> + */
> +enum {
> +	CMD_CLK_GET_RATE = 1,
> +	CMD_CLK_SET_RATE = 2,
> +	CMD_CLK_ROUND_RATE = 3,
> +	CMD_CLK_GET_PARENT = 4,
> +	CMD_CLK_SET_PARENT = 5,
> +	CMD_CLK_IS_ENABLED = 6,
> +	CMD_CLK_ENABLE = 7,
> +	CMD_CLK_DISABLE = 8,
> +	CMD_CLK_GET_ALL_INFO = 14,
> +	CMD_CLK_GET_MAX_CLK_ID = 15,
> +	CMD_CLK_MAX,
> +};
> +/** @} */
> +
> +#define MRQ_CLK_NAME_MAXLEN	40
> +#define MRQ_CLK_MAX_PARENTS	16
> +
> +/** @private */
> +struct cmd_clk_get_rate_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_round_rate_request {
> +	int32_t unused;
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_round_rate_response {
> +	int64_t rate;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_parent_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_parent_response {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_parent_request {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +struct cmd_clk_set_parent_response {
> +	uint32_t parent_id;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_is_enabled_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_is_enabled_response {
> +	int32_t state;
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_enable_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_enable_response {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_disable_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_disable_response {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_all_info_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_all_info_response {
> +	uint32_t flags;
> +	uint32_t parent;
> +	uint32_t parents[MRQ_CLK_MAX_PARENTS];
> +	uint8_t num_parents;
> +	uint8_t name[MRQ_CLK_NAME_MAXLEN];
> +} __ABI_PACKED;
> +
> +/** @private */
> +struct cmd_clk_get_max_clk_id_request {
> +	EMPTY
> +} __ABI_PACKED;
> +
> +struct cmd_clk_get_max_clk_id_response {
> +	uint32_t max_id;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup Clocks
> + * @brief request with #MRQ_CLK
> + *
> + * Used by the sender of an #MRQ_CLK message to control clocks. The
> + * clk_request is split into several sub-commands. Some sub-commands
> + * require no additional data. Others have a sub-command specific
> + * payload
> + *
> + * |sub-command                 |payload                |
> + * |----------------------------|-----------------------|
> + * |CMD_CLK_GET_RATE            |-                      |
> + * |CMD_CLK_SET_RATE            |clk_set_rate           |
> + * |CMD_CLK_ROUND_RATE          |clk_round_rate         |
> + * |CMD_CLK_GET_PARENT          |-                      |
> + * |CMD_CLK_SET_PARENT          |clk_set_parent         |
> + * |CMD_CLK_IS_ENABLED          |-                      |
> + * |CMD_CLK_ENABLE              |-                      |
> + * |CMD_CLK_DISABLE             |-                      |
> + * |CMD_CLK_GET_ALL_INFO        |-                      |
> + * |CMD_CLK_GET_MAX_CLK_ID      |-                      |
> + *
> + */
> +
> +struct mrq_clk_request {
> +	/** @brief sub-command and clock id concatenated to 32-bit word.
> +	 * - bits[31..24] is the sub-cmd.
> +	 * - bits[23..0] is the clock id
> +	 */
> +	uint32_t cmd_and_id;
> +
> +	union {
> +		/** @private */
> +		struct cmd_clk_get_rate_request clk_get_rate;
> +		struct cmd_clk_set_rate_request clk_set_rate;
> +		struct cmd_clk_round_rate_request clk_round_rate;
> +		/** @private */
> +		struct cmd_clk_get_parent_request clk_get_parent;
> +		struct cmd_clk_set_parent_request clk_set_parent;
> +		/** @private */
> +		struct cmd_clk_enable_request clk_enable;
> +		/** @private */
> +		struct cmd_clk_disable_request clk_disable;
> +		/** @private */
> +		struct cmd_clk_is_enabled_request clk_is_enabled;
> +		/** @private */
> +		struct cmd_clk_get_all_info_request clk_get_all_info;
> +		/** @private */
> +		struct cmd_clk_get_max_clk_id_request clk_get_max_clk_id;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup Clocks
> + * @brief response to MRQ_CLK
> + *
> + * Each sub-command supported by @ref mrq_clk_request may return
> + * sub-command-specific data. Some do and some do not as indicated in
> + * the following table
> + *
> + * |sub-command                 |payload                 |
> + * |----------------------------|------------------------|
> + * |CMD_CLK_GET_RATE            |clk_get_rate            |
> + * |CMD_CLK_SET_RATE            |clk_set_rate            |
> + * |CMD_CLK_ROUND_RATE          |clk_round_rate          |
> + * |CMD_CLK_GET_PARENT          |clk_get_parent          |
> + * |CMD_CLK_SET_PARENT          |clk_set_parent          |
> + * |CMD_CLK_IS_ENABLED          |clk_is_enabled          |
> + * |CMD_CLK_ENABLE              |-                       |
> + * |CMD_CLK_DISABLE             |-                       |
> + * |CMD_CLK_GET_ALL_INFO        |clk_get_all_info        |
> + * |CMD_CLK_GET_MAX_CLK_ID      |clk_get_max_id          |
> + *
> + */
> +
> +struct mrq_clk_response {
> +	union {
> +		struct cmd_clk_get_rate_response clk_get_rate;
> +		struct cmd_clk_set_rate_response clk_set_rate;
> +		struct cmd_clk_round_rate_response clk_round_rate;
> +		struct cmd_clk_get_parent_response clk_get_parent;
> +		struct cmd_clk_set_parent_response clk_set_parent;
> +		/** @private */
> +		struct cmd_clk_enable_response clk_enable;
> +		/** @private */
> +		struct cmd_clk_disable_response clk_disable;
> +		struct cmd_clk_is_enabled_response clk_is_enabled;
> +		struct cmd_clk_get_all_info_response clk_get_all_info;
> +		struct cmd_clk_get_max_clk_id_response clk_get_max_clk_id;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_QUERY_ABI
> + * @brief check if an MRQ is implemented
> + *
> + * * Platforms: All
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: @ref mrq_query_abi_request
> + * * Response Payload: @ref mrq_query_abi_response
> + */
> +
> +/**
> + * @ingroup ABI_info
> + * @brief request with MRQ_QUERY_ABI
> + *
> + * Used by #MRQ_QUERY_ABI call to check if MRQ code #mrq is supported
> + * by the recipient.
> + */
> +struct mrq_query_abi_request {
> +	/** @brief MRQ code to query */
> +	uint32_t mrq;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup ABI_info
> + * @brief response to MRQ_QUERY_ABI
> + */
> +struct mrq_query_abi_response {
> +	/** @brief 0 if queried MRQ is supported. Else, -#BPMP_ENODEV */
> +	int32_t status;
> +} __ABI_PACKED;
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PG_READ_STATE
> + * @brief read the power-gating state of a partition
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_pg_read_state_request
> + * * Response Payload: @ref mrq_pg_read_state_response
> + * @addtogroup Powergating
> + * @{
> + */
> +
> +/**
> + * @brief request with #MRQ_PG_READ_STATE
> + *
> + * Used by MRQ_PG_READ_STATE call to read the current state of a
> + * partition.
> + */
> +struct mrq_pg_read_state_request {
> +	/** @brief ID of partition */
> +	uint32_t partition_id;
> +} __ABI_PACKED;
> +
> +/**
> + * @brief response to MRQ_PG_READ_STATE
> + * @todo define possible errors.
> + */
> +struct mrq_pg_read_state_response {
> +	/** @brief read as don't care */
> +	uint32_t sram_state;
> +	/** @brief state of power partition
> +	 * * 0 : off
> +	 * * 1 : on
> +	 */
> +	uint32_t logic_state;
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_PG_UPDATE_STATE
> + * @brief modify the power-gating state of a partition
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_pg_update_state_request
> + * * Response Payload: N/A
> + * @addtogroup Powergating
> + * @{
> + */
> +
> +/**
> + * @brief request with mrq_pg_update_state_request
> + *
> + * Used by #MRQ_PG_UPDATE_STATE call to request BPMP to change the
> + * state of a power partition #partition_id.
> + */
> +struct mrq_pg_update_state_request {
> +	/** @brief ID of partition */
> +	uint32_t partition_id;
> +	/** @brief secondary control of power partition
> +	 *  @details Ignored by many versions of the BPMP
> +	 *  firmware. For maximum compatibility, set the value
> +	 *  according to @logic_state
> +	 * *  0x1: power ON partition (@ref logic_state == 0x3)
> +	 * *  0x3: power OFF partition (@ref logic_state == 0x1)
> +	 */
> +	uint32_t sram_state;
> +	/** @brief controls state of power partition, legal values are
> +	 * *  0x1 : power OFF partition
> +	 * *  0x3 : power ON partition
> +	 */
> +	uint32_t logic_state;
> +	/** @brief change state of clocks of the power partition, legal values
> +	 * *  0x0 : do not change clock state
> +	 * *  0x1 : disable partition clocks (only applicable when
> +	 *          @ref logic_state == 0x1)
> +	 * *  0x3 : enable partition clocks (only applicable when
> +	 *          @ref logic_state == 0x3)
> +	 */
> +	uint32_t clock_state;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_THERMAL
> + * @brief interact with BPMP thermal framework
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: Any
> + * * Request Payload: TODO
> + * * Response Payload: TODO
> + *
> + * @addtogroup Thermal
> + *
> + * The BPMP firmware includes a thermal framework. Drivers within the
> + * bpmp firmware register with the framework to provide thermal
> + * zones. Each thermal zone corresponds to an entity whose temperature
> + * can be measured. The framework also has a notion of trip points. A
> + * trip point consists of a thermal zone id, a temperature, and a
> + * callback routine. The framework invokes the callback when the zone
> + * hits the indicated temperature. The BPMP firmware uses this thermal
> + * framework interally to implement various temperature-dependent
> + * functions.
> + *
> + * Software on the CPU can use #MRQ_THERMAL (with payload @ref
> + * mrq_thermal_host_to_bpmp_request) to interact with the BPMP thermal
> + * framework. The CPU must It can query the number of supported zones,
> + * query zone temperatures, and set trip points.
> + *
> + * When a trip point set by the CPU gets crossed, BPMP firmware issues
> + * an IPC to the CPU having mrq_request::mrq = #MRQ_THERMAL and a
> + * payload of @ref mrq_thermal_bpmp_to_host_request.
> + * @{
> + */
> +enum mrq_thermal_host_to_bpmp_cmd {
> +	/**
> +	 * @brief Check whether the BPMP driver supports the specified
> +	 * request type.
> +	 *
> +	 * Host needs to supply request parameters.
> +	 *
> +	 * mrq_response::err is 0 if the specified request is
> +	 * supported and -#BPMP_ENODEV otherwise.
> +	 */
> +	CMD_THERMAL_QUERY_ABI = 0,
> +
> +	/**
> +	 * @brief Get the current temperature of the specified zone.
> +	 *
> +	 * Host needs to supply request parameters.
> +	 *
> +	 * mrq_response::err is
> +	 * *  0: Temperature query succeeded.
> +	 * *  -#BPMP_EINVAL: Invalid request parameters.
> +	 * *  -#BPMP_ENOENT: No driver registered for thermal zone..
> +	 * *  -#BPMP_EFAULT: Problem reading temperature measurement.
> +	 */
> +	CMD_THERMAL_GET_TEMP = 1,
> +
> +	/**
> +	 * @brief Enable or disable and set the lower and upper
> +	 *   thermal limits for a thermal trip point. Each zone has
> +	 *   one trip point.
> +	 *
> +	 * Host needs to supply request parameters. Once the
> +	 * temperature hits a trip point, the BPMP will send a message
> +	 * to the CPU having MRQ=MRQ_THERMAL and
> +	 * type=CMD_THERMAL_HOST_TRIP_REACHED
> +	 *
> +	 * mrq_response::err is
> +	 * *  0: Trip successfully set.
> +	 * *  -#BPMP_EINVAL: Invalid request parameters.
> +	 * *  -#BPMP_ENOENT: No driver registered for thermal zone.
> +	 * *  -#BPMP_EFAULT: Problem setting trip point.
> +	 */
> +	CMD_THERMAL_SET_TRIP = 2,
> +
> +	/**
> +	 * @brief Get the number of supported thermal zones.
> +	 *
> +	 * No request parameters required.
> +	 *
> +	 * mrq_response::err is always 0, indicating success.
> +	 */
> +	CMD_THERMAL_GET_NUM_ZONES = 3,
> +
> +	/** @brief: number of supported host-to-bpmp commands. May
> +	 * increase in future
> +	 */
> +	CMD_THERMAL_HOST_TO_BPMP_NUM
> +};
> +
> +enum mrq_thermal_bpmp_to_host_cmd {
> +	/**
> +	 * @brief Indication that the temperature for a zone has
> +	 *   exceeded the range indicated in the thermal trip point
> +	 *   for the zone.
> +	 *
> +	 * BPMP needs to supply request parameters. Host only needs to
> +	 * acknowledge.
> +	 */
> +	CMD_THERMAL_HOST_TRIP_REACHED = 100,
> +
> +	/** @brief: number of supported bpmp-to-host commands. May
> +	 * increase in future
> +	 */
> +	CMD_THERMAL_BPMP_TO_HOST_NUM
> +};
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_QUERY_ABI
> + *
> + * zone: Request type for which to check existence.
> + */
> +struct cmd_thermal_query_abi_request {
> +	uint32_t type;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_GET_TEMP
> + *
> + * zone: Number of thermal zone.
> + */
> +struct cmd_thermal_get_temp_request {
> +	uint32_t zone;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host reply data for request CMD_THERMAL_GET_TEMP
> + *
> + * error: 0 if request succeeded.
> + *	-BPMP_EINVAL if request parameters were invalid.
> + *      -BPMP_ENOENT if no driver was registered for the specified thermal zone.
> + *      -BPMP_EFAULT for other thermal zone driver errors.
> + * temp: Current temperature in millicelsius.
> + */
> +struct cmd_thermal_get_temp_response {
> +	int32_t temp;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data for request type CMD_THERMAL_SET_TRIP
> + *
> + * zone: Number of thermal zone.
> + * low: Temperature of lower trip point in millicelsius
> + * high: Temperature of upper trip point in millicelsius
> + * enabled: 1 to enable trip point, 0 to disable trip point
> + */
> +struct cmd_thermal_set_trip_request {
> +	uint32_t zone;
> +	int32_t low;
> +	int32_t high;
> +	uint32_t enabled;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host request data for request type CMD_THERMAL_HOST_TRIP_REACHED
> + *
> + * zone: Number of thermal zone where trip point was reached.
> + */
> +struct cmd_thermal_host_trip_reached_request {
> +	uint32_t zone;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host reply data for request type CMD_THERMAL_GET_NUM_ZONES
> + *
> + * num: Number of supported thermal zones. The thermal zones are indexed
> + *      starting from zero.
> + */
> +struct cmd_thermal_get_num_zones_response {
> +	uint32_t num;
> +} __ABI_PACKED;
> +
> +/*
> + * Host->BPMP request data.
> + *
> + * Reply type is union mrq_thermal_bpmp_to_host_response.
> + *
> + * type: Type of request. Values listed in enum mrq_thermal_type.
> + * data: Request type specific parameters.
> + */
> +struct mrq_thermal_host_to_bpmp_request {
> +	uint32_t type;
> +	union {
> +		struct cmd_thermal_query_abi_request query_abi;
> +		struct cmd_thermal_get_temp_request get_temp;
> +		struct cmd_thermal_set_trip_request set_trip;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/*
> + * BPMP->Host request data.
> + *
> + * type: Type of request. Values listed in enum mrq_thermal_type.
> + * data: Request type specific parameters.
> + */
> +struct mrq_thermal_bpmp_to_host_request {
> +	uint32_t type;
> +	union {
> +		struct cmd_thermal_host_trip_reached_request host_trip_reached;
> +	} __UNION_ANON;
> +} __ABI_PACKED;
> +
> +/*
> + * Data in reply to a Host->BPMP request.
> + */
> +union mrq_thermal_bpmp_to_host_response {
> +	struct cmd_thermal_get_temp_response get_temp;
> +	struct cmd_thermal_get_num_zones_response get_num_zones;
> +} __ABI_PACKED;
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_CPU_VHINT
> + * @brief Query CPU voltage hint data
> + *
> + * * Platforms: T186
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_cpu_vhint_request
> + * * Response Payload: N/A
> + *
> + * @addtogroup Vhint CPU Voltage hint
> + * @{
> + */
> +
> +/**
> + * @brief request with #MRQ_CPU_VHINT
> + *
> + * Used by #MRQ_CPU_VHINT call by CCPLEX to retrieve voltage hint data
> + * from BPMP to memory space pointed by #addr. CCPLEX is responsible
> + * to allocate sizeof(cpu_vhint_data) sized block of memory and
> + * appropriately map it for BPMP before sending the request.
> + */
> +struct mrq_cpu_vhint_request {
> +	/** @brief IOVA address for the #cpu_vhint_data */
> +	uint32_t addr; /* struct cpu_vhint_data * */
> +	/** @brief ID of the cluster whose data is requested */
> +	uint32_t cluster_id; /* enum cluster_id */
> +} __ABI_PACKED;
> +
> +/**
> + * @brief description of the CPU v/f relation
> + *
> + * Used by #MRQ_CPU_VHINT call to carry data pointed by #addr of
> + * struct mrq_cpu_vhint_request
> + */
> +struct cpu_vhint_data {
> +	uint32_t ref_clk_hz; /**< reference frequency in Hz */
> +	uint16_t pdiv; /**< post divider value */
> +	uint16_t mdiv; /**< input divider value */
> +	uint16_t ndiv_max; /**< fMAX expressed with max NDIV value */
> +	/** table of ndiv values as a function of vINDEX (voltage index) */
> +	uint16_t ndiv[80];
> +	/** minimum allowed NDIV value */
> +	uint16_t ndiv_min;
> +	/** minimum allowed voltage hint value (as in vINDEX) */
> +	uint16_t vfloor;
> +	/** maximum allowed voltage hint value (as in vINDEX) */
> +	uint16_t vceil;
> +	/** post-multiplier for vindex value */
> +	uint16_t vindex_mult;
> +	/** post-divider for vindex value */
> +	uint16_t vindex_div;
> +	/** reserved for future use */
> +	uint16_t reserved[328];
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_ABI_RATCHET
> + * @brief ABI ratchet value query
> + *
> + * * Platforms: T186
> + * * Initiators: Any
> + * * Targets: BPMP
> + * * Request Payload: @ref mrq_abi_ratchet_request
> + * * Response Payload: @ref mrq_abi_ratchet_response
> + * @addtogroup ABI_info
> + * @{
> + */
> +
> +/**
> + * @brief an ABI compatibility mechanism
> + *
> + * BPMP_ABI_RATCHET_VALUE may increase for various reasons in a future
> + * revision of this header file.
> + * 1. That future revision deprecates some MRQ
> + * 2. That future revision introduces a breaking change to an existing
> + *    MRQ or
> + * 3. A bug is discovered in an existing implementation of the BPMP-FW
> + *    (or possibly one of its clients) which warrants deprecating that
> + *    implementation.
> + */
> +#define BPMP_ABI_RATCHET_VALUE 3
> +
> +/**
> + * @brief request with #MRQ_ABI_RATCHET.
> + *
> + * #ratchet should be #BPMP_ABI_RATCHET_VALUE from the ABI header
> + * against which the requester was compiled.
> + *
> + * If ratchet is less than BPMP's #BPMP_ABI_RATCHET_VALUE, BPMP may
> + * reply with mrq_response::err = -#BPMP_ERANGE to indicate that
> + * BPMP-FW cannot interoperate correctly with the requester. Requester
> + * should cease further communication with BPMP.
> + *
> + * Otherwise, err shall be 0.
> + */
> +struct mrq_abi_ratchet_request {
> +	/** @brief requester's ratchet value */
> +	uint16_t ratchet;
> +};
> +
> +/**
> + * @brief response to #MRQ_ABI_RATCHET
> + *
> + * #ratchet shall be #BPMP_ABI_RATCHET_VALUE from the ABI header
> + * against which BPMP firwmare was compiled.
> + *
> + * If #ratchet is less than the requester's #BPMP_ABI_RATCHET_VALUE,
> + * the requster must either interoperate with BPMP according to an ABI
> + * header version with BPMP_ABI_RATCHET_VALUE = ratchet or cease
> + * communication with BPMP.
> + *
> + * If mrq_response::err is 0 and ratchet is greater than or equal to the
> + * requester's BPMP_ABI_RATCHET_VALUE, the requester should continue
> + * normal operation.
> + */
> +struct mrq_abi_ratchet_response {
> +	/** @brief BPMP's ratchet value */
> +	uint16_t ratchet;
> +};
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_EMC_DVFS_LATENCY
> + * @brief query frequency dependent EMC DVFS latency
> + *
> + * * Platforms: T186
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: N/A
> + * * Response Payload: @ref mrq_emc_dvfs_latency_response
> + * @addtogroup EMC
> + * @{
> + */
> +
> +/**
> + * @brief used by @ref mrq_emc_dvfs_latency_response
> + */
> +struct emc_dvfs_latency {
> +	/** @brief EMC frequency in kHz */
> +	uint32_t freq;
> +	/** @brief EMC DVFS latency in nanoseconds */
> +	uint32_t latency;
> +} __ABI_PACKED;
> +
> +#define EMC_DVFS_LATENCY_MAX_SIZE	14
> +/**
> + * @brief response to #MRQ_EMC_DVFS_LATENCY
> + */
> +struct mrq_emc_dvfs_latency_response {
> +	/** @brief the number valid entries in #pairs */
> +	uint32_t num_pairs;
> +	/** @brief EMC <frequency, latency> information */
> +	struct emc_dvfs_latency pairs[EMC_DVFS_LATENCY_MAX_SIZE];
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/**
> + * @ingroup MRQ_Codes
> + * @def MRQ_TRACE_ITER
> + * @brief manage the trace iterator
> + *
> + * * Platforms: All
> + * * Initiators: CCPLEX
> + * * Targets: BPMP
> + * * Request Payload: N/A
> + * * Response Payload: @ref mrq_trace_iter_request
> + * @addtogroup Trace
> + * @{
> + */
> +enum {
> +	/** @brief (re)start the tracing now. Ignore older events */
> +	TRACE_ITER_INIT = 0,
> +	/** @brief clobber all events in the trace buffer */
> +	TRACE_ITER_CLEAN = 1
> +};
> +
> +/**
> + * @brief request with #MRQ_TRACE_ITER
> + */
> +struct mrq_trace_iter_request {
> +	/** @brief TRACE_ITER_INIT or TRACE_ITER_CLEAN */
> +	uint32_t cmd;
> +} __ABI_PACKED;
> +
> +/** @} */
> +
> +/*
> + *  4. Enumerations
> + */
> +
> +/*
> + *   4.1 CPU enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + *
> + *   4.2 CPU Cluster enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + *
> + *   4.3 System low power state enumerations
> + *
> + * See <mach-t186/system-t186.h>
> + */
> +
> +/*
> + *   4.4 Clock enumerations
> + *
> + * For clock enumerations, see <mach-t186/clk-t186.h>
> + */
> +
> +/*
> + *   4.5 Reset enumerations
> + *
> + * For reset enumerations, see <mach-t186/reset-t186.h>
> + */
> +
> +/*
> + *   4.6 Thermal sensor enumerations
> + *
> + * For thermal sensor enumerations, see <mach-t186/thermal-t186.h>
> + */
> +
> +/**
> + * @defgroup Error_Codes
> + * Negative values for mrq_response::err generally indicate some
> + * error. The ABI defines the following error codes. Negating these
> + * defines is an exercise left to the user.
> + * @{
> + */
> +/** @brief No such file or directory */
> +#define BPMP_ENOENT	2
> +/** @brief No MRQ handler */
> +#define BPMP_ENOHANDLER	3
> +/** @brief I/O error */
> +#define BPMP_EIO	5
> +/** @brief Bad sub-MRQ command */
> +#define BPMP_EBADCMD	6
> +/** @brief Not enough memory */
> +#define BPMP_ENOMEM	12
> +/** @brief Permission denied */
> +#define BPMP_EACCES	13
> +/** @brief Bad address */
> +#define BPMP_EFAULT	14
> +/** @brief No such device */
> +#define BPMP_ENODEV	19
> +/** @brief Argument is a directory */
> +#define BPMP_EISDIR	21
> +/** @brief Invalid argument */
> +#define BPMP_EINVAL	22
> +/** @brief Timeout during operation */
> +#define BPMP_ETIMEDOUT  23
> +/** @brief Out of range */
> +#define BPMP_ERANGE	34
> +/** @} */
> +/** @} */
> +#endif
> diff --git a/include/soc/tegra/bpmp.h b/include/soc/tegra/bpmp.h
> new file mode 100644
> index 000000000000..e17d091e8a9a
> --- /dev/null
> +++ b/include/soc/tegra/bpmp.h
> @@ -0,0 +1,122 @@
> +/*
> + * Copyright (c) 2016, NVIDIA CORPORATION.  All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify it
> + * under the terms and conditions of the GNU General Public License,
> + * version 2, as published by the Free Software Foundation.
> + *
> + * This program is distributed in the hope 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.
> + */
> +
> +#ifndef __SOC_TEGRA_BPMP_H
> +#define __SOC_TEGRA_BPMP_H
> +
> +#include <linux/mailbox_client.h>
> +#include <linux/reset-controller.h>
> +#include <linux/semaphore.h>
> +#include <linux/types.h>
> +
> +#define TEGRA_BPMP_MSG_SIZE 128
> +#define TEGRA_BPMP_MSG_DATA_SIZE 120

Why aren't we using the ABI equivalents?

> +
> +struct tegra_bpmp_soc {
> +	struct {
> +		struct {
> +			unsigned int offset;
> +			unsigned int count;
> +			unsigned int timeout;
> +		} cpu_tx, thread, cpu_rx;
> +	} channels;
> +	unsigned int num_resets;
> +};
> +
> +struct tegra_bpmp_mb_data {
> +	u32 code;
> +	u32 flags;
> +	u8 data[TEGRA_BPMP_MSG_DATA_SIZE];
> +} __packed;
> +
> +struct tegra_bpmp_channel {
> +	struct tegra_bpmp *bpmp;
> +	struct tegra_bpmp_mb_data *ib;
> +	struct tegra_bpmp_mb_data *ob;
> +	struct completion completion;
> +	struct tegra_ivc *ivc;
> +};
> +
> +typedef void (*tegra_bpmp_mrq_handler_t)(unsigned int mrq,
> +					 struct tegra_bpmp_channel *channel,
> +					 void *data);
> +
> +struct tegra_bpmp_mrq {
> +	struct list_head list;
> +	unsigned int mrq;
> +	tegra_bpmp_mrq_handler_t handler;
> +	void *data;
> +};
> +
> +struct tegra_bpmp {
> +	const struct tegra_bpmp_soc *soc;
> +	struct device *dev;
> +
> +	void __iomem *tx_base;
> +	void __iomem *rx_base;
> +
> +	struct {
> +		struct mbox_client client;
> +		struct mbox_chan *channel;
> +	} mbox;
> +
> +	struct tegra_bpmp_channel *channels;
> +	unsigned int num_channels;
> +
> +	struct {
> +		unsigned long *allocated;
> +		unsigned long *busy;
> +		unsigned int count;
> +		struct semaphore lock;
> +	} threaded;
> +
> +	struct list_head mrqs;
> +	spinlock_t lock;
> +
> +	unsigned int num_clocks;
> +	struct clk_hw **clocks;
> +
> +	struct reset_controller_dev rstc;
> +};
> +
> +struct tegra_bpmp *tegra_bpmp_get(struct device *dev);
> +void tegra_bpmp_put(struct tegra_bpmp *bpmp);
> +
> +struct tegra_bpmp_message {
> +	unsigned int mrq;
> +
> +	struct {
> +		const void *data;
> +		size_t size;
> +	} tx;
> +
> +	struct {
> +		void *data;
> +		size_t size;
> +	} rx;
> +};
> +
> +int tegra_bpmp_transfer_atomic(struct tegra_bpmp *bpmp,
> +			       struct tegra_bpmp_message *msg);
> +int tegra_bpmp_transfer(struct tegra_bpmp *bpmp,
> +			struct tegra_bpmp_message *msg);
> +
> +int tegra_bpmp_request_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			   tegra_bpmp_mrq_handler_t handler, void *data);
> +void tegra_bpmp_free_mrq(struct tegra_bpmp *bpmp, unsigned int mrq,
> +			 void *data);
> +
> +int tegra_bpmp_init_clocks(struct tegra_bpmp *bpmp);
> +int tegra_bpmp_init_resets(struct tegra_bpmp *bpmp);

I believe this will break bisect (because the functions are defined in a
later patch)? But I like the clock/reset driver to refer to bpmp driver
than in the reverse direction.

> +
> +#endif /* __SOC_TEGRA_BPMP_H */
> -- 
> 2.9.0
> 

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

* Re: [PATCH v3 04/12] firmware: tegra: Add IVC library
  2016-08-19 17:32     ` Thierry Reding
@ 2016-08-24 15:13       ` Jon Hunter
  -1 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-24 15:13 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Sivaram Nair, devicetree, Peter De Schrijver, Timo Alho,
	Joseph Lo, linux-tegra, linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
> 
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
> 
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
> 
> Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a more object oriented design
> 
>  drivers/firmware/Kconfig        |   1 +
>  drivers/firmware/Makefile       |   1 +
>  drivers/firmware/tegra/Kconfig  |  13 +
>  drivers/firmware/tegra/Makefile |   1 +
>  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/ivc.h         | 109 +++++++
>  6 files changed, 808 insertions(+)
>  create mode 100644 drivers/firmware/tegra/Kconfig
>  create mode 100644 drivers/firmware/tegra/Makefile
>  create mode 100644 drivers/firmware/tegra/ivc.c
>  create mode 100644 include/soc/tegra/ivc.h

[snip]

> +static int check_ivc_params(unsigned long base1, unsigned long base2,
> +			    unsigned int num_frames, size_t frame_size)
> +{
> +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
> +
> +	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
> +		pr_err("num_frames * frame_size overflows\n");
> +		return -EINVAL;
> +	}

This generates the following sparse warning ...

drivers/firmware/tegra/ivc.c:574:60: warning: constant 0x100000000 is so
big it is long

I think we need to append a UL.

> +
> +	/*
> +	 * The headers must at least be aligned enough for counters
> +	 * to be accessed atomically.
> +	 */
> +	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base1);
> +		return -EINVAL;
> +	}
> +
> +	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base2);
> +		return -EINVAL;
> +	}
> +
> +	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("frame size not adequately aligned: %zu\n", frame_size);
> +		return -EINVAL;
> +	}
> +
> +	if (base1 < base2) {
> +		if (base1 + frame_size * num_frames > base2) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base1, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (base2 + frame_size * num_frames > base1) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base2, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> +		   unsigned int num_frames, size_t frame_size,
> +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> +		   void *data)
> +{
> +	size_t queue_size;
> +	int err;
> +
> +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> +			       num_frames, frame_size);
> +	if (err < 0)
> +		return err;
> +
> +	BUG_ON(!ivc);
> +	BUG_ON(!notify);
> +
> +	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
> +
> +	/*
> +	 * All sizes that can be returned by communication functions should
> +	 * fit in an int.
> +	 */
> +	if (frame_size > INT_MAX)
> +		return -E2BIG;
> +
> +	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
> +	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;

And these generate the sparse warnings ...

drivers/firmware/tegra/ivc.c:642:28: warning: cast removes address space
of expression
drivers/firmware/tegra/ivc.c:643:28: warning: cast removes address space
of expression

Cheers
Jon

-- 
nvpublic

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

* [PATCH v3 04/12] firmware: tegra: Add IVC library
@ 2016-08-24 15:13       ` Jon Hunter
  0 siblings, 0 replies; 104+ messages in thread
From: Jon Hunter @ 2016-08-24 15:13 UTC (permalink / raw)
  To: linux-arm-kernel


On 19/08/16 18:32, Thierry Reding wrote:
> From: Thierry Reding <treding@nvidia.com>
> 
> The Inter-VM communication (IVC) is a communication protocol which is
> designed for interprocessor communication (IPC) or the communication
> between the hypervisor and the virtual machine with a guest OS.
> 
> Message channels are used to communicate between processors. They are
> backed by DRAM or SRAM, so care must be taken to maintain coherence of
> data.
> 
> The IVC library maintains memory-based descriptors for the transmission
> and reception channels as well as the data coherence of the counter and
> payload. Clients, such as the driver for the BPMP firmware, can use the
> library to exchange messages with remote processors.
> 
> Based on work by Peter Newman <pnewman@nvidia.com> and Joseph Lo
> <josephl@nvidia.com>.
> 
> Signed-off-by: Thierry Reding <treding@nvidia.com>
> ---
> Changes in v3:
> - use a more object oriented design
> 
>  drivers/firmware/Kconfig        |   1 +
>  drivers/firmware/Makefile       |   1 +
>  drivers/firmware/tegra/Kconfig  |  13 +
>  drivers/firmware/tegra/Makefile |   1 +
>  drivers/firmware/tegra/ivc.c    | 683 ++++++++++++++++++++++++++++++++++++++++
>  include/soc/tegra/ivc.h         | 109 +++++++
>  6 files changed, 808 insertions(+)
>  create mode 100644 drivers/firmware/tegra/Kconfig
>  create mode 100644 drivers/firmware/tegra/Makefile
>  create mode 100644 drivers/firmware/tegra/ivc.c
>  create mode 100644 include/soc/tegra/ivc.h

[snip]

> +static int check_ivc_params(unsigned long base1, unsigned long base2,
> +			    unsigned int num_frames, size_t frame_size)
> +{
> +	BUG_ON(offsetof(struct tegra_ivc_header, tx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(offsetof(struct tegra_ivc_header, rx.count) & (TEGRA_IVC_ALIGN - 1));
> +	BUG_ON(sizeof(struct tegra_ivc_header) & (TEGRA_IVC_ALIGN - 1));
> +
> +	if ((uint64_t)num_frames * (uint64_t)frame_size >= 0x100000000) {
> +		pr_err("num_frames * frame_size overflows\n");
> +		return -EINVAL;
> +	}

This generates the following sparse warning ...

drivers/firmware/tegra/ivc.c:574:60: warning: constant 0x100000000 is so
big it is long

I think we need to append a UL.

> +
> +	/*
> +	 * The headers must at least be aligned enough for counters
> +	 * to be accessed atomically.
> +	 */
> +	if (base1 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base1);
> +		return -EINVAL;
> +	}
> +
> +	if (base2 & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("IVC channel start not aligned: %lx\n", base2);
> +		return -EINVAL;
> +	}
> +
> +	if (frame_size & (TEGRA_IVC_ALIGN - 1)) {
> +		pr_err("frame size not adequately aligned: %zu\n", frame_size);
> +		return -EINVAL;
> +	}
> +
> +	if (base1 < base2) {
> +		if (base1 + frame_size * num_frames > base2) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base1, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	} else {
> +		if (base2 + frame_size * num_frames > base1) {
> +			pr_err("queue regions overlap: %lx + %zx, %zx\n",
> +			       base2, frame_size, frame_size * num_frames);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	return 0;
> +}
> +
> +int tegra_ivc_init(struct tegra_ivc *ivc, struct device *peer,
> +		   void __iomem *rx_virt, dma_addr_t rx_phys,
> +		   void __iomem *tx_virt, dma_addr_t tx_phys,
> +		   unsigned int num_frames, size_t frame_size,
> +		   void (*notify)(struct tegra_ivc *ivc, void *data),
> +		   void *data)
> +{
> +	size_t queue_size;
> +	int err;
> +
> +	err = check_ivc_params((unsigned long)rx_virt, (unsigned long)tx_virt,
> +			       num_frames, frame_size);
> +	if (err < 0)
> +		return err;
> +
> +	BUG_ON(!ivc);
> +	BUG_ON(!notify);
> +
> +	queue_size = tegra_ivc_total_queue_size(num_frames * frame_size);
> +
> +	/*
> +	 * All sizes that can be returned by communication functions should
> +	 * fit in an int.
> +	 */
> +	if (frame_size > INT_MAX)
> +		return -E2BIG;
> +
> +	ivc->rx.channel = (struct tegra_ivc_header *)rx_virt;
> +	ivc->tx.channel = (struct tegra_ivc_header *)tx_virt;

And these generate the sparse warnings ...

drivers/firmware/tegra/ivc.c:642:28: warning: cast removes address space
of expression
drivers/firmware/tegra/ivc.c:643:28: warning: cast removes address space
of expression

Cheers
Jon

-- 
nvpublic

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

* Re: [PATCH v3 00/12] Initial Tegra186 support
  2016-08-19 17:32 ` Thierry Reding
@ 2016-11-26 13:39     ` Pavel Machek
  -1 siblings, 0 replies; 104+ messages in thread
From: Pavel Machek @ 2016-11-26 13:39 UTC (permalink / raw)
  To: Thierry Reding
  Cc: Sivaram Nair, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Peter De Schrijver, Timo Alho, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

Hi!

> From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> 
> Hi everyone,
> 
> This is a set of patches to add initial support for Tegra186. It is
> based on Joseph's patches but I rewrote some of the drivers to be a
> little easier to comprehend and maintain (hopefully). I've also
> included clock and reset drivers as a proof of concept.

Is there any phone/tablet on the market with this chipset?

								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* [PATCH v3 00/12] Initial Tegra186 support
@ 2016-11-26 13:39     ` Pavel Machek
  0 siblings, 0 replies; 104+ messages in thread
From: Pavel Machek @ 2016-11-26 13:39 UTC (permalink / raw)
  To: linux-arm-kernel

Hi!

> From: Thierry Reding <treding@nvidia.com>
> 
> Hi everyone,
> 
> This is a set of patches to add initial support for Tegra186. It is
> based on Joseph's patches but I rewrote some of the drivers to be a
> little easier to comprehend and maintain (hopefully). I've also
> included clock and reset drivers as a proof of concept.

Is there any phone/tablet on the market with this chipset?

								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html

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

* Re: [PATCH v3 00/12] Initial Tegra186 support
  2016-11-26 13:39     ` Pavel Machek
@ 2016-11-28  7:33         ` Thierry Reding
  -1 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-11-28  7:33 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Sivaram Nair, devicetree-u79uwXL29TY76Z2rM5mHXA,
	Peter De Schrijver, Timo Alho, Joseph Lo,
	linux-tegra-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r

[-- Attachment #1: Type: text/plain, Size: 662 bytes --]

On Sat, Nov 26, 2016 at 02:39:27PM +0100, Pavel Machek wrote:
> Hi!
> 
> > From: Thierry Reding <treding-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
> > 
> > Hi everyone,
> > 
> > This is a set of patches to add initial support for Tegra186. It is
> > based on Joseph's patches but I rewrote some of the drivers to be a
> > little easier to comprehend and maintain (hopefully). I've also
> > included clock and reset drivers as a proof of concept.
> 
> Is there any phone/tablet on the market with this chipset?

I'm not aware of any. The chip is primarily targetted at automotive
use-cases and probably not suited for phones or tablets.

Thierry

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 801 bytes --]

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

* [PATCH v3 00/12] Initial Tegra186 support
@ 2016-11-28  7:33         ` Thierry Reding
  0 siblings, 0 replies; 104+ messages in thread
From: Thierry Reding @ 2016-11-28  7:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Sat, Nov 26, 2016 at 02:39:27PM +0100, Pavel Machek wrote:
> Hi!
> 
> > From: Thierry Reding <treding@nvidia.com>
> > 
> > Hi everyone,
> > 
> > This is a set of patches to add initial support for Tegra186. It is
> > based on Joseph's patches but I rewrote some of the drivers to be a
> > little easier to comprehend and maintain (hopefully). I've also
> > included clock and reset drivers as a proof of concept.
> 
> Is there any phone/tablet on the market with this chipset?

I'm not aware of any. The chip is primarily targetted at automotive
use-cases and probably not suited for phones or tablets.

Thierry
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 801 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20161128/602266c6/attachment-0001.sig>

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

end of thread, other threads:[~2016-11-28  7:33 UTC | newest]

Thread overview: 104+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2016-08-19 17:32 [PATCH v3 00/12] Initial Tegra186 support Thierry Reding
2016-08-19 17:32 ` Thierry Reding
     [not found] ` <20160819173233.13260-1-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-19 17:32   ` [PATCH v3 01/12] dt-bindings: mailbox: Add Tegra HSP binding Thierry Reding
2016-08-19 17:32     ` Thierry Reding
2016-08-19 17:32   ` [PATCH v3 02/12] mailbox: Add Tegra HSP driver Thierry Reding
2016-08-19 17:32     ` Thierry Reding
2016-08-22 13:43     ` Arnd Bergmann
2016-08-22 13:43       ` Arnd Bergmann
2016-08-22 14:17       ` Thierry Reding
2016-08-22 14:17         ` Thierry Reding
     [not found]         ` <20160822141728.GF17367-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-08-22 16:42           ` Stephen Warren
2016-08-22 16:42             ` Stephen Warren
     [not found]     ` <20160819173233.13260-3-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 16:53       ` Stephen Warren
2016-08-22 16:53         ` Stephen Warren
2016-08-23  0:06       ` Sivaram Nair
2016-08-23  0:06         ` Sivaram Nair
2016-08-23  0:12       ` Sivaram Nair
2016-08-23  0:12         ` Sivaram Nair
2016-08-19 17:32   ` [PATCH v3 03/12] dt-bindings: firmware: Add bindings for Tegra BPMP Thierry Reding
2016-08-19 17:32     ` Thierry Reding
2016-08-19 17:32   ` [PATCH v3 04/12] firmware: tegra: Add IVC library Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-5-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 10:46       ` Jon Hunter
2016-08-22 10:46         ` Jon Hunter
     [not found]         ` <90222c3a-7c69-6fa3-d161-4ed0c5759f34-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2016-08-22 12:40           ` Thierry Reding
2016-08-22 12:40             ` Thierry Reding
2016-08-22 18:49       ` Stephen Warren
2016-08-22 18:49         ` Stephen Warren
2016-08-24 15:13     ` Jon Hunter
2016-08-24 15:13       ` Jon Hunter
2016-08-19 17:32   ` [PATCH v3 05/12] firmware: tegra: Add BPMP support Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-6-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22  9:26       ` Jon Hunter
2016-08-22  9:26         ` Jon Hunter
     [not found]         ` <94227d94-1d60-fda7-731b-26656633d585-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2016-08-22 12:54           ` Thierry Reding
2016-08-22 12:54             ` Thierry Reding
     [not found]             ` <20160822125458.GC17367-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-08-22 14:24               ` Jon Hunter
2016-08-22 14:24                 ` Jon Hunter
     [not found]                 ` <6bb4d32f-4f13-285e-430e-672f375a9a46-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2016-08-22 15:00                   ` Thierry Reding
2016-08-22 15:00                     ` Thierry Reding
2016-08-22 18:51               ` Stephen Warren
2016-08-22 18:51                 ` Stephen Warren
2016-08-22 13:34       ` Arnd Bergmann
2016-08-22 13:34         ` Arnd Bergmann
2016-08-22 14:02         ` Thierry Reding
2016-08-22 14:02           ` Thierry Reding
     [not found]           ` <20160822140211.GE17367-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-08-22 14:42             ` Arnd Bergmann
2016-08-22 14:42               ` Arnd Bergmann
2016-08-22 15:32               ` Thierry Reding
2016-08-22 15:32                 ` Thierry Reding
     [not found]                 ` <20160822153258.GB21012-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-08-22 15:43                   ` Arnd Bergmann
2016-08-22 15:43                     ` Arnd Bergmann
2016-08-22 18:56               ` Stephen Warren
2016-08-22 18:56                 ` Stephen Warren
2016-08-23 14:58                 ` Arnd Bergmann
2016-08-23 14:58                   ` Arnd Bergmann
2016-08-23 23:26       ` Sivaram Nair
2016-08-23 23:26         ` Sivaram Nair
2016-08-22 22:23     ` Stephen Warren
2016-08-22 22:23       ` Stephen Warren
2016-08-19 17:32   ` [PATCH v3 06/12] soc/tegra: Add Tegra186 support Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-7-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 19:01       ` Stephen Warren
2016-08-22 19:01         ` Stephen Warren
2016-08-23 13:44       ` Jon Hunter
2016-08-23 13:44         ` Jon Hunter
2016-08-19 17:32   ` [PATCH v3 07/12] arm64: defconfig: Enable Tegra186 SoC Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-8-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 19:01       ` Stephen Warren
2016-08-22 19:01         ` Stephen Warren
2016-08-19 17:32   ` [PATCH v3 08/12] arm64: dts: tegra: Add Tegra186 support Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-9-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 17:11       ` Stephen Warren
2016-08-22 17:11         ` Stephen Warren
2016-08-22 19:07       ` Stephen Warren
2016-08-22 19:07         ` Stephen Warren
2016-08-19 17:32   ` [PATCH v3 09/12] arm64: dts: tegra: Add NVIDIA P3310 main board support Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-10-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 19:08       ` Stephen Warren
2016-08-22 19:08         ` Stephen Warren
2016-08-23 17:35       ` Jon Hunter
2016-08-23 17:35         ` Jon Hunter
2016-08-19 17:32   ` [PATCH v3 10/12] arm64: dts: tegra: Add NVIDIA P2771 " Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-11-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 19:11       ` Stephen Warren
2016-08-22 19:11         ` Stephen Warren
2016-08-19 17:32   ` [PATCH v3 11/12] clk: tegra: Add BPMP clock driver Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-12-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 10:11       ` Jon Hunter
2016-08-22 10:11         ` Jon Hunter
     [not found]         ` <0d7080bc-9e82-75dd-7169-0a5b7429801e-DDmLM1+adcrQT0dZR+AlfA@public.gmane.org>
2016-08-22 13:28           ` Thierry Reding
2016-08-22 13:28             ` Thierry Reding
     [not found]             ` <20160822132833.GD17367-EkSeR96xj6Pcmrwk2tT4+A@public.gmane.org>
2016-08-23 13:49               ` Jon Hunter
2016-08-23 13:49                 ` Jon Hunter
2016-08-22 19:47       ` Stephen Warren
2016-08-22 19:47         ` Stephen Warren
2016-08-19 17:32   ` [PATCH v3 12/12] reset: Add Tegra BPMP reset driver Thierry Reding
2016-08-19 17:32     ` Thierry Reding
     [not found]     ` <20160819173233.13260-13-thierry.reding-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
2016-08-22 19:56       ` Stephen Warren
2016-08-22 19:56         ` Stephen Warren
2016-11-26 13:39   ` [PATCH v3 00/12] Initial Tegra186 support Pavel Machek
2016-11-26 13:39     ` Pavel Machek
     [not found]     ` <20161126133927.GE20568-5NIqAleC692hcjWhqY66xCZi+YwRKgec@public.gmane.org>
2016-11-28  7:33       ` Thierry Reding
2016-11-28  7:33         ` Thierry Reding

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.