All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:28   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:28 UTC (permalink / raw)
  To: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: andy.green-QSEj5FYQhm4dnm+yROfE0A, arnd-r2nGTMty4D4,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	olof-nZhT3qVonbNeoWH0uzbU5w, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, patches-QSEj5FYQhm4dnm+yROfE0A,
	Mollie Wu, Tetsuya Takinishi

The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
And the remote master firmware (called SCB) running on CM3. Linux asks
for things to be done over Mailbox API, to SCB which controls most of
the important things. variations S70 & S73 are supported.

Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Olof <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>
Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 .../bindings/arm/fujistu/power_domain.txt          |  22 +
 arch/arm/Kconfig                                   |   2 +
 arch/arm/Makefile                                  |   1 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
 arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
 arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
 arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
 arch/arm/configs/fujitsu_defconfig                 | 156 ++++
 arch/arm/mach-mb86s7x/Kconfig                      |  18 +
 arch/arm/mach-mb86s7x/Makefile                     |   2 +
 arch/arm/mach-mb86s7x/board.c                      |  65 ++
 arch/arm/mach-mb86s7x/iomap.h                      |  34 +
 arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
 arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
 arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
 include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
 17 files changed, 3183 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
 create mode 100644 arch/arm/boot/dts/mb86s70.dtsi
 create mode 100644 arch/arm/boot/dts/mb86s70eb.dts
 create mode 100644 arch/arm/boot/dts/mb86s73.dtsi
 create mode 100644 arch/arm/boot/dts/mb86s73eb.dts
 create mode 100644 arch/arm/configs/fujitsu_defconfig
 create mode 100644 arch/arm/mach-mb86s7x/Kconfig
 create mode 100644 arch/arm/mach-mb86s7x/Makefile
 create mode 100644 arch/arm/mach-mb86s7x/board.c
 create mode 100644 arch/arm/mach-mb86s7x/iomap.h
 create mode 100644 arch/arm/mach-mb86s7x/mcpm.c
 create mode 100644 arch/arm/mach-mb86s7x/pm_domains.c
 create mode 100644 arch/arm/mach-mb86s7x/scb_mhu.c
 create mode 100644 include/linux/platform_data/mb86s7x_mbox.h

diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
new file mode 100644
index 0000000..44abfe8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
@@ -0,0 +1,22 @@
+* Fujitsu MB86S7x Power Domains
+
+Remote f/w on MB86S7x can enable/disable power to various IPs.
+
+Required Properties:
+- compatible: Should be "fujitsu,mb86s7x-pd"
+- index: Index of the power gate control for the block
+
+Example:
+
+	pd_cpu: genpd@3 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <3>;
+	};
+
+Example of the node using power domain:
+
+	node {
+		/* ... */
+		fujitsu,power-domain = <&pd_cpu>;
+		/* ... */
+	};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 245058b..44fd319 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -955,6 +955,8 @@ source "arch/arm/mach-kirkwood/Kconfig"
 
 source "arch/arm/mach-ks8695/Kconfig"
 
+source "arch/arm/mach-mb86s7x/Kconfig"
+
 source "arch/arm/mach-msm/Kconfig"
 
 source "arch/arm/mach-moxart/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6721fab..d6ec5cd 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_KEYSTONE)		+= keystone
 machine-$(CONFIG_ARCH_KIRKWOOD)		+= kirkwood
 machine-$(CONFIG_ARCH_KS8695)		+= ks8695
 machine-$(CONFIG_ARCH_LPC32XX)		+= lpc32xx
+machine-$(CONFIG_ARCH_MB86S7X)		+= mb86s7x
 machine-$(CONFIG_ARCH_MMP)		+= mmp
 machine-$(CONFIG_ARCH_MOXART)		+= moxart
 machine-$(CONFIG_ARCH_MSM)		+= msm
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index adb5ed9..0c8addb 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -154,6 +154,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += $(kirkwood)
 dtb-$(CONFIG_MACH_KIRKWOOD) += $(kirkwood)
 dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
 dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
+dtb-$(CONFIG_ARCH_MB86S7X) += mb86s70eb.dtb mb86s73eb.dtb
 dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
 dtb-$(CONFIG_ARCH_MXC) += \
 	imx25-eukrea-mbimxsd25-baseboard.dtb \
diff --git a/arch/arm/boot/dts/mb86s70.dtsi b/arch/arm/boot/dts/mb86s70.dtsi
new file mode 100644
index 0000000..b6d8970
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s70.dtsi
@@ -0,0 +1,635 @@
+
+/dts-v1/;
+
+/ {
+	model = "Fujitsu mb86s70";
+	compatible = "fujitsu,mb86s70";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <1>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x100>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000  900000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x101>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000  900000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu2: cpu@2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x0>;
+			cci-control-port = <&cci_control4>;
+			clock-frequency = <1000000000>;
+			operating-points = <
+				/* kHz    uV */
+				1200000 900000
+				1600000 1000000
+				2000000 1100000
+				2400000 1200000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu3: cpu@3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x1>;
+			cci-control-port = <&cci_control4>;
+			clock-frequency = <1000000000>;
+			operating-points = <
+				/* kHz    uV */
+				1200000 900000
+				1600000 1000000
+				2000000 1100000
+				2400000 1200000
+			>;
+			clock-latency = <100000>;
+		};
+	};
+
+	cci@2c090000 {
+		compatible = "arm,cci-400";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0 0x2c090000 0x1000>;
+		ranges = <0x0 0x0 0x2c090000 0x10000>;
+
+		cci_control3: slave-if@4000 {
+			compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x4000 0x1000>;
+		};
+
+		cci_control4: slave-if@5000 {
+			compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x5000 0x1000>;
+		};
+
+		pmu@9000 {
+			compatible = "arm,cci-400-pmu";
+			reg = <0x9000 0x5000>;
+			interrupts = <0 77 4>,
+					<0 77 4>,
+					<0 77 4>,
+					<0 77 4>,
+					<0 77 4>;
+		};
+	};
+
+	/**
+	 * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
+	 * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+	 */
+	clocks {
+		clk_alw_0_0: clk_alw_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_alw_0_1: clk_alw_0_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <1>;
+		};
+
+		clk_alw_0_2: clk_alw_0_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <2>;
+		};
+
+		clk_alw_0_4: clk_alw_0_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <4>;
+		};
+
+		clk_alw_0_5: clk_alw_0_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <5>;
+		};
+
+		clk_alw_0_8: clk_alw_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_alw_1_0: clk_alw_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_alw_1_1: clk_alw_1_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <1>;
+		};
+
+		clk_alw_1_8: clk_alw_1_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <8>;
+		};
+
+		clk_alw_2_0: clk_alw_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_alw_2_1: clk_alw_2_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <1>;
+		};
+
+		clk_alw_2_2: clk_alw_2_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <2>;
+		};
+
+		clk_alw_2_4: clk_alw_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_alw_2_5: clk_alw_2_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <5>;
+		};
+
+		clk_alw_2_8: clk_alw_2_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <8>;
+		};
+
+		clk_alw_6_8: clk_alw_6_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <6>;
+			port = <8>;
+		};
+
+		clk_alw_7_0: clk_alw_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_alw_8_0: clk_alw_8_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <8>;
+			port = <0>;
+		};
+
+		clk_alw_a_0: clk_alw_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <0>;
+		};
+
+		clk_alw_a_1: clk_alw_a_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <1>;
+		};
+
+		clk_ddr3_0_0: clk_ddr3_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <1>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_0: clk_main_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_8: clk_main_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_main_1_3: clk_main_1_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <3>;
+		};
+
+		clk_main_1_4: clk_main_1_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <4>;
+		};
+
+		clk_main_2_0: clk_main_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_main_2_3: clk_main_2_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <3>;
+		};
+
+		clk_main_2_7: clk_main_2_7 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <7>;
+		};
+
+		clk_main_3_0: clk_main_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_main_3_3: clk_main_3_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <3>;
+		};
+
+		clk_main_3_4: clk_main_3_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <4>;
+		};
+
+		clk_main_3_5: clk_main_3_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <5>;
+		};
+
+		clk_main_3_6: clk_main_3_6 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <6>;
+		};
+
+		clk_main_4_0: clk_main_4_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <0>;
+		};
+
+		clk_main_4_1: clk_main_4_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <1>;
+		};
+
+		clk_main_5_0: clk_main_5_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <0>;
+		};
+
+		clk_main_5_3: clk_main_5_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <3>;
+		};
+
+		clk_main_5_4: clk_main_5_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <4>;
+		};
+
+		clk_main_5_5: clk_main_5_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <5>;
+		};
+
+		clk_main_7_0: clk_main_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_main_8_1: clk_main_8_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <8>;
+			port = <1>;
+		};
+
+		clk_main_9_0: clk_main_9_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <9>;
+			port = <0>;
+		};
+
+		clk_hdmi_0_0: clk_hdmi_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_hdmi_1_0: clk_hdmi_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_hdmi_2_0: clk_hdmi_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_hdmi_3_0: clk_hdmi_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_dphy_0_0: clk_dphy_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_dphy_1_0: clk_dphy_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <1>;
+			port = <0>;
+		};
+	};
+
+	gic: interrupt-controller@2c001000 {
+		compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0 0x2c001000 0x1000>,
+		      <0 0x2c002000 0x1000>,
+		      <0 0x2c004000 0x2000>,
+		      <0 0x2c006000 0x2000>;
+		interrupts = <1 9 0xf04>;
+	};
+
+	timer0: timer0@31080000 {
+		compatible = "arm,sp804";
+		reg = <0 0x31080000 0x10000>;
+		interrupts = <0 324 4>,
+			     <0 325 4>;
+		clocks = <&clk_alw_6_8>;
+	};
+
+	mhu: mhu0@2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+	};
+
+	mhu_client: scb@0 {
+		compatible = "fujitsu,scb";
+		mbox = <&mhu 1>;
+		mbox-names = "HP_NonSec";
+	};
+
+	pinctrl: pinctrl@2a4d0000 {
+		compatible = "fujitsu,mb86s70-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>;
+		#gpio-range-cells = <3>;
+	};
+
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	gpio1: mb86s70_gpio1 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31010000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 32 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	uart0: serial@0x31040000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31040000 0x100>;
+		interrupts = <0 320 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	uart1: serial@0x31050000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31050000 0x100>;
+		interrupts = <0 321 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	uart2: serial@0x31060000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31060000 0x100>;
+		interrupts = <0 322 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	sdhci0: emmc@300c0000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x300c0000 0x1000>;
+		interrupts = <0 164 0x4>,
+			     <0 165 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		bus-width = <8>;
+		clocks = <&clk_alw_1_8>, <&clk_alw_6_8>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
+
+	sdhci1: sdio@36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		pwr-mux-gpios = <&gpio0 7 0>;
+		clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
+
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>,
+			<0 0x31618000 0x4000>,
+			<0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy@1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s70eb.dts b/arch/arm/boot/dts/mb86s70eb.dts
new file mode 100644
index 0000000..ee25dd9
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s70eb.dts
@@ -0,0 +1,38 @@
+
+/include/ "mb86s70.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	model = "Fujitsu MB86S70 EVB";
+	compatible = "fujitsu,mb86s70-evb";
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000 0x80000000>, <0x08 0x80000000 0x80000000>;
+
+	};
+
+	chosen {
+		bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait rw";
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		d3 {
+			label = "led3";
+			gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "default-on";
+		};
+		d4 {
+			label = "led4";
+			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "mmc1";
+		};
+		d5 {
+			label = "led5";
+			gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s73.dtsi b/arch/arm/boot/dts/mb86s73.dtsi
new file mode 100644
index 0000000..ef4f5a0
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s73.dtsi
@@ -0,0 +1,910 @@
+
+/ {
+	model = "Fujitsu mb86s73";
+	compatible = "fujitsu,mb86s73";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <1>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu@0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x100>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000   900000
+				1200000  1000000
+			>;
+			clock-latency = <100000>;
+			fujitsu,power-domain = <&pd_cpu>;
+		};
+
+		cpu1: cpu@1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x101>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000   900000
+				1200000  1000000
+			>;
+			clock-latency = <100000>;
+			fujitsu,power-domain = <&pd_cpu>;
+		};
+	};
+
+	cci@2c090000 {
+		compatible = "arm,cci-400";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0 0x2c090000 0x1000>;
+		ranges = <0x0 0x0 0x2c090000 0x10000>;
+		fujitsu,power-domain = <&pd_offchip>;
+
+		cci_control3: slave-if@4000 {
+		compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x4000 0x1000>;
+		};
+	};
+
+	/**
+	 * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA7, 4->USB, 5->FPDLINK
+	 * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+	 */
+	clocks {
+		clk_alw_0_0: clk_alw_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_alw_0_1: clk_alw_0_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <1>;
+		};
+
+		clk_alw_0_2: clk_alw_0_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <2>;
+		};
+
+		clk_alw_0_4: clk_alw_0_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <4>;
+		};
+
+		clk_alw_0_5: clk_alw_0_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <5>;
+		};
+
+		clk_alw_0_8: clk_alw_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_alw_1_0: clk_alw_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_alw_1_1: clk_alw_1_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <1>;
+		};
+
+		clk_alw_1_2: clk_alw_1_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <2>;
+		};
+
+		clk_alw_1_8: clk_alw_1_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <8>;
+		};
+
+		clk_alw_2_0: clk_alw_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_alw_2_1: clk_alw_2_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <1>;
+		};
+
+		clk_alw_2_2: clk_alw_2_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <2>;
+		};
+
+		clk_alw_2_4: clk_alw_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_alw_2_5: clk_alw_2_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <5>;
+		};
+
+		clk_alw_2_8: clk_alw_2_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <8>;
+		};
+
+		clk_alw_6_8: clk_alw_6_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <6>;
+			port = <8>;
+		};
+
+		clk_alw_7_0: clk_alw_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_alw_8_0: clk_alw_8_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <8>;
+			port = <0>;
+		};
+
+		clk_alw_a_0: clk_alw_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <0>;
+		};
+
+		clk_alw_a_1: clk_alw_a_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <1>;
+		};
+
+		clk_alw_b_0: clk_alw_b_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0b>;
+			port = <0>;
+		};
+
+		clk_alw_c_0: clk_alw_c_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0c>;
+			port = <0>;
+		};
+
+		clk_ddr3_0_0: clk_ddr3_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <1>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_0: clk_main_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_8: clk_main_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_main_1_3: clk_main_1_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <3>;
+		};
+
+		clk_main_1_4: clk_main_1_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <4>;
+		};
+
+		clk_main_2_0: clk_main_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_main_2_3: clk_main_2_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <3>;
+		};
+
+		clk_main_2_4: clk_main_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_main_2_7: clk_main_2_7 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <7>;
+		};
+
+		clk_main_3_0: clk_main_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_main_3_3: clk_main_3_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <3>;
+		};
+
+		clk_main_3_4: clk_main_3_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <4>;
+		};
+
+		clk_main_3_5: clk_main_3_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <5>;
+		};
+
+		clk_main_3_6: clk_main_3_6 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <6>;
+		};
+
+		clk_main_4_0: clk_main_4_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <0>;
+		};
+
+		clk_main_4_4: clk_main_4_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <4>;
+		};
+
+		clk_main_4_5: clk_main_4_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <5>;
+		};
+
+		clk_main_5_0: clk_main_5_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <0>;
+		};
+
+		clk_main_5_3: clk_main_5_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <3>;
+		};
+
+		clk_main_5_4: clk_main_5_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <4>;
+		};
+
+		clk_main_5_5: clk_main_5_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <5>;
+		};
+
+		clk_main_7_0: clk_main_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_main_8_1: clk_main_8_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <8>;
+			port = <1>;
+		};
+
+		clk_main_9_0: clk_main_9_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <9>;
+			port = <0>;
+		};
+
+		clk_main_a_0: clk_main_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xa>;
+			port = <0>;
+		};
+
+		clk_main_b_0: clk_main_b_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xb>;
+			port = <0>;
+		};
+
+		clk_main_c_0: clk_main_c_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xc>;
+			port = <0>;
+		};
+
+		clk_main_d_0: clk_main_d_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xd>;
+			port = <0>;
+		};
+
+		clk_usb_0_0: clk_usb_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_usb_1_0: clk_usb_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_usb_2_0: clk_usb_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_usb_3_0: clk_usb_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_fpdlink_0_0: clk_fpdlink_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_fpdlink_1_0: clk_fpdlink_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <1>;
+			port = <0>;
+		};
+	};
+
+	timer0: timer0@31080000 {
+		compatible = "arm,sp804";
+		reg = <0 0x31080000 0x10000>;
+		interrupts = <0 324 4>,
+			     <0 325 4>;
+		clocks = <&clk_alw_6_8>;
+	};
+
+	timer1: archtimer {
+		compatible = "arm,armv7-timer";
+		clock-frequency = <125000000>;
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+
+	pinctrl: pinctrl@2a4d0000 {
+		compatible = "fujitsu,mb86s73-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
+		#gpio-range-cells = <3>;
+
+		pcie0_pins_active: pcie0_active {
+			mb86s7x,function = "pcie0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pcie0_pins_sleep: pcie0_sleep {
+			mb86s7x,function = "pcie0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb3h0_pins_active: usb3h0_active {
+			mb86s7x,function = "usb3h0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb3h0_pins_sleep: usb3h0_sleep {
+			mb86s7x,function = "usb3h0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb2h0_pins_active: usb2h0_active {
+			mb86s7x,function = "usb2h0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb2h0_pins_sleep: usb2h0_sleep {
+			mb86s7x,function = "usb2h0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb2d0_pins_active: usb2d0_active {
+			mb86s7x,function = "usb2d0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb2d0_pins_sleep: usb2d0_sleep {
+			mb86s7x,function = "usb2d0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		sdh30_pins_active: sdh30_active {
+			mb86s7x,function = "sdh30";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		sdh30_pins_sleep: sdh30_sleep {
+			mb86s7x,function = "sdh30";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		emmc0_pins_active: emmc0_active {
+			mb86s7x,function = "emmc0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		emmc0_pins_sleep: emmc0_sleep {
+			mb86s7x,function = "emmc0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		hsspi0_pins_active: hsspi0_active {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <8>; /* in mA */
+		};
+		hsspi0_pins_sleep: hsspi0_sleep {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		gmacd0_pins_active: gmacd0_active {
+			mb86s7x,function = "gmacd0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		gmacd0_pins_sleep: gmacd0_sleep {
+			mb86s7x,function = "gmacd0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		gmacm0_pins_active: gmacm0_active {
+			mb86s7x,function = "gmacm0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		gmacm0_pins_sleep: gmacm0_sleep {
+			mb86s7x,function = "gmacm0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		i2s0_pins_active: i2s0_active {
+			mb86s7x,function = "i2s0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		i2s0_pins_sleep: i2s0_sleep {
+			mb86s7x,function = "i2s0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		other0_pins_active: other0_active {
+			mb86s7x,function = "other0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		other0_pins_sleep: other0_sleep {
+			mb86s7x,function = "other0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		jtag0_pins_active: jtag0_active {
+			mb86s7x,function = "jtag0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		jtag0_pins_sleep: jtag0_sleep {
+			mb86s7x,function = "jtag0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		pcie1_pins_active: pcie1_active {
+			mb86s7x,function = "pcie1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pcie1_pins_sleep: pcie1_sleep {
+			mb86s7x,function = "pcie1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb3h1_pins_active: usb3h1_active {
+			mb86s7x,function = "usb3h1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb3h1_pins_sleep: usb3h1_sleep {
+			mb86s7x,function = "usb3h1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		cfg_pins_active: cfg_active {
+			mb86s7x,function = "cfg";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		cfg_pins_sleep: cfg_sleep {
+			mb86s7x,function = "cfg";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart0_pins_active: uart0_active {
+			mb86s7x,function = "uart0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart0_pins_sleep: uart0_sleep {
+			mb86s7x,function = "uart0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart1_pins_active: uart1_active {
+			mb86s7x,function = "uart1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart1_pins_sleep: uart1_sleep {
+			mb86s7x,function = "uart1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart2_pins_active: uart2_active {
+			mb86s7x,function = "uart2";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart2_pins_sleep: uart2_sleep {
+			mb86s7x,function = "uart2";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		trace_pins_active: trace_active {
+			mb86s7x,function = "trace";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		trace_pins_sleep: trace_sleep {
+			mb86s7x,function = "trace";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		pl244_pins_active: pl244_active {
+			mb86s7x,function = "pl244";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pl244_pins_sleep: pl244_sleep {
+			mb86s7x,function = "pl244";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		smt_pins_active: smt_active {
+			mb86s7x,function = "smt";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		smt_pins_sleep: smt_sleep {
+			mb86s7x,function = "smt";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		memcs_pins_active: memcs_active {
+			mb86s7x,function = "memcs";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		memcs_pins_sleep: memcs_sleep {
+			mb86s7x,function = "memcs";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+	};
+
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	gpio1: mb86s70_gpio1 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31010000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 32 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	pd_alwayson: genpd@0 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <0>;
+	};
+
+	pd_default: genpd@1 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <1>;
+	};
+
+	pd_offchip: genpd@2 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <2>;
+	};
+
+	pd_cpu: genpd@3 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <3>;
+	};
+
+	gic: interrupt-controller@2c001000 {
+		compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0 0x2c001000 0x1000>,
+		      <0 0x2c002000 0x1000>,
+		      <0 0x2c004000 0x2000>,
+		      <0 0x2c006000 0x2000>;
+		interrupts = <1 9 0xf04>;
+	};
+
+	pmu_a7 {
+		compatible = "arm,cortex-a7-pmu";
+		interrupts = <0 18 4>,
+			     <0 22 4>;
+		fujitsu,power-domain = <&pd_cpu>;
+	};
+
+	mhu: mhu0@2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+		fujitsu,power-domain = <&pd_default>;
+	};
+
+	mhu_client: scb@0 {
+		compatible = "fujitsu,scb";
+		mbox = <&mhu 1>;
+		mbox-names = "HP_NonSec";
+	};
+
+	uart0: serial@0x31040000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31040000 0x100>;
+		interrupts = <0 320 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart0_pins_active>;
+		pinctrl-1 = <&uart0_pins_sleep>;
+	};
+
+	uart1: serial@0x31050000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31050000 0x100>;
+		interrupts = <0 321 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart1_pins_active>;
+		pinctrl-1 = <&uart1_pins_sleep>;
+	};
+
+	uart2: serial@0x31060000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31060000 0x100>;
+		interrupts = <0 322 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart2_pins_active>;
+		pinctrl-1 = <&uart2_pins_sleep>;
+	};
+
+	sdhci0: emmc@300c0000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x300c0000 0x1000>;
+		interrupts = <0 164 0x4>,
+			     <0 165 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		bus-width = <8>;
+		clocks = <&clk_alw_c_0>, <&clk_alw_b_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+		fujitsu,power-domain = <&pd_default>;
+	};
+
+	sdhci1: sdio@36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		clocks = <&clk_main_c_0>, <&clk_main_d_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+		resume-detect-retry;
+		fujitsu,power-domain = <&pd_offchip>;
+	};
+
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+		fujitsu,power-domain = <&pd_default>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy@1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s73eb.dts b/arch/arm/boot/dts/mb86s73eb.dts
new file mode 100644
index 0000000..81862d9
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s73eb.dts
@@ -0,0 +1,73 @@
+
+/include/ "mb86s73.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+	model = "Fujitsu MB86S73 EVB";
+	compatible = "fujitsu,mb86s73-evb";
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000 0x80000000>;
+	};
+
+	chosen {
+		bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw";
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		d34_a {
+			label = "led34a";
+			gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "heartbeat";
+		};
+		d34_b {
+			label = "led34b";
+			gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "mmc1";
+		};
+		d37 {
+			label = "led37";
+			gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "default-on";
+		};
+		d38 {
+			label = "led38";
+			gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys-polled";
+		poll-interval = <20>;
+		home {
+			label = "Home";
+			gpios = <&gpio1 4 0>;
+			linux,code = <KEY_HOME>;
+			gpio-key;
+		};
+		power {
+			label = "Power";
+			gpios = <&gpio1 5 0>;
+			linux,code = <KEY_POWER>;
+			gpio-key;
+		};
+
+		up {
+			label = "Up";
+			gpios = <&gpio1 6 0>;
+			linux,code = <KEY_UP>;
+			gpio-key;
+		};
+		down {
+			label = "Down";
+			gpios = <&gpio1 7 0>;
+			linux,code = <KEY_DOWN>;
+			gpio-key;
+		};
+	};
+};
diff --git a/arch/arm/configs/fujitsu_defconfig b/arch/arm/configs/fujitsu_defconfig
new file mode 100644
index 0000000..bfc78c9
--- /dev/null
+++ b/arch/arm/configs/fujitsu_defconfig
@@ -0,0 +1,156 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_DEFAULT_HOSTNAME="MB86S7x"
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_NAMESPACES=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_RD_XZ=y
+CONFIG_RD_LZO=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EXPERT=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_ARCH_MB86S7X=y
+CONFIG_ARM_LPAE=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_754327=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_ARM_ERRATA_798181=y
+CONFIG_PCI=y
+CONFIG_PCIEASPM_POWERSAVE=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_BL_SWITCHER=y
+CONFIG_BL_SWITCHER_DUMMY_IF=m
+CONFIG_VMSPLIT_2G=y
+CONFIG_PREEMPT=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+# CONFIG_ATAGS is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=2048M earlycon=ttyS0,115200 earlyprintk console=ttyS0,115200 console=tty0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+CONFIG_KEXEC=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_IPV6_SIT=m
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER_USER_HELPER is not set
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=192
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=1024
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+CONFIG_INPUT_EVDEV=m
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO_POLLED=y
+# CONFIG_MOUSE_PS2 is not set
+CONFIG_MOUSE_GPIO=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_HID is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_F_SDH30=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=m
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_MAILBOX=y
+CONFIG_MBOX_F_MHU=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_VFAT_FS=m
+CONFIG_TMPFS=y
+CONFIG_CONFIGFS_FS=m
+CONFIG_CRAMFS=y
+CONFIG_ROMFS_FS=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_RCU_CPU_STALL_VERBOSE is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_LL is not set
+CONFIG_DEBUG_LL_UART_8250=y
+CONFIG_DEBUG_UART_PHYS=0x31040000
+CONFIG_DEBUG_UART_VIRT=0xfe000000
+CONFIG_DEBUG_UART_8250_WORD=y
+# CONFIG_EARLY_PRINTK is not set
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
new file mode 100644
index 0000000..44f5b0c
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/Kconfig
@@ -0,0 +1,18 @@
+config ARCH_MB86S7X
+	bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
+	select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+	select ARM_AMBA
+	select ARM_GIC
+	select ARM_TIMER_SP804
+	select HAVE_ARM_ARCH_TIMER
+	select ARCH_REQUIRE_GPIOLIB
+	select ARCH_HAS_CPUFREQ
+	select ARCH_HAS_OPP
+	select PM_OPP
+	select PINCTRL
+	select PINCTRL_MB86S7X
+	select ARM_CCI
+	select BIG_LITTLE
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  Support for Fujitsu MB86S7x based platforms
diff --git a/arch/arm/mach-mb86s7x/Makefile b/arch/arm/mach-mb86s7x/Makefile
new file mode 100644
index 0000000..5524a6c
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/Makefile
@@ -0,0 +1,2 @@
+obj-y		+= board.o scb_mhu.o mcpm.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
new file mode 100644
index 0000000..d6e76ec
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/board.c
@@ -0,0 +1,65 @@
+/*
+ * Support for the Fujitsu's MB86S7x based devices.
+ *
+ * Copyright (C) 2014 Linaro, LTD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+
+#include <asm/mcpm.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+#include "iomap.h"
+
+bool __init mb86s7x_smp_init_ops(void)
+{
+	struct device_node *node;
+
+	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
+	if (node && of_device_is_available(node)) {
+		mcpm_smp_set_ops();
+		return true;
+	}
+
+	return false;
+}
+
+#define IOMAP_DEV(name) { \
+		.virtual = (unsigned long) MB86S7X_##name##_VIRT, \
+		.pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
+		.length = MB86S7X_##name##_SIZE, \
+		.type = MT_DEVICE, \
+	}
+
+static struct map_desc mb86s7x_io_desc[] = {
+	IOMAP_DEV(MHU),
+	IOMAP_DEV(ISRAM),
+};
+
+void __init mb86s7x_dt_map_io(void)
+{
+	iotable_init(mb86s7x_io_desc, ARRAY_SIZE(mb86s7x_io_desc));
+}
+
+static const char *mb86s7x_dt_match[] __initconst = {
+	"fujitsu,mb86s70-evb",
+	"fujitsu,mb86s73-evb",
+	NULL,
+};
+
+DT_MACHINE_START(MB86S7X_DT, "Fujitsu MB86S7X-based board")
+	.dt_compat	= mb86s7x_dt_match,
+	.smp_init	= smp_init_ops(mb86s7x_smp_init_ops),
+	.map_io		= mb86s7x_dt_map_io,
+MACHINE_END
diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
new file mode 100644
index 0000000..2b8db1d
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/iomap.h
@@ -0,0 +1,34 @@
+/*
+ * arch/arm/mach-mb86s7x/iomap.h
+ *
+ * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MB86S7X_IOMAP_H
+#define __MB86S7X_IOMAP_H
+
+#include <linux/sizes.h>
+
+#define SEC_RSTADDR_OFF		0x3fc
+
+#define MB86S7X_MHU_PHYS	0x2b1f0000
+#define MB86S7X_MHU_SIZE	SZ_4K
+
+/* Internal SRAM */
+#define MB86S7X_ISRAM_PHYS	0x2e000000
+#define MB86S7X_ISRAM_SIZE	SZ_16K
+#define MB86S7X_TRAMPOLINE_PHYS	(MB86S7X_ISRAM_PHYS + 0x3c00)
+
+#define MB86S7X_ISRAM_VIRT	IOMEM(0xfe000000)
+#define MB86S7X_MHU_VIRT	(MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
+
+/* Non-Secure High-Priority Channel is used */
+#define MB86S7X_SHM_FROM_SCB_VIRT	(MB86S7X_ISRAM_VIRT + 0x3800)
+#define MB86S7X_TRAMPOLINE_VIRT		(MB86S7X_ISRAM_VIRT + 0x3c00)
+
+#endif /* __MB86S7X_IOMAP_H */
diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
new file mode 100644
index 0000000..86d223f
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/mcpm.c
@@ -0,0 +1,293 @@
+/*
+ * arch/arm/mach-mb86s7x/mcpm.c
+ *
+ * "Inspired" by tc_pm.c
+ * Copyright:	(C) 2013-2014  Fujitsu Semiconductor Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/arm-cci.h>
+#include <linux/spinlock.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#include <asm/mcpm.h>
+#include <asm/cp15.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+
+#include "iomap.h"
+
+static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int mb86s7x_pm_use_count[2][2];
+
+#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
+
+static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
+{
+	u8 val;
+
+	if (clr & ~AT_WFI_COLOR_MASK)
+		return;
+
+	val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
+	val &= ~AT_WFI_COLOR_MASK;
+	val |= clr;
+	writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
+}
+
+static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
+{
+	struct completion got_rsp;
+	int ret = 0;
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+
+	arch_spin_lock(&mb86s7x_pm_lock);
+
+	mb86s7x_pm_use_count[cpu][cluster]++;
+
+	if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
+		struct mb86s7x_cpu_gate cmd;
+
+		arch_spin_unlock(&mb86s7x_pm_lock);
+		cmd.payload_size = sizeof(cmd);
+		cmd.cluster_class = 0;
+		cmd.cluster_id = cluster;
+		cmd.cpu_id = cpu;
+		cmd.cpu_state = SCB_CPU_STATE_ON;
+
+		pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
+			 __func__, __LINE__, cmd.cluster_class,
+			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
+
+		init_completion(&got_rsp);
+		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
+		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed!\n", __func__, __LINE__);
+			return ret;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+
+		pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
+			 __func__, __LINE__, cmd.cluster_class,
+			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
+
+		if (cmd.cpu_state != SCB_CPU_STATE_ON)
+			return -ENODEV;
+
+	} else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
+		/*
+		 * The only possible values are:
+		 * 0 = CPU down
+		 * 1 = CPU (still) up
+		 * 2 = CPU requested to be up before it had a chance
+		 *     to actually make itself down.
+		 * Any other value is a bug.
+		 */
+		BUG();
+	}
+
+	arch_spin_unlock(&mb86s7x_pm_lock);
+
+	return 0;
+}
+
+static void mb86s7x_pm_suspend(u64 ignored)
+{
+	unsigned int mpidr, cpu, cluster;
+	bool last_man = false, skip_wfi = false;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	__mcpm_cpu_going_down(cpu, cluster);
+
+	arch_spin_lock(&mb86s7x_pm_lock);
+	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+
+	mb86s7x_pm_use_count[cpu][cluster]--;
+
+	if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
+		if (!mb86s7x_pm_use_count[0][cluster] &&
+		    !mb86s7x_pm_use_count[1][cluster])
+			last_man = true;
+		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
+	} else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
+		skip_wfi = true; /* Overtaken by a power up */
+	} else {
+		BUG();
+	}
+
+	if (!skip_wfi)
+		gic_cpu_if_down();
+
+	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
+		arch_spin_unlock(&mb86s7x_pm_lock);
+
+		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
+			/*
+			 * On the Cortex-A15 we need to disable
+			 * L2 prefetching before flushing the cache.
+			 */
+			asm volatile(
+			"mcr p15, 1, %0, c15, c0, 3\n\t"
+			"isb\n\t"
+			"dsb"
+			: : "r" (0x400));
+		}
+
+		v7_exit_coherency_flush(all);
+
+		cci_disable_port_by_cpu(mpidr);
+
+		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
+	} else {
+		arch_spin_unlock(&mb86s7x_pm_lock);
+		v7_exit_coherency_flush(louis);
+	}
+
+	__mcpm_cpu_down(cpu, cluster);
+
+	/* Now we are prepared for power-down, do it: */
+	if (!skip_wfi)
+		wfi();
+
+	/* Not dead at this point?  Let our caller cope. */
+}
+
+static void mb86s7x_pm_power_down(void)
+{
+	mb86s7x_pm_suspend(0);
+}
+
+static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
+{
+	struct mb86s7x_cpu_gate cmd;
+	struct completion got_rsp;
+	int i, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cluster_class = 0;
+	cmd.cluster_id = cluster;
+	cmd.cpu_id = cpu;
+	cmd.cpu_state = SCB_CPU_STATE_ON;
+
+	for (i = 0; i < 50; i++) {
+		init_completion(&got_rsp);
+		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed to get CPU status\n",
+			       __func__, __LINE__);
+			return -ETIMEDOUT;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+
+		pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
+			 __func__, __LINE__,
+			 cmd.cluster_class, cmd.cluster_id,
+			 cmd.cpu_id, cmd.cpu_state);
+
+		if (cmd.cpu_state == SCB_CPU_STATE_OFF)
+			return 0;
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
+	.power_up		= mb86s7x_pm_power_up,
+	.power_down		= mb86s7x_pm_power_down,
+	.wait_for_powerdown	= mb86s7x_wait_for_powerdown,
+	.suspend		= mb86s7x_pm_suspend,
+};
+
+static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
+{
+	/* Reboot immediately */
+	mb86s7x_reboot(50);
+}
+
+static void mb86s7x_poweroff(void)
+{
+	/* Reboot never, remain dead */
+	mb86s7x_reboot(~0);
+}
+
+/*
+ * Enable cluster-level coherency, in preparation for turning on the MMU.
+ */
+static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
+{
+	asm volatile ("\n"
+"	cmp	r0, #1\n"
+"	bxne	lr\n"
+"	b	cci_enable_port_for_self");
+}
+
+static int __init mb86s7x_mcpm_init(void)
+{
+	unsigned int mpidr, cpu, cluster;
+	struct mb86s7x_scb_version cmd;
+	struct completion got_rsp;
+	int ret;
+
+	arm_pm_restart = mb86s7x_restart;
+	pm_power_off = mb86s7x_poweroff;
+
+	if (!cci_probed())
+		return -ENODEV;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
+	mb86s7x_pm_use_count[cpu][cluster] = 1;
+
+	/* reset the wfi 'color' for primary cpu */
+	mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
+
+	/* Set entry point for any CPU nowonwards */
+	writel_relaxed(virt_to_phys(mcpm_entry_point),
+		     MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.version = 0;
+	cmd.config_version = 0;
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
+			      &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0)
+		pr_err("%s:%d failed to get SCB version\n",
+		       __func__, __LINE__);
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_info("MB86S7x MCPM initialized: SCB version 0x%x:0x%x\n",
+		cmd.version, cmd.config_version);
+
+	ret = mcpm_platform_register(&mb86s7x_pm_power_ops);
+	if (!ret)
+		ret = mcpm_sync_init(mb86s7x_pm_power_up_setup);
+
+	return ret;
+}
+early_initcall(mb86s7x_mcpm_init);
diff --git a/arch/arm/mach-mb86s7x/pm_domains.c b/arch/arm/mach-mb86s7x/pm_domains.c
new file mode 100644
index 0000000..1fec5ed
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/pm_domains.c
@@ -0,0 +1,237 @@
+/*
+ * mb86s7x generic powerdomain support
+ * Copyright (C) 2014 Linaro, Ltd  Andy Green <andy.green-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
+ *
+ * based on -->
+ *
+ * Exynos Generic power domain support.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Implementation of Exynos specific power domain control which is used in
+ * conjunction with runtime-pm. Support for both device-tree and non-device-tree
+ * based power domain support is included.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pm_domain.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+/*
+ * mb86s7x specific wrapper around the generic power domain
+ */
+struct mb86s7x_pm_domain {
+	struct generic_pm_domain pd;
+	char const *name;
+	int powerdomain_index;
+	bool is_off;
+};
+
+static int mb86s7x_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+	struct mb86s7x_pm_domain *pd;
+	struct mb86s7x_pmd_cmd cmd;
+	struct completion got_rsp;
+	int ret;
+
+	pd = container_of(domain, struct mb86s7x_pm_domain, pd);
+
+	pr_debug("%s: domain %s <- %d\n", __func__, pd->name, power_on);
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.powerdomain_index = pd->powerdomain_index;
+	cmd.state = power_on;
+	if (pd->powerdomain_index >= 0) {
+		pr_info("First powerdomain index1 set :%d .\n",
+			pd->powerdomain_index);
+		init_completion(&got_rsp);
+
+		ret = mhu_send_packet(CMD_POWERDOMAIN_SET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed to set powerdomain\n",
+			       __func__, __LINE__);
+			return ret;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+	}
+	pd->is_off = !power_on;
+	return 0;
+}
+
+static int mb86s7x_pd_power_on(struct generic_pm_domain *domain)
+{
+	return mb86s7x_pd_power(domain, true);
+}
+
+static int mb86s7x_pd_power_off(struct generic_pm_domain *domain)
+{
+	return mb86s7x_pd_power(domain, false);
+}
+
+static void mb86s7x_add_device_to_domain(struct mb86s7x_pm_domain *pd,
+					 struct device *dev)
+{
+	int ret;
+
+	dev_info(dev, "adding to power domain %s\n", pd->pd.name);
+
+	while (1) {
+		ret = pm_genpd_add_device(&pd->pd, dev);
+		if (ret != -EAGAIN)
+			break;
+		cond_resched();
+	}
+
+	pm_genpd_dev_need_restore(dev, true);
+}
+
+static void mb86s7x_remove_device_from_domain(struct device *dev)
+{
+	struct generic_pm_domain *genpd = dev_to_genpd(dev);
+	int ret;
+
+	if (genpd == ERR_PTR(-EINVAL)) {
+		dev_dbg(dev, "device has no power domain\n");
+		return;
+	}
+
+	dev_info(dev, "removing from power domain %s\n", genpd->name);
+
+	while (1) {
+		ret = pm_genpd_remove_device(genpd, dev);
+		if (ret != -EAGAIN)
+			break;
+		cond_resched();
+	}
+}
+
+static void mb86s7x_read_domain_from_dt(struct device *dev)
+{
+	struct platform_device *pd_pdev;
+	struct mb86s7x_pm_domain *pd;
+	struct device_node *node;
+
+	node = of_parse_phandle(dev->of_node, "fujitsu,power-domain", 0);
+	if (!node)
+		return;
+	pd_pdev = of_find_device_by_node(node);
+	if (!pd_pdev)
+		return;
+	pd = platform_get_drvdata(pd_pdev);
+	mb86s7x_add_device_to_domain(pd, dev);
+}
+
+static int mb86s7x_pm_notifier_call(struct notifier_block *nb,
+				    unsigned long event, void *data)
+{
+	struct device *dev = data;
+
+	switch (event) {
+	case BUS_NOTIFY_BIND_DRIVER:
+		if (dev->of_node)
+			mb86s7x_read_domain_from_dt(dev);
+
+		break;
+
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		mb86s7x_remove_device_from_domain(dev);
+
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block platform_nb = {
+	.notifier_call = mb86s7x_pm_notifier_call,
+};
+
+static __init int mb86s7x_pm_init_power_domain(void)
+{
+	struct platform_device *pdev, *master_pd_pdev;
+	struct device_node *np, *master_node;
+	struct mb86s7x_pmd_cmd cmd;
+	struct completion got_rsp;
+	int ret;
+
+	for_each_compatible_node(np, NULL, "fujitsu,mb86s7x-pd") {
+		struct mb86s7x_pm_domain *pd, *master_pd;
+
+		pdev = of_find_device_by_node(np);
+
+		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+		if (!pd) {
+			pr_err("%s: failed to allocate memory for domain\n",
+			       __func__);
+			return -ENOMEM;
+		}
+
+		pd->pd.name = kstrdup(np->name, GFP_KERNEL);
+		pd->name = pd->pd.name;
+		pd->pd.power_off = mb86s7x_pd_power_off;
+		pd->pd.power_on = mb86s7x_pd_power_on;
+		pd->pd.of_node = np;
+
+		if (of_property_read_u32(np, "index", &pd->powerdomain_index))
+			pd->powerdomain_index = -1;
+
+		platform_set_drvdata(pdev, pd);
+
+		/* first power domain */
+		cmd.payload_size = sizeof(cmd);
+		cmd.powerdomain_index = pd->powerdomain_index;
+		cmd.state = 0;
+
+		if (pd->powerdomain_index >= 0) {
+			init_completion(&got_rsp);
+
+			ret = mhu_send_packet(CMD_POWERDOMAIN_GET_REQ,
+					      &cmd, sizeof(cmd), &got_rsp);
+			if (ret < 0)
+				pr_err("%s:%d failed to get SCB version\n",
+				       __func__, __LINE__);
+			if (ret)
+				wait_for_completion(&got_rsp);
+		}
+
+		pm_genpd_init(&pd->pd, NULL, !cmd.state);
+
+		dev_info(&pdev->dev, "power domain %s starting\n", pd->pd.name);
+
+		/* find any master domain and register self to it*/
+		master_node = of_parse_phandle(np, "fujitsu,power-domain", 0);
+		if (!master_node)
+			continue;
+		master_pd_pdev = of_find_device_by_node(master_node);
+		if (!master_pd_pdev) {
+			pr_err("sneeker : this should never happened.\n");
+			continue;
+		}
+		master_pd = platform_get_drvdata(master_pd_pdev);
+		while (1) {
+			ret = pm_genpd_add_subdomain(&master_pd->pd, &pd->pd);
+			if (ret != -EAGAIN)
+				break;
+			cond_resched();
+		}
+	}
+
+	bus_register_notifier(&platform_bus_type, &platform_nb);
+
+	pr_info("mb86s7x Power Domain support initialized\n");
+
+	return 0;
+}
+arch_initcall(mb86s7x_pm_init_power_domain);
diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
new file mode 100644
index 0000000..fd5f034
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/scb_mhu.c
@@ -0,0 +1,447 @@
+/*
+ * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
+ *
+ * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#include "iomap.h"
+
+#define INTR_STAT_OFS	0x0
+#define INTR_SET_OFS	0x8
+#define INTR_CLR_OFS	0x10
+
+static int do_xfer(void);
+static void try_xfer(struct work_struct *ignored);
+
+static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
+static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
+static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
+static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
+
+static LIST_HEAD(free_xfers);
+static LIST_HEAD(pending_xfers);
+static DEFINE_SPINLOCK(fsm_lock);
+static struct mbox_client mhu_cl;
+static struct mbox_chan *mhu_chan;
+static DECLARE_WORK(scb_work, try_xfer);
+static mhu_handler_t handler[MHU_NUM_CMDS];
+
+static enum {
+	MHU_PARK = 0,
+	MHU_WRR, /* Waiting to get Remote's Reply */
+	MHU_WRL, /* Waiting to send Reply */
+	MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
+	MHU_INVLD,
+} fsm_state;
+
+enum fsm_event {
+	EV_LC = 0, /* Local sent a command */
+	EV_RC, /* Remote sent a command */
+	EV_RR, /* Remote sent a reply */
+	EV_LR, /* Local sent a reply */
+};
+
+static int mhu_fsm[4][4] = {
+	[MHU_PARK] = {
+		[EV_LC] = MHU_WRR,
+		[EV_RC] = MHU_WRL,
+		[EV_RR] = MHU_INVLD,
+		[EV_LR] = MHU_INVLD,
+	},
+	[MHU_WRR] = {
+		[EV_LC] = MHU_INVLD,
+		[EV_RC] = MHU_WRRL,
+		[EV_RR] = MHU_PARK,
+		[EV_LR] = MHU_INVLD,
+	},
+	[MHU_WRL] = {
+		[EV_LC] = MHU_WRRL,
+		[EV_RC] = MHU_INVLD,
+		[EV_RR] = MHU_INVLD,
+		[EV_LR] = MHU_PARK,
+	},
+	[MHU_WRRL] = {
+		[EV_LC] = MHU_INVLD,
+		[EV_RC] = MHU_INVLD,
+		[EV_RR] = MHU_WRL,
+		[EV_LR] = MHU_WRR,
+	},
+};
+
+struct mhu_xfer {
+	int code;
+	int len;
+	void *buf;
+	struct completion *c;
+	struct list_head node;
+};
+
+static struct mhu_xfer *ax; /* stages of xfer */
+
+static int mhu_alloc_xfers(int n, struct list_head *list)
+{
+	struct mhu_xfer *x = kcalloc(n, sizeof(struct mhu_xfer), GFP_ATOMIC);
+	int i;
+
+	if (!x)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++)
+		list_add(&x[i].node, &free_xfers);
+
+	return 0;
+}
+
+static void try_xfer(struct work_struct *ignored)
+{
+	/* If polling overtook the scheduled work */
+	if (mhu_chan == NULL)
+		return;
+
+	/* Now that we got a reply to last TX, that
+	 * must mean the last TX was successful */
+	mbox_client_txdone(mhu_chan, 0);
+
+	do_xfer();
+}
+
+static void got_data(u32 code)
+{
+	struct completion *c = NULL;
+	mhu_handler_t hndlr = NULL;
+	unsigned long flags;
+	int ev;
+
+	if (code & RESP_BIT)
+		ev = EV_RR;
+	else
+		ev = EV_RC;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
+		return;
+	}
+	fsm_state = mhu_fsm[fsm_state][ev];
+
+	if (code & RESP_BIT) {
+		c = ax->c;
+		memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
+		list_move(&ax->node, &free_xfers);
+		ax = NULL;
+	} else {
+		/* Find and dispatch relevant registered handler */
+		if (code < MHU_NUM_CMDS)
+			hndlr = handler[code];
+		if (!hndlr)
+			pr_err("No handler for CMD_%u\n", code);
+	}
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	if (hndlr)
+		hndlr(code, cmd_from_scb);
+	if (c)
+		complete(c);
+}
+
+static void mhu_recv(struct mbox_client *cl, void *data)
+{
+	got_data((u32)data);
+	schedule_work(&scb_work);
+}
+
+static int do_xfer(void)
+{
+	unsigned long flags;
+	struct mhu_xfer *x;
+	int ev;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (list_empty(&pending_xfers)) {
+		struct mbox_chan *_ch = NULL;
+		int cmd;
+
+		for (cmd = 0; cmd < MHU_NUM_CMDS && !handler[cmd]; cmd++)
+			;
+		/* Don't free channel if any user is listening */
+		if (cmd != MHU_NUM_CMDS) {
+			spin_unlock_irqrestore(&fsm_lock, flags);
+			return 1;
+		}
+
+		if (fsm_state == MHU_PARK) {
+			_ch = mhu_chan;
+			mhu_chan = NULL;
+		}
+
+		spin_unlock_irqrestore(&fsm_lock, flags);
+
+		if (_ch)
+			mbox_free_channel(_ch);
+
+		return 1;
+	}
+
+	x = list_first_entry(&pending_xfers, struct mhu_xfer, node);
+	ev = x->code & RESP_BIT ? EV_LR : EV_LC;
+
+	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return 1;
+	}
+	list_del_init(&x->node);
+
+	/* Layout the SHM */
+	if (x->code & RESP_BIT)
+		memcpy_toio(rsp_to_scb, x->buf, x->len);
+	else
+		memcpy_toio(cmd_to_scb, x->buf, x->len);
+
+	ev = x->code & RESP_BIT ? EV_LR : EV_LC;
+	if (ev == EV_LC)
+		ax = x;
+	else
+		list_move(&x->node, &free_xfers);
+	fsm_state = mhu_fsm[fsm_state][ev];
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	/* Prefer mailbox API */
+	if (!mhu_chan) {
+		struct mbox_chan *_ch;
+
+		_ch = mbox_request_channel(&mhu_cl);
+		if (!IS_ERR(_ch))
+			mhu_chan = _ch;
+	}
+
+	if (mhu_chan) {
+		/* Send via generic api */
+		mbox_send_message(mhu_chan, (void *)x->code);
+	} else {
+		void __iomem *tx_reg = MB86S7X_MHU_VIRT + 0x120; /* HP-NonSec */
+		void __iomem *rx_reg = MB86S7X_MHU_VIRT + 0x20; /* HP-NonSec */
+		u32 val, count;
+
+		/* Send via early-boot api */
+		val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+		if (val) {
+			pr_err("Last CMD not yet read by SCB\n");
+			writel_relaxed(val, tx_reg + INTR_CLR_OFS);
+		}
+
+		writel_relaxed(x->code, tx_reg + INTR_SET_OFS);
+
+		/* Wait until this message is read */
+		count = 0x1000000;
+		do {
+			cpu_relax();
+			val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+		} while (--count && val);
+		if (val)
+			pr_err("%s:%d SCB not listening!\n",
+			       __func__, __LINE__);
+
+		if (!ax) {
+			/* A quick poll for pending remote cmd */
+			val = readl_relaxed(rx_reg + INTR_STAT_OFS);
+			if (val) {
+				got_data(val);
+				writel_relaxed(val, rx_reg + INTR_CLR_OFS);
+			}
+		} else {
+			do {
+retry:
+				/* Wait until we get reply */
+				count = 0x1000000;
+				do {
+					cpu_relax();
+					val = readl_relaxed(
+						rx_reg + INTR_STAT_OFS);
+				} while (--count && !val);
+
+				if (!val) {
+					pr_err("%s:%d SCB didn't reply\n",
+					       __func__, __LINE__);
+					goto retry;
+				} else {
+					got_data(val);
+					writel_relaxed(val,
+						     rx_reg + INTR_CLR_OFS);
+				}
+			} while (!(val & RESP_BIT));
+		}
+		return 0;
+	}
+
+	return do_xfer();
+}
+
+int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
+{
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+	if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
+		ret = 0;
+		handler[cmd] = hndlr;
+	}
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	if (!mhu_chan) {
+		struct mbox_chan *_ch;
+
+		_ch = mbox_request_channel(&mhu_cl);
+		if (!IS_ERR(_ch))
+			mhu_chan = _ch;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mhu_hndlr_set);
+
+void mhu_hndlr_clr(u32 cmd, mhu_handler_t hndlr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (cmd < MHU_NUM_CMDS && handler[cmd] == hndlr)
+		handler[cmd] = NULL;
+
+	if (list_empty(&pending_xfers)) {
+		struct mbox_chan *_ch = NULL;
+
+		for (cmd = 0; cmd < MHU_NUM_CMDS && !handler[cmd]; cmd++)
+			;
+		/* Don't free channel if any user is listening */
+		if (cmd != MHU_NUM_CMDS) {
+			spin_unlock_irqrestore(&fsm_lock, flags);
+			return;
+		}
+
+		if (fsm_state == MHU_PARK) {
+			_ch = mhu_chan;
+			mhu_chan = NULL;
+		}
+
+		spin_unlock_irqrestore(&fsm_lock, flags);
+
+		if (_ch)
+			mbox_free_channel(_ch);
+
+		return;
+	}
+	spin_unlock_irqrestore(&fsm_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mhu_hndlr_clr);
+
+int mhu_send_packet(int code, void *buf, int len, struct completion *c)
+{
+	struct mhu_xfer *x;
+	unsigned long flags;
+
+	if (code & ~0xff) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	if ((code & RESP_BIT) &&
+			fsm_state != MHU_WRRL && fsm_state != MHU_WRL) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (list_empty(&free_xfers) && mhu_alloc_xfers(5, &free_xfers)) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("%s:%d OOM\n", __func__, __LINE__);
+		return -EAGAIN;
+	}
+
+	x = list_first_entry(&free_xfers, struct mhu_xfer, node);
+	x->code = code;
+	x->buf = buf;
+	x->len = len;
+	x->c = c;
+
+	if (code & RESP_BIT)
+		list_move(&x->node, &pending_xfers);
+	else
+		list_move_tail(&x->node, &pending_xfers);
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	return do_xfer();
+}
+EXPORT_SYMBOL_GPL(mhu_send_packet);
+
+void mb86s7x_reboot(u32 delay)
+{
+	void __iomem *tx_reg = MB86S7X_MHU_VIRT + 0x120; /* HP-NonSec */
+	struct mb86s7x_hard_reset cmd;
+	u32 val;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.delay = delay;
+
+	val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+	if (val) /* Flush anything pending */
+		writel_relaxed(val, tx_reg + INTR_CLR_OFS);
+
+	memcpy_toio(cmd_to_scb, &cmd, sizeof(cmd));
+	writel_relaxed(CMD_HARD_RESET_REQ, tx_reg + INTR_SET_OFS);
+}
+EXPORT_SYMBOL_GPL(mb86s7x_reboot);
+
+static int f_scb_probe(struct platform_device *pdev)
+{
+	mhu_cl.tx_block = true;
+	mhu_cl.knows_txdone = true;
+	mhu_cl.rx_callback = mhu_recv;
+	mhu_cl.dev = &pdev->dev;
+	mhu_cl.chan_name = "HP_NonSec";
+	return 0;
+}
+
+static const struct of_device_id scb_dt_ids[] = {
+	{ .compatible = "fujitsu,scb" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, scb_dt_ids);
+
+static struct platform_driver f_scb_driver = {
+	.driver		= {
+		.name	= "f_scb",
+		.owner = THIS_MODULE,
+		.of_match_table = scb_dt_ids,
+	},
+	.probe = f_scb_probe,
+};
+
+static int __init f_scb_init(void)
+{
+	return platform_driver_register(&f_scb_driver);
+}
+module_init(f_scb_init);
diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
new file mode 100644
index 0000000..4f4287e
--- /dev/null
+++ b/include/linux/platform_data/mb86s7x_mbox.h
@@ -0,0 +1,249 @@
+/*
+ * include/linux/platform_data/mb86s7x_mbox.h
+ *
+ * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MB86S7X_MBOX_API_H
+#define __MB86S7X_MBOX_API_H
+
+#define CMD_MASK    0x7f    /* 128 possible commands */
+#define RESP_BIT    (1 << 7) /* If it's a response */
+
+#define ENC_CMD(c)  ((c) & CMD_MASK)
+#define DEC_CMD(v)  (((v) & ~CMD_MASK) ? CMD_INVALID : ((v) & CMD_MASK))
+
+#define ENC_REP(r)  (((r) & CMD_MASK) | RESP_BIT)
+
+/* If v is the reply to command c */
+#define IS_A_REP(v, c)  (((v) & RESP_BIT) && (((v) & CMD_MASK) == (c)))
+
+enum {
+	CMD_INVALID = 0,
+	CMD_I2C_XFER_REQ = 1,
+	CMD_PERI_POWER_SET_REQ = 2,
+	CMD_PERI_CLOCK_GATE_SET_REQ = 3,
+	CMD_PERI_CLOCK_GATE_GET_REQ = 4,
+	CMD_PERI_CLOCK_RATE_SET_REQ = 5,
+	CMD_PERI_CLOCK_RATE_GET_REQ = 6,
+	CMD_CPU_CLOCK_GATE_SET_REQ = 7,
+	CMD_CPU_CLOCK_GATE_GET_REQ = 8,
+	CMD_CPU_CLOCK_RATE_SET_REQ = 9,
+	CMD_CPU_CLOCK_RATE_GET_REQ = 0xa,
+	CMD_CLUSTER_OPP_GET_REQ = 0xb,
+	CMD_CLOCK_DSI_PIXEL_REQ = 0xc,
+	CMD_SCB_CAPABILITY_GET_REQ = 0xd,
+	CMD_SYS_RESET_CAUSE_GET_REQ = 0xe,
+	CMD_SYS_SPECIFIC_INFO_GET_REQ = 0xf,
+	CMD_REBOOT_AP_AFTER_REQ = 0x10,
+	CMD_TAIKI_REQ = 0x11,
+	CMD_TAIKI_ASYNC_MSG_REQ = 0x12,
+	CMD_GET_WORD_REQ = 0x13,
+	CMD_HARD_RESET_REQ = 0x14,
+	CMD_MAINTENANCE_MODE_REQ = 0x15,
+	CMD_STG_GET_SIZE_REQ = 0x16,
+	CMD_STG_BLOCK_READ_REQ = 0x17,
+	CMD_STG_BLOCK_WRITE_REQ = 0x18,
+	CMD_MEMORY_LAYOUT_GET_REQ = 0x19,
+	CMD_POWERDOMAIN_GET_REQ = 0x1a,
+	CMD_POWERDOMAIN_SET_REQ = 0x1b,
+	CMD_STG_BLOCK_ERASE_REQ = 0x1c,
+
+	/* Do NOT add new commands below this line */
+	MHU_NUM_CMDS,
+};
+
+#define CMD_I2C_XFER_REP	ENC_REP(CMD_I2C_XFER_REQ)
+#define CMD_PERI_POWER_SET_REP	ENC_REP(CMD_PERI_POWER_SET_REQ)
+#define CMD_PERI_CLOCK_GATE_SET_REP	ENC_REP(CMD_PERI_CLOCK_GATE_SET_REQ)
+#define CMD_PERI_CLOCK_GATE_GET_REP	ENC_REP(CMD_PERI_CLOCK_GATE_GET_REQ)
+#define CMD_PERI_CLOCK_RATE_SET_REP	ENC_REP(CMD_PERI_CLOCK_RATE_SET_REQ)
+#define CMD_PERI_CLOCK_RATE_GET_REP	ENC_REP(CMD_PERI_CLOCK_RATE_GET_REQ)
+#define CMD_CPU_CLOCK_GATE_SET_REP	ENC_REP(CMD_CPU_CLOCK_GATE_SET_REQ)
+#define CMD_CPU_CLOCK_GATE_GET_REP	ENC_REP(CMD_CPU_CLOCK_GATE_GET_REQ)
+#define CMD_CPU_CLOCK_RATE_SET_REP	ENC_REP(CMD_CPU_CLOCK_RATE_SET_REQ)
+#define CMD_CPU_CLOCK_RATE_GET_REP	ENC_REP(CMD_CPU_CLOCK_RATE_GET_REQ)
+#define CMD_CLUSTER_OPP_GET_REP	ENC_REP(CMD_CLUSTER_OPP_GET_REQ)
+#define CMD_CLOCK_DSI_PIXEL_REP	ENC_REP(CMD_CLOCK_DSI_PIXEL_REQ)
+#define CMD_SCB_CAPABILITY_GET_REP	ENC_REP(CMD_SCB_CAPABILITY_GET_REQ)
+#define CMD_SYS_RESET_CAUSE_GET_REP	ENC_REP(CMD_SYS_RESET_CAUSE_GET_REQ)
+#define CMD_SYS_SPECIFIC_INFO_GET_REP	ENC_REP(CMD_SYS_SPECIFIC_INFO_GET_REQ)
+#define CMD_GET_WORD_REP	ENC_REP(CMD_GET_WORD_REQ)
+#define CMD_REBOOT_AP_AFTER_REP	ENC_REP(CMD_REBOOT_AP_AFTER_REQ)
+#define CMD_TAIKI_REP			ENC_REP(CMD_TAIKI_REQ)
+#define CMD_TAIKI_ASYNC_MSG_REP		ENC_REP(CMD_TAIKI_ASYNC_MSG_REQ)
+#define CMD_HARD_RESET_REP		ENC_REP(CMD_HARD_RESET_REQ)
+#define CMD_MAINTENANCE_MODE_REP	ENC_RSP(CMD_MAINTENANCE_MODE_REQ)
+#define CMD_STG_GET_SIZE_REP		ENC_REP(CMD_STG_GET_SIZE_REQ)
+#define CMD_STG_BLOCK_READ_REP		ENC_REP(CMD_STG_BLOCK_READ_REQ)
+#define CMD_STG_BLOCK_WRITE_REP		ENC_REP(CMD_STG_BLOCK_WRITE_REQ)
+#define CMD_MEMORY_LAYOUT_GET_REP	ENC_REP(CMD_MEMORY_LAYOUT_GET_REQ)
+#define CMD_POWERDOMAIN_GET_REP		ENC_REP(CMD_POWERDOMAIN_GET_REQ)
+#define CMD_POWERDOMAIN_SET_REP		ENC_REP(CMD_POWERDOMAIN_SET_REQ)
+#define CMD_STG_BLOCK_ERASE_REP		ENC_REP(CMD_STG_BLOCK_ERASE_REQ)
+
+struct mb86s7x_peri_clk {
+	u32 payload_size;
+	u32 cntrlr;
+	u32 domain;
+	u32 port;
+	u32 en;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_peri_power {
+	u32 payload_size;
+	u32 peri_id;
+	u32 en;
+} __packed;
+
+#define AT_WFI_DO_NOTHING	0x0
+#define AT_WFI_DO_SUSPEND	0x1
+#define AT_WFI_DO_POWEROFF	0x2
+#define AT_WFI_COLOR_MASK	0x3
+
+#define WFI_COLOR_REG_OFFSET	0x3f00
+
+struct mb86s7x_cpu_gate {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 cpu_id;
+#define SCB_CPU_STATE_OFF	0x0
+#define SCB_CPU_STATE_ON	0x1
+#define SCB_CPU_STATE_SUSP	0x2
+	u32 cpu_state;
+} __packed;
+
+struct mb86s7x_cpu_freq {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 cpu_id;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_cluster_opp {
+	u32 voltage;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_cluster_opp_get {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 opp_num;
+	struct mb86s7x_cluster_opp opp[0];
+} __packed;
+
+struct mb86s7x_clk_dsipxl {
+	u32 payload_size;
+	u32 mode;
+} __packed;
+
+struct mb86s7x_scb_version {
+	u32 payload_size;
+	u32 version;
+	u32 config_version;
+} __packed;
+
+struct mb86s7x_reset_cause {
+	u32 payload_size;
+#define RESET_BY_COLDBOOT	0x0
+#define RESET_BY_SW		0x1
+#define RESET_BY_WDOG		0x2
+#define RESET_BY_xxyz		0x3
+	u32 cause;
+} __packed;
+
+struct mb86s7x_sys_info {
+	u32 payload_size;
+	u64 specific_info;
+} __packed;
+
+struct mb86s7x_softreboot {
+	u32 payload_size;
+	u32 delay;
+} __packed;
+
+struct mb86s7x_debug_readl {
+	u32 payload_size;
+#define REG_TOP_STATUS_CFG_CTL		0x44010000
+#define REG_TOP_STATUS_CRG11_ALW	0xF1000000
+#define REG_TOP_STATUS_CRG11_DDR3	0xF1010000
+#define REG_TOP_STATUS_CRG11_MAIN	0xF1020000
+#define REG_TOP_STATUS_CRG11_HDMI	0xF1040000
+#define REG_TOP_STATUS_CRG11_DPHY	0xF1050000
+#define REG_TOP_STATUS_CRGSYSOC		0xF1080000
+#define REG_TOP_STATUS_PRMUX		0xCA4D0000
+	u32 address;
+	u32 data;
+} __packed;
+
+struct mb86s7x_taiki {
+	u32	payload_data[64];
+} __packed;
+
+struct mb86s7x_taiki_async_msg {
+	u32	payload_data[64];
+} __packed;
+
+struct mb86s7x_hard_reset {
+	u32 payload_size;
+	u32 delay;
+} __packed;
+
+struct mb86s7x_stg_get_size {
+	u32 payload_size;
+#define SCB_SECTOR_SIZE 128
+	u32 count_sectors;
+	u32 erase_block_size_bytes;
+} __packed;
+
+struct mb86s7x_stg_block_rw {
+	u32 payload_size;
+	u32 sector;
+	u32 result;
+	u8 data[SCB_SECTOR_SIZE];
+} __packed;
+
+struct mb86s7x_stg_block_erase {
+	u32 payload_size;
+	u32 sector;
+	u32 result;
+} __packed;
+
+struct mb86s7x_memory_region {
+	u64 start;
+	u64 length;
+} __packed;
+
+struct mb86s7x_memory_layout {
+	u32 payload_size;
+	u32 count_regions;
+	struct mb86s7x_memory_region regions[0];
+} __packed;
+
+struct mb86s7x_pmd_cmd {
+	u32 payload_size;
+	u32 powerdomain_index;
+	u32 state;
+} __packed;
+
+#define MB86S7X_VERSION_SUPPORT_BLOCKDEV 0x509
+
+int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
+void mb86s7x_reboot(u32 delay);
+
+/* This function must not sleep */
+typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
+
+int mhu_hndlr_set(u32 cmd, mhu_handler_t);
+void mhu_hndlr_clr(u32 cmd, mhu_handler_t);
+
+#endif /* __MB86S7X_MBOX_API_H */
-- 
1.8.1.2

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-13  6:28   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:28 UTC (permalink / raw)
  To: linux-arm-kernel

The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
And the remote master firmware (called SCB) running on CM3. Linux asks
for things to be done over Mailbox API, to SCB which controls most of
the important things. variations S70 & S73 are supported.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../bindings/arm/fujistu/power_domain.txt          |  22 +
 arch/arm/Kconfig                                   |   2 +
 arch/arm/Makefile                                  |   1 +
 arch/arm/boot/dts/Makefile                         |   1 +
 arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
 arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
 arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
 arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
 arch/arm/configs/fujitsu_defconfig                 | 156 ++++
 arch/arm/mach-mb86s7x/Kconfig                      |  18 +
 arch/arm/mach-mb86s7x/Makefile                     |   2 +
 arch/arm/mach-mb86s7x/board.c                      |  65 ++
 arch/arm/mach-mb86s7x/iomap.h                      |  34 +
 arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
 arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
 arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
 include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
 17 files changed, 3183 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
 create mode 100644 arch/arm/boot/dts/mb86s70.dtsi
 create mode 100644 arch/arm/boot/dts/mb86s70eb.dts
 create mode 100644 arch/arm/boot/dts/mb86s73.dtsi
 create mode 100644 arch/arm/boot/dts/mb86s73eb.dts
 create mode 100644 arch/arm/configs/fujitsu_defconfig
 create mode 100644 arch/arm/mach-mb86s7x/Kconfig
 create mode 100644 arch/arm/mach-mb86s7x/Makefile
 create mode 100644 arch/arm/mach-mb86s7x/board.c
 create mode 100644 arch/arm/mach-mb86s7x/iomap.h
 create mode 100644 arch/arm/mach-mb86s7x/mcpm.c
 create mode 100644 arch/arm/mach-mb86s7x/pm_domains.c
 create mode 100644 arch/arm/mach-mb86s7x/scb_mhu.c
 create mode 100644 include/linux/platform_data/mb86s7x_mbox.h

diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
new file mode 100644
index 0000000..44abfe8
--- /dev/null
+++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
@@ -0,0 +1,22 @@
+* Fujitsu MB86S7x Power Domains
+
+Remote f/w on MB86S7x can enable/disable power to various IPs.
+
+Required Properties:
+- compatible: Should be "fujitsu,mb86s7x-pd"
+- index: Index of the power gate control for the block
+
+Example:
+
+	pd_cpu: genpd at 3 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <3>;
+	};
+
+Example of the node using power domain:
+
+	node {
+		/* ... */
+		fujitsu,power-domain = <&pd_cpu>;
+		/* ... */
+	};
diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 245058b..44fd319 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -955,6 +955,8 @@ source "arch/arm/mach-kirkwood/Kconfig"
 
 source "arch/arm/mach-ks8695/Kconfig"
 
+source "arch/arm/mach-mb86s7x/Kconfig"
+
 source "arch/arm/mach-msm/Kconfig"
 
 source "arch/arm/mach-moxart/Kconfig"
diff --git a/arch/arm/Makefile b/arch/arm/Makefile
index 6721fab..d6ec5cd 100644
--- a/arch/arm/Makefile
+++ b/arch/arm/Makefile
@@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_KEYSTONE)		+= keystone
 machine-$(CONFIG_ARCH_KIRKWOOD)		+= kirkwood
 machine-$(CONFIG_ARCH_KS8695)		+= ks8695
 machine-$(CONFIG_ARCH_LPC32XX)		+= lpc32xx
+machine-$(CONFIG_ARCH_MB86S7X)		+= mb86s7x
 machine-$(CONFIG_ARCH_MMP)		+= mmp
 machine-$(CONFIG_ARCH_MOXART)		+= moxart
 machine-$(CONFIG_ARCH_MSM)		+= msm
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index adb5ed9..0c8addb 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -154,6 +154,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += $(kirkwood)
 dtb-$(CONFIG_MACH_KIRKWOOD) += $(kirkwood)
 dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
 dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
+dtb-$(CONFIG_ARCH_MB86S7X) += mb86s70eb.dtb mb86s73eb.dtb
 dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
 dtb-$(CONFIG_ARCH_MXC) += \
 	imx25-eukrea-mbimxsd25-baseboard.dtb \
diff --git a/arch/arm/boot/dts/mb86s70.dtsi b/arch/arm/boot/dts/mb86s70.dtsi
new file mode 100644
index 0000000..b6d8970
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s70.dtsi
@@ -0,0 +1,635 @@
+
+/dts-v1/;
+
+/ {
+	model = "Fujitsu mb86s70";
+	compatible = "fujitsu,mb86s70";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <1>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x100>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000  900000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x101>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000  900000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu2: cpu at 2 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x0>;
+			cci-control-port = <&cci_control4>;
+			clock-frequency = <1000000000>;
+			operating-points = <
+				/* kHz    uV */
+				1200000 900000
+				1600000 1000000
+				2000000 1100000
+				2400000 1200000
+			>;
+			clock-latency = <100000>;
+		};
+
+		cpu3: cpu at 3 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a15";
+			reg = <0x1>;
+			cci-control-port = <&cci_control4>;
+			clock-frequency = <1000000000>;
+			operating-points = <
+				/* kHz    uV */
+				1200000 900000
+				1600000 1000000
+				2000000 1100000
+				2400000 1200000
+			>;
+			clock-latency = <100000>;
+		};
+	};
+
+	cci at 2c090000 {
+		compatible = "arm,cci-400";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0 0x2c090000 0x1000>;
+		ranges = <0x0 0x0 0x2c090000 0x10000>;
+
+		cci_control3: slave-if at 4000 {
+			compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x4000 0x1000>;
+		};
+
+		cci_control4: slave-if at 5000 {
+			compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x5000 0x1000>;
+		};
+
+		pmu at 9000 {
+			compatible = "arm,cci-400-pmu";
+			reg = <0x9000 0x5000>;
+			interrupts = <0 77 4>,
+					<0 77 4>,
+					<0 77 4>,
+					<0 77 4>,
+					<0 77 4>;
+		};
+	};
+
+	/**
+	 * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
+	 * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+	 */
+	clocks {
+		clk_alw_0_0: clk_alw_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_alw_0_1: clk_alw_0_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <1>;
+		};
+
+		clk_alw_0_2: clk_alw_0_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <2>;
+		};
+
+		clk_alw_0_4: clk_alw_0_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <4>;
+		};
+
+		clk_alw_0_5: clk_alw_0_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <5>;
+		};
+
+		clk_alw_0_8: clk_alw_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_alw_1_0: clk_alw_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_alw_1_1: clk_alw_1_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <1>;
+		};
+
+		clk_alw_1_8: clk_alw_1_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <8>;
+		};
+
+		clk_alw_2_0: clk_alw_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_alw_2_1: clk_alw_2_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <1>;
+		};
+
+		clk_alw_2_2: clk_alw_2_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <2>;
+		};
+
+		clk_alw_2_4: clk_alw_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_alw_2_5: clk_alw_2_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <5>;
+		};
+
+		clk_alw_2_8: clk_alw_2_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <8>;
+		};
+
+		clk_alw_6_8: clk_alw_6_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <6>;
+			port = <8>;
+		};
+
+		clk_alw_7_0: clk_alw_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_alw_8_0: clk_alw_8_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <8>;
+			port = <0>;
+		};
+
+		clk_alw_a_0: clk_alw_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <0>;
+		};
+
+		clk_alw_a_1: clk_alw_a_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <1>;
+		};
+
+		clk_ddr3_0_0: clk_ddr3_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <1>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_0: clk_main_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_8: clk_main_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_main_1_3: clk_main_1_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <3>;
+		};
+
+		clk_main_1_4: clk_main_1_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <4>;
+		};
+
+		clk_main_2_0: clk_main_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_main_2_3: clk_main_2_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <3>;
+		};
+
+		clk_main_2_7: clk_main_2_7 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <7>;
+		};
+
+		clk_main_3_0: clk_main_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_main_3_3: clk_main_3_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <3>;
+		};
+
+		clk_main_3_4: clk_main_3_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <4>;
+		};
+
+		clk_main_3_5: clk_main_3_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <5>;
+		};
+
+		clk_main_3_6: clk_main_3_6 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <6>;
+		};
+
+		clk_main_4_0: clk_main_4_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <0>;
+		};
+
+		clk_main_4_1: clk_main_4_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <1>;
+		};
+
+		clk_main_5_0: clk_main_5_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <0>;
+		};
+
+		clk_main_5_3: clk_main_5_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <3>;
+		};
+
+		clk_main_5_4: clk_main_5_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <4>;
+		};
+
+		clk_main_5_5: clk_main_5_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <5>;
+		};
+
+		clk_main_7_0: clk_main_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_main_8_1: clk_main_8_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <8>;
+			port = <1>;
+		};
+
+		clk_main_9_0: clk_main_9_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <9>;
+			port = <0>;
+		};
+
+		clk_hdmi_0_0: clk_hdmi_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_hdmi_1_0: clk_hdmi_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_hdmi_2_0: clk_hdmi_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_hdmi_3_0: clk_hdmi_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_dphy_0_0: clk_dphy_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_dphy_1_0: clk_dphy_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <1>;
+			port = <0>;
+		};
+	};
+
+	gic: interrupt-controller at 2c001000 {
+		compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0 0x2c001000 0x1000>,
+		      <0 0x2c002000 0x1000>,
+		      <0 0x2c004000 0x2000>,
+		      <0 0x2c006000 0x2000>;
+		interrupts = <1 9 0xf04>;
+	};
+
+	timer0: timer0 at 31080000 {
+		compatible = "arm,sp804";
+		reg = <0 0x31080000 0x10000>;
+		interrupts = <0 324 4>,
+			     <0 325 4>;
+		clocks = <&clk_alw_6_8>;
+	};
+
+	mhu: mhu0 at 2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+	};
+
+	mhu_client: scb at 0 {
+		compatible = "fujitsu,scb";
+		mbox = <&mhu 1>;
+		mbox-names = "HP_NonSec";
+	};
+
+	pinctrl: pinctrl at 2a4d0000 {
+		compatible = "fujitsu,mb86s70-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>;
+		#gpio-range-cells = <3>;
+	};
+
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	gpio1: mb86s70_gpio1 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31010000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 32 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	uart0: serial at 0x31040000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31040000 0x100>;
+		interrupts = <0 320 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	uart1: serial at 0x31050000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31050000 0x100>;
+		interrupts = <0 321 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	uart2: serial at 0x31060000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31060000 0x100>;
+		interrupts = <0 322 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+	};
+
+	sdhci0: emmc at 300c0000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x300c0000 0x1000>;
+		interrupts = <0 164 0x4>,
+			     <0 165 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		bus-width = <8>;
+		clocks = <&clk_alw_1_8>, <&clk_alw_6_8>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
+
+	sdhci1: sdio at 36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		pwr-mux-gpios = <&gpio0 7 0>;
+		clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
+
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>,
+			<0 0x31618000 0x4000>,
+			<0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy at 1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s70eb.dts b/arch/arm/boot/dts/mb86s70eb.dts
new file mode 100644
index 0000000..ee25dd9
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s70eb.dts
@@ -0,0 +1,38 @@
+
+/include/ "mb86s70.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+
+/ {
+	model = "Fujitsu MB86S70 EVB";
+	compatible = "fujitsu,mb86s70-evb";
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000 0x80000000>, <0x08 0x80000000 0x80000000>;
+
+	};
+
+	chosen {
+		bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait rw";
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		d3 {
+			label = "led3";
+			gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "default-on";
+		};
+		d4 {
+			label = "led4";
+			gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "mmc1";
+		};
+		d5 {
+			label = "led5";
+			gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s73.dtsi b/arch/arm/boot/dts/mb86s73.dtsi
new file mode 100644
index 0000000..ef4f5a0
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s73.dtsi
@@ -0,0 +1,910 @@
+
+/ {
+	model = "Fujitsu mb86s73";
+	compatible = "fujitsu,mb86s73";
+	interrupt-parent = <&gic>;
+	#address-cells = <2>;
+	#size-cells = <1>;
+
+	aliases {
+		serial0 = &uart0;
+		serial1 = &uart1;
+		serial2 = &uart2;
+	};
+
+	cpus {
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		cpu0: cpu at 0 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x100>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000   900000
+				1200000  1000000
+			>;
+			clock-latency = <100000>;
+			fujitsu,power-domain = <&pd_cpu>;
+		};
+
+		cpu1: cpu at 1 {
+			device_type = "cpu";
+			compatible = "arm,cortex-a7";
+			reg = <0x101>;
+			cci-control-port = <&cci_control3>;
+			clock-frequency = <800000000>;
+			operating-points = <
+				/* kHz    uV */
+				800000   900000
+				1200000  1000000
+			>;
+			clock-latency = <100000>;
+			fujitsu,power-domain = <&pd_cpu>;
+		};
+	};
+
+	cci at 2c090000 {
+		compatible = "arm,cci-400";
+		#address-cells = <1>;
+		#size-cells = <1>;
+		reg = <0 0x2c090000 0x1000>;
+		ranges = <0x0 0x0 0x2c090000 0x10000>;
+		fujitsu,power-domain = <&pd_offchip>;
+
+		cci_control3: slave-if at 4000 {
+		compatible = "arm,cci-400-ctrl-if";
+			interface-type = "ace";
+			reg = <0x4000 0x1000>;
+		};
+	};
+
+	/**
+	 * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA7, 4->USB, 5->FPDLINK
+	 * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+	 */
+	clocks {
+		clk_alw_0_0: clk_alw_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_alw_0_1: clk_alw_0_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <1>;
+		};
+
+		clk_alw_0_2: clk_alw_0_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <2>;
+		};
+
+		clk_alw_0_4: clk_alw_0_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <4>;
+		};
+
+		clk_alw_0_5: clk_alw_0_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <5>;
+		};
+
+		clk_alw_0_8: clk_alw_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_alw_1_0: clk_alw_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_alw_1_1: clk_alw_1_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <1>;
+		};
+
+		clk_alw_1_2: clk_alw_1_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <2>;
+		};
+
+		clk_alw_1_8: clk_alw_1_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <1>;
+			port = <8>;
+		};
+
+		clk_alw_2_0: clk_alw_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_alw_2_1: clk_alw_2_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <1>;
+		};
+
+		clk_alw_2_2: clk_alw_2_2 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <2>;
+		};
+
+		clk_alw_2_4: clk_alw_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_alw_2_5: clk_alw_2_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <5>;
+		};
+
+		clk_alw_2_8: clk_alw_2_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <2>;
+			port = <8>;
+		};
+
+		clk_alw_6_8: clk_alw_6_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <6>;
+			port = <8>;
+		};
+
+		clk_alw_7_0: clk_alw_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_alw_8_0: clk_alw_8_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <8>;
+			port = <0>;
+		};
+
+		clk_alw_a_0: clk_alw_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <0>;
+		};
+
+		clk_alw_a_1: clk_alw_a_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0a>;
+			port = <1>;
+		};
+
+		clk_alw_b_0: clk_alw_b_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0b>;
+			port = <0>;
+		};
+
+		clk_alw_c_0: clk_alw_c_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <0>;
+			domain = <0x0c>;
+			port = <0>;
+		};
+
+		clk_ddr3_0_0: clk_ddr3_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <1>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_0: clk_main_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_main_0_8: clk_main_0_8 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0>;
+			port = <8>;
+		};
+
+		clk_main_1_3: clk_main_1_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <3>;
+		};
+
+		clk_main_1_4: clk_main_1_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <1>;
+			port = <4>;
+		};
+
+		clk_main_2_0: clk_main_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_main_2_3: clk_main_2_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <3>;
+		};
+
+		clk_main_2_4: clk_main_2_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <4>;
+		};
+
+		clk_main_2_7: clk_main_2_7 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <2>;
+			port = <7>;
+		};
+
+		clk_main_3_0: clk_main_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_main_3_3: clk_main_3_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <3>;
+		};
+
+		clk_main_3_4: clk_main_3_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <4>;
+		};
+
+		clk_main_3_5: clk_main_3_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <5>;
+		};
+
+		clk_main_3_6: clk_main_3_6 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <3>;
+			port = <6>;
+		};
+
+		clk_main_4_0: clk_main_4_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <0>;
+		};
+
+		clk_main_4_4: clk_main_4_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <4>;
+		};
+
+		clk_main_4_5: clk_main_4_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <4>;
+			port = <5>;
+		};
+
+		clk_main_5_0: clk_main_5_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <0>;
+		};
+
+		clk_main_5_3: clk_main_5_3 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <3>;
+		};
+
+		clk_main_5_4: clk_main_5_4 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <4>;
+		};
+
+		clk_main_5_5: clk_main_5_5 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <5>;
+			port = <5>;
+		};
+
+		clk_main_7_0: clk_main_7_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <7>;
+			port = <0>;
+		};
+
+		clk_main_8_1: clk_main_8_1 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <8>;
+			port = <1>;
+		};
+
+		clk_main_9_0: clk_main_9_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <9>;
+			port = <0>;
+		};
+
+		clk_main_a_0: clk_main_a_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xa>;
+			port = <0>;
+		};
+
+		clk_main_b_0: clk_main_b_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xb>;
+			port = <0>;
+		};
+
+		clk_main_c_0: clk_main_c_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xc>;
+			port = <0>;
+		};
+
+		clk_main_d_0: clk_main_d_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <2>;
+			domain = <0xd>;
+			port = <0>;
+		};
+
+		clk_usb_0_0: clk_usb_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_usb_1_0: clk_usb_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <1>;
+			port = <0>;
+		};
+
+		clk_usb_2_0: clk_usb_2_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <2>;
+			port = <0>;
+		};
+
+		clk_usb_3_0: clk_usb_3_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <4>;
+			domain = <3>;
+			port = <0>;
+		};
+
+		clk_fpdlink_0_0: clk_fpdlink_0_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <0>;
+			port = <0>;
+		};
+
+		clk_fpdlink_1_0: clk_fpdlink_1_0 {
+			compatible = "fujitsu,mb86s7x_clk";
+			#clock-cells = <0>;
+			cntrlr = <5>;
+			domain = <1>;
+			port = <0>;
+		};
+	};
+
+	timer0: timer0 at 31080000 {
+		compatible = "arm,sp804";
+		reg = <0 0x31080000 0x10000>;
+		interrupts = <0 324 4>,
+			     <0 325 4>;
+		clocks = <&clk_alw_6_8>;
+	};
+
+	timer1: archtimer {
+		compatible = "arm,armv7-timer";
+		clock-frequency = <125000000>;
+		interrupts = <1 13 0xf08>,
+			     <1 14 0xf08>,
+			     <1 11 0xf08>,
+			     <1 10 0xf08>;
+	};
+
+	pinctrl: pinctrl at 2a4d0000 {
+		compatible = "fujitsu,mb86s73-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
+		#gpio-range-cells = <3>;
+
+		pcie0_pins_active: pcie0_active {
+			mb86s7x,function = "pcie0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pcie0_pins_sleep: pcie0_sleep {
+			mb86s7x,function = "pcie0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb3h0_pins_active: usb3h0_active {
+			mb86s7x,function = "usb3h0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb3h0_pins_sleep: usb3h0_sleep {
+			mb86s7x,function = "usb3h0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb2h0_pins_active: usb2h0_active {
+			mb86s7x,function = "usb2h0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb2h0_pins_sleep: usb2h0_sleep {
+			mb86s7x,function = "usb2h0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb2d0_pins_active: usb2d0_active {
+			mb86s7x,function = "usb2d0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb2d0_pins_sleep: usb2d0_sleep {
+			mb86s7x,function = "usb2d0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		sdh30_pins_active: sdh30_active {
+			mb86s7x,function = "sdh30";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		sdh30_pins_sleep: sdh30_sleep {
+			mb86s7x,function = "sdh30";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		emmc0_pins_active: emmc0_active {
+			mb86s7x,function = "emmc0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		emmc0_pins_sleep: emmc0_sleep {
+			mb86s7x,function = "emmc0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		hsspi0_pins_active: hsspi0_active {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <8>; /* in mA */
+		};
+		hsspi0_pins_sleep: hsspi0_sleep {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		gmacd0_pins_active: gmacd0_active {
+			mb86s7x,function = "gmacd0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		gmacd0_pins_sleep: gmacd0_sleep {
+			mb86s7x,function = "gmacd0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		gmacm0_pins_active: gmacm0_active {
+			mb86s7x,function = "gmacm0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		gmacm0_pins_sleep: gmacm0_sleep {
+			mb86s7x,function = "gmacm0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		i2s0_pins_active: i2s0_active {
+			mb86s7x,function = "i2s0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		i2s0_pins_sleep: i2s0_sleep {
+			mb86s7x,function = "i2s0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		other0_pins_active: other0_active {
+			mb86s7x,function = "other0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		other0_pins_sleep: other0_sleep {
+			mb86s7x,function = "other0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		jtag0_pins_active: jtag0_active {
+			mb86s7x,function = "jtag0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		jtag0_pins_sleep: jtag0_sleep {
+			mb86s7x,function = "jtag0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		pcie1_pins_active: pcie1_active {
+			mb86s7x,function = "pcie1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pcie1_pins_sleep: pcie1_sleep {
+			mb86s7x,function = "pcie1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		usb3h1_pins_active: usb3h1_active {
+			mb86s7x,function = "usb3h1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		usb3h1_pins_sleep: usb3h1_sleep {
+			mb86s7x,function = "usb3h1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		cfg_pins_active: cfg_active {
+			mb86s7x,function = "cfg";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		cfg_pins_sleep: cfg_sleep {
+			mb86s7x,function = "cfg";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart0_pins_active: uart0_active {
+			mb86s7x,function = "uart0";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart0_pins_sleep: uart0_sleep {
+			mb86s7x,function = "uart0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart1_pins_active: uart1_active {
+			mb86s7x,function = "uart1";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart1_pins_sleep: uart1_sleep {
+			mb86s7x,function = "uart1";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		uart2_pins_active: uart2_active {
+			mb86s7x,function = "uart2";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		uart2_pins_sleep: uart2_sleep {
+			mb86s7x,function = "uart2";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		trace_pins_active: trace_active {
+			mb86s7x,function = "trace";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		trace_pins_sleep: trace_sleep {
+			mb86s7x,function = "trace";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		pl244_pins_active: pl244_active {
+			mb86s7x,function = "pl244";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		pl244_pins_sleep: pl244_sleep {
+			mb86s7x,function = "pl244";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		smt_pins_active: smt_active {
+			mb86s7x,function = "smt";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		smt_pins_sleep: smt_sleep {
+			mb86s7x,function = "smt";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+
+		memcs_pins_active: memcs_active {
+			mb86s7x,function = "memcs";
+			mb86s7x,drvst = <4>; /* in mA */
+		};
+		memcs_pins_sleep: memcs_sleep {
+			mb86s7x,function = "memcs";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+	};
+
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	gpio1: mb86s70_gpio1 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31010000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 32 32>;
+		clocks = <&clk_alw_2_1>;
+	};
+
+	pd_alwayson: genpd at 0 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <0>;
+	};
+
+	pd_default: genpd at 1 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <1>;
+	};
+
+	pd_offchip: genpd at 2 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <2>;
+	};
+
+	pd_cpu: genpd at 3 {
+		compatible = "fujitsu,mb86s7x-pd";
+		index = <3>;
+	};
+
+	gic: interrupt-controller at 2c001000 {
+		compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
+		#interrupt-cells = <3>;
+		interrupt-controller;
+		reg = <0 0x2c001000 0x1000>,
+		      <0 0x2c002000 0x1000>,
+		      <0 0x2c004000 0x2000>,
+		      <0 0x2c006000 0x2000>;
+		interrupts = <1 9 0xf04>;
+	};
+
+	pmu_a7 {
+		compatible = "arm,cortex-a7-pmu";
+		interrupts = <0 18 4>,
+			     <0 22 4>;
+		fujitsu,power-domain = <&pd_cpu>;
+	};
+
+	mhu: mhu0 at 2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+		fujitsu,power-domain = <&pd_default>;
+	};
+
+	mhu_client: scb at 0 {
+		compatible = "fujitsu,scb";
+		mbox = <&mhu 1>;
+		mbox-names = "HP_NonSec";
+	};
+
+	uart0: serial at 0x31040000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31040000 0x100>;
+		interrupts = <0 320 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart0_pins_active>;
+		pinctrl-1 = <&uart0_pins_sleep>;
+	};
+
+	uart1: serial at 0x31050000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31050000 0x100>;
+		interrupts = <0 321 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart1_pins_active>;
+		pinctrl-1 = <&uart1_pins_sleep>;
+	};
+
+	uart2: serial at 0x31060000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0 0x31060000 0x100>;
+		interrupts = <0 322 0x4>;
+		clock-frequency = <62500000>;
+		reg-io-width = <4>;
+		reg-shift = <2>;
+		clocks = <&clk_alw_2_1>;
+		clock-names = "sclk";
+		fujitsu,power-domain = <&pd_default>;
+		pinctrl-names = "default", "sleep";
+		pinctrl-0 = <&uart2_pins_active>;
+		pinctrl-1 = <&uart2_pins_sleep>;
+	};
+
+	sdhci0: emmc at 300c0000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x300c0000 0x1000>;
+		interrupts = <0 164 0x4>,
+			     <0 165 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		bus-width = <8>;
+		clocks = <&clk_alw_c_0>, <&clk_alw_b_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+		fujitsu,power-domain = <&pd_default>;
+	};
+
+	sdhci1: sdio at 36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		clocks = <&clk_main_c_0>, <&clk_main_d_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+		resume-detect-retry;
+		fujitsu,power-domain = <&pd_offchip>;
+	};
+
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+		fujitsu,power-domain = <&pd_default>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy at 1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
+};
diff --git a/arch/arm/boot/dts/mb86s73eb.dts b/arch/arm/boot/dts/mb86s73eb.dts
new file mode 100644
index 0000000..81862d9
--- /dev/null
+++ b/arch/arm/boot/dts/mb86s73eb.dts
@@ -0,0 +1,73 @@
+
+/include/ "mb86s73.dtsi"
+
+#include <dt-bindings/gpio/gpio.h>
+#include <dt-bindings/input/input.h>
+
+/ {
+	model = "Fujitsu MB86S73 EVB";
+	compatible = "fujitsu,mb86s73-evb";
+
+	memory {
+		device_type = "memory";
+		reg = <0 0x80000000 0x80000000>;
+	};
+
+	chosen {
+		bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw";
+	};
+
+	gpio-leds {
+		compatible = "gpio-leds";
+
+		d34_a {
+			label = "led34a";
+			gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "heartbeat";
+		};
+		d34_b {
+			label = "led34b";
+			gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "mmc1";
+		};
+		d37 {
+			label = "led37";
+			gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
+			linux,default-trigger = "default-on";
+		};
+		d38 {
+			label = "led38";
+			gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
+		};
+	};
+
+	gpio-keys {
+		compatible = "gpio-keys-polled";
+		poll-interval = <20>;
+		home {
+			label = "Home";
+			gpios = <&gpio1 4 0>;
+			linux,code = <KEY_HOME>;
+			gpio-key;
+		};
+		power {
+			label = "Power";
+			gpios = <&gpio1 5 0>;
+			linux,code = <KEY_POWER>;
+			gpio-key;
+		};
+
+		up {
+			label = "Up";
+			gpios = <&gpio1 6 0>;
+			linux,code = <KEY_UP>;
+			gpio-key;
+		};
+		down {
+			label = "Down";
+			gpios = <&gpio1 7 0>;
+			linux,code = <KEY_DOWN>;
+			gpio-key;
+		};
+	};
+};
diff --git a/arch/arm/configs/fujitsu_defconfig b/arch/arm/configs/fujitsu_defconfig
new file mode 100644
index 0000000..bfc78c9
--- /dev/null
+++ b/arch/arm/configs/fujitsu_defconfig
@@ -0,0 +1,156 @@
+# CONFIG_LOCALVERSION_AUTO is not set
+CONFIG_DEFAULT_HOSTNAME="MB86S7x"
+CONFIG_SYSVIPC=y
+CONFIG_NO_HZ=y
+CONFIG_HIGH_RES_TIMERS=y
+CONFIG_IKCONFIG=y
+CONFIG_IKCONFIG_PROC=y
+CONFIG_NAMESPACES=y
+CONFIG_BLK_DEV_INITRD=y
+CONFIG_RD_BZIP2=y
+CONFIG_RD_LZMA=y
+CONFIG_RD_XZ=y
+CONFIG_RD_LZO=y
+CONFIG_CC_OPTIMIZE_FOR_SIZE=y
+CONFIG_EXPERT=y
+# CONFIG_COMPAT_BRK is not set
+CONFIG_SLAB=y
+CONFIG_PROFILING=y
+CONFIG_MODULES=y
+CONFIG_MODULE_UNLOAD=y
+CONFIG_ARCH_MB86S7X=y
+CONFIG_ARM_LPAE=y
+# CONFIG_SWP_EMULATE is not set
+CONFIG_ARM_ERRATA_754322=y
+CONFIG_ARM_ERRATA_754327=y
+CONFIG_ARM_ERRATA_764369=y
+CONFIG_ARM_ERRATA_775420=y
+CONFIG_ARM_ERRATA_798181=y
+CONFIG_PCI=y
+CONFIG_PCIEASPM_POWERSAVE=y
+CONFIG_SMP=y
+CONFIG_SCHED_MC=y
+CONFIG_SCHED_SMT=y
+CONFIG_BL_SWITCHER=y
+CONFIG_BL_SWITCHER_DUMMY_IF=m
+CONFIG_VMSPLIT_2G=y
+CONFIG_PREEMPT=y
+CONFIG_THUMB2_KERNEL=y
+CONFIG_HIGHMEM=y
+CONFIG_CMA=y
+# CONFIG_ATAGS is not set
+CONFIG_ZBOOT_ROM_TEXT=0x0
+CONFIG_ZBOOT_ROM_BSS=0x0
+CONFIG_CMDLINE="mem=2048M earlycon=ttyS0,115200 earlyprintk console=ttyS0,115200 console=tty0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
+CONFIG_KEXEC=y
+CONFIG_CPU_IDLE=y
+CONFIG_VFP=y
+CONFIG_NEON=y
+CONFIG_PM_WAKELOCKS=y
+CONFIG_PM_RUNTIME=y
+CONFIG_PM_DEBUG=y
+CONFIG_NET=y
+CONFIG_PACKET=y
+CONFIG_UNIX=y
+CONFIG_INET=y
+CONFIG_IP_MULTICAST=y
+CONFIG_IP_PNP=y
+CONFIG_IP_PNP_DHCP=y
+CONFIG_IP_PNP_BOOTP=y
+# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
+# CONFIG_INET_XFRM_MODE_TUNNEL is not set
+# CONFIG_INET_XFRM_MODE_BEET is not set
+# CONFIG_INET_LRO is not set
+CONFIG_IPV6=y
+CONFIG_INET6_XFRM_MODE_TRANSPORT=m
+CONFIG_INET6_XFRM_MODE_TUNNEL=m
+CONFIG_INET6_XFRM_MODE_BEET=m
+CONFIG_IPV6_SIT=m
+# CONFIG_ANDROID_PARANOID_NETWORK is not set
+# CONFIG_WIRELESS is not set
+CONFIG_DEVTMPFS=y
+CONFIG_DEVTMPFS_MOUNT=y
+# CONFIG_FW_LOADER_USER_HELPER is not set
+CONFIG_DMA_CMA=y
+CONFIG_CMA_SIZE_MBYTES=192
+CONFIG_BLK_DEV_LOOP=m
+CONFIG_BLK_DEV_CRYPTOLOOP=m
+CONFIG_BLK_DEV_RAM=y
+CONFIG_BLK_DEV_RAM_COUNT=2
+CONFIG_BLK_DEV_RAM_SIZE=1024
+# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
+CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
+CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
+CONFIG_INPUT_EVDEV=m
+# CONFIG_KEYBOARD_ATKBD is not set
+CONFIG_KEYBOARD_GPIO_POLLED=y
+# CONFIG_MOUSE_PS2 is not set
+CONFIG_MOUSE_GPIO=m
+CONFIG_INPUT_TOUCHSCREEN=y
+CONFIG_TOUCHSCREEN_EGALAX=m
+CONFIG_TOUCHSCREEN_ILI210X=m
+# CONFIG_SERIO_SERPORT is not set
+CONFIG_VT_HW_CONSOLE_BINDING=y
+CONFIG_LEGACY_PTY_COUNT=16
+# CONFIG_DEVKMEM is not set
+CONFIG_SERIAL_8250=y
+# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
+CONFIG_SERIAL_8250_CONSOLE=y
+CONFIG_SERIAL_8250_EXTENDED=y
+CONFIG_SERIAL_8250_DW=y
+CONFIG_SERIAL_OF_PLATFORM=y
+# CONFIG_HW_RANDOM is not set
+CONFIG_I2C=y
+# CONFIG_I2C_COMPAT is not set
+# CONFIG_I2C_HELPER_AUTO is not set
+CONFIG_GPIO_SYSFS=y
+# CONFIG_HWMON is not set
+# CONFIG_VGA_ARB is not set
+# CONFIG_HID is not set
+# CONFIG_USB_SUPPORT is not set
+CONFIG_MMC=y
+CONFIG_MMC_CLKGATE=y
+CONFIG_MMC_SDHCI=y
+CONFIG_MMC_SDHCI_F_SDH30=y
+CONFIG_NEW_LEDS=y
+CONFIG_LEDS_CLASS=y
+CONFIG_LEDS_GPIO=y
+CONFIG_LEDS_TRIGGERS=y
+CONFIG_LEDS_TRIGGER_TIMER=y
+CONFIG_LEDS_TRIGGER_HEARTBEAT=y
+CONFIG_LEDS_TRIGGER_BACKLIGHT=m
+CONFIG_LEDS_TRIGGER_CPU=y
+CONFIG_LEDS_TRIGGER_GPIO=y
+CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
+CONFIG_LEDS_TRIGGER_TRANSIENT=m
+CONFIG_MAILBOX=y
+CONFIG_MBOX_F_MHU=y
+# CONFIG_IOMMU_SUPPORT is not set
+CONFIG_EXT4_FS=y
+CONFIG_EXT4_FS_POSIX_ACL=y
+CONFIG_EXT4_FS_SECURITY=y
+CONFIG_VFAT_FS=m
+CONFIG_TMPFS=y
+CONFIG_CONFIGFS_FS=m
+CONFIG_CRAMFS=y
+CONFIG_ROMFS_FS=y
+CONFIG_NLS_CODEPAGE_437=m
+CONFIG_NLS_ISO8859_1=m
+CONFIG_PRINTK_TIME=y
+CONFIG_DEBUG_INFO=y
+CONFIG_DEBUG_FS=y
+CONFIG_MAGIC_SYSRQ=y
+CONFIG_DEBUG_MEMORY_INIT=y
+# CONFIG_DEBUG_PREEMPT is not set
+# CONFIG_RCU_CPU_STALL_VERBOSE is not set
+# CONFIG_FTRACE is not set
+CONFIG_DEBUG_USER=y
+# CONFIG_DEBUG_LL is not set
+CONFIG_DEBUG_LL_UART_8250=y
+CONFIG_DEBUG_UART_PHYS=0x31040000
+CONFIG_DEBUG_UART_VIRT=0xfe000000
+CONFIG_DEBUG_UART_8250_WORD=y
+# CONFIG_EARLY_PRINTK is not set
+CONFIG_CRYPTO_AES=y
+# CONFIG_CRYPTO_ANSI_CPRNG is not set
+# CONFIG_CRYPTO_HW is not set
diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
new file mode 100644
index 0000000..44f5b0c
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/Kconfig
@@ -0,0 +1,18 @@
+config ARCH_MB86S7X
+	bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
+	select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
+	select ARM_AMBA
+	select ARM_GIC
+	select ARM_TIMER_SP804
+	select HAVE_ARM_ARCH_TIMER
+	select ARCH_REQUIRE_GPIOLIB
+	select ARCH_HAS_CPUFREQ
+	select ARCH_HAS_OPP
+	select PM_OPP
+	select PINCTRL
+	select PINCTRL_MB86S7X
+	select ARM_CCI
+	select BIG_LITTLE
+	select PM_GENERIC_DOMAINS if PM
+	help
+	  Support for Fujitsu MB86S7x based platforms
diff --git a/arch/arm/mach-mb86s7x/Makefile b/arch/arm/mach-mb86s7x/Makefile
new file mode 100644
index 0000000..5524a6c
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/Makefile
@@ -0,0 +1,2 @@
+obj-y		+= board.o scb_mhu.o mcpm.o
+obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
new file mode 100644
index 0000000..d6e76ec
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/board.c
@@ -0,0 +1,65 @@
+/*
+ * Support for the Fujitsu's MB86S7x based devices.
+ *
+ * Copyright (C) 2014 Linaro, LTD
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ */
+
+#include <linux/of.h>
+
+#include <asm/mcpm.h>
+#include <asm/mach/map.h>
+#include <asm/mach/arch.h>
+
+#include "iomap.h"
+
+bool __init mb86s7x_smp_init_ops(void)
+{
+	struct device_node *node;
+
+	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
+	if (node && of_device_is_available(node)) {
+		mcpm_smp_set_ops();
+		return true;
+	}
+
+	return false;
+}
+
+#define IOMAP_DEV(name) { \
+		.virtual = (unsigned long) MB86S7X_##name##_VIRT, \
+		.pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
+		.length = MB86S7X_##name##_SIZE, \
+		.type = MT_DEVICE, \
+	}
+
+static struct map_desc mb86s7x_io_desc[] = {
+	IOMAP_DEV(MHU),
+	IOMAP_DEV(ISRAM),
+};
+
+void __init mb86s7x_dt_map_io(void)
+{
+	iotable_init(mb86s7x_io_desc, ARRAY_SIZE(mb86s7x_io_desc));
+}
+
+static const char *mb86s7x_dt_match[] __initconst = {
+	"fujitsu,mb86s70-evb",
+	"fujitsu,mb86s73-evb",
+	NULL,
+};
+
+DT_MACHINE_START(MB86S7X_DT, "Fujitsu MB86S7X-based board")
+	.dt_compat	= mb86s7x_dt_match,
+	.smp_init	= smp_init_ops(mb86s7x_smp_init_ops),
+	.map_io		= mb86s7x_dt_map_io,
+MACHINE_END
diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
new file mode 100644
index 0000000..2b8db1d
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/iomap.h
@@ -0,0 +1,34 @@
+/*
+ * arch/arm/mach-mb86s7x/iomap.h
+ *
+ * Created by: Jassi Brar <jassisinghbrar@gmail.com>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MB86S7X_IOMAP_H
+#define __MB86S7X_IOMAP_H
+
+#include <linux/sizes.h>
+
+#define SEC_RSTADDR_OFF		0x3fc
+
+#define MB86S7X_MHU_PHYS	0x2b1f0000
+#define MB86S7X_MHU_SIZE	SZ_4K
+
+/* Internal SRAM */
+#define MB86S7X_ISRAM_PHYS	0x2e000000
+#define MB86S7X_ISRAM_SIZE	SZ_16K
+#define MB86S7X_TRAMPOLINE_PHYS	(MB86S7X_ISRAM_PHYS + 0x3c00)
+
+#define MB86S7X_ISRAM_VIRT	IOMEM(0xfe000000)
+#define MB86S7X_MHU_VIRT	(MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
+
+/* Non-Secure High-Priority Channel is used */
+#define MB86S7X_SHM_FROM_SCB_VIRT	(MB86S7X_ISRAM_VIRT + 0x3800)
+#define MB86S7X_TRAMPOLINE_VIRT		(MB86S7X_ISRAM_VIRT + 0x3c00)
+
+#endif /* __MB86S7X_IOMAP_H */
diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
new file mode 100644
index 0000000..86d223f
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/mcpm.c
@@ -0,0 +1,293 @@
+/*
+ * arch/arm/mach-mb86s7x/mcpm.c
+ *
+ * "Inspired" by tc_pm.c
+ * Copyright:	(C) 2013-2014  Fujitsu Semiconductor Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/pm.h>
+#include <linux/delay.h>
+#include <linux/kernel.h>
+#include <linux/reboot.h>
+#include <linux/arm-cci.h>
+#include <linux/spinlock.h>
+#include <linux/irqchip/arm-gic.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#include <asm/mcpm.h>
+#include <asm/cp15.h>
+#include <asm/cputype.h>
+#include <asm/system_misc.h>
+
+#include "iomap.h"
+
+static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
+static int mb86s7x_pm_use_count[2][2];
+
+#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
+
+static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
+{
+	u8 val;
+
+	if (clr & ~AT_WFI_COLOR_MASK)
+		return;
+
+	val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
+	val &= ~AT_WFI_COLOR_MASK;
+	val |= clr;
+	writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
+}
+
+static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
+{
+	struct completion got_rsp;
+	int ret = 0;
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+
+	arch_spin_lock(&mb86s7x_pm_lock);
+
+	mb86s7x_pm_use_count[cpu][cluster]++;
+
+	if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
+		struct mb86s7x_cpu_gate cmd;
+
+		arch_spin_unlock(&mb86s7x_pm_lock);
+		cmd.payload_size = sizeof(cmd);
+		cmd.cluster_class = 0;
+		cmd.cluster_id = cluster;
+		cmd.cpu_id = cpu;
+		cmd.cpu_state = SCB_CPU_STATE_ON;
+
+		pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
+			 __func__, __LINE__, cmd.cluster_class,
+			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
+
+		init_completion(&got_rsp);
+		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
+		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed!\n", __func__, __LINE__);
+			return ret;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+
+		pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
+			 __func__, __LINE__, cmd.cluster_class,
+			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
+
+		if (cmd.cpu_state != SCB_CPU_STATE_ON)
+			return -ENODEV;
+
+	} else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
+		/*
+		 * The only possible values are:
+		 * 0 = CPU down
+		 * 1 = CPU (still) up
+		 * 2 = CPU requested to be up before it had a chance
+		 *     to actually make itself down.
+		 * Any other value is a bug.
+		 */
+		BUG();
+	}
+
+	arch_spin_unlock(&mb86s7x_pm_lock);
+
+	return 0;
+}
+
+static void mb86s7x_pm_suspend(u64 ignored)
+{
+	unsigned int mpidr, cpu, cluster;
+	bool last_man = false, skip_wfi = false;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
+	__mcpm_cpu_going_down(cpu, cluster);
+
+	arch_spin_lock(&mb86s7x_pm_lock);
+	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
+
+	mb86s7x_pm_use_count[cpu][cluster]--;
+
+	if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
+		if (!mb86s7x_pm_use_count[0][cluster] &&
+		    !mb86s7x_pm_use_count[1][cluster])
+			last_man = true;
+		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
+	} else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
+		skip_wfi = true; /* Overtaken by a power up */
+	} else {
+		BUG();
+	}
+
+	if (!skip_wfi)
+		gic_cpu_if_down();
+
+	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
+		arch_spin_unlock(&mb86s7x_pm_lock);
+
+		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
+			/*
+			 * On the Cortex-A15 we need to disable
+			 * L2 prefetching before flushing the cache.
+			 */
+			asm volatile(
+			"mcr p15, 1, %0, c15, c0, 3\n\t"
+			"isb\n\t"
+			"dsb"
+			: : "r" (0x400));
+		}
+
+		v7_exit_coherency_flush(all);
+
+		cci_disable_port_by_cpu(mpidr);
+
+		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
+	} else {
+		arch_spin_unlock(&mb86s7x_pm_lock);
+		v7_exit_coherency_flush(louis);
+	}
+
+	__mcpm_cpu_down(cpu, cluster);
+
+	/* Now we are prepared for power-down, do it: */
+	if (!skip_wfi)
+		wfi();
+
+	/* Not dead at this point?  Let our caller cope. */
+}
+
+static void mb86s7x_pm_power_down(void)
+{
+	mb86s7x_pm_suspend(0);
+}
+
+static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
+{
+	struct mb86s7x_cpu_gate cmd;
+	struct completion got_rsp;
+	int i, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cluster_class = 0;
+	cmd.cluster_id = cluster;
+	cmd.cpu_id = cpu;
+	cmd.cpu_state = SCB_CPU_STATE_ON;
+
+	for (i = 0; i < 50; i++) {
+		init_completion(&got_rsp);
+		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed to get CPU status\n",
+			       __func__, __LINE__);
+			return -ETIMEDOUT;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+
+		pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
+			 __func__, __LINE__,
+			 cmd.cluster_class, cmd.cluster_id,
+			 cmd.cpu_id, cmd.cpu_state);
+
+		if (cmd.cpu_state == SCB_CPU_STATE_OFF)
+			return 0;
+
+		msleep(20);
+	}
+
+	return -ETIMEDOUT;
+}
+
+static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
+	.power_up		= mb86s7x_pm_power_up,
+	.power_down		= mb86s7x_pm_power_down,
+	.wait_for_powerdown	= mb86s7x_wait_for_powerdown,
+	.suspend		= mb86s7x_pm_suspend,
+};
+
+static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
+{
+	/* Reboot immediately */
+	mb86s7x_reboot(50);
+}
+
+static void mb86s7x_poweroff(void)
+{
+	/* Reboot never, remain dead */
+	mb86s7x_reboot(~0);
+}
+
+/*
+ * Enable cluster-level coherency, in preparation for turning on the MMU.
+ */
+static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
+{
+	asm volatile ("\n"
+"	cmp	r0, #1\n"
+"	bxne	lr\n"
+"	b	cci_enable_port_for_self");
+}
+
+static int __init mb86s7x_mcpm_init(void)
+{
+	unsigned int mpidr, cpu, cluster;
+	struct mb86s7x_scb_version cmd;
+	struct completion got_rsp;
+	int ret;
+
+	arm_pm_restart = mb86s7x_restart;
+	pm_power_off = mb86s7x_poweroff;
+
+	if (!cci_probed())
+		return -ENODEV;
+
+	mpidr = read_cpuid_mpidr();
+	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
+	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
+
+	pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
+	mb86s7x_pm_use_count[cpu][cluster] = 1;
+
+	/* reset the wfi 'color' for primary cpu */
+	mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
+
+	/* Set entry point for any CPU nowonwards */
+	writel_relaxed(virt_to_phys(mcpm_entry_point),
+		     MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.version = 0;
+	cmd.config_version = 0;
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
+			      &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0)
+		pr_err("%s:%d failed to get SCB version\n",
+		       __func__, __LINE__);
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_info("MB86S7x MCPM initialized: SCB version 0x%x:0x%x\n",
+		cmd.version, cmd.config_version);
+
+	ret = mcpm_platform_register(&mb86s7x_pm_power_ops);
+	if (!ret)
+		ret = mcpm_sync_init(mb86s7x_pm_power_up_setup);
+
+	return ret;
+}
+early_initcall(mb86s7x_mcpm_init);
diff --git a/arch/arm/mach-mb86s7x/pm_domains.c b/arch/arm/mach-mb86s7x/pm_domains.c
new file mode 100644
index 0000000..1fec5ed
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/pm_domains.c
@@ -0,0 +1,237 @@
+/*
+ * mb86s7x generic powerdomain support
+ * Copyright (C) 2014 Linaro, Ltd  Andy Green <andy.green@linaro.org>
+ *
+ * based on -->
+ *
+ * Exynos Generic power domain support.
+ *
+ * Copyright (c) 2012 Samsung Electronics Co., Ltd.
+ *		http://www.samsung.com
+ *
+ * Implementation of Exynos specific power domain control which is used in
+ * conjunction with runtime-pm. Support for both device-tree and non-device-tree
+ * based power domain support is included.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+*/
+
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/pm_domain.h>
+#include <linux/delay.h>
+#include <linux/of_address.h>
+#include <linux/of_platform.h>
+#include <linux/sched.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+/*
+ * mb86s7x specific wrapper around the generic power domain
+ */
+struct mb86s7x_pm_domain {
+	struct generic_pm_domain pd;
+	char const *name;
+	int powerdomain_index;
+	bool is_off;
+};
+
+static int mb86s7x_pd_power(struct generic_pm_domain *domain, bool power_on)
+{
+	struct mb86s7x_pm_domain *pd;
+	struct mb86s7x_pmd_cmd cmd;
+	struct completion got_rsp;
+	int ret;
+
+	pd = container_of(domain, struct mb86s7x_pm_domain, pd);
+
+	pr_debug("%s: domain %s <- %d\n", __func__, pd->name, power_on);
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.powerdomain_index = pd->powerdomain_index;
+	cmd.state = power_on;
+	if (pd->powerdomain_index >= 0) {
+		pr_info("First powerdomain index1 set :%d .\n",
+			pd->powerdomain_index);
+		init_completion(&got_rsp);
+
+		ret = mhu_send_packet(CMD_POWERDOMAIN_SET_REQ,
+				      &cmd, sizeof(cmd), &got_rsp);
+		if (ret < 0) {
+			pr_err("%s:%d failed to set powerdomain\n",
+			       __func__, __LINE__);
+			return ret;
+		}
+		if (ret)
+			wait_for_completion(&got_rsp);
+	}
+	pd->is_off = !power_on;
+	return 0;
+}
+
+static int mb86s7x_pd_power_on(struct generic_pm_domain *domain)
+{
+	return mb86s7x_pd_power(domain, true);
+}
+
+static int mb86s7x_pd_power_off(struct generic_pm_domain *domain)
+{
+	return mb86s7x_pd_power(domain, false);
+}
+
+static void mb86s7x_add_device_to_domain(struct mb86s7x_pm_domain *pd,
+					 struct device *dev)
+{
+	int ret;
+
+	dev_info(dev, "adding to power domain %s\n", pd->pd.name);
+
+	while (1) {
+		ret = pm_genpd_add_device(&pd->pd, dev);
+		if (ret != -EAGAIN)
+			break;
+		cond_resched();
+	}
+
+	pm_genpd_dev_need_restore(dev, true);
+}
+
+static void mb86s7x_remove_device_from_domain(struct device *dev)
+{
+	struct generic_pm_domain *genpd = dev_to_genpd(dev);
+	int ret;
+
+	if (genpd == ERR_PTR(-EINVAL)) {
+		dev_dbg(dev, "device has no power domain\n");
+		return;
+	}
+
+	dev_info(dev, "removing from power domain %s\n", genpd->name);
+
+	while (1) {
+		ret = pm_genpd_remove_device(genpd, dev);
+		if (ret != -EAGAIN)
+			break;
+		cond_resched();
+	}
+}
+
+static void mb86s7x_read_domain_from_dt(struct device *dev)
+{
+	struct platform_device *pd_pdev;
+	struct mb86s7x_pm_domain *pd;
+	struct device_node *node;
+
+	node = of_parse_phandle(dev->of_node, "fujitsu,power-domain", 0);
+	if (!node)
+		return;
+	pd_pdev = of_find_device_by_node(node);
+	if (!pd_pdev)
+		return;
+	pd = platform_get_drvdata(pd_pdev);
+	mb86s7x_add_device_to_domain(pd, dev);
+}
+
+static int mb86s7x_pm_notifier_call(struct notifier_block *nb,
+				    unsigned long event, void *data)
+{
+	struct device *dev = data;
+
+	switch (event) {
+	case BUS_NOTIFY_BIND_DRIVER:
+		if (dev->of_node)
+			mb86s7x_read_domain_from_dt(dev);
+
+		break;
+
+	case BUS_NOTIFY_UNBOUND_DRIVER:
+		mb86s7x_remove_device_from_domain(dev);
+
+		break;
+	}
+	return NOTIFY_DONE;
+}
+
+static struct notifier_block platform_nb = {
+	.notifier_call = mb86s7x_pm_notifier_call,
+};
+
+static __init int mb86s7x_pm_init_power_domain(void)
+{
+	struct platform_device *pdev, *master_pd_pdev;
+	struct device_node *np, *master_node;
+	struct mb86s7x_pmd_cmd cmd;
+	struct completion got_rsp;
+	int ret;
+
+	for_each_compatible_node(np, NULL, "fujitsu,mb86s7x-pd") {
+		struct mb86s7x_pm_domain *pd, *master_pd;
+
+		pdev = of_find_device_by_node(np);
+
+		pd = kzalloc(sizeof(*pd), GFP_KERNEL);
+		if (!pd) {
+			pr_err("%s: failed to allocate memory for domain\n",
+			       __func__);
+			return -ENOMEM;
+		}
+
+		pd->pd.name = kstrdup(np->name, GFP_KERNEL);
+		pd->name = pd->pd.name;
+		pd->pd.power_off = mb86s7x_pd_power_off;
+		pd->pd.power_on = mb86s7x_pd_power_on;
+		pd->pd.of_node = np;
+
+		if (of_property_read_u32(np, "index", &pd->powerdomain_index))
+			pd->powerdomain_index = -1;
+
+		platform_set_drvdata(pdev, pd);
+
+		/* first power domain */
+		cmd.payload_size = sizeof(cmd);
+		cmd.powerdomain_index = pd->powerdomain_index;
+		cmd.state = 0;
+
+		if (pd->powerdomain_index >= 0) {
+			init_completion(&got_rsp);
+
+			ret = mhu_send_packet(CMD_POWERDOMAIN_GET_REQ,
+					      &cmd, sizeof(cmd), &got_rsp);
+			if (ret < 0)
+				pr_err("%s:%d failed to get SCB version\n",
+				       __func__, __LINE__);
+			if (ret)
+				wait_for_completion(&got_rsp);
+		}
+
+		pm_genpd_init(&pd->pd, NULL, !cmd.state);
+
+		dev_info(&pdev->dev, "power domain %s starting\n", pd->pd.name);
+
+		/* find any master domain and register self to it*/
+		master_node = of_parse_phandle(np, "fujitsu,power-domain", 0);
+		if (!master_node)
+			continue;
+		master_pd_pdev = of_find_device_by_node(master_node);
+		if (!master_pd_pdev) {
+			pr_err("sneeker : this should never happened.\n");
+			continue;
+		}
+		master_pd = platform_get_drvdata(master_pd_pdev);
+		while (1) {
+			ret = pm_genpd_add_subdomain(&master_pd->pd, &pd->pd);
+			if (ret != -EAGAIN)
+				break;
+			cond_resched();
+		}
+	}
+
+	bus_register_notifier(&platform_bus_type, &platform_nb);
+
+	pr_info("mb86s7x Power Domain support initialized\n");
+
+	return 0;
+}
+arch_initcall(mb86s7x_pm_init_power_domain);
diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
new file mode 100644
index 0000000..fd5f034
--- /dev/null
+++ b/arch/arm/mach-mb86s7x/scb_mhu.c
@@ -0,0 +1,447 @@
+/*
+ * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
+ *
+ * Created by: Jassi Brar <jassisinghbrar@gmail.com>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#include "iomap.h"
+
+#define INTR_STAT_OFS	0x0
+#define INTR_SET_OFS	0x8
+#define INTR_CLR_OFS	0x10
+
+static int do_xfer(void);
+static void try_xfer(struct work_struct *ignored);
+
+static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
+static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
+static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
+static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
+
+static LIST_HEAD(free_xfers);
+static LIST_HEAD(pending_xfers);
+static DEFINE_SPINLOCK(fsm_lock);
+static struct mbox_client mhu_cl;
+static struct mbox_chan *mhu_chan;
+static DECLARE_WORK(scb_work, try_xfer);
+static mhu_handler_t handler[MHU_NUM_CMDS];
+
+static enum {
+	MHU_PARK = 0,
+	MHU_WRR, /* Waiting to get Remote's Reply */
+	MHU_WRL, /* Waiting to send Reply */
+	MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
+	MHU_INVLD,
+} fsm_state;
+
+enum fsm_event {
+	EV_LC = 0, /* Local sent a command */
+	EV_RC, /* Remote sent a command */
+	EV_RR, /* Remote sent a reply */
+	EV_LR, /* Local sent a reply */
+};
+
+static int mhu_fsm[4][4] = {
+	[MHU_PARK] = {
+		[EV_LC] = MHU_WRR,
+		[EV_RC] = MHU_WRL,
+		[EV_RR] = MHU_INVLD,
+		[EV_LR] = MHU_INVLD,
+	},
+	[MHU_WRR] = {
+		[EV_LC] = MHU_INVLD,
+		[EV_RC] = MHU_WRRL,
+		[EV_RR] = MHU_PARK,
+		[EV_LR] = MHU_INVLD,
+	},
+	[MHU_WRL] = {
+		[EV_LC] = MHU_WRRL,
+		[EV_RC] = MHU_INVLD,
+		[EV_RR] = MHU_INVLD,
+		[EV_LR] = MHU_PARK,
+	},
+	[MHU_WRRL] = {
+		[EV_LC] = MHU_INVLD,
+		[EV_RC] = MHU_INVLD,
+		[EV_RR] = MHU_WRL,
+		[EV_LR] = MHU_WRR,
+	},
+};
+
+struct mhu_xfer {
+	int code;
+	int len;
+	void *buf;
+	struct completion *c;
+	struct list_head node;
+};
+
+static struct mhu_xfer *ax; /* stages of xfer */
+
+static int mhu_alloc_xfers(int n, struct list_head *list)
+{
+	struct mhu_xfer *x = kcalloc(n, sizeof(struct mhu_xfer), GFP_ATOMIC);
+	int i;
+
+	if (!x)
+		return -ENOMEM;
+
+	for (i = 0; i < n; i++)
+		list_add(&x[i].node, &free_xfers);
+
+	return 0;
+}
+
+static void try_xfer(struct work_struct *ignored)
+{
+	/* If polling overtook the scheduled work */
+	if (mhu_chan == NULL)
+		return;
+
+	/* Now that we got a reply to last TX, that
+	 * must mean the last TX was successful */
+	mbox_client_txdone(mhu_chan, 0);
+
+	do_xfer();
+}
+
+static void got_data(u32 code)
+{
+	struct completion *c = NULL;
+	mhu_handler_t hndlr = NULL;
+	unsigned long flags;
+	int ev;
+
+	if (code & RESP_BIT)
+		ev = EV_RR;
+	else
+		ev = EV_RC;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
+		return;
+	}
+	fsm_state = mhu_fsm[fsm_state][ev];
+
+	if (code & RESP_BIT) {
+		c = ax->c;
+		memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
+		list_move(&ax->node, &free_xfers);
+		ax = NULL;
+	} else {
+		/* Find and dispatch relevant registered handler */
+		if (code < MHU_NUM_CMDS)
+			hndlr = handler[code];
+		if (!hndlr)
+			pr_err("No handler for CMD_%u\n", code);
+	}
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	if (hndlr)
+		hndlr(code, cmd_from_scb);
+	if (c)
+		complete(c);
+}
+
+static void mhu_recv(struct mbox_client *cl, void *data)
+{
+	got_data((u32)data);
+	schedule_work(&scb_work);
+}
+
+static int do_xfer(void)
+{
+	unsigned long flags;
+	struct mhu_xfer *x;
+	int ev;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (list_empty(&pending_xfers)) {
+		struct mbox_chan *_ch = NULL;
+		int cmd;
+
+		for (cmd = 0; cmd < MHU_NUM_CMDS && !handler[cmd]; cmd++)
+			;
+		/* Don't free channel if any user is listening */
+		if (cmd != MHU_NUM_CMDS) {
+			spin_unlock_irqrestore(&fsm_lock, flags);
+			return 1;
+		}
+
+		if (fsm_state == MHU_PARK) {
+			_ch = mhu_chan;
+			mhu_chan = NULL;
+		}
+
+		spin_unlock_irqrestore(&fsm_lock, flags);
+
+		if (_ch)
+			mbox_free_channel(_ch);
+
+		return 1;
+	}
+
+	x = list_first_entry(&pending_xfers, struct mhu_xfer, node);
+	ev = x->code & RESP_BIT ? EV_LR : EV_LC;
+
+	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return 1;
+	}
+	list_del_init(&x->node);
+
+	/* Layout the SHM */
+	if (x->code & RESP_BIT)
+		memcpy_toio(rsp_to_scb, x->buf, x->len);
+	else
+		memcpy_toio(cmd_to_scb, x->buf, x->len);
+
+	ev = x->code & RESP_BIT ? EV_LR : EV_LC;
+	if (ev == EV_LC)
+		ax = x;
+	else
+		list_move(&x->node, &free_xfers);
+	fsm_state = mhu_fsm[fsm_state][ev];
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	/* Prefer mailbox API */
+	if (!mhu_chan) {
+		struct mbox_chan *_ch;
+
+		_ch = mbox_request_channel(&mhu_cl);
+		if (!IS_ERR(_ch))
+			mhu_chan = _ch;
+	}
+
+	if (mhu_chan) {
+		/* Send via generic api */
+		mbox_send_message(mhu_chan, (void *)x->code);
+	} else {
+		void __iomem *tx_reg = MB86S7X_MHU_VIRT + 0x120; /* HP-NonSec */
+		void __iomem *rx_reg = MB86S7X_MHU_VIRT + 0x20; /* HP-NonSec */
+		u32 val, count;
+
+		/* Send via early-boot api */
+		val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+		if (val) {
+			pr_err("Last CMD not yet read by SCB\n");
+			writel_relaxed(val, tx_reg + INTR_CLR_OFS);
+		}
+
+		writel_relaxed(x->code, tx_reg + INTR_SET_OFS);
+
+		/* Wait until this message is read */
+		count = 0x1000000;
+		do {
+			cpu_relax();
+			val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+		} while (--count && val);
+		if (val)
+			pr_err("%s:%d SCB not listening!\n",
+			       __func__, __LINE__);
+
+		if (!ax) {
+			/* A quick poll for pending remote cmd */
+			val = readl_relaxed(rx_reg + INTR_STAT_OFS);
+			if (val) {
+				got_data(val);
+				writel_relaxed(val, rx_reg + INTR_CLR_OFS);
+			}
+		} else {
+			do {
+retry:
+				/* Wait until we get reply */
+				count = 0x1000000;
+				do {
+					cpu_relax();
+					val = readl_relaxed(
+						rx_reg + INTR_STAT_OFS);
+				} while (--count && !val);
+
+				if (!val) {
+					pr_err("%s:%d SCB didn't reply\n",
+					       __func__, __LINE__);
+					goto retry;
+				} else {
+					got_data(val);
+					writel_relaxed(val,
+						     rx_reg + INTR_CLR_OFS);
+				}
+			} while (!(val & RESP_BIT));
+		}
+		return 0;
+	}
+
+	return do_xfer();
+}
+
+int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
+{
+	unsigned long flags;
+	int ret = -EINVAL;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+	if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
+		ret = 0;
+		handler[cmd] = hndlr;
+	}
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	if (!mhu_chan) {
+		struct mbox_chan *_ch;
+
+		_ch = mbox_request_channel(&mhu_cl);
+		if (!IS_ERR(_ch))
+			mhu_chan = _ch;
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(mhu_hndlr_set);
+
+void mhu_hndlr_clr(u32 cmd, mhu_handler_t hndlr)
+{
+	unsigned long flags;
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (cmd < MHU_NUM_CMDS && handler[cmd] == hndlr)
+		handler[cmd] = NULL;
+
+	if (list_empty(&pending_xfers)) {
+		struct mbox_chan *_ch = NULL;
+
+		for (cmd = 0; cmd < MHU_NUM_CMDS && !handler[cmd]; cmd++)
+			;
+		/* Don't free channel if any user is listening */
+		if (cmd != MHU_NUM_CMDS) {
+			spin_unlock_irqrestore(&fsm_lock, flags);
+			return;
+		}
+
+		if (fsm_state == MHU_PARK) {
+			_ch = mhu_chan;
+			mhu_chan = NULL;
+		}
+
+		spin_unlock_irqrestore(&fsm_lock, flags);
+
+		if (_ch)
+			mbox_free_channel(_ch);
+
+		return;
+	}
+	spin_unlock_irqrestore(&fsm_lock, flags);
+}
+EXPORT_SYMBOL_GPL(mhu_hndlr_clr);
+
+int mhu_send_packet(int code, void *buf, int len, struct completion *c)
+{
+	struct mhu_xfer *x;
+	unsigned long flags;
+
+	if (code & ~0xff) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	if ((code & RESP_BIT) &&
+			fsm_state != MHU_WRRL && fsm_state != MHU_WRL) {
+		WARN_ON(1);
+		return -EINVAL;
+	}
+
+	spin_lock_irqsave(&fsm_lock, flags);
+
+	if (list_empty(&free_xfers) && mhu_alloc_xfers(5, &free_xfers)) {
+		spin_unlock_irqrestore(&fsm_lock, flags);
+		pr_err("%s:%d OOM\n", __func__, __LINE__);
+		return -EAGAIN;
+	}
+
+	x = list_first_entry(&free_xfers, struct mhu_xfer, node);
+	x->code = code;
+	x->buf = buf;
+	x->len = len;
+	x->c = c;
+
+	if (code & RESP_BIT)
+		list_move(&x->node, &pending_xfers);
+	else
+		list_move_tail(&x->node, &pending_xfers);
+
+	spin_unlock_irqrestore(&fsm_lock, flags);
+
+	return do_xfer();
+}
+EXPORT_SYMBOL_GPL(mhu_send_packet);
+
+void mb86s7x_reboot(u32 delay)
+{
+	void __iomem *tx_reg = MB86S7X_MHU_VIRT + 0x120; /* HP-NonSec */
+	struct mb86s7x_hard_reset cmd;
+	u32 val;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.delay = delay;
+
+	val = readl_relaxed(tx_reg + INTR_STAT_OFS);
+	if (val) /* Flush anything pending */
+		writel_relaxed(val, tx_reg + INTR_CLR_OFS);
+
+	memcpy_toio(cmd_to_scb, &cmd, sizeof(cmd));
+	writel_relaxed(CMD_HARD_RESET_REQ, tx_reg + INTR_SET_OFS);
+}
+EXPORT_SYMBOL_GPL(mb86s7x_reboot);
+
+static int f_scb_probe(struct platform_device *pdev)
+{
+	mhu_cl.tx_block = true;
+	mhu_cl.knows_txdone = true;
+	mhu_cl.rx_callback = mhu_recv;
+	mhu_cl.dev = &pdev->dev;
+	mhu_cl.chan_name = "HP_NonSec";
+	return 0;
+}
+
+static const struct of_device_id scb_dt_ids[] = {
+	{ .compatible = "fujitsu,scb" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, scb_dt_ids);
+
+static struct platform_driver f_scb_driver = {
+	.driver		= {
+		.name	= "f_scb",
+		.owner = THIS_MODULE,
+		.of_match_table = scb_dt_ids,
+	},
+	.probe = f_scb_probe,
+};
+
+static int __init f_scb_init(void)
+{
+	return platform_driver_register(&f_scb_driver);
+}
+module_init(f_scb_init);
diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
new file mode 100644
index 0000000..4f4287e
--- /dev/null
+++ b/include/linux/platform_data/mb86s7x_mbox.h
@@ -0,0 +1,249 @@
+/*
+ * include/linux/platform_data/mb86s7x_mbox.h
+ *
+ * Created by: Jassi Brar <jassisinghbrar@gmail.com>
+ * Copyright:	(C) 2013-2014 Linaro Limited
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __MB86S7X_MBOX_API_H
+#define __MB86S7X_MBOX_API_H
+
+#define CMD_MASK    0x7f    /* 128 possible commands */
+#define RESP_BIT    (1 << 7) /* If it's a response */
+
+#define ENC_CMD(c)  ((c) & CMD_MASK)
+#define DEC_CMD(v)  (((v) & ~CMD_MASK) ? CMD_INVALID : ((v) & CMD_MASK))
+
+#define ENC_REP(r)  (((r) & CMD_MASK) | RESP_BIT)
+
+/* If v is the reply to command c */
+#define IS_A_REP(v, c)  (((v) & RESP_BIT) && (((v) & CMD_MASK) == (c)))
+
+enum {
+	CMD_INVALID = 0,
+	CMD_I2C_XFER_REQ = 1,
+	CMD_PERI_POWER_SET_REQ = 2,
+	CMD_PERI_CLOCK_GATE_SET_REQ = 3,
+	CMD_PERI_CLOCK_GATE_GET_REQ = 4,
+	CMD_PERI_CLOCK_RATE_SET_REQ = 5,
+	CMD_PERI_CLOCK_RATE_GET_REQ = 6,
+	CMD_CPU_CLOCK_GATE_SET_REQ = 7,
+	CMD_CPU_CLOCK_GATE_GET_REQ = 8,
+	CMD_CPU_CLOCK_RATE_SET_REQ = 9,
+	CMD_CPU_CLOCK_RATE_GET_REQ = 0xa,
+	CMD_CLUSTER_OPP_GET_REQ = 0xb,
+	CMD_CLOCK_DSI_PIXEL_REQ = 0xc,
+	CMD_SCB_CAPABILITY_GET_REQ = 0xd,
+	CMD_SYS_RESET_CAUSE_GET_REQ = 0xe,
+	CMD_SYS_SPECIFIC_INFO_GET_REQ = 0xf,
+	CMD_REBOOT_AP_AFTER_REQ = 0x10,
+	CMD_TAIKI_REQ = 0x11,
+	CMD_TAIKI_ASYNC_MSG_REQ = 0x12,
+	CMD_GET_WORD_REQ = 0x13,
+	CMD_HARD_RESET_REQ = 0x14,
+	CMD_MAINTENANCE_MODE_REQ = 0x15,
+	CMD_STG_GET_SIZE_REQ = 0x16,
+	CMD_STG_BLOCK_READ_REQ = 0x17,
+	CMD_STG_BLOCK_WRITE_REQ = 0x18,
+	CMD_MEMORY_LAYOUT_GET_REQ = 0x19,
+	CMD_POWERDOMAIN_GET_REQ = 0x1a,
+	CMD_POWERDOMAIN_SET_REQ = 0x1b,
+	CMD_STG_BLOCK_ERASE_REQ = 0x1c,
+
+	/* Do NOT add new commands below this line */
+	MHU_NUM_CMDS,
+};
+
+#define CMD_I2C_XFER_REP	ENC_REP(CMD_I2C_XFER_REQ)
+#define CMD_PERI_POWER_SET_REP	ENC_REP(CMD_PERI_POWER_SET_REQ)
+#define CMD_PERI_CLOCK_GATE_SET_REP	ENC_REP(CMD_PERI_CLOCK_GATE_SET_REQ)
+#define CMD_PERI_CLOCK_GATE_GET_REP	ENC_REP(CMD_PERI_CLOCK_GATE_GET_REQ)
+#define CMD_PERI_CLOCK_RATE_SET_REP	ENC_REP(CMD_PERI_CLOCK_RATE_SET_REQ)
+#define CMD_PERI_CLOCK_RATE_GET_REP	ENC_REP(CMD_PERI_CLOCK_RATE_GET_REQ)
+#define CMD_CPU_CLOCK_GATE_SET_REP	ENC_REP(CMD_CPU_CLOCK_GATE_SET_REQ)
+#define CMD_CPU_CLOCK_GATE_GET_REP	ENC_REP(CMD_CPU_CLOCK_GATE_GET_REQ)
+#define CMD_CPU_CLOCK_RATE_SET_REP	ENC_REP(CMD_CPU_CLOCK_RATE_SET_REQ)
+#define CMD_CPU_CLOCK_RATE_GET_REP	ENC_REP(CMD_CPU_CLOCK_RATE_GET_REQ)
+#define CMD_CLUSTER_OPP_GET_REP	ENC_REP(CMD_CLUSTER_OPP_GET_REQ)
+#define CMD_CLOCK_DSI_PIXEL_REP	ENC_REP(CMD_CLOCK_DSI_PIXEL_REQ)
+#define CMD_SCB_CAPABILITY_GET_REP	ENC_REP(CMD_SCB_CAPABILITY_GET_REQ)
+#define CMD_SYS_RESET_CAUSE_GET_REP	ENC_REP(CMD_SYS_RESET_CAUSE_GET_REQ)
+#define CMD_SYS_SPECIFIC_INFO_GET_REP	ENC_REP(CMD_SYS_SPECIFIC_INFO_GET_REQ)
+#define CMD_GET_WORD_REP	ENC_REP(CMD_GET_WORD_REQ)
+#define CMD_REBOOT_AP_AFTER_REP	ENC_REP(CMD_REBOOT_AP_AFTER_REQ)
+#define CMD_TAIKI_REP			ENC_REP(CMD_TAIKI_REQ)
+#define CMD_TAIKI_ASYNC_MSG_REP		ENC_REP(CMD_TAIKI_ASYNC_MSG_REQ)
+#define CMD_HARD_RESET_REP		ENC_REP(CMD_HARD_RESET_REQ)
+#define CMD_MAINTENANCE_MODE_REP	ENC_RSP(CMD_MAINTENANCE_MODE_REQ)
+#define CMD_STG_GET_SIZE_REP		ENC_REP(CMD_STG_GET_SIZE_REQ)
+#define CMD_STG_BLOCK_READ_REP		ENC_REP(CMD_STG_BLOCK_READ_REQ)
+#define CMD_STG_BLOCK_WRITE_REP		ENC_REP(CMD_STG_BLOCK_WRITE_REQ)
+#define CMD_MEMORY_LAYOUT_GET_REP	ENC_REP(CMD_MEMORY_LAYOUT_GET_REQ)
+#define CMD_POWERDOMAIN_GET_REP		ENC_REP(CMD_POWERDOMAIN_GET_REQ)
+#define CMD_POWERDOMAIN_SET_REP		ENC_REP(CMD_POWERDOMAIN_SET_REQ)
+#define CMD_STG_BLOCK_ERASE_REP		ENC_REP(CMD_STG_BLOCK_ERASE_REQ)
+
+struct mb86s7x_peri_clk {
+	u32 payload_size;
+	u32 cntrlr;
+	u32 domain;
+	u32 port;
+	u32 en;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_peri_power {
+	u32 payload_size;
+	u32 peri_id;
+	u32 en;
+} __packed;
+
+#define AT_WFI_DO_NOTHING	0x0
+#define AT_WFI_DO_SUSPEND	0x1
+#define AT_WFI_DO_POWEROFF	0x2
+#define AT_WFI_COLOR_MASK	0x3
+
+#define WFI_COLOR_REG_OFFSET	0x3f00
+
+struct mb86s7x_cpu_gate {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 cpu_id;
+#define SCB_CPU_STATE_OFF	0x0
+#define SCB_CPU_STATE_ON	0x1
+#define SCB_CPU_STATE_SUSP	0x2
+	u32 cpu_state;
+} __packed;
+
+struct mb86s7x_cpu_freq {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 cpu_id;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_cluster_opp {
+	u32 voltage;
+	u64 freqency;
+} __packed;
+
+struct mb86s7x_cluster_opp_get {
+	u32 payload_size;
+	u32 cluster_class;
+	u32 cluster_id;
+	u32 opp_num;
+	struct mb86s7x_cluster_opp opp[0];
+} __packed;
+
+struct mb86s7x_clk_dsipxl {
+	u32 payload_size;
+	u32 mode;
+} __packed;
+
+struct mb86s7x_scb_version {
+	u32 payload_size;
+	u32 version;
+	u32 config_version;
+} __packed;
+
+struct mb86s7x_reset_cause {
+	u32 payload_size;
+#define RESET_BY_COLDBOOT	0x0
+#define RESET_BY_SW		0x1
+#define RESET_BY_WDOG		0x2
+#define RESET_BY_xxyz		0x3
+	u32 cause;
+} __packed;
+
+struct mb86s7x_sys_info {
+	u32 payload_size;
+	u64 specific_info;
+} __packed;
+
+struct mb86s7x_softreboot {
+	u32 payload_size;
+	u32 delay;
+} __packed;
+
+struct mb86s7x_debug_readl {
+	u32 payload_size;
+#define REG_TOP_STATUS_CFG_CTL		0x44010000
+#define REG_TOP_STATUS_CRG11_ALW	0xF1000000
+#define REG_TOP_STATUS_CRG11_DDR3	0xF1010000
+#define REG_TOP_STATUS_CRG11_MAIN	0xF1020000
+#define REG_TOP_STATUS_CRG11_HDMI	0xF1040000
+#define REG_TOP_STATUS_CRG11_DPHY	0xF1050000
+#define REG_TOP_STATUS_CRGSYSOC		0xF1080000
+#define REG_TOP_STATUS_PRMUX		0xCA4D0000
+	u32 address;
+	u32 data;
+} __packed;
+
+struct mb86s7x_taiki {
+	u32	payload_data[64];
+} __packed;
+
+struct mb86s7x_taiki_async_msg {
+	u32	payload_data[64];
+} __packed;
+
+struct mb86s7x_hard_reset {
+	u32 payload_size;
+	u32 delay;
+} __packed;
+
+struct mb86s7x_stg_get_size {
+	u32 payload_size;
+#define SCB_SECTOR_SIZE 128
+	u32 count_sectors;
+	u32 erase_block_size_bytes;
+} __packed;
+
+struct mb86s7x_stg_block_rw {
+	u32 payload_size;
+	u32 sector;
+	u32 result;
+	u8 data[SCB_SECTOR_SIZE];
+} __packed;
+
+struct mb86s7x_stg_block_erase {
+	u32 payload_size;
+	u32 sector;
+	u32 result;
+} __packed;
+
+struct mb86s7x_memory_region {
+	u64 start;
+	u64 length;
+} __packed;
+
+struct mb86s7x_memory_layout {
+	u32 payload_size;
+	u32 count_regions;
+	struct mb86s7x_memory_region regions[0];
+} __packed;
+
+struct mb86s7x_pmd_cmd {
+	u32 payload_size;
+	u32 powerdomain_index;
+	u32 state;
+} __packed;
+
+#define MB86S7X_VERSION_SUPPORT_BLOCKDEV 0x509
+
+int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
+void mb86s7x_reboot(u32 delay);
+
+/* This function must not sleep */
+typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
+
+int mhu_hndlr_set(u32 cmd, mhu_handler_t);
+void mhu_hndlr_clr(u32 cmd, mhu_handler_t);
+
+#endif /* __MB86S7X_MBOX_API_H */
-- 
1.8.1.2

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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:29   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:29 UTC (permalink / raw)
  To: linux-mmc, devicetree, linux-arm-kernel
  Cc: andy.green, patches, jaswinder.singh, linux, arnd, olof, chris,
	anton, mark.rutland, robh+dt, pawel.moll, Mollie Wu,
	Vincent Yang, Tetsuya Takinishi

This patch adds new host controller driver for
Fujitsu SDHCI controller f_sdh30.

Signed-off-by: Vincent Yang <Vincent.Yang@tw.fujitsu.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../devicetree/bindings/mmc/sdhci-fujitsu.txt      |  36 ++
 drivers/mmc/host/Kconfig                           |   7 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/sdhci_f_sdh30.c                   | 380 +++++++++++++++++++++
 4 files changed, 424 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
 create mode 100644 drivers/mmc/host/sdhci_f_sdh30.c

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
new file mode 100644
index 0000000..34f20c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
@@ -0,0 +1,36 @@
+* Fujitsu SDHCI controller
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci_f_sdh30 driver.
+
+Required properties:
+- compatible: "fujitsu,f-sdh30"
+- voltage-ranges : This is a list of pairs. In each pair, two cells
+  are required. First cell specifies minimum slot voltage (mV), second
+  cell specifies maximum slot voltage (mV). In case of supported voltage
+  range is discontinuous, several ranges could be specified as a list.
+
+Optional properties:
+- pwr-mux-gpios: This is one optional gpio for controlling a power mux
+  which switches between two power supplies. 3.3V is selected when gpio
+  is high, and 1.8V is selected when gpio is low. This voltage is used
+  for signal level.
+- clocks: Must contain an entry for each entry in clock-names. It is a
+  list of phandles and clock-specifier pairs.
+  See ../clocks/clock-bindings.txt for details.
+- clock-names: Should contain the following two entries:
+	"sd_sd4clk" - clock primarily used for tuning process
+	"sd_bclk"   - base clock for sdhci controller
+
+Example:
+
+	sdhci1: sdio@36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		pwr-mux-gpios = <&gpio0 7 0>;
+		clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index a565254..1d5975c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -279,6 +279,13 @@ config MMC_SDHCI_BCM2835
 	  This selects the BCM2835 SD/MMC controller. If you have a BCM2835
 	  platform with SD or MMC devices, say Y or M here.
 
+config MMC_SDHCI_F_SDH30
+	tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
+	depends on MMC_SDHCI && ARCH_MB86S7X
+	help
+	  This selects the Secure Digital Host Controller Interface (SDHCI)
+	  Needed by some Fujitsu SoC for MMC / SD / SDIO support.
+	  If you have a controller with this interface, say Y or M here.
 	  If unsure, say N.
 
 config MMC_MOXART
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 7f81ddf..a4c89e5 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3)	+= sdhci-pxav3.o
 obj-$(CONFIG_MMC_SDHCI_PXAV2)	+= sdhci-pxav2.o
 obj-$(CONFIG_MMC_SDHCI_S3C)	+= sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SIRF)   	+= sdhci-sirf.o
+obj-$(CONFIG_MMC_SDHCI_F_SDH30)	+= sdhci_f_sdh30.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)	+= sdhci-spear.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
new file mode 100644
index 0000000..8d23f2d
--- /dev/null
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -0,0 +1,380 @@
+/*
+ * linux/drivers/mmc/host/sdhci_f_sdh30.c
+ *
+ * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
+ *              Vincent Yang <vincent.yang@tw.fujitsu.com>
+ * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+#include "../core/core.h"
+
+#define DRIVER_NAME "f_sdh30"
+
+/* F_SDH30 extended Controller registers */
+#define F_SDH30_AHB_CONFIG		0x100
+#define  F_SDH30_AHB_BIGED		0x00000040
+#define  F_SDH30_BUSLOCK_DMA		0x00000020
+#define  F_SDH30_BUSLOCK_EN		0x00000010
+#define  F_SDH30_SIN			0x00000008
+#define  F_SDH30_AHB_INCR_16		0x00000004
+#define  F_SDH30_AHB_INCR_8		0x00000002
+#define  F_SDH30_AHB_INCR_4		0x00000001
+
+#define F_SDH30_TUNING_SETTING		0x108
+#define  F_SDH30_CMD_CHK_DIS		0x00010000
+
+#define F_SDH30_IO_CONTROL2		0x114
+#define  F_SDH30_CRES_O_DN		0x00080000
+#define  F_SDH30_MSEL_O_1_8		0x00040000
+
+#define F_SDH30_ESD_CONTROL		0x124
+#define  F_SDH30_EMMC_RST		0x00000002
+#define  F_SDH30_EMMC_HS200		0x01000000
+
+#define F_SDH30_CMD_DAT_DELAY		0x200
+
+#define F_SDH30_MIN_CLOCK		400000
+
+struct f_sdhost_priv {
+	struct clk *clk_sd4;
+	struct clk *clk_b;
+	int gpio_select_1v8;
+	u32 vendor_hs200;
+	struct device *dev;
+};
+
+void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
+{
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+	u32 ctrl = 0;
+
+	usleep_range(2500, 3000);
+	ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
+	ctrl |= F_SDH30_CRES_O_DN;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+	ctrl |= F_SDH30_MSEL_O_1_8;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		dev_info(priv->dev, "%s: setting gpio\n", __func__);
+		gpio_direction_output(priv->gpio_select_1v8, 0);
+	}
+
+	ctrl &= ~F_SDH30_CRES_O_DN;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+	usleep_range(2500, 3000);
+
+	if (priv->vendor_hs200) {
+		dev_info(priv->dev, "%s: setting hs200\n", __func__);
+		ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+		ctrl |= priv->vendor_hs200;
+		sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
+	}
+
+	ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
+	ctrl |= F_SDH30_CMD_CHK_DIS;
+	sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
+}
+
+unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
+{
+	return F_SDH30_MIN_CLOCK;
+}
+
+void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
+{
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+
+	if (gpio_is_valid(priv->gpio_select_1v8))
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+
+	if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
+		sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
+		mmiowb();
+	}
+
+	sdhci_reset(host, mask);
+}
+
+static const struct sdhci_ops sdhci_f_sdh30_ops = {
+	.voltage_switch = sdhci_f_sdh30_soft_voltage_switch,
+	.get_min_clock = sdhci_f_sdh30_get_min_clock,
+	.reset = sdhci_f_sdh30_reset,
+	.set_clock = sdhci_set_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static int sdhci_f_sdh30_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct device *dev = &pdev->dev;
+	int irq, ctrl = 0, ret = 0;
+	struct f_sdhost_priv *priv;
+	u32 reg = 0, bus_width;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "%s: no irq specified\n", __func__);
+		ret = irq;
+		goto err;
+	}
+
+	host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
+						sizeof(struct f_sdhost_priv));
+	if (IS_ERR(host)) {
+		dev_err(dev, "%s: host allocate error\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+	priv = sdhci_priv(host);
+	priv->dev = dev;
+
+	host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+		       SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+	host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
+			SDHCI_QUIRK2_VOLTAGE_SWITCH |
+			SDHCI_QUIRK2_TUNING_WORK_AROUND;
+
+	if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width)) {
+		switch (bus_width) {
+		case 8:
+			dev_info(dev, "Applying 8 bit bus width\n");
+			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+			break;
+		case 4:
+			dev_info(dev, "Applying 4 bit bus width\n");
+			host->mmc->caps |= MMC_CAP_4_BIT_DATA;
+			break;
+		case 1:
+		default:
+			dev_err(dev, "Invalid bus width: %u\n", bus_width);
+			break;
+		}
+	}
+
+	ret = mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
+	if (ret) {
+		dev_err(dev, "%s: parse voltage error\n", __func__);
+		goto err_voltage;
+	}
+
+	host->hw_name = DRIVER_NAME;
+	host->ops = &sdhci_f_sdh30_ops;
+	host->irq = irq;
+
+	host->ioaddr = of_iomap(pdev->dev.of_node, 0);
+	if (!host->ioaddr) {
+		dev_err(dev, "%s: failed to remap registers\n", __func__);
+		ret = -ENXIO;
+		goto err_remap;
+	}
+
+	priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
+	if (!IS_ERR(priv->clk_sd4)) {
+		ret = clk_prepare_enable(priv->clk_sd4);
+		if (ret < 0) {
+			dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
+			goto err_clk1;
+		}
+	}
+	priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
+	if (!IS_ERR(priv->clk_b)) {
+		ret = clk_prepare_enable(priv->clk_b);
+		if (ret < 0) {
+			dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
+			goto err_clk2;
+		}
+	}
+
+	/* optional */
+	priv->gpio_select_1v8 = of_get_named_gpio(pdev->dev.of_node,
+						  "pwr-mux-gpios", 0);
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		ret = gpio_request(priv->gpio_select_1v8, "sdhci");
+		if (unlikely(ret)) {
+			dev_err(dev, " gpio %d request failed ",
+				priv->gpio_select_1v8);
+			goto err_gpio;
+		}
+		/* initially 3.3V */
+		ret = gpio_direction_output(priv->gpio_select_1v8, 1);
+		if (unlikely(ret)) {
+			dev_err(dev, "failed to set phy_enable gpio\n");
+			goto err_gpio;
+		}
+	}
+
+	platform_set_drvdata(pdev, host);
+
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0)
+		dev_err(dev, "Failed to pm_runtime_get_sync: %d\n", ret);
+#endif
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(dev, "%s: host add error\n", __func__);
+		goto err_add_host;
+	}
+
+	/* init vendor specific regs */
+	ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
+	ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
+		F_SDH30_AHB_INCR_4;
+	ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
+	sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG);
+	mmiowb();
+
+	reg = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+	sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+	msleep(20);
+	sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+	mmiowb();
+
+	reg = sdhci_readl(host, SDHCI_CAPABILITIES);
+	if (reg & SDHCI_CAN_DO_8BIT)
+		priv->vendor_hs200 = F_SDH30_EMMC_HS200;
+	else
+		priv->vendor_hs200 = 0;
+
+	return 0;
+
+err_add_host:
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+		gpio_free(priv->gpio_select_1v8);
+	}
+err_gpio:
+	clk_put(priv->clk_b);
+err_clk2:
+	clk_put(priv->clk_sd4);
+err_clk1:
+	iounmap(host->ioaddr);
+err_remap:
+err_voltage:
+	sdhci_free_host(host);
+err:
+	return ret;
+}
+
+static int sdhci_f_sdh30_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+	struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
+			  0xffffffff);
+	iounmap(host->ioaddr);
+	release_mem_region(iomem->start, resource_size(iomem));
+
+	clk_disable_unprepare(priv->clk_sd4);
+	clk_disable_unprepare(priv->clk_b);
+
+	clk_put(priv->clk_b);
+	clk_put(priv->clk_sd4);
+
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+		gpio_free(priv->gpio_select_1v8);
+	}
+
+	sdhci_free_host(host);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_f_sdh30_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_suspend_host(host);
+}
+
+static int sdhci_f_sdh30_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_resume_host(host);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_f_sdh30_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_runtime_suspend_host(host);
+}
+
+static int sdhci_f_sdh30_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_runtime_resume_host(host);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops sdhci_f_sdh30_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sdhci_f_sdh30_suspend, sdhci_f_sdh30_resume)
+	SET_RUNTIME_PM_OPS(sdhci_f_sdh30_runtime_suspend,
+			   sdhci_f_sdh30_runtime_resume, NULL)
+};
+
+#define SDHCI_F_SDH30_PMOPS (&sdhci_f_sdh30_pmops)
+
+#else
+#define SDHCI_F_SDH30_PMOPS NULL
+#endif
+
+static const struct of_device_id f_sdh30_dt_ids[] = {
+	{ .compatible = "fujitsu,f-sdh30" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
+
+static struct platform_driver sdhci_f_sdh30_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = f_sdh30_dt_ids,
+#ifdef CONFIG_PM_SLEEP
+		.pm	= SDHCI_F_SDH30_PMOPS,
+#endif
+	},
+	.probe	= sdhci_f_sdh30_probe,
+	.remove	= sdhci_f_sdh30_remove,
+};
+
+module_platform_driver(sdhci_f_sdh30_driver);
+
+MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
+MODULE_ALIAS("platform: " DRIVER_NAME);
-- 
1.8.1.2


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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
@ 2014-07-13  6:29   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:29 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds new host controller driver for
Fujitsu SDHCI controller f_sdh30.

Signed-off-by: Vincent Yang <Vincent.Yang@tw.fujitsu.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../devicetree/bindings/mmc/sdhci-fujitsu.txt      |  36 ++
 drivers/mmc/host/Kconfig                           |   7 +
 drivers/mmc/host/Makefile                          |   1 +
 drivers/mmc/host/sdhci_f_sdh30.c                   | 380 +++++++++++++++++++++
 4 files changed, 424 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
 create mode 100644 drivers/mmc/host/sdhci_f_sdh30.c

diff --git a/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
new file mode 100644
index 0000000..34f20c9
--- /dev/null
+++ b/Documentation/devicetree/bindings/mmc/sdhci-fujitsu.txt
@@ -0,0 +1,36 @@
+* Fujitsu SDHCI controller
+
+This file documents differences between the core properties in mmc.txt
+and the properties used by the sdhci_f_sdh30 driver.
+
+Required properties:
+- compatible: "fujitsu,f-sdh30"
+- voltage-ranges : This is a list of pairs. In each pair, two cells
+  are required. First cell specifies minimum slot voltage (mV), second
+  cell specifies maximum slot voltage (mV). In case of supported voltage
+  range is discontinuous, several ranges could be specified as a list.
+
+Optional properties:
+- pwr-mux-gpios: This is one optional gpio for controlling a power mux
+  which switches between two power supplies. 3.3V is selected when gpio
+  is high, and 1.8V is selected when gpio is low. This voltage is used
+  for signal level.
+- clocks: Must contain an entry for each entry in clock-names. It is a
+  list of phandles and clock-specifier pairs.
+  See ../clocks/clock-bindings.txt for details.
+- clock-names: Should contain the following two entries:
+	"sd_sd4clk" - clock primarily used for tuning process
+	"sd_bclk"   - base clock for sdhci controller
+
+Example:
+
+	sdhci1: sdio at 36600000 {
+		compatible = "fujitsu,f-sdh30";
+		reg = <0 0x36600000 0x1000>;
+		interrupts = <0 172 0x4>,
+			     <0 173 0x4>;
+		voltage-ranges = <1800 1800>, <3300 3300>;
+		pwr-mux-gpios = <&gpio0 7 0>;
+		clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
+		clock-names = "sd_sd4clk", "sd_bclk";
+	};
diff --git a/drivers/mmc/host/Kconfig b/drivers/mmc/host/Kconfig
index a565254..1d5975c 100644
--- a/drivers/mmc/host/Kconfig
+++ b/drivers/mmc/host/Kconfig
@@ -279,6 +279,13 @@ config MMC_SDHCI_BCM2835
 	  This selects the BCM2835 SD/MMC controller. If you have a BCM2835
 	  platform with SD or MMC devices, say Y or M here.
 
+config MMC_SDHCI_F_SDH30
+	tristate "SDHCI support for Fujitsu Semiconductor F_SDH30"
+	depends on MMC_SDHCI && ARCH_MB86S7X
+	help
+	  This selects the Secure Digital Host Controller Interface (SDHCI)
+	  Needed by some Fujitsu SoC for MMC / SD / SDIO support.
+	  If you have a controller with this interface, say Y or M here.
 	  If unsure, say N.
 
 config MMC_MOXART
diff --git a/drivers/mmc/host/Makefile b/drivers/mmc/host/Makefile
index 7f81ddf..a4c89e5 100644
--- a/drivers/mmc/host/Makefile
+++ b/drivers/mmc/host/Makefile
@@ -15,6 +15,7 @@ obj-$(CONFIG_MMC_SDHCI_PXAV3)	+= sdhci-pxav3.o
 obj-$(CONFIG_MMC_SDHCI_PXAV2)	+= sdhci-pxav2.o
 obj-$(CONFIG_MMC_SDHCI_S3C)	+= sdhci-s3c.o
 obj-$(CONFIG_MMC_SDHCI_SIRF)   	+= sdhci-sirf.o
+obj-$(CONFIG_MMC_SDHCI_F_SDH30)	+= sdhci_f_sdh30.o
 obj-$(CONFIG_MMC_SDHCI_SPEAR)	+= sdhci-spear.o
 obj-$(CONFIG_MMC_WBSD)		+= wbsd.o
 obj-$(CONFIG_MMC_AU1X)		+= au1xmmc.o
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
new file mode 100644
index 0000000..8d23f2d
--- /dev/null
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -0,0 +1,380 @@
+/*
+ * linux/drivers/mmc/host/sdhci_f_sdh30.c
+ *
+ * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
+ *              Vincent Yang <vincent.yang@tw.fujitsu.com>
+ * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ */
+
+#include <linux/err.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/mmc/sd.h>
+#include <linux/mmc/host.h>
+#include <linux/mmc/card.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <linux/of_address.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/suspend.h>
+
+#include "sdhci.h"
+#include "sdhci-pltfm.h"
+#include "../core/core.h"
+
+#define DRIVER_NAME "f_sdh30"
+
+/* F_SDH30 extended Controller registers */
+#define F_SDH30_AHB_CONFIG		0x100
+#define  F_SDH30_AHB_BIGED		0x00000040
+#define  F_SDH30_BUSLOCK_DMA		0x00000020
+#define  F_SDH30_BUSLOCK_EN		0x00000010
+#define  F_SDH30_SIN			0x00000008
+#define  F_SDH30_AHB_INCR_16		0x00000004
+#define  F_SDH30_AHB_INCR_8		0x00000002
+#define  F_SDH30_AHB_INCR_4		0x00000001
+
+#define F_SDH30_TUNING_SETTING		0x108
+#define  F_SDH30_CMD_CHK_DIS		0x00010000
+
+#define F_SDH30_IO_CONTROL2		0x114
+#define  F_SDH30_CRES_O_DN		0x00080000
+#define  F_SDH30_MSEL_O_1_8		0x00040000
+
+#define F_SDH30_ESD_CONTROL		0x124
+#define  F_SDH30_EMMC_RST		0x00000002
+#define  F_SDH30_EMMC_HS200		0x01000000
+
+#define F_SDH30_CMD_DAT_DELAY		0x200
+
+#define F_SDH30_MIN_CLOCK		400000
+
+struct f_sdhost_priv {
+	struct clk *clk_sd4;
+	struct clk *clk_b;
+	int gpio_select_1v8;
+	u32 vendor_hs200;
+	struct device *dev;
+};
+
+void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
+{
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+	u32 ctrl = 0;
+
+	usleep_range(2500, 3000);
+	ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
+	ctrl |= F_SDH30_CRES_O_DN;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+	ctrl |= F_SDH30_MSEL_O_1_8;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		dev_info(priv->dev, "%s: setting gpio\n", __func__);
+		gpio_direction_output(priv->gpio_select_1v8, 0);
+	}
+
+	ctrl &= ~F_SDH30_CRES_O_DN;
+	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
+	usleep_range(2500, 3000);
+
+	if (priv->vendor_hs200) {
+		dev_info(priv->dev, "%s: setting hs200\n", __func__);
+		ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+		ctrl |= priv->vendor_hs200;
+		sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
+	}
+
+	ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
+	ctrl |= F_SDH30_CMD_CHK_DIS;
+	sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
+}
+
+unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
+{
+	return F_SDH30_MIN_CLOCK;
+}
+
+void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
+{
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+
+	if (gpio_is_valid(priv->gpio_select_1v8))
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+
+	if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
+		sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
+		mmiowb();
+	}
+
+	sdhci_reset(host, mask);
+}
+
+static const struct sdhci_ops sdhci_f_sdh30_ops = {
+	.voltage_switch = sdhci_f_sdh30_soft_voltage_switch,
+	.get_min_clock = sdhci_f_sdh30_get_min_clock,
+	.reset = sdhci_f_sdh30_reset,
+	.set_clock = sdhci_set_clock,
+	.set_bus_width = sdhci_set_bus_width,
+	.set_uhs_signaling = sdhci_set_uhs_signaling,
+};
+
+static int sdhci_f_sdh30_probe(struct platform_device *pdev)
+{
+	struct sdhci_host *host;
+	struct device *dev = &pdev->dev;
+	int irq, ctrl = 0, ret = 0;
+	struct f_sdhost_priv *priv;
+	u32 reg = 0, bus_width;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq < 0) {
+		dev_err(dev, "%s: no irq specified\n", __func__);
+		ret = irq;
+		goto err;
+	}
+
+	host = sdhci_alloc_host(dev, sizeof(struct sdhci_host) +
+						sizeof(struct f_sdhost_priv));
+	if (IS_ERR(host)) {
+		dev_err(dev, "%s: host allocate error\n", __func__);
+		ret = -ENOMEM;
+		goto err;
+	}
+	priv = sdhci_priv(host);
+	priv->dev = dev;
+
+	host->quirks = SDHCI_QUIRK_NO_ENDATTR_IN_NOPDESC |
+		       SDHCI_QUIRK_INVERTED_WRITE_PROTECT;
+	host->quirks2 = SDHCI_QUIRK2_SUPPORT_SINGLE |
+			SDHCI_QUIRK2_VOLTAGE_SWITCH |
+			SDHCI_QUIRK2_TUNING_WORK_AROUND;
+
+	if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width)) {
+		switch (bus_width) {
+		case 8:
+			dev_info(dev, "Applying 8 bit bus width\n");
+			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
+			break;
+		case 4:
+			dev_info(dev, "Applying 4 bit bus width\n");
+			host->mmc->caps |= MMC_CAP_4_BIT_DATA;
+			break;
+		case 1:
+		default:
+			dev_err(dev, "Invalid bus width: %u\n", bus_width);
+			break;
+		}
+	}
+
+	ret = mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
+	if (ret) {
+		dev_err(dev, "%s: parse voltage error\n", __func__);
+		goto err_voltage;
+	}
+
+	host->hw_name = DRIVER_NAME;
+	host->ops = &sdhci_f_sdh30_ops;
+	host->irq = irq;
+
+	host->ioaddr = of_iomap(pdev->dev.of_node, 0);
+	if (!host->ioaddr) {
+		dev_err(dev, "%s: failed to remap registers\n", __func__);
+		ret = -ENXIO;
+		goto err_remap;
+	}
+
+	priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
+	if (!IS_ERR(priv->clk_sd4)) {
+		ret = clk_prepare_enable(priv->clk_sd4);
+		if (ret < 0) {
+			dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
+			goto err_clk1;
+		}
+	}
+	priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
+	if (!IS_ERR(priv->clk_b)) {
+		ret = clk_prepare_enable(priv->clk_b);
+		if (ret < 0) {
+			dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
+			goto err_clk2;
+		}
+	}
+
+	/* optional */
+	priv->gpio_select_1v8 = of_get_named_gpio(pdev->dev.of_node,
+						  "pwr-mux-gpios", 0);
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		ret = gpio_request(priv->gpio_select_1v8, "sdhci");
+		if (unlikely(ret)) {
+			dev_err(dev, " gpio %d request failed ",
+				priv->gpio_select_1v8);
+			goto err_gpio;
+		}
+		/* initially 3.3V */
+		ret = gpio_direction_output(priv->gpio_select_1v8, 1);
+		if (unlikely(ret)) {
+			dev_err(dev, "failed to set phy_enable gpio\n");
+			goto err_gpio;
+		}
+	}
+
+	platform_set_drvdata(pdev, host);
+
+#ifdef CONFIG_PM_RUNTIME
+	pm_runtime_enable(&pdev->dev);
+	ret = pm_runtime_get_sync(&pdev->dev);
+	if (ret < 0)
+		dev_err(dev, "Failed to pm_runtime_get_sync: %d\n", ret);
+#endif
+
+	ret = sdhci_add_host(host);
+	if (ret) {
+		dev_err(dev, "%s: host add error\n", __func__);
+		goto err_add_host;
+	}
+
+	/* init vendor specific regs */
+	ctrl = sdhci_readw(host, F_SDH30_AHB_CONFIG);
+	ctrl |= F_SDH30_SIN | F_SDH30_AHB_INCR_16 | F_SDH30_AHB_INCR_8 |
+		F_SDH30_AHB_INCR_4;
+	ctrl &= ~(F_SDH30_AHB_BIGED | F_SDH30_BUSLOCK_EN);
+	sdhci_writew(host, ctrl, F_SDH30_AHB_CONFIG);
+	mmiowb();
+
+	reg = sdhci_readl(host, F_SDH30_ESD_CONTROL);
+	sdhci_writel(host, reg & ~F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+	msleep(20);
+	sdhci_writel(host, reg | F_SDH30_EMMC_RST, F_SDH30_ESD_CONTROL);
+	mmiowb();
+
+	reg = sdhci_readl(host, SDHCI_CAPABILITIES);
+	if (reg & SDHCI_CAN_DO_8BIT)
+		priv->vendor_hs200 = F_SDH30_EMMC_HS200;
+	else
+		priv->vendor_hs200 = 0;
+
+	return 0;
+
+err_add_host:
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+		gpio_free(priv->gpio_select_1v8);
+	}
+err_gpio:
+	clk_put(priv->clk_b);
+err_clk2:
+	clk_put(priv->clk_sd4);
+err_clk1:
+	iounmap(host->ioaddr);
+err_remap:
+err_voltage:
+	sdhci_free_host(host);
+err:
+	return ret;
+}
+
+static int sdhci_f_sdh30_remove(struct platform_device *pdev)
+{
+	struct sdhci_host *host = platform_get_drvdata(pdev);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+	struct resource *iomem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+	sdhci_remove_host(host, readl(host->ioaddr + SDHCI_INT_STATUS) ==
+			  0xffffffff);
+	iounmap(host->ioaddr);
+	release_mem_region(iomem->start, resource_size(iomem));
+
+	clk_disable_unprepare(priv->clk_sd4);
+	clk_disable_unprepare(priv->clk_b);
+
+	clk_put(priv->clk_b);
+	clk_put(priv->clk_sd4);
+
+	if (gpio_is_valid(priv->gpio_select_1v8)) {
+		gpio_direction_output(priv->gpio_select_1v8, 1);
+		gpio_free(priv->gpio_select_1v8);
+	}
+
+	sdhci_free_host(host);
+	platform_set_drvdata(pdev, NULL);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int sdhci_f_sdh30_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_suspend_host(host);
+}
+
+static int sdhci_f_sdh30_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_resume_host(host);
+}
+#endif
+
+#ifdef CONFIG_PM_RUNTIME
+static int sdhci_f_sdh30_runtime_suspend(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_runtime_suspend_host(host);
+}
+
+static int sdhci_f_sdh30_runtime_resume(struct device *dev)
+{
+	struct sdhci_host *host = dev_get_drvdata(dev);
+
+	return sdhci_runtime_resume_host(host);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops sdhci_f_sdh30_pmops = {
+	SET_SYSTEM_SLEEP_PM_OPS(sdhci_f_sdh30_suspend, sdhci_f_sdh30_resume)
+	SET_RUNTIME_PM_OPS(sdhci_f_sdh30_runtime_suspend,
+			   sdhci_f_sdh30_runtime_resume, NULL)
+};
+
+#define SDHCI_F_SDH30_PMOPS (&sdhci_f_sdh30_pmops)
+
+#else
+#define SDHCI_F_SDH30_PMOPS NULL
+#endif
+
+static const struct of_device_id f_sdh30_dt_ids[] = {
+	{ .compatible = "fujitsu,f-sdh30" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_sdh30_dt_ids);
+
+static struct platform_driver sdhci_f_sdh30_driver = {
+	.driver = {
+		.name = DRIVER_NAME,
+		.owner = THIS_MODULE,
+		.of_match_table = f_sdh30_dt_ids,
+#ifdef CONFIG_PM_SLEEP
+		.pm	= SDHCI_F_SDH30_PMOPS,
+#endif
+	},
+	.probe	= sdhci_f_sdh30_probe,
+	.remove	= sdhci_f_sdh30_remove,
+};
+
+module_platform_driver(sdhci_f_sdh30_driver);
+
+MODULE_DESCRIPTION("F_SDH30 SD Card Controller driver");
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("FUJITSU SEMICONDUCTOR LTD.");
+MODULE_ALIAS("platform: " DRIVER_NAME);
-- 
1.8.1.2

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

* [PATCH 3/8] mmc: core: add manual resume capability
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:30   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:30 UTC (permalink / raw)
  To: linux-mmc, linux-arm-kernel
  Cc: andy.green, patches, jaswinder.singh, arnd, olof, linux, chris,
	anton, Mollie Wu, Vincent Yang, Tetsuya Takinishi

This patch adds manual resume for some embedded platforms with rootfs
stored in SD card. It references CONFIG_MMC_BLOCK_DEFERRED_RESUME in
kernel 3.10. It lets host controller driver to manually handle resume
by itself.

[symptom]
This issue is found on mb86s7x platforms with rootfs stored in SD card.
It failed to resume form STR suspend mode because SD card cannot be ready
in time. It take longer time (e.g., 600ms) to be ready for access.
The error log looks like below:

root@localhost:~# echo mem > /sys/power/state
[   30.441974] SUSPEND

SCB Firmware : Category 01 Version 02.03 Rev. 00_
    Config   : (no configuration)
root@localhost:~# [   30.702976] Buffer I/O error on device mmcblk1p2, logical block 31349
[   30.709678] Buffer I/O error on device mmcblk1p2, logical block 168073
[   30.716220] Buffer I/O error on device mmcblk1p2, logical block 168074
[   30.722759] Buffer I/O error on device mmcblk1p2, logical block 168075
[   30.729456] Buffer I/O error on device mmcblk1p2, logical block 31349
[   30.735916] Buffer I/O error on device mmcblk1p2, logical block 31350
[   30.742370] Buffer I/O error on device mmcblk1p2, logical block 31351
[   30.749025] Buffer I/O error on device mmcblk1p2, logical block 168075
[   30.755657] Buffer I/O error on device mmcblk1p2, logical block 31351
[   30.763130] Aborting journal on device mmcblk1p2-8.
[   30.768060] JBD2: Error -5 detected when updating journal superblock for mmcblk1p2-8.
[   30.776085] EXT4-fs error (device mmcblk1p2): ext4_journal_check_start:56: Detected aborted journal
[   30.785259] EXT4-fs (mmcblk1p2): Remounting filesystem read-only
[   31.370716] EXT4-fs error (device mmcblk1p2): ext4_find_entry:1309: inode #2490369: comm udevd: reading directory lblock 0
[   31.382485] EXT4-fs error (device mmcblk1p2): ext4_find_entry:1309: inode #1048577: comm udevd: reading directory lblock 0

[analysis]
In system resume path, mmc_sd_resume() is failed with error code -123
because at that time SD card is still not ready on mb86s7x platforms.

[solution]
In order to not blocking system resume path, this patch just sets a flag
MMC_BUSRESUME_MANUAL_RESUME when this error happened, and then host controller
driver can understand it by this flag. Then host controller driver have to
resume SD card manually and asynchronously.

Signed-off-by: Vincent Yang <Vincent.Yang@tw.fujitsu.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 drivers/mmc/core/core.c          |  4 ++
 drivers/mmc/core/sd.c            |  4 ++
 drivers/mmc/host/sdhci_f_sdh30.c | 89 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h         | 14 +++++++
 4 files changed, 111 insertions(+)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 764af63..51fce49 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2648,6 +2648,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 	case PM_POST_RESTORE:
 
 		spin_lock_irqsave(&host->lock, flags);
+		if (mmc_bus_manual_resume(host)) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			break;
+		}
 		host->rescan_disable = 0;
 		spin_unlock_irqrestore(&host->lock, flags);
 		_mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0c44510..859390d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1133,6 +1133,10 @@ static int mmc_sd_resume(struct mmc_host *host)
 
 	if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
 		err = _mmc_sd_resume(host);
+		if ((host->caps2 & MMC_CAP2_MANUAL_RESUME) && err)
+			mmc_set_bus_resume_policy(host, 1);
+		else
+			mmc_set_bus_resume_policy(host, 0);
 		pm_runtime_set_active(&host->card->dev);
 		pm_runtime_mark_last_busy(&host->card->dev);
 	}
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index 8d23f2d..32864ba 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -29,6 +29,12 @@
 #include "../core/core.h"
 
 #define DRIVER_NAME "f_sdh30"
+#define RESUME_WAIT_COUNT	100
+#define RESUME_WAIT_TIME	50
+#define RESUME_WAIT_JIFFIES	msecs_to_jiffies(RESUME_DETECT_TIME)
+#define RESUME_DETECT_COUNT	16
+#define RESUME_DETECT_TIME	50
+#define RESUME_DETECT_JIFFIES	msecs_to_jiffies(RESUME_DETECT_TIME)
 
 /* F_SDH30 extended Controller registers */
 #define F_SDH30_AHB_CONFIG		0x100
@@ -61,8 +67,59 @@ struct f_sdhost_priv {
 	int gpio_select_1v8;
 	u32 vendor_hs200;
 	struct device *dev;
+	unsigned int quirks;	/* Deviations from spec. */
+
+/* retry to detect mmc device when resume */
+#define F_SDH30_QUIRK_RESUME_DETECT_RETRY		(1<<0)
+
+	struct workqueue_struct *resume_detect_wq;
+	struct delayed_work resume_detect_work;
+	unsigned int		resume_detect_count;
+	unsigned int		resume_wait_count;
 };
 
+static void sdhci_f_sdh30_resume_detect_work_func(struct work_struct *work)
+{
+	struct f_sdhost_priv *priv = container_of(work, struct f_sdhost_priv,
+		resume_detect_work.work);
+	struct sdhci_host *host = dev_get_drvdata(priv->dev);
+	int err = 0;
+
+	if (mmc_bus_manual_resume(host->mmc)) {
+		pm_runtime_disable(&host->mmc->card->dev);
+		mmc_card_set_suspended(host->mmc->card);
+		err = host->mmc->bus_ops->resume(host->mmc);
+		if (priv->resume_detect_count-- && err)
+			queue_delayed_work(priv->resume_detect_wq,
+					   &priv->resume_detect_work,
+				RESUME_DETECT_JIFFIES);
+		else
+			pr_debug("%s: resume detection done (count:%d, wait:%d, err:%d)\n",
+				 mmc_hostname(host->mmc),
+				 priv->resume_detect_count,
+				 priv->resume_wait_count, err);
+	} else {
+		if (priv->resume_wait_count--)
+			queue_delayed_work(priv->resume_detect_wq,
+					   &priv->resume_detect_work,
+				RESUME_WAIT_JIFFIES);
+		else
+			pr_debug("%s: resume done\n", mmc_hostname(host->mmc));
+	}
+}
+
+static void sdhci_f_sdh30_resume_detect(struct mmc_host *mmc,
+					int detect, int wait)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+
+	priv->resume_detect_count = detect;
+	priv->resume_wait_count = wait;
+	queue_delayed_work(priv->resume_detect_wq,
+			   &priv->resume_detect_work, 0);
+}
+
 void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
 {
 	struct f_sdhost_priv *priv = sdhci_priv(host);
@@ -173,6 +230,12 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (of_find_property(pdev->dev.of_node, "resume-detect-retry", NULL)) {
+		dev_info(dev, "Applying resume detect retry quirk\n");
+		priv->quirks |= F_SDH30_QUIRK_RESUME_DETECT_RETRY;
+		host->mmc->caps2 |= MMC_CAP2_MANUAL_RESUME;
+	}
+
 	ret = mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
 	if (ret) {
 		dev_err(dev, "%s: parse voltage error\n", __func__);
@@ -225,6 +288,18 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 		}
 	}
 
+	/* Init workqueue */
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+		priv->resume_detect_wq = create_workqueue("sdhci_f_sdh30");
+		if (priv->resume_detect_wq == NULL) {
+			ret = -ENOMEM;
+			dev_err(dev, "Failed to create resume detection workqueue\n");
+			goto err_init_wq;
+		}
+		INIT_DELAYED_WORK(&priv->resume_detect_work,
+				  sdhci_f_sdh30_resume_detect_work_func);
+	}
+
 	platform_set_drvdata(pdev, host);
 
 #ifdef CONFIG_PM_RUNTIME
@@ -263,6 +338,9 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 	return 0;
 
 err_add_host:
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+		destroy_workqueue(priv->resume_detect_wq);
+err_init_wq:
 	if (gpio_is_valid(priv->gpio_select_1v8)) {
 		gpio_direction_output(priv->gpio_select_1v8, 1);
 		gpio_free(priv->gpio_select_1v8);
@@ -302,6 +380,9 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev)
 		gpio_free(priv->gpio_select_1v8);
 	}
 
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+		destroy_workqueue(priv->resume_detect_wq);
+
 	sdhci_free_host(host);
 	platform_set_drvdata(pdev, NULL);
 
@@ -319,7 +400,15 @@ static int sdhci_f_sdh30_suspend(struct device *dev)
 static int sdhci_f_sdh30_resume(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
 
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+		pr_debug("%s: start resume detect\n",
+			 mmc_hostname(host->mmc));
+		sdhci_f_sdh30_resume_detect(host->mmc,
+					    RESUME_DETECT_COUNT,
+					    RESUME_WAIT_COUNT);
+	}
 	return sdhci_resume_host(host);
 }
 #endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7960424..55221dd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -283,6 +283,7 @@ struct mmc_host {
 #define MMC_CAP2_HS400		(MMC_CAP2_HS400_1_8V | \
 				 MMC_CAP2_HS400_1_2V)
 #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
+#define MMC_CAP2_MANUAL_RESUME     (1 << 18)	/* Resume manually when error */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
@@ -338,6 +339,9 @@ struct mmc_host {
 	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
 	unsigned int		bus_refs;	/* reference counter */
 
+	unsigned int		bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME	(1 << 0)
+
 	unsigned int		sdio_irqs;
 	struct task_struct	*sdio_irq_thread;
 	bool			sdio_irq_pending;
@@ -384,6 +388,16 @@ static inline void *mmc_priv(struct mmc_host *host)
 #define mmc_dev(x)	((x)->parent)
 #define mmc_classdev(x)	(&(x)->class_dev)
 #define mmc_hostname(x)	(dev_name(&(x)->class_dev))
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags &		\
+	MMC_BUSRESUME_MANUAL_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual)
+{
+	if (manual)
+		host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+	else
+		host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME;
+}
 
 int mmc_power_save_host(struct mmc_host *host);
 int mmc_power_restore_host(struct mmc_host *host);
-- 
1.8.1.2


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

* [PATCH 3/8] mmc: core: add manual resume capability
@ 2014-07-13  6:30   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:30 UTC (permalink / raw)
  To: linux-arm-kernel

This patch adds manual resume for some embedded platforms with rootfs
stored in SD card. It references CONFIG_MMC_BLOCK_DEFERRED_RESUME in
kernel 3.10. It lets host controller driver to manually handle resume
by itself.

[symptom]
This issue is found on mb86s7x platforms with rootfs stored in SD card.
It failed to resume form STR suspend mode because SD card cannot be ready
in time. It take longer time (e.g., 600ms) to be ready for access.
The error log looks like below:

root at localhost:~# echo mem > /sys/power/state
[   30.441974] SUSPEND

SCB Firmware : Category 01 Version 02.03 Rev. 00_
    Config   : (no configuration)
root at localhost:~# [   30.702976] Buffer I/O error on device mmcblk1p2, logical block 31349
[   30.709678] Buffer I/O error on device mmcblk1p2, logical block 168073
[   30.716220] Buffer I/O error on device mmcblk1p2, logical block 168074
[   30.722759] Buffer I/O error on device mmcblk1p2, logical block 168075
[   30.729456] Buffer I/O error on device mmcblk1p2, logical block 31349
[   30.735916] Buffer I/O error on device mmcblk1p2, logical block 31350
[   30.742370] Buffer I/O error on device mmcblk1p2, logical block 31351
[   30.749025] Buffer I/O error on device mmcblk1p2, logical block 168075
[   30.755657] Buffer I/O error on device mmcblk1p2, logical block 31351
[   30.763130] Aborting journal on device mmcblk1p2-8.
[   30.768060] JBD2: Error -5 detected when updating journal superblock for mmcblk1p2-8.
[   30.776085] EXT4-fs error (device mmcblk1p2): ext4_journal_check_start:56: Detected aborted journal
[   30.785259] EXT4-fs (mmcblk1p2): Remounting filesystem read-only
[   31.370716] EXT4-fs error (device mmcblk1p2): ext4_find_entry:1309: inode #2490369: comm udevd: reading directory lblock 0
[   31.382485] EXT4-fs error (device mmcblk1p2): ext4_find_entry:1309: inode #1048577: comm udevd: reading directory lblock 0

[analysis]
In system resume path, mmc_sd_resume() is failed with error code -123
because at that time SD card is still not ready on mb86s7x platforms.

[solution]
In order to not blocking system resume path, this patch just sets a flag
MMC_BUSRESUME_MANUAL_RESUME when this error happened, and then host controller
driver can understand it by this flag. Then host controller driver have to
resume SD card manually and asynchronously.

Signed-off-by: Vincent Yang <Vincent.Yang@tw.fujitsu.com>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 drivers/mmc/core/core.c          |  4 ++
 drivers/mmc/core/sd.c            |  4 ++
 drivers/mmc/host/sdhci_f_sdh30.c | 89 ++++++++++++++++++++++++++++++++++++++++
 include/linux/mmc/host.h         | 14 +++++++
 4 files changed, 111 insertions(+)

diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c
index 764af63..51fce49 100644
--- a/drivers/mmc/core/core.c
+++ b/drivers/mmc/core/core.c
@@ -2648,6 +2648,10 @@ int mmc_pm_notify(struct notifier_block *notify_block,
 	case PM_POST_RESTORE:
 
 		spin_lock_irqsave(&host->lock, flags);
+		if (mmc_bus_manual_resume(host)) {
+			spin_unlock_irqrestore(&host->lock, flags);
+			break;
+		}
 		host->rescan_disable = 0;
 		spin_unlock_irqrestore(&host->lock, flags);
 		_mmc_detect_change(host, 0, false);
diff --git a/drivers/mmc/core/sd.c b/drivers/mmc/core/sd.c
index 0c44510..859390d 100644
--- a/drivers/mmc/core/sd.c
+++ b/drivers/mmc/core/sd.c
@@ -1133,6 +1133,10 @@ static int mmc_sd_resume(struct mmc_host *host)
 
 	if (!(host->caps & MMC_CAP_RUNTIME_RESUME)) {
 		err = _mmc_sd_resume(host);
+		if ((host->caps2 & MMC_CAP2_MANUAL_RESUME) && err)
+			mmc_set_bus_resume_policy(host, 1);
+		else
+			mmc_set_bus_resume_policy(host, 0);
 		pm_runtime_set_active(&host->card->dev);
 		pm_runtime_mark_last_busy(&host->card->dev);
 	}
diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
index 8d23f2d..32864ba 100644
--- a/drivers/mmc/host/sdhci_f_sdh30.c
+++ b/drivers/mmc/host/sdhci_f_sdh30.c
@@ -29,6 +29,12 @@
 #include "../core/core.h"
 
 #define DRIVER_NAME "f_sdh30"
+#define RESUME_WAIT_COUNT	100
+#define RESUME_WAIT_TIME	50
+#define RESUME_WAIT_JIFFIES	msecs_to_jiffies(RESUME_DETECT_TIME)
+#define RESUME_DETECT_COUNT	16
+#define RESUME_DETECT_TIME	50
+#define RESUME_DETECT_JIFFIES	msecs_to_jiffies(RESUME_DETECT_TIME)
 
 /* F_SDH30 extended Controller registers */
 #define F_SDH30_AHB_CONFIG		0x100
@@ -61,8 +67,59 @@ struct f_sdhost_priv {
 	int gpio_select_1v8;
 	u32 vendor_hs200;
 	struct device *dev;
+	unsigned int quirks;	/* Deviations from spec. */
+
+/* retry to detect mmc device when resume */
+#define F_SDH30_QUIRK_RESUME_DETECT_RETRY		(1<<0)
+
+	struct workqueue_struct *resume_detect_wq;
+	struct delayed_work resume_detect_work;
+	unsigned int		resume_detect_count;
+	unsigned int		resume_wait_count;
 };
 
+static void sdhci_f_sdh30_resume_detect_work_func(struct work_struct *work)
+{
+	struct f_sdhost_priv *priv = container_of(work, struct f_sdhost_priv,
+		resume_detect_work.work);
+	struct sdhci_host *host = dev_get_drvdata(priv->dev);
+	int err = 0;
+
+	if (mmc_bus_manual_resume(host->mmc)) {
+		pm_runtime_disable(&host->mmc->card->dev);
+		mmc_card_set_suspended(host->mmc->card);
+		err = host->mmc->bus_ops->resume(host->mmc);
+		if (priv->resume_detect_count-- && err)
+			queue_delayed_work(priv->resume_detect_wq,
+					   &priv->resume_detect_work,
+				RESUME_DETECT_JIFFIES);
+		else
+			pr_debug("%s: resume detection done (count:%d, wait:%d, err:%d)\n",
+				 mmc_hostname(host->mmc),
+				 priv->resume_detect_count,
+				 priv->resume_wait_count, err);
+	} else {
+		if (priv->resume_wait_count--)
+			queue_delayed_work(priv->resume_detect_wq,
+					   &priv->resume_detect_work,
+				RESUME_WAIT_JIFFIES);
+		else
+			pr_debug("%s: resume done\n", mmc_hostname(host->mmc));
+	}
+}
+
+static void sdhci_f_sdh30_resume_detect(struct mmc_host *mmc,
+					int detect, int wait)
+{
+	struct sdhci_host *host = mmc_priv(mmc);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
+
+	priv->resume_detect_count = detect;
+	priv->resume_wait_count = wait;
+	queue_delayed_work(priv->resume_detect_wq,
+			   &priv->resume_detect_work, 0);
+}
+
 void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
 {
 	struct f_sdhost_priv *priv = sdhci_priv(host);
@@ -173,6 +230,12 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 		}
 	}
 
+	if (of_find_property(pdev->dev.of_node, "resume-detect-retry", NULL)) {
+		dev_info(dev, "Applying resume detect retry quirk\n");
+		priv->quirks |= F_SDH30_QUIRK_RESUME_DETECT_RETRY;
+		host->mmc->caps2 |= MMC_CAP2_MANUAL_RESUME;
+	}
+
 	ret = mmc_of_parse_voltage(pdev->dev.of_node, &host->ocr_mask);
 	if (ret) {
 		dev_err(dev, "%s: parse voltage error\n", __func__);
@@ -225,6 +288,18 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 		}
 	}
 
+	/* Init workqueue */
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+		priv->resume_detect_wq = create_workqueue("sdhci_f_sdh30");
+		if (priv->resume_detect_wq == NULL) {
+			ret = -ENOMEM;
+			dev_err(dev, "Failed to create resume detection workqueue\n");
+			goto err_init_wq;
+		}
+		INIT_DELAYED_WORK(&priv->resume_detect_work,
+				  sdhci_f_sdh30_resume_detect_work_func);
+	}
+
 	platform_set_drvdata(pdev, host);
 
 #ifdef CONFIG_PM_RUNTIME
@@ -263,6 +338,9 @@ static int sdhci_f_sdh30_probe(struct platform_device *pdev)
 	return 0;
 
 err_add_host:
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+		destroy_workqueue(priv->resume_detect_wq);
+err_init_wq:
 	if (gpio_is_valid(priv->gpio_select_1v8)) {
 		gpio_direction_output(priv->gpio_select_1v8, 1);
 		gpio_free(priv->gpio_select_1v8);
@@ -302,6 +380,9 @@ static int sdhci_f_sdh30_remove(struct platform_device *pdev)
 		gpio_free(priv->gpio_select_1v8);
 	}
 
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY)
+		destroy_workqueue(priv->resume_detect_wq);
+
 	sdhci_free_host(host);
 	platform_set_drvdata(pdev, NULL);
 
@@ -319,7 +400,15 @@ static int sdhci_f_sdh30_suspend(struct device *dev)
 static int sdhci_f_sdh30_resume(struct device *dev)
 {
 	struct sdhci_host *host = dev_get_drvdata(dev);
+	struct f_sdhost_priv *priv = sdhci_priv(host);
 
+	if (priv->quirks & F_SDH30_QUIRK_RESUME_DETECT_RETRY) {
+		pr_debug("%s: start resume detect\n",
+			 mmc_hostname(host->mmc));
+		sdhci_f_sdh30_resume_detect(host->mmc,
+					    RESUME_DETECT_COUNT,
+					    RESUME_WAIT_COUNT);
+	}
 	return sdhci_resume_host(host);
 }
 #endif
diff --git a/include/linux/mmc/host.h b/include/linux/mmc/host.h
index 7960424..55221dd 100644
--- a/include/linux/mmc/host.h
+++ b/include/linux/mmc/host.h
@@ -283,6 +283,7 @@ struct mmc_host {
 #define MMC_CAP2_HS400		(MMC_CAP2_HS400_1_8V | \
 				 MMC_CAP2_HS400_1_2V)
 #define MMC_CAP2_SDIO_IRQ_NOTHREAD (1 << 17)
+#define MMC_CAP2_MANUAL_RESUME     (1 << 18)	/* Resume manually when error */
 
 	mmc_pm_flag_t		pm_caps;	/* supported pm features */
 
@@ -338,6 +339,9 @@ struct mmc_host {
 	const struct mmc_bus_ops *bus_ops;	/* current bus driver */
 	unsigned int		bus_refs;	/* reference counter */
 
+	unsigned int		bus_resume_flags;
+#define MMC_BUSRESUME_MANUAL_RESUME	(1 << 0)
+
 	unsigned int		sdio_irqs;
 	struct task_struct	*sdio_irq_thread;
 	bool			sdio_irq_pending;
@@ -384,6 +388,16 @@ static inline void *mmc_priv(struct mmc_host *host)
 #define mmc_dev(x)	((x)->parent)
 #define mmc_classdev(x)	(&(x)->class_dev)
 #define mmc_hostname(x)	(dev_name(&(x)->class_dev))
+#define mmc_bus_manual_resume(host) ((host)->bus_resume_flags &		\
+	MMC_BUSRESUME_MANUAL_RESUME)
+
+static inline void mmc_set_bus_resume_policy(struct mmc_host *host, int manual)
+{
+	if (manual)
+		host->bus_resume_flags |= MMC_BUSRESUME_MANUAL_RESUME;
+	else
+		host->bus_resume_flags &= ~MMC_BUSRESUME_MANUAL_RESUME;
+}
 
 int mmc_power_save_host(struct mmc_host *host);
 int mmc_power_restore_host(struct mmc_host *host);
-- 
1.8.1.2

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

* [PATCH 4/8] clk: Add clock driver for mb86s7x
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:30   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:30 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A, arnd-r2nGTMty4D4,
	olof-nZhT3qVonbNeoWH0uzbU5w, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, Mollie Wu, Tetsuya Takinishi

 The CRG11 clock controller is managed by remote f/w.
This driver simply maps Linux CLK ops onto mailbox api.

Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
Cc: Olof <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>
Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
 3 files changed, 385 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
 create mode 100644 drivers/clk/clk-mb86s7x.c

diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
new file mode 100644
index 0000000..4a17d79
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
@@ -0,0 +1,32 @@
+Fujitsu CRG11 clock driver bindings
+-----------------------------------
+
+Required properties :
+- compatible : Shall contain "fujitsu,mb86s7x_clk"
+- #clock-cells : Shall be 0
+- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
+- domain : [0, 15]
+- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+
+The consumer specifies the desired clock pointing to its phandle.
+
+Example:
+
+	clk_alw_2_1: clk_alw_2_1 {
+		compatible = "fujitsu,mb86s7x_clk";
+		#clock-cells = <0>;
+		cntrlr = <0>;
+		domain = <2>;
+		port = <1>;
+	};
+
+	mhu: mhu0@2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+		clocks = <&clk_alw_2_1>;
+		clock-names = "clk";
+	};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..ccbce66 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
 obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
+obj-$(CONFIG_ARCH_MB86S7X)		+= clk-mb86s7x.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
 obj-$(CONFIG_CLK_PPC_CORENET)		+= clk-ppc-corenet.o
diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c
new file mode 100644
index 0000000..ddb5f13
--- /dev/null
+++ b/drivers/clk/clk-mb86s7x.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2013-2014 FUJITSU SEMICONDUCTOR LIMITED
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/cpu.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#define to_crg_clk(p) container_of(p, struct crg_clk, hw)
+#define to_clc_clk(p) container_of(p, struct cl_clk, hw)
+
+struct hack_rate {
+	unsigned clk_id;
+	unsigned long rate;
+	int gated;
+};
+
+struct crg_clk {
+	struct clk_hw hw;
+	u8 cntrlr, domain, port;
+};
+
+static int crg_gate_control(struct clk_hw *hw, int en)
+{
+	struct crg_clk *crgclk = to_crg_clk(hw);
+	struct mb86s7x_peri_clk cmd;
+	struct completion got_rsp;
+	int ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cntrlr = crgclk->cntrlr;
+	cmd.domain = crgclk->domain;
+	cmd.port = crgclk->port;
+	cmd.en = en;
+
+	/* Port-8 is UngatedCLK */
+	if (cmd.port == 8)
+		return en ? 0 : -EINVAL;
+
+	pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u En-%u}\n",
+		 __func__, __LINE__, cmd.cntrlr,
+		 cmd.domain, cmd.port, cmd.en);
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(CMD_PERI_CLOCK_GATE_SET_REQ,
+			      &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return ret;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u En-%u}\n",
+		 __func__, __LINE__, cmd.cntrlr,
+		 cmd.domain, cmd.port, cmd.en);
+
+	/* If the request was rejected */
+	if (cmd.en != en)
+		ret = -EINVAL;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+static int crg_port_prepare(struct clk_hw *hw)
+{
+	return crg_gate_control(hw, 1);
+}
+
+static void crg_port_unprepare(struct clk_hw *hw)
+{
+	crg_gate_control(hw, 0);
+}
+
+static int
+crg_rate_control(struct clk_hw *hw, int set, unsigned long *rate)
+{
+	struct crg_clk *crgclk = to_crg_clk(hw);
+	struct mb86s7x_peri_clk cmd;
+	struct completion got_rsp;
+	int code, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cntrlr = crgclk->cntrlr;
+	cmd.domain = crgclk->domain;
+	cmd.port = crgclk->port;
+	cmd.freqency = *rate;
+
+	if (set) {
+		code = CMD_PERI_CLOCK_RATE_SET_REQ;
+		pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+	} else {
+		code = CMD_PERI_CLOCK_RATE_GET_REQ;
+		pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-GET}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port);
+	}
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(code, &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return ret;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	if (set)
+		pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+	else
+		pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-GOT %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+
+	*rate = cmd.freqency;
+	return 0;
+}
+
+static unsigned long
+crg_port_recalc_rate(struct clk_hw *hw,	unsigned long parent_rate)
+{
+	unsigned long rate;
+
+	crg_rate_control(hw, 0, &rate);
+
+	return rate;
+}
+
+static long
+crg_port_round_rate(struct clk_hw *hw,
+		    unsigned long rate, unsigned long *pr)
+{
+	return rate;
+}
+
+static int
+crg_port_set_rate(struct clk_hw *hw,
+		  unsigned long rate, unsigned long parent_rate)
+{
+	return crg_rate_control(hw, 1, &rate);
+}
+
+const struct clk_ops crg_port_ops = {
+	.prepare = crg_port_prepare,
+	.unprepare = crg_port_unprepare,
+	.recalc_rate = crg_port_recalc_rate,
+	.round_rate = crg_port_round_rate,
+	.set_rate = crg_port_set_rate,
+};
+
+static void __init crg_port_init(struct device_node *node)
+{
+	struct clk_init_data init;
+	u32 cntrlr, domain, port;
+	struct crg_clk *crgclk;
+	struct clk *clk;
+	char clkp[20];
+	int rc;
+
+	rc = of_property_read_u32(node, "cntrlr", &cntrlr);
+	if (WARN_ON(rc))
+		return;
+	rc = of_property_read_u32(node, "domain", &domain);
+	if (WARN_ON(rc))
+		return;
+	rc = of_property_read_u32(node, "port", &port);
+	if (WARN_ON(rc))
+		return;
+
+	if (port > 7)
+		snprintf(clkp, 20, "UngatedCLK%d_%X", cntrlr, domain);
+	else
+		snprintf(clkp, 20, "CLK%d_%X_%d", cntrlr, domain, port);
+
+	clk = __clk_lookup(clkp);
+	if (clk)
+		return;
+
+	crgclk = kzalloc(sizeof(*crgclk), GFP_KERNEL);
+	if (!crgclk)
+		return;
+	init.name = clkp;
+	init.num_parents = 0;
+	init.ops = &crg_port_ops;
+	init.flags = CLK_IS_ROOT;
+	crgclk->hw.init = &init;
+	crgclk->cntrlr = cntrlr;
+	crgclk->domain = domain;
+	crgclk->port = port;
+	clk = clk_register(NULL, &crgclk->hw);
+	if (IS_ERR(clk))
+		pr_err("%s:%d Error!\n", __func__, __LINE__);
+	else
+		pr_debug("Registered %s\n", clkp);
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+	clk_register_clkdev(clk, clkp, NULL);
+}
+CLK_OF_DECLARE(crg11_gate, "fujitsu,mb86s7x_clk", crg_port_init);
+
+struct cl_clk {
+	struct clk_hw hw;
+	int cluster;
+};
+
+static void mhu_cluster_rate(struct clk_hw *hw, unsigned long *rate, int get)
+{
+	struct cl_clk *clc = to_clc_clk(hw);
+	struct mb86s7x_cpu_freq cmd;
+	struct completion got_rsp;
+	int code, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cluster_class = 0;
+	cmd.cluster_id = clc->cluster;
+	cmd.cpu_id = 0;
+	cmd.freqency = *rate;
+
+	if (get)
+		code = CMD_CPU_CLOCK_RATE_GET_REQ;
+	else
+		code = CMD_CPU_CLOCK_RATE_SET_REQ;
+
+	pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n",
+		 __func__, __LINE__, cmd.cluster_class,
+		 cmd.cluster_id, cmd.cpu_id, cmd.freqency);
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(code, &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n",
+		 __func__, __LINE__, cmd.cluster_class,
+		 cmd.cluster_id, cmd.cpu_id, cmd.freqency);
+
+	*rate = cmd.freqency;
+}
+
+static unsigned long
+clc_recalc_rate(struct clk_hw *hw, unsigned long unused)
+{
+	unsigned long rate;
+
+	mhu_cluster_rate(hw, &rate, 1);
+	return rate;
+}
+
+static long
+clc_round_rate(struct clk_hw *hw, unsigned long rate,
+	       unsigned long *unused)
+{
+	return rate;
+}
+
+static int
+clc_set_rate(struct clk_hw *hw, unsigned long rate,
+	     unsigned long unused)
+{
+	unsigned long res = rate;
+
+	mhu_cluster_rate(hw, &res, 0);
+
+	return (res == rate) ? 0 : -EINVAL;
+}
+
+static struct clk_ops clk_clc_ops = {
+	.recalc_rate = clc_recalc_rate,
+	.round_rate = clc_round_rate,
+	.set_rate = clc_set_rate,
+};
+
+struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
+{
+	struct clk_init_data init;
+	struct cl_clk *clc;
+
+	clc = kzalloc(sizeof(*clc), GFP_KERNEL);
+	if (!clc) {
+		pr_err("could not allocate cl_clk\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	clc->hw.init = &init;
+	clc->cluster = topology_physical_package_id(cpu_dev->id);
+
+	init.name = dev_name(cpu_dev);
+	init.ops = &clk_clc_ops;
+	init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
+	init.num_parents = 0;
+
+	return devm_clk_register(cpu_dev, &clc->hw);
+}
+
+static int mb86s7x_clclk_of_init(void)
+{
+	int cpu;
+	struct clk *clk;
+
+	for_each_possible_cpu(cpu) {
+		struct device *cpu_dev = get_cpu_device(cpu);
+
+		if (!cpu_dev) {
+			pr_err("failed to get cpu%d device\n", cpu);
+			continue;
+		}
+
+		clk = mb86s7x_clclk_register(cpu_dev);
+		if (IS_ERR(clk)) {
+			pr_err("failed to register cpu%d clock\n", cpu);
+			continue;
+		}
+		if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
+			pr_err("failed to register cpu%d clock lookup\n", cpu);
+			continue;
+		}
+		pr_debug("registered clk for %s\n", dev_name(cpu_dev));
+	}
+
+	platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
+
+	return 0;
+}
+module_init(mb86s7x_clclk_of_init);
-- 
1.8.1.2

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

* [PATCH 4/8] clk: Add clock driver for mb86s7x
@ 2014-07-13  6:30   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:30 UTC (permalink / raw)
  To: linux-arm-kernel

 The CRG11 clock controller is managed by remote f/w.
This driver simply maps Linux CLK ops onto mailbox api.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
Cc: Arnd Bergmann <arnd@arndb.de>
Cc: Olof <olof@lixom.net>
Cc: Russell King <linux@arm.linux.org.uk>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
 3 files changed, 385 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
 create mode 100644 drivers/clk/clk-mb86s7x.c

diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
new file mode 100644
index 0000000..4a17d79
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
@@ -0,0 +1,32 @@
+Fujitsu CRG11 clock driver bindings
+-----------------------------------
+
+Required properties :
+- compatible : Shall contain "fujitsu,mb86s7x_clk"
+- #clock-cells : Shall be 0
+- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
+- domain : [0, 15]
+- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
+
+The consumer specifies the desired clock pointing to its phandle.
+
+Example:
+
+	clk_alw_2_1: clk_alw_2_1 {
+		compatible = "fujitsu,mb86s7x_clk";
+		#clock-cells = <0>;
+		cntrlr = <0>;
+		domain = <2>;
+		port = <1>;
+	};
+
+	mhu: mhu0 at 2b1f0000 {
+		#mbox-cells = <1>;
+		compatible = "fujitsu,mhu";
+		reg = <0 0x2B1F0000 0x1000>;
+		interrupts = <0 36 4>, /* LP Non-Sec */
+			     <0 35 4>, /* HP Non-Sec */
+			     <0 37 4>; /* Secure */
+		clocks = <&clk_alw_2_1>;
+		clock-names = "clk";
+	};
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 567f102..ccbce66 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@ obj-$(CONFIG_ARCH_HIGHBANK)		+= clk-highbank.o
 obj-$(CONFIG_MACH_LOONGSON1)		+= clk-ls1x.o
 obj-$(CONFIG_COMMON_CLK_MAX77686)	+= clk-max77686.o
 obj-$(CONFIG_ARCH_MOXART)		+= clk-moxart.o
+obj-$(CONFIG_ARCH_MB86S7X)		+= clk-mb86s7x.o
 obj-$(CONFIG_ARCH_NOMADIK)		+= clk-nomadik.o
 obj-$(CONFIG_ARCH_NSPIRE)		+= clk-nspire.o
 obj-$(CONFIG_CLK_PPC_CORENET)		+= clk-ppc-corenet.o
diff --git a/drivers/clk/clk-mb86s7x.c b/drivers/clk/clk-mb86s7x.c
new file mode 100644
index 0000000..ddb5f13
--- /dev/null
+++ b/drivers/clk/clk-mb86s7x.c
@@ -0,0 +1,352 @@
+/*
+ * Copyright (C) 2013-2014 FUJITSU SEMICONDUCTOR LIMITED
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clkdev.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/cpu.h>
+#include <linux/clk-provider.h>
+#include <linux/spinlock.h>
+#include <linux/module.h>
+#include <linux/topology.h>
+#include <linux/mailbox_client.h>
+#include <linux/platform_device.h>
+#include <linux/platform_data/mb86s7x_mbox.h>
+
+#define to_crg_clk(p) container_of(p, struct crg_clk, hw)
+#define to_clc_clk(p) container_of(p, struct cl_clk, hw)
+
+struct hack_rate {
+	unsigned clk_id;
+	unsigned long rate;
+	int gated;
+};
+
+struct crg_clk {
+	struct clk_hw hw;
+	u8 cntrlr, domain, port;
+};
+
+static int crg_gate_control(struct clk_hw *hw, int en)
+{
+	struct crg_clk *crgclk = to_crg_clk(hw);
+	struct mb86s7x_peri_clk cmd;
+	struct completion got_rsp;
+	int ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cntrlr = crgclk->cntrlr;
+	cmd.domain = crgclk->domain;
+	cmd.port = crgclk->port;
+	cmd.en = en;
+
+	/* Port-8 is UngatedCLK */
+	if (cmd.port == 8)
+		return en ? 0 : -EINVAL;
+
+	pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u En-%u}\n",
+		 __func__, __LINE__, cmd.cntrlr,
+		 cmd.domain, cmd.port, cmd.en);
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(CMD_PERI_CLOCK_GATE_SET_REQ,
+			      &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return ret;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u En-%u}\n",
+		 __func__, __LINE__, cmd.cntrlr,
+		 cmd.domain, cmd.port, cmd.en);
+
+	/* If the request was rejected */
+	if (cmd.en != en)
+		ret = -EINVAL;
+	else
+		ret = 0;
+
+	return ret;
+}
+
+static int crg_port_prepare(struct clk_hw *hw)
+{
+	return crg_gate_control(hw, 1);
+}
+
+static void crg_port_unprepare(struct clk_hw *hw)
+{
+	crg_gate_control(hw, 0);
+}
+
+static int
+crg_rate_control(struct clk_hw *hw, int set, unsigned long *rate)
+{
+	struct crg_clk *crgclk = to_crg_clk(hw);
+	struct mb86s7x_peri_clk cmd;
+	struct completion got_rsp;
+	int code, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cntrlr = crgclk->cntrlr;
+	cmd.domain = crgclk->domain;
+	cmd.port = crgclk->port;
+	cmd.freqency = *rate;
+
+	if (set) {
+		code = CMD_PERI_CLOCK_RATE_SET_REQ;
+		pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+	} else {
+		code = CMD_PERI_CLOCK_RATE_GET_REQ;
+		pr_debug("%s:%d CMD Cntrlr-%u Dom-%u Port-%u Rate-GET}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port);
+	}
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(code, &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return ret;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	if (set)
+		pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-SET %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+	else
+		pr_debug("%s:%d REP Cntrlr-%u Dom-%u Port-%u Rate-GOT %lluHz}\n",
+			 __func__, __LINE__, cmd.cntrlr,
+			 cmd.domain, cmd.port, cmd.freqency);
+
+	*rate = cmd.freqency;
+	return 0;
+}
+
+static unsigned long
+crg_port_recalc_rate(struct clk_hw *hw,	unsigned long parent_rate)
+{
+	unsigned long rate;
+
+	crg_rate_control(hw, 0, &rate);
+
+	return rate;
+}
+
+static long
+crg_port_round_rate(struct clk_hw *hw,
+		    unsigned long rate, unsigned long *pr)
+{
+	return rate;
+}
+
+static int
+crg_port_set_rate(struct clk_hw *hw,
+		  unsigned long rate, unsigned long parent_rate)
+{
+	return crg_rate_control(hw, 1, &rate);
+}
+
+const struct clk_ops crg_port_ops = {
+	.prepare = crg_port_prepare,
+	.unprepare = crg_port_unprepare,
+	.recalc_rate = crg_port_recalc_rate,
+	.round_rate = crg_port_round_rate,
+	.set_rate = crg_port_set_rate,
+};
+
+static void __init crg_port_init(struct device_node *node)
+{
+	struct clk_init_data init;
+	u32 cntrlr, domain, port;
+	struct crg_clk *crgclk;
+	struct clk *clk;
+	char clkp[20];
+	int rc;
+
+	rc = of_property_read_u32(node, "cntrlr", &cntrlr);
+	if (WARN_ON(rc))
+		return;
+	rc = of_property_read_u32(node, "domain", &domain);
+	if (WARN_ON(rc))
+		return;
+	rc = of_property_read_u32(node, "port", &port);
+	if (WARN_ON(rc))
+		return;
+
+	if (port > 7)
+		snprintf(clkp, 20, "UngatedCLK%d_%X", cntrlr, domain);
+	else
+		snprintf(clkp, 20, "CLK%d_%X_%d", cntrlr, domain, port);
+
+	clk = __clk_lookup(clkp);
+	if (clk)
+		return;
+
+	crgclk = kzalloc(sizeof(*crgclk), GFP_KERNEL);
+	if (!crgclk)
+		return;
+	init.name = clkp;
+	init.num_parents = 0;
+	init.ops = &crg_port_ops;
+	init.flags = CLK_IS_ROOT;
+	crgclk->hw.init = &init;
+	crgclk->cntrlr = cntrlr;
+	crgclk->domain = domain;
+	crgclk->port = port;
+	clk = clk_register(NULL, &crgclk->hw);
+	if (IS_ERR(clk))
+		pr_err("%s:%d Error!\n", __func__, __LINE__);
+	else
+		pr_debug("Registered %s\n", clkp);
+
+	of_clk_add_provider(node, of_clk_src_simple_get, clk);
+	clk_register_clkdev(clk, clkp, NULL);
+}
+CLK_OF_DECLARE(crg11_gate, "fujitsu,mb86s7x_clk", crg_port_init);
+
+struct cl_clk {
+	struct clk_hw hw;
+	int cluster;
+};
+
+static void mhu_cluster_rate(struct clk_hw *hw, unsigned long *rate, int get)
+{
+	struct cl_clk *clc = to_clc_clk(hw);
+	struct mb86s7x_cpu_freq cmd;
+	struct completion got_rsp;
+	int code, ret;
+
+	cmd.payload_size = sizeof(cmd);
+	cmd.cluster_class = 0;
+	cmd.cluster_id = clc->cluster;
+	cmd.cpu_id = 0;
+	cmd.freqency = *rate;
+
+	if (get)
+		code = CMD_CPU_CLOCK_RATE_GET_REQ;
+	else
+		code = CMD_CPU_CLOCK_RATE_SET_REQ;
+
+	pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n",
+		 __func__, __LINE__, cmd.cluster_class,
+		 cmd.cluster_id, cmd.cpu_id, cmd.freqency);
+
+	init_completion(&got_rsp);
+	ret = mhu_send_packet(code, &cmd, sizeof(cmd), &got_rsp);
+	if (ret < 0) {
+		pr_err("%s:%d failed!\n", __func__, __LINE__);
+		return;
+	}
+	if (ret)
+		wait_for_completion(&got_rsp);
+
+	pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u Freq-%llu}\n",
+		 __func__, __LINE__, cmd.cluster_class,
+		 cmd.cluster_id, cmd.cpu_id, cmd.freqency);
+
+	*rate = cmd.freqency;
+}
+
+static unsigned long
+clc_recalc_rate(struct clk_hw *hw, unsigned long unused)
+{
+	unsigned long rate;
+
+	mhu_cluster_rate(hw, &rate, 1);
+	return rate;
+}
+
+static long
+clc_round_rate(struct clk_hw *hw, unsigned long rate,
+	       unsigned long *unused)
+{
+	return rate;
+}
+
+static int
+clc_set_rate(struct clk_hw *hw, unsigned long rate,
+	     unsigned long unused)
+{
+	unsigned long res = rate;
+
+	mhu_cluster_rate(hw, &res, 0);
+
+	return (res == rate) ? 0 : -EINVAL;
+}
+
+static struct clk_ops clk_clc_ops = {
+	.recalc_rate = clc_recalc_rate,
+	.round_rate = clc_round_rate,
+	.set_rate = clc_set_rate,
+};
+
+struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
+{
+	struct clk_init_data init;
+	struct cl_clk *clc;
+
+	clc = kzalloc(sizeof(*clc), GFP_KERNEL);
+	if (!clc) {
+		pr_err("could not allocate cl_clk\n");
+		return ERR_PTR(-ENOMEM);
+	}
+
+	clc->hw.init = &init;
+	clc->cluster = topology_physical_package_id(cpu_dev->id);
+
+	init.name = dev_name(cpu_dev);
+	init.ops = &clk_clc_ops;
+	init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
+	init.num_parents = 0;
+
+	return devm_clk_register(cpu_dev, &clc->hw);
+}
+
+static int mb86s7x_clclk_of_init(void)
+{
+	int cpu;
+	struct clk *clk;
+
+	for_each_possible_cpu(cpu) {
+		struct device *cpu_dev = get_cpu_device(cpu);
+
+		if (!cpu_dev) {
+			pr_err("failed to get cpu%d device\n", cpu);
+			continue;
+		}
+
+		clk = mb86s7x_clclk_register(cpu_dev);
+		if (IS_ERR(clk)) {
+			pr_err("failed to register cpu%d clock\n", cpu);
+			continue;
+		}
+		if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
+			pr_err("failed to register cpu%d clock lookup\n", cpu);
+			continue;
+		}
+		pr_debug("registered clk for %s\n", dev_name(cpu_dev));
+	}
+
+	platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
+
+	return 0;
+}
+module_init(mb86s7x_clclk_of_init);
-- 
1.8.1.2

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:31   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:31 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	linux-lFZ/pmaqli7XmaaqVzeoHQ, arnd-r2nGTMty4D4,
	olof-nZhT3qVonbNeoWH0uzbU5w, mark.rutland-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	linus.walleij-QSEj5FYQhm4dnm+yROfE0A, Mollie Wu,
	Tetsuya Takinishi

The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
GPIOs are supported on top of Pinctrl api.

Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Cc: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 .../bindings/gpio/fujitsu,mb86s7x-gpio.txt         |   22 +
 .../bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt   |   30 +
 drivers/pinctrl/Kconfig                            |    5 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-mb86s7x.c                  | 1281 ++++++++++++++++++++
 5 files changed, 1339 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-mb86s7x.c

diff --git a/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
new file mode 100644
index 0000000..c262efa
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
@@ -0,0 +1,22 @@
+Fujitsu MB86S7x GPIO Controller
+-------------------------------
+
+Required properties:
+- compatible: Should be "fujitsu,mb86s7x-gpio"
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <2>. The first cell is the pin number and the
+  second cell is used to specify optional parameters:
+   - bit 0 specifies polarity (0 for normal, 1 for inverted).
+
+GPIO ranges are specified as described in
+Documentation/devicetree/bindings/gpio/gpio.txt
+
+Examples:
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
new file mode 100644
index 0000000..ce2011b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
@@ -0,0 +1,30 @@
+Fujitsu MB86S7x Pin Controller
+------------------------------
+
+Required properties:
+- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
+- reg: Should contain the register physical address and length for the
+  pin controller.
+- #gpio-range-cells: Should be 3
+
+Optional subnode-properties:
+- mb86s7x,function: String specifying the function name.
+- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
+    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
+- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up
+
+Examples:
+	pinctrl: pinctrl@2a4d0000 {
+		compatible = "fujitsu,mb86s73-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
+		#gpio-range-cells = <3>;
+
+		hsspi0_pins_active: hsspi0_active {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <8>; /* in mA */
+		};
+		hsspi0_pins_sleep: hsspi0_sleep {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+	};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0042ccb..1f053fc 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -213,6 +213,11 @@ config PINCTRL_FALCON
 	depends on SOC_FALCON
 	depends on PINCTRL_LANTIQ
 
+config PINCTRL_MB86S7X
+	bool
+	select PINMUX
+	select GENERIC_PINCONF
+
 config PINCTRL_MXS
 	bool
 	select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c4b5d40..edcb823 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PINCTRL_IMX6Q)	+= pinctrl-imx6dl.o
 obj-$(CONFIG_PINCTRL_IMX6SL)	+= pinctrl-imx6sl.o
 obj-$(CONFIG_PINCTRL_IMX6SX)	+= pinctrl-imx6sx.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
+obj-$(CONFIG_PINCTRL_MB86S7X)	+= pinctrl-mb86s7x.o
 obj-$(CONFIG_PINCTRL_MXS)	+= pinctrl-mxs.o
 obj-$(CONFIG_PINCTRL_IMX23)	+= pinctrl-imx23.o
 obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
diff --git a/drivers/pinctrl/pinctrl-mb86s7x.c b/drivers/pinctrl/pinctrl-mb86s7x.c
new file mode 100644
index 0000000..74c72ec8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mb86s7x.c
@@ -0,0 +1,1281 @@
+/*
+ *  linux/drivers/pinctrl/pinctrl-mb86s7x.c
+ *
+ *  Copyright (C) 2014 Fujitsu Semiconductor Limited
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#define PDR(x)	(0x0 + x)
+#define DDR(x)	(0x10 + x)
+#define PFR(x)	(0x20 + x)
+
+#define CDRV_PDG(x)	(0x0 + ((x) / 16) * 4)
+#define FORCEZ_PDG(x)	(0x100 + ((x) / 16) * 4)
+#define PULL_PDG(x)	(0x200 + ((x) / 16) * 4)
+#define PIN_OFF(x)	(((x) % 16) * 2)
+
+#define PMUX_MB86S70	0
+#define PMUX_MB86S73	1
+
+/* Virtual pin numbers start so that reg offset calculations get simple */
+enum {
+	PINS_VIRT = 128,
+	PINS_PCIE0 = PINS_VIRT,
+	PINS_HOLE1,
+	PINS_USB3H0,
+	PINS_HOLE2,
+	PINS_USB2H0,
+	PINS_USB2D0,
+	PINS_SDH30,
+	PINS_HOLE3,
+	PINS_EMMC0,
+	PINS_HSSPI0,
+	PINS_GMACD0,
+	PINS_GMACM0,
+	PINS_I2S0,
+	PINS_UART0,
+	PINS_OTHER0,
+	PINS_JTAG0,
+	PINS_PCIE1,
+	PINS_HOLE4,
+	PINS_USB3H1,
+	MB86S70_PMUX_PINS,
+};
+
+struct mb86s70_gpio_chip {
+	spinlock_t lock; /* for goio regs */
+	struct clk *clk;
+	void __iomem *base;
+	struct gpio_chip gc;
+};
+
+static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+	int ret = pinctrl_request_gpio(gc->base + offset);
+
+	if (!ret) {
+		struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+		unsigned long flags;
+		u32 val;
+
+		spin_lock_irqsave(&gchip->lock, flags);
+		val = readl(gchip->base + PFR(offset / 8 * 4));
+		val &= ~(1 << (offset % 8)); /* as gpio-port */
+		writel(val, gchip->base + PFR(offset / 8 * 4));
+		spin_unlock_irqrestore(&gchip->lock, flags);
+	}
+
+	return ret;
+}
+
+static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+					struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	val = readl(gchip->base + PFR(offset / 8 * 4));
+	val |= (1 << (offset % 8)); /* as peri-port */
+	writel(val, gchip->base + PFR(offset / 8 * 4));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	pinctrl_free_gpio(gc->base + offset);
+}
+
+static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	val = readl(gchip->base + DDR(offset / 8 * 4));
+	val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + DDR(offset / 8 * 4));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
+					 unsigned offset, int value)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	if (value)
+		val |= (1 << (offset % 8));
+	else
+		val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + PDR(offset / 8 * 4));
+
+	val = readl(gchip->base + DDR(offset / 8 * 4));
+	val |= (1 << (offset % 8));
+	writel(val, gchip->base + DDR(offset / 8 * 4));
+
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned char val;
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	val &= (1 << (offset % 8));
+	return val ? 1 : 0;
+}
+
+static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	if (value)
+		val |= (1 << (offset % 8));
+	else
+		val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + PDR(offset / 8 * 4));
+
+	spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static int mb86s70_gpio_probe(struct platform_device *pdev)
+{
+	struct mb86s70_gpio_chip *gchip;
+	struct resource *res;
+	int ret;
+
+	gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
+	if (gchip == NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, gchip);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	gchip->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(gchip->base))
+		return PTR_ERR(gchip->base);
+
+	gchip->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(gchip->clk))
+		return PTR_ERR(gchip->clk);
+
+	clk_prepare_enable(gchip->clk);
+
+	spin_lock_init(&gchip->lock);
+
+	gchip->gc.direction_output = mb86s70_gpio_direction_output;
+	gchip->gc.direction_input = mb86s70_gpio_direction_input;
+	gchip->gc.request = mb86s70_gpio_request;
+	gchip->gc.free = mb86s70_gpio_free;
+	gchip->gc.get = mb86s70_gpio_get;
+	gchip->gc.set = mb86s70_gpio_set;
+	gchip->gc.label = dev_name(&pdev->dev);
+	gchip->gc.ngpio = 32;
+	gchip->gc.owner = THIS_MODULE;
+	gchip->gc.dev = &pdev->dev;
+	gchip->gc.base = -1;
+
+	ret = gpiochip_add(&gchip->gc);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't register gpio driver\n");
+		clk_disable_unprepare(gchip->clk);
+	}
+
+	return ret;
+}
+
+struct mb86s70_pmux_chip {
+	spinlock_t lock; /* mux regs */
+	struct device *dev;
+	void __iomem *base;
+	void __iomem *conf_base;
+	struct pinctrl_dev *pctl;
+	struct pinctrl_desc desc;
+	bool pin_busy[MB86S70_PMUX_PINS];
+	char pin_names[5 * MB86S70_PMUX_PINS];
+	struct pinctrl_pin_desc pins[MB86S70_PMUX_PINS];
+};
+
+struct mb86s70_pmx_grp {
+	const char *name;
+	const unsigned *pins;
+	const unsigned num_pins;
+};
+
+struct mb86s70_pmx_function {
+	const unsigned num_groups;
+	unsigned prog_val;
+	const char *name;
+
+	const char *const *groups;
+};
+
+static const unsigned extint16_s70[] = {0};
+static const unsigned tsif0_s70[] = {16, 17, 18, 19};
+static const unsigned tsif1_s70[] = {16, 17, 18, 19};
+static const unsigned uart1_s70[] = {56, 57, 58, 59};
+static const unsigned uart2_s70[] = {60, 61, 62, 63};
+static const unsigned pl244_s70[] = {40, 41, 42, 43, 44, 45, 46, 47, 48,
+					49, 50, 51, 52, 53, 54, 55, 56, 57,
+					58, 59, 60, 61, 62};
+static const unsigned trace_s70[] = {24, 25, 26, 27, 28, 29, 30, 31, 32,
+					33, 34, 35, 36, 37, 38, 39, 64, 65};
+static const unsigned memcs_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24,
+					25, 26, 27, 28, 29, 30, 31, 32, 33,
+					34, 35, 36, 37, 38, 39, 40, 41, 42,
+					43, 44, 45, 46, 47, 48, 49, 50, 51,
+					52, 53, 54, 55, 56, 57, 58, 59, 60,
+					61, 62, 63, 64, 65};
+static const unsigned cap_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+					26, 27, 28, 29, 30, 31, 32, 33, 34,
+					35, 36, 37, 38, 39, 40, 41, 42, 43,
+					44, 45, 46, 47};
+
+static const struct mb86s70_pmx_grp mb86s70_pgrps[] = {
+	{
+		.name = "extint16_grp",
+		.pins = extint16_s70,
+		.num_pins = ARRAY_SIZE(extint16_s70),
+	},
+	{
+		.name = "tsif0_grp",
+		.pins = tsif0_s70,
+		.num_pins = ARRAY_SIZE(tsif0_s70),
+	},
+	{
+		.name = "tsif1_grp",
+		.pins = tsif1_s70,
+		.num_pins = ARRAY_SIZE(tsif1_s70),
+	},
+	{
+		.name = "uart1_grp",
+		.pins = uart1_s70,
+		.num_pins = ARRAY_SIZE(uart1_s70),
+	},
+	{
+		.name = "uart2_grp",
+		.pins = uart2_s70,
+		.num_pins = ARRAY_SIZE(uart2_s70),
+	},
+	{
+		.name = "pl244_grp",
+		.pins = pl244_s70,
+		.num_pins = ARRAY_SIZE(pl244_s70),
+	},
+	{
+		.name = "trace_grp",
+		.pins = trace_s70,
+		.num_pins = ARRAY_SIZE(trace_s70),
+	},
+	{
+		.name = "memcs_grp",
+		.pins = memcs_s70,
+		.num_pins = ARRAY_SIZE(memcs_s70),
+	},
+	{
+		.name = "cap_grp",
+		.pins = cap_s70,
+		.num_pins = ARRAY_SIZE(cap_s70),
+	}
+};
+
+static const unsigned pcie0_s73[] = {PINS_PCIE0};
+static const unsigned usb3h0_s73[] = {PINS_USB3H0};
+static const unsigned usb2h0_s73[] = {PINS_USB2H0};
+static const unsigned usb2d0_s73[] = {PINS_USB2D0};
+static const unsigned sdh30_s73[] = {PINS_SDH30};
+static const unsigned emmc0_s73[] = {PINS_EMMC0};
+static const unsigned hsspi0_s73[] = {PINS_HSSPI0};
+static const unsigned gmacd0_s73[] = {PINS_GMACD0};
+static const unsigned gmacm0_s73[] = {PINS_GMACM0};
+static const unsigned i2s0_s73[] = {PINS_I2S0};
+static const unsigned other0_s73[] = {PINS_OTHER0};
+static const unsigned jtag0_s73[] = {PINS_JTAG0};
+static const unsigned pcie1_s73[] = {PINS_PCIE1};
+static const unsigned usb3h1_s73[] = {PINS_USB3H1};
+static const unsigned extint5_s73[] = {37};
+static const unsigned cfg_s73[] = {56, 57, 58, 59, 60, 61, 62, 63};
+static const unsigned uart0_s73[] = {8, 11, 12, 15};
+static const unsigned uart1_s73[] = {16, 17, 18, 19, 20, 21, 22, 23};
+static const unsigned uart2_s73[] = {24, 25, 26, 27, 28, 29, 30, 31};
+static const unsigned trace_s73[] = {44, 45, 46, 47, 48, 49, 50,
+					51, 52, 53, 54, 55, 56, 57,
+					58, 59, 60, 61};
+static const unsigned smt_s73[] = {56, 57, 58, 59, 60, 61};
+static const unsigned memcs_s73[] = {8, 9, 10, 11, 12, 13, 14, 15,
+					16, 17, 18, 19, 20, 21, 22,
+					23, 24, 25, 26, 27, 28, 29,
+					30, 31, 32, 33, 34, 35, 36,
+					37, 38, 39, 40, 41, 42, 43,
+					44, 45, 46, 47, 48, 49, 50,
+					51, 52, 53, 54, 55};
+static const unsigned pl244_s73[] = {8, 9, 10, 11, 12, 13, 14, 15,
+					16, 17, 18, 19, 20, 21, 22,
+					23, 24, 25, 26, 27, 28, 29,
+					30, 31};
+
+static const struct mb86s70_pmx_grp mb86s73_pgrps[] = {
+	{
+		.name = "extint5_grp",
+		.pins = extint5_s73,
+		.num_pins = ARRAY_SIZE(extint5_s73),
+	},
+	{
+		.name = "pcie0_grp",
+		.pins = pcie0_s73,
+		.num_pins = ARRAY_SIZE(pcie0_s73),
+	},
+	{
+		.name = "usb3h0_grp",
+		.pins = usb3h0_s73,
+		.num_pins = ARRAY_SIZE(usb3h0_s73),
+	},
+	{
+		.name = "usb2h0_grp",
+		.pins = usb2h0_s73,
+		.num_pins = ARRAY_SIZE(usb2h0_s73),
+	},
+	{
+		.name = "usb2d0_grp",
+		.pins = usb2d0_s73,
+		.num_pins = ARRAY_SIZE(usb2d0_s73),
+	},
+	{
+		.name = "sdh30_grp",
+		.pins = sdh30_s73,
+		.num_pins = ARRAY_SIZE(sdh30_s73),
+	},
+	{
+		.name = "emmc0_grp",
+		.pins = emmc0_s73,
+		.num_pins = ARRAY_SIZE(emmc0_s73),
+	},
+	{
+		.name = "hsspi0_grp",
+		.pins = hsspi0_s73,
+		.num_pins = ARRAY_SIZE(hsspi0_s73),
+	},
+	{
+		.name = "gmacd0_grp",
+		.pins = gmacd0_s73,
+		.num_pins = ARRAY_SIZE(gmacd0_s73),
+	},
+	{
+		.name = "gmacm0_grp",
+		.pins = gmacm0_s73,
+		.num_pins = ARRAY_SIZE(gmacm0_s73),
+	},
+	{
+		.name = "i2s0_grp",
+		.pins = i2s0_s73,
+		.num_pins = ARRAY_SIZE(i2s0_s73),
+	},
+	{
+		.name = "other0_grp",
+		.pins = other0_s73,
+		.num_pins = ARRAY_SIZE(other0_s73),
+	},
+	{
+		.name = "jtag0_grp",
+		.pins = jtag0_s73,
+		.num_pins = ARRAY_SIZE(jtag0_s73),
+	},
+	{
+		.name = "pcie1_grp",
+		.pins = pcie1_s73,
+		.num_pins = ARRAY_SIZE(pcie1_s73),
+	},
+	{
+		.name = "usb3h1_grp",
+		.pins = usb3h1_s73,
+		.num_pins = ARRAY_SIZE(usb3h1_s73),
+	},
+	{
+		.name = "cfg_grp",
+		.pins = cfg_s73,
+		.num_pins = ARRAY_SIZE(cfg_s73),
+	},
+	{
+		.name = "uart0_grp",
+		.pins = uart0_s73,
+		.num_pins = ARRAY_SIZE(uart0_s73),
+	},
+	{
+		.name = "uart1_grp",
+		.pins = uart1_s73,
+		.num_pins = ARRAY_SIZE(uart1_s73),
+	},
+	{
+		.name = "uart2_grp",
+		.pins = uart2_s73,
+		.num_pins = ARRAY_SIZE(uart2_s73),
+	},
+	{
+		.name = "trace_grp",
+		.pins = trace_s73,
+		.num_pins = ARRAY_SIZE(trace_s73),
+	},
+	{
+		.name = "smt_grp",
+		.pins = smt_s73,
+		.num_pins = ARRAY_SIZE(smt_s73),
+	},
+	{
+		.name = "memcs_grp",
+		.pins = memcs_s73,
+		.num_pins = ARRAY_SIZE(memcs_s73),
+	},
+	{
+		.name = "pl244_grp",
+		.pins = pl244_s73,
+		.num_pins = ARRAY_SIZE(pl244_s73),
+	},
+};
+
+static int func_count, grp_count;
+static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
+static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;
+
+static int mb86s70_pctrl_get_groups_count(struct pinctrl_dev *pctl)
+{
+	return grp_count;
+}
+
+static const char *
+mb86s70_pctrl_get_group_name(struct pinctrl_dev *pctl, unsigned selector)
+{
+	return mb86s7x_pgrps[selector].name;
+}
+
+static int
+mb86s70_pctrl_get_group_pins(struct pinctrl_dev *pctl, unsigned selector,
+			     const unsigned **pins, unsigned *num_pins)
+{
+	*pins = mb86s7x_pgrps[selector].pins;
+	*num_pins = mb86s7x_pgrps[selector].num_pins;
+
+	return 0;
+}
+
+static int
+mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
+			     struct device_node *node,
+			     struct pinctrl_map **map, unsigned *num_maps)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	int ret, i, configlen = 0;
+	unsigned long *pinconfig;
+	const char *function;
+	const char *group;
+	u32 val;
+
+	*map = NULL;
+	*num_maps = 0;
+
+	ret = of_property_read_string(node, "mb86s7x,function", &function);
+	if (ret) {
+		dev_err(pchip->dev,
+			"missing mb86s7x,function property in node %s\n",
+			node->name);
+		return -EINVAL;
+	}
+
+	*map = devm_kzalloc(pchip->dev,
+			    2 * sizeof(struct pinctrl_map), GFP_KERNEL);
+	if (!*map)
+		return -ENOMEM;
+
+	for (i = 0; i < func_count; i++)
+		if (!strcmp(mb86s7x_pmx_funcs[i].name, function))
+			break;
+
+	if (i == func_count) {
+		dev_err(pchip->dev, "function(%s) not found!\n", function);
+		return -EINVAL;
+	}
+	group = mb86s7x_pmx_funcs[i].groups[0];
+
+	(*map)[0].type = PIN_MAP_TYPE_MUX_GROUP;
+	(*map)[0].data.mux.group = group;
+	(*map)[0].data.mux.function = function;
+	*num_maps = 1;
+
+	if (of_find_property(node, "mb86s7x,drvst", NULL))
+		configlen++;
+	if (of_find_property(node, "mb86s7x,pullup", NULL))
+		configlen++;
+
+	if (configlen == 0) /* For S70 */
+		return 0;
+
+	i = 0;
+	pinconfig = devm_kzalloc(pchip->dev,
+				 configlen * sizeof(*pinconfig), GFP_KERNEL);
+
+	if (!of_property_read_u32(node, "mb86s7x,drvst", &val))
+		pinconfig[i++] =
+			pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
+						 val);
+
+	if (!of_property_read_u32(node, "mb86s7x,pullup", &val)) {
+		enum pin_config_param pull;
+
+		if (val)
+			pull = PIN_CONFIG_BIAS_PULL_UP;
+		else
+			pull = PIN_CONFIG_BIAS_PULL_DOWN;
+		pinconfig[i++] = pinconf_to_config_packed(pull, 0);
+	}
+
+	(*map)[1].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	(*map)[1].data.configs.group_or_pin = group;
+	(*map)[1].data.configs.configs = pinconfig;
+	(*map)[1].data.configs.num_configs = configlen;
+
+	*num_maps = 2;
+
+	return 0;
+}
+
+static void
+mb86s70_pctrl_dt_free_map(struct pinctrl_dev *pctl,
+			  struct pinctrl_map *map, unsigned num_maps)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	int i;
+
+	for (i = 0; i < num_maps; i++)
+		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+			devm_kfree(pchip->dev, map[i].data.configs.configs);
+
+	devm_kfree(pchip->dev, map);
+}
+
+static const struct pinctrl_ops mb86s70_pctrl_ops = {
+	.get_groups_count	= mb86s70_pctrl_get_groups_count,
+	.get_group_name		= mb86s70_pctrl_get_group_name,
+	.get_group_pins		= mb86s70_pctrl_get_group_pins,
+	.dt_node_to_map		= mb86s70_pctrl_dt_node_to_map,
+	.dt_free_map		= mb86s70_pctrl_dt_free_map,
+};
+
+static const char * const pcie0_groups[] = {"pcie0_grp"};
+static const char * const usb3h0_groups[] = {"usb3h0_grp"};
+static const char * const usb2h0_groups[] = {"usb2h0_grp"};
+static const char * const usb2d0_groups[] = {"usb2d0_grp"};
+static const char * const sdh30_groups[] = {"sdh30_grp"};
+static const char * const emmc0_groups[] = {"emmc0_grp"};
+static const char * const hsspi0_groups[] = {"hsspi0_grp"};
+static const char * const gmacd0_groups[] = {"gmacd0_grp"};
+static const char * const gmacm0_groups[] = {"gmacm0_grp"};
+static const char * const i2s0_groups[] = {"i2s0_grp"};
+static const char * const other0_groups[] = {"other0_grp"};
+static const char * const jtag0_groups[] = {"jtag0_grp"};
+static const char * const pcie1_groups[] = {"pcie1_grp"};
+static const char * const usb3h1_groups[] = {"usb3h1_grp"};
+static const char * const extint16_groups[] = {"extint16_grp"};
+static const char * const extint5_groups[] = {"extint5_grp"};
+static const char * const tsif0_groups[] = {"tsif0_grp"};
+static const char * const tsif1_groups[] = {"tsif1_grp"};
+static const char * const cfg_groups[] = {"cfg_grp"};
+static const char * const uart0_groups[] = {"uart0_grp"};
+static const char * const uart1_groups[] = {"uart1_grp"};
+static const char * const uart2_groups[] = {"uart2_grp"};
+static const char * const pl244_groups[] = {"pl244_grp"};
+static const char * const trace_groups[] = {"trace_grp"};
+static const char * const memcs_groups[] = {"memcs_grp"};
+static const char * const cap_groups[] = {"cap_grp"};
+static const char * const smt_groups[] = {"smt_grp"};
+
+static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
+	{
+		.prog_val = 0x1,
+		.name = "extint5",
+		.groups = extint5_groups,
+		.num_groups = ARRAY_SIZE(extint5_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "pcie0",
+		.groups = pcie0_groups,
+		.num_groups = ARRAY_SIZE(pcie0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb3h0",
+		.groups = usb3h0_groups,
+		.num_groups = ARRAY_SIZE(usb3h0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb2h0",
+		.groups = usb2h0_groups,
+		.num_groups = ARRAY_SIZE(usb2h0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb2d0",
+		.groups = usb2d0_groups,
+		.num_groups = ARRAY_SIZE(usb2d0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "sdh30",
+		.groups = sdh30_groups,
+		.num_groups = ARRAY_SIZE(sdh30_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "emmc0",
+		.groups = emmc0_groups,
+		.num_groups = ARRAY_SIZE(emmc0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "hsspi0",
+		.groups = hsspi0_groups,
+		.num_groups = ARRAY_SIZE(hsspi0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "gmacd0",
+		.groups = gmacd0_groups,
+		.num_groups = ARRAY_SIZE(gmacd0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "gmacm0",
+		.groups = gmacm0_groups,
+		.num_groups = ARRAY_SIZE(gmacm0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "i2s0",
+		.groups = i2s0_groups,
+		.num_groups = ARRAY_SIZE(i2s0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "other0",
+		.groups = other0_groups,
+		.num_groups = ARRAY_SIZE(other0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "jtag0",
+		.groups = jtag0_groups,
+		.num_groups = ARRAY_SIZE(jtag0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "pcie1",
+		.groups = pcie1_groups,
+		.num_groups = ARRAY_SIZE(pcie1_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb3h1",
+		.groups = usb3h1_groups,
+		.num_groups = ARRAY_SIZE(usb3h1_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "cfg",
+		.groups = cfg_groups,
+		.num_groups = ARRAY_SIZE(cfg_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart0",
+		.groups = uart0_groups,
+		.num_groups = ARRAY_SIZE(uart0_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart1",
+		.groups = uart1_groups,
+		.num_groups = ARRAY_SIZE(uart1_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart2",
+		.groups = uart2_groups,
+		.num_groups = ARRAY_SIZE(uart2_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "trace",
+		.groups = trace_groups,
+		.num_groups = ARRAY_SIZE(trace_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "pl244",
+		.groups = pl244_groups,
+		.num_groups = ARRAY_SIZE(pl244_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "smt",
+		.groups = smt_groups,
+		.num_groups = ARRAY_SIZE(smt_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "memcs",
+		.groups = memcs_groups,
+		.num_groups = ARRAY_SIZE(memcs_groups),
+	},
+};
+
+static const struct mb86s70_pmx_function mb86s70_pmx_funcs[] = {
+	{
+		.prog_val = 0x2,
+		.name = "extint16",
+		.groups = extint16_groups,
+		.num_groups = ARRAY_SIZE(extint16_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "tsif0",
+		.groups = tsif0_groups,
+		.num_groups = ARRAY_SIZE(tsif0_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "tsif1",
+		.groups = tsif1_groups,
+		.num_groups = ARRAY_SIZE(tsif1_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "uart1",
+		.groups = uart1_groups,
+		.num_groups = ARRAY_SIZE(uart1_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "uart2",
+		.groups = uart2_groups,
+		.num_groups = ARRAY_SIZE(uart2_groups),
+	},
+	{
+		.prog_val = 0x5,
+		.name = "pl244",
+		.groups = pl244_groups,
+		.num_groups = ARRAY_SIZE(pl244_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "trace",
+		.groups = trace_groups,
+		.num_groups = ARRAY_SIZE(trace_groups),
+	},
+	{
+		.prog_val = 0x4,
+		.name = "memcs",
+		.groups = memcs_groups,
+		.num_groups = ARRAY_SIZE(memcs_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "capture",
+		.groups = cap_groups,
+		.num_groups = ARRAY_SIZE(cap_groups),
+	}
+};
+
+static int
+mb86s70_pmx_get_functions_count(struct pinctrl_dev *pctl)
+{
+	return func_count;
+}
+
+static const char *
+mb86s70_pmx_get_function_name(struct pinctrl_dev *pctl, unsigned selector)
+{
+	return mb86s7x_pmx_funcs[selector].name;
+}
+
+static int
+mb86s70_pmx_get_function_groups(struct pinctrl_dev *pctl,
+				unsigned selector, const char * const **groups,
+				unsigned * const num_groups)
+{
+	*groups = mb86s7x_pmx_funcs[selector].groups;
+	*num_groups = mb86s7x_pmx_funcs[selector].num_groups;
+
+	return 0;
+}
+
+static int
+mb86s70_pmx_enable(struct pinctrl_dev *pctl,
+		   unsigned func_selector, unsigned group_selector)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pins;
+	int i, j;
+
+	if (group_selector >= grp_count) {
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	j = mb86s7x_pgrps[group_selector].num_pins;
+	pins = mb86s7x_pgrps[group_selector].pins;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+
+	/* Busy if any pin in the same 'bunch' is taken */
+	for (i = 0; i < j; i++) {
+		u32 val;
+		int p = pins[i] / 4 * 4;
+
+		if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
+			continue;
+
+		val = readl(pchip->base + p);
+		/* skip if no change needed */
+		if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
+			continue;
+
+		if (pchip->pin_busy[p]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p);
+			goto busy_exit;
+		}
+
+		if (pchip->pin_busy[p + 1]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+1);
+			goto busy_exit;
+		}
+
+		if (p == 64)
+			continue;
+
+		if (pchip->pin_busy[p + 2]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+2);
+			goto busy_exit;
+		}
+
+		if (pchip->pin_busy[p + 3]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+3);
+			goto busy_exit;
+		}
+
+		continue;
+busy_exit:
+		spin_unlock_irqrestore(&pchip->lock, flags);
+		pr_err("%s:%d Take a look!\n", __func__, __LINE__);
+		return -EBUSY;
+	}
+
+	pr_debug("Going to enable %s on pins -",
+		 mb86s7x_pmx_funcs[func_selector].name);
+	for (i = 0; i < j; i++) {
+		int p = pins[i];
+
+		pr_debug(" %d", p);
+		pchip->pin_busy[p] = true;
+		if (p < PINS_VIRT) /* Not for virtual pins */
+			writel(mb86s7x_pmx_funcs[func_selector].prog_val,
+			       pchip->base + p / 4 * 4);
+	}
+
+	spin_unlock_irqrestore(&pchip->lock, flags);
+	pr_debug("\n");
+
+	return 0;
+}
+
+static void
+mb86s70_pmx_disable(struct pinctrl_dev *pctl,
+		    unsigned func_selector, unsigned group_selector)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pins;
+	int i, j;
+
+	if (group_selector >= grp_count) {
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return;
+	}
+
+	j = mb86s7x_pgrps[group_selector].num_pins;
+	pins = mb86s7x_pgrps[group_selector].pins;
+
+	pr_debug("Going to disable %s on pins -",
+		 mb86s7x_pmx_funcs[func_selector].name);
+	spin_lock_irqsave(&pchip->lock, flags);
+	for (i = 0; i < j; i++) {
+		pr_debug(" %d", pins[i]);
+		pchip->pin_busy[pins[i]] = false;
+	}
+	spin_unlock_irqrestore(&pchip->lock, flags);
+	pr_debug("\n");
+}
+
+static int
+mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
+			       struct pinctrl_gpio_range *range,
+			       unsigned pin, bool input)
+{
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 off, bit, val;
+
+	if (pin >= 64)
+		return -EINVAL;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - gc->base) % 8;
+	off = (pin - gc->base) / 8 * 4;
+	val = readl(gchip->base + DDR(off));
+	if (input)
+		val &= ~(1 << bit);
+	else
+		val |= (1 << bit);
+	writel(val, gchip->base + DDR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int
+mb86s70_pmx_gpio_request_enable(struct pinctrl_dev *pctl,
+				struct pinctrl_gpio_range *range, unsigned pin)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						 struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	int p = pin / 4 * 4;
+	u32 val, off, bit;
+
+	if (p == 64)
+		return -ENODEV;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+	if (pchip->pin_busy[p] || pchip->pin_busy[p + 1] ||
+			pchip->pin_busy[p + 2] || pchip->pin_busy[p + 3]) {
+		val = readl(pchip->base + p);
+		if (val != 0x1) {
+			spin_unlock_irqrestore(&pchip->lock, flags);
+			return -EBUSY;
+		}
+	}
+
+	pchip->pin_busy[pin] = true;
+	writel(0x1, pchip->base + p);
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - range->pin_base) % 8;
+	off = (pin - range->pin_base) / 8 * 4;
+	val = readl(gchip->base + PFR(off));
+	val &= ~(1 << bit);
+	writel(val, gchip->base + PFR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static void
+mb86s70_pmx_gpio_disable_free(struct pinctrl_dev *pctl,
+			      struct pinctrl_gpio_range *range, unsigned pin)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 off, bit, val;
+
+	if (pin >= 64)
+		return;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+	pchip->pin_busy[pin] = false;
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - range->pin_base) % 8;
+	off = (pin - range->pin_base) / 8 * 4;
+	val = readl(gchip->base + PFR(off));
+	val |= (1 << bit);
+	writel(val, gchip->base + PFR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static const struct pinmux_ops mb86s70_pmx_ops = {
+	.get_functions_count	= mb86s70_pmx_get_functions_count,
+	.get_function_name	= mb86s70_pmx_get_function_name,
+	.get_function_groups	= mb86s70_pmx_get_function_groups,
+	.enable			= mb86s70_pmx_enable,
+	.disable		= mb86s70_pmx_disable,
+	.gpio_set_direction	= mb86s70_pmx_gpio_set_direction,
+	.gpio_request_enable	= mb86s70_pmx_gpio_request_enable,
+	.gpio_disable_free	= mb86s70_pmx_gpio_disable_free,
+};
+
+static int
+mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
+			     unsigned long *config)
+{
+	return -EINVAL;
+}
+
+static int
+mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
+			     unsigned long *configs, unsigned num_configs)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pin;
+	int i, j, p;
+	u32 val, ds;
+
+	p = mb86s7x_pgrps[group].num_pins;
+	pin = mb86s7x_pgrps[group].pins;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+
+	for (i = 0; i < num_configs; i++) {
+		switch (pinconf_to_config_param(configs[i])) {
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			/* Drive Strength should be 2, 4, 6 or 8 mA */
+			ds = pinconf_to_config_argument(configs[i]);
+			ds = (ds - 2) / 2;
+			for (j = 0; j < p; j++) {
+				/* Clear the Hi-z */
+				val = readl_relaxed(pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val = readl_relaxed(pchip->conf_base +
+							CDRV_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (ds << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							CDRV_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (0x1 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							PULL_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							PULL_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							PULL_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (1 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							PULL_PDG(pin[j]));
+			}
+			break;
+		default:
+			pr_err("%s:%d!!\n", __func__, __LINE__);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	return 0;
+}
+
+static const struct pinconf_ops mb86s70_conf_ops = {
+	.pin_config_group_get = mb86s70_pin_config_group_get,
+	.pin_config_group_set = mb86s70_pin_config_group_set,
+};
+
+static const struct of_device_id mb86s70_pinmux_dt_ids[] = {
+	{.compatible = "fujitsu,mb86s70-pinctrl", .data = (void *)PMUX_MB86S70},
+	{.compatible = "fujitsu,mb86s73-pinctrl", .data = (void *)PMUX_MB86S73},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mb86s70_pinmux_dt_ids);
+
+static int mb86s70_pinmux_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *device;
+	struct mb86s70_pmux_chip *pchip;
+	struct resource *res;
+	int i, type;
+
+	device = of_match_device(mb86s70_pinmux_dt_ids, &pdev->dev);
+	if (!device)
+		return -ENODEV;
+
+	type = (int)device->data;
+
+	/* reserve memory space for pmux chip */
+	pchip = devm_kzalloc(&pdev->dev, sizeof(*pchip), GFP_KERNEL);
+	if (pchip == NULL) {
+		dev_err(&pdev->dev, "can't allocate pinmux chip data.\n");
+		return -ENOMEM;
+	}
+	pchip->dev = &pdev->dev;
+
+	/* initiate pmux chip structure */
+	platform_set_drvdata(pdev, pchip);
+
+	/* IO resource */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pchip->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pchip->base))
+		return PTR_ERR(pchip->base);
+
+	/* initiate lock */
+	spin_lock_init(&pchip->lock);
+
+	pchip->desc.name = dev_name(&pdev->dev);
+	pchip->desc.npins = MB86S70_PMUX_PINS;
+	pchip->desc.pins = pchip->pins;
+	pchip->desc.pctlops = &mb86s70_pctrl_ops;
+	pchip->desc.pmxops = &mb86s70_pmx_ops;
+	pchip->desc.owner = THIS_MODULE;
+	if (type == PMUX_MB86S73) {
+		pchip->desc.confops = &mb86s70_conf_ops;
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(pchip->conf_base))
+			return PTR_ERR(pchip->conf_base);
+		mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
+		func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
+		mb86s7x_pgrps = mb86s73_pgrps;
+		grp_count = ARRAY_SIZE(mb86s73_pgrps);
+	} else {
+		mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
+		func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
+		mb86s7x_pgrps = mb86s70_pgrps;
+		grp_count = ARRAY_SIZE(mb86s70_pgrps);
+	}
+
+	for (i = 0; i < MB86S70_PMUX_PINS; i++) {
+		pchip->pin_busy[i] = false;
+		pchip->pins[i].number = i;
+		pchip->pins[i].name = &pchip->pin_names[5 * i];
+		snprintf(&pchip->pin_names[5 * i], 5, "PD%d", i);
+	}
+
+	pchip->pctl = pinctrl_register(&pchip->desc, &pdev->dev, pchip);
+	if (!pchip->pctl) {
+		dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id mb86s70_gpio_dt_ids[] = {
+	{ .compatible = "fujitsu,mb86s7x-gpio" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
+
+static struct platform_driver mb86s70_gpio_driver = {
+	.probe     = mb86s70_gpio_probe,
+	.driver    = {
+		.name  = "mb86s70-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = mb86s70_gpio_dt_ids,
+	},
+};
+
+static struct platform_driver mb86s70_pinmux_driver = {
+	.probe     = mb86s70_pinmux_probe,
+	.driver    = {
+		.name  = "mb86s70-pinmux",
+		.owner = THIS_MODULE,
+		.of_match_table = mb86s70_pinmux_dt_ids,
+	},
+};
+
+static int __init mb86s70_pins_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mb86s70_pinmux_driver);
+	if (ret)
+		return ret;
+
+	return platform_driver_register(&mb86s70_gpio_driver);
+}
+subsys_initcall(mb86s70_pins_init);
+
+MODULE_DESCRIPTION("MB86S7x PinCtrl Driver");
+MODULE_ALIAS("platform:mb86s70-pinmux");
+MODULE_ALIAS("platform:mb86s70-gpio");
+MODULE_LICENSE("GPL");
-- 
1.8.1.2

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-07-13  6:31   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:31 UTC (permalink / raw)
  To: linux-arm-kernel

The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
GPIOs are supported on top of Pinctrl api.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
Cc: Linus Walleij <linus.walleij@linaro.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../bindings/gpio/fujitsu,mb86s7x-gpio.txt         |   22 +
 .../bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt   |   30 +
 drivers/pinctrl/Kconfig                            |    5 +
 drivers/pinctrl/Makefile                           |    1 +
 drivers/pinctrl/pinctrl-mb86s7x.c                  | 1281 ++++++++++++++++++++
 5 files changed, 1339 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
 create mode 100644 Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
 create mode 100644 drivers/pinctrl/pinctrl-mb86s7x.c

diff --git a/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
new file mode 100644
index 0000000..c262efa
--- /dev/null
+++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
@@ -0,0 +1,22 @@
+Fujitsu MB86S7x GPIO Controller
+-------------------------------
+
+Required properties:
+- compatible: Should be "fujitsu,mb86s7x-gpio"
+- gpio-controller: Marks the device node as a gpio controller.
+- #gpio-cells: Should be <2>. The first cell is the pin number and the
+  second cell is used to specify optional parameters:
+   - bit 0 specifies polarity (0 for normal, 1 for inverted).
+
+GPIO ranges are specified as described in
+Documentation/devicetree/bindings/gpio/gpio.txt
+
+Examples:
+	gpio0: mb86s70_gpio0 {
+		compatible = "fujitsu,mb86s7x-gpio";
+		reg = <0 0x31000000 0x10000>;
+		gpio-controller;
+		#gpio-cells = <2>;
+		gpio-ranges = <&pinctrl 0 0 32>;
+		clocks = <&clk_alw_2_1>;
+	};
diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
new file mode 100644
index 0000000..ce2011b
--- /dev/null
+++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
@@ -0,0 +1,30 @@
+Fujitsu MB86S7x Pin Controller
+------------------------------
+
+Required properties:
+- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
+- reg: Should contain the register physical address and length for the
+  pin controller.
+- #gpio-range-cells: Should be 3
+
+Optional subnode-properties:
+- mb86s7x,function: String specifying the function name.
+- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
+    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
+- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up
+
+Examples:
+	pinctrl: pinctrl at 2a4d0000 {
+		compatible = "fujitsu,mb86s73-pinctrl";
+		reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
+		#gpio-range-cells = <3>;
+
+		hsspi0_pins_active: hsspi0_active {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <8>; /* in mA */
+		};
+		hsspi0_pins_sleep: hsspi0_sleep {
+			mb86s7x,function = "hsspi0";
+			mb86s7x,drvst = <0>; /* Implies Hi-z */
+		};
+	};
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 0042ccb..1f053fc 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -213,6 +213,11 @@ config PINCTRL_FALCON
 	depends on SOC_FALCON
 	depends on PINCTRL_LANTIQ
 
+config PINCTRL_MB86S7X
+	bool
+	select PINMUX
+	select GENERIC_PINCONF
+
 config PINCTRL_MXS
 	bool
 	select PINMUX
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index c4b5d40..edcb823 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -34,6 +34,7 @@ obj-$(CONFIG_PINCTRL_IMX6Q)	+= pinctrl-imx6dl.o
 obj-$(CONFIG_PINCTRL_IMX6SL)	+= pinctrl-imx6sl.o
 obj-$(CONFIG_PINCTRL_IMX6SX)	+= pinctrl-imx6sx.o
 obj-$(CONFIG_PINCTRL_FALCON)	+= pinctrl-falcon.o
+obj-$(CONFIG_PINCTRL_MB86S7X)	+= pinctrl-mb86s7x.o
 obj-$(CONFIG_PINCTRL_MXS)	+= pinctrl-mxs.o
 obj-$(CONFIG_PINCTRL_IMX23)	+= pinctrl-imx23.o
 obj-$(CONFIG_PINCTRL_IMX25)	+= pinctrl-imx25.o
diff --git a/drivers/pinctrl/pinctrl-mb86s7x.c b/drivers/pinctrl/pinctrl-mb86s7x.c
new file mode 100644
index 0000000..74c72ec8
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-mb86s7x.c
@@ -0,0 +1,1281 @@
+/*
+ *  linux/drivers/pinctrl/pinctrl-mb86s7x.c
+ *
+ *  Copyright (C) 2014 Fujitsu Semiconductor Limited
+ *
+ *  This program is free software: you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation, version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ *  GNU General Public License for more details.
+ */
+
+#include <linux/io.h>
+#include <linux/init.h>
+#include <linux/clk.h>
+#include <linux/gpio.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinctrl.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+#include <linux/spinlock.h>
+#include <linux/slab.h>
+
+#define PDR(x)	(0x0 + x)
+#define DDR(x)	(0x10 + x)
+#define PFR(x)	(0x20 + x)
+
+#define CDRV_PDG(x)	(0x0 + ((x) / 16) * 4)
+#define FORCEZ_PDG(x)	(0x100 + ((x) / 16) * 4)
+#define PULL_PDG(x)	(0x200 + ((x) / 16) * 4)
+#define PIN_OFF(x)	(((x) % 16) * 2)
+
+#define PMUX_MB86S70	0
+#define PMUX_MB86S73	1
+
+/* Virtual pin numbers start so that reg offset calculations get simple */
+enum {
+	PINS_VIRT = 128,
+	PINS_PCIE0 = PINS_VIRT,
+	PINS_HOLE1,
+	PINS_USB3H0,
+	PINS_HOLE2,
+	PINS_USB2H0,
+	PINS_USB2D0,
+	PINS_SDH30,
+	PINS_HOLE3,
+	PINS_EMMC0,
+	PINS_HSSPI0,
+	PINS_GMACD0,
+	PINS_GMACM0,
+	PINS_I2S0,
+	PINS_UART0,
+	PINS_OTHER0,
+	PINS_JTAG0,
+	PINS_PCIE1,
+	PINS_HOLE4,
+	PINS_USB3H1,
+	MB86S70_PMUX_PINS,
+};
+
+struct mb86s70_gpio_chip {
+	spinlock_t lock; /* for goio regs */
+	struct clk *clk;
+	void __iomem *base;
+	struct gpio_chip gc;
+};
+
+static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
+{
+	int ret = pinctrl_request_gpio(gc->base + offset);
+
+	if (!ret) {
+		struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+		unsigned long flags;
+		u32 val;
+
+		spin_lock_irqsave(&gchip->lock, flags);
+		val = readl(gchip->base + PFR(offset / 8 * 4));
+		val &= ~(1 << (offset % 8)); /* as gpio-port */
+		writel(val, gchip->base + PFR(offset / 8 * 4));
+		spin_unlock_irqrestore(&gchip->lock, flags);
+	}
+
+	return ret;
+}
+
+static void mb86s70_gpio_free(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+					struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	val = readl(gchip->base + PFR(offset / 8 * 4));
+	val |= (1 << (offset % 8)); /* as peri-port */
+	writel(val, gchip->base + PFR(offset / 8 * 4));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	pinctrl_free_gpio(gc->base + offset);
+}
+
+static int mb86s70_gpio_direction_input(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	val = readl(gchip->base + DDR(offset / 8 * 4));
+	val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + DDR(offset / 8 * 4));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
+					 unsigned offset, int value)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	if (value)
+		val |= (1 << (offset % 8));
+	else
+		val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + PDR(offset / 8 * 4));
+
+	val = readl(gchip->base + DDR(offset / 8 * 4));
+	val |= (1 << (offset % 8));
+	writel(val, gchip->base + DDR(offset / 8 * 4));
+
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned char val;
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	val &= (1 << (offset % 8));
+	return val ? 1 : 0;
+}
+
+static void mb86s70_gpio_set(struct gpio_chip *gc, unsigned offset, int value)
+{
+	struct mb86s70_gpio_chip *gchip =
+			container_of(gc, struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	unsigned char val;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+
+	val = readl(gchip->base + PDR(offset / 8 * 4));
+	if (value)
+		val |= (1 << (offset % 8));
+	else
+		val &= ~(1 << (offset % 8));
+	writel(val, gchip->base + PDR(offset / 8 * 4));
+
+	spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static int mb86s70_gpio_probe(struct platform_device *pdev)
+{
+	struct mb86s70_gpio_chip *gchip;
+	struct resource *res;
+	int ret;
+
+	gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
+	if (gchip == NULL)
+		return -ENOMEM;
+
+	platform_set_drvdata(pdev, gchip);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	gchip->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(gchip->base))
+		return PTR_ERR(gchip->base);
+
+	gchip->clk = devm_clk_get(&pdev->dev, NULL);
+	if (IS_ERR(gchip->clk))
+		return PTR_ERR(gchip->clk);
+
+	clk_prepare_enable(gchip->clk);
+
+	spin_lock_init(&gchip->lock);
+
+	gchip->gc.direction_output = mb86s70_gpio_direction_output;
+	gchip->gc.direction_input = mb86s70_gpio_direction_input;
+	gchip->gc.request = mb86s70_gpio_request;
+	gchip->gc.free = mb86s70_gpio_free;
+	gchip->gc.get = mb86s70_gpio_get;
+	gchip->gc.set = mb86s70_gpio_set;
+	gchip->gc.label = dev_name(&pdev->dev);
+	gchip->gc.ngpio = 32;
+	gchip->gc.owner = THIS_MODULE;
+	gchip->gc.dev = &pdev->dev;
+	gchip->gc.base = -1;
+
+	ret = gpiochip_add(&gchip->gc);
+	if (ret) {
+		dev_err(&pdev->dev, "couldn't register gpio driver\n");
+		clk_disable_unprepare(gchip->clk);
+	}
+
+	return ret;
+}
+
+struct mb86s70_pmux_chip {
+	spinlock_t lock; /* mux regs */
+	struct device *dev;
+	void __iomem *base;
+	void __iomem *conf_base;
+	struct pinctrl_dev *pctl;
+	struct pinctrl_desc desc;
+	bool pin_busy[MB86S70_PMUX_PINS];
+	char pin_names[5 * MB86S70_PMUX_PINS];
+	struct pinctrl_pin_desc pins[MB86S70_PMUX_PINS];
+};
+
+struct mb86s70_pmx_grp {
+	const char *name;
+	const unsigned *pins;
+	const unsigned num_pins;
+};
+
+struct mb86s70_pmx_function {
+	const unsigned num_groups;
+	unsigned prog_val;
+	const char *name;
+
+	const char *const *groups;
+};
+
+static const unsigned extint16_s70[] = {0};
+static const unsigned tsif0_s70[] = {16, 17, 18, 19};
+static const unsigned tsif1_s70[] = {16, 17, 18, 19};
+static const unsigned uart1_s70[] = {56, 57, 58, 59};
+static const unsigned uart2_s70[] = {60, 61, 62, 63};
+static const unsigned pl244_s70[] = {40, 41, 42, 43, 44, 45, 46, 47, 48,
+					49, 50, 51, 52, 53, 54, 55, 56, 57,
+					58, 59, 60, 61, 62};
+static const unsigned trace_s70[] = {24, 25, 26, 27, 28, 29, 30, 31, 32,
+					33, 34, 35, 36, 37, 38, 39, 64, 65};
+static const unsigned memcs_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24,
+					25, 26, 27, 28, 29, 30, 31, 32, 33,
+					34, 35, 36, 37, 38, 39, 40, 41, 42,
+					43, 44, 45, 46, 47, 48, 49, 50, 51,
+					52, 53, 54, 55, 56, 57, 58, 59, 60,
+					61, 62, 63, 64, 65};
+static const unsigned cap_s70[] = {16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+					26, 27, 28, 29, 30, 31, 32, 33, 34,
+					35, 36, 37, 38, 39, 40, 41, 42, 43,
+					44, 45, 46, 47};
+
+static const struct mb86s70_pmx_grp mb86s70_pgrps[] = {
+	{
+		.name = "extint16_grp",
+		.pins = extint16_s70,
+		.num_pins = ARRAY_SIZE(extint16_s70),
+	},
+	{
+		.name = "tsif0_grp",
+		.pins = tsif0_s70,
+		.num_pins = ARRAY_SIZE(tsif0_s70),
+	},
+	{
+		.name = "tsif1_grp",
+		.pins = tsif1_s70,
+		.num_pins = ARRAY_SIZE(tsif1_s70),
+	},
+	{
+		.name = "uart1_grp",
+		.pins = uart1_s70,
+		.num_pins = ARRAY_SIZE(uart1_s70),
+	},
+	{
+		.name = "uart2_grp",
+		.pins = uart2_s70,
+		.num_pins = ARRAY_SIZE(uart2_s70),
+	},
+	{
+		.name = "pl244_grp",
+		.pins = pl244_s70,
+		.num_pins = ARRAY_SIZE(pl244_s70),
+	},
+	{
+		.name = "trace_grp",
+		.pins = trace_s70,
+		.num_pins = ARRAY_SIZE(trace_s70),
+	},
+	{
+		.name = "memcs_grp",
+		.pins = memcs_s70,
+		.num_pins = ARRAY_SIZE(memcs_s70),
+	},
+	{
+		.name = "cap_grp",
+		.pins = cap_s70,
+		.num_pins = ARRAY_SIZE(cap_s70),
+	}
+};
+
+static const unsigned pcie0_s73[] = {PINS_PCIE0};
+static const unsigned usb3h0_s73[] = {PINS_USB3H0};
+static const unsigned usb2h0_s73[] = {PINS_USB2H0};
+static const unsigned usb2d0_s73[] = {PINS_USB2D0};
+static const unsigned sdh30_s73[] = {PINS_SDH30};
+static const unsigned emmc0_s73[] = {PINS_EMMC0};
+static const unsigned hsspi0_s73[] = {PINS_HSSPI0};
+static const unsigned gmacd0_s73[] = {PINS_GMACD0};
+static const unsigned gmacm0_s73[] = {PINS_GMACM0};
+static const unsigned i2s0_s73[] = {PINS_I2S0};
+static const unsigned other0_s73[] = {PINS_OTHER0};
+static const unsigned jtag0_s73[] = {PINS_JTAG0};
+static const unsigned pcie1_s73[] = {PINS_PCIE1};
+static const unsigned usb3h1_s73[] = {PINS_USB3H1};
+static const unsigned extint5_s73[] = {37};
+static const unsigned cfg_s73[] = {56, 57, 58, 59, 60, 61, 62, 63};
+static const unsigned uart0_s73[] = {8, 11, 12, 15};
+static const unsigned uart1_s73[] = {16, 17, 18, 19, 20, 21, 22, 23};
+static const unsigned uart2_s73[] = {24, 25, 26, 27, 28, 29, 30, 31};
+static const unsigned trace_s73[] = {44, 45, 46, 47, 48, 49, 50,
+					51, 52, 53, 54, 55, 56, 57,
+					58, 59, 60, 61};
+static const unsigned smt_s73[] = {56, 57, 58, 59, 60, 61};
+static const unsigned memcs_s73[] = {8, 9, 10, 11, 12, 13, 14, 15,
+					16, 17, 18, 19, 20, 21, 22,
+					23, 24, 25, 26, 27, 28, 29,
+					30, 31, 32, 33, 34, 35, 36,
+					37, 38, 39, 40, 41, 42, 43,
+					44, 45, 46, 47, 48, 49, 50,
+					51, 52, 53, 54, 55};
+static const unsigned pl244_s73[] = {8, 9, 10, 11, 12, 13, 14, 15,
+					16, 17, 18, 19, 20, 21, 22,
+					23, 24, 25, 26, 27, 28, 29,
+					30, 31};
+
+static const struct mb86s70_pmx_grp mb86s73_pgrps[] = {
+	{
+		.name = "extint5_grp",
+		.pins = extint5_s73,
+		.num_pins = ARRAY_SIZE(extint5_s73),
+	},
+	{
+		.name = "pcie0_grp",
+		.pins = pcie0_s73,
+		.num_pins = ARRAY_SIZE(pcie0_s73),
+	},
+	{
+		.name = "usb3h0_grp",
+		.pins = usb3h0_s73,
+		.num_pins = ARRAY_SIZE(usb3h0_s73),
+	},
+	{
+		.name = "usb2h0_grp",
+		.pins = usb2h0_s73,
+		.num_pins = ARRAY_SIZE(usb2h0_s73),
+	},
+	{
+		.name = "usb2d0_grp",
+		.pins = usb2d0_s73,
+		.num_pins = ARRAY_SIZE(usb2d0_s73),
+	},
+	{
+		.name = "sdh30_grp",
+		.pins = sdh30_s73,
+		.num_pins = ARRAY_SIZE(sdh30_s73),
+	},
+	{
+		.name = "emmc0_grp",
+		.pins = emmc0_s73,
+		.num_pins = ARRAY_SIZE(emmc0_s73),
+	},
+	{
+		.name = "hsspi0_grp",
+		.pins = hsspi0_s73,
+		.num_pins = ARRAY_SIZE(hsspi0_s73),
+	},
+	{
+		.name = "gmacd0_grp",
+		.pins = gmacd0_s73,
+		.num_pins = ARRAY_SIZE(gmacd0_s73),
+	},
+	{
+		.name = "gmacm0_grp",
+		.pins = gmacm0_s73,
+		.num_pins = ARRAY_SIZE(gmacm0_s73),
+	},
+	{
+		.name = "i2s0_grp",
+		.pins = i2s0_s73,
+		.num_pins = ARRAY_SIZE(i2s0_s73),
+	},
+	{
+		.name = "other0_grp",
+		.pins = other0_s73,
+		.num_pins = ARRAY_SIZE(other0_s73),
+	},
+	{
+		.name = "jtag0_grp",
+		.pins = jtag0_s73,
+		.num_pins = ARRAY_SIZE(jtag0_s73),
+	},
+	{
+		.name = "pcie1_grp",
+		.pins = pcie1_s73,
+		.num_pins = ARRAY_SIZE(pcie1_s73),
+	},
+	{
+		.name = "usb3h1_grp",
+		.pins = usb3h1_s73,
+		.num_pins = ARRAY_SIZE(usb3h1_s73),
+	},
+	{
+		.name = "cfg_grp",
+		.pins = cfg_s73,
+		.num_pins = ARRAY_SIZE(cfg_s73),
+	},
+	{
+		.name = "uart0_grp",
+		.pins = uart0_s73,
+		.num_pins = ARRAY_SIZE(uart0_s73),
+	},
+	{
+		.name = "uart1_grp",
+		.pins = uart1_s73,
+		.num_pins = ARRAY_SIZE(uart1_s73),
+	},
+	{
+		.name = "uart2_grp",
+		.pins = uart2_s73,
+		.num_pins = ARRAY_SIZE(uart2_s73),
+	},
+	{
+		.name = "trace_grp",
+		.pins = trace_s73,
+		.num_pins = ARRAY_SIZE(trace_s73),
+	},
+	{
+		.name = "smt_grp",
+		.pins = smt_s73,
+		.num_pins = ARRAY_SIZE(smt_s73),
+	},
+	{
+		.name = "memcs_grp",
+		.pins = memcs_s73,
+		.num_pins = ARRAY_SIZE(memcs_s73),
+	},
+	{
+		.name = "pl244_grp",
+		.pins = pl244_s73,
+		.num_pins = ARRAY_SIZE(pl244_s73),
+	},
+};
+
+static int func_count, grp_count;
+static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
+static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;
+
+static int mb86s70_pctrl_get_groups_count(struct pinctrl_dev *pctl)
+{
+	return grp_count;
+}
+
+static const char *
+mb86s70_pctrl_get_group_name(struct pinctrl_dev *pctl, unsigned selector)
+{
+	return mb86s7x_pgrps[selector].name;
+}
+
+static int
+mb86s70_pctrl_get_group_pins(struct pinctrl_dev *pctl, unsigned selector,
+			     const unsigned **pins, unsigned *num_pins)
+{
+	*pins = mb86s7x_pgrps[selector].pins;
+	*num_pins = mb86s7x_pgrps[selector].num_pins;
+
+	return 0;
+}
+
+static int
+mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
+			     struct device_node *node,
+			     struct pinctrl_map **map, unsigned *num_maps)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	int ret, i, configlen = 0;
+	unsigned long *pinconfig;
+	const char *function;
+	const char *group;
+	u32 val;
+
+	*map = NULL;
+	*num_maps = 0;
+
+	ret = of_property_read_string(node, "mb86s7x,function", &function);
+	if (ret) {
+		dev_err(pchip->dev,
+			"missing mb86s7x,function property in node %s\n",
+			node->name);
+		return -EINVAL;
+	}
+
+	*map = devm_kzalloc(pchip->dev,
+			    2 * sizeof(struct pinctrl_map), GFP_KERNEL);
+	if (!*map)
+		return -ENOMEM;
+
+	for (i = 0; i < func_count; i++)
+		if (!strcmp(mb86s7x_pmx_funcs[i].name, function))
+			break;
+
+	if (i == func_count) {
+		dev_err(pchip->dev, "function(%s) not found!\n", function);
+		return -EINVAL;
+	}
+	group = mb86s7x_pmx_funcs[i].groups[0];
+
+	(*map)[0].type = PIN_MAP_TYPE_MUX_GROUP;
+	(*map)[0].data.mux.group = group;
+	(*map)[0].data.mux.function = function;
+	*num_maps = 1;
+
+	if (of_find_property(node, "mb86s7x,drvst", NULL))
+		configlen++;
+	if (of_find_property(node, "mb86s7x,pullup", NULL))
+		configlen++;
+
+	if (configlen == 0) /* For S70 */
+		return 0;
+
+	i = 0;
+	pinconfig = devm_kzalloc(pchip->dev,
+				 configlen * sizeof(*pinconfig), GFP_KERNEL);
+
+	if (!of_property_read_u32(node, "mb86s7x,drvst", &val))
+		pinconfig[i++] =
+			pinconf_to_config_packed(PIN_CONFIG_DRIVE_STRENGTH,
+						 val);
+
+	if (!of_property_read_u32(node, "mb86s7x,pullup", &val)) {
+		enum pin_config_param pull;
+
+		if (val)
+			pull = PIN_CONFIG_BIAS_PULL_UP;
+		else
+			pull = PIN_CONFIG_BIAS_PULL_DOWN;
+		pinconfig[i++] = pinconf_to_config_packed(pull, 0);
+	}
+
+	(*map)[1].type = PIN_MAP_TYPE_CONFIGS_GROUP;
+	(*map)[1].data.configs.group_or_pin = group;
+	(*map)[1].data.configs.configs = pinconfig;
+	(*map)[1].data.configs.num_configs = configlen;
+
+	*num_maps = 2;
+
+	return 0;
+}
+
+static void
+mb86s70_pctrl_dt_free_map(struct pinctrl_dev *pctl,
+			  struct pinctrl_map *map, unsigned num_maps)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	int i;
+
+	for (i = 0; i < num_maps; i++)
+		if (map[i].type == PIN_MAP_TYPE_CONFIGS_GROUP)
+			devm_kfree(pchip->dev, map[i].data.configs.configs);
+
+	devm_kfree(pchip->dev, map);
+}
+
+static const struct pinctrl_ops mb86s70_pctrl_ops = {
+	.get_groups_count	= mb86s70_pctrl_get_groups_count,
+	.get_group_name		= mb86s70_pctrl_get_group_name,
+	.get_group_pins		= mb86s70_pctrl_get_group_pins,
+	.dt_node_to_map		= mb86s70_pctrl_dt_node_to_map,
+	.dt_free_map		= mb86s70_pctrl_dt_free_map,
+};
+
+static const char * const pcie0_groups[] = {"pcie0_grp"};
+static const char * const usb3h0_groups[] = {"usb3h0_grp"};
+static const char * const usb2h0_groups[] = {"usb2h0_grp"};
+static const char * const usb2d0_groups[] = {"usb2d0_grp"};
+static const char * const sdh30_groups[] = {"sdh30_grp"};
+static const char * const emmc0_groups[] = {"emmc0_grp"};
+static const char * const hsspi0_groups[] = {"hsspi0_grp"};
+static const char * const gmacd0_groups[] = {"gmacd0_grp"};
+static const char * const gmacm0_groups[] = {"gmacm0_grp"};
+static const char * const i2s0_groups[] = {"i2s0_grp"};
+static const char * const other0_groups[] = {"other0_grp"};
+static const char * const jtag0_groups[] = {"jtag0_grp"};
+static const char * const pcie1_groups[] = {"pcie1_grp"};
+static const char * const usb3h1_groups[] = {"usb3h1_grp"};
+static const char * const extint16_groups[] = {"extint16_grp"};
+static const char * const extint5_groups[] = {"extint5_grp"};
+static const char * const tsif0_groups[] = {"tsif0_grp"};
+static const char * const tsif1_groups[] = {"tsif1_grp"};
+static const char * const cfg_groups[] = {"cfg_grp"};
+static const char * const uart0_groups[] = {"uart0_grp"};
+static const char * const uart1_groups[] = {"uart1_grp"};
+static const char * const uart2_groups[] = {"uart2_grp"};
+static const char * const pl244_groups[] = {"pl244_grp"};
+static const char * const trace_groups[] = {"trace_grp"};
+static const char * const memcs_groups[] = {"memcs_grp"};
+static const char * const cap_groups[] = {"cap_grp"};
+static const char * const smt_groups[] = {"smt_grp"};
+
+static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
+	{
+		.prog_val = 0x1,
+		.name = "extint5",
+		.groups = extint5_groups,
+		.num_groups = ARRAY_SIZE(extint5_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "pcie0",
+		.groups = pcie0_groups,
+		.num_groups = ARRAY_SIZE(pcie0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb3h0",
+		.groups = usb3h0_groups,
+		.num_groups = ARRAY_SIZE(usb3h0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb2h0",
+		.groups = usb2h0_groups,
+		.num_groups = ARRAY_SIZE(usb2h0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb2d0",
+		.groups = usb2d0_groups,
+		.num_groups = ARRAY_SIZE(usb2d0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "sdh30",
+		.groups = sdh30_groups,
+		.num_groups = ARRAY_SIZE(sdh30_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "emmc0",
+		.groups = emmc0_groups,
+		.num_groups = ARRAY_SIZE(emmc0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "hsspi0",
+		.groups = hsspi0_groups,
+		.num_groups = ARRAY_SIZE(hsspi0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "gmacd0",
+		.groups = gmacd0_groups,
+		.num_groups = ARRAY_SIZE(gmacd0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "gmacm0",
+		.groups = gmacm0_groups,
+		.num_groups = ARRAY_SIZE(gmacm0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "i2s0",
+		.groups = i2s0_groups,
+		.num_groups = ARRAY_SIZE(i2s0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "other0",
+		.groups = other0_groups,
+		.num_groups = ARRAY_SIZE(other0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "jtag0",
+		.groups = jtag0_groups,
+		.num_groups = ARRAY_SIZE(jtag0_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "pcie1",
+		.groups = pcie1_groups,
+		.num_groups = ARRAY_SIZE(pcie1_groups),
+	},
+	{
+		.prog_val = 0x0,
+		.name = "usb3h1",
+		.groups = usb3h1_groups,
+		.num_groups = ARRAY_SIZE(usb3h1_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "cfg",
+		.groups = cfg_groups,
+		.num_groups = ARRAY_SIZE(cfg_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart0",
+		.groups = uart0_groups,
+		.num_groups = ARRAY_SIZE(uart0_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart1",
+		.groups = uart1_groups,
+		.num_groups = ARRAY_SIZE(uart1_groups),
+	},
+	{
+		.prog_val = 0x1,
+		.name = "uart2",
+		.groups = uart2_groups,
+		.num_groups = ARRAY_SIZE(uart2_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "trace",
+		.groups = trace_groups,
+		.num_groups = ARRAY_SIZE(trace_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "pl244",
+		.groups = pl244_groups,
+		.num_groups = ARRAY_SIZE(pl244_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "smt",
+		.groups = smt_groups,
+		.num_groups = ARRAY_SIZE(smt_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "memcs",
+		.groups = memcs_groups,
+		.num_groups = ARRAY_SIZE(memcs_groups),
+	},
+};
+
+static const struct mb86s70_pmx_function mb86s70_pmx_funcs[] = {
+	{
+		.prog_val = 0x2,
+		.name = "extint16",
+		.groups = extint16_groups,
+		.num_groups = ARRAY_SIZE(extint16_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "tsif0",
+		.groups = tsif0_groups,
+		.num_groups = ARRAY_SIZE(tsif0_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "tsif1",
+		.groups = tsif1_groups,
+		.num_groups = ARRAY_SIZE(tsif1_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "uart1",
+		.groups = uart1_groups,
+		.num_groups = ARRAY_SIZE(uart1_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "uart2",
+		.groups = uart2_groups,
+		.num_groups = ARRAY_SIZE(uart2_groups),
+	},
+	{
+		.prog_val = 0x5,
+		.name = "pl244",
+		.groups = pl244_groups,
+		.num_groups = ARRAY_SIZE(pl244_groups),
+	},
+	{
+		.prog_val = 0x3,
+		.name = "trace",
+		.groups = trace_groups,
+		.num_groups = ARRAY_SIZE(trace_groups),
+	},
+	{
+		.prog_val = 0x4,
+		.name = "memcs",
+		.groups = memcs_groups,
+		.num_groups = ARRAY_SIZE(memcs_groups),
+	},
+	{
+		.prog_val = 0x2,
+		.name = "capture",
+		.groups = cap_groups,
+		.num_groups = ARRAY_SIZE(cap_groups),
+	}
+};
+
+static int
+mb86s70_pmx_get_functions_count(struct pinctrl_dev *pctl)
+{
+	return func_count;
+}
+
+static const char *
+mb86s70_pmx_get_function_name(struct pinctrl_dev *pctl, unsigned selector)
+{
+	return mb86s7x_pmx_funcs[selector].name;
+}
+
+static int
+mb86s70_pmx_get_function_groups(struct pinctrl_dev *pctl,
+				unsigned selector, const char * const **groups,
+				unsigned * const num_groups)
+{
+	*groups = mb86s7x_pmx_funcs[selector].groups;
+	*num_groups = mb86s7x_pmx_funcs[selector].num_groups;
+
+	return 0;
+}
+
+static int
+mb86s70_pmx_enable(struct pinctrl_dev *pctl,
+		   unsigned func_selector, unsigned group_selector)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pins;
+	int i, j;
+
+	if (group_selector >= grp_count) {
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return -EINVAL;
+	}
+
+	j = mb86s7x_pgrps[group_selector].num_pins;
+	pins = mb86s7x_pgrps[group_selector].pins;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+
+	/* Busy if any pin in the same 'bunch' is taken */
+	for (i = 0; i < j; i++) {
+		u32 val;
+		int p = pins[i] / 4 * 4;
+
+		if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
+			continue;
+
+		val = readl(pchip->base + p);
+		/* skip if no change needed */
+		if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
+			continue;
+
+		if (pchip->pin_busy[p]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p);
+			goto busy_exit;
+		}
+
+		if (pchip->pin_busy[p + 1]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+1);
+			goto busy_exit;
+		}
+
+		if (p == 64)
+			continue;
+
+		if (pchip->pin_busy[p + 2]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+2);
+			goto busy_exit;
+		}
+
+		if (pchip->pin_busy[p + 3]) {
+			pr_err("%s:%d:%d %d busy\n",
+			       __func__, __LINE__, pins[i], p+3);
+			goto busy_exit;
+		}
+
+		continue;
+busy_exit:
+		spin_unlock_irqrestore(&pchip->lock, flags);
+		pr_err("%s:%d Take a look!\n", __func__, __LINE__);
+		return -EBUSY;
+	}
+
+	pr_debug("Going to enable %s on pins -",
+		 mb86s7x_pmx_funcs[func_selector].name);
+	for (i = 0; i < j; i++) {
+		int p = pins[i];
+
+		pr_debug(" %d", p);
+		pchip->pin_busy[p] = true;
+		if (p < PINS_VIRT) /* Not for virtual pins */
+			writel(mb86s7x_pmx_funcs[func_selector].prog_val,
+			       pchip->base + p / 4 * 4);
+	}
+
+	spin_unlock_irqrestore(&pchip->lock, flags);
+	pr_debug("\n");
+
+	return 0;
+}
+
+static void
+mb86s70_pmx_disable(struct pinctrl_dev *pctl,
+		    unsigned func_selector, unsigned group_selector)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pins;
+	int i, j;
+
+	if (group_selector >= grp_count) {
+		pr_err("%s:%d\n", __func__, __LINE__);
+		return;
+	}
+
+	j = mb86s7x_pgrps[group_selector].num_pins;
+	pins = mb86s7x_pgrps[group_selector].pins;
+
+	pr_debug("Going to disable %s on pins -",
+		 mb86s7x_pmx_funcs[func_selector].name);
+	spin_lock_irqsave(&pchip->lock, flags);
+	for (i = 0; i < j; i++) {
+		pr_debug(" %d", pins[i]);
+		pchip->pin_busy[pins[i]] = false;
+	}
+	spin_unlock_irqrestore(&pchip->lock, flags);
+	pr_debug("\n");
+}
+
+static int
+mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
+			       struct pinctrl_gpio_range *range,
+			       unsigned pin, bool input)
+{
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 off, bit, val;
+
+	if (pin >= 64)
+		return -EINVAL;
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - gc->base) % 8;
+	off = (pin - gc->base) / 8 * 4;
+	val = readl(gchip->base + DDR(off));
+	if (input)
+		val &= ~(1 << bit);
+	else
+		val |= (1 << bit);
+	writel(val, gchip->base + DDR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static int
+mb86s70_pmx_gpio_request_enable(struct pinctrl_dev *pctl,
+				struct pinctrl_gpio_range *range, unsigned pin)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						 struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	int p = pin / 4 * 4;
+	u32 val, off, bit;
+
+	if (p == 64)
+		return -ENODEV;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+	if (pchip->pin_busy[p] || pchip->pin_busy[p + 1] ||
+			pchip->pin_busy[p + 2] || pchip->pin_busy[p + 3]) {
+		val = readl(pchip->base + p);
+		if (val != 0x1) {
+			spin_unlock_irqrestore(&pchip->lock, flags);
+			return -EBUSY;
+		}
+	}
+
+	pchip->pin_busy[pin] = true;
+	writel(0x1, pchip->base + p);
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - range->pin_base) % 8;
+	off = (pin - range->pin_base) / 8 * 4;
+	val = readl(gchip->base + PFR(off));
+	val &= ~(1 << bit);
+	writel(val, gchip->base + PFR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+
+	return 0;
+}
+
+static void
+mb86s70_pmx_gpio_disable_free(struct pinctrl_dev *pctl,
+			      struct pinctrl_gpio_range *range, unsigned pin)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	struct gpio_chip *gc = range->gc;
+	struct mb86s70_gpio_chip *gchip = container_of(gc,
+						struct mb86s70_gpio_chip, gc);
+	unsigned long flags;
+	u32 off, bit, val;
+
+	if (pin >= 64)
+		return;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+	pchip->pin_busy[pin] = false;
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	spin_lock_irqsave(&gchip->lock, flags);
+	bit = (pin - range->pin_base) % 8;
+	off = (pin - range->pin_base) / 8 * 4;
+	val = readl(gchip->base + PFR(off));
+	val |= (1 << bit);
+	writel(val, gchip->base + PFR(off));
+	spin_unlock_irqrestore(&gchip->lock, flags);
+}
+
+static const struct pinmux_ops mb86s70_pmx_ops = {
+	.get_functions_count	= mb86s70_pmx_get_functions_count,
+	.get_function_name	= mb86s70_pmx_get_function_name,
+	.get_function_groups	= mb86s70_pmx_get_function_groups,
+	.enable			= mb86s70_pmx_enable,
+	.disable		= mb86s70_pmx_disable,
+	.gpio_set_direction	= mb86s70_pmx_gpio_set_direction,
+	.gpio_request_enable	= mb86s70_pmx_gpio_request_enable,
+	.gpio_disable_free	= mb86s70_pmx_gpio_disable_free,
+};
+
+static int
+mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
+			     unsigned long *config)
+{
+	return -EINVAL;
+}
+
+static int
+mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
+			     unsigned long *configs, unsigned num_configs)
+{
+	struct mb86s70_pmux_chip *pchip	= pinctrl_dev_get_drvdata(pctl);
+	unsigned long flags;
+	const unsigned *pin;
+	int i, j, p;
+	u32 val, ds;
+
+	p = mb86s7x_pgrps[group].num_pins;
+	pin = mb86s7x_pgrps[group].pins;
+
+	spin_lock_irqsave(&pchip->lock, flags);
+
+	for (i = 0; i < num_configs; i++) {
+		switch (pinconf_to_config_param(configs[i])) {
+		case PIN_CONFIG_DRIVE_STRENGTH:
+			/* Drive Strength should be 2, 4, 6 or 8 mA */
+			ds = pinconf_to_config_argument(configs[i]);
+			ds = (ds - 2) / 2;
+			for (j = 0; j < p; j++) {
+				/* Clear the Hi-z */
+				val = readl_relaxed(pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val = readl_relaxed(pchip->conf_base +
+							CDRV_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (ds << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							CDRV_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_HIGH_IMPEDANCE:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (0x1 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							FORCEZ_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_PULL_DOWN:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							PULL_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							PULL_PDG(pin[j]));
+			}
+			break;
+		case PIN_CONFIG_BIAS_PULL_UP:
+			for (j = 0; j < p; j++) {
+				val = readl_relaxed(pchip->conf_base +
+							PULL_PDG(pin[j]));
+				val &= ~(0x3 << PIN_OFF(pin[j]));
+				val |= (1 << PIN_OFF(pin[j]));
+				writel_relaxed(val, pchip->conf_base +
+							PULL_PDG(pin[j]));
+			}
+			break;
+		default:
+			pr_err("%s:%d!!\n", __func__, __LINE__);
+			break;
+		}
+	}
+
+	spin_unlock_irqrestore(&pchip->lock, flags);
+
+	return 0;
+}
+
+static const struct pinconf_ops mb86s70_conf_ops = {
+	.pin_config_group_get = mb86s70_pin_config_group_get,
+	.pin_config_group_set = mb86s70_pin_config_group_set,
+};
+
+static const struct of_device_id mb86s70_pinmux_dt_ids[] = {
+	{.compatible = "fujitsu,mb86s70-pinctrl", .data = (void *)PMUX_MB86S70},
+	{.compatible = "fujitsu,mb86s73-pinctrl", .data = (void *)PMUX_MB86S73},
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mb86s70_pinmux_dt_ids);
+
+static int mb86s70_pinmux_probe(struct platform_device *pdev)
+{
+	const struct of_device_id *device;
+	struct mb86s70_pmux_chip *pchip;
+	struct resource *res;
+	int i, type;
+
+	device = of_match_device(mb86s70_pinmux_dt_ids, &pdev->dev);
+	if (!device)
+		return -ENODEV;
+
+	type = (int)device->data;
+
+	/* reserve memory space for pmux chip */
+	pchip = devm_kzalloc(&pdev->dev, sizeof(*pchip), GFP_KERNEL);
+	if (pchip == NULL) {
+		dev_err(&pdev->dev, "can't allocate pinmux chip data.\n");
+		return -ENOMEM;
+	}
+	pchip->dev = &pdev->dev;
+
+	/* initiate pmux chip structure */
+	platform_set_drvdata(pdev, pchip);
+
+	/* IO resource */
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	pchip->base = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(pchip->base))
+		return PTR_ERR(pchip->base);
+
+	/* initiate lock */
+	spin_lock_init(&pchip->lock);
+
+	pchip->desc.name = dev_name(&pdev->dev);
+	pchip->desc.npins = MB86S70_PMUX_PINS;
+	pchip->desc.pins = pchip->pins;
+	pchip->desc.pctlops = &mb86s70_pctrl_ops;
+	pchip->desc.pmxops = &mb86s70_pmx_ops;
+	pchip->desc.owner = THIS_MODULE;
+	if (type == PMUX_MB86S73) {
+		pchip->desc.confops = &mb86s70_conf_ops;
+		res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+		pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
+		if (IS_ERR(pchip->conf_base))
+			return PTR_ERR(pchip->conf_base);
+		mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
+		func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
+		mb86s7x_pgrps = mb86s73_pgrps;
+		grp_count = ARRAY_SIZE(mb86s73_pgrps);
+	} else {
+		mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
+		func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
+		mb86s7x_pgrps = mb86s70_pgrps;
+		grp_count = ARRAY_SIZE(mb86s70_pgrps);
+	}
+
+	for (i = 0; i < MB86S70_PMUX_PINS; i++) {
+		pchip->pin_busy[i] = false;
+		pchip->pins[i].number = i;
+		pchip->pins[i].name = &pchip->pin_names[5 * i];
+		snprintf(&pchip->pin_names[5 * i], 5, "PD%d", i);
+	}
+
+	pchip->pctl = pinctrl_register(&pchip->desc, &pdev->dev, pchip);
+	if (!pchip->pctl) {
+		dev_err(&pdev->dev, "couldn't register pinctrl driver\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static const struct of_device_id mb86s70_gpio_dt_ids[] = {
+	{ .compatible = "fujitsu,mb86s7x-gpio" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
+
+static struct platform_driver mb86s70_gpio_driver = {
+	.probe     = mb86s70_gpio_probe,
+	.driver    = {
+		.name  = "mb86s70-gpio",
+		.owner = THIS_MODULE,
+		.of_match_table = mb86s70_gpio_dt_ids,
+	},
+};
+
+static struct platform_driver mb86s70_pinmux_driver = {
+	.probe     = mb86s70_pinmux_probe,
+	.driver    = {
+		.name  = "mb86s70-pinmux",
+		.owner = THIS_MODULE,
+		.of_match_table = mb86s70_pinmux_dt_ids,
+	},
+};
+
+static int __init mb86s70_pins_init(void)
+{
+	int ret;
+
+	ret = platform_driver_register(&mb86s70_pinmux_driver);
+	if (ret)
+		return ret;
+
+	return platform_driver_register(&mb86s70_gpio_driver);
+}
+subsys_initcall(mb86s70_pins_init);
+
+MODULE_DESCRIPTION("MB86S7x PinCtrl Driver");
+MODULE_ALIAS("platform:mb86s70-pinmux");
+MODULE_ALIAS("platform:mb86s70-gpio");
+MODULE_LICENSE("GPL");
-- 
1.8.1.2

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

* [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:31   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:31 UTC (permalink / raw)
  To: linux-arm-kernel, devicetree, netdev
  Cc: andy.green, patches, jaswinder.singh, linux, arnd, olof,
	mark.rutland, robh+dt, pawel.moll, davem, stephen, f.fainelli,
	romieu, Mollie Wu, Tetsuya Takinishi

This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
Ethernet + PHY IP used in a variety of their ARM-based ASICs.

This is being sent as part of a series including the arch support that uses it,
Fujitsu mb86s7x.

This driver was originally written by guys inside Fujitsu as an abstracted
"you can build this for Windows as well" type code, I've removed all that,
modernized various things, added runtime_pm, and ported it to work with
Device Tree, using only the bindings already mentioned in

./Documentation/devicetree/bindings/net/ethernet.txt

Bindings documentation is added by this patch.

Signed-off-by: Andy Green <andy.green@linaro.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
 drivers/net/ethernet/fujitsu/Kconfig               |  12 +
 drivers/net/ethernet/fujitsu/Makefile              |   1 +
 drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
 drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
 .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
 .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
 10 files changed, 2384 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

diff --git a/Documentation/devicetree/bindings/net/fujitsu-ogma.txt b/Documentation/devicetree/bindings/net/fujitsu-ogma.txt
new file mode 100644
index 0000000..1fd680f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/fujitsu-ogma.txt
@@ -0,0 +1,43 @@
+* Fujistu OGMA Ethernet Controller IP
+
+Required properties:
+- compatible: Should be "fujitsu,ogma"
+- reg: Address and length of the register sets, the first is the main
+	registers, then the rdlar and tdlar regions for the SoC
+- interrupts: Should contain ethernet controller interrupt
+- clocks: phandle to any clocks to be switched by runtime_pm
+- phy-mode: See ethernet.txt file in the same directory
+- max-speed: See ethernet.txt file in the same directory
+- max-frame-size: See ethernet.txt file in the same directory, if 9000 or
+	above jumbo frames are enabled
+- local-mac-address: See ethernet.txt file in the same directory
+- phy-handle: phandle to select child phy
+
+For the child phy
+
+- compatible "ethernet-phy-ieee802.3-c22" is needed
+- device_type "ethernet-phy"
+- reg: phy address
+
+
+Example:
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy@1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig
index 1085257..bbe3ee8 100644
--- a/drivers/net/ethernet/fujitsu/Kconfig
+++ b/drivers/net/ethernet/fujitsu/Kconfig
@@ -28,4 +28,16 @@ config PCMCIA_FMVJ18X
 	  To compile this driver as a module, choose M here: the module will be
 	  called fmvj18x_cs.  If unsure, say N.
 
+config NET_FUJITSU_OGMA
+	tristate "Fujitsu OGMA network support"
+	depends on OF
+	select PHYLIB
+	select MII
+help
+	  Enable for OGMA support of Fujitsu FGAMC4 IP
+	  Provides Gigabit ethernet support
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ogma.  If unsure, say N.
+
 endif # NET_VENDOR_FUJITSU
diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile
index 21561fd..b90a445 100644
--- a/drivers/net/ethernet/fujitsu/Makefile
+++ b/drivers/net/ethernet/fujitsu/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
+obj-$(CONFIG_NET_FUJITSU_OGMA) += ogma/
diff --git a/drivers/net/ethernet/fujitsu/ogma/Makefile b/drivers/net/ethernet/fujitsu/ogma/Makefile
new file mode 100644
index 0000000..8661027
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/Makefile
@@ -0,0 +1,6 @@
+obj-m := ogma.o
+ogma-objs := ogma_desc_ring_access.o \
+		ogma_netdev.o \
+		ogma_ethtool.o \
+		ogma_platform.o \
+		ogma_gmac_access.o
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma.h b/drivers/net/ethernet/fujitsu/ogma/ogma.h
new file mode 100644
index 0000000..2fe2820
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma.h
@@ -0,0 +1,380 @@
+/**
+ * ogma.h
+ *
+ *  Copyright (C) 2011 - 2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#ifndef OGMA_INTERNAL_H
+#define OGMA_INTERNAL_H
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#define OGMA_FLOW_CONTROL_START_THRESHOLD	36
+#define OGMA_FLOW_CONTROL_STOP_THRESHOLD	48
+
+#define OGMA_CLK_MHZ				1000000
+
+#define OGMA_RX_PKT_BUF_LEN			1522
+#define OGMA_RX_JUMBO_PKT_BUF_LEN		9022
+
+#define OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX		19
+
+#define DESC_NUM 128
+
+#define OGMA_TX_SHIFT_OWN_FIELD			31
+#define OGMA_TX_SHIFT_LD_FIELD			30
+#define OGMA_TX_SHIFT_DRID_FIELD		24
+#define OGMA_TX_SHIFT_PT_FIELD			21
+#define OGMA_TX_SHIFT_TDRID_FIELD		16
+#define OGMA_TX_SHIFT_CC_FIELD			15
+#define OGMA_TX_SHIFT_FS_FIELD			9
+#define OGMA_TX_LAST				8
+#define OGMA_TX_SHIFT_CO			7
+#define OGMA_TX_SHIFT_SO			6
+#define OGMA_TX_SHIFT_TRS_FIELD			4
+#define OGMA_RX_PKT_OWN_FIELD			31
+#define OGMA_RX_PKT_LD_FIELD			30
+#define OGMA_RX_PKT_SDRID_FIELD			24
+#define OGMA_RX_PKT_FR_FIELD			23
+#define OGMA_RX_PKT_ER_FIELD			21
+#define OGMA_RX_PKT_ERR_FIELD			16
+#define OGMA_RX_PKT_TDRID_FIELD			12
+#define OGMA_RX_PKT_FS_FIELD			9
+#define OGMA_RX_PKT_LS_FIELD			8
+#define OGMA_RX_PKT_CO_FIELD			6
+
+#define OGMA_RX_PKT_ERR_MASK			3
+
+#define OGMA_MAX_TX_PKT_LEN			1518
+#define OGMA_MAX_TX_JUMBO_PKT_LEN		9018
+
+enum ogma_rings {
+	OGMA_RING_TX,
+	OGMA_RING_RX
+};
+
+#define OGMA_RING_GMAC				15
+#define OGMA_RING_MAX				1
+
+#define OGMA_TCP_SEG_LEN_MAX			1460
+#define OGMA_TCP_JUMBO_SEG_LEN_MAX		8960
+#define OGMA_TCP_SEG_LEN_MIN			536
+
+#define OGMA_RX_CKSUM_NOTAVAIL			0
+#define OGMA_RX_CKSUM_OK			1
+#define OGMA_RX_CKSUM_NG			2
+
+#define OGMA_TOP_IRQ_REG_CODE_LOAD_END		(1 << 20)
+#define OGMA_IRQ_TRANSITION_COMPLETE		(1 <<  4)
+#define OGMA_IRQ_RX				(1 <<  1)
+#define OGMA_IRQ_TX				(1 <<  0)
+
+#define OGMA_IRQ_EMPTY				(1 << 17)
+#define OGMA_IRQ_ERR				(1 << 16)
+#define OGMA_IRQ_PKT_CNT			(1 << 15)
+#define OGMA_IRQ_TIMEUP				(1 << 14)
+#define OGMA_IRQ_RCV			(OGMA_IRQ_PKT_CNT | OGMA_IRQ_TIMEUP)
+
+#define OGMA_IRQ_TX_DONE			(1 << 15)
+#define OGMA_IRQ_SND			(OGMA_IRQ_TX_DONE | OGMA_IRQ_TIMEUP)
+
+#define OGMA_MODE_TRANS_COMP_IRQ_N2T		(1 << 20)
+#define OGMA_MODE_TRANS_COMP_IRQ_T2N		(1 << 19)
+
+#define OGMA_DESC_MIN				2
+#define OGMA_DESC_MAX				2047
+#define OGMA_INT_PKTCNT_MAX			2047
+
+#define OGMA_FLOW_START_TH_MAX			383
+#define OGMA_FLOW_STOP_TH_MAX			383
+#define OGMA_FLOW_PAUSE_TIME_MIN		5
+
+#define OGMA_CLK_EN_REG_DOM_ALL			0x3f
+
+#define OGMA_REG_TOP_STATUS			0x80
+#define OGMA_REG_TOP_INTEN			0x81
+#define OGMA_REG_INTEN_SET			0x8d
+#define OGMA_REG_INTEN_CLR			0x8e
+#define OGMA_REG_NRM_TX_STATUS			0x100
+#define OGMA_REG_NRM_TX_INTEN			0x101
+#define OGMA_REG_NRM_TX_INTEN_SET		0x10a
+#define OGMA_REG_NRM_TX_INTEN_CLR		0x10b
+#define OGMA_REG_NRM_RX_STATUS			0x110
+#define OGMA_REG_NRM_RX_INTEN			0x111
+#define OGMA_REG_NRM_RX_INTEN_SET		0x11a
+#define OGMA_REG_NRM_RX_INTEN_CLR		0x11b
+#define OGMA_REG_RESERVED_RX_DESC_START		0x122
+#define OGMA_REG_RESERVED_TX_DESC_START		0x132
+#define OGMA_REG_CLK_EN				0x40
+#define OGMA_REG_SOFT_RST			0x41
+#define OGMA_REG_PKT_CTRL			0x50
+#define OGMA_REG_COM_INIT			0x48
+#define OGMA_REG_DMA_TMR_CTRL			0x83
+#define OGMA_REG_F_TAIKI_MC_VER			0x8b
+#define OGMA_REG_F_TAIKI_VER			0x8c
+#define OGMA_REG_DMA_HM_CTRL			0x85
+#define OGMA_REG_DMA_MH_CTRL			0x88
+#define OGMA_REG_NRM_TX_PKTCNT			0x104
+#define OGMA_REG_NRM_TX_DONE_TXINT_PKTCNT	0x106
+#define OGMA_REG_NRM_RX_RXINT_PKTCNT		0x116
+#define OGMA_REG_NRM_TX_TXINT_TMR		0x108
+#define OGMA_REG_NRM_RX_RXINT_TMR		0x118
+#define OGMA_REG_NRM_TX_DONE_PKTCNT		0x105
+#define OGMA_REG_NRM_RX_PKTCNT			0x115
+#define OGMA_REG_NRM_TX_TMR			0x107
+#define OGMA_REG_NRM_RX_TMR			0x117
+#define OGMA_REG_NRM_TX_DESC_START		0x102
+#define OGMA_REG_NRM_RX_DESC_START		0x112
+#define OGMA_REG_NRM_TX_CONFIG			0x10c
+#define OGMA_REG_NRM_RX_CONFIG			0x11c
+#define MAC_REG_DATA				0x470
+#define MAC_REG_CMD				0x471
+#define MAC_REG_FLOW_TH				0x473
+#define MAC_REG_INTF_SEL			0x475
+#define MAC_REG_DESC_INIT			0x47f
+#define MAC_REG_DESC_SOFT_RST			0x481
+#define OGMA_REG_MODE_TRANS_COMP_STATUS		0x140
+#define GMAC_REG_MCR				0x0000
+#define GMAC_REG_MFFR				0x0004
+#define GMAC_REG_GAR				0x0010
+#define GMAC_REG_GDR				0x0014
+#define GMAC_REG_FCR				0x0018
+#define GMAC_REG_BMR				0x1000
+#define GMAC_REG_RDLAR				0x100c
+#define GMAC_REG_TDLAR				0x1010
+#define GMAC_REG_OMR				0x1018
+
+#define OGMA_PKT_CTRL_REG_MODE_NRM		(1 << 28)
+#define OGMA_PKT_CTRL_REG_EN_JUMBO		(1 << 27)
+#define OGMA_PKT_CTRL_REG_LOG_CHKSUM_ER		(1 << 3)
+#define OGMA_PKT_CTRL_REG_LOG_HD_INCOMPLETE	(1 << 2)
+#define OGMA_PKT_CTRL_REG_LOG_HD_ER		(1 << 1)
+
+#define OGMA_CLK_EN_REG_DOM_G			(1 << 5)
+#define OGMA_CLK_EN_REG_DOM_C			(1 << 1)
+#define OGMA_CLK_EN_REG_DOM_D			(1 << 0)
+
+#define OGMA_COM_INIT_REG_PKT			(1 << 1)
+#define OGMA_COM_INIT_REG_CORE			(1 << 0)
+#define OGMA_COM_INIT_REG_ALL  (OGMA_COM_INIT_REG_CORE | OGMA_COM_INIT_REG_PKT)
+
+#define OGMA_SOFT_RST_REG_RESET			0
+#define OGMA_SOFT_RST_REG_RUN			(1 << 31)
+
+#define OGMA_DMA_CTRL_REG_STOP			1
+#define MH_CTRL__MODE_TRANS			(1 << 20)
+
+#define OGMA_GMAC_CMD_ST_READ			0
+#define OGMA_GMAC_CMD_ST_WRITE			(1 << 28)
+#define OGMA_GMAC_CMD_ST_BUSY			(1 << 31)
+
+#define OGMA_GMAC_BMR_REG_COMMON		(0x00412080)
+#define OGMA_GMAC_BMR_REG_RESET			(0x00020181)
+#define OGMA_GMAC_BMR_REG_SWR			(0x00000001)
+
+#define OGMA_GMAC_OMR_REG_ST			(1 << 13)
+#define OGMA_GMAC_OMR_REG_SR			(1 << 1)
+
+#define OGMA_GMAC_MCR_REG_CST			(1 << 25)
+#define OGMA_GMAC_MCR_REG_JE			(1 << 20)
+#define OGMA_MCR_PS				(1 << 15)
+#define OGMA_GMAC_MCR_REG_FES			(1 << 14)
+#define OGMA_GMAC_MCR_REG_FULL_DUPLEX_COMMON	(0x0000280c)
+#define OGMA_GMAC_MCR_REG_HALF_DUPLEX_COMMON	(0x0001a00c)
+
+#define OGMA_FCR_RFE				(1 << 2)
+#define OGMA_FCR_TFE				(1 << 1)
+
+#define OGMA_GMAC_GAR_REG_GW			(1 << 1)
+#define OGMA_GMAC_GAR_REG_GB			(1 << 0)
+
+#define OGMA_GMAC_GAR_REG_SHIFT_PA		11
+#define OGMA_GMAC_GAR_REG_SHIFT_GR		6
+#define GMAC_REG_SHIFT_CR_GAR			2
+
+#define OGMA_GMAC_GAR_REG_CR_25_35_MHZ		2
+#define OGMA_GMAC_GAR_REG_CR_35_60_MHZ		3
+#define OGMA_GMAC_GAR_REG_CR_60_100_MHZ		0
+#define OGMA_GMAC_GAR_REG_CR_100_150_MHZ	1
+#define OGMA_GMAC_GAR_REG_CR_150_250_MHZ	4
+#define OGMA_GMAC_GAR_REG_CR_250_300_MHZ	5
+
+#define OGMA_REG_OGMA_VER_F_TAIKI		0x20000
+
+#define OGMA_REG_DESC_RING_CONFIG_CFG_UP	31
+#define OGMA_REG_DESC_RING_CONFIG_CH_RST	30
+#define OGMA_REG_DESC_TMR_MODE			4
+#define OGMA_REG_DESC_ENDIAN			0
+
+#define OGMA_MAC_DESC_SOFT_RST_SOFT_RST		1
+#define OGMA_MAC_DESC_INIT_REG_INIT		1
+
+/* this is used to interpret a register layout */
+struct ogma_pkt_ctrlaram {
+	u8 log_chksum_er_flag:1;
+	u8 log_hd_imcomplete_flag:1;
+	u8 log_hd_er_flag:1;
+};
+
+struct ogma_param {
+	struct ogma_pkt_ctrlaram pkt_ctrlaram;
+	bool use_jumbo_pkt_flag;
+};
+
+struct ogma_mac_mode {
+	u16 flow_start_th;
+	u16 flow_stop_th;
+	u16 pause_time;
+	bool flow_ctrl_enable_flag;
+};
+
+struct ogma_desc_ring {
+	spinlock_t spinlock_desc; /* protect descriptor access */
+	phys_addr_t desc_phys;
+	struct ogma_frag_info *frag;
+	struct sk_buff **priv;
+	void *ring_vaddr;
+	enum ogma_rings id;
+	int len;
+	u16 tx_done_num;
+	u16 rx_num;
+	u16 head;
+	u16 tail;
+	bool running;
+	bool full;
+};
+
+struct ogma_frag_info {
+	dma_addr_t dma_addr;
+	void *addr;
+	u16 len;
+};
+
+struct ogma_priv {
+	struct ogma_desc_ring desc_ring[OGMA_RING_MAX + 1];
+	struct ethtool_coalesce et_coalesce;
+	struct ogma_mac_mode mac_mode;
+	struct ogma_param param;
+	struct napi_struct napi;
+	phys_addr_t rdlar_pa, tdlar_pa;
+	phy_interface_t phy_interface;
+	spinlock_t tx_queue_lock; /* protect transmit queue */
+	struct ogma_frag_info tx_info[MAX_SKB_FRAGS];
+	struct net_device *net_device;
+	struct device_node *phy_np;
+	struct phy_device *phydev;
+	struct mii_bus *mii_bus;
+	void __iomem *ioaddr;
+	struct device *dev;
+	struct clk *clk[3];
+	u32 scb_set_normal_tx_paddr;
+	u32 scb_pkt_ctrl_reg;
+	u32 rx_pkt_buf_len;
+	u32 msg_enable;
+	u32 freq;
+	int actual_link_speed;
+	int clock_count;
+	bool rx_cksum_offload_flag;
+	bool actual_duplex;
+	bool irq_registered;
+};
+
+struct ogma_tx_de {
+	u32 attr;
+	u32 data_buf_addr;
+	u32 buf_len_info;
+	u32 reserved;
+};
+
+struct ogma_rx_de {
+	u32 attr;
+	u32 data_buf_addr;
+	u32 buf_len_info;
+	u32 reserved;
+};
+
+struct ogma_tx_pkt_ctrl {
+	u16 tcp_seg_len;
+	bool tcp_seg_offload_flag;
+	bool cksum_offload_flag;
+};
+
+struct ogma_rx_pkt_info {
+	int rx_cksum_result;
+	int err_code;
+	bool is_fragmented;
+	bool err_flag;
+};
+
+struct ogma_skb_cb {
+	bool is_rx;
+};
+
+static inline void ogma_writel(struct ogma_priv *priv, u32 reg_addr, u32 val)
+{
+	writel(val, priv->ioaddr + (reg_addr << 2));
+}
+
+static inline u32 ogma_readl(struct ogma_priv *priv, u32 reg_addr)
+{
+	return readl(priv->ioaddr + (reg_addr << 2));
+}
+
+static inline void ogma_mark_skb_type(struct sk_buff *skb, bool is_rx)
+{
+	struct ogma_skb_cb *cb = (struct ogma_skb_cb *)skb->cb;
+
+	cb->is_rx = is_rx;
+}
+
+static inline bool skb_is_rx(struct sk_buff *skb)
+{
+	struct ogma_skb_cb *cb = (struct ogma_skb_cb *)skb->cb;
+
+	return cb->is_rx;
+}
+
+extern const struct net_device_ops ogma_netdev_ops;
+extern const struct ethtool_ops ogma_ethtool_ops;
+
+int ogma_start_gmac(struct ogma_priv *priv);
+int ogma_stop_gmac(struct ogma_priv *priv);
+int ogma_mii_register(struct ogma_priv *priv);
+void ogma_mii_unregister(struct ogma_priv *priv);
+int ogma_start_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+void ogma_stop_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+u16 ogma_get_rx_num(struct ogma_priv *priv);
+u16 ogma_get_tx_avail_num(struct ogma_priv *priv);
+int ogma_clean_tx_desc_ring(struct ogma_priv *priv);
+int ogma_clean_rx_desc_ring(struct ogma_priv *priv);
+int ogma_set_tx_pkt_data(struct ogma_priv *priv,
+			 const struct ogma_tx_pkt_ctrl *tx_ctrl, u8 count_frags,
+			 const struct ogma_frag_info *info,
+			 struct sk_buff *skb);
+int ogma_get_rx_pkt_data(struct ogma_priv *priv,
+			 struct ogma_rx_pkt_info *rxpi,
+			 struct ogma_frag_info *frag, u16 *len,
+			 struct sk_buff **skb);
+void ogma_ring_irq_enable(struct ogma_priv *priv, enum ogma_rings id, u32 i);
+void ogma_ring_irq_disable(struct ogma_priv *priv, enum ogma_rings id, u32 i);
+int ogma_alloc_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+void ogma_free_desc_ring(struct ogma_priv *priv, struct ogma_desc_ring *desc);
+int ogma_setup_rx_desc(struct ogma_priv *priv,
+		       struct ogma_desc_ring *desc);
+int ogma_netdev_napi_poll(struct napi_struct *napi_p, int budget);
+
+#endif /* OGMA_INTERNAL_H */
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c b/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
new file mode 100644
index 0000000..151d60a
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
@@ -0,0 +1,627 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+
+#include "ogma.h"
+
+static const u32 ads_irq_set[] = {
+	OGMA_REG_NRM_TX_INTEN_SET,
+	OGMA_REG_NRM_RX_INTEN_SET,
+};
+
+static const u32 desc_ring_irq_inten_clr_reg_addr[] = {
+	OGMA_REG_NRM_TX_INTEN_CLR,
+	OGMA_REG_NRM_RX_INTEN_CLR,
+};
+
+static const u32 int_tmr_reg_addr[] = {
+	OGMA_REG_NRM_TX_TXINT_TMR,
+	OGMA_REG_NRM_RX_RXINT_TMR,
+};
+
+static const u32 rx_pkt_cnt_reg_addr[] = {
+	0,
+	OGMA_REG_NRM_RX_PKTCNT,
+};
+
+static const u32 tx_pkt_cnt_reg_addr[] = {
+	OGMA_REG_NRM_TX_PKTCNT,
+	0,
+};
+
+static const u32 int_pkt_cnt_reg_addr[] = {
+	OGMA_REG_NRM_TX_DONE_TXINT_PKTCNT,
+	OGMA_REG_NRM_RX_RXINT_PKTCNT,
+};
+
+static const u32 tx_done_pkt_addr[] = {
+	OGMA_REG_NRM_TX_DONE_PKTCNT,
+	0,
+};
+
+static const u32 ogma_desc_mask[] = {
+	[OGMA_RING_TX] = OGMA_GMAC_OMR_REG_ST,
+	[OGMA_RING_RX] = OGMA_GMAC_OMR_REG_SR
+};
+
+static void ogma_check_desc_sanity(const struct ogma_desc_ring *desc,
+				   u16 idx, unsigned int expected_own)
+{
+	u32 tmp = *(u32 *)(desc->ring_vaddr + desc->len * idx);
+
+	BUG_ON((tmp >> 31) != expected_own);
+}
+
+void ogma_ring_irq_enable(struct ogma_priv *priv, enum ogma_rings id, u32 irqf)
+{
+	ogma_writel(priv, ads_irq_set[id], irqf);
+}
+
+void ogma_ring_irq_disable(struct ogma_priv *priv, enum ogma_rings id, u32 irqf)
+{
+	ogma_writel(priv, desc_ring_irq_inten_clr_reg_addr[id], irqf);
+}
+
+static struct sk_buff *alloc_rx_pkt_buf(struct ogma_priv *priv,
+					struct ogma_frag_info *info)
+{
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb_ip_align(priv->net_device, info->len);
+	if (!skb)
+		return NULL;
+
+	ogma_mark_skb_type(skb, OGMA_RING_RX);
+	info->addr = skb->data;
+	info->dma_addr = dma_map_single(priv->dev, info->addr, info->len,
+					DMA_FROM_DEVICE);
+	if (dma_mapping_error(priv->dev, info->dma_addr)) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+
+	return skb;
+}
+
+int ogma_alloc_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	desc->id = id;
+	desc->len = sizeof(struct ogma_tx_de); /* rx and tx desc same size */
+
+	spin_lock_init(&desc->spinlock_desc);
+
+	desc->ring_vaddr = dma_zalloc_coherent(priv->dev, desc->len * DESC_NUM,
+					      &desc->desc_phys, GFP_KERNEL);
+	if (!desc->ring_vaddr) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->frag = kcalloc(DESC_NUM, sizeof(*desc->frag), GFP_KERNEL);
+	if (!desc->frag) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->priv = kcalloc(DESC_NUM, sizeof(struct sk_buff *), GFP_KERNEL);
+	if (!desc->priv) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ogma_free_desc_ring(priv, desc);
+
+	return ret;
+}
+
+static void ogma_uninit_pkt_desc_ring(struct ogma_priv *priv,
+				      struct ogma_desc_ring *desc)
+{
+	struct ogma_frag_info *frag;
+	u32 status;
+	u16 idx;
+
+	for (idx = 0; idx < DESC_NUM; idx++) {
+		frag = &desc->frag[idx];
+		if (!frag->addr)
+			continue;
+
+		status = *(u32 *)(desc->ring_vaddr + desc->len * idx);
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 skb_is_rx(desc->priv[idx]) ? DMA_FROM_DEVICE :
+							      DMA_TO_DEVICE);
+		if ((status >> OGMA_TX_LAST) & 1)
+			dev_kfree_skb(desc->priv[idx]);
+	}
+
+	memset(desc->frag, 0, sizeof(struct ogma_frag_info) * DESC_NUM);
+	memset(desc->priv, 0, sizeof(struct sk_buff *) * DESC_NUM);
+	memset(desc->ring_vaddr, 0, desc->len * DESC_NUM);
+}
+
+void ogma_free_desc_ring(struct ogma_priv *priv, struct ogma_desc_ring *desc)
+{
+	if (desc->ring_vaddr && desc->frag && desc->priv)
+		ogma_uninit_pkt_desc_ring(priv, desc);
+
+	if (desc->ring_vaddr) {
+		dma_free_coherent(priv->dev, desc->len * DESC_NUM,
+				  desc->ring_vaddr, desc->desc_phys);
+		desc->ring_vaddr = NULL;
+	}
+	kfree(desc->frag);
+	desc->frag = NULL;
+	kfree(desc->priv);
+	desc->priv = NULL;
+}
+
+static void ogma_set_rx_de(struct ogma_priv *priv,
+			   struct ogma_desc_ring *desc, u16 idx,
+			   const struct ogma_frag_info *info,
+			   struct sk_buff *skb)
+{
+	struct ogma_rx_de *de = desc->ring_vaddr + desc->len * idx;
+	u32 attr = 1 << OGMA_RX_PKT_OWN_FIELD | 1 << OGMA_RX_PKT_FS_FIELD |
+			       1 << OGMA_RX_PKT_LS_FIELD;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+
+	if (idx == DESC_NUM - 1)
+		attr |= 1 << OGMA_RX_PKT_LD_FIELD;
+
+	de->data_buf_addr = info->dma_addr;
+	de->buf_len_info = info->len;
+	de->reserved = 0;
+	/* desc->attr makes the descriptor live, so it must be physically
+	 * written last after the rest of the descriptor body is already there
+	 */
+	wmb();
+	de->attr = attr;
+
+	desc->frag[idx].dma_addr = info->dma_addr;
+	desc->frag[idx].addr = info->addr;
+	desc->frag[idx].len = info->len;
+
+	desc->priv[idx] = skb;
+}
+
+int ogma_setup_rx_desc(struct ogma_priv *priv, struct ogma_desc_ring *desc)
+{
+	struct ogma_frag_info info;
+	struct sk_buff *skb;
+	int n;
+
+	info.len = priv->rx_pkt_buf_len;
+
+	for (n = 0; n < DESC_NUM; n++) {
+		skb = alloc_rx_pkt_buf(priv, &info);
+		if (!skb) {
+			ogma_uninit_pkt_desc_ring(priv, desc);
+			return -ENOMEM;
+		}
+		ogma_set_rx_de(priv, desc, n, &info, skb);
+	}
+
+	return 0;
+}
+
+static void ogma_set_tx_desc_entry(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc,
+				   const struct ogma_tx_pkt_ctrl *tx_ctrl,
+				   bool first_flag, bool last_flag,
+				   const struct ogma_frag_info *frag,
+				   struct sk_buff *skb)
+{
+	struct ogma_tx_de tx_desc_entry;
+	int idx = desc->head;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+
+	memset(&tx_desc_entry, 0, sizeof(struct ogma_tx_de));
+
+	tx_desc_entry.attr = 1 << OGMA_TX_SHIFT_OWN_FIELD |
+			     (idx == (DESC_NUM - 1)) << OGMA_TX_SHIFT_LD_FIELD |
+			     desc->id << OGMA_TX_SHIFT_DRID_FIELD |
+			     1 << OGMA_TX_SHIFT_PT_FIELD |
+			     OGMA_RING_GMAC << OGMA_TX_SHIFT_TDRID_FIELD |
+			     first_flag << OGMA_TX_SHIFT_FS_FIELD |
+			     last_flag << OGMA_TX_LAST |
+			     tx_ctrl->cksum_offload_flag << OGMA_TX_SHIFT_CO |
+			     tx_ctrl->tcp_seg_offload_flag << OGMA_TX_SHIFT_SO |
+			     1 << OGMA_TX_SHIFT_TRS_FIELD;
+
+	tx_desc_entry.data_buf_addr = frag->dma_addr;
+	tx_desc_entry.buf_len_info = (tx_ctrl->tcp_seg_len << 16) | frag->len;
+
+	memcpy(desc->ring_vaddr + (desc->len * idx), &tx_desc_entry, desc->len);
+
+	desc->frag[idx].dma_addr = frag->dma_addr;
+	desc->frag[idx].addr = frag->addr;
+	desc->frag[idx].len = frag->len;
+
+	desc->priv[idx] = skb;
+}
+
+static void ogma_get_rx_de(struct ogma_priv *priv,
+			   struct ogma_desc_ring *desc, u16 idx,
+			   struct ogma_rx_pkt_info *rxpi,
+			   struct ogma_frag_info *frag, u16 *len,
+			   struct sk_buff **skb)
+{
+	struct ogma_rx_de de;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+	memset(&de, 0, sizeof(struct ogma_rx_de));
+	memset(rxpi, 0, sizeof(struct ogma_rx_pkt_info));
+	memcpy(&de, ((void *)desc->ring_vaddr + desc->len * idx), desc->len);
+
+	dev_dbg(priv->dev, "%08x\n", *(u32 *)&de);
+	*len = de.buf_len_info >> 16;
+
+	rxpi->is_fragmented = (de.attr >> OGMA_RX_PKT_FR_FIELD) & 1;
+	rxpi->err_flag = (de.attr >> OGMA_RX_PKT_ER_FIELD) & 1;
+	rxpi->rx_cksum_result = (de.attr >> OGMA_RX_PKT_CO_FIELD) & 3;
+	rxpi->err_code = (de.attr >> OGMA_RX_PKT_ERR_FIELD) &
+							OGMA_RX_PKT_ERR_MASK;
+	memcpy(frag, &desc->frag[idx], sizeof(*frag));
+	*skb = desc->priv[idx];
+}
+
+static void ogma_inc_desc_head_idx(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc, u16 inc)
+{
+	u32 sum;
+
+	if ((desc->tail > desc->head) || desc->full)
+		BUG_ON(inc > (desc->tail - desc->head));
+	else
+		BUG_ON(inc > (DESC_NUM + desc->tail - desc->head));
+
+	sum = desc->head + inc;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->head = sum;
+	desc->full = desc->head == desc->tail;
+}
+
+static void ogma_inc_desc_tail_idx(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc)
+{
+	u32 sum;
+
+	if ((desc->head >= desc->tail) && (!desc->full))
+		BUG_ON(1 > (desc->head - desc->tail));
+	else
+		BUG_ON(1 > (DESC_NUM + desc->head - desc->tail));
+
+	sum = desc->tail + 1;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->tail = sum;
+	desc->full = false;
+}
+
+static u16 ogma_get_tx_avail_num_sub(struct ogma_priv *priv,
+				     const struct ogma_desc_ring *desc)
+{
+	if (desc->full)
+		return 0;
+
+	if (desc->tail > desc->head)
+		return desc->tail - desc->head;
+
+	return DESC_NUM + desc->tail - desc->head;
+}
+
+static u16 ogma_get_tx_done_num_sub(struct ogma_priv *priv,
+				    struct ogma_desc_ring *desc)
+{
+	desc->tx_done_num += ogma_readl(priv, tx_done_pkt_addr[desc->id]);
+
+	return desc->tx_done_num;
+}
+
+static int ogma_set_irq_coalesce_param(struct ogma_priv *priv,
+				       enum ogma_rings id)
+{
+	int max_frames, tmr;
+
+	switch (id) {
+	case OGMA_RING_TX:
+		max_frames = priv->et_coalesce.tx_max_coalesced_frames;
+		tmr = priv->et_coalesce.tx_coalesce_usecs;
+		break;
+	case OGMA_RING_RX:
+		max_frames = priv->et_coalesce.rx_max_coalesced_frames;
+		tmr = priv->et_coalesce.rx_coalesce_usecs;
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	ogma_writel(priv, int_pkt_cnt_reg_addr[id], max_frames);
+	ogma_writel(priv, int_tmr_reg_addr[id], ((tmr != 0) << 31) | tmr);
+
+	return 0;
+}
+
+int ogma_start_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	spin_lock_bh(&desc->spinlock_desc);
+
+	if (desc->running) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	switch (desc->id) {
+	case OGMA_RING_RX:
+		ogma_writel(priv, ads_irq_set[id], OGMA_IRQ_RCV);
+		break;
+	case OGMA_RING_TX:
+		ogma_writel(priv, ads_irq_set[id], OGMA_IRQ_EMPTY);
+		break;
+	}
+
+	ogma_set_irq_coalesce_param(priv, desc->id);
+	desc->running = true;
+
+err:
+	spin_unlock_bh(&desc->spinlock_desc);
+
+	return ret;
+}
+
+void ogma_stop_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+
+	spin_lock_bh(&desc->spinlock_desc);
+	if (desc->running)
+		ogma_writel(priv, desc_ring_irq_inten_clr_reg_addr[id],
+			    OGMA_IRQ_RCV | OGMA_IRQ_EMPTY | OGMA_IRQ_SND);
+
+	desc->running = false;
+	spin_unlock_bh(&desc->spinlock_desc);
+}
+
+u16 ogma_get_rx_num(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+	u32 result;
+
+	spin_lock(&desc->spinlock_desc);
+	if (desc->running) {
+		result = ogma_readl(priv, rx_pkt_cnt_reg_addr[OGMA_RING_RX]);
+		desc->rx_num += result;
+		if (result)
+			ogma_inc_desc_head_idx(priv, desc, result);
+	}
+	spin_unlock(&desc->spinlock_desc);
+
+	return desc->rx_num;
+}
+
+u16 ogma_get_tx_avail_num(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_TX];
+	u16 result;
+
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: not running tx desc\n", __func__);
+		result = 0;
+		goto err;
+	}
+
+	result = ogma_get_tx_avail_num_sub(priv, desc);
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return result;
+}
+
+int ogma_clean_tx_desc_ring(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_TX];
+	struct ogma_frag_info *frag;
+	struct ogma_tx_de *entry;
+	bool is_last;
+
+	spin_lock(&desc->spinlock_desc);
+
+	ogma_get_tx_done_num_sub(priv, desc);
+
+	while ((desc->tail != desc->head || desc->full) && desc->tx_done_num) {
+		frag = &desc->frag[desc->tail];
+		entry = desc->ring_vaddr + desc->len * desc->tail;
+		is_last = (entry->attr >> OGMA_TX_LAST) & 1;
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 DMA_TO_DEVICE);
+		if (is_last) {
+			priv->net_device->stats.tx_packets++;
+			priv->net_device->stats.tx_bytes +=
+						desc->priv[desc->tail]->len;
+			dev_kfree_skb(desc->priv[desc->tail]);
+		}
+		memset(frag, 0, sizeof(*frag));
+		ogma_inc_desc_tail_idx(priv, desc);
+
+		if (is_last) {
+			BUG_ON(!desc->tx_done_num);
+			desc->tx_done_num--;
+		}
+	}
+
+	spin_unlock(&desc->spinlock_desc);
+
+	return 0;
+}
+
+int ogma_clean_rx_desc_ring(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+
+	spin_lock(&desc->spinlock_desc);
+
+	while (desc->full || (desc->tail != desc->head)) {
+		ogma_set_rx_de(priv, desc, desc->tail, &desc->frag[desc->tail],
+			       desc->priv[desc->tail]);
+		desc->rx_num--;
+		ogma_inc_desc_tail_idx(priv, desc);
+	}
+
+	BUG_ON(desc->rx_num);	/* error check */
+
+	spin_unlock(&desc->spinlock_desc);
+
+	return 0;
+}
+
+int ogma_set_tx_pkt_data(struct ogma_priv *priv,
+			 const struct ogma_tx_pkt_ctrl *tx_ctrl, u8 count_frags,
+			 const struct ogma_frag_info *info, struct sk_buff *skb)
+{
+	struct ogma_desc_ring *desc;
+	u32 sum_len = 0;
+	unsigned int i;
+	int ret = 0;
+
+	if (tx_ctrl->tcp_seg_offload_flag && !tx_ctrl->cksum_offload_flag)
+		return -EINVAL;
+
+	if (tx_ctrl->tcp_seg_offload_flag) {
+		if (tx_ctrl->tcp_seg_len < OGMA_TCP_SEG_LEN_MIN)
+			return -EINVAL;
+
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (tx_ctrl->tcp_seg_len > OGMA_TCP_JUMBO_SEG_LEN_MAX)
+				return -EINVAL;
+		} else {
+			if (tx_ctrl->tcp_seg_len > OGMA_TCP_SEG_LEN_MAX)
+				return -EINVAL;
+		}
+	} else
+		if (tx_ctrl->tcp_seg_len)
+			return -EINVAL;
+
+	if (!count_frags)
+		return -ERANGE;
+
+	for (i = 0; i < count_frags; i++) {
+		if ((info[i].len == 0) || (info[i].len > 0xffff)) {
+			netif_err(priv, drv, priv->net_device,
+				  "%s: bad info len\n", __func__);
+			return -EINVAL;
+		}
+		sum_len += info[i].len;
+	}
+
+	if (!tx_ctrl->tcp_seg_offload_flag) {
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (sum_len > OGMA_MAX_TX_JUMBO_PKT_LEN)
+				return -EINVAL;
+		} else
+			if (sum_len > OGMA_MAX_TX_PKT_LEN)
+				return -EINVAL;
+	}
+
+	desc = &priv->desc_ring[OGMA_RING_TX];
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		ret = -ENODEV;
+		goto end;
+	}
+
+	smp_rmb(); /* we need to see a consistent view of pending tx count */
+	if (count_frags > ogma_get_tx_avail_num_sub(priv, desc)) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	for (i = 0; i < count_frags; i++) {
+		ogma_set_tx_desc_entry(priv, desc, tx_ctrl, i == 0,
+				       i == count_frags - 1, &info[i], skb);
+		ogma_inc_desc_head_idx(priv, desc, 1);
+	}
+
+	wmb(); /* ensure the descriptor is flushed */
+	ogma_writel(priv, tx_pkt_cnt_reg_addr[OGMA_RING_TX], 1);
+
+end:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
+
+int ogma_get_rx_pkt_data(struct ogma_priv *priv,
+			 struct ogma_rx_pkt_info *rxpi,
+			 struct ogma_frag_info *frag, u16 *len,
+			 struct sk_buff **skb)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+	struct ogma_frag_info info;
+	struct sk_buff *tmp_skb;
+	int ret = 0;
+
+	spin_lock(&desc->spinlock_desc);
+	BUG_ON(!desc->running);
+
+	if (desc->rx_num == 0) {
+		dev_err(priv->dev, "%s 0 len rx\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	info.len = priv->rx_pkt_buf_len;
+	rmb(); /* we need to ensure we only see current data in descriptor */
+	tmp_skb = alloc_rx_pkt_buf(priv, &info);
+	if (!tmp_skb) {
+		ogma_set_rx_de(priv, desc, desc->tail, &desc->frag[desc->tail],
+			       desc->priv[desc->tail]);
+		ret = -ENOMEM;
+	} else {
+		ogma_get_rx_de(priv, desc, desc->tail, rxpi, frag, len, skb);
+		ogma_set_rx_de(priv, desc, desc->tail, &info, tmp_skb);
+	}
+
+	ogma_inc_desc_tail_idx(priv, desc);
+	desc->rx_num--;
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c b/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
new file mode 100644
index 0000000..e0c441f
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
@@ -0,0 +1,95 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include "ogma.h"
+
+static void ogma_et_get_drvinfo(struct net_device *net_device,
+				struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, "ogma", sizeof(info->driver));
+	strlcpy(info->bus_info, dev_name(net_device->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static int ogma_et_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	if (!priv->phydev)
+		return -ENODEV;
+
+	return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int ogma_et_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	if (!priv->phydev)
+		return -ENODEV;
+
+	return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int ogma_et_get_coalesce(struct net_device *net_device,
+				struct ethtool_coalesce *et_coalesce)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	*et_coalesce = priv->et_coalesce;
+
+	return 0;
+}
+static int ogma_et_set_coalesce(struct net_device *net_device,
+				struct ethtool_coalesce *et_coalesce)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	if (et_coalesce->rx_max_coalesced_frames > OGMA_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (et_coalesce->tx_max_coalesced_frames > OGMA_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (!et_coalesce->rx_max_coalesced_frames)
+		return -EINVAL;
+	if (!et_coalesce->tx_max_coalesced_frames)
+		return -EINVAL;
+
+	priv->et_coalesce = *et_coalesce;
+
+	return 0;
+}
+
+static u32 ogma_et_get_msglevel(struct net_device *dev)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	return priv->msg_enable;
+}
+
+static void ogma_et_set_msglevel(struct net_device *dev, u32 datum)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	priv->msg_enable = datum;
+}
+
+const struct ethtool_ops ogma_ethtool_ops = {
+	.get_drvinfo		= ogma_et_get_drvinfo,
+	.get_settings		= ogma_et_get_settings,
+	.set_settings		= ogma_et_set_settings,
+	.get_link		= ethtool_op_get_link,
+	.get_coalesce		= ogma_et_get_coalesce,
+	.set_coalesce		= ogma_et_set_coalesce,
+	.get_msglevel		= ogma_et_get_msglevel,
+	.set_msglevel		= ogma_et_set_msglevel,
+};
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c b/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
new file mode 100644
index 0000000..59fb67b
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
@@ -0,0 +1,295 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#include "ogma.h"
+
+#define TIMEOUT_SPINS_MAC 1000000
+
+static u32 ogma_clk_type(u32 freq)
+{
+	if (freq < 35 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_25_35_MHZ;
+	if (freq < 60 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_35_60_MHZ;
+	if (freq < 100 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_60_100_MHZ;
+	if (freq < 150 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_100_150_MHZ;
+	if (freq < 250 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_150_250_MHZ;
+
+	return OGMA_GMAC_GAR_REG_CR_250_300_MHZ;
+}
+
+static int ogma_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+
+	while (--timeout && ogma_readl(priv, addr) & mask)
+		;
+	if (!timeout) {
+		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ogma_mac_write(struct ogma_priv *priv, u32 addr, u32 value)
+{
+	ogma_writel(priv, MAC_REG_DATA, value);
+	ogma_writel(priv, MAC_REG_CMD, addr | OGMA_GMAC_CMD_ST_WRITE);
+	return ogma_wait_while_busy(priv, MAC_REG_CMD, OGMA_GMAC_CMD_ST_BUSY);
+}
+
+static int ogma_mac_read(struct ogma_priv *priv, u32 addr, u32 *read)
+{
+	int ret;
+
+	ogma_writel(priv, MAC_REG_CMD, addr | OGMA_GMAC_CMD_ST_READ);
+	ret = ogma_wait_while_busy(priv, MAC_REG_CMD, OGMA_GMAC_CMD_ST_BUSY);
+	if (ret)
+		return ret;
+
+	*read = ogma_readl(priv, MAC_REG_DATA);
+
+	return 0;
+}
+
+static int ogma_mac_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+	int ret, data;
+
+	do {
+		ret = ogma_mac_read(priv, addr, &data);
+		if (ret)
+			break;
+	} while (--timeout && (data & mask));
+
+	if (!timeout || ret) {
+		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ogma_mac_update_to_phy_state(struct ogma_priv *priv)
+{
+	u32 value = 0;
+
+	if (priv->phydev->speed != SPEED_1000)
+		value = OGMA_MCR_PS;
+	if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+		return -ETIMEDOUT;
+
+	value = priv->phydev->duplex ? OGMA_GMAC_MCR_REG_FULL_DUPLEX_COMMON :
+				       OGMA_GMAC_MCR_REG_HALF_DUPLEX_COMMON;
+
+	if (priv->phydev->speed != SPEED_1000)
+		value |= OGMA_MCR_PS;
+
+	if ((priv->phy_interface != PHY_INTERFACE_MODE_GMII) &&
+	    (priv->phydev->speed == SPEED_100))
+		value |= OGMA_GMAC_MCR_REG_FES;
+
+	value |= OGMA_GMAC_MCR_REG_CST | OGMA_GMAC_MCR_REG_JE;
+	if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+		return -ETIMEDOUT;
+
+	priv->actual_link_speed = priv->phydev->speed;
+	priv->actual_duplex = priv->phydev->duplex;
+	netif_info(priv, drv, priv->net_device, "%s: %uMbps, duplex:%d\n",
+		   __func__, priv->phydev->speed, priv->phydev->duplex);
+
+	return 0;
+}
+
+/* NB ogma_start_gmac() only called from adjust_link */
+
+int ogma_start_gmac(struct ogma_priv *priv)
+{
+	u32 value = 0;
+	int ret;
+
+	if (priv->desc_ring[OGMA_RING_TX].running &&
+	    priv->desc_ring[OGMA_RING_RX].running)
+		return 0;
+
+	if (!priv->desc_ring[OGMA_RING_RX].running &&
+	    !priv->desc_ring[OGMA_RING_TX].running) {
+		if (priv->phydev->speed != SPEED_1000)
+			value = OGMA_MCR_PS;
+		if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_BMR, OGMA_GMAC_BMR_REG_RESET))
+			return -ETIMEDOUT;
+		/* Wait soft reset */
+		usleep_range(1000, 5000);
+
+		ret = ogma_mac_read(priv, GMAC_REG_BMR, &value);
+		if (ret)
+			return ret;
+		if (value & OGMA_GMAC_BMR_REG_SWR)
+			return -EAGAIN;
+
+		ogma_writel(priv, MAC_REG_DESC_SOFT_RST, 1);
+		if (ogma_wait_while_busy(priv, MAC_REG_DESC_SOFT_RST, 1))
+			return -ETIMEDOUT;
+
+		ogma_writel(priv, MAC_REG_DESC_INIT, 1);
+		if (ogma_wait_while_busy(priv, MAC_REG_DESC_INIT, 1))
+			return -ETIMEDOUT;
+
+		if (ogma_mac_write(priv, GMAC_REG_BMR,
+				   OGMA_GMAC_BMR_REG_COMMON))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_RDLAR, priv->rdlar_pa))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_TDLAR, priv->tdlar_pa))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_MFFR, 0x80000001))
+			return -ETIMEDOUT;
+
+		ret = ogma_mac_update_to_phy_state(priv);
+		if (ret)
+			return ret;
+
+		if (priv->mac_mode.flow_ctrl_enable_flag) {
+			ogma_writel(priv, MAC_REG_FLOW_TH,
+				    (priv->mac_mode.flow_stop_th << 16) |
+				    priv->mac_mode.flow_start_th);
+			if (ogma_mac_write(priv, GMAC_REG_FCR,
+					   (priv->mac_mode.pause_time << 16) |
+					   OGMA_FCR_RFE | OGMA_FCR_TFE))
+				return -ETIMEDOUT;
+		}
+	}
+
+	ret = ogma_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (!priv->desc_ring[OGMA_RING_RX].running) {
+		value |= OGMA_GMAC_OMR_REG_SR;
+		ogma_start_desc_ring(priv, OGMA_RING_RX);
+	}
+	if (!priv->desc_ring[OGMA_RING_TX].running) {
+		value |= OGMA_GMAC_OMR_REG_ST;
+		ogma_start_desc_ring(priv, OGMA_RING_TX);
+	}
+
+	if (ogma_mac_write(priv, GMAC_REG_OMR, value))
+		return -ETIMEDOUT;
+
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return 0;
+}
+
+int ogma_stop_gmac(struct ogma_priv *priv)
+{
+	u32 value;
+	int ret;
+
+	ret = ogma_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (priv->desc_ring[OGMA_RING_RX].running) {
+		value &= ~OGMA_GMAC_OMR_REG_SR;
+		ogma_stop_desc_ring(priv, OGMA_RING_RX);
+	}
+	if (priv->desc_ring[OGMA_RING_TX].running) {
+		value &= ~OGMA_GMAC_OMR_REG_ST;
+		ogma_stop_desc_ring(priv, OGMA_RING_TX);
+	}
+
+	priv->actual_link_speed = 0;
+	priv->actual_duplex = false;
+
+	return ogma_mac_write(priv, GMAC_REG_OMR, value);
+}
+
+static int ogma_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val)
+{
+	struct ogma_priv *priv = bus->priv;
+
+	if (ogma_mac_write(priv, GMAC_REG_GDR, val))
+		return -ETIMEDOUT;
+	if (ogma_mac_write(priv, GMAC_REG_GAR,
+			   phy_addr << OGMA_GMAC_GAR_REG_SHIFT_PA |
+			   reg << OGMA_GMAC_GAR_REG_SHIFT_GR |
+			   ogma_clk_type(priv->freq) << GMAC_REG_SHIFT_CR_GAR |
+			   OGMA_GMAC_GAR_REG_GW | OGMA_GMAC_GAR_REG_GB))
+		return -ETIMEDOUT;
+
+	return ogma_mac_wait_while_busy(priv, GMAC_REG_GAR,
+					OGMA_GMAC_GAR_REG_GB);
+}
+
+static int ogma_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr)
+{
+	struct ogma_priv *priv = bus->priv;
+	u32 data;
+	int ret;
+
+	if (ogma_mac_write(priv, GMAC_REG_GAR, OGMA_GMAC_GAR_REG_GB |
+			   phy_addr << OGMA_GMAC_GAR_REG_SHIFT_PA |
+			   reg_addr << OGMA_GMAC_GAR_REG_SHIFT_GR |
+			   ogma_clk_type(priv->freq) << GMAC_REG_SHIFT_CR_GAR))
+		return -ETIMEDOUT;
+
+	if (ogma_mac_wait_while_busy(priv, GMAC_REG_GAR, OGMA_GMAC_GAR_REG_GB))
+		return 0;
+
+	ret = ogma_mac_read(priv, GMAC_REG_GDR, &data);
+	if (ret)
+		return ret;
+
+	return data;
+}
+
+int ogma_mii_register(struct ogma_priv *priv)
+{
+	struct mii_bus *bus = mdiobus_alloc();
+	struct resource res;
+	int ret;
+
+	if (!bus)
+		return -ENOMEM;
+
+	of_address_to_resource(priv->dev->of_node, 0, &res);
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", priv->dev->of_node->full_name);
+	bus->priv = priv;
+	bus->name = "Fujitsu OGMA MDIO";
+	bus->read = ogma_phy_read;
+	bus->write = ogma_phy_write;
+	bus->parent = priv->dev;
+	priv->mii_bus = bus;
+
+	ret = of_mdiobus_register(bus, priv->dev->of_node);
+	if (ret) {
+		mdiobus_free(bus);
+		return ret;
+	}
+
+	return 0;
+}
+
+void ogma_mii_unregister(struct ogma_priv *priv)
+{
+	mdiobus_unregister(priv->mii_bus);
+	mdiobus_free(priv->mii_bus);
+	priv->mii_bus = NULL;
+}
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c b/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
new file mode 100644
index 0000000..a10ffb8
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
@@ -0,0 +1,592 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include <linux/pm_runtime.h>
+
+#include "ogma.h"
+
+#define OGMA_PHY_SR_REG_AN_C			0x20
+#define OGMA_PHY_SR_REG_LINK			4
+
+#define SR_1GBIT				0x800
+
+#define OGMA_PHY_ANLPA_REG_TXF			0x100
+#define OGMA_PHY_ANLPA_REG_TXD			0x80
+#define OGMA_PHY_ANLPA_REG_TF			0x40
+#define OGMA_PHY_ANLPA_REG_TD			0x20
+
+#define OGMA_PHY_CTRL_REG_RESET			(1 << 15)
+#define OGMA_PHY_CTRL_REG_LOOPBACK		(1 << 14)
+#define OGMA_PHY_CTRL_REG_SPSEL_LSB		(1 << 13)
+#define OGMA_PHY_CTRL_REG_AUTO_NEGO_EN		(1 << 12)
+#define OGMA_PHY_CTRL_REG_POWER_DOWN		(1 << 11)
+#define OGMA_PHY_CTRL_REG_ISOLATE		(1 << 10)
+#define OGMA_PHY_CTRL_REG_RESTART_AUTO_NEGO	(1 << 9)
+#define OGMA_PHY_CTRL_REG_DUPLEX_MODE		(1 << 8)
+#define OGMA_PHY_CTRL_REG_COL_TEST		(1 << 7)
+#define OGMA_PHY_CTRL_REG_SPSEL_MSB		(1 << 6)
+#define OGMA_PHY_CTRL_REG_UNIDIR_EN		(1 << 5)
+
+#define MSC_1GBIT				(1 << 9)
+
+#define OGMA_PHY_ADDR_CTRL			0
+#define OGMA_PHY_ADDR_SR			1
+#define OGMA_PHY_ADDR_ANA			4
+#define OGMA_PHY_ADDR_ANLPA			5
+#define OGMA_PHY_ADDR_MSC			9
+#define OGMA_PHY_ADDR_1000BASE_SR		10
+
+#define WAIT_FW_RDY_TIMEOUT 50
+
+static const u32 desc_ring_irq_status_reg_addr[] = {
+	OGMA_REG_NRM_TX_STATUS,
+	OGMA_REG_NRM_RX_STATUS,
+};
+
+static const u32 desc_ads[] = {
+	OGMA_REG_NRM_TX_CONFIG,
+	OGMA_REG_NRM_RX_CONFIG,
+};
+
+static const u32 ogma_desc_start_reg_addr[] = {
+	OGMA_REG_NRM_TX_DESC_START,
+	OGMA_REG_NRM_RX_DESC_START,
+};
+
+static int ogma_wait_for_ring_config_ready(struct ogma_priv *priv, int ring)
+{
+	int timeout = WAIT_FW_RDY_TIMEOUT;
+
+	while (--timeout && (ogma_readl(priv, desc_ads[ring]) &
+			    (1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP)))
+		usleep_range(1000, 2000);
+
+	if (!timeout) {
+		netif_err(priv, hw, priv->net_device,
+			  "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static u32 ogma_calc_pkt_ctrl_reg_param(const struct ogma_pkt_ctrlaram
+					*pkt_ctrlaram_p)
+{
+	u32 param = OGMA_PKT_CTRL_REG_MODE_NRM;
+
+	if (pkt_ctrlaram_p->log_chksum_er_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_CHKSUM_ER;
+
+	if (pkt_ctrlaram_p->log_hd_imcomplete_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_HD_INCOMPLETE;
+
+	if (pkt_ctrlaram_p->log_hd_er_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_HD_ER;
+
+	return param;
+}
+
+static int ogma_configure_normal_mode(struct ogma_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	/* save scb set value  */
+	priv->scb_set_normal_tx_paddr = ogma_readl(priv,
+			ogma_desc_start_reg_addr[OGMA_RING_TX]);
+
+	/* set desc_start addr */
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_RX],
+		    priv->desc_ring[OGMA_RING_RX].desc_phys);
+
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_TX],
+		    priv->desc_ring[OGMA_RING_TX].desc_phys);
+
+	/* set normal tx desc ring config */
+	value = 0 << OGMA_REG_DESC_TMR_MODE |
+		(cpu_to_le32(1) == 1) << OGMA_REG_DESC_ENDIAN |
+		1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+	ogma_writel(priv, desc_ads[OGMA_RING_TX], value);
+
+	value = 0 << OGMA_REG_DESC_TMR_MODE |
+		(cpu_to_le32(1) == 1) << OGMA_REG_DESC_ENDIAN |
+		1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+	ogma_writel(priv, desc_ads[OGMA_RING_RX], value);
+
+	if (ogma_wait_for_ring_config_ready(priv, OGMA_RING_TX) ||
+	    ogma_wait_for_ring_config_ready(priv, OGMA_RING_RX))
+		return -ETIMEDOUT;
+
+	return ret;
+}
+
+static int ogma_change_mode_to_normal(struct ogma_priv *priv)
+{
+	u32 value;
+
+	priv->scb_pkt_ctrl_reg = ogma_readl(priv, OGMA_REG_PKT_CTRL);
+
+	value = ogma_calc_pkt_ctrl_reg_param(&priv->param.pkt_ctrlaram);
+
+	if (priv->param.use_jumbo_pkt_flag)
+		value |= OGMA_PKT_CTRL_REG_EN_JUMBO;
+
+	value |= OGMA_PKT_CTRL_REG_MODE_NRM;
+
+	/* change to normal mode */
+	ogma_writel(priv, OGMA_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	ogma_writel(priv, OGMA_REG_PKT_CTRL, value);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return 0;
+}
+
+static int ogma_change_mode_to_taiki(struct ogma_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_TX],
+		    priv->scb_set_normal_tx_paddr);
+
+	value = 1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+
+	ogma_writel(priv, desc_ads[OGMA_RING_TX], value);
+
+	if (ogma_wait_for_ring_config_ready(priv, OGMA_RING_TX))
+		return -ETIMEDOUT;
+
+	ogma_writel(priv, OGMA_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	ogma_writel(priv, OGMA_REG_PKT_CTRL, priv->scb_pkt_ctrl_reg);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return ret;
+}
+
+static int ogma_clear_modechange_irq(struct ogma_priv *priv, u32 value)
+{
+	ogma_writel(priv, OGMA_REG_MODE_TRANS_COMP_STATUS,
+		    (value & (OGMA_MODE_TRANS_COMP_IRQ_N2T |
+		    OGMA_MODE_TRANS_COMP_IRQ_T2N)));
+
+	return 0;
+}
+
+static int ogma_hw_configure_to_normal(struct ogma_priv *priv)
+{
+	int err;
+
+	err = ogma_configure_normal_mode(priv);
+	if (err) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: normal conf fail\n", __func__);
+		return err;
+	}
+	err = ogma_change_mode_to_normal(priv);
+	if (err) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: normal set fail\n", __func__);
+		return err;
+	}
+
+	return err;
+}
+
+static int ogma_hw_configure_to_taiki(struct ogma_priv *priv)
+{
+	int ret;
+
+	ret = ogma_change_mode_to_taiki(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: taiki set fail\n", __func__);
+		return ret;
+	}
+
+	/* Clear mode change complete IRQ */
+	ret = ogma_clear_modechange_irq(priv, OGMA_MODE_TRANS_COMP_IRQ_T2N |
+					OGMA_MODE_TRANS_COMP_IRQ_N2T);
+
+	if (ret)
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clear mode fail\n", __func__);
+
+	return ret;
+}
+
+static void ogma_ring_irq_clr(struct ogma_priv *priv,
+			      unsigned int id, u32 value)
+{
+	BUG_ON(id > OGMA_RING_MAX);
+
+	ogma_writel(priv, desc_ring_irq_status_reg_addr[id],
+		    value & (OGMA_IRQ_EMPTY | OGMA_IRQ_ERR));
+}
+
+static void ogma_napi_tx_processing(struct napi_struct *napi_p)
+{
+	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+	ogma_clean_tx_desc_ring(priv);
+
+	if (netif_queue_stopped(priv->net_device) &&
+	    ogma_get_tx_avail_num(priv) >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX)
+		netif_wake_queue(priv->net_device);
+}
+
+int ogma_netdev_napi_poll(struct napi_struct *napi_p, int budget)
+{
+	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
+	struct net_device *net_device = priv->net_device;
+	struct ogma_rx_pkt_info rx_info;
+	int ret, done = 0, rx_num = 0;
+	struct ogma_frag_info frag;
+	struct sk_buff *skb;
+	u16 len;
+
+	ogma_napi_tx_processing(napi_p);
+
+	while (done < budget) {
+		if (!rx_num) {
+			rx_num = ogma_get_rx_num(priv);
+			if (!rx_num)
+				break;
+		}
+		done++;
+		rx_num--;
+		ret = ogma_get_rx_pkt_data(priv, &rx_info, &frag, &len, &skb);
+		if (unlikely(ret == -ENOMEM)) {
+			netif_err(priv, drv, priv->net_device,
+				  "%s: rx fail %d\n", __func__, ret);
+			net_device->stats.rx_dropped++;
+			continue;
+		}
+		dma_unmap_single(priv->dev, frag.dma_addr, frag.len,
+				 DMA_FROM_DEVICE);
+		skb_put(skb, len);
+		skb->protocol = eth_type_trans(skb, priv->net_device);
+
+		if (priv->rx_cksum_offload_flag &&
+		    rx_info.rx_cksum_result == OGMA_RX_CKSUM_OK)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+		napi_gro_receive(napi_p, skb);
+
+		net_device->stats.rx_packets++;
+		net_device->stats.rx_bytes += len;
+	}
+
+	if (done == budget)
+		return budget;
+
+	napi_complete(napi_p);
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return done;
+}
+
+static netdev_tx_t ogma_netdev_start_xmit(struct sk_buff *skb,
+					  struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	struct ogma_tx_pkt_ctrl tx_ctrl;
+	u16 pend_tx, tso_seg_len = 0;
+	skb_frag_t *frag;
+	int count_frags;
+	int ret, i;
+
+	memset(&tx_ctrl, 0, sizeof(struct ogma_tx_pkt_ctrl));
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	BUG_ON(skb_shinfo(skb)->nr_frags >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX);
+	count_frags = skb_shinfo(skb)->nr_frags + 1;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL)
+		tx_ctrl.cksum_offload_flag = true;
+
+	if (skb_is_gso(skb)) {
+		tso_seg_len = skb_shinfo(skb)->gso_size;
+
+		BUG_ON(skb->ip_summed != CHECKSUM_PARTIAL);
+		BUG_ON(!tso_seg_len);
+		BUG_ON(tso_seg_len > (priv->param.use_jumbo_pkt_flag ?
+			    OGMA_TCP_JUMBO_SEG_LEN_MAX : OGMA_TCP_SEG_LEN_MAX));
+
+		if (tso_seg_len < OGMA_TCP_SEG_LEN_MIN) {
+			tso_seg_len = OGMA_TCP_SEG_LEN_MIN;
+
+			if (skb->data_len < OGMA_TCP_SEG_LEN_MIN)
+				tso_seg_len = 0;
+		}
+	}
+
+	if (tso_seg_len > 0) {
+		if (skb->protocol == htons(ETH_P_IP)) {
+			BUG_ON(!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4));
+
+			ip_hdr(skb)->tot_len = 0;
+			tcp_hdr(skb)->check =
+				~tcp_v4_check(0, ip_hdr(skb)->saddr,
+					      ip_hdr(skb)->daddr, 0);
+		} else {
+			BUG_ON(!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6));
+			ipv6_hdr(skb)->payload_len = 0;
+			tcp_hdr(skb)->check =
+				~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						 &ipv6_hdr(skb)->daddr,
+						 0, IPPROTO_TCP, 0);
+		}
+
+		tx_ctrl.tcp_seg_offload_flag = true;
+		tx_ctrl.tcp_seg_len = tso_seg_len;
+	}
+
+	priv->tx_info[0].dma_addr = dma_map_single(priv->dev, skb->data,
+					  skb_headlen(skb), DMA_TO_DEVICE);
+	if (dma_mapping_error(priv->dev, priv->tx_info[0].dma_addr)) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: DMA mapping failed\n", __func__);
+		return NETDEV_TX_OK;
+	}
+	priv->tx_info[0].addr = skb->data;
+	priv->tx_info[0].len = skb_headlen(skb);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		priv->tx_info[i + 1].dma_addr =
+			skb_frag_dma_map(priv->dev, frag, 0,
+					 skb_frag_size(frag), DMA_TO_DEVICE);
+		priv->tx_info[i + 1].addr = skb_frag_address(frag);
+		priv->tx_info[i + 1].len = frag->size;
+	}
+
+	ogma_mark_skb_type(skb, OGMA_RING_TX);
+
+	ret = ogma_set_tx_pkt_data(priv, &tx_ctrl, count_frags, priv->tx_info,
+				   skb);
+	if (ret) {
+		netif_info(priv, drv, priv->net_device,
+			   "set tx pkt failed %d\n", ret);
+		for (i = 0; i < count_frags; i++)
+			dma_unmap_single(priv->dev, priv->tx_info[i].dma_addr,
+					 priv->tx_info[i].len, DMA_TO_DEVICE);
+		net_device->stats.tx_dropped++;
+
+		return NETDEV_TX_OK;
+	}
+
+	spin_lock(&priv->tx_queue_lock);
+	pend_tx = ogma_get_tx_avail_num(priv);
+
+	if (pend_tx < OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX) {
+		ogma_ring_irq_enable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+		netif_stop_queue(net_device);
+		goto err;
+	}
+	if (pend_tx <= DESC_NUM - 2) {
+		ogma_ring_irq_enable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+		goto err;
+	}
+	ogma_ring_irq_disable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+err:
+	spin_unlock(&priv->tx_queue_lock);
+
+	return NETDEV_TX_OK;
+}
+
+static int ogma_netdev_set_features(struct net_device *net_device,
+				    netdev_features_t features)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	priv->rx_cksum_offload_flag = !!(features & NETIF_F_RXCSUM);
+
+	return 0;
+}
+
+static void ogma_phy_adjust_link(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	if (priv->actual_link_speed == priv->phydev->speed &&
+	    priv->actual_duplex == priv->phydev->duplex)
+		return;
+
+	ogma_stop_gmac(priv);
+	ogma_start_gmac(priv);
+}
+
+static irqreturn_t ogma_irq_handler(int irq, void *dev_id)
+{
+	struct ogma_priv *priv = dev_id;
+	u32 status = ogma_readl(priv, OGMA_REG_TOP_STATUS) &
+		     ogma_readl(priv, OGMA_REG_TOP_INTEN);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & (OGMA_IRQ_TX | OGMA_IRQ_RX)) {
+		ogma_writel(priv, OGMA_REG_INTEN_CLR,
+			    status & (OGMA_IRQ_TX | OGMA_IRQ_RX));
+		napi_schedule(&priv->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ogma_netdev_open(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	u32 scb_irq_temp;
+	int ret, n;
+
+	scb_irq_temp = ogma_readl(priv, OGMA_REG_TOP_INTEN);
+
+	for (n = 0; n <= OGMA_RING_MAX; n++) {
+		ret = ogma_alloc_desc_ring(priv, n);
+		if (ret) {
+			netif_err(priv, probe, priv->net_device,
+				  "%s: alloc ring failed\n", __func__);
+			goto err;
+		}
+	}
+
+	ret = ogma_setup_rx_desc(priv, &priv->desc_ring[OGMA_RING_RX]);
+	if (ret) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: fail setup ring\n", __func__);
+		goto err1;
+	}
+
+	pm_runtime_get_sync(priv->dev);
+
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, scb_irq_temp);
+
+	ret = ogma_hw_configure_to_normal(priv);
+	if (ret) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: normal fail %d\n", __func__, ret);
+		goto err1;
+	}
+
+	ret = request_irq(priv->net_device->irq, ogma_irq_handler,
+			  IRQF_SHARED, "ogma", priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device, "request_irq failed\n");
+		goto err1;
+	}
+	priv->irq_registered = true;
+
+	ret = ogma_clean_rx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clean rx desc fail\n", __func__);
+		goto err2;
+	}
+
+	ret = ogma_clean_tx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clean tx desc fail\n", __func__);
+		goto err2;
+	}
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	priv->phydev = of_phy_connect(priv->net_device, priv->phy_np,
+				      &ogma_phy_adjust_link, 0,
+				      priv->phy_interface);
+	if (!priv->phydev) {
+		netif_err(priv, link, priv->net_device, "missing PHY\n");
+		goto err2;
+	}
+
+	phy_start_aneg(priv->phydev);
+
+	ogma_ring_irq_disable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	ogma_start_gmac(priv);
+	napi_enable(&priv->napi);
+	netif_start_queue(net_device);
+
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return 0;
+
+err2:
+	pm_runtime_put_sync(priv->dev);
+	free_irq(priv->net_device->irq, priv);
+	priv->irq_registered = false;
+err1:
+	for (n = 0; n <= OGMA_RING_MAX; n++)
+		ogma_free_desc_ring(priv, &priv->desc_ring[n]);
+err:
+	ogma_writel(priv, OGMA_REG_INTEN_SET, scb_irq_temp);
+
+	pm_runtime_put_sync(priv->dev);
+
+	return ret;
+}
+
+static int ogma_netdev_stop(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	int n;
+
+	phy_stop(priv->phydev);
+	phy_disconnect(priv->phydev);
+	priv->phydev = NULL;
+
+	netif_stop_queue(priv->net_device);
+	napi_disable(&priv->napi);
+
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, ~0);
+	ogma_stop_gmac(priv);
+	BUG_ON(ogma_hw_configure_to_taiki(priv));
+
+	pm_runtime_put_sync(priv->dev);
+
+	for (n = 0; n <= OGMA_RING_MAX; n++)
+		ogma_free_desc_ring(priv, &priv->desc_ring[n]);
+
+	free_irq(priv->net_device->irq, priv);
+	priv->irq_registered = false;
+
+	return 0;
+}
+
+const struct net_device_ops ogma_netdev_ops = {
+	.ndo_open		= ogma_netdev_open,
+	.ndo_stop		= ogma_netdev_stop,
+	.ndo_start_xmit		= ogma_netdev_start_xmit,
+	.ndo_set_features	= ogma_netdev_set_features,
+	.ndo_change_mtu         = eth_change_mtu,
+	.ndo_set_mac_address    = eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
new file mode 100644
index 0000000..09071da
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
@@ -0,0 +1,333 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "ogma.h"
+
+#define OGMA_F_NETSEC_VER_MAJOR_NUM(x) (x & 0xffff0000)
+
+static int napi_weight = 64;
+unsigned short pause_time = 256;
+
+static int ogma_probe(struct platform_device *pdev)
+{
+	struct net_device *net_device;
+	struct ogma_priv *priv;
+	struct resource *res;
+	const u8 *mac;
+	const u32 *p;
+	u32 hw_ver;
+	int err;
+	int ret;
+
+	net_device = alloc_etherdev(sizeof(*priv));
+	if (!net_device)
+		return -ENOMEM;
+
+	priv = netdev_priv(net_device);
+	priv->net_device = net_device;
+	SET_NETDEV_DEV(priv->net_device, &pdev->dev);
+	platform_set_drvdata(pdev, priv);
+	priv->dev = &pdev->dev;
+
+	priv->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV |
+			   NETIF_MSG_LINK | NETIF_MSG_PROBE;
+
+	priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+	if (!priv->phy_np) {
+		netif_err(priv, probe, priv->net_device,
+			  "missing phy in DT\n");
+		goto err1;
+	}
+
+	mac = of_get_mac_address(pdev->dev.of_node);
+	if (mac)
+		ether_addr_copy(priv->net_device->dev_addr, mac);
+
+	priv->phy_interface = of_get_phy_mode(pdev->dev.of_node);
+	if (priv->phy_interface < 0) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: bad phy-if\n", __func__);
+		goto err1;
+	}
+
+	priv->ioaddr = of_iomap(priv->dev->of_node, 0);
+	if (!priv->ioaddr) {
+		netif_err(priv, probe, priv->net_device, "of_iomap() failed\n");
+		err = -EINVAL;
+		goto err1;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing rdlar resource\n");
+		goto err1;
+	}
+	priv->rdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing tdlar resource\n");
+		goto err1;
+	}
+	priv->tdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing IRQ resource\n");
+		goto err2;
+	}
+	priv->net_device->irq = res->start;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+	while (priv->clock_count < ARRAY_SIZE(priv->clk)) {
+		priv->clk[priv->clock_count] =
+			of_clk_get(pdev->dev.of_node, priv->clock_count);
+		if (IS_ERR(priv->clk[priv->clock_count])) {
+			if (!priv->clock_count) {
+				netif_err(priv, probe, priv->net_device,
+					  "Failed to get clock\n");
+				goto err3;
+			}
+			break;
+		}
+		priv->clock_count++;
+	}
+
+	/* disable by default */
+	priv->et_coalesce.rx_coalesce_usecs = 0;
+	priv->et_coalesce.rx_max_coalesced_frames = 1;
+	priv->et_coalesce.tx_coalesce_usecs = 0;
+	priv->et_coalesce.tx_max_coalesced_frames = 1;
+
+	pm_runtime_enable(&pdev->dev);
+	/* runtime_pm coverage just for probe, open/close also cover it */
+	pm_runtime_get_sync(&pdev->dev);
+
+	priv->param.use_jumbo_pkt_flag = false;
+	p = of_get_property(pdev->dev.of_node, "max-frame-size", NULL);
+	if (p)
+		priv->param.use_jumbo_pkt_flag = !!(be32_to_cpu(*p) > 8000);
+
+	hw_ver = ogma_readl(priv, OGMA_REG_F_TAIKI_VER);
+	/* this driver only supports F_TAIKI style OGMA */
+	if (OGMA_F_NETSEC_VER_MAJOR_NUM(hw_ver) !=
+	    OGMA_F_NETSEC_VER_MAJOR_NUM(OGMA_REG_OGMA_VER_F_TAIKI)) {
+		ret = -ENODEV;
+		goto err3;
+	}
+
+	if (priv->param.use_jumbo_pkt_flag)
+		priv->rx_pkt_buf_len = OGMA_RX_JUMBO_PKT_BUF_LEN;
+	else
+		priv->rx_pkt_buf_len = OGMA_RX_PKT_BUF_LEN;
+
+	dev_info(&pdev->dev, "IP rev %d.%d\n", hw_ver >> 16, hw_ver & 0xffff);
+
+	priv->mac_mode.flow_start_th = OGMA_FLOW_CONTROL_START_THRESHOLD;
+	priv->mac_mode.flow_stop_th = OGMA_FLOW_CONTROL_STOP_THRESHOLD;
+	priv->mac_mode.pause_time = pause_time;
+	priv->mac_mode.flow_ctrl_enable_flag = false;
+	priv->freq = clk_get_rate(priv->clk[0]);
+
+	netif_napi_add(priv->net_device, &priv->napi, ogma_netdev_napi_poll,
+		       napi_weight);
+
+	net_device->netdev_ops = &ogma_netdev_ops;
+	net_device->ethtool_ops = &ogma_ethtool_ops;
+	net_device->features = NETIF_F_SG | NETIF_F_IP_CSUM |
+			       NETIF_F_IPV6_CSUM | NETIF_F_TSO |
+			       NETIF_F_TSO6 | NETIF_F_GSO |
+			       NETIF_F_HIGHDMA | NETIF_F_RXCSUM;
+	priv->net_device->hw_features = priv->net_device->features;
+
+	priv->rx_cksum_offload_flag = true;
+	spin_lock_init(&priv->tx_queue_lock);
+
+	err = ogma_mii_register(priv);
+	if (err) {
+		netif_err(priv, probe, priv->net_device,
+			  "mii bus registration failed %d\n", err);
+		goto err3;
+	}
+
+	/* disable all other interrupt sources */
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, ~0);
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	err = register_netdev(priv->net_device);
+	if (err) {
+		netif_err(priv, probe, priv->net_device,
+			  "register_netdev() failed\n");
+		goto err4;
+	}
+
+	pm_runtime_put_sync_suspend(&pdev->dev);
+
+	netif_info(priv, probe, priv->net_device, "initialized\n");
+
+	return 0;
+
+err4:
+	ogma_mii_unregister(priv);
+
+err3:
+	pm_runtime_put_sync_suspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	while (priv->clock_count > 0) {
+		priv->clock_count--;
+		clk_put(priv->clk[priv->clock_count]);
+	}
+err2:
+	iounmap(priv->ioaddr);
+err1:
+	free_netdev(priv->net_device);
+
+	dev_err(&pdev->dev, "init failed\n");
+
+	return ret;
+}
+
+static int ogma_remove(struct platform_device *pdev)
+{
+	struct ogma_priv *priv = platform_get_drvdata(pdev);
+
+	unregister_netdev(priv->net_device);
+	ogma_mii_unregister(priv);
+	pm_runtime_disable(&pdev->dev);
+	iounmap(priv->ioaddr);
+	free_netdev(priv->net_device);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int ogma_runtime_suspend(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (priv->irq_registered)
+		disable_irq(priv->net_device->irq);
+
+	ogma_writel(priv, OGMA_REG_CLK_EN, 0);
+
+	for (n = priv->clock_count - 1; n >= 0; n--)
+		clk_disable_unprepare(priv->clk[n]);
+
+	return 0;
+}
+
+static int ogma_runtime_resume(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	/* first let the clocks back on */
+
+	for (n = 0; n < priv->clock_count; n++)
+		clk_prepare_enable(priv->clk[n]);
+
+	ogma_writel(priv, OGMA_REG_CLK_EN, OGMA_CLK_EN_REG_DOM_D |
+			OGMA_CLK_EN_REG_DOM_C | OGMA_CLK_EN_REG_DOM_G);
+
+	if (priv->irq_registered)
+		enable_irq(priv->net_device->irq);
+
+	return 0;
+}
+#endif
+
+static int ogma_pm_suspend(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return ogma_runtime_suspend(dev);
+}
+
+static int ogma_pm_resume(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return ogma_runtime_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ogma_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
+	SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
+};
+#endif
+
+static const struct of_device_id ogma_dt_ids[] = {
+	{.compatible = "fujitsu,ogma"},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ogma_dt_ids);
+
+static struct platform_driver ogma_driver = {
+	.probe = ogma_probe,
+	.remove = ogma_remove,
+	.driver = {
+		.name = "ogma",
+		.of_match_table = ogma_dt_ids,
+#ifdef CONFIG_PM
+		.pm = &ogma_pm_ops,
+#endif
+		},
+};
+
+module_platform_driver(ogma_driver);
+
+MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
+MODULE_DESCRIPTION("OGMA Ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("platform:ogma");
-- 
1.8.1.2

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

* [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
@ 2014-07-13  6:31   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:31 UTC (permalink / raw)
  To: linux-arm-kernel

This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
Ethernet + PHY IP used in a variety of their ARM-based ASICs.

This is being sent as part of a series including the arch support that uses it,
Fujitsu mb86s7x.

This driver was originally written by guys inside Fujitsu as an abstracted
"you can build this for Windows as well" type code, I've removed all that,
modernized various things, added runtime_pm, and ported it to work with
Device Tree, using only the bindings already mentioned in

./Documentation/devicetree/bindings/net/ethernet.txt

Bindings documentation is added by this patch.

Signed-off-by: Andy Green <andy.green@linaro.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
 drivers/net/ethernet/fujitsu/Kconfig               |  12 +
 drivers/net/ethernet/fujitsu/Makefile              |   1 +
 drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
 drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
 .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
 .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
 10 files changed, 2384 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
 create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

diff --git a/Documentation/devicetree/bindings/net/fujitsu-ogma.txt b/Documentation/devicetree/bindings/net/fujitsu-ogma.txt
new file mode 100644
index 0000000..1fd680f
--- /dev/null
+++ b/Documentation/devicetree/bindings/net/fujitsu-ogma.txt
@@ -0,0 +1,43 @@
+* Fujistu OGMA Ethernet Controller IP
+
+Required properties:
+- compatible: Should be "fujitsu,ogma"
+- reg: Address and length of the register sets, the first is the main
+	registers, then the rdlar and tdlar regions for the SoC
+- interrupts: Should contain ethernet controller interrupt
+- clocks: phandle to any clocks to be switched by runtime_pm
+- phy-mode: See ethernet.txt file in the same directory
+- max-speed: See ethernet.txt file in the same directory
+- max-frame-size: See ethernet.txt file in the same directory, if 9000 or
+	above jumbo frames are enabled
+- local-mac-address: See ethernet.txt file in the same directory
+- phy-handle: phandle to select child phy
+
+For the child phy
+
+- compatible "ethernet-phy-ieee802.3-c22" is needed
+- device_type "ethernet-phy"
+- reg: phy address
+
+
+Example:
+	eth0: f_taiki {
+                compatible = "fujitsu,ogma";
+		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
+		interrupts = <0 163 0x4>;
+		clocks = <&clk_alw_0_8>;
+		phy-mode = "rgmii";
+		max-speed = <1000>;
+		max-frame-size = <9000>;
+		local-mac-address = [ a4 17 31 00 00 ed ];
+		phy-handle = <&ethphy0>;
+
+		#address-cells = <1>;
+		#size-cells = <0>;
+
+		ethphy0: ethernet-phy at 1 {
+			device_type = "ethernet-phy";
+			compatible = "ethernet-phy-ieee802.3-c22";
+			reg = <1>;
+		};
+	};
diff --git a/drivers/net/ethernet/fujitsu/Kconfig b/drivers/net/ethernet/fujitsu/Kconfig
index 1085257..bbe3ee8 100644
--- a/drivers/net/ethernet/fujitsu/Kconfig
+++ b/drivers/net/ethernet/fujitsu/Kconfig
@@ -28,4 +28,16 @@ config PCMCIA_FMVJ18X
 	  To compile this driver as a module, choose M here: the module will be
 	  called fmvj18x_cs.  If unsure, say N.
 
+config NET_FUJITSU_OGMA
+	tristate "Fujitsu OGMA network support"
+	depends on OF
+	select PHYLIB
+	select MII
+help
+	  Enable for OGMA support of Fujitsu FGAMC4 IP
+	  Provides Gigabit ethernet support
+
+	  To compile this driver as a module, choose M here: the module will be
+	  called ogma.  If unsure, say N.
+
 endif # NET_VENDOR_FUJITSU
diff --git a/drivers/net/ethernet/fujitsu/Makefile b/drivers/net/ethernet/fujitsu/Makefile
index 21561fd..b90a445 100644
--- a/drivers/net/ethernet/fujitsu/Makefile
+++ b/drivers/net/ethernet/fujitsu/Makefile
@@ -3,3 +3,4 @@
 #
 
 obj-$(CONFIG_PCMCIA_FMVJ18X) += fmvj18x_cs.o
+obj-$(CONFIG_NET_FUJITSU_OGMA) += ogma/
diff --git a/drivers/net/ethernet/fujitsu/ogma/Makefile b/drivers/net/ethernet/fujitsu/ogma/Makefile
new file mode 100644
index 0000000..8661027
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/Makefile
@@ -0,0 +1,6 @@
+obj-m := ogma.o
+ogma-objs := ogma_desc_ring_access.o \
+		ogma_netdev.o \
+		ogma_ethtool.o \
+		ogma_platform.o \
+		ogma_gmac_access.o
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma.h b/drivers/net/ethernet/fujitsu/ogma/ogma.h
new file mode 100644
index 0000000..2fe2820
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma.h
@@ -0,0 +1,380 @@
+/**
+ * ogma.h
+ *
+ *  Copyright (C) 2011 - 2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#ifndef OGMA_INTERNAL_H
+#define OGMA_INTERNAL_H
+
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/phy.h>
+#include <linux/ethtool.h>
+#include <linux/of_address.h>
+#include <linux/of_mdio.h>
+#include <linux/etherdevice.h>
+#include <net/sock.h>
+
+#define OGMA_FLOW_CONTROL_START_THRESHOLD	36
+#define OGMA_FLOW_CONTROL_STOP_THRESHOLD	48
+
+#define OGMA_CLK_MHZ				1000000
+
+#define OGMA_RX_PKT_BUF_LEN			1522
+#define OGMA_RX_JUMBO_PKT_BUF_LEN		9022
+
+#define OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX		19
+
+#define DESC_NUM 128
+
+#define OGMA_TX_SHIFT_OWN_FIELD			31
+#define OGMA_TX_SHIFT_LD_FIELD			30
+#define OGMA_TX_SHIFT_DRID_FIELD		24
+#define OGMA_TX_SHIFT_PT_FIELD			21
+#define OGMA_TX_SHIFT_TDRID_FIELD		16
+#define OGMA_TX_SHIFT_CC_FIELD			15
+#define OGMA_TX_SHIFT_FS_FIELD			9
+#define OGMA_TX_LAST				8
+#define OGMA_TX_SHIFT_CO			7
+#define OGMA_TX_SHIFT_SO			6
+#define OGMA_TX_SHIFT_TRS_FIELD			4
+#define OGMA_RX_PKT_OWN_FIELD			31
+#define OGMA_RX_PKT_LD_FIELD			30
+#define OGMA_RX_PKT_SDRID_FIELD			24
+#define OGMA_RX_PKT_FR_FIELD			23
+#define OGMA_RX_PKT_ER_FIELD			21
+#define OGMA_RX_PKT_ERR_FIELD			16
+#define OGMA_RX_PKT_TDRID_FIELD			12
+#define OGMA_RX_PKT_FS_FIELD			9
+#define OGMA_RX_PKT_LS_FIELD			8
+#define OGMA_RX_PKT_CO_FIELD			6
+
+#define OGMA_RX_PKT_ERR_MASK			3
+
+#define OGMA_MAX_TX_PKT_LEN			1518
+#define OGMA_MAX_TX_JUMBO_PKT_LEN		9018
+
+enum ogma_rings {
+	OGMA_RING_TX,
+	OGMA_RING_RX
+};
+
+#define OGMA_RING_GMAC				15
+#define OGMA_RING_MAX				1
+
+#define OGMA_TCP_SEG_LEN_MAX			1460
+#define OGMA_TCP_JUMBO_SEG_LEN_MAX		8960
+#define OGMA_TCP_SEG_LEN_MIN			536
+
+#define OGMA_RX_CKSUM_NOTAVAIL			0
+#define OGMA_RX_CKSUM_OK			1
+#define OGMA_RX_CKSUM_NG			2
+
+#define OGMA_TOP_IRQ_REG_CODE_LOAD_END		(1 << 20)
+#define OGMA_IRQ_TRANSITION_COMPLETE		(1 <<  4)
+#define OGMA_IRQ_RX				(1 <<  1)
+#define OGMA_IRQ_TX				(1 <<  0)
+
+#define OGMA_IRQ_EMPTY				(1 << 17)
+#define OGMA_IRQ_ERR				(1 << 16)
+#define OGMA_IRQ_PKT_CNT			(1 << 15)
+#define OGMA_IRQ_TIMEUP				(1 << 14)
+#define OGMA_IRQ_RCV			(OGMA_IRQ_PKT_CNT | OGMA_IRQ_TIMEUP)
+
+#define OGMA_IRQ_TX_DONE			(1 << 15)
+#define OGMA_IRQ_SND			(OGMA_IRQ_TX_DONE | OGMA_IRQ_TIMEUP)
+
+#define OGMA_MODE_TRANS_COMP_IRQ_N2T		(1 << 20)
+#define OGMA_MODE_TRANS_COMP_IRQ_T2N		(1 << 19)
+
+#define OGMA_DESC_MIN				2
+#define OGMA_DESC_MAX				2047
+#define OGMA_INT_PKTCNT_MAX			2047
+
+#define OGMA_FLOW_START_TH_MAX			383
+#define OGMA_FLOW_STOP_TH_MAX			383
+#define OGMA_FLOW_PAUSE_TIME_MIN		5
+
+#define OGMA_CLK_EN_REG_DOM_ALL			0x3f
+
+#define OGMA_REG_TOP_STATUS			0x80
+#define OGMA_REG_TOP_INTEN			0x81
+#define OGMA_REG_INTEN_SET			0x8d
+#define OGMA_REG_INTEN_CLR			0x8e
+#define OGMA_REG_NRM_TX_STATUS			0x100
+#define OGMA_REG_NRM_TX_INTEN			0x101
+#define OGMA_REG_NRM_TX_INTEN_SET		0x10a
+#define OGMA_REG_NRM_TX_INTEN_CLR		0x10b
+#define OGMA_REG_NRM_RX_STATUS			0x110
+#define OGMA_REG_NRM_RX_INTEN			0x111
+#define OGMA_REG_NRM_RX_INTEN_SET		0x11a
+#define OGMA_REG_NRM_RX_INTEN_CLR		0x11b
+#define OGMA_REG_RESERVED_RX_DESC_START		0x122
+#define OGMA_REG_RESERVED_TX_DESC_START		0x132
+#define OGMA_REG_CLK_EN				0x40
+#define OGMA_REG_SOFT_RST			0x41
+#define OGMA_REG_PKT_CTRL			0x50
+#define OGMA_REG_COM_INIT			0x48
+#define OGMA_REG_DMA_TMR_CTRL			0x83
+#define OGMA_REG_F_TAIKI_MC_VER			0x8b
+#define OGMA_REG_F_TAIKI_VER			0x8c
+#define OGMA_REG_DMA_HM_CTRL			0x85
+#define OGMA_REG_DMA_MH_CTRL			0x88
+#define OGMA_REG_NRM_TX_PKTCNT			0x104
+#define OGMA_REG_NRM_TX_DONE_TXINT_PKTCNT	0x106
+#define OGMA_REG_NRM_RX_RXINT_PKTCNT		0x116
+#define OGMA_REG_NRM_TX_TXINT_TMR		0x108
+#define OGMA_REG_NRM_RX_RXINT_TMR		0x118
+#define OGMA_REG_NRM_TX_DONE_PKTCNT		0x105
+#define OGMA_REG_NRM_RX_PKTCNT			0x115
+#define OGMA_REG_NRM_TX_TMR			0x107
+#define OGMA_REG_NRM_RX_TMR			0x117
+#define OGMA_REG_NRM_TX_DESC_START		0x102
+#define OGMA_REG_NRM_RX_DESC_START		0x112
+#define OGMA_REG_NRM_TX_CONFIG			0x10c
+#define OGMA_REG_NRM_RX_CONFIG			0x11c
+#define MAC_REG_DATA				0x470
+#define MAC_REG_CMD				0x471
+#define MAC_REG_FLOW_TH				0x473
+#define MAC_REG_INTF_SEL			0x475
+#define MAC_REG_DESC_INIT			0x47f
+#define MAC_REG_DESC_SOFT_RST			0x481
+#define OGMA_REG_MODE_TRANS_COMP_STATUS		0x140
+#define GMAC_REG_MCR				0x0000
+#define GMAC_REG_MFFR				0x0004
+#define GMAC_REG_GAR				0x0010
+#define GMAC_REG_GDR				0x0014
+#define GMAC_REG_FCR				0x0018
+#define GMAC_REG_BMR				0x1000
+#define GMAC_REG_RDLAR				0x100c
+#define GMAC_REG_TDLAR				0x1010
+#define GMAC_REG_OMR				0x1018
+
+#define OGMA_PKT_CTRL_REG_MODE_NRM		(1 << 28)
+#define OGMA_PKT_CTRL_REG_EN_JUMBO		(1 << 27)
+#define OGMA_PKT_CTRL_REG_LOG_CHKSUM_ER		(1 << 3)
+#define OGMA_PKT_CTRL_REG_LOG_HD_INCOMPLETE	(1 << 2)
+#define OGMA_PKT_CTRL_REG_LOG_HD_ER		(1 << 1)
+
+#define OGMA_CLK_EN_REG_DOM_G			(1 << 5)
+#define OGMA_CLK_EN_REG_DOM_C			(1 << 1)
+#define OGMA_CLK_EN_REG_DOM_D			(1 << 0)
+
+#define OGMA_COM_INIT_REG_PKT			(1 << 1)
+#define OGMA_COM_INIT_REG_CORE			(1 << 0)
+#define OGMA_COM_INIT_REG_ALL  (OGMA_COM_INIT_REG_CORE | OGMA_COM_INIT_REG_PKT)
+
+#define OGMA_SOFT_RST_REG_RESET			0
+#define OGMA_SOFT_RST_REG_RUN			(1 << 31)
+
+#define OGMA_DMA_CTRL_REG_STOP			1
+#define MH_CTRL__MODE_TRANS			(1 << 20)
+
+#define OGMA_GMAC_CMD_ST_READ			0
+#define OGMA_GMAC_CMD_ST_WRITE			(1 << 28)
+#define OGMA_GMAC_CMD_ST_BUSY			(1 << 31)
+
+#define OGMA_GMAC_BMR_REG_COMMON		(0x00412080)
+#define OGMA_GMAC_BMR_REG_RESET			(0x00020181)
+#define OGMA_GMAC_BMR_REG_SWR			(0x00000001)
+
+#define OGMA_GMAC_OMR_REG_ST			(1 << 13)
+#define OGMA_GMAC_OMR_REG_SR			(1 << 1)
+
+#define OGMA_GMAC_MCR_REG_CST			(1 << 25)
+#define OGMA_GMAC_MCR_REG_JE			(1 << 20)
+#define OGMA_MCR_PS				(1 << 15)
+#define OGMA_GMAC_MCR_REG_FES			(1 << 14)
+#define OGMA_GMAC_MCR_REG_FULL_DUPLEX_COMMON	(0x0000280c)
+#define OGMA_GMAC_MCR_REG_HALF_DUPLEX_COMMON	(0x0001a00c)
+
+#define OGMA_FCR_RFE				(1 << 2)
+#define OGMA_FCR_TFE				(1 << 1)
+
+#define OGMA_GMAC_GAR_REG_GW			(1 << 1)
+#define OGMA_GMAC_GAR_REG_GB			(1 << 0)
+
+#define OGMA_GMAC_GAR_REG_SHIFT_PA		11
+#define OGMA_GMAC_GAR_REG_SHIFT_GR		6
+#define GMAC_REG_SHIFT_CR_GAR			2
+
+#define OGMA_GMAC_GAR_REG_CR_25_35_MHZ		2
+#define OGMA_GMAC_GAR_REG_CR_35_60_MHZ		3
+#define OGMA_GMAC_GAR_REG_CR_60_100_MHZ		0
+#define OGMA_GMAC_GAR_REG_CR_100_150_MHZ	1
+#define OGMA_GMAC_GAR_REG_CR_150_250_MHZ	4
+#define OGMA_GMAC_GAR_REG_CR_250_300_MHZ	5
+
+#define OGMA_REG_OGMA_VER_F_TAIKI		0x20000
+
+#define OGMA_REG_DESC_RING_CONFIG_CFG_UP	31
+#define OGMA_REG_DESC_RING_CONFIG_CH_RST	30
+#define OGMA_REG_DESC_TMR_MODE			4
+#define OGMA_REG_DESC_ENDIAN			0
+
+#define OGMA_MAC_DESC_SOFT_RST_SOFT_RST		1
+#define OGMA_MAC_DESC_INIT_REG_INIT		1
+
+/* this is used to interpret a register layout */
+struct ogma_pkt_ctrlaram {
+	u8 log_chksum_er_flag:1;
+	u8 log_hd_imcomplete_flag:1;
+	u8 log_hd_er_flag:1;
+};
+
+struct ogma_param {
+	struct ogma_pkt_ctrlaram pkt_ctrlaram;
+	bool use_jumbo_pkt_flag;
+};
+
+struct ogma_mac_mode {
+	u16 flow_start_th;
+	u16 flow_stop_th;
+	u16 pause_time;
+	bool flow_ctrl_enable_flag;
+};
+
+struct ogma_desc_ring {
+	spinlock_t spinlock_desc; /* protect descriptor access */
+	phys_addr_t desc_phys;
+	struct ogma_frag_info *frag;
+	struct sk_buff **priv;
+	void *ring_vaddr;
+	enum ogma_rings id;
+	int len;
+	u16 tx_done_num;
+	u16 rx_num;
+	u16 head;
+	u16 tail;
+	bool running;
+	bool full;
+};
+
+struct ogma_frag_info {
+	dma_addr_t dma_addr;
+	void *addr;
+	u16 len;
+};
+
+struct ogma_priv {
+	struct ogma_desc_ring desc_ring[OGMA_RING_MAX + 1];
+	struct ethtool_coalesce et_coalesce;
+	struct ogma_mac_mode mac_mode;
+	struct ogma_param param;
+	struct napi_struct napi;
+	phys_addr_t rdlar_pa, tdlar_pa;
+	phy_interface_t phy_interface;
+	spinlock_t tx_queue_lock; /* protect transmit queue */
+	struct ogma_frag_info tx_info[MAX_SKB_FRAGS];
+	struct net_device *net_device;
+	struct device_node *phy_np;
+	struct phy_device *phydev;
+	struct mii_bus *mii_bus;
+	void __iomem *ioaddr;
+	struct device *dev;
+	struct clk *clk[3];
+	u32 scb_set_normal_tx_paddr;
+	u32 scb_pkt_ctrl_reg;
+	u32 rx_pkt_buf_len;
+	u32 msg_enable;
+	u32 freq;
+	int actual_link_speed;
+	int clock_count;
+	bool rx_cksum_offload_flag;
+	bool actual_duplex;
+	bool irq_registered;
+};
+
+struct ogma_tx_de {
+	u32 attr;
+	u32 data_buf_addr;
+	u32 buf_len_info;
+	u32 reserved;
+};
+
+struct ogma_rx_de {
+	u32 attr;
+	u32 data_buf_addr;
+	u32 buf_len_info;
+	u32 reserved;
+};
+
+struct ogma_tx_pkt_ctrl {
+	u16 tcp_seg_len;
+	bool tcp_seg_offload_flag;
+	bool cksum_offload_flag;
+};
+
+struct ogma_rx_pkt_info {
+	int rx_cksum_result;
+	int err_code;
+	bool is_fragmented;
+	bool err_flag;
+};
+
+struct ogma_skb_cb {
+	bool is_rx;
+};
+
+static inline void ogma_writel(struct ogma_priv *priv, u32 reg_addr, u32 val)
+{
+	writel(val, priv->ioaddr + (reg_addr << 2));
+}
+
+static inline u32 ogma_readl(struct ogma_priv *priv, u32 reg_addr)
+{
+	return readl(priv->ioaddr + (reg_addr << 2));
+}
+
+static inline void ogma_mark_skb_type(struct sk_buff *skb, bool is_rx)
+{
+	struct ogma_skb_cb *cb = (struct ogma_skb_cb *)skb->cb;
+
+	cb->is_rx = is_rx;
+}
+
+static inline bool skb_is_rx(struct sk_buff *skb)
+{
+	struct ogma_skb_cb *cb = (struct ogma_skb_cb *)skb->cb;
+
+	return cb->is_rx;
+}
+
+extern const struct net_device_ops ogma_netdev_ops;
+extern const struct ethtool_ops ogma_ethtool_ops;
+
+int ogma_start_gmac(struct ogma_priv *priv);
+int ogma_stop_gmac(struct ogma_priv *priv);
+int ogma_mii_register(struct ogma_priv *priv);
+void ogma_mii_unregister(struct ogma_priv *priv);
+int ogma_start_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+void ogma_stop_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+u16 ogma_get_rx_num(struct ogma_priv *priv);
+u16 ogma_get_tx_avail_num(struct ogma_priv *priv);
+int ogma_clean_tx_desc_ring(struct ogma_priv *priv);
+int ogma_clean_rx_desc_ring(struct ogma_priv *priv);
+int ogma_set_tx_pkt_data(struct ogma_priv *priv,
+			 const struct ogma_tx_pkt_ctrl *tx_ctrl, u8 count_frags,
+			 const struct ogma_frag_info *info,
+			 struct sk_buff *skb);
+int ogma_get_rx_pkt_data(struct ogma_priv *priv,
+			 struct ogma_rx_pkt_info *rxpi,
+			 struct ogma_frag_info *frag, u16 *len,
+			 struct sk_buff **skb);
+void ogma_ring_irq_enable(struct ogma_priv *priv, enum ogma_rings id, u32 i);
+void ogma_ring_irq_disable(struct ogma_priv *priv, enum ogma_rings id, u32 i);
+int ogma_alloc_desc_ring(struct ogma_priv *priv, enum ogma_rings id);
+void ogma_free_desc_ring(struct ogma_priv *priv, struct ogma_desc_ring *desc);
+int ogma_setup_rx_desc(struct ogma_priv *priv,
+		       struct ogma_desc_ring *desc);
+int ogma_netdev_napi_poll(struct napi_struct *napi_p, int budget);
+
+#endif /* OGMA_INTERNAL_H */
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c b/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
new file mode 100644
index 0000000..151d60a
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
@@ -0,0 +1,627 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/spinlock.h>
+#include <linux/dma-mapping.h>
+
+#include "ogma.h"
+
+static const u32 ads_irq_set[] = {
+	OGMA_REG_NRM_TX_INTEN_SET,
+	OGMA_REG_NRM_RX_INTEN_SET,
+};
+
+static const u32 desc_ring_irq_inten_clr_reg_addr[] = {
+	OGMA_REG_NRM_TX_INTEN_CLR,
+	OGMA_REG_NRM_RX_INTEN_CLR,
+};
+
+static const u32 int_tmr_reg_addr[] = {
+	OGMA_REG_NRM_TX_TXINT_TMR,
+	OGMA_REG_NRM_RX_RXINT_TMR,
+};
+
+static const u32 rx_pkt_cnt_reg_addr[] = {
+	0,
+	OGMA_REG_NRM_RX_PKTCNT,
+};
+
+static const u32 tx_pkt_cnt_reg_addr[] = {
+	OGMA_REG_NRM_TX_PKTCNT,
+	0,
+};
+
+static const u32 int_pkt_cnt_reg_addr[] = {
+	OGMA_REG_NRM_TX_DONE_TXINT_PKTCNT,
+	OGMA_REG_NRM_RX_RXINT_PKTCNT,
+};
+
+static const u32 tx_done_pkt_addr[] = {
+	OGMA_REG_NRM_TX_DONE_PKTCNT,
+	0,
+};
+
+static const u32 ogma_desc_mask[] = {
+	[OGMA_RING_TX] = OGMA_GMAC_OMR_REG_ST,
+	[OGMA_RING_RX] = OGMA_GMAC_OMR_REG_SR
+};
+
+static void ogma_check_desc_sanity(const struct ogma_desc_ring *desc,
+				   u16 idx, unsigned int expected_own)
+{
+	u32 tmp = *(u32 *)(desc->ring_vaddr + desc->len * idx);
+
+	BUG_ON((tmp >> 31) != expected_own);
+}
+
+void ogma_ring_irq_enable(struct ogma_priv *priv, enum ogma_rings id, u32 irqf)
+{
+	ogma_writel(priv, ads_irq_set[id], irqf);
+}
+
+void ogma_ring_irq_disable(struct ogma_priv *priv, enum ogma_rings id, u32 irqf)
+{
+	ogma_writel(priv, desc_ring_irq_inten_clr_reg_addr[id], irqf);
+}
+
+static struct sk_buff *alloc_rx_pkt_buf(struct ogma_priv *priv,
+					struct ogma_frag_info *info)
+{
+	struct sk_buff *skb;
+
+	skb = netdev_alloc_skb_ip_align(priv->net_device, info->len);
+	if (!skb)
+		return NULL;
+
+	ogma_mark_skb_type(skb, OGMA_RING_RX);
+	info->addr = skb->data;
+	info->dma_addr = dma_map_single(priv->dev, info->addr, info->len,
+					DMA_FROM_DEVICE);
+	if (dma_mapping_error(priv->dev, info->dma_addr)) {
+		dev_kfree_skb(skb);
+		return NULL;
+	}
+
+	return skb;
+}
+
+int ogma_alloc_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	desc->id = id;
+	desc->len = sizeof(struct ogma_tx_de); /* rx and tx desc same size */
+
+	spin_lock_init(&desc->spinlock_desc);
+
+	desc->ring_vaddr = dma_zalloc_coherent(priv->dev, desc->len * DESC_NUM,
+					      &desc->desc_phys, GFP_KERNEL);
+	if (!desc->ring_vaddr) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->frag = kcalloc(DESC_NUM, sizeof(*desc->frag), GFP_KERNEL);
+	if (!desc->frag) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	desc->priv = kcalloc(DESC_NUM, sizeof(struct sk_buff *), GFP_KERNEL);
+	if (!desc->priv) {
+		ret = -ENOMEM;
+		goto err;
+	}
+
+	return 0;
+
+err:
+	ogma_free_desc_ring(priv, desc);
+
+	return ret;
+}
+
+static void ogma_uninit_pkt_desc_ring(struct ogma_priv *priv,
+				      struct ogma_desc_ring *desc)
+{
+	struct ogma_frag_info *frag;
+	u32 status;
+	u16 idx;
+
+	for (idx = 0; idx < DESC_NUM; idx++) {
+		frag = &desc->frag[idx];
+		if (!frag->addr)
+			continue;
+
+		status = *(u32 *)(desc->ring_vaddr + desc->len * idx);
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 skb_is_rx(desc->priv[idx]) ? DMA_FROM_DEVICE :
+							      DMA_TO_DEVICE);
+		if ((status >> OGMA_TX_LAST) & 1)
+			dev_kfree_skb(desc->priv[idx]);
+	}
+
+	memset(desc->frag, 0, sizeof(struct ogma_frag_info) * DESC_NUM);
+	memset(desc->priv, 0, sizeof(struct sk_buff *) * DESC_NUM);
+	memset(desc->ring_vaddr, 0, desc->len * DESC_NUM);
+}
+
+void ogma_free_desc_ring(struct ogma_priv *priv, struct ogma_desc_ring *desc)
+{
+	if (desc->ring_vaddr && desc->frag && desc->priv)
+		ogma_uninit_pkt_desc_ring(priv, desc);
+
+	if (desc->ring_vaddr) {
+		dma_free_coherent(priv->dev, desc->len * DESC_NUM,
+				  desc->ring_vaddr, desc->desc_phys);
+		desc->ring_vaddr = NULL;
+	}
+	kfree(desc->frag);
+	desc->frag = NULL;
+	kfree(desc->priv);
+	desc->priv = NULL;
+}
+
+static void ogma_set_rx_de(struct ogma_priv *priv,
+			   struct ogma_desc_ring *desc, u16 idx,
+			   const struct ogma_frag_info *info,
+			   struct sk_buff *skb)
+{
+	struct ogma_rx_de *de = desc->ring_vaddr + desc->len * idx;
+	u32 attr = 1 << OGMA_RX_PKT_OWN_FIELD | 1 << OGMA_RX_PKT_FS_FIELD |
+			       1 << OGMA_RX_PKT_LS_FIELD;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+
+	if (idx == DESC_NUM - 1)
+		attr |= 1 << OGMA_RX_PKT_LD_FIELD;
+
+	de->data_buf_addr = info->dma_addr;
+	de->buf_len_info = info->len;
+	de->reserved = 0;
+	/* desc->attr makes the descriptor live, so it must be physically
+	 * written last after the rest of the descriptor body is already there
+	 */
+	wmb();
+	de->attr = attr;
+
+	desc->frag[idx].dma_addr = info->dma_addr;
+	desc->frag[idx].addr = info->addr;
+	desc->frag[idx].len = info->len;
+
+	desc->priv[idx] = skb;
+}
+
+int ogma_setup_rx_desc(struct ogma_priv *priv, struct ogma_desc_ring *desc)
+{
+	struct ogma_frag_info info;
+	struct sk_buff *skb;
+	int n;
+
+	info.len = priv->rx_pkt_buf_len;
+
+	for (n = 0; n < DESC_NUM; n++) {
+		skb = alloc_rx_pkt_buf(priv, &info);
+		if (!skb) {
+			ogma_uninit_pkt_desc_ring(priv, desc);
+			return -ENOMEM;
+		}
+		ogma_set_rx_de(priv, desc, n, &info, skb);
+	}
+
+	return 0;
+}
+
+static void ogma_set_tx_desc_entry(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc,
+				   const struct ogma_tx_pkt_ctrl *tx_ctrl,
+				   bool first_flag, bool last_flag,
+				   const struct ogma_frag_info *frag,
+				   struct sk_buff *skb)
+{
+	struct ogma_tx_de tx_desc_entry;
+	int idx = desc->head;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+
+	memset(&tx_desc_entry, 0, sizeof(struct ogma_tx_de));
+
+	tx_desc_entry.attr = 1 << OGMA_TX_SHIFT_OWN_FIELD |
+			     (idx == (DESC_NUM - 1)) << OGMA_TX_SHIFT_LD_FIELD |
+			     desc->id << OGMA_TX_SHIFT_DRID_FIELD |
+			     1 << OGMA_TX_SHIFT_PT_FIELD |
+			     OGMA_RING_GMAC << OGMA_TX_SHIFT_TDRID_FIELD |
+			     first_flag << OGMA_TX_SHIFT_FS_FIELD |
+			     last_flag << OGMA_TX_LAST |
+			     tx_ctrl->cksum_offload_flag << OGMA_TX_SHIFT_CO |
+			     tx_ctrl->tcp_seg_offload_flag << OGMA_TX_SHIFT_SO |
+			     1 << OGMA_TX_SHIFT_TRS_FIELD;
+
+	tx_desc_entry.data_buf_addr = frag->dma_addr;
+	tx_desc_entry.buf_len_info = (tx_ctrl->tcp_seg_len << 16) | frag->len;
+
+	memcpy(desc->ring_vaddr + (desc->len * idx), &tx_desc_entry, desc->len);
+
+	desc->frag[idx].dma_addr = frag->dma_addr;
+	desc->frag[idx].addr = frag->addr;
+	desc->frag[idx].len = frag->len;
+
+	desc->priv[idx] = skb;
+}
+
+static void ogma_get_rx_de(struct ogma_priv *priv,
+			   struct ogma_desc_ring *desc, u16 idx,
+			   struct ogma_rx_pkt_info *rxpi,
+			   struct ogma_frag_info *frag, u16 *len,
+			   struct sk_buff **skb)
+{
+	struct ogma_rx_de de;
+
+	ogma_check_desc_sanity(desc, idx, 0);
+	memset(&de, 0, sizeof(struct ogma_rx_de));
+	memset(rxpi, 0, sizeof(struct ogma_rx_pkt_info));
+	memcpy(&de, ((void *)desc->ring_vaddr + desc->len * idx), desc->len);
+
+	dev_dbg(priv->dev, "%08x\n", *(u32 *)&de);
+	*len = de.buf_len_info >> 16;
+
+	rxpi->is_fragmented = (de.attr >> OGMA_RX_PKT_FR_FIELD) & 1;
+	rxpi->err_flag = (de.attr >> OGMA_RX_PKT_ER_FIELD) & 1;
+	rxpi->rx_cksum_result = (de.attr >> OGMA_RX_PKT_CO_FIELD) & 3;
+	rxpi->err_code = (de.attr >> OGMA_RX_PKT_ERR_FIELD) &
+							OGMA_RX_PKT_ERR_MASK;
+	memcpy(frag, &desc->frag[idx], sizeof(*frag));
+	*skb = desc->priv[idx];
+}
+
+static void ogma_inc_desc_head_idx(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc, u16 inc)
+{
+	u32 sum;
+
+	if ((desc->tail > desc->head) || desc->full)
+		BUG_ON(inc > (desc->tail - desc->head));
+	else
+		BUG_ON(inc > (DESC_NUM + desc->tail - desc->head));
+
+	sum = desc->head + inc;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->head = sum;
+	desc->full = desc->head == desc->tail;
+}
+
+static void ogma_inc_desc_tail_idx(struct ogma_priv *priv,
+				   struct ogma_desc_ring *desc)
+{
+	u32 sum;
+
+	if ((desc->head >= desc->tail) && (!desc->full))
+		BUG_ON(1 > (desc->head - desc->tail));
+	else
+		BUG_ON(1 > (DESC_NUM + desc->head - desc->tail));
+
+	sum = desc->tail + 1;
+
+	if (sum >= DESC_NUM)
+		sum -= DESC_NUM;
+
+	desc->tail = sum;
+	desc->full = false;
+}
+
+static u16 ogma_get_tx_avail_num_sub(struct ogma_priv *priv,
+				     const struct ogma_desc_ring *desc)
+{
+	if (desc->full)
+		return 0;
+
+	if (desc->tail > desc->head)
+		return desc->tail - desc->head;
+
+	return DESC_NUM + desc->tail - desc->head;
+}
+
+static u16 ogma_get_tx_done_num_sub(struct ogma_priv *priv,
+				    struct ogma_desc_ring *desc)
+{
+	desc->tx_done_num += ogma_readl(priv, tx_done_pkt_addr[desc->id]);
+
+	return desc->tx_done_num;
+}
+
+static int ogma_set_irq_coalesce_param(struct ogma_priv *priv,
+				       enum ogma_rings id)
+{
+	int max_frames, tmr;
+
+	switch (id) {
+	case OGMA_RING_TX:
+		max_frames = priv->et_coalesce.tx_max_coalesced_frames;
+		tmr = priv->et_coalesce.tx_coalesce_usecs;
+		break;
+	case OGMA_RING_RX:
+		max_frames = priv->et_coalesce.rx_max_coalesced_frames;
+		tmr = priv->et_coalesce.rx_coalesce_usecs;
+		break;
+	default:
+		BUG();
+		break;
+	}
+
+	ogma_writel(priv, int_pkt_cnt_reg_addr[id], max_frames);
+	ogma_writel(priv, int_tmr_reg_addr[id], ((tmr != 0) << 31) | tmr);
+
+	return 0;
+}
+
+int ogma_start_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+	int ret = 0;
+
+	spin_lock_bh(&desc->spinlock_desc);
+
+	if (desc->running) {
+		ret = -EBUSY;
+		goto err;
+	}
+
+	switch (desc->id) {
+	case OGMA_RING_RX:
+		ogma_writel(priv, ads_irq_set[id], OGMA_IRQ_RCV);
+		break;
+	case OGMA_RING_TX:
+		ogma_writel(priv, ads_irq_set[id], OGMA_IRQ_EMPTY);
+		break;
+	}
+
+	ogma_set_irq_coalesce_param(priv, desc->id);
+	desc->running = true;
+
+err:
+	spin_unlock_bh(&desc->spinlock_desc);
+
+	return ret;
+}
+
+void ogma_stop_desc_ring(struct ogma_priv *priv, enum ogma_rings id)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[id];
+
+	spin_lock_bh(&desc->spinlock_desc);
+	if (desc->running)
+		ogma_writel(priv, desc_ring_irq_inten_clr_reg_addr[id],
+			    OGMA_IRQ_RCV | OGMA_IRQ_EMPTY | OGMA_IRQ_SND);
+
+	desc->running = false;
+	spin_unlock_bh(&desc->spinlock_desc);
+}
+
+u16 ogma_get_rx_num(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+	u32 result;
+
+	spin_lock(&desc->spinlock_desc);
+	if (desc->running) {
+		result = ogma_readl(priv, rx_pkt_cnt_reg_addr[OGMA_RING_RX]);
+		desc->rx_num += result;
+		if (result)
+			ogma_inc_desc_head_idx(priv, desc, result);
+	}
+	spin_unlock(&desc->spinlock_desc);
+
+	return desc->rx_num;
+}
+
+u16 ogma_get_tx_avail_num(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_TX];
+	u16 result;
+
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: not running tx desc\n", __func__);
+		result = 0;
+		goto err;
+	}
+
+	result = ogma_get_tx_avail_num_sub(priv, desc);
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return result;
+}
+
+int ogma_clean_tx_desc_ring(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_TX];
+	struct ogma_frag_info *frag;
+	struct ogma_tx_de *entry;
+	bool is_last;
+
+	spin_lock(&desc->spinlock_desc);
+
+	ogma_get_tx_done_num_sub(priv, desc);
+
+	while ((desc->tail != desc->head || desc->full) && desc->tx_done_num) {
+		frag = &desc->frag[desc->tail];
+		entry = desc->ring_vaddr + desc->len * desc->tail;
+		is_last = (entry->attr >> OGMA_TX_LAST) & 1;
+
+		dma_unmap_single(priv->dev, frag->dma_addr, frag->len,
+				 DMA_TO_DEVICE);
+		if (is_last) {
+			priv->net_device->stats.tx_packets++;
+			priv->net_device->stats.tx_bytes +=
+						desc->priv[desc->tail]->len;
+			dev_kfree_skb(desc->priv[desc->tail]);
+		}
+		memset(frag, 0, sizeof(*frag));
+		ogma_inc_desc_tail_idx(priv, desc);
+
+		if (is_last) {
+			BUG_ON(!desc->tx_done_num);
+			desc->tx_done_num--;
+		}
+	}
+
+	spin_unlock(&desc->spinlock_desc);
+
+	return 0;
+}
+
+int ogma_clean_rx_desc_ring(struct ogma_priv *priv)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+
+	spin_lock(&desc->spinlock_desc);
+
+	while (desc->full || (desc->tail != desc->head)) {
+		ogma_set_rx_de(priv, desc, desc->tail, &desc->frag[desc->tail],
+			       desc->priv[desc->tail]);
+		desc->rx_num--;
+		ogma_inc_desc_tail_idx(priv, desc);
+	}
+
+	BUG_ON(desc->rx_num);	/* error check */
+
+	spin_unlock(&desc->spinlock_desc);
+
+	return 0;
+}
+
+int ogma_set_tx_pkt_data(struct ogma_priv *priv,
+			 const struct ogma_tx_pkt_ctrl *tx_ctrl, u8 count_frags,
+			 const struct ogma_frag_info *info, struct sk_buff *skb)
+{
+	struct ogma_desc_ring *desc;
+	u32 sum_len = 0;
+	unsigned int i;
+	int ret = 0;
+
+	if (tx_ctrl->tcp_seg_offload_flag && !tx_ctrl->cksum_offload_flag)
+		return -EINVAL;
+
+	if (tx_ctrl->tcp_seg_offload_flag) {
+		if (tx_ctrl->tcp_seg_len < OGMA_TCP_SEG_LEN_MIN)
+			return -EINVAL;
+
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (tx_ctrl->tcp_seg_len > OGMA_TCP_JUMBO_SEG_LEN_MAX)
+				return -EINVAL;
+		} else {
+			if (tx_ctrl->tcp_seg_len > OGMA_TCP_SEG_LEN_MAX)
+				return -EINVAL;
+		}
+	} else
+		if (tx_ctrl->tcp_seg_len)
+			return -EINVAL;
+
+	if (!count_frags)
+		return -ERANGE;
+
+	for (i = 0; i < count_frags; i++) {
+		if ((info[i].len == 0) || (info[i].len > 0xffff)) {
+			netif_err(priv, drv, priv->net_device,
+				  "%s: bad info len\n", __func__);
+			return -EINVAL;
+		}
+		sum_len += info[i].len;
+	}
+
+	if (!tx_ctrl->tcp_seg_offload_flag) {
+		if (priv->param.use_jumbo_pkt_flag) {
+			if (sum_len > OGMA_MAX_TX_JUMBO_PKT_LEN)
+				return -EINVAL;
+		} else
+			if (sum_len > OGMA_MAX_TX_PKT_LEN)
+				return -EINVAL;
+	}
+
+	desc = &priv->desc_ring[OGMA_RING_TX];
+	spin_lock(&desc->spinlock_desc);
+
+	if (!desc->running) {
+		ret = -ENODEV;
+		goto end;
+	}
+
+	smp_rmb(); /* we need to see a consistent view of pending tx count */
+	if (count_frags > ogma_get_tx_avail_num_sub(priv, desc)) {
+		ret = -EBUSY;
+		goto end;
+	}
+
+	for (i = 0; i < count_frags; i++) {
+		ogma_set_tx_desc_entry(priv, desc, tx_ctrl, i == 0,
+				       i == count_frags - 1, &info[i], skb);
+		ogma_inc_desc_head_idx(priv, desc, 1);
+	}
+
+	wmb(); /* ensure the descriptor is flushed */
+	ogma_writel(priv, tx_pkt_cnt_reg_addr[OGMA_RING_TX], 1);
+
+end:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
+
+int ogma_get_rx_pkt_data(struct ogma_priv *priv,
+			 struct ogma_rx_pkt_info *rxpi,
+			 struct ogma_frag_info *frag, u16 *len,
+			 struct sk_buff **skb)
+{
+	struct ogma_desc_ring *desc = &priv->desc_ring[OGMA_RING_RX];
+	struct ogma_frag_info info;
+	struct sk_buff *tmp_skb;
+	int ret = 0;
+
+	spin_lock(&desc->spinlock_desc);
+	BUG_ON(!desc->running);
+
+	if (desc->rx_num == 0) {
+		dev_err(priv->dev, "%s 0 len rx\n", __func__);
+		ret = -EINVAL;
+		goto err;
+	}
+
+	info.len = priv->rx_pkt_buf_len;
+	rmb(); /* we need to ensure we only see current data in descriptor */
+	tmp_skb = alloc_rx_pkt_buf(priv, &info);
+	if (!tmp_skb) {
+		ogma_set_rx_de(priv, desc, desc->tail, &desc->frag[desc->tail],
+			       desc->priv[desc->tail]);
+		ret = -ENOMEM;
+	} else {
+		ogma_get_rx_de(priv, desc, desc->tail, rxpi, frag, len, skb);
+		ogma_set_rx_de(priv, desc, desc->tail, &info, tmp_skb);
+	}
+
+	ogma_inc_desc_tail_idx(priv, desc);
+	desc->rx_num--;
+
+err:
+	spin_unlock(&desc->spinlock_desc);
+
+	return ret;
+}
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c b/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
new file mode 100644
index 0000000..e0c441f
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
@@ -0,0 +1,95 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include "ogma.h"
+
+static void ogma_et_get_drvinfo(struct net_device *net_device,
+				struct ethtool_drvinfo *info)
+{
+	strlcpy(info->driver, "ogma", sizeof(info->driver));
+	strlcpy(info->bus_info, dev_name(net_device->dev.parent),
+		sizeof(info->bus_info));
+}
+
+static int ogma_et_get_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	if (!priv->phydev)
+		return -ENODEV;
+
+	return phy_ethtool_gset(priv->phydev, cmd);
+}
+
+static int ogma_et_set_settings(struct net_device *dev, struct ethtool_cmd *cmd)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	if (!priv->phydev)
+		return -ENODEV;
+
+	return phy_ethtool_sset(priv->phydev, cmd);
+}
+
+static int ogma_et_get_coalesce(struct net_device *net_device,
+				struct ethtool_coalesce *et_coalesce)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	*et_coalesce = priv->et_coalesce;
+
+	return 0;
+}
+static int ogma_et_set_coalesce(struct net_device *net_device,
+				struct ethtool_coalesce *et_coalesce)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	if (et_coalesce->rx_max_coalesced_frames > OGMA_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (et_coalesce->tx_max_coalesced_frames > OGMA_INT_PKTCNT_MAX)
+		return -EINVAL;
+	if (!et_coalesce->rx_max_coalesced_frames)
+		return -EINVAL;
+	if (!et_coalesce->tx_max_coalesced_frames)
+		return -EINVAL;
+
+	priv->et_coalesce = *et_coalesce;
+
+	return 0;
+}
+
+static u32 ogma_et_get_msglevel(struct net_device *dev)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	return priv->msg_enable;
+}
+
+static void ogma_et_set_msglevel(struct net_device *dev, u32 datum)
+{
+	struct ogma_priv *priv = netdev_priv(dev);
+
+	priv->msg_enable = datum;
+}
+
+const struct ethtool_ops ogma_ethtool_ops = {
+	.get_drvinfo		= ogma_et_get_drvinfo,
+	.get_settings		= ogma_et_get_settings,
+	.set_settings		= ogma_et_set_settings,
+	.get_link		= ethtool_op_get_link,
+	.get_coalesce		= ogma_et_get_coalesce,
+	.set_coalesce		= ogma_et_set_coalesce,
+	.get_msglevel		= ogma_et_get_msglevel,
+	.set_msglevel		= ogma_et_set_msglevel,
+};
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c b/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
new file mode 100644
index 0000000..59fb67b
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
@@ -0,0 +1,295 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
+ *
+ *  Copyright (C) 2011-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+#include "ogma.h"
+
+#define TIMEOUT_SPINS_MAC 1000000
+
+static u32 ogma_clk_type(u32 freq)
+{
+	if (freq < 35 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_25_35_MHZ;
+	if (freq < 60 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_35_60_MHZ;
+	if (freq < 100 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_60_100_MHZ;
+	if (freq < 150 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_100_150_MHZ;
+	if (freq < 250 * OGMA_CLK_MHZ)
+		return OGMA_GMAC_GAR_REG_CR_150_250_MHZ;
+
+	return OGMA_GMAC_GAR_REG_CR_250_300_MHZ;
+}
+
+static int ogma_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+
+	while (--timeout && ogma_readl(priv, addr) & mask)
+		;
+	if (!timeout) {
+		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ogma_mac_write(struct ogma_priv *priv, u32 addr, u32 value)
+{
+	ogma_writel(priv, MAC_REG_DATA, value);
+	ogma_writel(priv, MAC_REG_CMD, addr | OGMA_GMAC_CMD_ST_WRITE);
+	return ogma_wait_while_busy(priv, MAC_REG_CMD, OGMA_GMAC_CMD_ST_BUSY);
+}
+
+static int ogma_mac_read(struct ogma_priv *priv, u32 addr, u32 *read)
+{
+	int ret;
+
+	ogma_writel(priv, MAC_REG_CMD, addr | OGMA_GMAC_CMD_ST_READ);
+	ret = ogma_wait_while_busy(priv, MAC_REG_CMD, OGMA_GMAC_CMD_ST_BUSY);
+	if (ret)
+		return ret;
+
+	*read = ogma_readl(priv, MAC_REG_DATA);
+
+	return 0;
+}
+
+static int ogma_mac_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
+{
+	u32 timeout = TIMEOUT_SPINS_MAC;
+	int ret, data;
+
+	do {
+		ret = ogma_mac_read(priv, addr, &data);
+		if (ret)
+			break;
+	} while (--timeout && (data & mask));
+
+	if (!timeout || ret) {
+		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static int ogma_mac_update_to_phy_state(struct ogma_priv *priv)
+{
+	u32 value = 0;
+
+	if (priv->phydev->speed != SPEED_1000)
+		value = OGMA_MCR_PS;
+	if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+		return -ETIMEDOUT;
+
+	value = priv->phydev->duplex ? OGMA_GMAC_MCR_REG_FULL_DUPLEX_COMMON :
+				       OGMA_GMAC_MCR_REG_HALF_DUPLEX_COMMON;
+
+	if (priv->phydev->speed != SPEED_1000)
+		value |= OGMA_MCR_PS;
+
+	if ((priv->phy_interface != PHY_INTERFACE_MODE_GMII) &&
+	    (priv->phydev->speed == SPEED_100))
+		value |= OGMA_GMAC_MCR_REG_FES;
+
+	value |= OGMA_GMAC_MCR_REG_CST | OGMA_GMAC_MCR_REG_JE;
+	if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+		return -ETIMEDOUT;
+
+	priv->actual_link_speed = priv->phydev->speed;
+	priv->actual_duplex = priv->phydev->duplex;
+	netif_info(priv, drv, priv->net_device, "%s: %uMbps, duplex:%d\n",
+		   __func__, priv->phydev->speed, priv->phydev->duplex);
+
+	return 0;
+}
+
+/* NB ogma_start_gmac() only called from adjust_link */
+
+int ogma_start_gmac(struct ogma_priv *priv)
+{
+	u32 value = 0;
+	int ret;
+
+	if (priv->desc_ring[OGMA_RING_TX].running &&
+	    priv->desc_ring[OGMA_RING_RX].running)
+		return 0;
+
+	if (!priv->desc_ring[OGMA_RING_RX].running &&
+	    !priv->desc_ring[OGMA_RING_TX].running) {
+		if (priv->phydev->speed != SPEED_1000)
+			value = OGMA_MCR_PS;
+		if (ogma_mac_write(priv, GMAC_REG_MCR, value))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_BMR, OGMA_GMAC_BMR_REG_RESET))
+			return -ETIMEDOUT;
+		/* Wait soft reset */
+		usleep_range(1000, 5000);
+
+		ret = ogma_mac_read(priv, GMAC_REG_BMR, &value);
+		if (ret)
+			return ret;
+		if (value & OGMA_GMAC_BMR_REG_SWR)
+			return -EAGAIN;
+
+		ogma_writel(priv, MAC_REG_DESC_SOFT_RST, 1);
+		if (ogma_wait_while_busy(priv, MAC_REG_DESC_SOFT_RST, 1))
+			return -ETIMEDOUT;
+
+		ogma_writel(priv, MAC_REG_DESC_INIT, 1);
+		if (ogma_wait_while_busy(priv, MAC_REG_DESC_INIT, 1))
+			return -ETIMEDOUT;
+
+		if (ogma_mac_write(priv, GMAC_REG_BMR,
+				   OGMA_GMAC_BMR_REG_COMMON))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_RDLAR, priv->rdlar_pa))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_TDLAR, priv->tdlar_pa))
+			return -ETIMEDOUT;
+		if (ogma_mac_write(priv, GMAC_REG_MFFR, 0x80000001))
+			return -ETIMEDOUT;
+
+		ret = ogma_mac_update_to_phy_state(priv);
+		if (ret)
+			return ret;
+
+		if (priv->mac_mode.flow_ctrl_enable_flag) {
+			ogma_writel(priv, MAC_REG_FLOW_TH,
+				    (priv->mac_mode.flow_stop_th << 16) |
+				    priv->mac_mode.flow_start_th);
+			if (ogma_mac_write(priv, GMAC_REG_FCR,
+					   (priv->mac_mode.pause_time << 16) |
+					   OGMA_FCR_RFE | OGMA_FCR_TFE))
+				return -ETIMEDOUT;
+		}
+	}
+
+	ret = ogma_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (!priv->desc_ring[OGMA_RING_RX].running) {
+		value |= OGMA_GMAC_OMR_REG_SR;
+		ogma_start_desc_ring(priv, OGMA_RING_RX);
+	}
+	if (!priv->desc_ring[OGMA_RING_TX].running) {
+		value |= OGMA_GMAC_OMR_REG_ST;
+		ogma_start_desc_ring(priv, OGMA_RING_TX);
+	}
+
+	if (ogma_mac_write(priv, GMAC_REG_OMR, value))
+		return -ETIMEDOUT;
+
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return 0;
+}
+
+int ogma_stop_gmac(struct ogma_priv *priv)
+{
+	u32 value;
+	int ret;
+
+	ret = ogma_mac_read(priv, GMAC_REG_OMR, &value);
+	if (ret)
+		return ret;
+
+	if (priv->desc_ring[OGMA_RING_RX].running) {
+		value &= ~OGMA_GMAC_OMR_REG_SR;
+		ogma_stop_desc_ring(priv, OGMA_RING_RX);
+	}
+	if (priv->desc_ring[OGMA_RING_TX].running) {
+		value &= ~OGMA_GMAC_OMR_REG_ST;
+		ogma_stop_desc_ring(priv, OGMA_RING_TX);
+	}
+
+	priv->actual_link_speed = 0;
+	priv->actual_duplex = false;
+
+	return ogma_mac_write(priv, GMAC_REG_OMR, value);
+}
+
+static int ogma_phy_write(struct mii_bus *bus, int phy_addr, int reg, u16 val)
+{
+	struct ogma_priv *priv = bus->priv;
+
+	if (ogma_mac_write(priv, GMAC_REG_GDR, val))
+		return -ETIMEDOUT;
+	if (ogma_mac_write(priv, GMAC_REG_GAR,
+			   phy_addr << OGMA_GMAC_GAR_REG_SHIFT_PA |
+			   reg << OGMA_GMAC_GAR_REG_SHIFT_GR |
+			   ogma_clk_type(priv->freq) << GMAC_REG_SHIFT_CR_GAR |
+			   OGMA_GMAC_GAR_REG_GW | OGMA_GMAC_GAR_REG_GB))
+		return -ETIMEDOUT;
+
+	return ogma_mac_wait_while_busy(priv, GMAC_REG_GAR,
+					OGMA_GMAC_GAR_REG_GB);
+}
+
+static int ogma_phy_read(struct mii_bus *bus, int phy_addr, int reg_addr)
+{
+	struct ogma_priv *priv = bus->priv;
+	u32 data;
+	int ret;
+
+	if (ogma_mac_write(priv, GMAC_REG_GAR, OGMA_GMAC_GAR_REG_GB |
+			   phy_addr << OGMA_GMAC_GAR_REG_SHIFT_PA |
+			   reg_addr << OGMA_GMAC_GAR_REG_SHIFT_GR |
+			   ogma_clk_type(priv->freq) << GMAC_REG_SHIFT_CR_GAR))
+		return -ETIMEDOUT;
+
+	if (ogma_mac_wait_while_busy(priv, GMAC_REG_GAR, OGMA_GMAC_GAR_REG_GB))
+		return 0;
+
+	ret = ogma_mac_read(priv, GMAC_REG_GDR, &data);
+	if (ret)
+		return ret;
+
+	return data;
+}
+
+int ogma_mii_register(struct ogma_priv *priv)
+{
+	struct mii_bus *bus = mdiobus_alloc();
+	struct resource res;
+	int ret;
+
+	if (!bus)
+		return -ENOMEM;
+
+	of_address_to_resource(priv->dev->of_node, 0, &res);
+	snprintf(bus->id, MII_BUS_ID_SIZE, "%s", priv->dev->of_node->full_name);
+	bus->priv = priv;
+	bus->name = "Fujitsu OGMA MDIO";
+	bus->read = ogma_phy_read;
+	bus->write = ogma_phy_write;
+	bus->parent = priv->dev;
+	priv->mii_bus = bus;
+
+	ret = of_mdiobus_register(bus, priv->dev->of_node);
+	if (ret) {
+		mdiobus_free(bus);
+		return ret;
+	}
+
+	return 0;
+}
+
+void ogma_mii_unregister(struct ogma_priv *priv)
+{
+	mdiobus_unregister(priv->mii_bus);
+	mdiobus_free(priv->mii_bus);
+	priv->mii_bus = NULL;
+}
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c b/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
new file mode 100644
index 0000000..a10ffb8
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
@@ -0,0 +1,592 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/ip.h>
+#include <linux/ipv6.h>
+#include <linux/tcp.h>
+#include <net/tcp.h>
+#include <net/ip6_checksum.h>
+#include <linux/pm_runtime.h>
+
+#include "ogma.h"
+
+#define OGMA_PHY_SR_REG_AN_C			0x20
+#define OGMA_PHY_SR_REG_LINK			4
+
+#define SR_1GBIT				0x800
+
+#define OGMA_PHY_ANLPA_REG_TXF			0x100
+#define OGMA_PHY_ANLPA_REG_TXD			0x80
+#define OGMA_PHY_ANLPA_REG_TF			0x40
+#define OGMA_PHY_ANLPA_REG_TD			0x20
+
+#define OGMA_PHY_CTRL_REG_RESET			(1 << 15)
+#define OGMA_PHY_CTRL_REG_LOOPBACK		(1 << 14)
+#define OGMA_PHY_CTRL_REG_SPSEL_LSB		(1 << 13)
+#define OGMA_PHY_CTRL_REG_AUTO_NEGO_EN		(1 << 12)
+#define OGMA_PHY_CTRL_REG_POWER_DOWN		(1 << 11)
+#define OGMA_PHY_CTRL_REG_ISOLATE		(1 << 10)
+#define OGMA_PHY_CTRL_REG_RESTART_AUTO_NEGO	(1 << 9)
+#define OGMA_PHY_CTRL_REG_DUPLEX_MODE		(1 << 8)
+#define OGMA_PHY_CTRL_REG_COL_TEST		(1 << 7)
+#define OGMA_PHY_CTRL_REG_SPSEL_MSB		(1 << 6)
+#define OGMA_PHY_CTRL_REG_UNIDIR_EN		(1 << 5)
+
+#define MSC_1GBIT				(1 << 9)
+
+#define OGMA_PHY_ADDR_CTRL			0
+#define OGMA_PHY_ADDR_SR			1
+#define OGMA_PHY_ADDR_ANA			4
+#define OGMA_PHY_ADDR_ANLPA			5
+#define OGMA_PHY_ADDR_MSC			9
+#define OGMA_PHY_ADDR_1000BASE_SR		10
+
+#define WAIT_FW_RDY_TIMEOUT 50
+
+static const u32 desc_ring_irq_status_reg_addr[] = {
+	OGMA_REG_NRM_TX_STATUS,
+	OGMA_REG_NRM_RX_STATUS,
+};
+
+static const u32 desc_ads[] = {
+	OGMA_REG_NRM_TX_CONFIG,
+	OGMA_REG_NRM_RX_CONFIG,
+};
+
+static const u32 ogma_desc_start_reg_addr[] = {
+	OGMA_REG_NRM_TX_DESC_START,
+	OGMA_REG_NRM_RX_DESC_START,
+};
+
+static int ogma_wait_for_ring_config_ready(struct ogma_priv *priv, int ring)
+{
+	int timeout = WAIT_FW_RDY_TIMEOUT;
+
+	while (--timeout && (ogma_readl(priv, desc_ads[ring]) &
+			    (1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP)))
+		usleep_range(1000, 2000);
+
+	if (!timeout) {
+		netif_err(priv, hw, priv->net_device,
+			  "%s: timeout\n", __func__);
+		return -ETIMEDOUT;
+	}
+
+	return 0;
+}
+
+static u32 ogma_calc_pkt_ctrl_reg_param(const struct ogma_pkt_ctrlaram
+					*pkt_ctrlaram_p)
+{
+	u32 param = OGMA_PKT_CTRL_REG_MODE_NRM;
+
+	if (pkt_ctrlaram_p->log_chksum_er_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_CHKSUM_ER;
+
+	if (pkt_ctrlaram_p->log_hd_imcomplete_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_HD_INCOMPLETE;
+
+	if (pkt_ctrlaram_p->log_hd_er_flag)
+		param |= OGMA_PKT_CTRL_REG_LOG_HD_ER;
+
+	return param;
+}
+
+static int ogma_configure_normal_mode(struct ogma_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	/* save scb set value  */
+	priv->scb_set_normal_tx_paddr = ogma_readl(priv,
+			ogma_desc_start_reg_addr[OGMA_RING_TX]);
+
+	/* set desc_start addr */
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_RX],
+		    priv->desc_ring[OGMA_RING_RX].desc_phys);
+
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_TX],
+		    priv->desc_ring[OGMA_RING_TX].desc_phys);
+
+	/* set normal tx desc ring config */
+	value = 0 << OGMA_REG_DESC_TMR_MODE |
+		(cpu_to_le32(1) == 1) << OGMA_REG_DESC_ENDIAN |
+		1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+	ogma_writel(priv, desc_ads[OGMA_RING_TX], value);
+
+	value = 0 << OGMA_REG_DESC_TMR_MODE |
+		(cpu_to_le32(1) == 1) << OGMA_REG_DESC_ENDIAN |
+		1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+	ogma_writel(priv, desc_ads[OGMA_RING_RX], value);
+
+	if (ogma_wait_for_ring_config_ready(priv, OGMA_RING_TX) ||
+	    ogma_wait_for_ring_config_ready(priv, OGMA_RING_RX))
+		return -ETIMEDOUT;
+
+	return ret;
+}
+
+static int ogma_change_mode_to_normal(struct ogma_priv *priv)
+{
+	u32 value;
+
+	priv->scb_pkt_ctrl_reg = ogma_readl(priv, OGMA_REG_PKT_CTRL);
+
+	value = ogma_calc_pkt_ctrl_reg_param(&priv->param.pkt_ctrlaram);
+
+	if (priv->param.use_jumbo_pkt_flag)
+		value |= OGMA_PKT_CTRL_REG_EN_JUMBO;
+
+	value |= OGMA_PKT_CTRL_REG_MODE_NRM;
+
+	/* change to normal mode */
+	ogma_writel(priv, OGMA_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	ogma_writel(priv, OGMA_REG_PKT_CTRL, value);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return 0;
+}
+
+static int ogma_change_mode_to_taiki(struct ogma_priv *priv)
+{
+	int ret = 0;
+	u32 value;
+
+	ogma_writel(priv, ogma_desc_start_reg_addr[OGMA_RING_TX],
+		    priv->scb_set_normal_tx_paddr);
+
+	value = 1 << OGMA_REG_DESC_RING_CONFIG_CFG_UP |
+		1 << OGMA_REG_DESC_RING_CONFIG_CH_RST;
+
+	ogma_writel(priv, desc_ads[OGMA_RING_TX], value);
+
+	if (ogma_wait_for_ring_config_ready(priv, OGMA_RING_TX))
+		return -ETIMEDOUT;
+
+	ogma_writel(priv, OGMA_REG_DMA_MH_CTRL, MH_CTRL__MODE_TRANS);
+	ogma_writel(priv, OGMA_REG_PKT_CTRL, priv->scb_pkt_ctrl_reg);
+
+	/* Wait Change mode Complete */
+	usleep_range(2000, 10000);
+
+	return ret;
+}
+
+static int ogma_clear_modechange_irq(struct ogma_priv *priv, u32 value)
+{
+	ogma_writel(priv, OGMA_REG_MODE_TRANS_COMP_STATUS,
+		    (value & (OGMA_MODE_TRANS_COMP_IRQ_N2T |
+		    OGMA_MODE_TRANS_COMP_IRQ_T2N)));
+
+	return 0;
+}
+
+static int ogma_hw_configure_to_normal(struct ogma_priv *priv)
+{
+	int err;
+
+	err = ogma_configure_normal_mode(priv);
+	if (err) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: normal conf fail\n", __func__);
+		return err;
+	}
+	err = ogma_change_mode_to_normal(priv);
+	if (err) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: normal set fail\n", __func__);
+		return err;
+	}
+
+	return err;
+}
+
+static int ogma_hw_configure_to_taiki(struct ogma_priv *priv)
+{
+	int ret;
+
+	ret = ogma_change_mode_to_taiki(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: taiki set fail\n", __func__);
+		return ret;
+	}
+
+	/* Clear mode change complete IRQ */
+	ret = ogma_clear_modechange_irq(priv, OGMA_MODE_TRANS_COMP_IRQ_T2N |
+					OGMA_MODE_TRANS_COMP_IRQ_N2T);
+
+	if (ret)
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clear mode fail\n", __func__);
+
+	return ret;
+}
+
+static void ogma_ring_irq_clr(struct ogma_priv *priv,
+			      unsigned int id, u32 value)
+{
+	BUG_ON(id > OGMA_RING_MAX);
+
+	ogma_writel(priv, desc_ring_irq_status_reg_addr[id],
+		    value & (OGMA_IRQ_EMPTY | OGMA_IRQ_ERR));
+}
+
+static void ogma_napi_tx_processing(struct napi_struct *napi_p)
+{
+	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+	ogma_clean_tx_desc_ring(priv);
+
+	if (netif_queue_stopped(priv->net_device) &&
+	    ogma_get_tx_avail_num(priv) >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX)
+		netif_wake_queue(priv->net_device);
+}
+
+int ogma_netdev_napi_poll(struct napi_struct *napi_p, int budget)
+{
+	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
+	struct net_device *net_device = priv->net_device;
+	struct ogma_rx_pkt_info rx_info;
+	int ret, done = 0, rx_num = 0;
+	struct ogma_frag_info frag;
+	struct sk_buff *skb;
+	u16 len;
+
+	ogma_napi_tx_processing(napi_p);
+
+	while (done < budget) {
+		if (!rx_num) {
+			rx_num = ogma_get_rx_num(priv);
+			if (!rx_num)
+				break;
+		}
+		done++;
+		rx_num--;
+		ret = ogma_get_rx_pkt_data(priv, &rx_info, &frag, &len, &skb);
+		if (unlikely(ret == -ENOMEM)) {
+			netif_err(priv, drv, priv->net_device,
+				  "%s: rx fail %d\n", __func__, ret);
+			net_device->stats.rx_dropped++;
+			continue;
+		}
+		dma_unmap_single(priv->dev, frag.dma_addr, frag.len,
+				 DMA_FROM_DEVICE);
+		skb_put(skb, len);
+		skb->protocol = eth_type_trans(skb, priv->net_device);
+
+		if (priv->rx_cksum_offload_flag &&
+		    rx_info.rx_cksum_result == OGMA_RX_CKSUM_OK)
+			skb->ip_summed = CHECKSUM_UNNECESSARY;
+
+		napi_gro_receive(napi_p, skb);
+
+		net_device->stats.rx_packets++;
+		net_device->stats.rx_bytes += len;
+	}
+
+	if (done == budget)
+		return budget;
+
+	napi_complete(napi_p);
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return done;
+}
+
+static netdev_tx_t ogma_netdev_start_xmit(struct sk_buff *skb,
+					  struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	struct ogma_tx_pkt_ctrl tx_ctrl;
+	u16 pend_tx, tso_seg_len = 0;
+	skb_frag_t *frag;
+	int count_frags;
+	int ret, i;
+
+	memset(&tx_ctrl, 0, sizeof(struct ogma_tx_pkt_ctrl));
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	BUG_ON(skb_shinfo(skb)->nr_frags >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX);
+	count_frags = skb_shinfo(skb)->nr_frags + 1;
+
+	if (skb->ip_summed == CHECKSUM_PARTIAL)
+		tx_ctrl.cksum_offload_flag = true;
+
+	if (skb_is_gso(skb)) {
+		tso_seg_len = skb_shinfo(skb)->gso_size;
+
+		BUG_ON(skb->ip_summed != CHECKSUM_PARTIAL);
+		BUG_ON(!tso_seg_len);
+		BUG_ON(tso_seg_len > (priv->param.use_jumbo_pkt_flag ?
+			    OGMA_TCP_JUMBO_SEG_LEN_MAX : OGMA_TCP_SEG_LEN_MAX));
+
+		if (tso_seg_len < OGMA_TCP_SEG_LEN_MIN) {
+			tso_seg_len = OGMA_TCP_SEG_LEN_MIN;
+
+			if (skb->data_len < OGMA_TCP_SEG_LEN_MIN)
+				tso_seg_len = 0;
+		}
+	}
+
+	if (tso_seg_len > 0) {
+		if (skb->protocol == htons(ETH_P_IP)) {
+			BUG_ON(!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV4));
+
+			ip_hdr(skb)->tot_len = 0;
+			tcp_hdr(skb)->check =
+				~tcp_v4_check(0, ip_hdr(skb)->saddr,
+					      ip_hdr(skb)->daddr, 0);
+		} else {
+			BUG_ON(!(skb_shinfo(skb)->gso_type & SKB_GSO_TCPV6));
+			ipv6_hdr(skb)->payload_len = 0;
+			tcp_hdr(skb)->check =
+				~csum_ipv6_magic(&ipv6_hdr(skb)->saddr,
+						 &ipv6_hdr(skb)->daddr,
+						 0, IPPROTO_TCP, 0);
+		}
+
+		tx_ctrl.tcp_seg_offload_flag = true;
+		tx_ctrl.tcp_seg_len = tso_seg_len;
+	}
+
+	priv->tx_info[0].dma_addr = dma_map_single(priv->dev, skb->data,
+					  skb_headlen(skb), DMA_TO_DEVICE);
+	if (dma_mapping_error(priv->dev, priv->tx_info[0].dma_addr)) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: DMA mapping failed\n", __func__);
+		return NETDEV_TX_OK;
+	}
+	priv->tx_info[0].addr = skb->data;
+	priv->tx_info[0].len = skb_headlen(skb);
+
+	for (i = 0; i < skb_shinfo(skb)->nr_frags; i++) {
+		frag = &skb_shinfo(skb)->frags[i];
+		priv->tx_info[i + 1].dma_addr =
+			skb_frag_dma_map(priv->dev, frag, 0,
+					 skb_frag_size(frag), DMA_TO_DEVICE);
+		priv->tx_info[i + 1].addr = skb_frag_address(frag);
+		priv->tx_info[i + 1].len = frag->size;
+	}
+
+	ogma_mark_skb_type(skb, OGMA_RING_TX);
+
+	ret = ogma_set_tx_pkt_data(priv, &tx_ctrl, count_frags, priv->tx_info,
+				   skb);
+	if (ret) {
+		netif_info(priv, drv, priv->net_device,
+			   "set tx pkt failed %d\n", ret);
+		for (i = 0; i < count_frags; i++)
+			dma_unmap_single(priv->dev, priv->tx_info[i].dma_addr,
+					 priv->tx_info[i].len, DMA_TO_DEVICE);
+		net_device->stats.tx_dropped++;
+
+		return NETDEV_TX_OK;
+	}
+
+	spin_lock(&priv->tx_queue_lock);
+	pend_tx = ogma_get_tx_avail_num(priv);
+
+	if (pend_tx < OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX) {
+		ogma_ring_irq_enable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+		netif_stop_queue(net_device);
+		goto err;
+	}
+	if (pend_tx <= DESC_NUM - 2) {
+		ogma_ring_irq_enable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+		goto err;
+	}
+	ogma_ring_irq_disable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+err:
+	spin_unlock(&priv->tx_queue_lock);
+
+	return NETDEV_TX_OK;
+}
+
+static int ogma_netdev_set_features(struct net_device *net_device,
+				    netdev_features_t features)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	priv->rx_cksum_offload_flag = !!(features & NETIF_F_RXCSUM);
+
+	return 0;
+}
+
+static void ogma_phy_adjust_link(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+
+	if (priv->actual_link_speed == priv->phydev->speed &&
+	    priv->actual_duplex == priv->phydev->duplex)
+		return;
+
+	ogma_stop_gmac(priv);
+	ogma_start_gmac(priv);
+}
+
+static irqreturn_t ogma_irq_handler(int irq, void *dev_id)
+{
+	struct ogma_priv *priv = dev_id;
+	u32 status = ogma_readl(priv, OGMA_REG_TOP_STATUS) &
+		     ogma_readl(priv, OGMA_REG_TOP_INTEN);
+
+	if (!status)
+		return IRQ_NONE;
+
+	if (status & (OGMA_IRQ_TX | OGMA_IRQ_RX)) {
+		ogma_writel(priv, OGMA_REG_INTEN_CLR,
+			    status & (OGMA_IRQ_TX | OGMA_IRQ_RX));
+		napi_schedule(&priv->napi);
+	}
+
+	return IRQ_HANDLED;
+}
+
+static int ogma_netdev_open(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	u32 scb_irq_temp;
+	int ret, n;
+
+	scb_irq_temp = ogma_readl(priv, OGMA_REG_TOP_INTEN);
+
+	for (n = 0; n <= OGMA_RING_MAX; n++) {
+		ret = ogma_alloc_desc_ring(priv, n);
+		if (ret) {
+			netif_err(priv, probe, priv->net_device,
+				  "%s: alloc ring failed\n", __func__);
+			goto err;
+		}
+	}
+
+	ret = ogma_setup_rx_desc(priv, &priv->desc_ring[OGMA_RING_RX]);
+	if (ret) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: fail setup ring\n", __func__);
+		goto err1;
+	}
+
+	pm_runtime_get_sync(priv->dev);
+
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, scb_irq_temp);
+
+	ret = ogma_hw_configure_to_normal(priv);
+	if (ret) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: normal fail %d\n", __func__, ret);
+		goto err1;
+	}
+
+	ret = request_irq(priv->net_device->irq, ogma_irq_handler,
+			  IRQF_SHARED, "ogma", priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device, "request_irq failed\n");
+		goto err1;
+	}
+	priv->irq_registered = true;
+
+	ret = ogma_clean_rx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clean rx desc fail\n", __func__);
+		goto err2;
+	}
+
+	ret = ogma_clean_tx_desc_ring(priv);
+	if (ret) {
+		netif_err(priv, drv, priv->net_device,
+			  "%s: clean tx desc fail\n", __func__);
+		goto err2;
+	}
+
+	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	priv->phydev = of_phy_connect(priv->net_device, priv->phy_np,
+				      &ogma_phy_adjust_link, 0,
+				      priv->phy_interface);
+	if (!priv->phydev) {
+		netif_err(priv, link, priv->net_device, "missing PHY\n");
+		goto err2;
+	}
+
+	phy_start_aneg(priv->phydev);
+
+	ogma_ring_irq_disable(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
+
+	ogma_start_gmac(priv);
+	napi_enable(&priv->napi);
+	netif_start_queue(net_device);
+
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	return 0;
+
+err2:
+	pm_runtime_put_sync(priv->dev);
+	free_irq(priv->net_device->irq, priv);
+	priv->irq_registered = false;
+err1:
+	for (n = 0; n <= OGMA_RING_MAX; n++)
+		ogma_free_desc_ring(priv, &priv->desc_ring[n]);
+err:
+	ogma_writel(priv, OGMA_REG_INTEN_SET, scb_irq_temp);
+
+	pm_runtime_put_sync(priv->dev);
+
+	return ret;
+}
+
+static int ogma_netdev_stop(struct net_device *net_device)
+{
+	struct ogma_priv *priv = netdev_priv(net_device);
+	int n;
+
+	phy_stop(priv->phydev);
+	phy_disconnect(priv->phydev);
+	priv->phydev = NULL;
+
+	netif_stop_queue(priv->net_device);
+	napi_disable(&priv->napi);
+
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, ~0);
+	ogma_stop_gmac(priv);
+	BUG_ON(ogma_hw_configure_to_taiki(priv));
+
+	pm_runtime_put_sync(priv->dev);
+
+	for (n = 0; n <= OGMA_RING_MAX; n++)
+		ogma_free_desc_ring(priv, &priv->desc_ring[n]);
+
+	free_irq(priv->net_device->irq, priv);
+	priv->irq_registered = false;
+
+	return 0;
+}
+
+const struct net_device_ops ogma_netdev_ops = {
+	.ndo_open		= ogma_netdev_open,
+	.ndo_stop		= ogma_netdev_stop,
+	.ndo_start_xmit		= ogma_netdev_start_xmit,
+	.ndo_set_features	= ogma_netdev_set_features,
+	.ndo_change_mtu         = eth_change_mtu,
+	.ndo_set_mac_address    = eth_mac_addr,
+	.ndo_validate_addr	= eth_validate_addr,
+};
diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
new file mode 100644
index 0000000..09071da
--- /dev/null
+++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
@@ -0,0 +1,333 @@
+/**
+ * drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
+ *
+ *  Copyright (C) 2013-2014 Fujitsu Semiconductor Limited.
+ *  Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
+ *  All rights reserved.
+ *
+ *  This program is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU General Public License
+ *  as published by the Free Software Foundation; either version 2
+ *  of the License, or (at your option) any later version.
+ */
+
+#include <linux/device.h>
+#include <linux/ctype.h>
+#include <linux/netdevice.h>
+#include <linux/types.h>
+#include <linux/bitops.h>
+#include <linux/dma-mapping.h>
+#include <linux/module.h>
+#include <linux/sizes.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_net.h>
+#include <linux/io.h>
+#include <linux/pm_runtime.h>
+
+#include "ogma.h"
+
+#define OGMA_F_NETSEC_VER_MAJOR_NUM(x) (x & 0xffff0000)
+
+static int napi_weight = 64;
+unsigned short pause_time = 256;
+
+static int ogma_probe(struct platform_device *pdev)
+{
+	struct net_device *net_device;
+	struct ogma_priv *priv;
+	struct resource *res;
+	const u8 *mac;
+	const u32 *p;
+	u32 hw_ver;
+	int err;
+	int ret;
+
+	net_device = alloc_etherdev(sizeof(*priv));
+	if (!net_device)
+		return -ENOMEM;
+
+	priv = netdev_priv(net_device);
+	priv->net_device = net_device;
+	SET_NETDEV_DEV(priv->net_device, &pdev->dev);
+	platform_set_drvdata(pdev, priv);
+	priv->dev = &pdev->dev;
+
+	priv->msg_enable = NETIF_MSG_TX_ERR | NETIF_MSG_HW | NETIF_MSG_DRV |
+			   NETIF_MSG_LINK | NETIF_MSG_PROBE;
+
+	priv->phy_np = of_parse_phandle(pdev->dev.of_node, "phy-handle", 0);
+	if (!priv->phy_np) {
+		netif_err(priv, probe, priv->net_device,
+			  "missing phy in DT\n");
+		goto err1;
+	}
+
+	mac = of_get_mac_address(pdev->dev.of_node);
+	if (mac)
+		ether_addr_copy(priv->net_device->dev_addr, mac);
+
+	priv->phy_interface = of_get_phy_mode(pdev->dev.of_node);
+	if (priv->phy_interface < 0) {
+		netif_err(priv, probe, priv->net_device,
+			  "%s: bad phy-if\n", __func__);
+		goto err1;
+	}
+
+	priv->ioaddr = of_iomap(priv->dev->of_node, 0);
+	if (!priv->ioaddr) {
+		netif_err(priv, probe, priv->net_device, "of_iomap() failed\n");
+		err = -EINVAL;
+		goto err1;
+	}
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing rdlar resource\n");
+		goto err1;
+	}
+	priv->rdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 2);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing tdlar resource\n");
+		goto err1;
+	}
+	priv->tdlar_pa = res->start;
+
+	res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+	if (!res) {
+		netif_err(priv, probe, priv->net_device,
+			  "Missing IRQ resource\n");
+		goto err2;
+	}
+	priv->net_device->irq = res->start;
+
+	pdev->dev.coherent_dma_mask = DMA_BIT_MASK(32);
+	pdev->dev.dma_mask = &pdev->dev.coherent_dma_mask;
+
+	while (priv->clock_count < ARRAY_SIZE(priv->clk)) {
+		priv->clk[priv->clock_count] =
+			of_clk_get(pdev->dev.of_node, priv->clock_count);
+		if (IS_ERR(priv->clk[priv->clock_count])) {
+			if (!priv->clock_count) {
+				netif_err(priv, probe, priv->net_device,
+					  "Failed to get clock\n");
+				goto err3;
+			}
+			break;
+		}
+		priv->clock_count++;
+	}
+
+	/* disable by default */
+	priv->et_coalesce.rx_coalesce_usecs = 0;
+	priv->et_coalesce.rx_max_coalesced_frames = 1;
+	priv->et_coalesce.tx_coalesce_usecs = 0;
+	priv->et_coalesce.tx_max_coalesced_frames = 1;
+
+	pm_runtime_enable(&pdev->dev);
+	/* runtime_pm coverage just for probe, open/close also cover it */
+	pm_runtime_get_sync(&pdev->dev);
+
+	priv->param.use_jumbo_pkt_flag = false;
+	p = of_get_property(pdev->dev.of_node, "max-frame-size", NULL);
+	if (p)
+		priv->param.use_jumbo_pkt_flag = !!(be32_to_cpu(*p) > 8000);
+
+	hw_ver = ogma_readl(priv, OGMA_REG_F_TAIKI_VER);
+	/* this driver only supports F_TAIKI style OGMA */
+	if (OGMA_F_NETSEC_VER_MAJOR_NUM(hw_ver) !=
+	    OGMA_F_NETSEC_VER_MAJOR_NUM(OGMA_REG_OGMA_VER_F_TAIKI)) {
+		ret = -ENODEV;
+		goto err3;
+	}
+
+	if (priv->param.use_jumbo_pkt_flag)
+		priv->rx_pkt_buf_len = OGMA_RX_JUMBO_PKT_BUF_LEN;
+	else
+		priv->rx_pkt_buf_len = OGMA_RX_PKT_BUF_LEN;
+
+	dev_info(&pdev->dev, "IP rev %d.%d\n", hw_ver >> 16, hw_ver & 0xffff);
+
+	priv->mac_mode.flow_start_th = OGMA_FLOW_CONTROL_START_THRESHOLD;
+	priv->mac_mode.flow_stop_th = OGMA_FLOW_CONTROL_STOP_THRESHOLD;
+	priv->mac_mode.pause_time = pause_time;
+	priv->mac_mode.flow_ctrl_enable_flag = false;
+	priv->freq = clk_get_rate(priv->clk[0]);
+
+	netif_napi_add(priv->net_device, &priv->napi, ogma_netdev_napi_poll,
+		       napi_weight);
+
+	net_device->netdev_ops = &ogma_netdev_ops;
+	net_device->ethtool_ops = &ogma_ethtool_ops;
+	net_device->features = NETIF_F_SG | NETIF_F_IP_CSUM |
+			       NETIF_F_IPV6_CSUM | NETIF_F_TSO |
+			       NETIF_F_TSO6 | NETIF_F_GSO |
+			       NETIF_F_HIGHDMA | NETIF_F_RXCSUM;
+	priv->net_device->hw_features = priv->net_device->features;
+
+	priv->rx_cksum_offload_flag = true;
+	spin_lock_init(&priv->tx_queue_lock);
+
+	err = ogma_mii_register(priv);
+	if (err) {
+		netif_err(priv, probe, priv->net_device,
+			  "mii bus registration failed %d\n", err);
+		goto err3;
+	}
+
+	/* disable all other interrupt sources */
+	ogma_writel(priv, OGMA_REG_INTEN_CLR, ~0);
+	ogma_writel(priv, OGMA_REG_INTEN_SET, OGMA_IRQ_TX | OGMA_IRQ_RX);
+
+	err = register_netdev(priv->net_device);
+	if (err) {
+		netif_err(priv, probe, priv->net_device,
+			  "register_netdev() failed\n");
+		goto err4;
+	}
+
+	pm_runtime_put_sync_suspend(&pdev->dev);
+
+	netif_info(priv, probe, priv->net_device, "initialized\n");
+
+	return 0;
+
+err4:
+	ogma_mii_unregister(priv);
+
+err3:
+	pm_runtime_put_sync_suspend(&pdev->dev);
+	pm_runtime_disable(&pdev->dev);
+	while (priv->clock_count > 0) {
+		priv->clock_count--;
+		clk_put(priv->clk[priv->clock_count]);
+	}
+err2:
+	iounmap(priv->ioaddr);
+err1:
+	free_netdev(priv->net_device);
+
+	dev_err(&pdev->dev, "init failed\n");
+
+	return ret;
+}
+
+static int ogma_remove(struct platform_device *pdev)
+{
+	struct ogma_priv *priv = platform_get_drvdata(pdev);
+
+	unregister_netdev(priv->net_device);
+	ogma_mii_unregister(priv);
+	pm_runtime_disable(&pdev->dev);
+	iounmap(priv->ioaddr);
+	free_netdev(priv->net_device);
+
+	return 0;
+}
+
+#ifdef CONFIG_PM
+#ifdef CONFIG_PM_RUNTIME
+static int ogma_runtime_suspend(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (priv->irq_registered)
+		disable_irq(priv->net_device->irq);
+
+	ogma_writel(priv, OGMA_REG_CLK_EN, 0);
+
+	for (n = priv->clock_count - 1; n >= 0; n--)
+		clk_disable_unprepare(priv->clk[n]);
+
+	return 0;
+}
+
+static int ogma_runtime_resume(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+	int n;
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	/* first let the clocks back on */
+
+	for (n = 0; n < priv->clock_count; n++)
+		clk_prepare_enable(priv->clk[n]);
+
+	ogma_writel(priv, OGMA_REG_CLK_EN, OGMA_CLK_EN_REG_DOM_D |
+			OGMA_CLK_EN_REG_DOM_C | OGMA_CLK_EN_REG_DOM_G);
+
+	if (priv->irq_registered)
+		enable_irq(priv->net_device->irq);
+
+	return 0;
+}
+#endif
+
+static int ogma_pm_suspend(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return ogma_runtime_suspend(dev);
+}
+
+static int ogma_pm_resume(struct device *dev)
+{
+	struct ogma_priv *priv = dev_get_drvdata(dev);
+
+	netif_dbg(priv, drv, priv->net_device, "%s\n", __func__);
+
+	if (pm_runtime_status_suspended(dev))
+		return 0;
+
+	return ogma_runtime_resume(dev);
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops ogma_pm_ops = {
+	SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
+	SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
+};
+#endif
+
+static const struct of_device_id ogma_dt_ids[] = {
+	{.compatible = "fujitsu,ogma"},
+	{ /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, ogma_dt_ids);
+
+static struct platform_driver ogma_driver = {
+	.probe = ogma_probe,
+	.remove = ogma_remove,
+	.driver = {
+		.name = "ogma",
+		.of_match_table = ogma_dt_ids,
+#ifdef CONFIG_PM
+		.pm = &ogma_pm_ops,
+#endif
+		},
+};
+
+module_platform_driver(ogma_driver);
+
+MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
+MODULE_DESCRIPTION("OGMA Ethernet driver");
+MODULE_LICENSE("GPL");
+
+MODULE_ALIAS("platform:ogma");
-- 
1.8.1.2

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
       [not found] <message-id-of-cover-letter>
@ 2014-07-13  6:32   ` Mollie Wu
  2014-07-13  6:29   ` Mollie Wu
                     ` (6 subsequent siblings)
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:32 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	linux-lFZ/pmaqli7XmaaqVzeoHQ, arnd-r2nGTMty4D4,
	olof-nZhT3qVonbNeoWH0uzbU5w, mark.rutland-5wv7dgnIgG8,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, pawel.moll-5wv7dgnIgG8,
	Mollie Wu, Tetsuya Takinishi

Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.
It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
The MB86S7x communicates over the HighPri-NonSecure channel.

Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 drivers/mailbox/Kconfig  |   7 ++
 drivers/mailbox/Makefile |   2 +
 drivers/mailbox/f_mhu.c  | 227 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 236 insertions(+)
 create mode 100644 drivers/mailbox/f_mhu.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c8b5c13..681aac2 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -6,6 +6,13 @@ menuconfig MAILBOX
 	  signals. Say Y if your platform supports hardware mailboxes.
 
 if MAILBOX
+
+config MBOX_F_MHU
+	bool
+	depends on ARCH_MB86S7X
+	help
+	  Say Y here if you want to use the F_MHU IPCM support.
+
 config PL320_MBOX
 	bool "ARM PL320 Mailbox"
 	depends on ARM_AMBA
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 2fa343a..3707e93 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -2,6 +2,8 @@
 
 obj-$(CONFIG_MAILBOX)		+= mailbox.o
 
+obj-$(CONFIG_MBOX_F_MHU)	+= f_mhu.o
+
 obj-$(CONFIG_PL320_MBOX)	+= pl320-ipc.o
 
 obj-$(CONFIG_OMAP_MBOX)		+= omap-mailbox.o
diff --git a/drivers/mailbox/f_mhu.c b/drivers/mailbox/f_mhu.c
new file mode 100644
index 0000000..cf5d3cd
--- /dev/null
+++ b/drivers/mailbox/f_mhu.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013-2014 Fujitsu Semiconductor Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mailbox_controller.h>
+#include <linux/platform_device.h>
+
+#define INTR_STAT_OFS	0x0
+#define INTR_SET_OFS	0x8
+#define INTR_CLR_OFS	0x10
+
+#define MHU_SCFG	0x400
+
+struct mhu_link {
+	unsigned irq;
+	spinlock_t lock; /* channel regs */
+	void __iomem *tx_reg;
+	void __iomem *rx_reg;
+};
+
+struct f_mhu {
+	void __iomem *base;
+	struct clk *clk;
+	struct mhu_link mlink[3];
+	struct mbox_chan chan[3];
+	struct mbox_controller mbox;
+};
+
+static irqreturn_t mhu_rx_interrupt(int irq, void *p)
+{
+	struct mbox_chan *chan = (struct mbox_chan *)p;
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	u32 val;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	/* See NOTE_RX_DONE */
+	val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
+	mbox_chan_received_data(chan, (void *)val);
+
+	/*
+	 * It is agreed with the remote firmware that the receiver
+	 * will clear the STAT register indicating it is ready to
+	 * receive next data - NOTE_RX_DONE
+	 */
+	writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
+
+	return IRQ_HANDLED;
+}
+
+static bool mhu_last_tx_done(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+	u32 val;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	spin_lock_irqsave(&mlink->lock, flags);
+	/* See NOTE_RX_DONE */
+	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	return (val == 0);
+}
+
+static int mhu_send_data(struct mbox_chan *chan, void *data)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!mhu_last_tx_done(chan)) {
+		pr_err("%s:%d Shouldn't have seen the day!\n",
+		       __func__, __LINE__);
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&mlink->lock, flags);
+	writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	return 0;
+}
+
+static int mhu_startup(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+	u32 val;
+	int ret;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	spin_lock_irqsave(&mlink->lock, flags);
+	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
+	writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	ret = request_irq(mlink->irq, mhu_rx_interrupt,
+			  IRQF_SHARED, "mhu_link", chan);
+	if (unlikely(ret)) {
+		pr_err("Unable to aquire IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mhu_shutdown(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	free_irq(mlink->irq, chan);
+}
+
+static struct mbox_chan_ops mhu_ops = {
+	.send_data = mhu_send_data,
+	.startup = mhu_startup,
+	.shutdown = mhu_shutdown,
+	.last_tx_done = mhu_last_tx_done,
+};
+
+static int f_mhu_probe(struct platform_device *pdev)
+{
+	int i, err;
+	struct f_mhu *mhu;
+	struct resource *res;
+	int mhu_reg[3] = {0x0, 0x20, 0x200};
+
+	/* Allocate memory for device */
+	mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
+	if (!mhu) {
+		dev_err(&pdev->dev, "failed to allocate memory.\n");
+		return -EBUSY;
+	}
+
+	mhu->clk = clk_get(&pdev->dev, "clk");
+	if (unlikely(IS_ERR(mhu->clk))) {
+		dev_err(&pdev->dev, "unable to init clock\n");
+		kfree(mhu);
+		return -EINVAL;
+	}
+	clk_prepare_enable(mhu->clk);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mhu->base = ioremap(res->start, resource_size(res));
+	if (!mhu->base) {
+		dev_err(&pdev->dev, "ioremap failed.\n");
+		kfree(mhu);
+		return -EBUSY;
+	}
+
+	/* Let UnTrustedOS's access violations don't bother us */
+	writel_relaxed(0, mhu->base + MHU_SCFG);
+
+	for (i = 0; i < 3; i++) {
+		mhu->chan[i].con_priv = &mhu->mlink[i];
+		spin_lock_init(&mhu->mlink[i].lock);
+		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+		mhu->mlink[i].irq = res->start;
+		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
+		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
+	}
+
+	mhu->mbox.dev = &pdev->dev;
+	mhu->mbox.chans = &mhu->chan[0];
+	mhu->mbox.num_chans = 3;
+	mhu->mbox.ops = &mhu_ops;
+	mhu->mbox.txdone_irq = false;
+	mhu->mbox.txdone_poll = true;
+	mhu->mbox.txpoll_period = 10;
+
+	platform_set_drvdata(pdev, mhu);
+
+	err = mbox_controller_register(&mhu->mbox);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err);
+		iounmap(mhu->base);
+		kfree(mhu);
+	} else {
+		dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
+	}
+
+	return 0;
+}
+
+static const struct of_device_id f_mhu_dt_ids[] = {
+	{ .compatible = "fujitsu,mhu" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
+
+static struct platform_driver f_mhu_driver = {
+	.driver		= {
+		.name	= "f_mhu",
+		.owner = THIS_MODULE,
+		.of_match_table = f_mhu_dt_ids,
+	},
+	.probe		= f_mhu_probe,
+};
+
+static int __init f_mhu_init(void)
+{
+	return platform_driver_register(&f_mhu_driver);
+}
+module_init(f_mhu_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Fujitsu MHU Driver");
+MODULE_AUTHOR("Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>");
-- 
1.8.1.2

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-13  6:32   ` Mollie Wu
  0 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:32 UTC (permalink / raw)
  To: linux-arm-kernel

Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.
It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
The MB86S7x communicates over the HighPri-NonSecure channel.

Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
---
 drivers/mailbox/Kconfig  |   7 ++
 drivers/mailbox/Makefile |   2 +
 drivers/mailbox/f_mhu.c  | 227 +++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 236 insertions(+)
 create mode 100644 drivers/mailbox/f_mhu.c

diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
index c8b5c13..681aac2 100644
--- a/drivers/mailbox/Kconfig
+++ b/drivers/mailbox/Kconfig
@@ -6,6 +6,13 @@ menuconfig MAILBOX
 	  signals. Say Y if your platform supports hardware mailboxes.
 
 if MAILBOX
+
+config MBOX_F_MHU
+	bool
+	depends on ARCH_MB86S7X
+	help
+	  Say Y here if you want to use the F_MHU IPCM support.
+
 config PL320_MBOX
 	bool "ARM PL320 Mailbox"
 	depends on ARM_AMBA
diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
index 2fa343a..3707e93 100644
--- a/drivers/mailbox/Makefile
+++ b/drivers/mailbox/Makefile
@@ -2,6 +2,8 @@
 
 obj-$(CONFIG_MAILBOX)		+= mailbox.o
 
+obj-$(CONFIG_MBOX_F_MHU)	+= f_mhu.o
+
 obj-$(CONFIG_PL320_MBOX)	+= pl320-ipc.o
 
 obj-$(CONFIG_OMAP_MBOX)		+= omap-mailbox.o
diff --git a/drivers/mailbox/f_mhu.c b/drivers/mailbox/f_mhu.c
new file mode 100644
index 0000000..cf5d3cd
--- /dev/null
+++ b/drivers/mailbox/f_mhu.c
@@ -0,0 +1,227 @@
+/*
+ * Copyright (C) 2013-2014 Fujitsu Semiconductor Ltd.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/mutex.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/mailbox_controller.h>
+#include <linux/platform_device.h>
+
+#define INTR_STAT_OFS	0x0
+#define INTR_SET_OFS	0x8
+#define INTR_CLR_OFS	0x10
+
+#define MHU_SCFG	0x400
+
+struct mhu_link {
+	unsigned irq;
+	spinlock_t lock; /* channel regs */
+	void __iomem *tx_reg;
+	void __iomem *rx_reg;
+};
+
+struct f_mhu {
+	void __iomem *base;
+	struct clk *clk;
+	struct mhu_link mlink[3];
+	struct mbox_chan chan[3];
+	struct mbox_controller mbox;
+};
+
+static irqreturn_t mhu_rx_interrupt(int irq, void *p)
+{
+	struct mbox_chan *chan = (struct mbox_chan *)p;
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	u32 val;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	/* See NOTE_RX_DONE */
+	val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
+	mbox_chan_received_data(chan, (void *)val);
+
+	/*
+	 * It is agreed with the remote firmware that the receiver
+	 * will clear the STAT register indicating it is ready to
+	 * receive next data - NOTE_RX_DONE
+	 */
+	writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
+
+	return IRQ_HANDLED;
+}
+
+static bool mhu_last_tx_done(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+	u32 val;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	spin_lock_irqsave(&mlink->lock, flags);
+	/* See NOTE_RX_DONE */
+	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	return (val == 0);
+}
+
+static int mhu_send_data(struct mbox_chan *chan, void *data)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	if (!mhu_last_tx_done(chan)) {
+		pr_err("%s:%d Shouldn't have seen the day!\n",
+		       __func__, __LINE__);
+		return -EBUSY;
+	}
+
+	spin_lock_irqsave(&mlink->lock, flags);
+	writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	return 0;
+}
+
+static int mhu_startup(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+	unsigned long flags;
+	u32 val;
+	int ret;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	spin_lock_irqsave(&mlink->lock, flags);
+	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
+	writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
+	spin_unlock_irqrestore(&mlink->lock, flags);
+
+	ret = request_irq(mlink->irq, mhu_rx_interrupt,
+			  IRQF_SHARED, "mhu_link", chan);
+	if (unlikely(ret)) {
+		pr_err("Unable to aquire IRQ\n");
+		return ret;
+	}
+
+	return 0;
+}
+
+static void mhu_shutdown(struct mbox_chan *chan)
+{
+	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
+
+	pr_debug("%s:%d\n", __func__, __LINE__);
+	free_irq(mlink->irq, chan);
+}
+
+static struct mbox_chan_ops mhu_ops = {
+	.send_data = mhu_send_data,
+	.startup = mhu_startup,
+	.shutdown = mhu_shutdown,
+	.last_tx_done = mhu_last_tx_done,
+};
+
+static int f_mhu_probe(struct platform_device *pdev)
+{
+	int i, err;
+	struct f_mhu *mhu;
+	struct resource *res;
+	int mhu_reg[3] = {0x0, 0x20, 0x200};
+
+	/* Allocate memory for device */
+	mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
+	if (!mhu) {
+		dev_err(&pdev->dev, "failed to allocate memory.\n");
+		return -EBUSY;
+	}
+
+	mhu->clk = clk_get(&pdev->dev, "clk");
+	if (unlikely(IS_ERR(mhu->clk))) {
+		dev_err(&pdev->dev, "unable to init clock\n");
+		kfree(mhu);
+		return -EINVAL;
+	}
+	clk_prepare_enable(mhu->clk);
+
+	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+	mhu->base = ioremap(res->start, resource_size(res));
+	if (!mhu->base) {
+		dev_err(&pdev->dev, "ioremap failed.\n");
+		kfree(mhu);
+		return -EBUSY;
+	}
+
+	/* Let UnTrustedOS's access violations don't bother us */
+	writel_relaxed(0, mhu->base + MHU_SCFG);
+
+	for (i = 0; i < 3; i++) {
+		mhu->chan[i].con_priv = &mhu->mlink[i];
+		spin_lock_init(&mhu->mlink[i].lock);
+		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
+		mhu->mlink[i].irq = res->start;
+		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
+		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
+	}
+
+	mhu->mbox.dev = &pdev->dev;
+	mhu->mbox.chans = &mhu->chan[0];
+	mhu->mbox.num_chans = 3;
+	mhu->mbox.ops = &mhu_ops;
+	mhu->mbox.txdone_irq = false;
+	mhu->mbox.txdone_poll = true;
+	mhu->mbox.txpoll_period = 10;
+
+	platform_set_drvdata(pdev, mhu);
+
+	err = mbox_controller_register(&mhu->mbox);
+	if (err) {
+		dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err);
+		iounmap(mhu->base);
+		kfree(mhu);
+	} else {
+		dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
+	}
+
+	return 0;
+}
+
+static const struct of_device_id f_mhu_dt_ids[] = {
+	{ .compatible = "fujitsu,mhu" },
+	{ /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
+
+static struct platform_driver f_mhu_driver = {
+	.driver		= {
+		.name	= "f_mhu",
+		.owner = THIS_MODULE,
+		.of_match_table = f_mhu_dt_ids,
+	},
+	.probe		= f_mhu_probe,
+};
+
+static int __init f_mhu_init(void)
+{
+	return platform_driver_register(&f_mhu_driver);
+}
+module_init(f_mhu_init);
+
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("Fujitsu MHU Driver");
+MODULE_AUTHOR("Jassi Brar <jaswinder.singh@linaro.org>");
-- 
1.8.1.2

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

* =?y?q?=5BPATCH=208/8=5D=20of=3A=20add=20Fujitsu=20vendor=20prefix?=
       [not found] <message-id-of-cover-letter>
                   ` (6 preceding siblings ...)
  2014-07-13  6:32   ` Mollie Wu
@ 2014-07-13  6:32 ` Mollie Wu
  7 siblings, 0 replies; 88+ messages in thread
From: Mollie Wu @ 2014-07-13  6:32 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: andy.green-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, Mollie Wu, Tetsuya Takinishi

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #1: Type: text/plain; charset=y, Size: 1183 bytes --]

Add 'fujitsu' as the vendor prefix for Fujitsu Semiconductor Ltd.

Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
---
 Documentation/devicetree/bindings/vendor-prefixes.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt
index 46a311e..43d1063 100644
--- a/Documentation/devicetree/bindings/vendor-prefixes.txt
+++ b/Documentation/devicetree/bindings/vendor-prefixes.txt
@@ -48,6 +48,7 @@ est	ESTeem Wireless Modems
 eukrea  Eukréa Electromatique
 excito	Excito
 fsl	Freescale Semiconductor
+fujitsu Fujitsu Semiconductor Ltd.
 GEFanuc	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
 gef	GE Fanuc Intelligent Platforms Embedded Systems, Inc.
 globalscale	Globalscale Technologies, Inc.
-- 
1.8.1.2

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

* Re: [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
  2014-07-13  6:31   ` Mollie Wu
@ 2014-07-14  9:06     ` Tobias Klauser
  -1 siblings, 0 replies; 88+ messages in thread
From: Tobias Klauser @ 2014-07-14  9:06 UTC (permalink / raw)
  To: Mollie Wu
  Cc: linux-arm-kernel, devicetree, netdev, andy.green, patches,
	jaswinder.singh, linux, arnd, olof, mark.rutland, robh+dt,
	pawel.moll, davem, stephen, f.fainelli, romieu,
	Tetsuya Takinishi

On 2014-07-13 at 08:31:47 +0200, Mollie Wu <mollie.wu@linaro.org> wrote:
> This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
> Ethernet + PHY IP used in a variety of their ARM-based ASICs.
> 
> This is being sent as part of a series including the arch support that uses it,
> Fujitsu mb86s7x.
> 
> This driver was originally written by guys inside Fujitsu as an abstracted
> "you can build this for Windows as well" type code, I've removed all that,
> modernized various things, added runtime_pm, and ported it to work with
> Device Tree, using only the bindings already mentioned in
> 
> ./Documentation/devicetree/bindings/net/ethernet.txt
> 
> Bindings documentation is added by this patch.
> 
> Signed-off-by: Andy Green <andy.green@linaro.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
> ---
>  .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
>  drivers/net/ethernet/fujitsu/Kconfig               |  12 +
>  drivers/net/ethernet/fujitsu/Makefile              |   1 +
>  drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
>  drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
>  .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
>  .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
>  10 files changed, 2384 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

[...]

> diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
> new file mode 100644
> index 0000000..09071da
> --- /dev/null
> +++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

[...]

> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops ogma_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
> +	SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
> +};
> +#endif

#ifdef CONFIG_PM can be omitted here since
SET_{SYSTEM_SLEEP,RUNTIME}_PM_OPS will just evaluate empty if CONFIG_PM
is not defined.

> +
> +static const struct of_device_id ogma_dt_ids[] = {
> +	{.compatible = "fujitsu,ogma"},
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, ogma_dt_ids);
> +
> +static struct platform_driver ogma_driver = {
> +	.probe = ogma_probe,
> +	.remove = ogma_remove,
> +	.driver = {
> +		.name = "ogma",
> +		.of_match_table = ogma_dt_ids,
> +#ifdef CONFIG_PM
> +		.pm = &ogma_pm_ops,
> +#endif

With the above #ifdef CONFIG_PM removed, ogma_pm_ops will be all NULL if
CONFIG_PM is not set. Thus, the #ifdef can be removed as well.

> +		},
> +};
> +
> +module_platform_driver(ogma_driver);
> +
> +MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
> +MODULE_DESCRIPTION("OGMA Ethernet driver");
> +MODULE_LICENSE("GPL");
> +
> +MODULE_ALIAS("platform:ogma");
> -- 
> 1.8.1.2

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

* [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
@ 2014-07-14  9:06     ` Tobias Klauser
  0 siblings, 0 replies; 88+ messages in thread
From: Tobias Klauser @ 2014-07-14  9:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 2014-07-13 at 08:31:47 +0200, Mollie Wu <mollie.wu@linaro.org> wrote:
> This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
> Ethernet + PHY IP used in a variety of their ARM-based ASICs.
> 
> This is being sent as part of a series including the arch support that uses it,
> Fujitsu mb86s7x.
> 
> This driver was originally written by guys inside Fujitsu as an abstracted
> "you can build this for Windows as well" type code, I've removed all that,
> modernized various things, added runtime_pm, and ported it to work with
> Device Tree, using only the bindings already mentioned in
> 
> ./Documentation/devicetree/bindings/net/ethernet.txt
> 
> Bindings documentation is added by this patch.
> 
> Signed-off-by: Andy Green <andy.green@linaro.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
> ---
>  .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
>  drivers/net/ethernet/fujitsu/Kconfig               |  12 +
>  drivers/net/ethernet/fujitsu/Makefile              |   1 +
>  drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
>  drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
>  .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
>  .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
>  drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
>  10 files changed, 2384 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

[...]

> diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
> new file mode 100644
> index 0000000..09071da
> --- /dev/null
> +++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c

[...]

> +#ifdef CONFIG_PM
> +static const struct dev_pm_ops ogma_pm_ops = {
> +	SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
> +	SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
> +};
> +#endif

#ifdef CONFIG_PM can be omitted here since
SET_{SYSTEM_SLEEP,RUNTIME}_PM_OPS will just evaluate empty if CONFIG_PM
is not defined.

> +
> +static const struct of_device_id ogma_dt_ids[] = {
> +	{.compatible = "fujitsu,ogma"},
> +	{ /* sentinel */ }
> +};
> +
> +MODULE_DEVICE_TABLE(of, ogma_dt_ids);
> +
> +static struct platform_driver ogma_driver = {
> +	.probe = ogma_probe,
> +	.remove = ogma_remove,
> +	.driver = {
> +		.name = "ogma",
> +		.of_match_table = ogma_dt_ids,
> +#ifdef CONFIG_PM
> +		.pm = &ogma_pm_ops,
> +#endif

With the above #ifdef CONFIG_PM removed, ogma_pm_ops will be all NULL if
CONFIG_PM is not set. Thus, the #ifdef can be removed as well.

> +		},
> +};
> +
> +module_platform_driver(ogma_driver);
> +
> +MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
> +MODULE_DESCRIPTION("OGMA Ethernet driver");
> +MODULE_LICENSE("GPL");
> +
> +MODULE_ALIAS("platform:ogma");
> -- 
> 1.8.1.2

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

* Re: [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
  2014-07-14  9:06     ` Tobias Klauser
@ 2014-07-14 10:36       ` Andy Green
  -1 siblings, 0 replies; 88+ messages in thread
From: Andy Green @ 2014-07-14 10:36 UTC (permalink / raw)
  To: Tobias Klauser
  Cc: Mollie Wu, linux-arm-kernel, devicetree, netdev, Patch Tracking,
	Jaswinder Singh, linux, arnd, Olof Johansson, mark.rutland,
	robh+dt, pawel.moll, David Miller, Stephen Hemminger,
	Florian Fainelli, Francois Romieu, Tetsuya Takinishi

On 14 July 2014 17:06, Tobias Klauser <tklauser@distanz.ch> wrote:
> On 2014-07-13 at 08:31:47 +0200, Mollie Wu <mollie.wu@linaro.org> wrote:
>> This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
>> Ethernet + PHY IP used in a variety of their ARM-based ASICs.
>>
>> This is being sent as part of a series including the arch support that uses it,
>> Fujitsu mb86s7x.
>>
>> This driver was originally written by guys inside Fujitsu as an abstracted
>> "you can build this for Windows as well" type code, I've removed all that,
>> modernized various things, added runtime_pm, and ported it to work with
>> Device Tree, using only the bindings already mentioned in
>>
>> ./Documentation/devicetree/bindings/net/ethernet.txt
>>
>> Bindings documentation is added by this patch.
>>
>> Signed-off-by: Andy Green <andy.green@linaro.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
>> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
>> ---
>>  .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
>>  drivers/net/ethernet/fujitsu/Kconfig               |  12 +
>>  drivers/net/ethernet/fujitsu/Makefile              |   1 +
>>  drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
>>  drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
>>  .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
>>  .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
>>  10 files changed, 2384 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>
> [...]
>
>> diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>> new file mode 100644
>> index 0000000..09071da
>> --- /dev/null
>> +++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>
> [...]
>
>> +#ifdef CONFIG_PM
>> +static const struct dev_pm_ops ogma_pm_ops = {
>> +     SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
>> +     SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
>> +};
>> +#endif
>
> #ifdef CONFIG_PM can be omitted here since
> SET_{SYSTEM_SLEEP,RUNTIME}_PM_OPS will just evaluate empty if CONFIG_PM
> is not defined.
>
>> +
>> +static const struct of_device_id ogma_dt_ids[] = {
>> +     {.compatible = "fujitsu,ogma"},
>> +     { /* sentinel */ }
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, ogma_dt_ids);
>> +
>> +static struct platform_driver ogma_driver = {
>> +     .probe = ogma_probe,
>> +     .remove = ogma_remove,
>> +     .driver = {
>> +             .name = "ogma",
>> +             .of_match_table = ogma_dt_ids,
>> +#ifdef CONFIG_PM
>> +             .pm = &ogma_pm_ops,
>> +#endif
>
> With the above #ifdef CONFIG_PM removed, ogma_pm_ops will be all NULL if
> CONFIG_PM is not set. Thus, the #ifdef can be removed as well.

OK will fix, thanks.

-Andy

>> +             },
>> +};
>> +
>> +module_platform_driver(ogma_driver);
>> +
>> +MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
>> +MODULE_DESCRIPTION("OGMA Ethernet driver");
>> +MODULE_LICENSE("GPL");
>> +
>> +MODULE_ALIAS("platform:ogma");
>> --
>> 1.8.1.2

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

* [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
@ 2014-07-14 10:36       ` Andy Green
  0 siblings, 0 replies; 88+ messages in thread
From: Andy Green @ 2014-07-14 10:36 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 July 2014 17:06, Tobias Klauser <tklauser@distanz.ch> wrote:
> On 2014-07-13 at 08:31:47 +0200, Mollie Wu <mollie.wu@linaro.org> wrote:
>> This driver adds support for "ogma", a Fujitsu Semiconductor Ltd IP Gigabit
>> Ethernet + PHY IP used in a variety of their ARM-based ASICs.
>>
>> This is being sent as part of a series including the arch support that uses it,
>> Fujitsu mb86s7x.
>>
>> This driver was originally written by guys inside Fujitsu as an abstracted
>> "you can build this for Windows as well" type code, I've removed all that,
>> modernized various things, added runtime_pm, and ported it to work with
>> Device Tree, using only the bindings already mentioned in
>>
>> ./Documentation/devicetree/bindings/net/ethernet.txt
>>
>> Bindings documentation is added by this patch.
>>
>> Signed-off-by: Andy Green <andy.green@linaro.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
>> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
>> ---
>>  .../devicetree/bindings/net/fujitsu-ogma.txt       |  43 ++
>>  drivers/net/ethernet/fujitsu/Kconfig               |  12 +
>>  drivers/net/ethernet/fujitsu/Makefile              |   1 +
>>  drivers/net/ethernet/fujitsu/ogma/Makefile         |   6 +
>>  drivers/net/ethernet/fujitsu/ogma/ogma.h           | 380 +++++++++++++
>>  .../ethernet/fujitsu/ogma/ogma_desc_ring_access.c  | 627 +++++++++++++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c   |  95 ++++
>>  .../net/ethernet/fujitsu/ogma/ogma_gmac_access.c   | 295 ++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c    | 592 +++++++++++++++++++
>>  drivers/net/ethernet/fujitsu/ogma/ogma_platform.c  | 333 +++++++++++
>>  10 files changed, 2384 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/net/fujitsu-ogma.txt
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/Makefile
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma.h
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_desc_ring_access.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_ethtool.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_gmac_access.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_netdev.c
>>  create mode 100644 drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>
> [...]
>
>> diff --git a/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>> new file mode 100644
>> index 0000000..09071da
>> --- /dev/null
>> +++ b/drivers/net/ethernet/fujitsu/ogma/ogma_platform.c
>
> [...]
>
>> +#ifdef CONFIG_PM
>> +static const struct dev_pm_ops ogma_pm_ops = {
>> +     SET_SYSTEM_SLEEP_PM_OPS(ogma_pm_suspend, ogma_pm_resume)
>> +     SET_RUNTIME_PM_OPS(ogma_runtime_suspend, ogma_runtime_resume, NULL)
>> +};
>> +#endif
>
> #ifdef CONFIG_PM can be omitted here since
> SET_{SYSTEM_SLEEP,RUNTIME}_PM_OPS will just evaluate empty if CONFIG_PM
> is not defined.
>
>> +
>> +static const struct of_device_id ogma_dt_ids[] = {
>> +     {.compatible = "fujitsu,ogma"},
>> +     { /* sentinel */ }
>> +};
>> +
>> +MODULE_DEVICE_TABLE(of, ogma_dt_ids);
>> +
>> +static struct platform_driver ogma_driver = {
>> +     .probe = ogma_probe,
>> +     .remove = ogma_remove,
>> +     .driver = {
>> +             .name = "ogma",
>> +             .of_match_table = ogma_dt_ids,
>> +#ifdef CONFIG_PM
>> +             .pm = &ogma_pm_ops,
>> +#endif
>
> With the above #ifdef CONFIG_PM removed, ogma_pm_ops will be all NULL if
> CONFIG_PM is not set. Thus, the #ifdef can be removed as well.

OK will fix, thanks.

-Andy

>> +             },
>> +};
>> +
>> +module_platform_driver(ogma_driver);
>> +
>> +MODULE_AUTHOR("Fujitsu Semiconductor Ltd");
>> +MODULE_DESCRIPTION("OGMA Ethernet driver");
>> +MODULE_LICENSE("GPL");
>> +
>> +MODULE_ALIAS("platform:ogma");
>> --
>> 1.8.1.2

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-13  6:28   ` Mollie Wu
@ 2014-07-14 13:33       ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 13:33 UTC (permalink / raw)
  To: Mollie Wu
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	olof-nZhT3qVonbNeoWH0uzbU5w, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	mark.rutland-5wv7dgnIgG8, robh+dt-DgEjT+Ai2ygdnm+yROfE0A,
	pawel.moll-5wv7dgnIgG8, patches-QSEj5FYQhm4dnm+yROfE0A,
	Tetsuya Takinishi

On Sunday 13 July 2014 14:28:31 Mollie Wu wrote:
> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
> And the remote master firmware (called SCB) running on CM3. Linux asks
> for things to be done over Mailbox API, to SCB which controls most of
> the important things. variations S70 & S73 are supported.
> 
> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> Cc: Olof <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>
> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>  arch/arm/Kconfig                                   |   2 +
>  arch/arm/Makefile                                  |   1 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++

Better split out the dts additions into a separate patch, to make the actual code
easier to review. 

> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> new file mode 100644
> index 0000000..44abfe8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> @@ -0,0 +1,22 @@
> +* Fujitsu MB86S7x Power Domains
> +
> +Remote f/w on MB86S7x can enable/disable power to various IPs.
> +
> +Required Properties:
> +- compatible: Should be "fujitsu,mb86s7x-pd"
> +- index: Index of the power gate control for the block

Please always use actual part names in a compatible string, not wildcards
with 'x' in them. If two models are mutually compatible, use the string
for the older product (possibly the smaller number if they
are the same age). In C code, wildcards are fine/.

> +Example:
> +
> +	pd_cpu: genpd@3 {
> +		compatible = "fujitsu,mb86s7x-pd";
> +		index = <3>;
> +	};
> +
> +Example of the node using power domain:
> +
> +	node {
> +		/* ... */
> +		fujitsu,power-domain = <&pd_cpu>;
> +		/* ... */
> +	};

I believe there has been a submission for a generic power domain binding
now. We really shouldn't use vendor specific power domain bindings
any more.

> --- /dev/null
> +++ b/arch/arm/configs/fujitsu_defconfig

Do you need your own defconfig, or can you just add this to the multi_v7_defconfig
file instead?

In either case, please also add an entry to multi_v7_defconfig and enable
all the drivers you need there.

> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> new file mode 100644
> index 0000000..44f5b0c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Kconfig
> @@ -0,0 +1,18 @@
> +config ARCH_MB86S7X
> +	bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> +	select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE

Why the LPAE dependency? Is none of the RAM reachable by regular
kernels?

> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +	struct device_node *node;
> +
> +	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +	if (node && of_device_is_available(node)) {
> +		mcpm_smp_set_ops();
> +		return true;
> +	}
> +
> +	return false;
> +}

Can you use the CPU_METHOD_OF_DECLARE() macro to set your
SMP ops instead?

> +#define IOMAP_DEV(name) { \
> +		.virtual = (unsigned long) MB86S7X_##name##_VIRT, \
> +		.pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
> +		.length = MB86S7X_##name##_SIZE, \
> +		.type = MT_DEVICE, \
> +	}
> +
> +static struct map_desc mb86s7x_io_desc[] = {
> +	IOMAP_DEV(MHU),
> +	IOMAP_DEV(ISRAM),
> +};

What do you need these entries for? You should be able to just ioremap
the registers where needed.

> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
> new file mode 100644
> index 0000000..2b8db1d
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/iomap.h
> @@ -0,0 +1,34 @@
> +/*
> + * arch/arm/mach-mb86s7x/iomap.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __MB86S7X_IOMAP_H
> +#define __MB86S7X_IOMAP_H
> +
> +#include <linux/sizes.h>
> +
> +#define SEC_RSTADDR_OFF		0x3fc
> +
> +#define MB86S7X_MHU_PHYS	0x2b1f0000
> +#define MB86S7X_MHU_SIZE	SZ_4K
> +
> +/* Internal SRAM */
> +#define MB86S7X_ISRAM_PHYS	0x2e000000
> +#define MB86S7X_ISRAM_SIZE	SZ_16K
> +#define MB86S7X_TRAMPOLINE_PHYS	(MB86S7X_ISRAM_PHYS + 0x3c00)
> +
> +#define MB86S7X_ISRAM_VIRT	IOMEM(0xfe000000)
> +#define MB86S7X_MHU_VIRT	(MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
> +
> +/* Non-Secure High-Priority Channel is used */
> +#define MB86S7X_SHM_FROM_SCB_VIRT	(MB86S7X_ISRAM_VIRT + 0x3800)
> +#define MB86S7X_TRAMPOLINE_VIRT		(MB86S7X_ISRAM_VIRT + 0x3c00)
> +
> +#endif /* __MB86S7X_IOMAP_H */

None of the hardcoded register locations should really be needed, please
move them into the device tree.

For the SRAM, we now have a binding that is already shared between multiple
platforms, see rockchips for an example.

> +static __init int mb86s7x_pm_init_power_domain(void)
> +{
> +	struct platform_device *pdev, *master_pd_pdev;
> +	struct device_node *np, *master_node;
> +	struct mb86s7x_pmd_cmd cmd;
> +	struct completion got_rsp;
> +	int ret;
> +
> +	for_each_compatible_node(np, NULL, "fujitsu,mb86s7x-pd") {
> +		struct mb86s7x_pm_domain *pd, *master_pd;

Is it possible to turn this into a platform driver?

Also, we should probably find a better location for pm-domain code
outside of arch/arm.

> diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
> new file mode 100644
> index 0000000..fd5f034
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/scb_mhu.c
> @@ -0,0 +1,447 @@
> +/*
> + * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
> + *
> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */

This should probably go into drivers/soc/

> +#define INTR_STAT_OFS	0x0
> +#define INTR_SET_OFS	0x8
> +#define INTR_CLR_OFS	0x10
> +
> +static int do_xfer(void);
> +static void try_xfer(struct work_struct *ignored);

Please remove the forward declarations by reordering the code.

> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
> +
> +static LIST_HEAD(free_xfers);
> +static LIST_HEAD(pending_xfers);
> +static DEFINE_SPINLOCK(fsm_lock);
> +static struct mbox_client mhu_cl;
> +static struct mbox_chan *mhu_chan;
> +static DECLARE_WORK(scb_work, try_xfer);
> +static mhu_handler_t handler[MHU_NUM_CMDS];
> +
> +static enum {
> +	MHU_PARK = 0,
> +	MHU_WRR, /* Waiting to get Remote's Reply */
> +	MHU_WRL, /* Waiting to send Reply */
> +	MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
> +	MHU_INVLD,
> +} fsm_state;

Ideally, these should all be part of a per-device data structure
referenced from pdev_get_drvdata().

> +static void got_data(u32 code)
> +{
> +	struct completion *c = NULL;
> +	mhu_handler_t hndlr = NULL;
> +	unsigned long flags;
> +	int ev;
> +
> +	if (code & RESP_BIT)
> +		ev = EV_RR;
> +	else
> +		ev = EV_RC;
> +
> +	spin_lock_irqsave(&fsm_lock, flags);
> +
> +	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> +		spin_unlock_irqrestore(&fsm_lock, flags);
> +		pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> +		return;
> +	}
> +	fsm_state = mhu_fsm[fsm_state][ev];
> +
> +	if (code & RESP_BIT) {
> +		c = ax->c;
> +		memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> +		list_move(&ax->node, &free_xfers);
> +		ax = NULL;
> +	} else {
> +		/* Find and dispatch relevant registered handler */
> +		if (code < MHU_NUM_CMDS)
> +			hndlr = handler[code];
> +		if (!hndlr)
> +			pr_err("No handler for CMD_%u\n", code);
> +	}
> +
> +	spin_unlock_irqrestore(&fsm_lock, flags);
> +
> +	if (hndlr)
> +		hndlr(code, cmd_from_scb);
> +	if (c)
> +		complete(c);
> +}
> +
> +static void mhu_recv(struct mbox_client *cl, void *data)
> +{
> +	got_data((u32)data);
> +	schedule_work(&scb_work);
> +}

Why the cast between integer and pointer?

> +			do {
> +retry:
> +				/* Wait until we get reply */
> +				count = 0x1000000;
> +				do {
> +					cpu_relax();
> +					val = readl_relaxed(
> +						rx_reg + INTR_STAT_OFS);
> +				} while (--count && !val);
> +
> +				if (!val) {
> +					pr_err("%s:%d SCB didn't reply\n",
> +					       __func__, __LINE__);
> +					goto retry;

It seems like this loop is unbounded and will just print an error every
16M loops but never give up or re-enable interrupts if called with
interrupts disabled.

It should probably be changed to either enforce being called with interrupts
enabled so you can call msleep() inbetween, or you should add error-handling
in case the remote side does not reply.

> +int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
> +{
> +	unsigned long flags;
> +	int ret = -EINVAL;
> +
> +	spin_lock_irqsave(&fsm_lock, flags);
> +	if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
> +		ret = 0;
> +		handler[cmd] = hndlr;
> +	}
> +	spin_unlock_irqrestore(&fsm_lock, flags);
> +
> +	if (!mhu_chan) {
> +		struct mbox_chan *_ch;
> +
> +		_ch = mbox_request_channel(&mhu_cl);
> +		if (!IS_ERR(_ch))
> +			mhu_chan = _ch;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(mhu_hndlr_set);

This is a rather generic name for an exported symbol, can you prefix
it with the platform name or something appropriate?

> +static const struct of_device_id scb_dt_ids[] = {
> +	{ .compatible = "fujitsu,scb" },
> +	{ /* sentinel */ }
> +};

I don't see a binding for "fujitsu,scb", and it seems like a far too generic
string. Fujitsu is a large company, I wouldn't be surprised if some other
product besides MB87S7X also came with something called an "scb".

> diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
> new file mode 100644
> index 0000000..4f4287e
> --- /dev/null
> +++ b/include/linux/platform_data/mb86s7x_mbox.h
> @@ -0,0 +1,249 @@
> +/*
> + * include/linux/platform_data/mb86s7x_mbox.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */

None of the contents in here are actually platform data, and it seems
they should not be shared across drivers. Most of the data structures
in this file look like they should only be used by one mailbox client
and get moved into the respective driver.

> +struct mb86s7x_peri_clk {
> +	u32 payload_size;
> +	u32 cntrlr;
> +	u32 domain;
> +	u32 port;
> +	u32 en;
> +	u64 freqency;
> +} __packed;

Just mark the last member by itself __packed. I assume you didn't
actually mean to change the alignment of the data structure to one
byte, but just want to say that the last one is misaligned.

> +struct mb86s7x_peri_power {
> +	u32 payload_size;
> +	u32 peri_id;
> +	u32 en;
> +} __packed;

This one doesn't need to be packed at all, same for most of the
others.

> +int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
> +void mb86s7x_reboot(u32 delay);
> +
> +/* This function must not sleep */
> +typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
> +
> +int mhu_hndlr_set(u32 cmd, mhu_handler_t);
> +void mhu_hndlr_clr(u32 cmd, mhu_handler_t);

Doesn't belong here.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-14 13:33       ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 13:33 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday 13 July 2014 14:28:31 Mollie Wu wrote:
> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
> And the remote master firmware (called SCB) running on CM3. Linux asks
> for things to be done over Mailbox API, to SCB which controls most of
> the important things. variations S70 & S73 are supported.
> 
> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Olof <olof@lixom.net>
> Cc: Russell King <linux@arm.linux.org.uk>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
> ---
>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>  arch/arm/Kconfig                                   |   2 +
>  arch/arm/Makefile                                  |   1 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++

Better split out the dts additions into a separate patch, to make the actual code
easier to review. 

> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> new file mode 100644
> index 0000000..44abfe8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> @@ -0,0 +1,22 @@
> +* Fujitsu MB86S7x Power Domains
> +
> +Remote f/w on MB86S7x can enable/disable power to various IPs.
> +
> +Required Properties:
> +- compatible: Should be "fujitsu,mb86s7x-pd"
> +- index: Index of the power gate control for the block

Please always use actual part names in a compatible string, not wildcards
with 'x' in them. If two models are mutually compatible, use the string
for the older product (possibly the smaller number if they
are the same age). In C code, wildcards are fine/.

> +Example:
> +
> +	pd_cpu: genpd at 3 {
> +		compatible = "fujitsu,mb86s7x-pd";
> +		index = <3>;
> +	};
> +
> +Example of the node using power domain:
> +
> +	node {
> +		/* ... */
> +		fujitsu,power-domain = <&pd_cpu>;
> +		/* ... */
> +	};

I believe there has been a submission for a generic power domain binding
now. We really shouldn't use vendor specific power domain bindings
any more.

> --- /dev/null
> +++ b/arch/arm/configs/fujitsu_defconfig

Do you need your own defconfig, or can you just add this to the multi_v7_defconfig
file instead?

In either case, please also add an entry to multi_v7_defconfig and enable
all the drivers you need there.

> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> new file mode 100644
> index 0000000..44f5b0c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Kconfig
> @@ -0,0 +1,18 @@
> +config ARCH_MB86S7X
> +	bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> +	select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE

Why the LPAE dependency? Is none of the RAM reachable by regular
kernels?

> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +	struct device_node *node;
> +
> +	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +	if (node && of_device_is_available(node)) {
> +		mcpm_smp_set_ops();
> +		return true;
> +	}
> +
> +	return false;
> +}

Can you use the CPU_METHOD_OF_DECLARE() macro to set your
SMP ops instead?

> +#define IOMAP_DEV(name) { \
> +		.virtual = (unsigned long) MB86S7X_##name##_VIRT, \
> +		.pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
> +		.length = MB86S7X_##name##_SIZE, \
> +		.type = MT_DEVICE, \
> +	}
> +
> +static struct map_desc mb86s7x_io_desc[] = {
> +	IOMAP_DEV(MHU),
> +	IOMAP_DEV(ISRAM),
> +};

What do you need these entries for? You should be able to just ioremap
the registers where needed.

> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
> new file mode 100644
> index 0000000..2b8db1d
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/iomap.h
> @@ -0,0 +1,34 @@
> +/*
> + * arch/arm/mach-mb86s7x/iomap.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __MB86S7X_IOMAP_H
> +#define __MB86S7X_IOMAP_H
> +
> +#include <linux/sizes.h>
> +
> +#define SEC_RSTADDR_OFF		0x3fc
> +
> +#define MB86S7X_MHU_PHYS	0x2b1f0000
> +#define MB86S7X_MHU_SIZE	SZ_4K
> +
> +/* Internal SRAM */
> +#define MB86S7X_ISRAM_PHYS	0x2e000000
> +#define MB86S7X_ISRAM_SIZE	SZ_16K
> +#define MB86S7X_TRAMPOLINE_PHYS	(MB86S7X_ISRAM_PHYS + 0x3c00)
> +
> +#define MB86S7X_ISRAM_VIRT	IOMEM(0xfe000000)
> +#define MB86S7X_MHU_VIRT	(MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
> +
> +/* Non-Secure High-Priority Channel is used */
> +#define MB86S7X_SHM_FROM_SCB_VIRT	(MB86S7X_ISRAM_VIRT + 0x3800)
> +#define MB86S7X_TRAMPOLINE_VIRT		(MB86S7X_ISRAM_VIRT + 0x3c00)
> +
> +#endif /* __MB86S7X_IOMAP_H */

None of the hardcoded register locations should really be needed, please
move them into the device tree.

For the SRAM, we now have a binding that is already shared between multiple
platforms, see rockchips for an example.

> +static __init int mb86s7x_pm_init_power_domain(void)
> +{
> +	struct platform_device *pdev, *master_pd_pdev;
> +	struct device_node *np, *master_node;
> +	struct mb86s7x_pmd_cmd cmd;
> +	struct completion got_rsp;
> +	int ret;
> +
> +	for_each_compatible_node(np, NULL, "fujitsu,mb86s7x-pd") {
> +		struct mb86s7x_pm_domain *pd, *master_pd;

Is it possible to turn this into a platform driver?

Also, we should probably find a better location for pm-domain code
outside of arch/arm.

> diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
> new file mode 100644
> index 0000000..fd5f034
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/scb_mhu.c
> @@ -0,0 +1,447 @@
> +/*
> + * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
> + *
> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */

This should probably go into drivers/soc/

> +#define INTR_STAT_OFS	0x0
> +#define INTR_SET_OFS	0x8
> +#define INTR_CLR_OFS	0x10
> +
> +static int do_xfer(void);
> +static void try_xfer(struct work_struct *ignored);

Please remove the forward declarations by reordering the code.

> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
> +
> +static LIST_HEAD(free_xfers);
> +static LIST_HEAD(pending_xfers);
> +static DEFINE_SPINLOCK(fsm_lock);
> +static struct mbox_client mhu_cl;
> +static struct mbox_chan *mhu_chan;
> +static DECLARE_WORK(scb_work, try_xfer);
> +static mhu_handler_t handler[MHU_NUM_CMDS];
> +
> +static enum {
> +	MHU_PARK = 0,
> +	MHU_WRR, /* Waiting to get Remote's Reply */
> +	MHU_WRL, /* Waiting to send Reply */
> +	MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
> +	MHU_INVLD,
> +} fsm_state;

Ideally, these should all be part of a per-device data structure
referenced from pdev_get_drvdata().

> +static void got_data(u32 code)
> +{
> +	struct completion *c = NULL;
> +	mhu_handler_t hndlr = NULL;
> +	unsigned long flags;
> +	int ev;
> +
> +	if (code & RESP_BIT)
> +		ev = EV_RR;
> +	else
> +		ev = EV_RC;
> +
> +	spin_lock_irqsave(&fsm_lock, flags);
> +
> +	if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> +		spin_unlock_irqrestore(&fsm_lock, flags);
> +		pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> +		return;
> +	}
> +	fsm_state = mhu_fsm[fsm_state][ev];
> +
> +	if (code & RESP_BIT) {
> +		c = ax->c;
> +		memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> +		list_move(&ax->node, &free_xfers);
> +		ax = NULL;
> +	} else {
> +		/* Find and dispatch relevant registered handler */
> +		if (code < MHU_NUM_CMDS)
> +			hndlr = handler[code];
> +		if (!hndlr)
> +			pr_err("No handler for CMD_%u\n", code);
> +	}
> +
> +	spin_unlock_irqrestore(&fsm_lock, flags);
> +
> +	if (hndlr)
> +		hndlr(code, cmd_from_scb);
> +	if (c)
> +		complete(c);
> +}
> +
> +static void mhu_recv(struct mbox_client *cl, void *data)
> +{
> +	got_data((u32)data);
> +	schedule_work(&scb_work);
> +}

Why the cast between integer and pointer?

> +			do {
> +retry:
> +				/* Wait until we get reply */
> +				count = 0x1000000;
> +				do {
> +					cpu_relax();
> +					val = readl_relaxed(
> +						rx_reg + INTR_STAT_OFS);
> +				} while (--count && !val);
> +
> +				if (!val) {
> +					pr_err("%s:%d SCB didn't reply\n",
> +					       __func__, __LINE__);
> +					goto retry;

It seems like this loop is unbounded and will just print an error every
16M loops but never give up or re-enable interrupts if called with
interrupts disabled.

It should probably be changed to either enforce being called with interrupts
enabled so you can call msleep() inbetween, or you should add error-handling
in case the remote side does not reply.

> +int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
> +{
> +	unsigned long flags;
> +	int ret = -EINVAL;
> +
> +	spin_lock_irqsave(&fsm_lock, flags);
> +	if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
> +		ret = 0;
> +		handler[cmd] = hndlr;
> +	}
> +	spin_unlock_irqrestore(&fsm_lock, flags);
> +
> +	if (!mhu_chan) {
> +		struct mbox_chan *_ch;
> +
> +		_ch = mbox_request_channel(&mhu_cl);
> +		if (!IS_ERR(_ch))
> +			mhu_chan = _ch;
> +	}
> +
> +	return ret;
> +}
> +EXPORT_SYMBOL_GPL(mhu_hndlr_set);

This is a rather generic name for an exported symbol, can you prefix
it with the platform name or something appropriate?

> +static const struct of_device_id scb_dt_ids[] = {
> +	{ .compatible = "fujitsu,scb" },
> +	{ /* sentinel */ }
> +};

I don't see a binding for "fujitsu,scb", and it seems like a far too generic
string. Fujitsu is a large company, I wouldn't be surprised if some other
product besides MB87S7X also came with something called an "scb".

> diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
> new file mode 100644
> index 0000000..4f4287e
> --- /dev/null
> +++ b/include/linux/platform_data/mb86s7x_mbox.h
> @@ -0,0 +1,249 @@
> +/*
> + * include/linux/platform_data/mb86s7x_mbox.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
> + * Copyright:	(C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */

None of the contents in here are actually platform data, and it seems
they should not be shared across drivers. Most of the data structures
in this file look like they should only be used by one mailbox client
and get moved into the respective driver.

> +struct mb86s7x_peri_clk {
> +	u32 payload_size;
> +	u32 cntrlr;
> +	u32 domain;
> +	u32 port;
> +	u32 en;
> +	u64 freqency;
> +} __packed;

Just mark the last member by itself __packed. I assume you didn't
actually mean to change the alignment of the data structure to one
byte, but just want to say that the last one is misaligned.

> +struct mb86s7x_peri_power {
> +	u32 payload_size;
> +	u32 peri_id;
> +	u32 en;
> +} __packed;

This one doesn't need to be packed at all, same for most of the
others.

> +int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
> +void mb86s7x_reboot(u32 delay);
> +
> +/* This function must not sleep */
> +typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
> +
> +int mhu_hndlr_set(u32 cmd, mhu_handler_t);
> +void mhu_hndlr_clr(u32 cmd, mhu_handler_t);

Doesn't belong here.

	Arnd

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

* Re: [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
  2014-07-13  6:31   ` Mollie Wu
@ 2014-07-14 13:50     ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 13:50 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Mollie Wu, devicetree, netdev, mark.rutland, Tetsuya Takinishi,
	andy.green, linux, pawel.moll, patches, stephen, jaswinder.singh,
	robh+dt, romieu, olof, f.fainelli, davem

On Sunday 13 July 2014 14:31:47 Mollie Wu wrote:
> +
> +Example:
> +	eth0: f_taiki {
> +                compatible = "fujitsu,ogma";
> +		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
> +		interrupts = <0 163 0x4>;
> +		clocks = <&clk_alw_0_8>;
> +		phy-mode = "rgmii";
> +		max-speed = <1000>;
> +		max-frame-size = <9000>;
> +		local-mac-address = [ a4 17 31 00 00 ed ];
> +		phy-handle = <&ethphy0>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		ethphy0: ethernet-phy@1 {
> +			device_type = "ethernet-phy";
> +			compatible = "ethernet-phy-ieee802.3-c22";
> +			reg = <1>;
> +		};
> +	};

The name of the device should be "ethernet" rather than "f_taiki".

For the compatible string, it would be good to have a version number included.
If you cannot find out the version of the ogma hardware, add the identifier for
the soc it is used in, e.g.

	compatible = "fujitsu,mb86s73-ogma", "fujitsu,ogma";

The driver only needs to match the generic string, but it's better
to be prepared for the case where we have to support slightly different
variants.

> +static inline void ogma_writel(struct ogma_priv *priv, u32 reg_addr, u32 val)
> +{
> +	writel(val, priv->ioaddr + (reg_addr << 2));
> +}
> +
> +static inline u32 ogma_readl(struct ogma_priv *priv, u32 reg_addr)
> +{
> +	return readl(priv->ioaddr + (reg_addr << 2));
> +}
for best performance, it may be better to use readl_relaxed() by default
and only use the nonrelaxed accesses in the cases where you have to
synchronize with DMA transfers.

> +
> +#define TIMEOUT_SPINS_MAC 1000000
> +
> +static u32 ogma_clk_type(u32 freq)
> +{
> +	if (freq < 35 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_25_35_MHZ;
> +	if (freq < 60 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_35_60_MHZ;
> +	if (freq < 100 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_60_100_MHZ;
> +	if (freq < 150 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_100_150_MHZ;
> +	if (freq < 250 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_150_250_MHZ;
> +
> +	return OGMA_GMAC_GAR_REG_CR_250_300_MHZ;
> +}
> +
> +static int ogma_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
> +{
> +	u32 timeout = TIMEOUT_SPINS_MAC;
> +
> +	while (--timeout && ogma_readl(priv, addr) & mask)
> +		;
> +	if (!timeout) {
> +		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}

The callers of this function seem to all be from non-atomic context, so it
would be good to occasionally msleep() here, either after each try, or
after initially spinning for as long as it takes to succeed most of the
time.

> +
> +static void ogma_napi_tx_processing(struct napi_struct *napi_p)
> +{
> +	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
> +
> +	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
> +	ogma_clean_tx_desc_ring(priv);
> +
> +	if (netif_queue_stopped(priv->net_device) &&
> +	    ogma_get_tx_avail_num(priv) >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX)
> +		netif_wake_queue(priv->net_device);
> +}

You should probably call netdev_tx_completed_queue() here and 
netdev_tx_sent_queue() when sending the frame. See http://lwn.net/Articles/454390/
for a description of the API.

	Arnd

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

* [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
@ 2014-07-14 13:50     ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 13:50 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday 13 July 2014 14:31:47 Mollie Wu wrote:
> +
> +Example:
> +	eth0: f_taiki {
> +                compatible = "fujitsu,ogma";
> +		reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
> +		interrupts = <0 163 0x4>;
> +		clocks = <&clk_alw_0_8>;
> +		phy-mode = "rgmii";
> +		max-speed = <1000>;
> +		max-frame-size = <9000>;
> +		local-mac-address = [ a4 17 31 00 00 ed ];
> +		phy-handle = <&ethphy0>;
> +
> +		#address-cells = <1>;
> +		#size-cells = <0>;
> +
> +		ethphy0: ethernet-phy at 1 {
> +			device_type = "ethernet-phy";
> +			compatible = "ethernet-phy-ieee802.3-c22";
> +			reg = <1>;
> +		};
> +	};

The name of the device should be "ethernet" rather than "f_taiki".

For the compatible string, it would be good to have a version number included.
If you cannot find out the version of the ogma hardware, add the identifier for
the soc it is used in, e.g.

	compatible = "fujitsu,mb86s73-ogma", "fujitsu,ogma";

The driver only needs to match the generic string, but it's better
to be prepared for the case where we have to support slightly different
variants.

> +static inline void ogma_writel(struct ogma_priv *priv, u32 reg_addr, u32 val)
> +{
> +	writel(val, priv->ioaddr + (reg_addr << 2));
> +}
> +
> +static inline u32 ogma_readl(struct ogma_priv *priv, u32 reg_addr)
> +{
> +	return readl(priv->ioaddr + (reg_addr << 2));
> +}
for best performance, it may be better to use readl_relaxed() by default
and only use the nonrelaxed accesses in the cases where you have to
synchronize with DMA transfers.

> +
> +#define TIMEOUT_SPINS_MAC 1000000
> +
> +static u32 ogma_clk_type(u32 freq)
> +{
> +	if (freq < 35 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_25_35_MHZ;
> +	if (freq < 60 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_35_60_MHZ;
> +	if (freq < 100 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_60_100_MHZ;
> +	if (freq < 150 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_100_150_MHZ;
> +	if (freq < 250 * OGMA_CLK_MHZ)
> +		return OGMA_GMAC_GAR_REG_CR_150_250_MHZ;
> +
> +	return OGMA_GMAC_GAR_REG_CR_250_300_MHZ;
> +}
> +
> +static int ogma_wait_while_busy(struct ogma_priv *priv, u32 addr, u32 mask)
> +{
> +	u32 timeout = TIMEOUT_SPINS_MAC;
> +
> +	while (--timeout && ogma_readl(priv, addr) & mask)
> +		;
> +	if (!timeout) {
> +		netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
> +		return -ETIMEDOUT;
> +	}
> +
> +	return 0;
> +}

The callers of this function seem to all be from non-atomic context, so it
would be good to occasionally msleep() here, either after each try, or
after initially spinning for as long as it takes to succeed most of the
time.

> +
> +static void ogma_napi_tx_processing(struct napi_struct *napi_p)
> +{
> +	struct ogma_priv *priv = container_of(napi_p, struct ogma_priv, napi);
> +
> +	ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
> +	ogma_clean_tx_desc_ring(priv);
> +
> +	if (netif_queue_stopped(priv->net_device) &&
> +	    ogma_get_tx_avail_num(priv) >= OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX)
> +		netif_wake_queue(priv->net_device);
> +}

You should probably call netdev_tx_completed_queue() here and 
netdev_tx_sent_queue() when sending the frame. See http://lwn.net/Articles/454390/
for a description of the API.

	Arnd

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

* Re: [PATCH 6/8] net: ethernet driver: Fujitsu OGMA
  2014-07-14 13:50     ` Arnd Bergmann
  (?)
@ 2014-07-14 14:00     ` Andy Green
  -1 siblings, 0 replies; 88+ messages in thread
From: Andy Green @ 2014-07-14 14:00 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: mark.rutland, David Miller, robh+dt, Mollie Wu, linux,
	Tetsuya Takinishi, stephen, romieu, linux-arm-kernel, netdev,
	olof, jaswinder.singh, f.fainelli, patches, pawel.moll,
	devicetree

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

On 14 Jul 2014 21:51, "Arnd Bergmann" <arnd@arndb.de> wrote:
>
> On Sunday 13 July 2014 14:31:47 Mollie Wu wrote:
> > +
> > +Example:
> > +     eth0: f_taiki {
> > +                compatible = "fujitsu,ogma";
> > +             reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0
0x3161c000 0x4000>;
> > +             interrupts = <0 163 0x4>;
> > +             clocks = <&clk_alw_0_8>;
> > +             phy-mode = "rgmii";
> > +             max-speed = <1000>;
> > +             max-frame-size = <9000>;
> > +             local-mac-address = [ a4 17 31 00 00 ed ];
> > +             phy-handle = <&ethphy0>;
> > +
> > +             #address-cells = <1>;
> > +             #size-cells = <0>;
> > +
> > +             ethphy0: ethernet-phy@1 {
> > +                     device_type = "ethernet-phy";
> > +                     compatible = "ethernet-phy-ieee802.3-c22";
> > +                     reg = <1>;
> > +             };
> > +     };
>
> The name of the device should be "ethernet" rather than "f_taiki".

OK.

> For the compatible string, it would be good to have a version number
included.
> If you cannot find out the version of the ogma hardware, add the
identifier for
> the soc it is used in, e.g.
>
>         compatible = "fujitsu,mb86s73-ogma", "fujitsu,ogma";
>
> The driver only needs to match the generic string, but it's better
> to be prepared for the case where we have to support slightly different
> variants.

Yes it's a good idea, thanks.

> > +static inline void ogma_writel(struct ogma_priv *priv, u32 reg_addr,
u32 val)
> > +{
> > +     writel(val, priv->ioaddr + (reg_addr << 2));
> > +}
> > +
> > +static inline u32 ogma_readl(struct ogma_priv *priv, u32 reg_addr)
> > +{
> > +     return readl(priv->ioaddr + (reg_addr << 2));
> > +}
> for best performance, it may be better to use readl_relaxed() by default
> and only use the nonrelaxed accesses in the cases where you have to
> synchronize with DMA transfers.

OK.

> > +
> > +#define TIMEOUT_SPINS_MAC 1000000
> > +
> > +static u32 ogma_clk_type(u32 freq)
> > +{
> > +     if (freq < 35 * OGMA_CLK_MHZ)
> > +             return OGMA_GMAC_GAR_REG_CR_25_35_MHZ;
> > +     if (freq < 60 * OGMA_CLK_MHZ)
> > +             return OGMA_GMAC_GAR_REG_CR_35_60_MHZ;
> > +     if (freq < 100 * OGMA_CLK_MHZ)
> > +             return OGMA_GMAC_GAR_REG_CR_60_100_MHZ;
> > +     if (freq < 150 * OGMA_CLK_MHZ)
> > +             return OGMA_GMAC_GAR_REG_CR_100_150_MHZ;
> > +     if (freq < 250 * OGMA_CLK_MHZ)
> > +             return OGMA_GMAC_GAR_REG_CR_150_250_MHZ;
> > +
> > +     return OGMA_GMAC_GAR_REG_CR_250_300_MHZ;
> > +}
> > +
> > +static int ogma_wait_while_busy(struct ogma_priv *priv, u32 addr, u32
mask)
> > +{
> > +     u32 timeout = TIMEOUT_SPINS_MAC;
> > +
> > +     while (--timeout && ogma_readl(priv, addr) & mask)
> > +             ;
> > +     if (!timeout) {
> > +             netdev_WARN(priv->net_device, "%s: timeout\n", __func__);
> > +             return -ETIMEDOUT;
> > +     }
> > +
> > +     return 0;
> > +}
>
> The callers of this function seem to all be from non-atomic context, so it
> would be good to occasionally msleep() here, either after each try, or
> after initially spinning for as long as it takes to succeed most of the
> time.

OK.  Normally it should complete quickly, so the initial spin idea sounds
like the way.

> > +
> > +static void ogma_napi_tx_processing(struct napi_struct *napi_p)
> > +{
> > +     struct ogma_priv *priv = container_of(napi_p, struct ogma_priv,
napi);
> > +
> > +     ogma_ring_irq_clr(priv, OGMA_RING_TX, OGMA_IRQ_EMPTY);
> > +     ogma_clean_tx_desc_ring(priv);
> > +
> > +     if (netif_queue_stopped(priv->net_device) &&
> > +         ogma_get_tx_avail_num(priv) >=
OGMA_NETDEV_TX_PKT_SCAT_NUM_MAX)
> > +             netif_wake_queue(priv->net_device);
> > +}
>
> You should probably call netdev_tx_completed_queue() here and
> netdev_tx_sent_queue() when sending the frame. See
http://lwn.net/Articles/454390/
> for a description of the API.

OK... thanks a lot for the review, I will fix these and send the rest
version in a few days.

-Andy

>         Arnd
>

[-- Attachment #2: Type: text/html, Size: 5898 bytes --]

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

* Re: [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
  2014-07-13  6:29   ` Mollie Wu
@ 2014-07-14 14:04     ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 14:04 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: mark.rutland, devicetree, Mollie Wu, linux, anton, pawel.moll,
	Tetsuya Takinishi, patches, linux-mmc, chris, robh+dt,
	Vincent Yang, jaswinder.singh, andy.green, olof

On Sunday 13 July 2014 14:29:31 Mollie Wu wrote:
> +Required properties:
> +- compatible: "fujitsu,f-sdh30"
> +- voltage-ranges : This is a list of pairs. In each pair, two cells
> +  are required. First cell specifies minimum slot voltage (mV), second
> +  cell specifies maximum slot voltage (mV). In case of supported voltage
> +  range is discontinuous, several ranges could be specified as a list.
> +
> +Optional properties:
> +- pwr-mux-gpios: This is one optional gpio for controlling a power mux
> +  which switches between two power supplies. 3.3V is selected when gpio
> +  is high, and 1.8V is selected when gpio is low. This voltage is used
> +  for signal level.

It sounds like what you really want here is a reference to a gpio-regulator
node and, and to put the details of the voltage switching in there.

> diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
> new file mode 100644
> index 0000000..8d23f2d
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci_f_sdh30.c
> @@ -0,0 +1,380 @@
> +/*
> + * linux/drivers/mmc/host/sdhci_f_sdh30.c
> + *
> + * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
> + *              Vincent Yang <vincent.yang@tw.fujitsu.com>
> + * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
> +#include <linux/gpio.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/suspend.h>
> +
> +#include "sdhci.h"
> +#include "sdhci-pltfm.h"
> +#include "../core/core.h"

Should this be <linux/mmc/core.h>? A device driver should not be looking
at the drivers/mmc/core/core.h.

> +#define DRIVER_NAME "f_sdh30"

This macro doesn't seem to serve any purpose, it would be easier to
read if you open-code this.

> +
> +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
> +{
> +	struct f_sdhost_priv *priv = sdhci_priv(host);
> +	u32 ctrl = 0;
> +
> +	usleep_range(2500, 3000);
> +	ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
> +	ctrl |= F_SDH30_CRES_O_DN;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +	ctrl |= F_SDH30_MSEL_O_1_8;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +
> +	if (gpio_is_valid(priv->gpio_select_1v8)) {
> +		dev_info(priv->dev, "%s: setting gpio\n", __func__);
> +		gpio_direction_output(priv->gpio_select_1v8, 0);
> +	}
> +
> +	ctrl &= ~F_SDH30_CRES_O_DN;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +	usleep_range(2500, 3000);
> +
> +	if (priv->vendor_hs200) {
> +		dev_info(priv->dev, "%s: setting hs200\n", __func__);
> +		ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
> +		ctrl |= priv->vendor_hs200;
> +		sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
> +	}
> +
> +	ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
> +	ctrl |= F_SDH30_CMD_CHK_DIS;
> +	sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
> +}

I think this should really use the regulator API.

If the regular is defined properly, this will work without any extra code.

> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
> +{
> +	return F_SDH30_MIN_CLOCK;
> +}
> +
> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
> +{
> +	struct f_sdhost_priv *priv = sdhci_priv(host);
> +
> +	if (gpio_is_valid(priv->gpio_select_1v8))
> +		gpio_direction_output(priv->gpio_select_1v8, 1);
> +
> +	if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
> +		sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
> +		mmiowb();
> +	}

Can you explain the mmiowb call here?

> +
> +	if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width)) {
> +		switch (bus_width) {
> +		case 8:
> +			dev_info(dev, "Applying 8 bit bus width\n");
> +			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
> +			break;
> +		case 4:
> +			dev_info(dev, "Applying 4 bit bus width\n");
> +			host->mmc->caps |= MMC_CAP_4_BIT_DATA;
> +			break;
> +		case 1:
> +		default:
> +			dev_err(dev, "Invalid bus width: %u\n", bus_width);
> +			break;
> +		}
> +	}

This should probably be done in generic sdhci code somewhere. How about
adding it to sdhci_get_of_property instead_
> +	priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
> +	if (!IS_ERR(priv->clk_sd4)) {
> +		ret = clk_prepare_enable(priv->clk_sd4);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
> +			goto err_clk1;
> +		}
> +	}
> +	priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
> +	if (!IS_ERR(priv->clk_b)) {
> +		ret = clk_prepare_enable(priv->clk_b);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
> +			goto err_clk2;
> +		}
> +	}

Please pick clock names that match what some of the other drivers use.

Ideally some of that should also move to common code.

	Arnd

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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
@ 2014-07-14 14:04     ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 14:04 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday 13 July 2014 14:29:31 Mollie Wu wrote:
> +Required properties:
> +- compatible: "fujitsu,f-sdh30"
> +- voltage-ranges : This is a list of pairs. In each pair, two cells
> +  are required. First cell specifies minimum slot voltage (mV), second
> +  cell specifies maximum slot voltage (mV). In case of supported voltage
> +  range is discontinuous, several ranges could be specified as a list.
> +
> +Optional properties:
> +- pwr-mux-gpios: This is one optional gpio for controlling a power mux
> +  which switches between two power supplies. 3.3V is selected when gpio
> +  is high, and 1.8V is selected when gpio is low. This voltage is used
> +  for signal level.

It sounds like what you really want here is a reference to a gpio-regulator
node and, and to put the details of the voltage switching in there.

> diff --git a/drivers/mmc/host/sdhci_f_sdh30.c b/drivers/mmc/host/sdhci_f_sdh30.c
> new file mode 100644
> index 0000000..8d23f2d
> --- /dev/null
> +++ b/drivers/mmc/host/sdhci_f_sdh30.c
> @@ -0,0 +1,380 @@
> +/*
> + * linux/drivers/mmc/host/sdhci_f_sdh30.c
> + *
> + * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
> + *              Vincent Yang <vincent.yang@tw.fujitsu.com>
> + * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + */
> +
> +#include <linux/err.h>
> +#include <linux/delay.h>
> +#include <linux/module.h>
> +#include <linux/mmc/sd.h>
> +#include <linux/mmc/host.h>
> +#include <linux/mmc/card.h>
> +#include <linux/gpio.h>
> +#include <linux/of_gpio.h>
> +#include <linux/of_address.h>
> +#include <linux/platform_device.h>
> +#include <linux/pm.h>
> +#include <linux/pm_runtime.h>
> +#include <linux/suspend.h>
> +
> +#include "sdhci.h"
> +#include "sdhci-pltfm.h"
> +#include "../core/core.h"

Should this be <linux/mmc/core.h>? A device driver should not be looking
at the drivers/mmc/core/core.h.

> +#define DRIVER_NAME "f_sdh30"

This macro doesn't seem to serve any purpose, it would be easier to
read if you open-code this.

> +
> +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
> +{
> +	struct f_sdhost_priv *priv = sdhci_priv(host);
> +	u32 ctrl = 0;
> +
> +	usleep_range(2500, 3000);
> +	ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
> +	ctrl |= F_SDH30_CRES_O_DN;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +	ctrl |= F_SDH30_MSEL_O_1_8;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +
> +	if (gpio_is_valid(priv->gpio_select_1v8)) {
> +		dev_info(priv->dev, "%s: setting gpio\n", __func__);
> +		gpio_direction_output(priv->gpio_select_1v8, 0);
> +	}
> +
> +	ctrl &= ~F_SDH30_CRES_O_DN;
> +	sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
> +	usleep_range(2500, 3000);
> +
> +	if (priv->vendor_hs200) {
> +		dev_info(priv->dev, "%s: setting hs200\n", __func__);
> +		ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
> +		ctrl |= priv->vendor_hs200;
> +		sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
> +	}
> +
> +	ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
> +	ctrl |= F_SDH30_CMD_CHK_DIS;
> +	sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
> +}

I think this should really use the regulator API.

If the regular is defined properly, this will work without any extra code.

> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
> +{
> +	return F_SDH30_MIN_CLOCK;
> +}
> +
> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
> +{
> +	struct f_sdhost_priv *priv = sdhci_priv(host);
> +
> +	if (gpio_is_valid(priv->gpio_select_1v8))
> +		gpio_direction_output(priv->gpio_select_1v8, 1);
> +
> +	if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
> +		sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
> +		mmiowb();
> +	}

Can you explain the mmiowb call here?

> +
> +	if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width)) {
> +		switch (bus_width) {
> +		case 8:
> +			dev_info(dev, "Applying 8 bit bus width\n");
> +			host->mmc->caps |= MMC_CAP_8_BIT_DATA;
> +			break;
> +		case 4:
> +			dev_info(dev, "Applying 4 bit bus width\n");
> +			host->mmc->caps |= MMC_CAP_4_BIT_DATA;
> +			break;
> +		case 1:
> +		default:
> +			dev_err(dev, "Invalid bus width: %u\n", bus_width);
> +			break;
> +		}
> +	}

This should probably be done in generic sdhci code somewhere. How about
adding it to sdhci_get_of_property instead_
> +	priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
> +	if (!IS_ERR(priv->clk_sd4)) {
> +		ret = clk_prepare_enable(priv->clk_sd4);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
> +			goto err_clk1;
> +		}
> +	}
> +	priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
> +	if (!IS_ERR(priv->clk_b)) {
> +		ret = clk_prepare_enable(priv->clk_b);
> +		if (ret < 0) {
> +			dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
> +			goto err_clk2;
> +		}
> +	}

Please pick clock names that match what some of the other drivers use.

Ideally some of that should also move to common code.

	Arnd

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

* Re: [PATCH 4/8] clk: Add clock driver for mb86s7x
  2014-07-13  6:30   ` Mollie Wu
@ 2014-07-14 14:08       ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 14:08 UTC (permalink / raw)
  To: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r
  Cc: Mollie Wu, devicetree-u79uwXL29TY76Z2rM5mHXA,
	mark.rutland-5wv7dgnIgG8, Tetsuya Takinishi,
	andy.green-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	pawel.moll-5wv7dgnIgG8, patches-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, olof-nZhT3qVonbNeoWH0uzbU5w

On Sunday 13 July 2014 14:30:52 Mollie Wu wrote:

> ---
>  .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
>  3 files changed, 385 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>  create mode 100644 drivers/clk/clk-mb86s7x.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
> new file mode 100644
> index 0000000..4a17d79
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
> @@ -0,0 +1,32 @@
> +Fujitsu CRG11 clock driver bindings
> +-----------------------------------
> +
> +Required properties :
> +- compatible : Shall contain "fujitsu,mb86s7x_clk"

No wildcards in compatible strings please.

> +- #clock-cells : Shall be 0
> +- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
> +- domain : [0, 15]
> +- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK

It would be good to be a bit more verbose here.

> +
> +struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
> +{
> +	struct clk_init_data init;
> +	struct cl_clk *clc;
> +
> +	clc = kzalloc(sizeof(*clc), GFP_KERNEL);
> +	if (!clc) {
> +		pr_err("could not allocate cl_clk\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	clc->hw.init = &init;
> +	clc->cluster = topology_physical_package_id(cpu_dev->id);
> +
> +	init.name = dev_name(cpu_dev);
> +	init.ops = &clk_clc_ops;
> +	init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
> +	init.num_parents = 0;
> +
> +	return devm_clk_register(cpu_dev, &clc->hw);
> +}
> +
> +static int mb86s7x_clclk_of_init(void)
> +{
> +	int cpu;
> +	struct clk *clk;
> +
> +	for_each_possible_cpu(cpu) {
> +		struct device *cpu_dev = get_cpu_device(cpu);
> +
> +		if (!cpu_dev) {
> +			pr_err("failed to get cpu%d device\n", cpu);
> +			continue;
> +		}
> +
> +		clk = mb86s7x_clclk_register(cpu_dev);
> +		if (IS_ERR(clk)) {
> +			pr_err("failed to register cpu%d clock\n", cpu);
> +			continue;
> +		}
> +		if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
> +			pr_err("failed to register cpu%d clock lookup\n", cpu);
> +			continue;
> +		}
> +		pr_debug("registered clk for %s\n", dev_name(cpu_dev));
> +	}
> +
> +	platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
> +
> +	return 0;
> +}
> +module_init(mb86s7x_clclk_of_init);
> 

This looks weird: why don't you probe the clocks from DT like normal?

Why do you register a platform device here? Are you trying to hide the fact that
the cpufreq stuff still doesn't use proper DT probing?

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

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

* [PATCH 4/8] clk: Add clock driver for mb86s7x
@ 2014-07-14 14:08       ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-14 14:08 UTC (permalink / raw)
  To: linux-arm-kernel

On Sunday 13 July 2014 14:30:52 Mollie Wu wrote:

> ---
>  .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
>  drivers/clk/Makefile                               |   1 +
>  drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
>  3 files changed, 385 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>  create mode 100644 drivers/clk/clk-mb86s7x.c
> 
> diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
> new file mode 100644
> index 0000000..4a17d79
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
> @@ -0,0 +1,32 @@
> +Fujitsu CRG11 clock driver bindings
> +-----------------------------------
> +
> +Required properties :
> +- compatible : Shall contain "fujitsu,mb86s7x_clk"

No wildcards in compatible strings please.

> +- #clock-cells : Shall be 0
> +- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
> +- domain : [0, 15]
> +- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK

It would be good to be a bit more verbose here.

> +
> +struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
> +{
> +	struct clk_init_data init;
> +	struct cl_clk *clc;
> +
> +	clc = kzalloc(sizeof(*clc), GFP_KERNEL);
> +	if (!clc) {
> +		pr_err("could not allocate cl_clk\n");
> +		return ERR_PTR(-ENOMEM);
> +	}
> +
> +	clc->hw.init = &init;
> +	clc->cluster = topology_physical_package_id(cpu_dev->id);
> +
> +	init.name = dev_name(cpu_dev);
> +	init.ops = &clk_clc_ops;
> +	init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
> +	init.num_parents = 0;
> +
> +	return devm_clk_register(cpu_dev, &clc->hw);
> +}
> +
> +static int mb86s7x_clclk_of_init(void)
> +{
> +	int cpu;
> +	struct clk *clk;
> +
> +	for_each_possible_cpu(cpu) {
> +		struct device *cpu_dev = get_cpu_device(cpu);
> +
> +		if (!cpu_dev) {
> +			pr_err("failed to get cpu%d device\n", cpu);
> +			continue;
> +		}
> +
> +		clk = mb86s7x_clclk_register(cpu_dev);
> +		if (IS_ERR(clk)) {
> +			pr_err("failed to register cpu%d clock\n", cpu);
> +			continue;
> +		}
> +		if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
> +			pr_err("failed to register cpu%d clock lookup\n", cpu);
> +			continue;
> +		}
> +		pr_debug("registered clk for %s\n", dev_name(cpu_dev));
> +	}
> +
> +	platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
> +
> +	return 0;
> +}
> +module_init(mb86s7x_clclk_of_init);
> 

This looks weird: why don't you probe the clocks from DT like normal?

Why do you register a platform device here? Are you trying to hide the fact that
the cpufreq stuff still doesn't use proper DT probing?

	Arnd

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-13  6:28   ` Mollie Wu
@ 2014-07-15 15:11       ` Rob Herring
  -1 siblings, 0 replies; 88+ messages in thread
From: Rob Herring @ 2014-07-15 15:11 UTC (permalink / raw)
  To: Mollie Wu
  Cc: devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	andy.green-QSEj5FYQhm4dnm+yROfE0A, Arnd Bergmann,
	Jaswinder Singh, Olof Johansson, Russell King - ARM Linux,
	Mark Rutland, Rob Herring, Pawel Moll, Linaro Patches,
	Tetsuya Takinishi, Nicolas Pitre

Adding Nico for cluster PM code.

On Sun, Jul 13, 2014 at 1:28 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
> And the remote master firmware (called SCB) running on CM3. Linux asks
> for things to be done over Mailbox API, to SCB which controls most of
> the important things. variations S70 & S73 are supported.
>
> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
> Cc: Olof <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>
> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>  arch/arm/Kconfig                                   |   2 +
>  arch/arm/Makefile                                  |   1 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
>  17 files changed, 3183 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>  create mode 100644 arch/arm/boot/dts/mb86s70.dtsi
>  create mode 100644 arch/arm/boot/dts/mb86s70eb.dts
>  create mode 100644 arch/arm/boot/dts/mb86s73.dtsi
>  create mode 100644 arch/arm/boot/dts/mb86s73eb.dts
>  create mode 100644 arch/arm/configs/fujitsu_defconfig
>  create mode 100644 arch/arm/mach-mb86s7x/Kconfig
>  create mode 100644 arch/arm/mach-mb86s7x/Makefile
>  create mode 100644 arch/arm/mach-mb86s7x/board.c
>  create mode 100644 arch/arm/mach-mb86s7x/iomap.h
>  create mode 100644 arch/arm/mach-mb86s7x/mcpm.c
>  create mode 100644 arch/arm/mach-mb86s7x/pm_domains.c
>  create mode 100644 arch/arm/mach-mb86s7x/scb_mhu.c
>  create mode 100644 include/linux/platform_data/mb86s7x_mbox.h
>
> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> new file mode 100644
> index 0000000..44abfe8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> @@ -0,0 +1,22 @@
> +* Fujitsu MB86S7x Power Domains
> +
> +Remote f/w on MB86S7x can enable/disable power to various IPs.
> +
> +Required Properties:
> +- compatible: Should be "fujitsu,mb86s7x-pd"
> +- index: Index of the power gate control for the block
> +
> +Example:
> +
> +       pd_cpu: genpd@3 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <3>;
> +       };
> +
> +Example of the node using power domain:
> +
> +       node {
> +               /* ... */
> +               fujitsu,power-domain = <&pd_cpu>;
> +               /* ... */
> +       };
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 245058b..44fd319 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -955,6 +955,8 @@ source "arch/arm/mach-kirkwood/Kconfig"
>
>  source "arch/arm/mach-ks8695/Kconfig"
>
> +source "arch/arm/mach-mb86s7x/Kconfig"
> +
>  source "arch/arm/mach-msm/Kconfig"
>
>  source "arch/arm/mach-moxart/Kconfig"
> diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> index 6721fab..d6ec5cd 100644
> --- a/arch/arm/Makefile
> +++ b/arch/arm/Makefile
> @@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_KEYSTONE)             += keystone
>  machine-$(CONFIG_ARCH_KIRKWOOD)                += kirkwood
>  machine-$(CONFIG_ARCH_KS8695)          += ks8695
>  machine-$(CONFIG_ARCH_LPC32XX)         += lpc32xx
> +machine-$(CONFIG_ARCH_MB86S7X)         += mb86s7x
>  machine-$(CONFIG_ARCH_MMP)             += mmp
>  machine-$(CONFIG_ARCH_MOXART)          += moxart
>  machine-$(CONFIG_ARCH_MSM)             += msm
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index adb5ed9..0c8addb 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -154,6 +154,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += $(kirkwood)
>  dtb-$(CONFIG_MACH_KIRKWOOD) += $(kirkwood)
>  dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
>  dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
> +dtb-$(CONFIG_ARCH_MB86S7X) += mb86s70eb.dtb mb86s73eb.dtb
>  dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
>  dtb-$(CONFIG_ARCH_MXC) += \
>         imx25-eukrea-mbimxsd25-baseboard.dtb \
> diff --git a/arch/arm/boot/dts/mb86s70.dtsi b/arch/arm/boot/dts/mb86s70.dtsi
> new file mode 100644
> index 0000000..b6d8970
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s70.dtsi
> @@ -0,0 +1,635 @@
> +
> +/dts-v1/;
> +
> +/ {
> +       model = "Fujitsu mb86s70";
> +       compatible = "fujitsu,mb86s70";
> +       interrupt-parent = <&gic>;
> +       #address-cells = <2>;
> +       #size-cells = <1>;
> +
> +       aliases {
> +               serial0 = &uart0;
> +               serial1 = &uart1;
> +               serial2 = &uart2;
> +       };
> +
> +       cpus {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               cpu0: cpu@0 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x100>;

reg and the unit-address (cpu@0) should match. So either you need
cpu@100 or "reg = <0>;".

Same thing on the other cpu entries.

> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000  900000
> +                       >;

What's the point in a single freq?

You can add this later as the current operating-points binding has
several limitations and is likely to get replaced.

> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu1: cpu@1 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x101>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000  900000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu2: cpu@2 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a15";
> +                       reg = <0x0>;
> +                       cci-control-port = <&cci_control4>;
> +                       clock-frequency = <1000000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               1200000 900000
> +                               1600000 1000000
> +                               2000000 1100000
> +                               2400000 1200000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu3: cpu@3 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a15";
> +                       reg = <0x1>;
> +                       cci-control-port = <&cci_control4>;
> +                       clock-frequency = <1000000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               1200000 900000
> +                               1600000 1000000
> +                               2000000 1100000
> +                               2400000 1200000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +       };
> +
> +       cci@2c090000 {
> +               compatible = "arm,cci-400";
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +               reg = <0 0x2c090000 0x1000>;
> +               ranges = <0x0 0x0 0x2c090000 0x10000>;
> +
> +               cci_control3: slave-if@4000 {
> +                       compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x4000 0x1000>;
> +               };
> +
> +               cci_control4: slave-if@5000 {
> +                       compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x5000 0x1000>;
> +               };
> +
> +               pmu@9000 {
> +                       compatible = "arm,cci-400-pmu";
> +                       reg = <0x9000 0x5000>;
> +                       interrupts = <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>;
> +               };
> +       };
> +
> +       /**
> +        * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
> +        * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
> +        */
> +       clocks {
> +               clk_alw_0_0: clk_alw_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_0_1: clk_alw_0_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_0_2: clk_alw_0_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_0_4: clk_alw_0_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_0_5: clk_alw_0_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_0_8: clk_alw_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_1_0: clk_alw_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_1_1: clk_alw_1_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_1_8: clk_alw_1_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_2_0: clk_alw_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_2_1: clk_alw_2_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_2_2: clk_alw_2_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_2_4: clk_alw_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_2_5: clk_alw_2_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_2_8: clk_alw_2_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_6_8: clk_alw_6_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <6>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_7_0: clk_alw_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_8_0: clk_alw_8_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <8>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_0: clk_alw_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_1: clk_alw_a_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <1>;
> +               };
> +
> +               clk_ddr3_0_0: clk_ddr3_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <1>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_0: clk_main_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_8: clk_main_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_main_1_3: clk_main_1_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_1_4: clk_main_1_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_0: clk_main_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_2_3: clk_main_2_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_2_7: clk_main_2_7 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <7>;
> +               };
> +
> +               clk_main_3_0: clk_main_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_3_3: clk_main_3_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_3_4: clk_main_3_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_3_5: clk_main_3_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_3_6: clk_main_3_6 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <6>;
> +               };
> +
> +               clk_main_4_0: clk_main_4_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_4_1: clk_main_4_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_5_0: clk_main_5_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_5_3: clk_main_5_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_5_4: clk_main_5_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_5_5: clk_main_5_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_7_0: clk_main_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_8_1: clk_main_8_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <8>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_9_0: clk_main_9_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <9>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_0_0: clk_hdmi_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_1_0: clk_hdmi_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_2_0: clk_hdmi_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_3_0: clk_hdmi_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_dphy_0_0: clk_dphy_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_dphy_1_0: clk_dphy_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +       };
> +
> +       gic: interrupt-controller@2c001000 {

All these devices should be under a bus node with simple-bus compatible string.

> +               compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
> +               #interrupt-cells = <3>;
> +               interrupt-controller;
> +               reg = <0 0x2c001000 0x1000>,
> +                     <0 0x2c002000 0x1000>,

I think the size should be 0x2000 here for the cpu interface.

> +                     <0 0x2c004000 0x2000>,
> +                     <0 0x2c006000 0x2000>;
> +               interrupts = <1 9 0xf04>;
> +       };
> +
> +       timer0: timer0@31080000 {

should be timer@31080000

> +               compatible = "arm,sp804";
> +               reg = <0 0x31080000 0x10000>;

sp804 size is 0x1000.

> +               interrupts = <0 324 4>,
> +                            <0 325 4>;
> +               clocks = <&clk_alw_6_8>;
> +       };

No arch timer?


> +
> +       mhu: mhu0@2b1f0000 {

mhu0 should be mbox or mailbox.

> +               #mbox-cells = <1>;
> +               compatible = "fujitsu,mhu";
> +               reg = <0 0x2B1F0000 0x1000>;

Lower case hex please.

> +               interrupts = <0 36 4>, /* LP Non-Sec */
> +                            <0 35 4>, /* HP Non-Sec */
> +                            <0 37 4>; /* Secure */
> +       };
> +
> +       mhu_client: scb@0 {
> +               compatible = "fujitsu,scb";
> +               mbox = <&mhu 1>;

Is the mailbox binding finalized?

> +               mbox-names = "HP_NonSec";
> +       };
> +
> +       pinctrl: pinctrl@2a4d0000 {
> +               compatible = "fujitsu,mb86s70-pinctrl";
> +               reg = <0 0x2a4d0000 0x1000>;
> +               #gpio-range-cells = <3>;
> +       };
> +
> +       gpio0: mb86s70_gpio0 {

node name should be gpio@31000000

> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31000000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 0 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       gpio1: mb86s70_gpio1 {

ditto

> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31010000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 32 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       uart0: serial@0x31040000 {

Drop the 0x in the unit-address.

> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31040000 0x100>;
> +               interrupts = <0 320 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       uart1: serial@0x31050000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31050000 0x100>;
> +               interrupts = <0 321 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       uart2: serial@0x31060000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31060000 0x100>;
> +               interrupts = <0 322 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       sdhci0: emmc@300c0000 {
> +               compatible = "fujitsu,f-sdh30";

This should include the chip part number the IP is in.

> +               reg = <0 0x300c0000 0x1000>;
> +               interrupts = <0 164 0x4>,
> +                            <0 165 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               bus-width = <8>;
> +               clocks = <&clk_alw_1_8>, <&clk_alw_6_8>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +       };
> +
> +       sdhci1: sdio@36600000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x36600000 0x1000>;
> +               interrupts = <0 172 0x4>,
> +                            <0 173 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               pwr-mux-gpios = <&gpio0 7 0>;
> +               clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +       };
> +
> +       eth0: f_taiki {
> +                compatible = "fujitsu,ogma";

This should include the chip part number the IP is in.

> +               reg = <0 0x31600000 0x10000>,
> +                       <0 0x31618000 0x4000>,
> +                       <0 0x3161c000 0x4000>;
> +               interrupts = <0 163 0x4>;
> +               clocks = <&clk_alw_0_8>;
> +               phy-mode = "rgmii";
> +               max-speed = <1000>;
> +               max-frame-size = <9000>;
> +               local-mac-address = [ a4 17 31 00 00 ed ];
> +               phy-handle = <&ethphy0>;
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               ethphy0: ethernet-phy@1 {
> +                       device_type = "ethernet-phy";
> +                       compatible = "ethernet-phy-ieee802.3-c22";
> +                       reg = <1>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s70eb.dts b/arch/arm/boot/dts/mb86s70eb.dts
> new file mode 100644
> index 0000000..ee25dd9
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s70eb.dts
> @@ -0,0 +1,38 @@
> +
> +/include/ "mb86s70.dtsi"
> +
> +#include <dt-bindings/gpio/gpio.h>
> +
> +/ {
> +       model = "Fujitsu MB86S70 EVB";
> +       compatible = "fujitsu,mb86s70-evb";
> +
> +       memory {
> +               device_type = "memory";
> +               reg = <0 0x80000000 0x80000000>, <0x08 0x80000000 0x80000000>;
> +
> +       };
> +
> +       chosen {
> +               bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait rw";
> +       };
> +
> +       gpio-leds {
> +               compatible = "gpio-leds";
> +
> +               d3 {
> +                       label = "led3";

This reflects the label on the board? If not, a better name is needed
like what is its function.

> +                       gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "default-on";
> +               };
> +               d4 {
> +                       label = "led4";
> +                       gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "mmc1";
> +               };
> +               d5 {
> +                       label = "led5";
> +                       gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s73.dtsi b/arch/arm/boot/dts/mb86s73.dtsi
> new file mode 100644
> index 0000000..ef4f5a0
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s73.dtsi

Similar comments apply to this dtsi file.

> @@ -0,0 +1,910 @@
> +
> +/ {
> +       model = "Fujitsu mb86s73";
> +       compatible = "fujitsu,mb86s73";
> +       interrupt-parent = <&gic>;
> +       #address-cells = <2>;
> +       #size-cells = <1>;
> +
> +       aliases {
> +               serial0 = &uart0;
> +               serial1 = &uart1;
> +               serial2 = &uart2;
> +       };
> +
> +       cpus {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               cpu0: cpu@0 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x100>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000   900000
> +                               1200000  1000000
> +                       >;
> +                       clock-latency = <100000>;
> +                       fujitsu,power-domain = <&pd_cpu>;
> +               };
> +
> +               cpu1: cpu@1 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x101>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000   900000
> +                               1200000  1000000
> +                       >;
> +                       clock-latency = <100000>;
> +                       fujitsu,power-domain = <&pd_cpu>;
> +               };
> +       };
> +
> +       cci@2c090000 {
> +               compatible = "arm,cci-400";
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +               reg = <0 0x2c090000 0x1000>;
> +               ranges = <0x0 0x0 0x2c090000 0x10000>;
> +               fujitsu,power-domain = <&pd_offchip>;
> +
> +               cci_control3: slave-if@4000 {
> +               compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x4000 0x1000>;
> +               };
> +       };
> +
> +       /**
> +        * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA7, 4->USB, 5->FPDLINK
> +        * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
> +        */
> +       clocks {
> +               clk_alw_0_0: clk_alw_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_0_1: clk_alw_0_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_0_2: clk_alw_0_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_0_4: clk_alw_0_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_0_5: clk_alw_0_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_0_8: clk_alw_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_1_0: clk_alw_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_1_1: clk_alw_1_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_1_2: clk_alw_1_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_1_8: clk_alw_1_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_2_0: clk_alw_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_2_1: clk_alw_2_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_2_2: clk_alw_2_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_2_4: clk_alw_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_2_5: clk_alw_2_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_2_8: clk_alw_2_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_6_8: clk_alw_6_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <6>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_7_0: clk_alw_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_8_0: clk_alw_8_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <8>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_0: clk_alw_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_1: clk_alw_a_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_b_0: clk_alw_b_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0b>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_c_0: clk_alw_c_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0c>;
> +                       port = <0>;
> +               };
> +
> +               clk_ddr3_0_0: clk_ddr3_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <1>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_0: clk_main_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_8: clk_main_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_main_1_3: clk_main_1_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_1_4: clk_main_1_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_0: clk_main_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_2_3: clk_main_2_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_2_4: clk_main_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_7: clk_main_2_7 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <7>;
> +               };
> +
> +               clk_main_3_0: clk_main_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_3_3: clk_main_3_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_3_4: clk_main_3_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_3_5: clk_main_3_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_3_6: clk_main_3_6 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <6>;
> +               };
> +
> +               clk_main_4_0: clk_main_4_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_4_4: clk_main_4_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_4_5: clk_main_4_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_5_0: clk_main_5_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_5_3: clk_main_5_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_5_4: clk_main_5_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_5_5: clk_main_5_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_7_0: clk_main_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_8_1: clk_main_8_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <8>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_9_0: clk_main_9_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <9>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_a_0: clk_main_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xa>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_b_0: clk_main_b_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xb>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_c_0: clk_main_c_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xc>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_d_0: clk_main_d_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xd>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_0_0: clk_usb_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_1_0: clk_usb_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_2_0: clk_usb_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_3_0: clk_usb_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_fpdlink_0_0: clk_fpdlink_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_fpdlink_1_0: clk_fpdlink_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +       };
> +
> +       timer0: timer0@31080000 {
> +               compatible = "arm,sp804";
> +               reg = <0 0x31080000 0x10000>;
> +               interrupts = <0 324 4>,
> +                            <0 325 4>;
> +               clocks = <&clk_alw_6_8>;
> +       };
> +
> +       timer1: archtimer {
> +               compatible = "arm,armv7-timer";
> +               clock-frequency = <125000000>;
> +               interrupts = <1 13 0xf08>,
> +                            <1 14 0xf08>,
> +                            <1 11 0xf08>,
> +                            <1 10 0xf08>;
> +       };
> +
> +       pinctrl: pinctrl@2a4d0000 {
> +               compatible = "fujitsu,mb86s73-pinctrl";
> +               reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
> +               #gpio-range-cells = <3>;
> +
> +               pcie0_pins_active: pcie0_active {
> +                       mb86s7x,function = "pcie0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pcie0_pins_sleep: pcie0_sleep {
> +                       mb86s7x,function = "pcie0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb3h0_pins_active: usb3h0_active {
> +                       mb86s7x,function = "usb3h0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb3h0_pins_sleep: usb3h0_sleep {
> +                       mb86s7x,function = "usb3h0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb2h0_pins_active: usb2h0_active {
> +                       mb86s7x,function = "usb2h0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb2h0_pins_sleep: usb2h0_sleep {
> +                       mb86s7x,function = "usb2h0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb2d0_pins_active: usb2d0_active {
> +                       mb86s7x,function = "usb2d0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb2d0_pins_sleep: usb2d0_sleep {
> +                       mb86s7x,function = "usb2d0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               sdh30_pins_active: sdh30_active {
> +                       mb86s7x,function = "sdh30";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               sdh30_pins_sleep: sdh30_sleep {
> +                       mb86s7x,function = "sdh30";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               emmc0_pins_active: emmc0_active {
> +                       mb86s7x,function = "emmc0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               emmc0_pins_sleep: emmc0_sleep {
> +                       mb86s7x,function = "emmc0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               hsspi0_pins_active: hsspi0_active {
> +                       mb86s7x,function = "hsspi0";
> +                       mb86s7x,drvst = <8>; /* in mA */
> +               };
> +               hsspi0_pins_sleep: hsspi0_sleep {
> +                       mb86s7x,function = "hsspi0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               gmacd0_pins_active: gmacd0_active {
> +                       mb86s7x,function = "gmacd0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               gmacd0_pins_sleep: gmacd0_sleep {
> +                       mb86s7x,function = "gmacd0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               gmacm0_pins_active: gmacm0_active {
> +                       mb86s7x,function = "gmacm0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               gmacm0_pins_sleep: gmacm0_sleep {
> +                       mb86s7x,function = "gmacm0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               i2s0_pins_active: i2s0_active {
> +                       mb86s7x,function = "i2s0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               i2s0_pins_sleep: i2s0_sleep {
> +                       mb86s7x,function = "i2s0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               other0_pins_active: other0_active {
> +                       mb86s7x,function = "other0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               other0_pins_sleep: other0_sleep {
> +                       mb86s7x,function = "other0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               jtag0_pins_active: jtag0_active {
> +                       mb86s7x,function = "jtag0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               jtag0_pins_sleep: jtag0_sleep {
> +                       mb86s7x,function = "jtag0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               pcie1_pins_active: pcie1_active {
> +                       mb86s7x,function = "pcie1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pcie1_pins_sleep: pcie1_sleep {
> +                       mb86s7x,function = "pcie1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb3h1_pins_active: usb3h1_active {
> +                       mb86s7x,function = "usb3h1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb3h1_pins_sleep: usb3h1_sleep {
> +                       mb86s7x,function = "usb3h1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               cfg_pins_active: cfg_active {
> +                       mb86s7x,function = "cfg";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               cfg_pins_sleep: cfg_sleep {
> +                       mb86s7x,function = "cfg";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart0_pins_active: uart0_active {
> +                       mb86s7x,function = "uart0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart0_pins_sleep: uart0_sleep {
> +                       mb86s7x,function = "uart0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart1_pins_active: uart1_active {
> +                       mb86s7x,function = "uart1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart1_pins_sleep: uart1_sleep {
> +                       mb86s7x,function = "uart1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart2_pins_active: uart2_active {
> +                       mb86s7x,function = "uart2";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart2_pins_sleep: uart2_sleep {
> +                       mb86s7x,function = "uart2";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               trace_pins_active: trace_active {
> +                       mb86s7x,function = "trace";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               trace_pins_sleep: trace_sleep {
> +                       mb86s7x,function = "trace";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               pl244_pins_active: pl244_active {
> +                       mb86s7x,function = "pl244";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pl244_pins_sleep: pl244_sleep {
> +                       mb86s7x,function = "pl244";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               smt_pins_active: smt_active {
> +                       mb86s7x,function = "smt";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               smt_pins_sleep: smt_sleep {
> +                       mb86s7x,function = "smt";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               memcs_pins_active: memcs_active {
> +                       mb86s7x,function = "memcs";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               memcs_pins_sleep: memcs_sleep {
> +                       mb86s7x,function = "memcs";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +       };
> +
> +       gpio0: mb86s70_gpio0 {
> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31000000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 0 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       gpio1: mb86s70_gpio1 {
> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31010000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 32 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       pd_alwayson: genpd@0 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <0>;
> +       };
> +
> +       pd_default: genpd@1 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <1>;
> +       };
> +
> +       pd_offchip: genpd@2 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <2>;
> +       };
> +
> +       pd_cpu: genpd@3 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <3>;
> +       };
> +
> +       gic: interrupt-controller@2c001000 {
> +               compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
> +               #interrupt-cells = <3>;
> +               interrupt-controller;
> +               reg = <0 0x2c001000 0x1000>,
> +                     <0 0x2c002000 0x1000>,
> +                     <0 0x2c004000 0x2000>,
> +                     <0 0x2c006000 0x2000>;
> +               interrupts = <1 9 0xf04>;
> +       };
> +
> +       pmu_a7 {
> +               compatible = "arm,cortex-a7-pmu";
> +               interrupts = <0 18 4>,
> +                            <0 22 4>;
> +               fujitsu,power-domain = <&pd_cpu>;
> +       };
> +
> +       mhu: mhu0@2b1f0000 {
> +               #mbox-cells = <1>;
> +               compatible = "fujitsu,mhu";
> +               reg = <0 0x2B1F0000 0x1000>;
> +               interrupts = <0 36 4>, /* LP Non-Sec */
> +                            <0 35 4>, /* HP Non-Sec */
> +                            <0 37 4>; /* Secure */
> +               fujitsu,power-domain = <&pd_default>;
> +       };
> +
> +       mhu_client: scb@0 {
> +               compatible = "fujitsu,scb";
> +               mbox = <&mhu 1>;
> +               mbox-names = "HP_NonSec";
> +       };
> +
> +       uart0: serial@0x31040000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31040000 0x100>;
> +               interrupts = <0 320 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart0_pins_active>;
> +               pinctrl-1 = <&uart0_pins_sleep>;
> +       };
> +
> +       uart1: serial@0x31050000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31050000 0x100>;
> +               interrupts = <0 321 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart1_pins_active>;
> +               pinctrl-1 = <&uart1_pins_sleep>;
> +       };
> +
> +       uart2: serial@0x31060000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31060000 0x100>;
> +               interrupts = <0 322 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart2_pins_active>;
> +               pinctrl-1 = <&uart2_pins_sleep>;
> +       };
> +
> +       sdhci0: emmc@300c0000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x300c0000 0x1000>;
> +               interrupts = <0 164 0x4>,
> +                            <0 165 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               bus-width = <8>;
> +               clocks = <&clk_alw_c_0>, <&clk_alw_b_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +               fujitsu,power-domain = <&pd_default>;
> +       };
> +
> +       sdhci1: sdio@36600000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x36600000 0x1000>;
> +               interrupts = <0 172 0x4>,
> +                            <0 173 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               clocks = <&clk_main_c_0>, <&clk_main_d_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +               resume-detect-retry;
> +               fujitsu,power-domain = <&pd_offchip>;
> +       };
> +
> +       eth0: f_taiki {
> +                compatible = "fujitsu,ogma";
> +               reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
> +               interrupts = <0 163 0x4>;
> +               clocks = <&clk_alw_0_8>;
> +               phy-mode = "rgmii";
> +               max-speed = <1000>;
> +               max-frame-size = <9000>;
> +               local-mac-address = [ a4 17 31 00 00 ed ];
> +               phy-handle = <&ethphy0>;
> +               fujitsu,power-domain = <&pd_default>;
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               ethphy0: ethernet-phy@1 {
> +                       device_type = "ethernet-phy";
> +                       compatible = "ethernet-phy-ieee802.3-c22";
> +                       reg = <1>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s73eb.dts b/arch/arm/boot/dts/mb86s73eb.dts
> new file mode 100644
> index 0000000..81862d9
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s73eb.dts
> @@ -0,0 +1,73 @@
> +
> +/include/ "mb86s73.dtsi"
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/input/input.h>
> +
> +/ {
> +       model = "Fujitsu MB86S73 EVB";
> +       compatible = "fujitsu,mb86s73-evb";
> +
> +       memory {
> +               device_type = "memory";
> +               reg = <0 0x80000000 0x80000000>;
> +       };
> +
> +       chosen {
> +               bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw";
> +       };
> +
> +       gpio-leds {
> +               compatible = "gpio-leds";
> +
> +               d34_a {
> +                       label = "led34a";
> +                       gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "heartbeat";
> +               };
> +               d34_b {
> +                       label = "led34b";
> +                       gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "mmc1";
> +               };
> +               d37 {
> +                       label = "led37";
> +                       gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "default-on";
> +               };
> +               d38 {
> +                       label = "led38";
> +                       gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
> +               };
> +       };
> +
> +       gpio-keys {
> +               compatible = "gpio-keys-polled";
> +               poll-interval = <20>;
> +               home {
> +                       label = "Home";
> +                       gpios = <&gpio1 4 0>;
> +                       linux,code = <KEY_HOME>;
> +                       gpio-key;
> +               };
> +               power {
> +                       label = "Power";
> +                       gpios = <&gpio1 5 0>;
> +                       linux,code = <KEY_POWER>;
> +                       gpio-key;
> +               };
> +
> +               up {
> +                       label = "Up";
> +                       gpios = <&gpio1 6 0>;
> +                       linux,code = <KEY_UP>;
> +                       gpio-key;
> +               };
> +               down {
> +                       label = "Down";
> +                       gpios = <&gpio1 7 0>;
> +                       linux,code = <KEY_DOWN>;
> +                       gpio-key;
> +               };
> +       };
> +};
> diff --git a/arch/arm/configs/fujitsu_defconfig b/arch/arm/configs/fujitsu_defconfig
> new file mode 100644
> index 0000000..bfc78c9
> --- /dev/null
> +++ b/arch/arm/configs/fujitsu_defconfig
> @@ -0,0 +1,156 @@
> +# CONFIG_LOCALVERSION_AUTO is not set
> +CONFIG_DEFAULT_HOSTNAME="MB86S7x"
> +CONFIG_SYSVIPC=y
> +CONFIG_NO_HZ=y
> +CONFIG_HIGH_RES_TIMERS=y
> +CONFIG_IKCONFIG=y
> +CONFIG_IKCONFIG_PROC=y
> +CONFIG_NAMESPACES=y
> +CONFIG_BLK_DEV_INITRD=y
> +CONFIG_RD_BZIP2=y
> +CONFIG_RD_LZMA=y
> +CONFIG_RD_XZ=y
> +CONFIG_RD_LZO=y
> +CONFIG_CC_OPTIMIZE_FOR_SIZE=y
> +CONFIG_EXPERT=y
> +# CONFIG_COMPAT_BRK is not set
> +CONFIG_SLAB=y
> +CONFIG_PROFILING=y
> +CONFIG_MODULES=y
> +CONFIG_MODULE_UNLOAD=y
> +CONFIG_ARCH_MB86S7X=y
> +CONFIG_ARM_LPAE=y
> +# CONFIG_SWP_EMULATE is not set
> +CONFIG_ARM_ERRATA_754322=y
> +CONFIG_ARM_ERRATA_754327=y
> +CONFIG_ARM_ERRATA_764369=y
> +CONFIG_ARM_ERRATA_775420=y
> +CONFIG_ARM_ERRATA_798181=y
> +CONFIG_PCI=y
> +CONFIG_PCIEASPM_POWERSAVE=y
> +CONFIG_SMP=y
> +CONFIG_SCHED_MC=y
> +CONFIG_SCHED_SMT=y
> +CONFIG_BL_SWITCHER=y
> +CONFIG_BL_SWITCHER_DUMMY_IF=m
> +CONFIG_VMSPLIT_2G=y
> +CONFIG_PREEMPT=y
> +CONFIG_THUMB2_KERNEL=y
> +CONFIG_HIGHMEM=y
> +CONFIG_CMA=y
> +# CONFIG_ATAGS is not set
> +CONFIG_ZBOOT_ROM_TEXT=0x0
> +CONFIG_ZBOOT_ROM_BSS=0x0
> +CONFIG_CMDLINE="mem=2048M earlycon=ttyS0,115200 earlyprintk console=ttyS0,115200 console=tty0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
> +CONFIG_KEXEC=y
> +CONFIG_CPU_IDLE=y
> +CONFIG_VFP=y
> +CONFIG_NEON=y
> +CONFIG_PM_WAKELOCKS=y
> +CONFIG_PM_RUNTIME=y
> +CONFIG_PM_DEBUG=y
> +CONFIG_NET=y
> +CONFIG_PACKET=y
> +CONFIG_UNIX=y
> +CONFIG_INET=y
> +CONFIG_IP_MULTICAST=y
> +CONFIG_IP_PNP=y
> +CONFIG_IP_PNP_DHCP=y
> +CONFIG_IP_PNP_BOOTP=y
> +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
> +# CONFIG_INET_XFRM_MODE_TUNNEL is not set
> +# CONFIG_INET_XFRM_MODE_BEET is not set
> +# CONFIG_INET_LRO is not set
> +CONFIG_IPV6=y
> +CONFIG_INET6_XFRM_MODE_TRANSPORT=m
> +CONFIG_INET6_XFRM_MODE_TUNNEL=m
> +CONFIG_INET6_XFRM_MODE_BEET=m
> +CONFIG_IPV6_SIT=m
> +# CONFIG_ANDROID_PARANOID_NETWORK is not set
> +# CONFIG_WIRELESS is not set
> +CONFIG_DEVTMPFS=y
> +CONFIG_DEVTMPFS_MOUNT=y
> +# CONFIG_FW_LOADER_USER_HELPER is not set
> +CONFIG_DMA_CMA=y
> +CONFIG_CMA_SIZE_MBYTES=192
> +CONFIG_BLK_DEV_LOOP=m
> +CONFIG_BLK_DEV_CRYPTOLOOP=m
> +CONFIG_BLK_DEV_RAM=y
> +CONFIG_BLK_DEV_RAM_COUNT=2
> +CONFIG_BLK_DEV_RAM_SIZE=1024
> +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> +CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
> +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
> +CONFIG_INPUT_EVDEV=m
> +# CONFIG_KEYBOARD_ATKBD is not set
> +CONFIG_KEYBOARD_GPIO_POLLED=y
> +# CONFIG_MOUSE_PS2 is not set
> +CONFIG_MOUSE_GPIO=m
> +CONFIG_INPUT_TOUCHSCREEN=y
> +CONFIG_TOUCHSCREEN_EGALAX=m
> +CONFIG_TOUCHSCREEN_ILI210X=m
> +# CONFIG_SERIO_SERPORT is not set
> +CONFIG_VT_HW_CONSOLE_BINDING=y
> +CONFIG_LEGACY_PTY_COUNT=16
> +# CONFIG_DEVKMEM is not set
> +CONFIG_SERIAL_8250=y
> +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
> +CONFIG_SERIAL_8250_CONSOLE=y
> +CONFIG_SERIAL_8250_EXTENDED=y
> +CONFIG_SERIAL_8250_DW=y
> +CONFIG_SERIAL_OF_PLATFORM=y
> +# CONFIG_HW_RANDOM is not set
> +CONFIG_I2C=y
> +# CONFIG_I2C_COMPAT is not set
> +# CONFIG_I2C_HELPER_AUTO is not set
> +CONFIG_GPIO_SYSFS=y
> +# CONFIG_HWMON is not set
> +# CONFIG_VGA_ARB is not set
> +# CONFIG_HID is not set
> +# CONFIG_USB_SUPPORT is not set
> +CONFIG_MMC=y
> +CONFIG_MMC_CLKGATE=y
> +CONFIG_MMC_SDHCI=y
> +CONFIG_MMC_SDHCI_F_SDH30=y
> +CONFIG_NEW_LEDS=y
> +CONFIG_LEDS_CLASS=y
> +CONFIG_LEDS_GPIO=y
> +CONFIG_LEDS_TRIGGERS=y
> +CONFIG_LEDS_TRIGGER_TIMER=y
> +CONFIG_LEDS_TRIGGER_HEARTBEAT=y
> +CONFIG_LEDS_TRIGGER_BACKLIGHT=m
> +CONFIG_LEDS_TRIGGER_CPU=y
> +CONFIG_LEDS_TRIGGER_GPIO=y
> +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
> +CONFIG_LEDS_TRIGGER_TRANSIENT=m
> +CONFIG_MAILBOX=y
> +CONFIG_MBOX_F_MHU=y
> +# CONFIG_IOMMU_SUPPORT is not set
> +CONFIG_EXT4_FS=y
> +CONFIG_EXT4_FS_POSIX_ACL=y
> +CONFIG_EXT4_FS_SECURITY=y
> +CONFIG_VFAT_FS=m
> +CONFIG_TMPFS=y
> +CONFIG_CONFIGFS_FS=m
> +CONFIG_CRAMFS=y
> +CONFIG_ROMFS_FS=y
> +CONFIG_NLS_CODEPAGE_437=m
> +CONFIG_NLS_ISO8859_1=m
> +CONFIG_PRINTK_TIME=y
> +CONFIG_DEBUG_INFO=y
> +CONFIG_DEBUG_FS=y
> +CONFIG_MAGIC_SYSRQ=y
> +CONFIG_DEBUG_MEMORY_INIT=y
> +# CONFIG_DEBUG_PREEMPT is not set
> +# CONFIG_RCU_CPU_STALL_VERBOSE is not set
> +# CONFIG_FTRACE is not set
> +CONFIG_DEBUG_USER=y
> +# CONFIG_DEBUG_LL is not set
> +CONFIG_DEBUG_LL_UART_8250=y
> +CONFIG_DEBUG_UART_PHYS=0x31040000
> +CONFIG_DEBUG_UART_VIRT=0xfe000000
> +CONFIG_DEBUG_UART_8250_WORD=y
> +# CONFIG_EARLY_PRINTK is not set
> +CONFIG_CRYPTO_AES=y
> +# CONFIG_CRYPTO_ANSI_CPRNG is not set
> +# CONFIG_CRYPTO_HW is not set
> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> new file mode 100644
> index 0000000..44f5b0c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Kconfig
> @@ -0,0 +1,18 @@
> +config ARCH_MB86S7X
> +       bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> +       select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> +       select ARM_AMBA
> +       select ARM_GIC
> +       select ARM_TIMER_SP804
> +       select HAVE_ARM_ARCH_TIMER
> +       select ARCH_REQUIRE_GPIOLIB
> +       select ARCH_HAS_CPUFREQ
> +       select ARCH_HAS_OPP
> +       select PM_OPP
> +       select PINCTRL
> +       select PINCTRL_MB86S7X
> +       select ARM_CCI
> +       select BIG_LITTLE
> +       select PM_GENERIC_DOMAINS if PM
> +       help
> +         Support for Fujitsu MB86S7x based platforms
> diff --git a/arch/arm/mach-mb86s7x/Makefile b/arch/arm/mach-mb86s7x/Makefile
> new file mode 100644
> index 0000000..5524a6c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Makefile
> @@ -0,0 +1,2 @@
> +obj-y          += board.o scb_mhu.o mcpm.o
> +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
> new file mode 100644
> index 0000000..d6e76ec
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/board.c
> @@ -0,0 +1,65 @@
> +/*
> + * Support for the Fujitsu's MB86S7x based devices.
> + *
> + * Copyright (C) 2014 Linaro, LTD
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/of.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/mach/map.h>
> +#include <asm/mach/arch.h>
> +
> +#include "iomap.h"
> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +       struct device_node *node;
> +
> +       node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +       if (node && of_device_is_available(node)) {
> +               mcpm_smp_set_ops();
> +               return true;
> +       }
> +
> +       return false;
> +}
> +
> +#define IOMAP_DEV(name) { \
> +               .virtual = (unsigned long) MB86S7X_##name##_VIRT, \
> +               .pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
> +               .length = MB86S7X_##name##_SIZE, \
> +               .type = MT_DEVICE, \
> +       }
> +
> +static struct map_desc mb86s7x_io_desc[] = {
> +       IOMAP_DEV(MHU),
> +       IOMAP_DEV(ISRAM),
> +};
> +
> +void __init mb86s7x_dt_map_io(void)
> +{
> +       iotable_init(mb86s7x_io_desc, ARRAY_SIZE(mb86s7x_io_desc));
> +}
> +
> +static const char *mb86s7x_dt_match[] __initconst = {
> +       "fujitsu,mb86s70-evb",
> +       "fujitsu,mb86s73-evb",
> +       NULL,
> +};
> +
> +DT_MACHINE_START(MB86S7X_DT, "Fujitsu MB86S7X-based board")
> +       .dt_compat      = mb86s7x_dt_match,
> +       .smp_init       = smp_init_ops(mb86s7x_smp_init_ops),
> +       .map_io         = mb86s7x_dt_map_io,
> +MACHINE_END
> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
> new file mode 100644
> index 0000000..2b8db1d
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/iomap.h
> @@ -0,0 +1,34 @@
> +/*
> + * arch/arm/mach-mb86s7x/iomap.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
> + * Copyright:  (C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __MB86S7X_IOMAP_H
> +#define __MB86S7X_IOMAP_H
> +
> +#include <linux/sizes.h>
> +
> +#define SEC_RSTADDR_OFF                0x3fc
> +
> +#define MB86S7X_MHU_PHYS       0x2b1f0000
> +#define MB86S7X_MHU_SIZE       SZ_4K
> +
> +/* Internal SRAM */
> +#define MB86S7X_ISRAM_PHYS     0x2e000000
> +#define MB86S7X_ISRAM_SIZE     SZ_16K
> +#define MB86S7X_TRAMPOLINE_PHYS        (MB86S7X_ISRAM_PHYS + 0x3c00)
> +
> +#define MB86S7X_ISRAM_VIRT     IOMEM(0xfe000000)
> +#define MB86S7X_MHU_VIRT       (MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
> +
> +/* Non-Secure High-Priority Channel is used */
> +#define MB86S7X_SHM_FROM_SCB_VIRT      (MB86S7X_ISRAM_VIRT + 0x3800)
> +#define MB86S7X_TRAMPOLINE_VIRT                (MB86S7X_ISRAM_VIRT + 0x3c00)
> +
> +#endif /* __MB86S7X_IOMAP_H */
> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
> new file mode 100644
> index 0000000..86d223f
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/mcpm.c
> @@ -0,0 +1,293 @@
> +/*
> + * arch/arm/mach-mb86s7x/mcpm.c
> + *
> + * "Inspired" by tc_pm.c
> + * Copyright:  (C) 2013-2014  Fujitsu Semiconductor Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/reboot.h>
> +#include <linux/arm-cci.h>
> +#include <linux/spinlock.h>
> +#include <linux/irqchip/arm-gic.h>
> +#include <linux/platform_data/mb86s7x_mbox.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/cp15.h>
> +#include <asm/cputype.h>
> +#include <asm/system_misc.h>
> +
> +#include "iomap.h"
> +
> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;

You should not be using arch_spinlock_t directly.

> +static int mb86s7x_pm_use_count[2][2];
> +
> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> +
> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> +{
> +       u8 val;
> +
> +       if (clr & ~AT_WFI_COLOR_MASK)
> +               return;
> +
> +       val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +       val &= ~AT_WFI_COLOR_MASK;
> +       val |= clr;
> +       writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +}
> +
> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +       struct completion got_rsp;
> +       int ret = 0;
> +
> +       pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> +
> +       arch_spin_lock(&mb86s7x_pm_lock);
> +
> +       mb86s7x_pm_use_count[cpu][cluster]++;
> +
> +       if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +               struct mb86s7x_cpu_gate cmd;
> +
> +               arch_spin_unlock(&mb86s7x_pm_lock);

Can't you use atomic operations here (i.e. atomic_inc_return) instead of a lock?

In any case, Nicolas should review the cluster PM code if he hasn't
already. As Arnd pointed out, this patch should be split up into
multiple patches. I'd suggest splitting into DT bindings, DTS files,
base support, mailbox, MCPM, and PM domains.

That's all I've got time and energy for reviewing for now.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 15:11       ` Rob Herring
  0 siblings, 0 replies; 88+ messages in thread
From: Rob Herring @ 2014-07-15 15:11 UTC (permalink / raw)
  To: linux-arm-kernel

Adding Nico for cluster PM code.

On Sun, Jul 13, 2014 at 1:28 AM, Mollie Wu <mollie.wu@linaro.org> wrote:
> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
> And the remote master firmware (called SCB) running on CM3. Linux asks
> for things to be done over Mailbox API, to SCB which controls most of
> the important things. variations S70 & S73 are supported.
>
> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
> Cc: Arnd Bergmann <arnd@arndb.de>
> Cc: Olof <olof@lixom.net>
> Cc: Russell King <linux@arm.linux.org.uk>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
> ---
>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>  arch/arm/Kconfig                                   |   2 +
>  arch/arm/Makefile                                  |   1 +
>  arch/arm/boot/dts/Makefile                         |   1 +
>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
>  17 files changed, 3183 insertions(+)
>  create mode 100644 Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>  create mode 100644 arch/arm/boot/dts/mb86s70.dtsi
>  create mode 100644 arch/arm/boot/dts/mb86s70eb.dts
>  create mode 100644 arch/arm/boot/dts/mb86s73.dtsi
>  create mode 100644 arch/arm/boot/dts/mb86s73eb.dts
>  create mode 100644 arch/arm/configs/fujitsu_defconfig
>  create mode 100644 arch/arm/mach-mb86s7x/Kconfig
>  create mode 100644 arch/arm/mach-mb86s7x/Makefile
>  create mode 100644 arch/arm/mach-mb86s7x/board.c
>  create mode 100644 arch/arm/mach-mb86s7x/iomap.h
>  create mode 100644 arch/arm/mach-mb86s7x/mcpm.c
>  create mode 100644 arch/arm/mach-mb86s7x/pm_domains.c
>  create mode 100644 arch/arm/mach-mb86s7x/scb_mhu.c
>  create mode 100644 include/linux/platform_data/mb86s7x_mbox.h
>
> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> new file mode 100644
> index 0000000..44abfe8
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
> @@ -0,0 +1,22 @@
> +* Fujitsu MB86S7x Power Domains
> +
> +Remote f/w on MB86S7x can enable/disable power to various IPs.
> +
> +Required Properties:
> +- compatible: Should be "fujitsu,mb86s7x-pd"
> +- index: Index of the power gate control for the block
> +
> +Example:
> +
> +       pd_cpu: genpd at 3 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <3>;
> +       };
> +
> +Example of the node using power domain:
> +
> +       node {
> +               /* ... */
> +               fujitsu,power-domain = <&pd_cpu>;
> +               /* ... */
> +       };
> diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
> index 245058b..44fd319 100644
> --- a/arch/arm/Kconfig
> +++ b/arch/arm/Kconfig
> @@ -955,6 +955,8 @@ source "arch/arm/mach-kirkwood/Kconfig"
>
>  source "arch/arm/mach-ks8695/Kconfig"
>
> +source "arch/arm/mach-mb86s7x/Kconfig"
> +
>  source "arch/arm/mach-msm/Kconfig"
>
>  source "arch/arm/mach-moxart/Kconfig"
> diff --git a/arch/arm/Makefile b/arch/arm/Makefile
> index 6721fab..d6ec5cd 100644
> --- a/arch/arm/Makefile
> +++ b/arch/arm/Makefile
> @@ -166,6 +166,7 @@ machine-$(CONFIG_ARCH_KEYSTONE)             += keystone
>  machine-$(CONFIG_ARCH_KIRKWOOD)                += kirkwood
>  machine-$(CONFIG_ARCH_KS8695)          += ks8695
>  machine-$(CONFIG_ARCH_LPC32XX)         += lpc32xx
> +machine-$(CONFIG_ARCH_MB86S7X)         += mb86s7x
>  machine-$(CONFIG_ARCH_MMP)             += mmp
>  machine-$(CONFIG_ARCH_MOXART)          += moxart
>  machine-$(CONFIG_ARCH_MSM)             += msm
> diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
> index adb5ed9..0c8addb 100644
> --- a/arch/arm/boot/dts/Makefile
> +++ b/arch/arm/boot/dts/Makefile
> @@ -154,6 +154,7 @@ dtb-$(CONFIG_ARCH_KIRKWOOD) += $(kirkwood)
>  dtb-$(CONFIG_MACH_KIRKWOOD) += $(kirkwood)
>  dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
>  dtb-$(CONFIG_ARCH_MARCO) += marco-evb.dtb
> +dtb-$(CONFIG_ARCH_MB86S7X) += mb86s70eb.dtb mb86s73eb.dtb
>  dtb-$(CONFIG_ARCH_MOXART) += moxart-uc7112lx.dtb
>  dtb-$(CONFIG_ARCH_MXC) += \
>         imx25-eukrea-mbimxsd25-baseboard.dtb \
> diff --git a/arch/arm/boot/dts/mb86s70.dtsi b/arch/arm/boot/dts/mb86s70.dtsi
> new file mode 100644
> index 0000000..b6d8970
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s70.dtsi
> @@ -0,0 +1,635 @@
> +
> +/dts-v1/;
> +
> +/ {
> +       model = "Fujitsu mb86s70";
> +       compatible = "fujitsu,mb86s70";
> +       interrupt-parent = <&gic>;
> +       #address-cells = <2>;
> +       #size-cells = <1>;
> +
> +       aliases {
> +               serial0 = &uart0;
> +               serial1 = &uart1;
> +               serial2 = &uart2;
> +       };
> +
> +       cpus {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               cpu0: cpu at 0 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x100>;

reg and the unit-address (cpu at 0) should match. So either you need
cpu at 100 or "reg = <0>;".

Same thing on the other cpu entries.

> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000  900000
> +                       >;

What's the point in a single freq?

You can add this later as the current operating-points binding has
several limitations and is likely to get replaced.

> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu1: cpu at 1 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x101>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000  900000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu2: cpu at 2 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a15";
> +                       reg = <0x0>;
> +                       cci-control-port = <&cci_control4>;
> +                       clock-frequency = <1000000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               1200000 900000
> +                               1600000 1000000
> +                               2000000 1100000
> +                               2400000 1200000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +
> +               cpu3: cpu at 3 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a15";
> +                       reg = <0x1>;
> +                       cci-control-port = <&cci_control4>;
> +                       clock-frequency = <1000000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               1200000 900000
> +                               1600000 1000000
> +                               2000000 1100000
> +                               2400000 1200000
> +                       >;
> +                       clock-latency = <100000>;
> +               };
> +       };
> +
> +       cci at 2c090000 {
> +               compatible = "arm,cci-400";
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +               reg = <0 0x2c090000 0x1000>;
> +               ranges = <0x0 0x0 0x2c090000 0x10000>;
> +
> +               cci_control3: slave-if at 4000 {
> +                       compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x4000 0x1000>;
> +               };
> +
> +               cci_control4: slave-if at 5000 {
> +                       compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x5000 0x1000>;
> +               };
> +
> +               pmu at 9000 {
> +                       compatible = "arm,cci-400-pmu";
> +                       reg = <0x9000 0x5000>;
> +                       interrupts = <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>,
> +                                       <0 77 4>;
> +               };
> +       };
> +
> +       /**
> +        * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
> +        * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
> +        */
> +       clocks {
> +               clk_alw_0_0: clk_alw_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_0_1: clk_alw_0_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_0_2: clk_alw_0_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_0_4: clk_alw_0_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_0_5: clk_alw_0_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_0_8: clk_alw_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_1_0: clk_alw_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_1_1: clk_alw_1_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_1_8: clk_alw_1_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_2_0: clk_alw_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_2_1: clk_alw_2_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_2_2: clk_alw_2_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_2_4: clk_alw_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_2_5: clk_alw_2_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_2_8: clk_alw_2_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_6_8: clk_alw_6_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <6>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_7_0: clk_alw_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_8_0: clk_alw_8_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <8>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_0: clk_alw_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_1: clk_alw_a_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <1>;
> +               };
> +
> +               clk_ddr3_0_0: clk_ddr3_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <1>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_0: clk_main_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_8: clk_main_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_main_1_3: clk_main_1_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_1_4: clk_main_1_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_0: clk_main_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_2_3: clk_main_2_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_2_7: clk_main_2_7 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <7>;
> +               };
> +
> +               clk_main_3_0: clk_main_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_3_3: clk_main_3_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_3_4: clk_main_3_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_3_5: clk_main_3_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_3_6: clk_main_3_6 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <6>;
> +               };
> +
> +               clk_main_4_0: clk_main_4_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_4_1: clk_main_4_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_5_0: clk_main_5_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_5_3: clk_main_5_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_5_4: clk_main_5_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_5_5: clk_main_5_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_7_0: clk_main_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_8_1: clk_main_8_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <8>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_9_0: clk_main_9_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <9>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_0_0: clk_hdmi_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_1_0: clk_hdmi_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_2_0: clk_hdmi_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_hdmi_3_0: clk_hdmi_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_dphy_0_0: clk_dphy_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_dphy_1_0: clk_dphy_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +       };
> +
> +       gic: interrupt-controller at 2c001000 {

All these devices should be under a bus node with simple-bus compatible string.

> +               compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
> +               #interrupt-cells = <3>;
> +               interrupt-controller;
> +               reg = <0 0x2c001000 0x1000>,
> +                     <0 0x2c002000 0x1000>,

I think the size should be 0x2000 here for the cpu interface.

> +                     <0 0x2c004000 0x2000>,
> +                     <0 0x2c006000 0x2000>;
> +               interrupts = <1 9 0xf04>;
> +       };
> +
> +       timer0: timer0 at 31080000 {

should be timer at 31080000

> +               compatible = "arm,sp804";
> +               reg = <0 0x31080000 0x10000>;

sp804 size is 0x1000.

> +               interrupts = <0 324 4>,
> +                            <0 325 4>;
> +               clocks = <&clk_alw_6_8>;
> +       };

No arch timer?


> +
> +       mhu: mhu0 at 2b1f0000 {

mhu0 should be mbox or mailbox.

> +               #mbox-cells = <1>;
> +               compatible = "fujitsu,mhu";
> +               reg = <0 0x2B1F0000 0x1000>;

Lower case hex please.

> +               interrupts = <0 36 4>, /* LP Non-Sec */
> +                            <0 35 4>, /* HP Non-Sec */
> +                            <0 37 4>; /* Secure */
> +       };
> +
> +       mhu_client: scb at 0 {
> +               compatible = "fujitsu,scb";
> +               mbox = <&mhu 1>;

Is the mailbox binding finalized?

> +               mbox-names = "HP_NonSec";
> +       };
> +
> +       pinctrl: pinctrl at 2a4d0000 {
> +               compatible = "fujitsu,mb86s70-pinctrl";
> +               reg = <0 0x2a4d0000 0x1000>;
> +               #gpio-range-cells = <3>;
> +       };
> +
> +       gpio0: mb86s70_gpio0 {

node name should be gpio at 31000000

> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31000000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 0 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       gpio1: mb86s70_gpio1 {

ditto

> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31010000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 32 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       uart0: serial at 0x31040000 {

Drop the 0x in the unit-address.

> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31040000 0x100>;
> +               interrupts = <0 320 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       uart1: serial at 0x31050000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31050000 0x100>;
> +               interrupts = <0 321 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       uart2: serial at 0x31060000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31060000 0x100>;
> +               interrupts = <0 322 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +       };
> +
> +       sdhci0: emmc at 300c0000 {
> +               compatible = "fujitsu,f-sdh30";

This should include the chip part number the IP is in.

> +               reg = <0 0x300c0000 0x1000>;
> +               interrupts = <0 164 0x4>,
> +                            <0 165 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               bus-width = <8>;
> +               clocks = <&clk_alw_1_8>, <&clk_alw_6_8>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +       };
> +
> +       sdhci1: sdio at 36600000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x36600000 0x1000>;
> +               interrupts = <0 172 0x4>,
> +                            <0 173 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               pwr-mux-gpios = <&gpio0 7 0>;
> +               clocks = <&clk_hdmi_2_0>, <&clk_hdmi_3_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +       };
> +
> +       eth0: f_taiki {
> +                compatible = "fujitsu,ogma";

This should include the chip part number the IP is in.

> +               reg = <0 0x31600000 0x10000>,
> +                       <0 0x31618000 0x4000>,
> +                       <0 0x3161c000 0x4000>;
> +               interrupts = <0 163 0x4>;
> +               clocks = <&clk_alw_0_8>;
> +               phy-mode = "rgmii";
> +               max-speed = <1000>;
> +               max-frame-size = <9000>;
> +               local-mac-address = [ a4 17 31 00 00 ed ];
> +               phy-handle = <&ethphy0>;
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               ethphy0: ethernet-phy at 1 {
> +                       device_type = "ethernet-phy";
> +                       compatible = "ethernet-phy-ieee802.3-c22";
> +                       reg = <1>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s70eb.dts b/arch/arm/boot/dts/mb86s70eb.dts
> new file mode 100644
> index 0000000..ee25dd9
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s70eb.dts
> @@ -0,0 +1,38 @@
> +
> +/include/ "mb86s70.dtsi"
> +
> +#include <dt-bindings/gpio/gpio.h>
> +
> +/ {
> +       model = "Fujitsu MB86S70 EVB";
> +       compatible = "fujitsu,mb86s70-evb";
> +
> +       memory {
> +               device_type = "memory";
> +               reg = <0 0x80000000 0x80000000>, <0x08 0x80000000 0x80000000>;
> +
> +       };
> +
> +       chosen {
> +               bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk1p2 rootfstype=ext4 rootwait rw";
> +       };
> +
> +       gpio-leds {
> +               compatible = "gpio-leds";
> +
> +               d3 {
> +                       label = "led3";

This reflects the label on the board? If not, a better name is needed
like what is its function.

> +                       gpios = <&gpio0 4 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "default-on";
> +               };
> +               d4 {
> +                       label = "led4";
> +                       gpios = <&gpio0 5 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "mmc1";
> +               };
> +               d5 {
> +                       label = "led5";
> +                       gpios = <&gpio0 6 GPIO_ACTIVE_HIGH>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s73.dtsi b/arch/arm/boot/dts/mb86s73.dtsi
> new file mode 100644
> index 0000000..ef4f5a0
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s73.dtsi

Similar comments apply to this dtsi file.

> @@ -0,0 +1,910 @@
> +
> +/ {
> +       model = "Fujitsu mb86s73";
> +       compatible = "fujitsu,mb86s73";
> +       interrupt-parent = <&gic>;
> +       #address-cells = <2>;
> +       #size-cells = <1>;
> +
> +       aliases {
> +               serial0 = &uart0;
> +               serial1 = &uart1;
> +               serial2 = &uart2;
> +       };
> +
> +       cpus {
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               cpu0: cpu at 0 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x100>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000   900000
> +                               1200000  1000000
> +                       >;
> +                       clock-latency = <100000>;
> +                       fujitsu,power-domain = <&pd_cpu>;
> +               };
> +
> +               cpu1: cpu at 1 {
> +                       device_type = "cpu";
> +                       compatible = "arm,cortex-a7";
> +                       reg = <0x101>;
> +                       cci-control-port = <&cci_control3>;
> +                       clock-frequency = <800000000>;
> +                       operating-points = <
> +                               /* kHz    uV */
> +                               800000   900000
> +                               1200000  1000000
> +                       >;
> +                       clock-latency = <100000>;
> +                       fujitsu,power-domain = <&pd_cpu>;
> +               };
> +       };
> +
> +       cci at 2c090000 {
> +               compatible = "arm,cci-400";
> +               #address-cells = <1>;
> +               #size-cells = <1>;
> +               reg = <0 0x2c090000 0x1000>;
> +               ranges = <0x0 0x0 0x2c090000 0x10000>;
> +               fujitsu,power-domain = <&pd_offchip>;
> +
> +               cci_control3: slave-if at 4000 {
> +               compatible = "arm,cci-400-ctrl-if";
> +                       interface-type = "ace";
> +                       reg = <0x4000 0x1000>;
> +               };
> +       };
> +
> +       /**
> +        * cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA7, 4->USB, 5->FPDLINK
> +        * port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
> +        */
> +       clocks {
> +               clk_alw_0_0: clk_alw_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_0_1: clk_alw_0_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_0_2: clk_alw_0_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_0_4: clk_alw_0_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_0_5: clk_alw_0_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_0_8: clk_alw_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_1_0: clk_alw_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_1_1: clk_alw_1_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_1_2: clk_alw_1_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_1_8: clk_alw_1_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <1>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_2_0: clk_alw_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_2_1: clk_alw_2_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_2_2: clk_alw_2_2 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <2>;
> +               };
> +
> +               clk_alw_2_4: clk_alw_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_alw_2_5: clk_alw_2_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <5>;
> +               };
> +
> +               clk_alw_2_8: clk_alw_2_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <2>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_6_8: clk_alw_6_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <6>;
> +                       port = <8>;
> +               };
> +
> +               clk_alw_7_0: clk_alw_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_8_0: clk_alw_8_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <8>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_0: clk_alw_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_a_1: clk_alw_a_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0a>;
> +                       port = <1>;
> +               };
> +
> +               clk_alw_b_0: clk_alw_b_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0b>;
> +                       port = <0>;
> +               };
> +
> +               clk_alw_c_0: clk_alw_c_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <0>;
> +                       domain = <0x0c>;
> +                       port = <0>;
> +               };
> +
> +               clk_ddr3_0_0: clk_ddr3_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <1>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_0: clk_main_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_0_8: clk_main_0_8 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0>;
> +                       port = <8>;
> +               };
> +
> +               clk_main_1_3: clk_main_1_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_1_4: clk_main_1_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <1>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_0: clk_main_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_2_3: clk_main_2_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_2_4: clk_main_2_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_2_7: clk_main_2_7 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <2>;
> +                       port = <7>;
> +               };
> +
> +               clk_main_3_0: clk_main_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_3_3: clk_main_3_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_3_4: clk_main_3_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_3_5: clk_main_3_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_3_6: clk_main_3_6 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <3>;
> +                       port = <6>;
> +               };
> +
> +               clk_main_4_0: clk_main_4_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_4_4: clk_main_4_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_4_5: clk_main_4_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <4>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_5_0: clk_main_5_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_5_3: clk_main_5_3 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <3>;
> +               };
> +
> +               clk_main_5_4: clk_main_5_4 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <4>;
> +               };
> +
> +               clk_main_5_5: clk_main_5_5 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <5>;
> +                       port = <5>;
> +               };
> +
> +               clk_main_7_0: clk_main_7_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <7>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_8_1: clk_main_8_1 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <8>;
> +                       port = <1>;
> +               };
> +
> +               clk_main_9_0: clk_main_9_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <9>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_a_0: clk_main_a_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xa>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_b_0: clk_main_b_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xb>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_c_0: clk_main_c_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xc>;
> +                       port = <0>;
> +               };
> +
> +               clk_main_d_0: clk_main_d_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <2>;
> +                       domain = <0xd>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_0_0: clk_usb_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_1_0: clk_usb_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_2_0: clk_usb_2_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <2>;
> +                       port = <0>;
> +               };
> +
> +               clk_usb_3_0: clk_usb_3_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <4>;
> +                       domain = <3>;
> +                       port = <0>;
> +               };
> +
> +               clk_fpdlink_0_0: clk_fpdlink_0_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <0>;
> +                       port = <0>;
> +               };
> +
> +               clk_fpdlink_1_0: clk_fpdlink_1_0 {
> +                       compatible = "fujitsu,mb86s7x_clk";
> +                       #clock-cells = <0>;
> +                       cntrlr = <5>;
> +                       domain = <1>;
> +                       port = <0>;
> +               };
> +       };
> +
> +       timer0: timer0 at 31080000 {
> +               compatible = "arm,sp804";
> +               reg = <0 0x31080000 0x10000>;
> +               interrupts = <0 324 4>,
> +                            <0 325 4>;
> +               clocks = <&clk_alw_6_8>;
> +       };
> +
> +       timer1: archtimer {
> +               compatible = "arm,armv7-timer";
> +               clock-frequency = <125000000>;
> +               interrupts = <1 13 0xf08>,
> +                            <1 14 0xf08>,
> +                            <1 11 0xf08>,
> +                            <1 10 0xf08>;
> +       };
> +
> +       pinctrl: pinctrl at 2a4d0000 {
> +               compatible = "fujitsu,mb86s73-pinctrl";
> +               reg = <0 0x2a4d0000 0x1000>, <0 0x312e0000 0x1000>;
> +               #gpio-range-cells = <3>;
> +
> +               pcie0_pins_active: pcie0_active {
> +                       mb86s7x,function = "pcie0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pcie0_pins_sleep: pcie0_sleep {
> +                       mb86s7x,function = "pcie0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb3h0_pins_active: usb3h0_active {
> +                       mb86s7x,function = "usb3h0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb3h0_pins_sleep: usb3h0_sleep {
> +                       mb86s7x,function = "usb3h0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb2h0_pins_active: usb2h0_active {
> +                       mb86s7x,function = "usb2h0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb2h0_pins_sleep: usb2h0_sleep {
> +                       mb86s7x,function = "usb2h0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb2d0_pins_active: usb2d0_active {
> +                       mb86s7x,function = "usb2d0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb2d0_pins_sleep: usb2d0_sleep {
> +                       mb86s7x,function = "usb2d0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               sdh30_pins_active: sdh30_active {
> +                       mb86s7x,function = "sdh30";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               sdh30_pins_sleep: sdh30_sleep {
> +                       mb86s7x,function = "sdh30";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               emmc0_pins_active: emmc0_active {
> +                       mb86s7x,function = "emmc0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               emmc0_pins_sleep: emmc0_sleep {
> +                       mb86s7x,function = "emmc0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               hsspi0_pins_active: hsspi0_active {
> +                       mb86s7x,function = "hsspi0";
> +                       mb86s7x,drvst = <8>; /* in mA */
> +               };
> +               hsspi0_pins_sleep: hsspi0_sleep {
> +                       mb86s7x,function = "hsspi0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               gmacd0_pins_active: gmacd0_active {
> +                       mb86s7x,function = "gmacd0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               gmacd0_pins_sleep: gmacd0_sleep {
> +                       mb86s7x,function = "gmacd0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               gmacm0_pins_active: gmacm0_active {
> +                       mb86s7x,function = "gmacm0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               gmacm0_pins_sleep: gmacm0_sleep {
> +                       mb86s7x,function = "gmacm0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               i2s0_pins_active: i2s0_active {
> +                       mb86s7x,function = "i2s0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               i2s0_pins_sleep: i2s0_sleep {
> +                       mb86s7x,function = "i2s0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               other0_pins_active: other0_active {
> +                       mb86s7x,function = "other0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               other0_pins_sleep: other0_sleep {
> +                       mb86s7x,function = "other0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               jtag0_pins_active: jtag0_active {
> +                       mb86s7x,function = "jtag0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               jtag0_pins_sleep: jtag0_sleep {
> +                       mb86s7x,function = "jtag0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               pcie1_pins_active: pcie1_active {
> +                       mb86s7x,function = "pcie1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pcie1_pins_sleep: pcie1_sleep {
> +                       mb86s7x,function = "pcie1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               usb3h1_pins_active: usb3h1_active {
> +                       mb86s7x,function = "usb3h1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               usb3h1_pins_sleep: usb3h1_sleep {
> +                       mb86s7x,function = "usb3h1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               cfg_pins_active: cfg_active {
> +                       mb86s7x,function = "cfg";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               cfg_pins_sleep: cfg_sleep {
> +                       mb86s7x,function = "cfg";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart0_pins_active: uart0_active {
> +                       mb86s7x,function = "uart0";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart0_pins_sleep: uart0_sleep {
> +                       mb86s7x,function = "uart0";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart1_pins_active: uart1_active {
> +                       mb86s7x,function = "uart1";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart1_pins_sleep: uart1_sleep {
> +                       mb86s7x,function = "uart1";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               uart2_pins_active: uart2_active {
> +                       mb86s7x,function = "uart2";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               uart2_pins_sleep: uart2_sleep {
> +                       mb86s7x,function = "uart2";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               trace_pins_active: trace_active {
> +                       mb86s7x,function = "trace";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               trace_pins_sleep: trace_sleep {
> +                       mb86s7x,function = "trace";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               pl244_pins_active: pl244_active {
> +                       mb86s7x,function = "pl244";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               pl244_pins_sleep: pl244_sleep {
> +                       mb86s7x,function = "pl244";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               smt_pins_active: smt_active {
> +                       mb86s7x,function = "smt";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               smt_pins_sleep: smt_sleep {
> +                       mb86s7x,function = "smt";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +
> +               memcs_pins_active: memcs_active {
> +                       mb86s7x,function = "memcs";
> +                       mb86s7x,drvst = <4>; /* in mA */
> +               };
> +               memcs_pins_sleep: memcs_sleep {
> +                       mb86s7x,function = "memcs";
> +                       mb86s7x,drvst = <0>; /* Implies Hi-z */
> +               };
> +       };
> +
> +       gpio0: mb86s70_gpio0 {
> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31000000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 0 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       gpio1: mb86s70_gpio1 {
> +               compatible = "fujitsu,mb86s7x-gpio";
> +               reg = <0 0x31010000 0x10000>;
> +               gpio-controller;
> +               #gpio-cells = <2>;
> +               gpio-ranges = <&pinctrl 0 32 32>;
> +               clocks = <&clk_alw_2_1>;
> +       };
> +
> +       pd_alwayson: genpd at 0 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <0>;
> +       };
> +
> +       pd_default: genpd at 1 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <1>;
> +       };
> +
> +       pd_offchip: genpd at 2 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <2>;
> +       };
> +
> +       pd_cpu: genpd at 3 {
> +               compatible = "fujitsu,mb86s7x-pd";
> +               index = <3>;
> +       };
> +
> +       gic: interrupt-controller at 2c001000 {
> +               compatible = "arm,cortex-a15-gic", "arm,cortex-a9-gic";
> +               #interrupt-cells = <3>;
> +               interrupt-controller;
> +               reg = <0 0x2c001000 0x1000>,
> +                     <0 0x2c002000 0x1000>,
> +                     <0 0x2c004000 0x2000>,
> +                     <0 0x2c006000 0x2000>;
> +               interrupts = <1 9 0xf04>;
> +       };
> +
> +       pmu_a7 {
> +               compatible = "arm,cortex-a7-pmu";
> +               interrupts = <0 18 4>,
> +                            <0 22 4>;
> +               fujitsu,power-domain = <&pd_cpu>;
> +       };
> +
> +       mhu: mhu0 at 2b1f0000 {
> +               #mbox-cells = <1>;
> +               compatible = "fujitsu,mhu";
> +               reg = <0 0x2B1F0000 0x1000>;
> +               interrupts = <0 36 4>, /* LP Non-Sec */
> +                            <0 35 4>, /* HP Non-Sec */
> +                            <0 37 4>; /* Secure */
> +               fujitsu,power-domain = <&pd_default>;
> +       };
> +
> +       mhu_client: scb at 0 {
> +               compatible = "fujitsu,scb";
> +               mbox = <&mhu 1>;
> +               mbox-names = "HP_NonSec";
> +       };
> +
> +       uart0: serial at 0x31040000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31040000 0x100>;
> +               interrupts = <0 320 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart0_pins_active>;
> +               pinctrl-1 = <&uart0_pins_sleep>;
> +       };
> +
> +       uart1: serial at 0x31050000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31050000 0x100>;
> +               interrupts = <0 321 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart1_pins_active>;
> +               pinctrl-1 = <&uart1_pins_sleep>;
> +       };
> +
> +       uart2: serial at 0x31060000 {
> +               compatible = "snps,dw-apb-uart";
> +               reg = <0 0x31060000 0x100>;
> +               interrupts = <0 322 0x4>;
> +               clock-frequency = <62500000>;
> +               reg-io-width = <4>;
> +               reg-shift = <2>;
> +               clocks = <&clk_alw_2_1>;
> +               clock-names = "sclk";
> +               fujitsu,power-domain = <&pd_default>;
> +               pinctrl-names = "default", "sleep";
> +               pinctrl-0 = <&uart2_pins_active>;
> +               pinctrl-1 = <&uart2_pins_sleep>;
> +       };
> +
> +       sdhci0: emmc at 300c0000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x300c0000 0x1000>;
> +               interrupts = <0 164 0x4>,
> +                            <0 165 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               bus-width = <8>;
> +               clocks = <&clk_alw_c_0>, <&clk_alw_b_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +               fujitsu,power-domain = <&pd_default>;
> +       };
> +
> +       sdhci1: sdio at 36600000 {
> +               compatible = "fujitsu,f-sdh30";
> +               reg = <0 0x36600000 0x1000>;
> +               interrupts = <0 172 0x4>,
> +                            <0 173 0x4>;
> +               voltage-ranges = <1800 1800>, <3300 3300>;
> +               clocks = <&clk_main_c_0>, <&clk_main_d_0>;
> +               clock-names = "sd_sd4clk", "sd_bclk";
> +               resume-detect-retry;
> +               fujitsu,power-domain = <&pd_offchip>;
> +       };
> +
> +       eth0: f_taiki {
> +                compatible = "fujitsu,ogma";
> +               reg = <0 0x31600000 0x10000>, <0 0x31618000 0x4000>, <0 0x3161c000 0x4000>;
> +               interrupts = <0 163 0x4>;
> +               clocks = <&clk_alw_0_8>;
> +               phy-mode = "rgmii";
> +               max-speed = <1000>;
> +               max-frame-size = <9000>;
> +               local-mac-address = [ a4 17 31 00 00 ed ];
> +               phy-handle = <&ethphy0>;
> +               fujitsu,power-domain = <&pd_default>;
> +
> +               #address-cells = <1>;
> +               #size-cells = <0>;
> +
> +               ethphy0: ethernet-phy at 1 {
> +                       device_type = "ethernet-phy";
> +                       compatible = "ethernet-phy-ieee802.3-c22";
> +                       reg = <1>;
> +               };
> +       };
> +};
> diff --git a/arch/arm/boot/dts/mb86s73eb.dts b/arch/arm/boot/dts/mb86s73eb.dts
> new file mode 100644
> index 0000000..81862d9
> --- /dev/null
> +++ b/arch/arm/boot/dts/mb86s73eb.dts
> @@ -0,0 +1,73 @@
> +
> +/include/ "mb86s73.dtsi"
> +
> +#include <dt-bindings/gpio/gpio.h>
> +#include <dt-bindings/input/input.h>
> +
> +/ {
> +       model = "Fujitsu MB86S73 EVB";
> +       compatible = "fujitsu,mb86s73-evb";
> +
> +       memory {
> +               device_type = "memory";
> +               reg = <0 0x80000000 0x80000000>;
> +       };
> +
> +       chosen {
> +               bootargs = "loglevel=4 console=ttyS0,115200 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait rw";
> +       };
> +
> +       gpio-leds {
> +               compatible = "gpio-leds";
> +
> +               d34_a {
> +                       label = "led34a";
> +                       gpios = <&gpio1 30 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "heartbeat";
> +               };
> +               d34_b {
> +                       label = "led34b";
> +                       gpios = <&gpio1 31 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "mmc1";
> +               };
> +               d37 {
> +                       label = "led37";
> +                       gpios = <&gpio0 30 GPIO_ACTIVE_HIGH>;
> +                       linux,default-trigger = "default-on";
> +               };
> +               d38 {
> +                       label = "led38";
> +                       gpios = <&gpio0 31 GPIO_ACTIVE_HIGH>;
> +               };
> +       };
> +
> +       gpio-keys {
> +               compatible = "gpio-keys-polled";
> +               poll-interval = <20>;
> +               home {
> +                       label = "Home";
> +                       gpios = <&gpio1 4 0>;
> +                       linux,code = <KEY_HOME>;
> +                       gpio-key;
> +               };
> +               power {
> +                       label = "Power";
> +                       gpios = <&gpio1 5 0>;
> +                       linux,code = <KEY_POWER>;
> +                       gpio-key;
> +               };
> +
> +               up {
> +                       label = "Up";
> +                       gpios = <&gpio1 6 0>;
> +                       linux,code = <KEY_UP>;
> +                       gpio-key;
> +               };
> +               down {
> +                       label = "Down";
> +                       gpios = <&gpio1 7 0>;
> +                       linux,code = <KEY_DOWN>;
> +                       gpio-key;
> +               };
> +       };
> +};
> diff --git a/arch/arm/configs/fujitsu_defconfig b/arch/arm/configs/fujitsu_defconfig
> new file mode 100644
> index 0000000..bfc78c9
> --- /dev/null
> +++ b/arch/arm/configs/fujitsu_defconfig
> @@ -0,0 +1,156 @@
> +# CONFIG_LOCALVERSION_AUTO is not set
> +CONFIG_DEFAULT_HOSTNAME="MB86S7x"
> +CONFIG_SYSVIPC=y
> +CONFIG_NO_HZ=y
> +CONFIG_HIGH_RES_TIMERS=y
> +CONFIG_IKCONFIG=y
> +CONFIG_IKCONFIG_PROC=y
> +CONFIG_NAMESPACES=y
> +CONFIG_BLK_DEV_INITRD=y
> +CONFIG_RD_BZIP2=y
> +CONFIG_RD_LZMA=y
> +CONFIG_RD_XZ=y
> +CONFIG_RD_LZO=y
> +CONFIG_CC_OPTIMIZE_FOR_SIZE=y
> +CONFIG_EXPERT=y
> +# CONFIG_COMPAT_BRK is not set
> +CONFIG_SLAB=y
> +CONFIG_PROFILING=y
> +CONFIG_MODULES=y
> +CONFIG_MODULE_UNLOAD=y
> +CONFIG_ARCH_MB86S7X=y
> +CONFIG_ARM_LPAE=y
> +# CONFIG_SWP_EMULATE is not set
> +CONFIG_ARM_ERRATA_754322=y
> +CONFIG_ARM_ERRATA_754327=y
> +CONFIG_ARM_ERRATA_764369=y
> +CONFIG_ARM_ERRATA_775420=y
> +CONFIG_ARM_ERRATA_798181=y
> +CONFIG_PCI=y
> +CONFIG_PCIEASPM_POWERSAVE=y
> +CONFIG_SMP=y
> +CONFIG_SCHED_MC=y
> +CONFIG_SCHED_SMT=y
> +CONFIG_BL_SWITCHER=y
> +CONFIG_BL_SWITCHER_DUMMY_IF=m
> +CONFIG_VMSPLIT_2G=y
> +CONFIG_PREEMPT=y
> +CONFIG_THUMB2_KERNEL=y
> +CONFIG_HIGHMEM=y
> +CONFIG_CMA=y
> +# CONFIG_ATAGS is not set
> +CONFIG_ZBOOT_ROM_TEXT=0x0
> +CONFIG_ZBOOT_ROM_BSS=0x0
> +CONFIG_CMDLINE="mem=2048M earlycon=ttyS0,115200 earlyprintk console=ttyS0,115200 console=tty0 root=/dev/mmcblk0p2 rootfstype=ext4 rootwait"
> +CONFIG_KEXEC=y
> +CONFIG_CPU_IDLE=y
> +CONFIG_VFP=y
> +CONFIG_NEON=y
> +CONFIG_PM_WAKELOCKS=y
> +CONFIG_PM_RUNTIME=y
> +CONFIG_PM_DEBUG=y
> +CONFIG_NET=y
> +CONFIG_PACKET=y
> +CONFIG_UNIX=y
> +CONFIG_INET=y
> +CONFIG_IP_MULTICAST=y
> +CONFIG_IP_PNP=y
> +CONFIG_IP_PNP_DHCP=y
> +CONFIG_IP_PNP_BOOTP=y
> +# CONFIG_INET_XFRM_MODE_TRANSPORT is not set
> +# CONFIG_INET_XFRM_MODE_TUNNEL is not set
> +# CONFIG_INET_XFRM_MODE_BEET is not set
> +# CONFIG_INET_LRO is not set
> +CONFIG_IPV6=y
> +CONFIG_INET6_XFRM_MODE_TRANSPORT=m
> +CONFIG_INET6_XFRM_MODE_TUNNEL=m
> +CONFIG_INET6_XFRM_MODE_BEET=m
> +CONFIG_IPV6_SIT=m
> +# CONFIG_ANDROID_PARANOID_NETWORK is not set
> +# CONFIG_WIRELESS is not set
> +CONFIG_DEVTMPFS=y
> +CONFIG_DEVTMPFS_MOUNT=y
> +# CONFIG_FW_LOADER_USER_HELPER is not set
> +CONFIG_DMA_CMA=y
> +CONFIG_CMA_SIZE_MBYTES=192
> +CONFIG_BLK_DEV_LOOP=m
> +CONFIG_BLK_DEV_CRYPTOLOOP=m
> +CONFIG_BLK_DEV_RAM=y
> +CONFIG_BLK_DEV_RAM_COUNT=2
> +CONFIG_BLK_DEV_RAM_SIZE=1024
> +# CONFIG_INPUT_MOUSEDEV_PSAUX is not set
> +CONFIG_INPUT_MOUSEDEV_SCREEN_X=800
> +CONFIG_INPUT_MOUSEDEV_SCREEN_Y=480
> +CONFIG_INPUT_EVDEV=m
> +# CONFIG_KEYBOARD_ATKBD is not set
> +CONFIG_KEYBOARD_GPIO_POLLED=y
> +# CONFIG_MOUSE_PS2 is not set
> +CONFIG_MOUSE_GPIO=m
> +CONFIG_INPUT_TOUCHSCREEN=y
> +CONFIG_TOUCHSCREEN_EGALAX=m
> +CONFIG_TOUCHSCREEN_ILI210X=m
> +# CONFIG_SERIO_SERPORT is not set
> +CONFIG_VT_HW_CONSOLE_BINDING=y
> +CONFIG_LEGACY_PTY_COUNT=16
> +# CONFIG_DEVKMEM is not set
> +CONFIG_SERIAL_8250=y
> +# CONFIG_SERIAL_8250_DEPRECATED_OPTIONS is not set
> +CONFIG_SERIAL_8250_CONSOLE=y
> +CONFIG_SERIAL_8250_EXTENDED=y
> +CONFIG_SERIAL_8250_DW=y
> +CONFIG_SERIAL_OF_PLATFORM=y
> +# CONFIG_HW_RANDOM is not set
> +CONFIG_I2C=y
> +# CONFIG_I2C_COMPAT is not set
> +# CONFIG_I2C_HELPER_AUTO is not set
> +CONFIG_GPIO_SYSFS=y
> +# CONFIG_HWMON is not set
> +# CONFIG_VGA_ARB is not set
> +# CONFIG_HID is not set
> +# CONFIG_USB_SUPPORT is not set
> +CONFIG_MMC=y
> +CONFIG_MMC_CLKGATE=y
> +CONFIG_MMC_SDHCI=y
> +CONFIG_MMC_SDHCI_F_SDH30=y
> +CONFIG_NEW_LEDS=y
> +CONFIG_LEDS_CLASS=y
> +CONFIG_LEDS_GPIO=y
> +CONFIG_LEDS_TRIGGERS=y
> +CONFIG_LEDS_TRIGGER_TIMER=y
> +CONFIG_LEDS_TRIGGER_HEARTBEAT=y
> +CONFIG_LEDS_TRIGGER_BACKLIGHT=m
> +CONFIG_LEDS_TRIGGER_CPU=y
> +CONFIG_LEDS_TRIGGER_GPIO=y
> +CONFIG_LEDS_TRIGGER_DEFAULT_ON=y
> +CONFIG_LEDS_TRIGGER_TRANSIENT=m
> +CONFIG_MAILBOX=y
> +CONFIG_MBOX_F_MHU=y
> +# CONFIG_IOMMU_SUPPORT is not set
> +CONFIG_EXT4_FS=y
> +CONFIG_EXT4_FS_POSIX_ACL=y
> +CONFIG_EXT4_FS_SECURITY=y
> +CONFIG_VFAT_FS=m
> +CONFIG_TMPFS=y
> +CONFIG_CONFIGFS_FS=m
> +CONFIG_CRAMFS=y
> +CONFIG_ROMFS_FS=y
> +CONFIG_NLS_CODEPAGE_437=m
> +CONFIG_NLS_ISO8859_1=m
> +CONFIG_PRINTK_TIME=y
> +CONFIG_DEBUG_INFO=y
> +CONFIG_DEBUG_FS=y
> +CONFIG_MAGIC_SYSRQ=y
> +CONFIG_DEBUG_MEMORY_INIT=y
> +# CONFIG_DEBUG_PREEMPT is not set
> +# CONFIG_RCU_CPU_STALL_VERBOSE is not set
> +# CONFIG_FTRACE is not set
> +CONFIG_DEBUG_USER=y
> +# CONFIG_DEBUG_LL is not set
> +CONFIG_DEBUG_LL_UART_8250=y
> +CONFIG_DEBUG_UART_PHYS=0x31040000
> +CONFIG_DEBUG_UART_VIRT=0xfe000000
> +CONFIG_DEBUG_UART_8250_WORD=y
> +# CONFIG_EARLY_PRINTK is not set
> +CONFIG_CRYPTO_AES=y
> +# CONFIG_CRYPTO_ANSI_CPRNG is not set
> +# CONFIG_CRYPTO_HW is not set
> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> new file mode 100644
> index 0000000..44f5b0c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Kconfig
> @@ -0,0 +1,18 @@
> +config ARCH_MB86S7X
> +       bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> +       select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> +       select ARM_AMBA
> +       select ARM_GIC
> +       select ARM_TIMER_SP804
> +       select HAVE_ARM_ARCH_TIMER
> +       select ARCH_REQUIRE_GPIOLIB
> +       select ARCH_HAS_CPUFREQ
> +       select ARCH_HAS_OPP
> +       select PM_OPP
> +       select PINCTRL
> +       select PINCTRL_MB86S7X
> +       select ARM_CCI
> +       select BIG_LITTLE
> +       select PM_GENERIC_DOMAINS if PM
> +       help
> +         Support for Fujitsu MB86S7x based platforms
> diff --git a/arch/arm/mach-mb86s7x/Makefile b/arch/arm/mach-mb86s7x/Makefile
> new file mode 100644
> index 0000000..5524a6c
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/Makefile
> @@ -0,0 +1,2 @@
> +obj-y          += board.o scb_mhu.o mcpm.o
> +obj-$(CONFIG_PM_GENERIC_DOMAINS) += pm_domains.o
> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
> new file mode 100644
> index 0000000..d6e76ec
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/board.c
> @@ -0,0 +1,65 @@
> +/*
> + * Support for the Fujitsu's MB86S7x based devices.
> + *
> + * Copyright (C) 2014 Linaro, LTD
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/of.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/mach/map.h>
> +#include <asm/mach/arch.h>
> +
> +#include "iomap.h"
> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +       struct device_node *node;
> +
> +       node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +       if (node && of_device_is_available(node)) {
> +               mcpm_smp_set_ops();
> +               return true;
> +       }
> +
> +       return false;
> +}
> +
> +#define IOMAP_DEV(name) { \
> +               .virtual = (unsigned long) MB86S7X_##name##_VIRT, \
> +               .pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
> +               .length = MB86S7X_##name##_SIZE, \
> +               .type = MT_DEVICE, \
> +       }
> +
> +static struct map_desc mb86s7x_io_desc[] = {
> +       IOMAP_DEV(MHU),
> +       IOMAP_DEV(ISRAM),
> +};
> +
> +void __init mb86s7x_dt_map_io(void)
> +{
> +       iotable_init(mb86s7x_io_desc, ARRAY_SIZE(mb86s7x_io_desc));
> +}
> +
> +static const char *mb86s7x_dt_match[] __initconst = {
> +       "fujitsu,mb86s70-evb",
> +       "fujitsu,mb86s73-evb",
> +       NULL,
> +};
> +
> +DT_MACHINE_START(MB86S7X_DT, "Fujitsu MB86S7X-based board")
> +       .dt_compat      = mb86s7x_dt_match,
> +       .smp_init       = smp_init_ops(mb86s7x_smp_init_ops),
> +       .map_io         = mb86s7x_dt_map_io,
> +MACHINE_END
> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
> new file mode 100644
> index 0000000..2b8db1d
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/iomap.h
> @@ -0,0 +1,34 @@
> +/*
> + * arch/arm/mach-mb86s7x/iomap.h
> + *
> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
> + * Copyright:  (C) 2013-2014 Linaro Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __MB86S7X_IOMAP_H
> +#define __MB86S7X_IOMAP_H
> +
> +#include <linux/sizes.h>
> +
> +#define SEC_RSTADDR_OFF                0x3fc
> +
> +#define MB86S7X_MHU_PHYS       0x2b1f0000
> +#define MB86S7X_MHU_SIZE       SZ_4K
> +
> +/* Internal SRAM */
> +#define MB86S7X_ISRAM_PHYS     0x2e000000
> +#define MB86S7X_ISRAM_SIZE     SZ_16K
> +#define MB86S7X_TRAMPOLINE_PHYS        (MB86S7X_ISRAM_PHYS + 0x3c00)
> +
> +#define MB86S7X_ISRAM_VIRT     IOMEM(0xfe000000)
> +#define MB86S7X_MHU_VIRT       (MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
> +
> +/* Non-Secure High-Priority Channel is used */
> +#define MB86S7X_SHM_FROM_SCB_VIRT      (MB86S7X_ISRAM_VIRT + 0x3800)
> +#define MB86S7X_TRAMPOLINE_VIRT                (MB86S7X_ISRAM_VIRT + 0x3c00)
> +
> +#endif /* __MB86S7X_IOMAP_H */
> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
> new file mode 100644
> index 0000000..86d223f
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/mcpm.c
> @@ -0,0 +1,293 @@
> +/*
> + * arch/arm/mach-mb86s7x/mcpm.c
> + *
> + * "Inspired" by tc_pm.c
> + * Copyright:  (C) 2013-2014  Fujitsu Semiconductor Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/reboot.h>
> +#include <linux/arm-cci.h>
> +#include <linux/spinlock.h>
> +#include <linux/irqchip/arm-gic.h>
> +#include <linux/platform_data/mb86s7x_mbox.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/cp15.h>
> +#include <asm/cputype.h>
> +#include <asm/system_misc.h>
> +
> +#include "iomap.h"
> +
> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;

You should not be using arch_spinlock_t directly.

> +static int mb86s7x_pm_use_count[2][2];
> +
> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> +
> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> +{
> +       u8 val;
> +
> +       if (clr & ~AT_WFI_COLOR_MASK)
> +               return;
> +
> +       val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +       val &= ~AT_WFI_COLOR_MASK;
> +       val |= clr;
> +       writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +}
> +
> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +       struct completion got_rsp;
> +       int ret = 0;
> +
> +       pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> +
> +       arch_spin_lock(&mb86s7x_pm_lock);
> +
> +       mb86s7x_pm_use_count[cpu][cluster]++;
> +
> +       if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +               struct mb86s7x_cpu_gate cmd;
> +
> +               arch_spin_unlock(&mb86s7x_pm_lock);

Can't you use atomic operations here (i.e. atomic_inc_return) instead of a lock?

In any case, Nicolas should review the cluster PM code if he hasn't
already. As Arnd pointed out, this patch should be split up into
multiple patches. I'd suggest splitting into DT bindings, DTS files,
base support, mailbox, MCPM, and PM domains.

That's all I've got time and energy for reviewing for now.

Rob

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 15:11       ` Rob Herring
@ 2014-07-15 16:11           ` Nicolas Pitre
  -1 siblings, 0 replies; 88+ messages in thread
From: Nicolas Pitre @ 2014-07-15 16:11 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mollie Wu, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	andy.green-QSEj5FYQhm4dnm+yROfE0A, Arnd Bergmann,
	Jaswinder Singh, Olof Johansson, Russell King - ARM Linux,
	Mark Rutland, Rob Herring, Pawel Moll, Linaro Patches,
	Tetsuya Takinishi

On Tue, 15 Jul 2014, Rob Herring wrote:

> Adding Nico for cluster PM code.

Thanks.

This really ought to be split into smaller patches.

[...]

> > index 0000000..86d223f
> > --- /dev/null
> > +++ b/arch/arm/mach-mb86s7x/mcpm.c
> > @@ -0,0 +1,293 @@
> > +/*
> > + * arch/arm/mach-mb86s7x/mcpm.c
> > + *
> > + * "Inspired" by tc_pm.c
> > + * Copyright:  (C) 2013-2014  Fujitsu Semiconductor Limited
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/pm.h>
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/reboot.h>
> > +#include <linux/arm-cci.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/irqchip/arm-gic.h>
> > +#include <linux/platform_data/mb86s7x_mbox.h>
> > +
> > +#include <asm/mcpm.h>
> > +#include <asm/cp15.h>
> > +#include <asm/cputype.h>
> > +#include <asm/system_misc.h>
> > +
> > +#include "iomap.h"
> > +
> > +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
> 
> You should not be using arch_spinlock_t directly.

Depends.  See comment from line 48 in arch/arm/mach-vexpress/tc2_pm.c.

> > +static int mb86s7x_pm_use_count[2][2];
> > +
> > +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> > +
> > +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> > +{
> > +       u8 val;
> > +
> > +       if (clr & ~AT_WFI_COLOR_MASK)
> > +               return;
> > +
> > +       val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> > +       val &= ~AT_WFI_COLOR_MASK;
> > +       val |= clr;
> > +       writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> > +}
> > +
> > +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> > +{
> > +       struct completion got_rsp;
> > +       int ret = 0;
> > +
> > +       pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> > +
> > +       arch_spin_lock(&mb86s7x_pm_lock);

This is called in a preemptible context so local_irq_disable() has to be 
used before arch_spin_lock().  There is no combined operator for it.

> > +       mb86s7x_pm_use_count[cpu][cluster]++;
> > +
> > +       if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> > +               struct mb86s7x_cpu_gate cmd;
> > +
> > +               arch_spin_unlock(&mb86s7x_pm_lock);
> 
> Can't you use atomic operations here (i.e. atomic_inc_return) instead of a lock?

There is more than the count that has to be atomic.

> In any case, Nicolas should review the cluster PM code if he hasn't
> already. As Arnd pointed out, this patch should be split up into
> multiple patches. I'd suggest splitting into DT bindings, DTS files,
> base support, mailbox, MCPM, and PM domains.

Agreed.


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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 16:11           ` Nicolas Pitre
  0 siblings, 0 replies; 88+ messages in thread
From: Nicolas Pitre @ 2014-07-15 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Tue, 15 Jul 2014, Rob Herring wrote:

> Adding Nico for cluster PM code.

Thanks.

This really ought to be split into smaller patches.

[...]

> > index 0000000..86d223f
> > --- /dev/null
> > +++ b/arch/arm/mach-mb86s7x/mcpm.c
> > @@ -0,0 +1,293 @@
> > +/*
> > + * arch/arm/mach-mb86s7x/mcpm.c
> > + *
> > + * "Inspired" by tc_pm.c
> > + * Copyright:  (C) 2013-2014  Fujitsu Semiconductor Limited
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/io.h>
> > +#include <linux/pm.h>
> > +#include <linux/delay.h>
> > +#include <linux/kernel.h>
> > +#include <linux/reboot.h>
> > +#include <linux/arm-cci.h>
> > +#include <linux/spinlock.h>
> > +#include <linux/irqchip/arm-gic.h>
> > +#include <linux/platform_data/mb86s7x_mbox.h>
> > +
> > +#include <asm/mcpm.h>
> > +#include <asm/cp15.h>
> > +#include <asm/cputype.h>
> > +#include <asm/system_misc.h>
> > +
> > +#include "iomap.h"
> > +
> > +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
> 
> You should not be using arch_spinlock_t directly.

Depends.  See comment from line 48 in arch/arm/mach-vexpress/tc2_pm.c.

> > +static int mb86s7x_pm_use_count[2][2];
> > +
> > +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> > +
> > +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> > +{
> > +       u8 val;
> > +
> > +       if (clr & ~AT_WFI_COLOR_MASK)
> > +               return;
> > +
> > +       val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> > +       val &= ~AT_WFI_COLOR_MASK;
> > +       val |= clr;
> > +       writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> > +}
> > +
> > +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> > +{
> > +       struct completion got_rsp;
> > +       int ret = 0;
> > +
> > +       pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> > +
> > +       arch_spin_lock(&mb86s7x_pm_lock);

This is called in a preemptible context so local_irq_disable() has to be 
used before arch_spin_lock().  There is no combined operator for it.

> > +       mb86s7x_pm_use_count[cpu][cluster]++;
> > +
> > +       if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> > +               struct mb86s7x_cpu_gate cmd;
> > +
> > +               arch_spin_unlock(&mb86s7x_pm_lock);
> 
> Can't you use atomic operations here (i.e. atomic_inc_return) instead of a lock?

There is more than the count that has to be atomic.

> In any case, Nicolas should review the cluster PM code if he hasn't
> already. As Arnd pointed out, this patch should be split up into
> multiple patches. I'd suggest splitting into DT bindings, DTS files,
> base support, mailbox, MCPM, and PM domains.

Agreed.


Nicolas

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-13  6:28   ` Mollie Wu
@ 2014-07-15 17:05     ` Nicolas Pitre
  -1 siblings, 0 replies; 88+ messages in thread
From: Nicolas Pitre @ 2014-07-15 17:05 UTC (permalink / raw)
  To: Mollie Wu
  Cc: mark.rutland, devicetree, andy.green, Russell King - ARM Linux,
	Arnd Bergmann, Pawel Moll, Tetsuya Takinishi, patches,
	jaswinder.singh, robh+dt, Olof Johansson, linux-arm-kernel

On Sun, 13 Jul 2014, Mollie Wu wrote:

> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
> new file mode 100644
> index 0000000..d6e76ec
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/board.c
> @@ -0,0 +1,65 @@
> +/*
> + * Support for the Fujitsu's MB86S7x based devices.
> + *
> + * Copyright (C) 2014 Linaro, LTD
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/of.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/mach/map.h>
> +#include <asm/mach/arch.h>
> +
> +#include "iomap.h"
> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +	struct device_node *node;
> +
> +	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +	if (node && of_device_is_available(node)) {
> +		mcpm_smp_set_ops();
> +		return true;
> +	}
> +
> +	return false;
> +}

You should be able to call mcpm_smp_set_ops() directly from 
mb86s7x_mcpm_init() and dispense with this function entirely.

[...]

> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
> new file mode 100644
> index 0000000..86d223f
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/mcpm.c
> @@ -0,0 +1,293 @@
> +/*
> + * arch/arm/mach-mb86s7x/mcpm.c
> + *
> + * "Inspired" by tc_pm.c
> + * Copyright:	(C) 2013-2014  Fujitsu Semiconductor Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/reboot.h>
> +#include <linux/arm-cci.h>
> +#include <linux/spinlock.h>
> +#include <linux/irqchip/arm-gic.h>
> +#include <linux/platform_data/mb86s7x_mbox.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/cp15.h>
> +#include <asm/cputype.h>
> +#include <asm/system_misc.h>
> +
> +#include "iomap.h"
> +
> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
> +static int mb86s7x_pm_use_count[2][2];
> +
> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> +
> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> +{
> +	u8 val;
> +
> +	if (clr & ~AT_WFI_COLOR_MASK)
> +		return;
> +
> +	val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +	val &= ~AT_WFI_COLOR_MASK;
> +	val |= clr;
> +	writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +}
> +
> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +	struct completion got_rsp;
> +	int ret = 0;
> +
> +	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);

You should ensure cpu and cluster are within the allowed range here.

> +
> +	arch_spin_lock(&mb86s7x_pm_lock);

As mentioned in my previous email there has to be a local_irq_disable() 
here before locking.

> +
> +	mb86s7x_pm_use_count[cpu][cluster]++;
> +
> +	if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +		struct mb86s7x_cpu_gate cmd;
> +
> +		arch_spin_unlock(&mb86s7x_pm_lock);

Hmmm.... I was about to say that you cannot drop the lock here, however 
if the count is now 1, that means the target CPU is either down or 
already prepared to get there, and it cannot race with the code here. So 
far so good.

> +		cmd.payload_size = sizeof(cmd);
> +		cmd.cluster_class = 0;
> +		cmd.cluster_id = cluster;
> +		cmd.cpu_id = cpu;
> +		cmd.cpu_state = SCB_CPU_STATE_ON;
> +
> +		pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
> +			 __func__, __LINE__, cmd.cluster_class,
> +			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
> +
> +		init_completion(&got_rsp);
> +		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
> +		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
> +				      &cmd, sizeof(cmd), &got_rsp);
> +		if (ret < 0) {
> +			pr_err("%s:%d failed!\n", __func__, __LINE__);
> +			return ret;
> +		}
> +		if (ret)
> +			wait_for_completion(&got_rsp);
> +
> +		pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
> +			 __func__, __LINE__, cmd.cluster_class,
> +			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
> +
> +		if (cmd.cpu_state != SCB_CPU_STATE_ON)
> +			return -ENODEV;
> +
> +	} else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
> +		/*
> +		 * The only possible values are:
> +		 * 0 = CPU down
> +		 * 1 = CPU (still) up
> +		 * 2 = CPU requested to be up before it had a chance
> +		 *     to actually make itself down.
> +		 * Any other value is a bug.
> +		 */
> +		BUG();
> +	}
> +
> +	arch_spin_unlock(&mb86s7x_pm_lock);

If the count became 1, the lock was already unlocked above.

> +
> +	return 0;
> +}
> +
> +static void mb86s7x_pm_suspend(u64 ignored)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +	bool last_man = false, skip_wfi = false;
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> +	__mcpm_cpu_going_down(cpu, cluster);
> +
> +	arch_spin_lock(&mb86s7x_pm_lock);
> +	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
> +
> +	mb86s7x_pm_use_count[cpu][cluster]--;
> +
> +	if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
> +		if (!mb86s7x_pm_use_count[0][cluster] &&
> +		    !mb86s7x_pm_use_count[1][cluster])
> +			last_man = true;
> +		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
> +	} else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +		skip_wfi = true; /* Overtaken by a power up */
> +	} else {
> +		BUG();
> +	}
> +
> +	if (!skip_wfi)
> +		gic_cpu_if_down();
> +
> +	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
> +		arch_spin_unlock(&mb86s7x_pm_lock);
> +
> +		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
> +			/*
> +			 * On the Cortex-A15 we need to disable
> +			 * L2 prefetching before flushing the cache.
> +			 */
> +			asm volatile(
> +			"mcr p15, 1, %0, c15, c0, 3\n\t"
> +			"isb\n\t"
> +			"dsb"
> +			: : "r" (0x400));
> +		}
> +
> +		v7_exit_coherency_flush(all);
> +
> +		cci_disable_port_by_cpu(mpidr);
> +
> +		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
> +	} else {
> +		arch_spin_unlock(&mb86s7x_pm_lock);
> +		v7_exit_coherency_flush(louis);
> +	}
> +
> +	__mcpm_cpu_down(cpu, cluster);
> +
> +	/* Now we are prepared for power-down, do it: */
> +	if (!skip_wfi)
> +		wfi();
> +
> +	/* Not dead at this point?  Let our caller cope. */
> +}
> +
> +static void mb86s7x_pm_power_down(void)
> +{
> +	mb86s7x_pm_suspend(0);
> +}
> +
> +static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
> +{
> +	struct mb86s7x_cpu_gate cmd;
> +	struct completion got_rsp;
> +	int i, ret;
> +
> +	cmd.payload_size = sizeof(cmd);
> +	cmd.cluster_class = 0;
> +	cmd.cluster_id = cluster;
> +	cmd.cpu_id = cpu;
> +	cmd.cpu_state = SCB_CPU_STATE_ON;
> +
> +	for (i = 0; i < 50; i++) {
> +		init_completion(&got_rsp);
> +		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
> +				      &cmd, sizeof(cmd), &got_rsp);
> +		if (ret < 0) {
> +			pr_err("%s:%d failed to get CPU status\n",
> +			       __func__, __LINE__);
> +			return -ETIMEDOUT;

You should probably return the actual error from mhu_send_packet() here 
as this is probably not going to be a time-out error ?

> +		}
> +		if (ret)
> +			wait_for_completion(&got_rsp);

Maybe wait_for_completion_timeout() so execution doesn't get stuck if 
the answer never comes back.  That is valid for the other call sites as 
well.

> +		pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
> +			 __func__, __LINE__,
> +			 cmd.cluster_class, cmd.cluster_id,
> +			 cmd.cpu_id, cmd.cpu_state);
> +
> +		if (cmd.cpu_state == SCB_CPU_STATE_OFF)
> +			return 0;
> +
> +		msleep(20);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
> +	.power_up		= mb86s7x_pm_power_up,
> +	.power_down		= mb86s7x_pm_power_down,
> +	.wait_for_powerdown	= mb86s7x_wait_for_powerdown,
> +	.suspend		= mb86s7x_pm_suspend,
> +};

For proper behavior with cpuidle you'll need to provide a powered_up 
method as well.

> +
> +static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
> +{
> +	/* Reboot immediately */
> +	mb86s7x_reboot(50);
> +}
> +
> +static void mb86s7x_poweroff(void)
> +{
> +	/* Reboot never, remain dead */
> +	mb86s7x_reboot(~0);
> +}
> +
> +/*
> + * Enable cluster-level coherency, in preparation for turning on the MMU.
> + */
> +static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
> +{
> +	asm volatile ("\n"
> +"	cmp	r0, #1\n"
> +"	bxne	lr\n"
> +"	b	cci_enable_port_for_self");
> +}
> +
> +static int __init mb86s7x_mcpm_init(void)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +	struct mb86s7x_scb_version cmd;
> +	struct completion got_rsp;
> +	int ret;
> +
> +	arm_pm_restart = mb86s7x_restart;
> +	pm_power_off = mb86s7x_poweroff;
> +
> +	if (!cci_probed())
> +		return -ENODEV;
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
> +	mb86s7x_pm_use_count[cpu][cluster] = 1;
> +
> +	/* reset the wfi 'color' for primary cpu */
> +	mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
> +
> +	/* Set entry point for any CPU nowonwards */
> +	writel_relaxed(virt_to_phys(mcpm_entry_point),
> +		     MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
> +
> +	cmd.payload_size = sizeof(cmd);
> +	cmd.version = 0;
> +	cmd.config_version = 0;
> +	init_completion(&got_rsp);
> +	ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
> +			      &cmd, sizeof(cmd), &got_rsp);
> +	if (ret < 0)
> +		pr_err("%s:%d failed to get SCB version\n",
> +		       __func__, __LINE__);
> +	if (ret)
> +		wait_for_completion(&got_rsp);

There appears to be a recurring pattern here:

  - fill up mb86s7x_scb_version structure
  - init_completion()
  - mhu_send_packet()
  - complain if error
  - otherwise possibly wait_for_completion()

All this could be abstracted in a common function to reduce code 
duplication.

> +
> +	pr_info("MB86S7x MCPM initialized: SCB version 0x%x:0x%x\n",
> +		cmd.version, cmd.config_version);
> +
> +	ret = mcpm_platform_register(&mb86s7x_pm_power_ops);
> +	if (!ret)
> +		ret = mcpm_sync_init(mb86s7x_pm_power_up_setup);
> +
> +	return ret;
> +}
> +early_initcall(mb86s7x_mcpm_init);


Nicolas

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 17:05     ` Nicolas Pitre
  0 siblings, 0 replies; 88+ messages in thread
From: Nicolas Pitre @ 2014-07-15 17:05 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, 13 Jul 2014, Mollie Wu wrote:

> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
> new file mode 100644
> index 0000000..d6e76ec
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/board.c
> @@ -0,0 +1,65 @@
> +/*
> + * Support for the Fujitsu's MB86S7x based devices.
> + *
> + * Copyright (C) 2014 Linaro, LTD
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + *
> + */
> +
> +#include <linux/of.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/mach/map.h>
> +#include <asm/mach/arch.h>
> +
> +#include "iomap.h"
> +
> +bool __init mb86s7x_smp_init_ops(void)
> +{
> +	struct device_node *node;
> +
> +	node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> +	if (node && of_device_is_available(node)) {
> +		mcpm_smp_set_ops();
> +		return true;
> +	}
> +
> +	return false;
> +}

You should be able to call mcpm_smp_set_ops() directly from 
mb86s7x_mcpm_init() and dispense with this function entirely.

[...]

> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
> new file mode 100644
> index 0000000..86d223f
> --- /dev/null
> +++ b/arch/arm/mach-mb86s7x/mcpm.c
> @@ -0,0 +1,293 @@
> +/*
> + * arch/arm/mach-mb86s7x/mcpm.c
> + *
> + * "Inspired" by tc_pm.c
> + * Copyright:	(C) 2013-2014  Fujitsu Semiconductor Limited
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/io.h>
> +#include <linux/pm.h>
> +#include <linux/delay.h>
> +#include <linux/kernel.h>
> +#include <linux/reboot.h>
> +#include <linux/arm-cci.h>
> +#include <linux/spinlock.h>
> +#include <linux/irqchip/arm-gic.h>
> +#include <linux/platform_data/mb86s7x_mbox.h>
> +
> +#include <asm/mcpm.h>
> +#include <asm/cp15.h>
> +#include <asm/cputype.h>
> +#include <asm/system_misc.h>
> +
> +#include "iomap.h"
> +
> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
> +static int mb86s7x_pm_use_count[2][2];
> +
> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
> +
> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
> +{
> +	u8 val;
> +
> +	if (clr & ~AT_WFI_COLOR_MASK)
> +		return;
> +
> +	val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +	val &= ~AT_WFI_COLOR_MASK;
> +	val |= clr;
> +	writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
> +}
> +
> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
> +{
> +	struct completion got_rsp;
> +	int ret = 0;
> +
> +	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);

You should ensure cpu and cluster are within the allowed range here.

> +
> +	arch_spin_lock(&mb86s7x_pm_lock);

As mentioned in my previous email there has to be a local_irq_disable() 
here before locking.

> +
> +	mb86s7x_pm_use_count[cpu][cluster]++;
> +
> +	if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +		struct mb86s7x_cpu_gate cmd;
> +
> +		arch_spin_unlock(&mb86s7x_pm_lock);

Hmmm.... I was about to say that you cannot drop the lock here, however 
if the count is now 1, that means the target CPU is either down or 
already prepared to get there, and it cannot race with the code here. So 
far so good.

> +		cmd.payload_size = sizeof(cmd);
> +		cmd.cluster_class = 0;
> +		cmd.cluster_id = cluster;
> +		cmd.cpu_id = cpu;
> +		cmd.cpu_state = SCB_CPU_STATE_ON;
> +
> +		pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
> +			 __func__, __LINE__, cmd.cluster_class,
> +			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
> +
> +		init_completion(&got_rsp);
> +		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
> +		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
> +				      &cmd, sizeof(cmd), &got_rsp);
> +		if (ret < 0) {
> +			pr_err("%s:%d failed!\n", __func__, __LINE__);
> +			return ret;
> +		}
> +		if (ret)
> +			wait_for_completion(&got_rsp);
> +
> +		pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
> +			 __func__, __LINE__, cmd.cluster_class,
> +			 cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
> +
> +		if (cmd.cpu_state != SCB_CPU_STATE_ON)
> +			return -ENODEV;
> +
> +	} else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
> +		/*
> +		 * The only possible values are:
> +		 * 0 = CPU down
> +		 * 1 = CPU (still) up
> +		 * 2 = CPU requested to be up before it had a chance
> +		 *     to actually make itself down.
> +		 * Any other value is a bug.
> +		 */
> +		BUG();
> +	}
> +
> +	arch_spin_unlock(&mb86s7x_pm_lock);

If the count became 1, the lock was already unlocked above.

> +
> +	return 0;
> +}
> +
> +static void mb86s7x_pm_suspend(u64 ignored)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +	bool last_man = false, skip_wfi = false;
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
> +	__mcpm_cpu_going_down(cpu, cluster);
> +
> +	arch_spin_lock(&mb86s7x_pm_lock);
> +	BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
> +
> +	mb86s7x_pm_use_count[cpu][cluster]--;
> +
> +	if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
> +		if (!mb86s7x_pm_use_count[0][cluster] &&
> +		    !mb86s7x_pm_use_count[1][cluster])
> +			last_man = true;
> +		mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
> +	} else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
> +		skip_wfi = true; /* Overtaken by a power up */
> +	} else {
> +		BUG();
> +	}
> +
> +	if (!skip_wfi)
> +		gic_cpu_if_down();
> +
> +	if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
> +		arch_spin_unlock(&mb86s7x_pm_lock);
> +
> +		if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
> +			/*
> +			 * On the Cortex-A15 we need to disable
> +			 * L2 prefetching before flushing the cache.
> +			 */
> +			asm volatile(
> +			"mcr p15, 1, %0, c15, c0, 3\n\t"
> +			"isb\n\t"
> +			"dsb"
> +			: : "r" (0x400));
> +		}
> +
> +		v7_exit_coherency_flush(all);
> +
> +		cci_disable_port_by_cpu(mpidr);
> +
> +		__mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
> +	} else {
> +		arch_spin_unlock(&mb86s7x_pm_lock);
> +		v7_exit_coherency_flush(louis);
> +	}
> +
> +	__mcpm_cpu_down(cpu, cluster);
> +
> +	/* Now we are prepared for power-down, do it: */
> +	if (!skip_wfi)
> +		wfi();
> +
> +	/* Not dead at this point?  Let our caller cope. */
> +}
> +
> +static void mb86s7x_pm_power_down(void)
> +{
> +	mb86s7x_pm_suspend(0);
> +}
> +
> +static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
> +{
> +	struct mb86s7x_cpu_gate cmd;
> +	struct completion got_rsp;
> +	int i, ret;
> +
> +	cmd.payload_size = sizeof(cmd);
> +	cmd.cluster_class = 0;
> +	cmd.cluster_id = cluster;
> +	cmd.cpu_id = cpu;
> +	cmd.cpu_state = SCB_CPU_STATE_ON;
> +
> +	for (i = 0; i < 50; i++) {
> +		init_completion(&got_rsp);
> +		ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
> +				      &cmd, sizeof(cmd), &got_rsp);
> +		if (ret < 0) {
> +			pr_err("%s:%d failed to get CPU status\n",
> +			       __func__, __LINE__);
> +			return -ETIMEDOUT;

You should probably return the actual error from mhu_send_packet() here 
as this is probably not going to be a time-out error ?

> +		}
> +		if (ret)
> +			wait_for_completion(&got_rsp);

Maybe wait_for_completion_timeout() so execution doesn't get stuck if 
the answer never comes back.  That is valid for the other call sites as 
well.

> +		pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
> +			 __func__, __LINE__,
> +			 cmd.cluster_class, cmd.cluster_id,
> +			 cmd.cpu_id, cmd.cpu_state);
> +
> +		if (cmd.cpu_state == SCB_CPU_STATE_OFF)
> +			return 0;
> +
> +		msleep(20);
> +	}
> +
> +	return -ETIMEDOUT;
> +}
> +
> +static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
> +	.power_up		= mb86s7x_pm_power_up,
> +	.power_down		= mb86s7x_pm_power_down,
> +	.wait_for_powerdown	= mb86s7x_wait_for_powerdown,
> +	.suspend		= mb86s7x_pm_suspend,
> +};

For proper behavior with cpuidle you'll need to provide a powered_up 
method as well.

> +
> +static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
> +{
> +	/* Reboot immediately */
> +	mb86s7x_reboot(50);
> +}
> +
> +static void mb86s7x_poweroff(void)
> +{
> +	/* Reboot never, remain dead */
> +	mb86s7x_reboot(~0);
> +}
> +
> +/*
> + * Enable cluster-level coherency, in preparation for turning on the MMU.
> + */
> +static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
> +{
> +	asm volatile ("\n"
> +"	cmp	r0, #1\n"
> +"	bxne	lr\n"
> +"	b	cci_enable_port_for_self");
> +}
> +
> +static int __init mb86s7x_mcpm_init(void)
> +{
> +	unsigned int mpidr, cpu, cluster;
> +	struct mb86s7x_scb_version cmd;
> +	struct completion got_rsp;
> +	int ret;
> +
> +	arm_pm_restart = mb86s7x_restart;
> +	pm_power_off = mb86s7x_poweroff;
> +
> +	if (!cci_probed())
> +		return -ENODEV;
> +
> +	mpidr = read_cpuid_mpidr();
> +	cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
> +	cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
> +
> +	pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
> +	mb86s7x_pm_use_count[cpu][cluster] = 1;
> +
> +	/* reset the wfi 'color' for primary cpu */
> +	mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
> +
> +	/* Set entry point for any CPU nowonwards */
> +	writel_relaxed(virt_to_phys(mcpm_entry_point),
> +		     MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
> +
> +	cmd.payload_size = sizeof(cmd);
> +	cmd.version = 0;
> +	cmd.config_version = 0;
> +	init_completion(&got_rsp);
> +	ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
> +			      &cmd, sizeof(cmd), &got_rsp);
> +	if (ret < 0)
> +		pr_err("%s:%d failed to get SCB version\n",
> +		       __func__, __LINE__);
> +	if (ret)
> +		wait_for_completion(&got_rsp);

There appears to be a recurring pattern here:

  - fill up mb86s7x_scb_version structure
  - init_completion()
  - mhu_send_packet()
  - complain if error
  - otherwise possibly wait_for_completion()

All this could be abstracted in a common function to reduce code 
duplication.

> +
> +	pr_info("MB86S7x MCPM initialized: SCB version 0x%x:0x%x\n",
> +		cmd.version, cmd.config_version);
> +
> +	ret = mcpm_platform_register(&mb86s7x_pm_power_ops);
> +	if (!ret)
> +		ret = mcpm_sync_init(mb86s7x_pm_power_up_setup);
> +
> +	return ret;
> +}
> +early_initcall(mb86s7x_mcpm_init);


Nicolas

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-14 13:33       ` Arnd Bergmann
@ 2014-07-15 17:37         ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 17:37 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On 14 July 2014 19:03, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> On Sunday 13 July 2014 14:28:31 Mollie Wu wrote:
>> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
>> And the remote master firmware (called SCB) running on CM3. Linux asks
>> for things to be done over Mailbox API, to SCB which controls most of
>> the important things. variations S70 & S73 are supported.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> Cc: Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org>
>> Cc: Olof <olof-nZhT3qVonbNeoWH0uzbU5w@public.gmane.org>
>> Cc: Russell King <linux-lFZ/pmaqli7XmaaqVzeoHQ@public.gmane.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
>> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>>  arch/arm/Kconfig                                   |   2 +
>>  arch/arm/Makefile                                  |   1 +
>>  arch/arm/boot/dts/Makefile                         |   1 +
>>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
>
> Better split out the dts additions into a separate patch, to make the actual code
> easier to review.
>
OK.

>> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>> new file mode 100644
>> index 0000000..44abfe8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>> @@ -0,0 +1,22 @@
>> +* Fujitsu MB86S7x Power Domains
>> +
>> +Remote f/w on MB86S7x can enable/disable power to various IPs.
>> +
>> +Required Properties:
>> +- compatible: Should be "fujitsu,mb86s7x-pd"
>> +- index: Index of the power gate control for the block
>
> Please always use actual part names in a compatible string, not wildcards
> with 'x' in them. If two models are mutually compatible, use the string
> for the older product (possibly the smaller number if they
> are the same age). In C code, wildcards are fine/.
>
OK and we'll change other such occurrences too.

>> +Example:
>> +
>> +     pd_cpu: genpd@3 {
>> +             compatible = "fujitsu,mb86s7x-pd";
>> +             index = <3>;
>> +     };
>> +
>> +Example of the node using power domain:
>> +
>> +     node {
>> +             /* ... */
>> +             fujitsu,power-domain = <&pd_cpu>;
>> +             /* ... */
>> +     };
>
> I believe there has been a submission for a generic power domain binding
> now. We really shouldn't use vendor specific power domain bindings
> any more.
>
IIUC the last submission v4 of that patchset was in May
http://comments.gmane.org/gmane.linux.kernel.samsung-soc/31029
Do we have to wait for that to get upstream? Or maybe we could adopt
that binding now so it becomes trivial to convert to that when that
gets upstream?


>> --- /dev/null
>> +++ b/arch/arm/configs/fujitsu_defconfig
>
> Do you need your own defconfig, or can you just add this to the multi_v7_defconfig
> file instead?
>
> In either case, please also add an entry to multi_v7_defconfig and enable
> all the drivers you need there.
>
OK

>> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
>> new file mode 100644
>> index 0000000..44f5b0c
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/Kconfig
>> @@ -0,0 +1,18 @@
>> +config ARCH_MB86S7X
>> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
>> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
>
> Why the LPAE dependency? Is none of the RAM reachable by regular
> kernels?
>
Some devices like PCI, LLI need it and the S70-evb has half of its ram
above 4GB.
Maybe ARM_LPAE should be selected by inclusion of support for those?


>> +
>> +bool __init mb86s7x_smp_init_ops(void)
>> +{
>> +     struct device_node *node;
>> +
>> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> +     if (node && of_device_is_available(node)) {
>> +             mcpm_smp_set_ops();
>> +             return true;
>> +     }
>> +
>> +     return false;
>> +}
>
> Can you use the CPU_METHOD_OF_DECLARE() macro to set your
> SMP ops instead?
>
CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
smp_ops which are statically defined. We have to call
mcpm_smp_set_ops() which does the real job.


>> +#define IOMAP_DEV(name) { \
>> +             .virtual = (unsigned long) MB86S7X_##name##_VIRT, \
>> +             .pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
>> +             .length = MB86S7X_##name##_SIZE, \
>> +             .type = MT_DEVICE, \
>> +     }
>> +
>> +static struct map_desc mb86s7x_io_desc[] = {
>> +     IOMAP_DEV(MHU),
>> +     IOMAP_DEV(ISRAM),
>> +};
>
> What do you need these entries for? You should be able to just ioremap
> the registers where needed.
>
Yes should be doable, unless I am overlooking some issue. Will try doing that.


>> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
>> new file mode 100644
>> index 0000000..2b8db1d
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/iomap.h
>> @@ -0,0 +1,34 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/iomap.h
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __MB86S7X_IOMAP_H
>> +#define __MB86S7X_IOMAP_H
>> +
>> +#include <linux/sizes.h>
>> +
>> +#define SEC_RSTADDR_OFF              0x3fc
>> +
>> +#define MB86S7X_MHU_PHYS     0x2b1f0000
>> +#define MB86S7X_MHU_SIZE     SZ_4K
>> +
>> +/* Internal SRAM */
>> +#define MB86S7X_ISRAM_PHYS   0x2e000000
>> +#define MB86S7X_ISRAM_SIZE   SZ_16K
>> +#define MB86S7X_TRAMPOLINE_PHYS      (MB86S7X_ISRAM_PHYS + 0x3c00)
>> +
>> +#define MB86S7X_ISRAM_VIRT   IOMEM(0xfe000000)
>> +#define MB86S7X_MHU_VIRT     (MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
>> +
>> +/* Non-Secure High-Priority Channel is used */
>> +#define MB86S7X_SHM_FROM_SCB_VIRT    (MB86S7X_ISRAM_VIRT + 0x3800)
>> +#define MB86S7X_TRAMPOLINE_VIRT              (MB86S7X_ISRAM_VIRT + 0x3c00)
>> +
>> +#endif /* __MB86S7X_IOMAP_H */
>
> None of the hardcoded register locations should really be needed, please
> move them into the device tree.
>
OK

> For the SRAM, we now have a binding that is already shared between multiple
> platforms, see rockchips for an example.
>
OK, will look at that.

>> diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
>> new file mode 100644
>> index 0000000..fd5f034
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/scb_mhu.c
>> @@ -0,0 +1,447 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>
> This should probably go into drivers/soc/
>
OK

>> +#define INTR_STAT_OFS        0x0
>> +#define INTR_SET_OFS 0x8
>> +#define INTR_CLR_OFS 0x10
>> +
>> +static int do_xfer(void);
>> +static void try_xfer(struct work_struct *ignored);
>
> Please remove the forward declarations by reordering the code.
>
OK

>> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
>> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
>> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
>> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
>> +
>> +static LIST_HEAD(free_xfers);
>> +static LIST_HEAD(pending_xfers);
>> +static DEFINE_SPINLOCK(fsm_lock);
>> +static struct mbox_client mhu_cl;
>> +static struct mbox_chan *mhu_chan;
>> +static DECLARE_WORK(scb_work, try_xfer);
>> +static mhu_handler_t handler[MHU_NUM_CMDS];
>> +
>> +static enum {
>> +     MHU_PARK = 0,
>> +     MHU_WRR, /* Waiting to get Remote's Reply */
>> +     MHU_WRL, /* Waiting to send Reply */
>> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
>> +     MHU_INVLD,
>> +} fsm_state;
>
> Ideally, these should all be part of a per-device data structure
> referenced from pdev_get_drvdata().
>
I saw that coming :)
We need this driver as early as when timers are populated and then for
secondary CPU power control ... when we don't have any platform_device
to hook the data on. And I think it is ok because this is the 'server'
driver for the platform which by definition won't have another
instance.


>> +static void got_data(u32 code)
>> +{
>> +     struct completion *c = NULL;
>> +     mhu_handler_t hndlr = NULL;
>> +     unsigned long flags;
>> +     int ev;
>> +
>> +     if (code & RESP_BIT)
>> +             ev = EV_RR;
>> +     else
>> +             ev = EV_RC;
>> +
>> +     spin_lock_irqsave(&fsm_lock, flags);
>> +
>> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> +             return;
>> +     }
>> +     fsm_state = mhu_fsm[fsm_state][ev];
>> +
>> +     if (code & RESP_BIT) {
>> +             c = ax->c;
>> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> +             list_move(&ax->node, &free_xfers);
>> +             ax = NULL;
>> +     } else {
>> +             /* Find and dispatch relevant registered handler */
>> +             if (code < MHU_NUM_CMDS)
>> +                     hndlr = handler[code];
>> +             if (!hndlr)
>> +                     pr_err("No handler for CMD_%u\n", code);
>> +     }
>> +
>> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> +
>> +     if (hndlr)
>> +             hndlr(code, cmd_from_scb);
>> +     if (c)
>> +             complete(c);
>> +}
>> +
>> +static void mhu_recv(struct mbox_client *cl, void *data)
>> +{
>> +     got_data((u32)data);
>> +     schedule_work(&scb_work);
>> +}
>
> Why the cast between integer and pointer?
>
The common mailbox framework passes around pointers to data packets.
Its completely between controller and client drivers to decide the
format of the packet. In my case the packet is a simple u32 value.

>> +                     do {
>> +retry:
>> +                             /* Wait until we get reply */
>> +                             count = 0x1000000;
>> +                             do {
>> +                                     cpu_relax();
>> +                                     val = readl_relaxed(
>> +                                             rx_reg + INTR_STAT_OFS);
>> +                             } while (--count && !val);
>> +
>> +                             if (!val) {
>> +                                     pr_err("%s:%d SCB didn't reply\n",
>> +                                            __func__, __LINE__);
>> +                                     goto retry;
>
> It seems like this loop is unbounded and will just print an error every
> 16M loops but never give up or re-enable interrupts if called with
> interrupts disabled.
>
> It should probably be changed to either enforce being called with interrupts
> enabled so you can call msleep() inbetween, or you should add error-handling
> in case the remote side does not reply.
>
We can do that for completeness but otherwise my platform is as good
as dead if the remote doesn't reply for some reason. And there is no
fool-proof way to recover the state-machine from a failed
communication.

>> +int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
>> +{
>> +     unsigned long flags;
>> +     int ret = -EINVAL;
>> +
>> +     spin_lock_irqsave(&fsm_lock, flags);
>> +     if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
>> +             ret = 0;
>> +             handler[cmd] = hndlr;
>> +     }
>> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> +
>> +     if (!mhu_chan) {
>> +             struct mbox_chan *_ch;
>> +
>> +             _ch = mbox_request_channel(&mhu_cl);
>> +             if (!IS_ERR(_ch))
>> +                     mhu_chan = _ch;
>> +     }
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(mhu_hndlr_set);
>
> This is a rather generic name for an exported symbol, can you prefix
> it with the platform name or something appropriate?
>
OK

>> +static const struct of_device_id scb_dt_ids[] = {
>> +     { .compatible = "fujitsu,scb" },
>> +     { /* sentinel */ }
>> +};
>
> I don't see a binding for "fujitsu,scb", and it seems like a far too generic
> string. Fujitsu is a large company, I wouldn't be surprised if some other
> product besides MB87S7X also came with something called an "scb".
>
OK

>> diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
>> new file mode 100644
>> index 0000000..4f4287e
>> --- /dev/null
>> +++ b/include/linux/platform_data/mb86s7x_mbox.h
>> @@ -0,0 +1,249 @@
>> +/*
>> + * include/linux/platform_data/mb86s7x_mbox.h
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>
> None of the contents in here are actually platform data, and it seems
> they should not be shared across drivers. Most of the data structures
> in this file look like they should only be used by one mailbox client
> and get moved into the respective driver.
>
OK will do that. My idea was to keep all info about the mailbox
protocol at one place.

>> +struct mb86s7x_peri_clk {
>> +     u32 payload_size;
>> +     u32 cntrlr;
>> +     u32 domain;
>> +     u32 port;
>> +     u32 en;
>> +     u64 freqency;
>> +} __packed;
>
> Just mark the last member by itself __packed. I assume you didn't
> actually mean to change the alignment of the data structure to one
> byte, but just want to say that the last one is misaligned.
>
This and others, are data packets that are passed between local and
remote via SharedMemory. __packed is only meant to specify that these
data structures have no holes in them.


>> +int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
>> +void mb86s7x_reboot(u32 delay);
>> +
>> +/* This function must not sleep */
>> +typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
>> +
>> +int mhu_hndlr_set(u32 cmd, mhu_handler_t);
>> +void mhu_hndlr_clr(u32 cmd, mhu_handler_t);
>
> Doesn't belong here.
>
OK

Thanks for review. We'll address the comments in next revision soon.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 17:37         ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 17:37 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 July 2014 19:03, Arnd Bergmann <arnd@arndb.de> wrote:
> On Sunday 13 July 2014 14:28:31 Mollie Wu wrote:
>> The MB86S7X is a bigLITTLE configuration of 2xCA7 & 2xCA15 under Linux.
>> And the remote master firmware (called SCB) running on CM3. Linux asks
>> for things to be done over Mailbox API, to SCB which controls most of
>> the important things. variations S70 & S73 are supported.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
>> Cc: Arnd Bergmann <arnd@arndb.de>
>> Cc: Olof <olof@lixom.net>
>> Cc: Russell King <linux@arm.linux.org.uk>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
>> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
>> ---
>>  .../bindings/arm/fujistu/power_domain.txt          |  22 +
>>  arch/arm/Kconfig                                   |   2 +
>>  arch/arm/Makefile                                  |   1 +
>>  arch/arm/boot/dts/Makefile                         |   1 +
>>  arch/arm/boot/dts/mb86s70.dtsi                     | 635 ++++++++++++++
>>  arch/arm/boot/dts/mb86s70eb.dts                    |  38 +
>>  arch/arm/boot/dts/mb86s73.dtsi                     | 910 +++++++++++++++++++++
>>  arch/arm/boot/dts/mb86s73eb.dts                    |  73 ++
>>  arch/arm/configs/fujitsu_defconfig                 | 156 ++++
>>  arch/arm/mach-mb86s7x/Kconfig                      |  18 +
>>  arch/arm/mach-mb86s7x/Makefile                     |   2 +
>>  arch/arm/mach-mb86s7x/board.c                      |  65 ++
>>  arch/arm/mach-mb86s7x/iomap.h                      |  34 +
>>  arch/arm/mach-mb86s7x/mcpm.c                       | 293 +++++++
>>  arch/arm/mach-mb86s7x/pm_domains.c                 | 237 ++++++
>>  arch/arm/mach-mb86s7x/scb_mhu.c                    | 447 ++++++++++
>>  include/linux/platform_data/mb86s7x_mbox.h         | 249 ++++++
>
> Better split out the dts additions into a separate patch, to make the actual code
> easier to review.
>
OK.

>> diff --git a/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>> new file mode 100644
>> index 0000000..44abfe8
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/arm/fujistu/power_domain.txt
>> @@ -0,0 +1,22 @@
>> +* Fujitsu MB86S7x Power Domains
>> +
>> +Remote f/w on MB86S7x can enable/disable power to various IPs.
>> +
>> +Required Properties:
>> +- compatible: Should be "fujitsu,mb86s7x-pd"
>> +- index: Index of the power gate control for the block
>
> Please always use actual part names in a compatible string, not wildcards
> with 'x' in them. If two models are mutually compatible, use the string
> for the older product (possibly the smaller number if they
> are the same age). In C code, wildcards are fine/.
>
OK and we'll change other such occurrences too.

>> +Example:
>> +
>> +     pd_cpu: genpd at 3 {
>> +             compatible = "fujitsu,mb86s7x-pd";
>> +             index = <3>;
>> +     };
>> +
>> +Example of the node using power domain:
>> +
>> +     node {
>> +             /* ... */
>> +             fujitsu,power-domain = <&pd_cpu>;
>> +             /* ... */
>> +     };
>
> I believe there has been a submission for a generic power domain binding
> now. We really shouldn't use vendor specific power domain bindings
> any more.
>
IIUC the last submission v4 of that patchset was in May
http://comments.gmane.org/gmane.linux.kernel.samsung-soc/31029
Do we have to wait for that to get upstream? Or maybe we could adopt
that binding now so it becomes trivial to convert to that when that
gets upstream?


>> --- /dev/null
>> +++ b/arch/arm/configs/fujitsu_defconfig
>
> Do you need your own defconfig, or can you just add this to the multi_v7_defconfig
> file instead?
>
> In either case, please also add an entry to multi_v7_defconfig and enable
> all the drivers you need there.
>
OK

>> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
>> new file mode 100644
>> index 0000000..44f5b0c
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/Kconfig
>> @@ -0,0 +1,18 @@
>> +config ARCH_MB86S7X
>> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
>> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
>
> Why the LPAE dependency? Is none of the RAM reachable by regular
> kernels?
>
Some devices like PCI, LLI need it and the S70-evb has half of its ram
above 4GB.
Maybe ARM_LPAE should be selected by inclusion of support for those?


>> +
>> +bool __init mb86s7x_smp_init_ops(void)
>> +{
>> +     struct device_node *node;
>> +
>> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> +     if (node && of_device_is_available(node)) {
>> +             mcpm_smp_set_ops();
>> +             return true;
>> +     }
>> +
>> +     return false;
>> +}
>
> Can you use the CPU_METHOD_OF_DECLARE() macro to set your
> SMP ops instead?
>
CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
smp_ops which are statically defined. We have to call
mcpm_smp_set_ops() which does the real job.


>> +#define IOMAP_DEV(name) { \
>> +             .virtual = (unsigned long) MB86S7X_##name##_VIRT, \
>> +             .pfn = __phys_to_pfn(MB86S7X_##name##_PHYS), \
>> +             .length = MB86S7X_##name##_SIZE, \
>> +             .type = MT_DEVICE, \
>> +     }
>> +
>> +static struct map_desc mb86s7x_io_desc[] = {
>> +     IOMAP_DEV(MHU),
>> +     IOMAP_DEV(ISRAM),
>> +};
>
> What do you need these entries for? You should be able to just ioremap
> the registers where needed.
>
Yes should be doable, unless I am overlooking some issue. Will try doing that.


>> diff --git a/arch/arm/mach-mb86s7x/iomap.h b/arch/arm/mach-mb86s7x/iomap.h
>> new file mode 100644
>> index 0000000..2b8db1d
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/iomap.h
>> @@ -0,0 +1,34 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/iomap.h
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __MB86S7X_IOMAP_H
>> +#define __MB86S7X_IOMAP_H
>> +
>> +#include <linux/sizes.h>
>> +
>> +#define SEC_RSTADDR_OFF              0x3fc
>> +
>> +#define MB86S7X_MHU_PHYS     0x2b1f0000
>> +#define MB86S7X_MHU_SIZE     SZ_4K
>> +
>> +/* Internal SRAM */
>> +#define MB86S7X_ISRAM_PHYS   0x2e000000
>> +#define MB86S7X_ISRAM_SIZE   SZ_16K
>> +#define MB86S7X_TRAMPOLINE_PHYS      (MB86S7X_ISRAM_PHYS + 0x3c00)
>> +
>> +#define MB86S7X_ISRAM_VIRT   IOMEM(0xfe000000)
>> +#define MB86S7X_MHU_VIRT     (MB86S7X_ISRAM_VIRT + MB86S7X_ISRAM_SIZE)
>> +
>> +/* Non-Secure High-Priority Channel is used */
>> +#define MB86S7X_SHM_FROM_SCB_VIRT    (MB86S7X_ISRAM_VIRT + 0x3800)
>> +#define MB86S7X_TRAMPOLINE_VIRT              (MB86S7X_ISRAM_VIRT + 0x3c00)
>> +
>> +#endif /* __MB86S7X_IOMAP_H */
>
> None of the hardcoded register locations should really be needed, please
> move them into the device tree.
>
OK

> For the SRAM, we now have a binding that is already shared between multiple
> platforms, see rockchips for an example.
>
OK, will look at that.

>> diff --git a/arch/arm/mach-mb86s7x/scb_mhu.c b/arch/arm/mach-mb86s7x/scb_mhu.c
>> new file mode 100644
>> index 0000000..fd5f034
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/scb_mhu.c
>> @@ -0,0 +1,447 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/scb_mhu.c Shim 'server' for Mailbox clients
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>
> This should probably go into drivers/soc/
>
OK

>> +#define INTR_STAT_OFS        0x0
>> +#define INTR_SET_OFS 0x8
>> +#define INTR_CLR_OFS 0x10
>> +
>> +static int do_xfer(void);
>> +static void try_xfer(struct work_struct *ignored);
>
> Please remove the forward declarations by reordering the code.
>
OK

>> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
>> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
>> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
>> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
>> +
>> +static LIST_HEAD(free_xfers);
>> +static LIST_HEAD(pending_xfers);
>> +static DEFINE_SPINLOCK(fsm_lock);
>> +static struct mbox_client mhu_cl;
>> +static struct mbox_chan *mhu_chan;
>> +static DECLARE_WORK(scb_work, try_xfer);
>> +static mhu_handler_t handler[MHU_NUM_CMDS];
>> +
>> +static enum {
>> +     MHU_PARK = 0,
>> +     MHU_WRR, /* Waiting to get Remote's Reply */
>> +     MHU_WRL, /* Waiting to send Reply */
>> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
>> +     MHU_INVLD,
>> +} fsm_state;
>
> Ideally, these should all be part of a per-device data structure
> referenced from pdev_get_drvdata().
>
I saw that coming :)
We need this driver as early as when timers are populated and then for
secondary CPU power control ... when we don't have any platform_device
to hook the data on. And I think it is ok because this is the 'server'
driver for the platform which by definition won't have another
instance.


>> +static void got_data(u32 code)
>> +{
>> +     struct completion *c = NULL;
>> +     mhu_handler_t hndlr = NULL;
>> +     unsigned long flags;
>> +     int ev;
>> +
>> +     if (code & RESP_BIT)
>> +             ev = EV_RR;
>> +     else
>> +             ev = EV_RC;
>> +
>> +     spin_lock_irqsave(&fsm_lock, flags);
>> +
>> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> +             return;
>> +     }
>> +     fsm_state = mhu_fsm[fsm_state][ev];
>> +
>> +     if (code & RESP_BIT) {
>> +             c = ax->c;
>> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> +             list_move(&ax->node, &free_xfers);
>> +             ax = NULL;
>> +     } else {
>> +             /* Find and dispatch relevant registered handler */
>> +             if (code < MHU_NUM_CMDS)
>> +                     hndlr = handler[code];
>> +             if (!hndlr)
>> +                     pr_err("No handler for CMD_%u\n", code);
>> +     }
>> +
>> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> +
>> +     if (hndlr)
>> +             hndlr(code, cmd_from_scb);
>> +     if (c)
>> +             complete(c);
>> +}
>> +
>> +static void mhu_recv(struct mbox_client *cl, void *data)
>> +{
>> +     got_data((u32)data);
>> +     schedule_work(&scb_work);
>> +}
>
> Why the cast between integer and pointer?
>
The common mailbox framework passes around pointers to data packets.
Its completely between controller and client drivers to decide the
format of the packet. In my case the packet is a simple u32 value.

>> +                     do {
>> +retry:
>> +                             /* Wait until we get reply */
>> +                             count = 0x1000000;
>> +                             do {
>> +                                     cpu_relax();
>> +                                     val = readl_relaxed(
>> +                                             rx_reg + INTR_STAT_OFS);
>> +                             } while (--count && !val);
>> +
>> +                             if (!val) {
>> +                                     pr_err("%s:%d SCB didn't reply\n",
>> +                                            __func__, __LINE__);
>> +                                     goto retry;
>
> It seems like this loop is unbounded and will just print an error every
> 16M loops but never give up or re-enable interrupts if called with
> interrupts disabled.
>
> It should probably be changed to either enforce being called with interrupts
> enabled so you can call msleep() inbetween, or you should add error-handling
> in case the remote side does not reply.
>
We can do that for completeness but otherwise my platform is as good
as dead if the remote doesn't reply for some reason. And there is no
fool-proof way to recover the state-machine from a failed
communication.

>> +int mhu_hndlr_set(u32 cmd, mhu_handler_t hndlr)
>> +{
>> +     unsigned long flags;
>> +     int ret = -EINVAL;
>> +
>> +     spin_lock_irqsave(&fsm_lock, flags);
>> +     if (cmd < MHU_NUM_CMDS && !handler[cmd]) {
>> +             ret = 0;
>> +             handler[cmd] = hndlr;
>> +     }
>> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> +
>> +     if (!mhu_chan) {
>> +             struct mbox_chan *_ch;
>> +
>> +             _ch = mbox_request_channel(&mhu_cl);
>> +             if (!IS_ERR(_ch))
>> +                     mhu_chan = _ch;
>> +     }
>> +
>> +     return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(mhu_hndlr_set);
>
> This is a rather generic name for an exported symbol, can you prefix
> it with the platform name or something appropriate?
>
OK

>> +static const struct of_device_id scb_dt_ids[] = {
>> +     { .compatible = "fujitsu,scb" },
>> +     { /* sentinel */ }
>> +};
>
> I don't see a binding for "fujitsu,scb", and it seems like a far too generic
> string. Fujitsu is a large company, I wouldn't be surprised if some other
> product besides MB87S7X also came with something called an "scb".
>
OK

>> diff --git a/include/linux/platform_data/mb86s7x_mbox.h b/include/linux/platform_data/mb86s7x_mbox.h
>> new file mode 100644
>> index 0000000..4f4287e
>> --- /dev/null
>> +++ b/include/linux/platform_data/mb86s7x_mbox.h
>> @@ -0,0 +1,249 @@
>> +/*
>> + * include/linux/platform_data/mb86s7x_mbox.h
>> + *
>> + * Created by: Jassi Brar <jassisinghbrar@gmail.com>
>> + * Copyright:        (C) 2013-2014 Linaro Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>
> None of the contents in here are actually platform data, and it seems
> they should not be shared across drivers. Most of the data structures
> in this file look like they should only be used by one mailbox client
> and get moved into the respective driver.
>
OK will do that. My idea was to keep all info about the mailbox
protocol at one place.

>> +struct mb86s7x_peri_clk {
>> +     u32 payload_size;
>> +     u32 cntrlr;
>> +     u32 domain;
>> +     u32 port;
>> +     u32 en;
>> +     u64 freqency;
>> +} __packed;
>
> Just mark the last member by itself __packed. I assume you didn't
> actually mean to change the alignment of the data structure to one
> byte, but just want to say that the last one is misaligned.
>
This and others, are data packets that are passed between local and
remote via SharedMemory. __packed is only meant to specify that these
data structures have no holes in them.


>> +int mhu_send_packet(int cmd, void *buf, int len, struct completion *c);
>> +void mb86s7x_reboot(u32 delay);
>> +
>> +/* This function must not sleep */
>> +typedef void (*mhu_handler_t)(u32 cmd, u8 rcbuf[]);
>> +
>> +int mhu_hndlr_set(u32 cmd, mhu_handler_t);
>> +void mhu_hndlr_clr(u32 cmd, mhu_handler_t);
>
> Doesn't belong here.
>
OK

Thanks for review. We'll address the comments in next revision soon.

Thanks
Jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 15:11       ` Rob Herring
@ 2014-07-15 18:03           ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 18:03 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mollie Wu, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Arnd Bergmann, Olof Johansson, Russell King - ARM Linux,
	Mark Rutland, Rob Herring, Pawel Moll, Linaro Patches,
	Tetsuya Takinishi, Nicolas Pitre

On 15 July 2014 20:41, Rob Herring <robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:

...

>> +               interrupts = <0 36 4>, /* LP Non-Sec */
>> +                            <0 35 4>, /* HP Non-Sec */
>> +                            <0 37 4>; /* Secure */
>> +       };
>> +
>> +       mhu_client: scb@0 {
>> +               compatible = "fujitsu,scb";
>> +               mbox = <&mhu 1>;
>
> Is the mailbox binding finalized?
>
I have sent a patchset for common mailbox and its bindings. You are on
CC list. Arnd seems OK with it.

....

Will address rest all of your comments.

>
> That's all I've got time and energy for reviewing for now.
>
Thanks a lot!
-Jassi
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 18:03           ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 18:03 UTC (permalink / raw)
  To: linux-arm-kernel

On 15 July 2014 20:41, Rob Herring <robherring2@gmail.com> wrote:

...

>> +               interrupts = <0 36 4>, /* LP Non-Sec */
>> +                            <0 35 4>, /* HP Non-Sec */
>> +                            <0 37 4>; /* Secure */
>> +       };
>> +
>> +       mhu_client: scb at 0 {
>> +               compatible = "fujitsu,scb";
>> +               mbox = <&mhu 1>;
>
> Is the mailbox binding finalized?
>
I have sent a patchset for common mailbox and its bindings. You are on
CC list. Arnd seems OK with it.

....

Will address rest all of your comments.

>
> That's all I've got time and energy for reviewing for now.
>
Thanks a lot!
-Jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 17:05     ` Nicolas Pitre
@ 2014-07-15 18:16         ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 18:16 UTC (permalink / raw)
  To: Nicolas Pitre
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mark Rutland,
	Tetsuya Takinishi, Andy Green, Russell King - ARM Linux,
	Pawel Moll, Arnd Bergmann, Patch Tracking, Rob Herring,
	Olof Johansson

On 15 July 2014 22:35, Nicolas Pitre <nicolas.pitre-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On Sun, 13 Jul 2014, Mollie Wu wrote:
>
>> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
>> new file mode 100644
>> index 0000000..d6e76ec
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/board.c
>> @@ -0,0 +1,65 @@
>> +/*
>> + * Support for the Fujitsu's MB86S7x based devices.
>> + *
>> + * Copyright (C) 2014 Linaro, LTD
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/of.h>
>> +
>> +#include <asm/mcpm.h>
>> +#include <asm/mach/map.h>
>> +#include <asm/mach/arch.h>
>> +
>> +#include "iomap.h"
>> +
>> +bool __init mb86s7x_smp_init_ops(void)
>> +{
>> +     struct device_node *node;
>> +
>> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> +     if (node && of_device_is_available(node)) {
>> +             mcpm_smp_set_ops();
>> +             return true;
>> +     }
>> +
>> +     return false;
>> +}
>
> You should be able to call mcpm_smp_set_ops() directly from
> mb86s7x_mcpm_init() and dispense with this function entirely.
>
Yup, thanks.

> [...]
>
>> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
>> new file mode 100644
>> index 0000000..86d223f
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/mcpm.c
>> @@ -0,0 +1,293 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/mcpm.c
>> + *
>> + * "Inspired" by tc_pm.c
>> + * Copyright:        (C) 2013-2014  Fujitsu Semiconductor Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/pm.h>
>> +#include <linux/delay.h>
>> +#include <linux/kernel.h>
>> +#include <linux/reboot.h>
>> +#include <linux/arm-cci.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/irqchip/arm-gic.h>
>> +#include <linux/platform_data/mb86s7x_mbox.h>
>> +
>> +#include <asm/mcpm.h>
>> +#include <asm/cp15.h>
>> +#include <asm/cputype.h>
>> +#include <asm/system_misc.h>
>> +
>> +#include "iomap.h"
>> +
>> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
>> +static int mb86s7x_pm_use_count[2][2];
>> +
>> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
>> +
>> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
>> +{
>> +     u8 val;
>> +
>> +     if (clr & ~AT_WFI_COLOR_MASK)
>> +             return;
>> +
>> +     val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
>> +     val &= ~AT_WFI_COLOR_MASK;
>> +     val |= clr;
>> +     writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
>> +}
>> +
>> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
>> +{
>> +     struct completion got_rsp;
>> +     int ret = 0;
>> +
>> +     pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
>
> You should ensure cpu and cluster are within the allowed range here.
>
OK, I didn't realize mcpm core could call with invalid cpu/cluster ids.


>> +
>> +     arch_spin_lock(&mb86s7x_pm_lock);
>
> As mentioned in my previous email there has to be a local_irq_disable()
> here before locking.
>
OK

>> +
>> +     mb86s7x_pm_use_count[cpu][cluster]++;
>> +
>> +     if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
>> +             struct mb86s7x_cpu_gate cmd;
>> +
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>
> Hmmm.... I was about to say that you cannot drop the lock here, however
> if the count is now 1, that means the target CPU is either down or
> already prepared to get there, and it cannot race with the code here. So
> far so good.
>
>> +             cmd.payload_size = sizeof(cmd);
>> +             cmd.cluster_class = 0;
>> +             cmd.cluster_id = cluster;
>> +             cmd.cpu_id = cpu;
>> +             cmd.cpu_state = SCB_CPU_STATE_ON;
>> +
>> +             pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
>> +                      __func__, __LINE__, cmd.cluster_class,
>> +                      cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
>> +
>> +             init_completion(&got_rsp);
>> +             mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
>> +             ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
>> +                                   &cmd, sizeof(cmd), &got_rsp);
>> +             if (ret < 0) {
>> +                     pr_err("%s:%d failed!\n", __func__, __LINE__);
>> +                     return ret;
>> +             }
>> +             if (ret)
>> +                     wait_for_completion(&got_rsp);
>> +
>> +             pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
>> +                      __func__, __LINE__, cmd.cluster_class,
>> +                      cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
>> +
>> +             if (cmd.cpu_state != SCB_CPU_STATE_ON)
>> +                     return -ENODEV;
>> +
>> +     } else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
>> +             /*
>> +              * The only possible values are:
>> +              * 0 = CPU down
>> +              * 1 = CPU (still) up
>> +              * 2 = CPU requested to be up before it had a chance
>> +              *     to actually make itself down.
>> +              * Any other value is a bug.
>> +              */
>> +             BUG();
>> +     }
>> +
>> +     arch_spin_unlock(&mb86s7x_pm_lock);
>
> If the count became 1, the lock was already unlocked above.
>
Ah, yes. thanks.

>> +
>> +     return 0;
>> +}
>> +
>> +static void mb86s7x_pm_suspend(u64 ignored)
>> +{
>> +     unsigned int mpidr, cpu, cluster;
>> +     bool last_man = false, skip_wfi = false;
>> +
>> +     mpidr = read_cpuid_mpidr();
>> +     cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
>> +     cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
>> +
>> +     pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
>> +     __mcpm_cpu_going_down(cpu, cluster);
>> +
>> +     arch_spin_lock(&mb86s7x_pm_lock);
>> +     BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
>> +
>> +     mb86s7x_pm_use_count[cpu][cluster]--;
>> +
>> +     if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
>> +             if (!mb86s7x_pm_use_count[0][cluster] &&
>> +                 !mb86s7x_pm_use_count[1][cluster])
>> +                     last_man = true;
>> +             mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
>> +     } else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
>> +             skip_wfi = true; /* Overtaken by a power up */
>> +     } else {
>> +             BUG();
>> +     }
>> +
>> +     if (!skip_wfi)
>> +             gic_cpu_if_down();
>> +
>> +     if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>> +
>> +             if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
>> +                     /*
>> +                      * On the Cortex-A15 we need to disable
>> +                      * L2 prefetching before flushing the cache.
>> +                      */
>> +                     asm volatile(
>> +                     "mcr p15, 1, %0, c15, c0, 3\n\t"
>> +                     "isb\n\t"
>> +                     "dsb"
>> +                     : : "r" (0x400));
>> +             }
>> +
>> +             v7_exit_coherency_flush(all);
>> +
>> +             cci_disable_port_by_cpu(mpidr);
>> +
>> +             __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
>> +     } else {
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>> +             v7_exit_coherency_flush(louis);
>> +     }
>> +
>> +     __mcpm_cpu_down(cpu, cluster);
>> +
>> +     /* Now we are prepared for power-down, do it: */
>> +     if (!skip_wfi)
>> +             wfi();
>> +
>> +     /* Not dead at this point?  Let our caller cope. */
>> +}
>> +
>> +static void mb86s7x_pm_power_down(void)
>> +{
>> +     mb86s7x_pm_suspend(0);
>> +}
>> +
>> +static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
>> +{
>> +     struct mb86s7x_cpu_gate cmd;
>> +     struct completion got_rsp;
>> +     int i, ret;
>> +
>> +     cmd.payload_size = sizeof(cmd);
>> +     cmd.cluster_class = 0;
>> +     cmd.cluster_id = cluster;
>> +     cmd.cpu_id = cpu;
>> +     cmd.cpu_state = SCB_CPU_STATE_ON;
>> +
>> +     for (i = 0; i < 50; i++) {
>> +             init_completion(&got_rsp);
>> +             ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
>> +                                   &cmd, sizeof(cmd), &got_rsp);
>> +             if (ret < 0) {
>> +                     pr_err("%s:%d failed to get CPU status\n",
>> +                            __func__, __LINE__);
>> +                     return -ETIMEDOUT;
>
> You should probably return the actual error from mhu_send_packet() here
> as this is probably not going to be a time-out error ?
>
OK.

>> +             }
>> +             if (ret)
>> +                     wait_for_completion(&got_rsp);
>
> Maybe wait_for_completion_timeout() so execution doesn't get stuck if
> the answer never comes back.  That is valid for the other call sites as
> well.
>
OK, though we are dead if communication with remote fails because
something real bad has to have happened to remote whose job is mainly
to serve our requests.

>> +             pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
>> +                      __func__, __LINE__,
>> +                      cmd.cluster_class, cmd.cluster_id,
>> +                      cmd.cpu_id, cmd.cpu_state);
>> +
>> +             if (cmd.cpu_state == SCB_CPU_STATE_OFF)
>> +                     return 0;
>> +
>> +             msleep(20);
>> +     }
>> +
>> +     return -ETIMEDOUT;
>> +}
>> +
>> +static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
>> +     .power_up               = mb86s7x_pm_power_up,
>> +     .power_down             = mb86s7x_pm_power_down,
>> +     .wait_for_powerdown     = mb86s7x_wait_for_powerdown,
>> +     .suspend                = mb86s7x_pm_suspend,
>> +};
>
> For proper behavior with cpuidle you'll need to provide a powered_up
> method as well.
>
OK.

>> +
>> +static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
>> +{
>> +     /* Reboot immediately */
>> +     mb86s7x_reboot(50);
>> +}
>> +
>> +static void mb86s7x_poweroff(void)
>> +{
>> +     /* Reboot never, remain dead */
>> +     mb86s7x_reboot(~0);
>> +}
>> +
>> +/*
>> + * Enable cluster-level coherency, in preparation for turning on the MMU.
>> + */
>> +static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
>> +{
>> +     asm volatile ("\n"
>> +"    cmp     r0, #1\n"
>> +"    bxne    lr\n"
>> +"    b       cci_enable_port_for_self");
>> +}
>> +
>> +static int __init mb86s7x_mcpm_init(void)
>> +{
>> +     unsigned int mpidr, cpu, cluster;
>> +     struct mb86s7x_scb_version cmd;
>> +     struct completion got_rsp;
>> +     int ret;
>> +
>> +     arm_pm_restart = mb86s7x_restart;
>> +     pm_power_off = mb86s7x_poweroff;
>> +
>> +     if (!cci_probed())
>> +             return -ENODEV;
>> +
>> +     mpidr = read_cpuid_mpidr();
>> +     cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
>> +     cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
>> +
>> +     pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
>> +     mb86s7x_pm_use_count[cpu][cluster] = 1;
>> +
>> +     /* reset the wfi 'color' for primary cpu */
>> +     mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
>> +
>> +     /* Set entry point for any CPU nowonwards */
>> +     writel_relaxed(virt_to_phys(mcpm_entry_point),
>> +                  MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
>> +
>> +     cmd.payload_size = sizeof(cmd);
>> +     cmd.version = 0;
>> +     cmd.config_version = 0;
>> +     init_completion(&got_rsp);
>> +     ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
>> +                           &cmd, sizeof(cmd), &got_rsp);
>> +     if (ret < 0)
>> +             pr_err("%s:%d failed to get SCB version\n",
>> +                    __func__, __LINE__);
>> +     if (ret)
>> +             wait_for_completion(&got_rsp);
>
> There appears to be a recurring pattern here:
>
>   - fill up mb86s7x_scb_version structure
>   - init_completion()
>   - mhu_send_packet()
>   - complain if error
>   - otherwise possibly wait_for_completion()
>
> All this could be abstracted in a common function to reduce code
> duplication.
>
OK.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 18:16         ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-15 18:16 UTC (permalink / raw)
  To: linux-arm-kernel

On 15 July 2014 22:35, Nicolas Pitre <nicolas.pitre@linaro.org> wrote:
> On Sun, 13 Jul 2014, Mollie Wu wrote:
>
>> diff --git a/arch/arm/mach-mb86s7x/board.c b/arch/arm/mach-mb86s7x/board.c
>> new file mode 100644
>> index 0000000..d6e76ec
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/board.c
>> @@ -0,0 +1,65 @@
>> +/*
>> + * Support for the Fujitsu's MB86S7x based devices.
>> + *
>> + * Copyright (C) 2014 Linaro, LTD
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, version 2 of the License.
>> + *
>> + * This program is distributed in the hope that it will be useful,
>> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
>> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
>> + * GNU General Public License for more details.
>> + *
>> + */
>> +
>> +#include <linux/of.h>
>> +
>> +#include <asm/mcpm.h>
>> +#include <asm/mach/map.h>
>> +#include <asm/mach/arch.h>
>> +
>> +#include "iomap.h"
>> +
>> +bool __init mb86s7x_smp_init_ops(void)
>> +{
>> +     struct device_node *node;
>> +
>> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> +     if (node && of_device_is_available(node)) {
>> +             mcpm_smp_set_ops();
>> +             return true;
>> +     }
>> +
>> +     return false;
>> +}
>
> You should be able to call mcpm_smp_set_ops() directly from
> mb86s7x_mcpm_init() and dispense with this function entirely.
>
Yup, thanks.

> [...]
>
>> diff --git a/arch/arm/mach-mb86s7x/mcpm.c b/arch/arm/mach-mb86s7x/mcpm.c
>> new file mode 100644
>> index 0000000..86d223f
>> --- /dev/null
>> +++ b/arch/arm/mach-mb86s7x/mcpm.c
>> @@ -0,0 +1,293 @@
>> +/*
>> + * arch/arm/mach-mb86s7x/mcpm.c
>> + *
>> + * "Inspired" by tc_pm.c
>> + * Copyright:        (C) 2013-2014  Fujitsu Semiconductor Limited
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/io.h>
>> +#include <linux/pm.h>
>> +#include <linux/delay.h>
>> +#include <linux/kernel.h>
>> +#include <linux/reboot.h>
>> +#include <linux/arm-cci.h>
>> +#include <linux/spinlock.h>
>> +#include <linux/irqchip/arm-gic.h>
>> +#include <linux/platform_data/mb86s7x_mbox.h>
>> +
>> +#include <asm/mcpm.h>
>> +#include <asm/cp15.h>
>> +#include <asm/cputype.h>
>> +#include <asm/system_misc.h>
>> +
>> +#include "iomap.h"
>> +
>> +static arch_spinlock_t mb86s7x_pm_lock = __ARCH_SPIN_LOCK_UNLOCKED;
>> +static int mb86s7x_pm_use_count[2][2];
>> +
>> +#define MB86S7X_WFICOLOR_VIRT (MB86S7X_ISRAM_VIRT + WFI_COLOR_REG_OFFSET)
>> +
>> +static void mb86s7x_set_wficolor(unsigned clstr, unsigned cpu, unsigned clr)
>> +{
>> +     u8 val;
>> +
>> +     if (clr & ~AT_WFI_COLOR_MASK)
>> +             return;
>> +
>> +     val = readb_relaxed(MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
>> +     val &= ~AT_WFI_COLOR_MASK;
>> +     val |= clr;
>> +     writeb_relaxed(val, MB86S7X_WFICOLOR_VIRT + clstr * 2 + cpu);
>> +}
>> +
>> +static int mb86s7x_pm_power_up(unsigned int cpu, unsigned int cluster)
>> +{
>> +     struct completion got_rsp;
>> +     int ret = 0;
>> +
>> +     pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
>
> You should ensure cpu and cluster are within the allowed range here.
>
OK, I didn't realize mcpm core could call with invalid cpu/cluster ids.


>> +
>> +     arch_spin_lock(&mb86s7x_pm_lock);
>
> As mentioned in my previous email there has to be a local_irq_disable()
> here before locking.
>
OK

>> +
>> +     mb86s7x_pm_use_count[cpu][cluster]++;
>> +
>> +     if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
>> +             struct mb86s7x_cpu_gate cmd;
>> +
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>
> Hmmm.... I was about to say that you cannot drop the lock here, however
> if the count is now 1, that means the target CPU is either down or
> already prepared to get there, and it cannot race with the code here. So
> far so good.
>
>> +             cmd.payload_size = sizeof(cmd);
>> +             cmd.cluster_class = 0;
>> +             cmd.cluster_id = cluster;
>> +             cmd.cpu_id = cpu;
>> +             cmd.cpu_state = SCB_CPU_STATE_ON;
>> +
>> +             pr_debug("%s:%d CMD Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
>> +                      __func__, __LINE__, cmd.cluster_class,
>> +                      cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
>> +
>> +             init_completion(&got_rsp);
>> +             mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
>> +             ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_SET_REQ,
>> +                                   &cmd, sizeof(cmd), &got_rsp);
>> +             if (ret < 0) {
>> +                     pr_err("%s:%d failed!\n", __func__, __LINE__);
>> +                     return ret;
>> +             }
>> +             if (ret)
>> +                     wait_for_completion(&got_rsp);
>> +
>> +             pr_debug("%s:%d REP Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u}\n",
>> +                      __func__, __LINE__, cmd.cluster_class,
>> +                      cmd.cluster_id, cmd.cpu_id, cmd.cpu_state);
>> +
>> +             if (cmd.cpu_state != SCB_CPU_STATE_ON)
>> +                     return -ENODEV;
>> +
>> +     } else if (mb86s7x_pm_use_count[cpu][cluster] != 2) {
>> +             /*
>> +              * The only possible values are:
>> +              * 0 = CPU down
>> +              * 1 = CPU (still) up
>> +              * 2 = CPU requested to be up before it had a chance
>> +              *     to actually make itself down.
>> +              * Any other value is a bug.
>> +              */
>> +             BUG();
>> +     }
>> +
>> +     arch_spin_unlock(&mb86s7x_pm_lock);
>
> If the count became 1, the lock was already unlocked above.
>
Ah, yes. thanks.

>> +
>> +     return 0;
>> +}
>> +
>> +static void mb86s7x_pm_suspend(u64 ignored)
>> +{
>> +     unsigned int mpidr, cpu, cluster;
>> +     bool last_man = false, skip_wfi = false;
>> +
>> +     mpidr = read_cpuid_mpidr();
>> +     cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
>> +     cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
>> +
>> +     pr_debug("%s: cpu %u cluster %u\n", __func__, cpu, cluster);
>> +     __mcpm_cpu_going_down(cpu, cluster);
>> +
>> +     arch_spin_lock(&mb86s7x_pm_lock);
>> +     BUG_ON(__mcpm_cluster_state(cluster) != CLUSTER_UP);
>> +
>> +     mb86s7x_pm_use_count[cpu][cluster]--;
>> +
>> +     if (mb86s7x_pm_use_count[cpu][cluster] == 0) {
>> +             if (!mb86s7x_pm_use_count[0][cluster] &&
>> +                 !mb86s7x_pm_use_count[1][cluster])
>> +                     last_man = true;
>> +             mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_POWEROFF);
>> +     } else if (mb86s7x_pm_use_count[cpu][cluster] == 1) {
>> +             skip_wfi = true; /* Overtaken by a power up */
>> +     } else {
>> +             BUG();
>> +     }
>> +
>> +     if (!skip_wfi)
>> +             gic_cpu_if_down();
>> +
>> +     if (last_man && __mcpm_outbound_enter_critical(cpu, cluster)) {
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>> +
>> +             if (read_cpuid_part_number() == ARM_CPU_PART_CORTEX_A15) {
>> +                     /*
>> +                      * On the Cortex-A15 we need to disable
>> +                      * L2 prefetching before flushing the cache.
>> +                      */
>> +                     asm volatile(
>> +                     "mcr p15, 1, %0, c15, c0, 3\n\t"
>> +                     "isb\n\t"
>> +                     "dsb"
>> +                     : : "r" (0x400));
>> +             }
>> +
>> +             v7_exit_coherency_flush(all);
>> +
>> +             cci_disable_port_by_cpu(mpidr);
>> +
>> +             __mcpm_outbound_leave_critical(cluster, CLUSTER_DOWN);
>> +     } else {
>> +             arch_spin_unlock(&mb86s7x_pm_lock);
>> +             v7_exit_coherency_flush(louis);
>> +     }
>> +
>> +     __mcpm_cpu_down(cpu, cluster);
>> +
>> +     /* Now we are prepared for power-down, do it: */
>> +     if (!skip_wfi)
>> +             wfi();
>> +
>> +     /* Not dead at this point?  Let our caller cope. */
>> +}
>> +
>> +static void mb86s7x_pm_power_down(void)
>> +{
>> +     mb86s7x_pm_suspend(0);
>> +}
>> +
>> +static int mb86s7x_wait_for_powerdown(unsigned int cpu, unsigned int cluster)
>> +{
>> +     struct mb86s7x_cpu_gate cmd;
>> +     struct completion got_rsp;
>> +     int i, ret;
>> +
>> +     cmd.payload_size = sizeof(cmd);
>> +     cmd.cluster_class = 0;
>> +     cmd.cluster_id = cluster;
>> +     cmd.cpu_id = cpu;
>> +     cmd.cpu_state = SCB_CPU_STATE_ON;
>> +
>> +     for (i = 0; i < 50; i++) {
>> +             init_completion(&got_rsp);
>> +             ret = mhu_send_packet(CMD_CPU_CLOCK_GATE_GET_REQ,
>> +                                   &cmd, sizeof(cmd), &got_rsp);
>> +             if (ret < 0) {
>> +                     pr_err("%s:%d failed to get CPU status\n",
>> +                            __func__, __LINE__);
>> +                     return -ETIMEDOUT;
>
> You should probably return the actual error from mhu_send_packet() here
> as this is probably not going to be a time-out error ?
>
OK.

>> +             }
>> +             if (ret)
>> +                     wait_for_completion(&got_rsp);
>
> Maybe wait_for_completion_timeout() so execution doesn't get stuck if
> the answer never comes back.  That is valid for the other call sites as
> well.
>
OK, though we are dead if communication with remote fails because
something real bad has to have happened to remote whose job is mainly
to serve our requests.

>> +             pr_debug("%s:%d Cl_Class-%u CL_ID-%u CPU_ID-%u STATE-%u\n",
>> +                      __func__, __LINE__,
>> +                      cmd.cluster_class, cmd.cluster_id,
>> +                      cmd.cpu_id, cmd.cpu_state);
>> +
>> +             if (cmd.cpu_state == SCB_CPU_STATE_OFF)
>> +                     return 0;
>> +
>> +             msleep(20);
>> +     }
>> +
>> +     return -ETIMEDOUT;
>> +}
>> +
>> +static const struct mcpm_platform_ops mb86s7x_pm_power_ops = {
>> +     .power_up               = mb86s7x_pm_power_up,
>> +     .power_down             = mb86s7x_pm_power_down,
>> +     .wait_for_powerdown     = mb86s7x_wait_for_powerdown,
>> +     .suspend                = mb86s7x_pm_suspend,
>> +};
>
> For proper behavior with cpuidle you'll need to provide a powered_up
> method as well.
>
OK.

>> +
>> +static void mb86s7x_restart(enum reboot_mode reboot_mode, const char *unused)
>> +{
>> +     /* Reboot immediately */
>> +     mb86s7x_reboot(50);
>> +}
>> +
>> +static void mb86s7x_poweroff(void)
>> +{
>> +     /* Reboot never, remain dead */
>> +     mb86s7x_reboot(~0);
>> +}
>> +
>> +/*
>> + * Enable cluster-level coherency, in preparation for turning on the MMU.
>> + */
>> +static void __naked mb86s7x_pm_power_up_setup(unsigned int affinity_level)
>> +{
>> +     asm volatile ("\n"
>> +"    cmp     r0, #1\n"
>> +"    bxne    lr\n"
>> +"    b       cci_enable_port_for_self");
>> +}
>> +
>> +static int __init mb86s7x_mcpm_init(void)
>> +{
>> +     unsigned int mpidr, cpu, cluster;
>> +     struct mb86s7x_scb_version cmd;
>> +     struct completion got_rsp;
>> +     int ret;
>> +
>> +     arm_pm_restart = mb86s7x_restart;
>> +     pm_power_off = mb86s7x_poweroff;
>> +
>> +     if (!cci_probed())
>> +             return -ENODEV;
>> +
>> +     mpidr = read_cpuid_mpidr();
>> +     cpu = MPIDR_AFFINITY_LEVEL(mpidr, 0);
>> +     cluster = MPIDR_AFFINITY_LEVEL(mpidr, 1);
>> +
>> +     pr_info("Booting on cpu_%u cluster_%u\n", cpu, cluster);
>> +     mb86s7x_pm_use_count[cpu][cluster] = 1;
>> +
>> +     /* reset the wfi 'color' for primary cpu */
>> +     mb86s7x_set_wficolor(cluster, cpu, AT_WFI_DO_NOTHING);
>> +
>> +     /* Set entry point for any CPU nowonwards */
>> +     writel_relaxed(virt_to_phys(mcpm_entry_point),
>> +                  MB86S7X_TRAMPOLINE_VIRT + SEC_RSTADDR_OFF);
>> +
>> +     cmd.payload_size = sizeof(cmd);
>> +     cmd.version = 0;
>> +     cmd.config_version = 0;
>> +     init_completion(&got_rsp);
>> +     ret = mhu_send_packet(CMD_SCB_CAPABILITY_GET_REQ,
>> +                           &cmd, sizeof(cmd), &got_rsp);
>> +     if (ret < 0)
>> +             pr_err("%s:%d failed to get SCB version\n",
>> +                    __func__, __LINE__);
>> +     if (ret)
>> +             wait_for_completion(&got_rsp);
>
> There appears to be a recurring pattern here:
>
>   - fill up mb86s7x_scb_version structure
>   - init_completion()
>   - mhu_send_packet()
>   - complain if error
>   - otherwise possibly wait_for_completion()
>
> All this could be abstracted in a common function to reduce code
> duplication.
>
OK.

Thank you.
-Jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 17:37         ` Jassi Brar
@ 2014-07-15 20:09             ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-15 20:09 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:
> On 14 July 2014 19:03, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> >> +Example:
> >> +
> >> +     pd_cpu: genpd@3 {
> >> +             compatible = "fujitsu,mb86s7x-pd";
> >> +             index = <3>;
> >> +     };
> >> +
> >> +Example of the node using power domain:
> >> +
> >> +     node {
> >> +             /* ... */
> >> +             fujitsu,power-domain = <&pd_cpu>;
> >> +             /* ... */
> >> +     };
> >
> > I believe there has been a submission for a generic power domain binding
> > now. We really shouldn't use vendor specific power domain bindings
> > any more.
> >
> IIUC the last submission v4 of that patchset was in May
> http://comments.gmane.org/gmane.linux.kernel.samsung-soc/31029
> Do we have to wait for that to get upstream? Or maybe we could adopt
> that binding now so it becomes trivial to convert to that when that
> gets upstream?

Just using the binding seems fine to me.
 
> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> >> new file mode 100644
> >> index 0000000..44f5b0c
> >> --- /dev/null
> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
> >> @@ -0,0 +1,18 @@
> >> +config ARCH_MB86S7X
> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> >
> > Why the LPAE dependency? Is none of the RAM reachable by regular
> > kernels?
> >
> Some devices like PCI, LLI need it and the S70-evb has half of its ram
> above 4GB.
> Maybe ARM_LPAE should be selected by inclusion of support for those?

No, you can't select ARM_LPAE because that would break machines that
do not support it in the same configuration.

Losing half the RAM or PCI should not be a problem, you'd just run
with reduced functionality. You wouldn't want to do that in practice,
but it's different from a hard dependency.

> >> +
> >> +bool __init mb86s7x_smp_init_ops(void)
> >> +{
> >> +     struct device_node *node;
> >> +
> >> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> >> +     if (node && of_device_is_available(node)) {
> >> +             mcpm_smp_set_ops();
> >> +             return true;
> >> +     }
> >> +
> >> +     return false;
> >> +}
> >
> > Can you use the CPU_METHOD_OF_DECLARE() macro to set your
> > SMP ops instead?
> >
> CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
> smp_ops which are statically defined. We have to call
> mcpm_smp_set_ops() which does the real job.

Hmm, that seems like a hole in the API. Maybe you can come up with
a solution for it that doesn't take too much effort. It seems the
way that MCPM was integrated is suboptimal. We don't have too many
users yet, can you try turning the logic for MCPM around so it fits
the CPU_METHOD_OF_DECLARE()?

> >> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
> >> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
> >> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
> >> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
> >> +
> >> +static LIST_HEAD(free_xfers);
> >> +static LIST_HEAD(pending_xfers);
> >> +static DEFINE_SPINLOCK(fsm_lock);
> >> +static struct mbox_client mhu_cl;
> >> +static struct mbox_chan *mhu_chan;
> >> +static DECLARE_WORK(scb_work, try_xfer);
> >> +static mhu_handler_t handler[MHU_NUM_CMDS];
> >> +
> >> +static enum {
> >> +     MHU_PARK = 0,
> >> +     MHU_WRR, /* Waiting to get Remote's Reply */
> >> +     MHU_WRL, /* Waiting to send Reply */
> >> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
> >> +     MHU_INVLD,
> >> +} fsm_state;
> >
> > Ideally, these should all be part of a per-device data structure
> > referenced from pdev_get_drvdata().
> >
> I saw that coming :)
> We need this driver as early as when timers are populated and then for
> secondary CPU power control ... when we don't have any platform_device
> to hook the data on. And I think it is ok because this is the 'server'
> driver for the platform which by definition won't have another
> instance.

Then for now put them into one local structure and pass around the
pointer where you can but access the local one where it's too early.

The effect will be the same, but it's easier to grasp by someone
who is used to reading regular device drivers. Also add a comment
in front of the data structure explaining the reasons for having
a static copy.

> >> +static void got_data(u32 code)
> >> +{
> >> +     struct completion *c = NULL;
> >> +     mhu_handler_t hndlr = NULL;
> >> +     unsigned long flags;
> >> +     int ev;
> >> +
> >> +     if (code & RESP_BIT)
> >> +             ev = EV_RR;
> >> +     else
> >> +             ev = EV_RC;
> >> +
> >> +     spin_lock_irqsave(&fsm_lock, flags);
> >> +
> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> >> +             return;
> >> +     }
> >> +     fsm_state = mhu_fsm[fsm_state][ev];
> >> +
> >> +     if (code & RESP_BIT) {
> >> +             c = ax->c;
> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> >> +             list_move(&ax->node, &free_xfers);
> >> +             ax = NULL;
> >> +     } else {
> >> +             /* Find and dispatch relevant registered handler */
> >> +             if (code < MHU_NUM_CMDS)
> >> +                     hndlr = handler[code];
> >> +             if (!hndlr)
> >> +                     pr_err("No handler for CMD_%u\n", code);
> >> +     }
> >> +
> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
> >> +
> >> +     if (hndlr)
> >> +             hndlr(code, cmd_from_scb);
> >> +     if (c)
> >> +             complete(c);
> >> +}
> >> +
> >> +static void mhu_recv(struct mbox_client *cl, void *data)
> >> +{
> >> +     got_data((u32)data);
> >> +     schedule_work(&scb_work);
> >> +}
> >
> > Why the cast between integer and pointer?
> >
> The common mailbox framework passes around pointers to data packets.
> Its completely between controller and client drivers to decide the
> format of the packet. In my case the packet is a simple u32 value.

I don't think the mailbox framework should allow that much
flexibility, because that would break portable drivers that
rely on a particular behavior from the mailbox provider.

I understand that your driver is not portable to another mailbox
provider, but it still seems like a mistake in the API that should
better be fixed sooner than later.

> >> +                     do {
> >> +retry:
> >> +                             /* Wait until we get reply */
> >> +                             count = 0x1000000;
> >> +                             do {
> >> +                                     cpu_relax();
> >> +                                     val = readl_relaxed(
> >> +                                             rx_reg + INTR_STAT_OFS);
> >> +                             } while (--count && !val);
> >> +
> >> +                             if (!val) {
> >> +                                     pr_err("%s:%d SCB didn't reply\n",
> >> +                                            __func__, __LINE__);
> >> +                                     goto retry;
> >
> > It seems like this loop is unbounded and will just print an error every
> > 16M loops but never give up or re-enable interrupts if called with
> > interrupts disabled.
> >
> > It should probably be changed to either enforce being called with interrupts
> > enabled so you can call msleep() inbetween, or you should add error-handling
> > in case the remote side does not reply.
> >
> We can do that for completeness but otherwise my platform is as good
> as dead if the remote doesn't reply for some reason. And there is no
> fool-proof way to recover the state-machine from a failed
> communication.

Do you know how long it takes normally? If you can prove that the
reply normally comes within a few microseconds, you can instead call
WARN_ON() once after too much time passes, and then continue polling.

The case to avoid here is just accidentally polling for multiple
milliseconds with interrupts disabled, which would cause a lot of
problems.

> >> +struct mb86s7x_peri_clk {
> >> +     u32 payload_size;
> >> +     u32 cntrlr;
> >> +     u32 domain;
> >> +     u32 port;
> >> +     u32 en;
> >> +     u64 freqency;
> >> +} __packed;
> >
> > Just mark the last member by itself __packed. I assume you didn't
> > actually mean to change the alignment of the data structure to one
> > byte, but just want to say that the last one is misaligned.
> >
> This and others, are data packets that are passed between local and
> remote via SharedMemory. __packed is only meant to specify that these
> data structures have no holes in them.

That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
already has no padding on any architecture that is supported by Linux.
The only reason you need the packing here is because the u64 member is
unaligned. Note that marking the entire structure as packed means that
accesses are no longer atomic because the compiler may prefer to do them
one byte at a time, which can break the protocol on the shared memory
area.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-15 20:09             ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-15 20:09 UTC (permalink / raw)
  To: linux-arm-kernel

On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:
> On 14 July 2014 19:03, Arnd Bergmann <arnd@arndb.de> wrote:
> >> +Example:
> >> +
> >> +     pd_cpu: genpd at 3 {
> >> +             compatible = "fujitsu,mb86s7x-pd";
> >> +             index = <3>;
> >> +     };
> >> +
> >> +Example of the node using power domain:
> >> +
> >> +     node {
> >> +             /* ... */
> >> +             fujitsu,power-domain = <&pd_cpu>;
> >> +             /* ... */
> >> +     };
> >
> > I believe there has been a submission for a generic power domain binding
> > now. We really shouldn't use vendor specific power domain bindings
> > any more.
> >
> IIUC the last submission v4 of that patchset was in May
> http://comments.gmane.org/gmane.linux.kernel.samsung-soc/31029
> Do we have to wait for that to get upstream? Or maybe we could adopt
> that binding now so it becomes trivial to convert to that when that
> gets upstream?

Just using the binding seems fine to me.
 
> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> >> new file mode 100644
> >> index 0000000..44f5b0c
> >> --- /dev/null
> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
> >> @@ -0,0 +1,18 @@
> >> +config ARCH_MB86S7X
> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> >
> > Why the LPAE dependency? Is none of the RAM reachable by regular
> > kernels?
> >
> Some devices like PCI, LLI need it and the S70-evb has half of its ram
> above 4GB.
> Maybe ARM_LPAE should be selected by inclusion of support for those?

No, you can't select ARM_LPAE because that would break machines that
do not support it in the same configuration.

Losing half the RAM or PCI should not be a problem, you'd just run
with reduced functionality. You wouldn't want to do that in practice,
but it's different from a hard dependency.

> >> +
> >> +bool __init mb86s7x_smp_init_ops(void)
> >> +{
> >> +     struct device_node *node;
> >> +
> >> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
> >> +     if (node && of_device_is_available(node)) {
> >> +             mcpm_smp_set_ops();
> >> +             return true;
> >> +     }
> >> +
> >> +     return false;
> >> +}
> >
> > Can you use the CPU_METHOD_OF_DECLARE() macro to set your
> > SMP ops instead?
> >
> CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
> smp_ops which are statically defined. We have to call
> mcpm_smp_set_ops() which does the real job.

Hmm, that seems like a hole in the API. Maybe you can come up with
a solution for it that doesn't take too much effort. It seems the
way that MCPM was integrated is suboptimal. We don't have too many
users yet, can you try turning the logic for MCPM around so it fits
the CPU_METHOD_OF_DECLARE()?

> >> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
> >> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
> >> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
> >> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
> >> +
> >> +static LIST_HEAD(free_xfers);
> >> +static LIST_HEAD(pending_xfers);
> >> +static DEFINE_SPINLOCK(fsm_lock);
> >> +static struct mbox_client mhu_cl;
> >> +static struct mbox_chan *mhu_chan;
> >> +static DECLARE_WORK(scb_work, try_xfer);
> >> +static mhu_handler_t handler[MHU_NUM_CMDS];
> >> +
> >> +static enum {
> >> +     MHU_PARK = 0,
> >> +     MHU_WRR, /* Waiting to get Remote's Reply */
> >> +     MHU_WRL, /* Waiting to send Reply */
> >> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
> >> +     MHU_INVLD,
> >> +} fsm_state;
> >
> > Ideally, these should all be part of a per-device data structure
> > referenced from pdev_get_drvdata().
> >
> I saw that coming :)
> We need this driver as early as when timers are populated and then for
> secondary CPU power control ... when we don't have any platform_device
> to hook the data on. And I think it is ok because this is the 'server'
> driver for the platform which by definition won't have another
> instance.

Then for now put them into one local structure and pass around the
pointer where you can but access the local one where it's too early.

The effect will be the same, but it's easier to grasp by someone
who is used to reading regular device drivers. Also add a comment
in front of the data structure explaining the reasons for having
a static copy.

> >> +static void got_data(u32 code)
> >> +{
> >> +     struct completion *c = NULL;
> >> +     mhu_handler_t hndlr = NULL;
> >> +     unsigned long flags;
> >> +     int ev;
> >> +
> >> +     if (code & RESP_BIT)
> >> +             ev = EV_RR;
> >> +     else
> >> +             ev = EV_RC;
> >> +
> >> +     spin_lock_irqsave(&fsm_lock, flags);
> >> +
> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> >> +             return;
> >> +     }
> >> +     fsm_state = mhu_fsm[fsm_state][ev];
> >> +
> >> +     if (code & RESP_BIT) {
> >> +             c = ax->c;
> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> >> +             list_move(&ax->node, &free_xfers);
> >> +             ax = NULL;
> >> +     } else {
> >> +             /* Find and dispatch relevant registered handler */
> >> +             if (code < MHU_NUM_CMDS)
> >> +                     hndlr = handler[code];
> >> +             if (!hndlr)
> >> +                     pr_err("No handler for CMD_%u\n", code);
> >> +     }
> >> +
> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
> >> +
> >> +     if (hndlr)
> >> +             hndlr(code, cmd_from_scb);
> >> +     if (c)
> >> +             complete(c);
> >> +}
> >> +
> >> +static void mhu_recv(struct mbox_client *cl, void *data)
> >> +{
> >> +     got_data((u32)data);
> >> +     schedule_work(&scb_work);
> >> +}
> >
> > Why the cast between integer and pointer?
> >
> The common mailbox framework passes around pointers to data packets.
> Its completely between controller and client drivers to decide the
> format of the packet. In my case the packet is a simple u32 value.

I don't think the mailbox framework should allow that much
flexibility, because that would break portable drivers that
rely on a particular behavior from the mailbox provider.

I understand that your driver is not portable to another mailbox
provider, but it still seems like a mistake in the API that should
better be fixed sooner than later.

> >> +                     do {
> >> +retry:
> >> +                             /* Wait until we get reply */
> >> +                             count = 0x1000000;
> >> +                             do {
> >> +                                     cpu_relax();
> >> +                                     val = readl_relaxed(
> >> +                                             rx_reg + INTR_STAT_OFS);
> >> +                             } while (--count && !val);
> >> +
> >> +                             if (!val) {
> >> +                                     pr_err("%s:%d SCB didn't reply\n",
> >> +                                            __func__, __LINE__);
> >> +                                     goto retry;
> >
> > It seems like this loop is unbounded and will just print an error every
> > 16M loops but never give up or re-enable interrupts if called with
> > interrupts disabled.
> >
> > It should probably be changed to either enforce being called with interrupts
> > enabled so you can call msleep() inbetween, or you should add error-handling
> > in case the remote side does not reply.
> >
> We can do that for completeness but otherwise my platform is as good
> as dead if the remote doesn't reply for some reason. And there is no
> fool-proof way to recover the state-machine from a failed
> communication.

Do you know how long it takes normally? If you can prove that the
reply normally comes within a few microseconds, you can instead call
WARN_ON() once after too much time passes, and then continue polling.

The case to avoid here is just accidentally polling for multiple
milliseconds with interrupts disabled, which would cause a lot of
problems.

> >> +struct mb86s7x_peri_clk {
> >> +     u32 payload_size;
> >> +     u32 cntrlr;
> >> +     u32 domain;
> >> +     u32 port;
> >> +     u32 en;
> >> +     u64 freqency;
> >> +} __packed;
> >
> > Just mark the last member by itself __packed. I assume you didn't
> > actually mean to change the alignment of the data structure to one
> > byte, but just want to say that the last one is misaligned.
> >
> This and others, are data packets that are passed between local and
> remote via SharedMemory. __packed is only meant to specify that these
> data structures have no holes in them.

That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
already has no padding on any architecture that is supported by Linux.
The only reason you need the packing here is because the u64 member is
unaligned. Note that marking the entire structure as packed means that
accesses are no longer atomic because the compiler may prefer to do them
one byte at a time, which can break the protocol on the shared memory
area.

	Arnd

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 15:11       ` Rob Herring
@ 2014-07-16  5:52           ` Andy Green
  -1 siblings, 0 replies; 88+ messages in thread
From: Andy Green @ 2014-07-16  5:52 UTC (permalink / raw)
  To: Rob Herring
  Cc: Mollie Wu, devicetree-u79uwXL29TY76Z2rM5mHXA,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Arnd Bergmann,
	Jaswinder Singh, Olof Johansson, Russell King - ARM Linux,
	Mark Rutland, Rob Herring, Pawel Moll, Linaro Patches,
	Tetsuya Takinishi, Nicolas Pitre

On 15 July 2014 23:11, Rob Herring <robherring2-Re5JQEeQqe8AvxtiuMwx3w@public.gmane.org> wrote:

>> +               interrupts = <0 324 4>,
>> +                            <0 325 4>;
>> +               clocks = <&clk_alw_6_8>;
>> +       };
>
> No arch timer?

As Jassi said he'll solve the rest of the issues you found, but about
the arch / localtimers... mb86s70 is a sort of initial demonstrator
for the new technologies in this series for Fujitsu.  As such it has
some errata and one of them is the localtimers are not workable, the
clocksource has to be provided instead by sp804.  If we define the
localtimers, the kernel will prefer them and we will fail to boot due
to the hardware errata.  So, they should not be defined for this
particular SoC.

Later variants fix this errata like mb86s73 (also introduced in this
patch series) and the Device Tree for that has the arch localtimers as
you would expect.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-16  5:52           ` Andy Green
  0 siblings, 0 replies; 88+ messages in thread
From: Andy Green @ 2014-07-16  5:52 UTC (permalink / raw)
  To: linux-arm-kernel

On 15 July 2014 23:11, Rob Herring <robherring2@gmail.com> wrote:

>> +               interrupts = <0 324 4>,
>> +                            <0 325 4>;
>> +               clocks = <&clk_alw_6_8>;
>> +       };
>
> No arch timer?

As Jassi said he'll solve the rest of the issues you found, but about
the arch / localtimers... mb86s70 is a sort of initial demonstrator
for the new technologies in this series for Fujitsu.  As such it has
some errata and one of them is the localtimers are not workable, the
clocksource has to be provided instead by sp804.  If we define the
localtimers, the kernel will prefer them and we will fail to boot due
to the hardware errata.  So, they should not be defined for this
particular SoC.

Later variants fix this errata like mb86s73 (also introduced in this
patch series) and the Device Tree for that has the arch localtimers as
you would expect.

-Andy

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

* Re: [PATCH 4/8] clk: Add clock driver for mb86s7x
  2014-07-14 14:08       ` Arnd Bergmann
@ 2014-07-16  7:09         ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-16  7:09 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Mollie Wu,
	Devicetree List, Mark Rutland, Tetsuya Takinishi, Andy Green,
	Russell King - ARM Linux, Pawel Moll, Patch Tracking,
	Rob Herring, Olof Johansson

On 14 July 2014 19:38, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> On Sunday 13 July 2014 14:30:52 Mollie Wu wrote:
>
>> ---
>>  .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
>>  drivers/clk/Makefile                               |   1 +
>>  drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
>>  3 files changed, 385 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>>  create mode 100644 drivers/clk/clk-mb86s7x.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>> new file mode 100644
>> index 0000000..4a17d79
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>> @@ -0,0 +1,32 @@
>> +Fujitsu CRG11 clock driver bindings
>> +-----------------------------------
>> +
>> +Required properties :
>> +- compatible : Shall contain "fujitsu,mb86s7x_clk"
>
> No wildcards in compatible strings please.
>
OK

>> +- #clock-cells : Shall be 0
>> +- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
>> +- domain : [0, 15]
>> +- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
>
> It would be good to be a bit more verbose here.
>
That is how the clock controller is on this soc. The UngatedCLK is the
source of 8 gateable clock ports as well as having its own output
port. The PLLs and divisors are internally programmed by the remote
master.

>> +
>> +struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
>> +{
>> +     struct clk_init_data init;
>> +     struct cl_clk *clc;
>> +
>> +     clc = kzalloc(sizeof(*clc), GFP_KERNEL);
>> +     if (!clc) {
>> +             pr_err("could not allocate cl_clk\n");
>> +             return ERR_PTR(-ENOMEM);
>> +     }
>> +
>> +     clc->hw.init = &init;
>> +     clc->cluster = topology_physical_package_id(cpu_dev->id);
>> +
>> +     init.name = dev_name(cpu_dev);
>> +     init.ops = &clk_clc_ops;
>> +     init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
>> +     init.num_parents = 0;
>> +
>> +     return devm_clk_register(cpu_dev, &clc->hw);
>> +}
>> +
>> +static int mb86s7x_clclk_of_init(void)
>> +{
>> +     int cpu;
>> +     struct clk *clk;
>> +
>> +     for_each_possible_cpu(cpu) {
>> +             struct device *cpu_dev = get_cpu_device(cpu);
>> +
>> +             if (!cpu_dev) {
>> +                     pr_err("failed to get cpu%d device\n", cpu);
>> +                     continue;
>> +             }
>> +
>> +             clk = mb86s7x_clclk_register(cpu_dev);
>> +             if (IS_ERR(clk)) {
>> +                     pr_err("failed to register cpu%d clock\n", cpu);
>> +                     continue;
>> +             }
>> +             if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
>> +                     pr_err("failed to register cpu%d clock lookup\n", cpu);
>> +                     continue;
>> +             }
>> +             pr_debug("registered clk for %s\n", dev_name(cpu_dev));
>> +     }
>> +
>> +     platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
>> +
>> +     return 0;
>> +}
>> +module_init(mb86s7x_clclk_of_init);
>>
>
> This looks weird: why don't you probe the clocks from DT like normal?
>
We need to register clocks for each cpu populated, which is only after
all clock providers are probed by CLK_OF_DECLARE()

> Why do you register a platform device here? Are you trying to hide the fact that
> the cpufreq stuff still doesn't use proper DT probing?
>
Yup, the generic  bL cpufreq driver doesn't probe by DT.

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

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

* [PATCH 4/8] clk: Add clock driver for mb86s7x
@ 2014-07-16  7:09         ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-16  7:09 UTC (permalink / raw)
  To: linux-arm-kernel

On 14 July 2014 19:38, Arnd Bergmann <arnd@arndb.de> wrote:
> On Sunday 13 July 2014 14:30:52 Mollie Wu wrote:
>
>> ---
>>  .../bindings/clock/fujitsu,mb86s7x_clk.txt         |  32 ++
>>  drivers/clk/Makefile                               |   1 +
>>  drivers/clk/clk-mb86s7x.c                          | 352 +++++++++++++++++++++
>>  3 files changed, 385 insertions(+)
>>  create mode 100644 Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>>  create mode 100644 drivers/clk/clk-mb86s7x.c
>>
>> diff --git a/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>> new file mode 100644
>> index 0000000..4a17d79
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/clock/fujitsu,mb86s7x_clk.txt
>> @@ -0,0 +1,32 @@
>> +Fujitsu CRG11 clock driver bindings
>> +-----------------------------------
>> +
>> +Required properties :
>> +- compatible : Shall contain "fujitsu,mb86s7x_clk"
>
> No wildcards in compatible strings please.
>
OK

>> +- #clock-cells : Shall be 0
>> +- cntrlr : 0->ALW, 1->DDR3, 2->MAIN, 3->CA15, 4->HDMI, 5->DPHY
>> +- domain : [0, 15]
>> +- port : [0,7] -> Gateable Clock Ports.  [8]->UngatedCLK
>
> It would be good to be a bit more verbose here.
>
That is how the clock controller is on this soc. The UngatedCLK is the
source of 8 gateable clock ports as well as having its own output
port. The PLLs and divisors are internally programmed by the remote
master.

>> +
>> +struct clk *mb86s7x_clclk_register(struct device *cpu_dev)
>> +{
>> +     struct clk_init_data init;
>> +     struct cl_clk *clc;
>> +
>> +     clc = kzalloc(sizeof(*clc), GFP_KERNEL);
>> +     if (!clc) {
>> +             pr_err("could not allocate cl_clk\n");
>> +             return ERR_PTR(-ENOMEM);
>> +     }
>> +
>> +     clc->hw.init = &init;
>> +     clc->cluster = topology_physical_package_id(cpu_dev->id);
>> +
>> +     init.name = dev_name(cpu_dev);
>> +     init.ops = &clk_clc_ops;
>> +     init.flags = CLK_IS_ROOT | CLK_GET_RATE_NOCACHE;
>> +     init.num_parents = 0;
>> +
>> +     return devm_clk_register(cpu_dev, &clc->hw);
>> +}
>> +
>> +static int mb86s7x_clclk_of_init(void)
>> +{
>> +     int cpu;
>> +     struct clk *clk;
>> +
>> +     for_each_possible_cpu(cpu) {
>> +             struct device *cpu_dev = get_cpu_device(cpu);
>> +
>> +             if (!cpu_dev) {
>> +                     pr_err("failed to get cpu%d device\n", cpu);
>> +                     continue;
>> +             }
>> +
>> +             clk = mb86s7x_clclk_register(cpu_dev);
>> +             if (IS_ERR(clk)) {
>> +                     pr_err("failed to register cpu%d clock\n", cpu);
>> +                     continue;
>> +             }
>> +             if (clk_register_clkdev(clk, NULL, dev_name(cpu_dev))) {
>> +                     pr_err("failed to register cpu%d clock lookup\n", cpu);
>> +                     continue;
>> +             }
>> +             pr_debug("registered clk for %s\n", dev_name(cpu_dev));
>> +     }
>> +
>> +     platform_device_register_simple("arm-bL-cpufreq-dt", -1, NULL, 0);
>> +
>> +     return 0;
>> +}
>> +module_init(mb86s7x_clclk_of_init);
>>
>
> This looks weird: why don't you probe the clocks from DT like normal?
>
We need to register clocks for each cpu populated, which is only after
all clock providers are probed by CLK_OF_DECLARE()

> Why do you register a platform device here? Are you trying to hide the fact that
> the cpufreq stuff still doesn't use proper DT probing?
>
Yup, the generic  bL cpufreq driver doesn't probe by DT.

thanks
-jassi

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

* RE: [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
  2014-07-14 14:04     ` Arnd Bergmann
@ 2014-07-16  9:35       ` Vincent.Yang
  -1 siblings, 0 replies; 88+ messages in thread
From: Vincent.Yang @ 2014-07-16  9:35 UTC (permalink / raw)
  To: Arnd Bergmann, linux-arm-kernel
  Cc: mark.rutland, devicetree, Mollie Wu, linux, anton, pawel.moll,
	Tetsuya Takinishi, patches, linux-mmc, chris, robh+dt,
	jaswinder.singh, andy.green, olof

>-----Original Message-----
>From: Arnd Bergmann [mailto:arnd@arndb.de]
>On Sunday 13 July 2014 14:29:31 Mollie Wu wrote:
>> +Required properties:
>> +- compatible: "fujitsu,f-sdh30"
>> +- voltage-ranges : This is a list of pairs. In each pair, two cells
>> +  are required. First cell specifies minimum slot voltage (mV), second
>> +  cell specifies maximum slot voltage (mV). In case of supported voltage
>> +  range is discontinuous, several ranges could be specified as a list.
>> +
>> +Optional properties:
>> +- pwr-mux-gpios: This is one optional gpio for controlling a power mux
>> +  which switches between two power supplies. 3.3V is selected when gpio
>> +  is high, and 1.8V is selected when gpio is low. This voltage is used
>> +  for signal level.
>
>It sounds like what you really want here is a reference to a gpio-regulator
>node and, and to put the details of the voltage switching in there.

Hi Arnd,
Yes, I'll use a gpio-regulator for it in next version.

>
>> diff --git a/drivers/mmc/host/sdhci_f_sdh30.c
>b/drivers/mmc/host/sdhci_f_sdh30.c
>> new file mode 100644
>> index 0000000..8d23f2d
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci_f_sdh30.c
>> @@ -0,0 +1,380 @@
>> +/*
>> + * linux/drivers/mmc/host/sdhci_f_sdh30.c
>> + *
>> + * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
>> + *              Vincent Yang <vincent.yang@tw.fujitsu.com>
>> + * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, version 2 of the License.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/mmc/sd.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/card.h>
>> +#include <linux/gpio.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/of_address.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/suspend.h>
>> +
>> +#include "sdhci.h"
>> +#include "sdhci-pltfm.h"
>> +#include "../core/core.h"
>
>Should this be <linux/mmc/core.h>? A device driver should not be looking
>at the drivers/mmc/core/core.h.

Yes, I'll remove it.

>
>> +#define DRIVER_NAME "f_sdh30"
>
>This macro doesn't seem to serve any purpose, it would be easier to
>read if you open-code this.

Yes, I'll do it.

>
>> +
>> +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
>> +{
>> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> +     u32 ctrl = 0;
>> +
>> +     usleep_range(2500, 3000);
>> +     ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
>> +     ctrl |= F_SDH30_CRES_O_DN;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +     ctrl |= F_SDH30_MSEL_O_1_8;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +
>> +     if (gpio_is_valid(priv->gpio_select_1v8)) {
>> +             dev_info(priv->dev, "%s: setting gpio\n", __func__);
>> +             gpio_direction_output(priv->gpio_select_1v8, 0);
>> +     }
>> +
>> +     ctrl &= ~F_SDH30_CRES_O_DN;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +     usleep_range(2500, 3000);
>> +
>> +     if (priv->vendor_hs200) {
>> +             dev_info(priv->dev, "%s: setting hs200\n", __func__);
>> +             ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
>> +             ctrl |= priv->vendor_hs200;
>> +             sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
>> +     }
>> +
>> +     ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
>> +     ctrl |= F_SDH30_CMD_CHK_DIS;
>> +     sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
>> +}
>
>I think this should really use the regulator API.
>
>If the regular is defined properly, this will work without any extra code.

Yes, I'll use a gpio-regulator for it.

>
>> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
>> +{
>> +     return F_SDH30_MIN_CLOCK;
>> +}
>> +
>> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
>> +{
>> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> +
>> +     if (gpio_is_valid(priv->gpio_select_1v8))
>> +             gpio_direction_output(priv->gpio_select_1v8, 1);
>> +
>> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
>> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
>> +             mmiowb();
>> +     }
>
>Can you explain the mmiowb call here?

This came from the original 3.0 based driver. It's trying to be a
write memory barrier. It wants to ensure the clock control change
actually happened before the following code.
I'll change it to a regular wmb in next version.

>
>> +
>> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width))
>{
>> +             switch (bus_width) {
>> +             case 8:
>> +                     dev_info(dev, "Applying 8 bit bus width\n");
>> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>> +                     break;
>> +             case 4:
>> +                     dev_info(dev, "Applying 4 bit bus width\n");
>> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
>> +                     break;
>> +             case 1:
>> +             default:
>> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
>> +                     break;
>> +             }
>> +     }
>
>This should probably be done in generic sdhci code somewhere. How about
>adding it to sdhci_get_of_property instead_

I should use generic mmc_of_parse for it. I'll update it in next version.

>> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
>> +     if (!IS_ERR(priv->clk_sd4)) {
>> +             ret = clk_prepare_enable(priv->clk_sd4);
>> +             if (ret < 0) {
>> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
>> +                     goto err_clk1;
>> +             }
>> +     }
>> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
>> +     if (!IS_ERR(priv->clk_b)) {
>> +             ret = clk_prepare_enable(priv->clk_b);
>> +             if (ret < 0) {
>> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
>> +                     goto err_clk2;
>> +             }
>> +     }
>
>Please pick clock names that match what some of the other drivers use.

I'll use "iface" and "core" for clock names because they match what used
in sdhci-msm driver.

Thanks a lot for your review!

Best regards,
Vincent Yang

>
>Ideally some of that should also move to common code.
>
>        Arnd

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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
@ 2014-07-16  9:35       ` Vincent.Yang
  0 siblings, 0 replies; 88+ messages in thread
From: Vincent.Yang @ 2014-07-16  9:35 UTC (permalink / raw)
  To: linux-arm-kernel

>-----Original Message-----
>From: Arnd Bergmann [mailto:arnd at arndb.de]
>On Sunday 13 July 2014 14:29:31 Mollie Wu wrote:
>> +Required properties:
>> +- compatible: "fujitsu,f-sdh30"
>> +- voltage-ranges : This is a list of pairs. In each pair, two cells
>> +  are required. First cell specifies minimum slot voltage (mV), second
>> +  cell specifies maximum slot voltage (mV). In case of supported voltage
>> +  range is discontinuous, several ranges could be specified as a list.
>> +
>> +Optional properties:
>> +- pwr-mux-gpios: This is one optional gpio for controlling a power mux
>> +  which switches between two power supplies. 3.3V is selected when gpio
>> +  is high, and 1.8V is selected when gpio is low. This voltage is used
>> +  for signal level.
>
>It sounds like what you really want here is a reference to a gpio-regulator
>node and, and to put the details of the voltage switching in there.

Hi Arnd,
Yes, I'll use a gpio-regulator for it in next version.

>
>> diff --git a/drivers/mmc/host/sdhci_f_sdh30.c
>b/drivers/mmc/host/sdhci_f_sdh30.c
>> new file mode 100644
>> index 0000000..8d23f2d
>> --- /dev/null
>> +++ b/drivers/mmc/host/sdhci_f_sdh30.c
>> @@ -0,0 +1,380 @@
>> +/*
>> + * linux/drivers/mmc/host/sdhci_f_sdh30.c
>> + *
>> + * Copyright (C) 2013 - 2014 Fujitsu Semiconductor, Ltd
>> + *              Vincent Yang <vincent.yang@tw.fujitsu.com>
>> + * Copyright (C) 2014 Linaro Ltd  Andy Green <andy.green@linaro.org>
>> + *
>> + * This program is free software: you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License as published by
>> + * the Free Software Foundation, version 2 of the License.
>> + */
>> +
>> +#include <linux/err.h>
>> +#include <linux/delay.h>
>> +#include <linux/module.h>
>> +#include <linux/mmc/sd.h>
>> +#include <linux/mmc/host.h>
>> +#include <linux/mmc/card.h>
>> +#include <linux/gpio.h>
>> +#include <linux/of_gpio.h>
>> +#include <linux/of_address.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/pm.h>
>> +#include <linux/pm_runtime.h>
>> +#include <linux/suspend.h>
>> +
>> +#include "sdhci.h"
>> +#include "sdhci-pltfm.h"
>> +#include "../core/core.h"
>
>Should this be <linux/mmc/core.h>? A device driver should not be looking
>at the drivers/mmc/core/core.h.

Yes, I'll remove it.

>
>> +#define DRIVER_NAME "f_sdh30"
>
>This macro doesn't seem to serve any purpose, it would be easier to
>read if you open-code this.

Yes, I'll do it.

>
>> +
>> +void sdhci_f_sdh30_soft_voltage_switch(struct sdhci_host *host)
>> +{
>> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> +     u32 ctrl = 0;
>> +
>> +     usleep_range(2500, 3000);
>> +     ctrl = sdhci_readl(host, F_SDH30_IO_CONTROL2);
>> +     ctrl |= F_SDH30_CRES_O_DN;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +     ctrl |= F_SDH30_MSEL_O_1_8;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +
>> +     if (gpio_is_valid(priv->gpio_select_1v8)) {
>> +             dev_info(priv->dev, "%s: setting gpio\n", __func__);
>> +             gpio_direction_output(priv->gpio_select_1v8, 0);
>> +     }
>> +
>> +     ctrl &= ~F_SDH30_CRES_O_DN;
>> +     sdhci_writel(host, ctrl, F_SDH30_IO_CONTROL2);
>> +     usleep_range(2500, 3000);
>> +
>> +     if (priv->vendor_hs200) {
>> +             dev_info(priv->dev, "%s: setting hs200\n", __func__);
>> +             ctrl = sdhci_readl(host, F_SDH30_ESD_CONTROL);
>> +             ctrl |= priv->vendor_hs200;
>> +             sdhci_writel(host, ctrl, F_SDH30_ESD_CONTROL);
>> +     }
>> +
>> +     ctrl = sdhci_readl(host, F_SDH30_TUNING_SETTING);
>> +     ctrl |= F_SDH30_CMD_CHK_DIS;
>> +     sdhci_writel(host, ctrl, F_SDH30_TUNING_SETTING);
>> +}
>
>I think this should really use the regulator API.
>
>If the regular is defined properly, this will work without any extra code.

Yes, I'll use a gpio-regulator for it.

>
>> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
>> +{
>> +     return F_SDH30_MIN_CLOCK;
>> +}
>> +
>> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
>> +{
>> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> +
>> +     if (gpio_is_valid(priv->gpio_select_1v8))
>> +             gpio_direction_output(priv->gpio_select_1v8, 1);
>> +
>> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
>> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
>> +             mmiowb();
>> +     }
>
>Can you explain the mmiowb call here?

This came from the original 3.0 based driver. It's trying to be a
write memory barrier. It wants to ensure the clock control change
actually happened before the following code.
I'll change it to a regular wmb in next version.

>
>> +
>> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width))
>{
>> +             switch (bus_width) {
>> +             case 8:
>> +                     dev_info(dev, "Applying 8 bit bus width\n");
>> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>> +                     break;
>> +             case 4:
>> +                     dev_info(dev, "Applying 4 bit bus width\n");
>> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
>> +                     break;
>> +             case 1:
>> +             default:
>> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
>> +                     break;
>> +             }
>> +     }
>
>This should probably be done in generic sdhci code somewhere. How about
>adding it to sdhci_get_of_property instead_

I should use generic mmc_of_parse for it. I'll update it in next version.

>> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
>> +     if (!IS_ERR(priv->clk_sd4)) {
>> +             ret = clk_prepare_enable(priv->clk_sd4);
>> +             if (ret < 0) {
>> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
>> +                     goto err_clk1;
>> +             }
>> +     }
>> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
>> +     if (!IS_ERR(priv->clk_b)) {
>> +             ret = clk_prepare_enable(priv->clk_b);
>> +             if (ret < 0) {
>> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
>> +                     goto err_clk2;
>> +             }
>> +     }
>
>Please pick clock names that match what some of the other drivers use.

I'll use "iface" and "core" for clock names because they match what used
in sdhci-msm driver.

Thanks a lot for your review!

Best regards,
Vincent Yang

>
>Ideally some of that should also move to common code.
>
>        Arnd

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

* Re: [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
  2014-07-16  9:35       ` Vincent.Yang
@ 2014-07-16 10:10         ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-16 10:10 UTC (permalink / raw)
  To: linux-arm-kernel
  Cc: Vincent. Yang, mark.rutland, devicetree, Mollie Wu, linux, anton,
	pawel.moll, Tetsuya Takinishi, patches, linux-mmc, chris,
	robh+dt, jaswinder.singh, andy.green, olof

On Wednesday 16 July 2014 17:35:41 Vincent. Yang wrote:

> >
> >> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
> >> +{
> >> +     return F_SDH30_MIN_CLOCK;
> >> +}
> >> +
> >> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
> >> +{
> >> +     struct f_sdhost_priv *priv = sdhci_priv(host);
> >> +
> >> +     if (gpio_is_valid(priv->gpio_select_1v8))
> >> +             gpio_direction_output(priv->gpio_select_1v8, 1);
> >> +
> >> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
> >> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
> >> +             mmiowb();
> >> +     }
> >
> >Can you explain the mmiowb call here?
> 
> This came from the original 3.0 based driver. It's trying to be a
> write memory barrier. It wants to ensure the clock control change
> actually happened before the following code.
> I'll change it to a regular wmb in next version.

Note that with 'readw'/writew', you shouldn't need any extra barriers.
If sdhci_writew() is readw_relaxed(), wmb() is the correct barrier.

> >> +
> >> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width))
> >{
> >> +             switch (bus_width) {
> >> +             case 8:
> >> +                     dev_info(dev, "Applying 8 bit bus width\n");
> >> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
> >> +                     break;
> >> +             case 4:
> >> +                     dev_info(dev, "Applying 4 bit bus width\n");
> >> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
> >> +                     break;
> >> +             case 1:
> >> +             default:
> >> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
> >> +                     break;
> >> +             }
> >> +     }
> >
> >This should probably be done in generic sdhci code somewhere. How about
> >adding it to sdhci_get_of_property instead_
> 
> I should use generic mmc_of_parse for it. I'll update it in next version.

Ah right, I thought we had this in common code already but couldn't find
it. Using mmc_of_parse() is definitely the correct solution here.

> >> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
> >> +     if (!IS_ERR(priv->clk_sd4)) {
> >> +             ret = clk_prepare_enable(priv->clk_sd4);
> >> +             if (ret < 0) {
> >> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
> >> +                     goto err_clk1;
> >> +             }
> >> +     }
> >> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
> >> +     if (!IS_ERR(priv->clk_b)) {
> >> +             ret = clk_prepare_enable(priv->clk_b);
> >> +             if (ret < 0) {
> >> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
> >> +                     goto err_clk2;
> >> +             }
> >> +     }
> >
> >Please pick clock names that match what some of the other drivers use.
> 
> I'll use "iface" and "core" for clock names because they match what used
> in sdhci-msm driver.

Ok, makes sense.

	Arnd

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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
@ 2014-07-16 10:10         ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-16 10:10 UTC (permalink / raw)
  To: linux-arm-kernel

On Wednesday 16 July 2014 17:35:41 Vincent. Yang wrote:

> >
> >> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
> >> +{
> >> +     return F_SDH30_MIN_CLOCK;
> >> +}
> >> +
> >> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
> >> +{
> >> +     struct f_sdhost_priv *priv = sdhci_priv(host);
> >> +
> >> +     if (gpio_is_valid(priv->gpio_select_1v8))
> >> +             gpio_direction_output(priv->gpio_select_1v8, 1);
> >> +
> >> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
> >> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
> >> +             mmiowb();
> >> +     }
> >
> >Can you explain the mmiowb call here?
> 
> This came from the original 3.0 based driver. It's trying to be a
> write memory barrier. It wants to ensure the clock control change
> actually happened before the following code.
> I'll change it to a regular wmb in next version.

Note that with 'readw'/writew', you shouldn't need any extra barriers.
If sdhci_writew() is readw_relaxed(), wmb() is the correct barrier.

> >> +
> >> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width", &bus_width))
> >{
> >> +             switch (bus_width) {
> >> +             case 8:
> >> +                     dev_info(dev, "Applying 8 bit bus width\n");
> >> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
> >> +                     break;
> >> +             case 4:
> >> +                     dev_info(dev, "Applying 4 bit bus width\n");
> >> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
> >> +                     break;
> >> +             case 1:
> >> +             default:
> >> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
> >> +                     break;
> >> +             }
> >> +     }
> >
> >This should probably be done in generic sdhci code somewhere. How about
> >adding it to sdhci_get_of_property instead_
> 
> I should use generic mmc_of_parse for it. I'll update it in next version.

Ah right, I thought we had this in common code already but couldn't find
it. Using mmc_of_parse() is definitely the correct solution here.

> >> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
> >> +     if (!IS_ERR(priv->clk_sd4)) {
> >> +             ret = clk_prepare_enable(priv->clk_sd4);
> >> +             if (ret < 0) {
> >> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
> >> +                     goto err_clk1;
> >> +             }
> >> +     }
> >> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
> >> +     if (!IS_ERR(priv->clk_b)) {
> >> +             ret = clk_prepare_enable(priv->clk_b);
> >> +             if (ret < 0) {
> >> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
> >> +                     goto err_clk2;
> >> +             }
> >> +     }
> >
> >Please pick clock names that match what some of the other drivers use.
> 
> I'll use "iface" and "core" for clock names because they match what used
> in sdhci-msm driver.

Ok, makes sense.

	Arnd

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

* RE: [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
  2014-07-16 10:10         ` Arnd Bergmann
@ 2014-07-16 11:07           ` Vincent.Yang
  -1 siblings, 0 replies; 88+ messages in thread
From: Vincent.Yang @ 2014-07-16 11:07 UTC (permalink / raw)
  To: Arnd Bergmann, linux-arm-kernel
  Cc: mark.rutland, devicetree, Mollie Wu, linux, pawel.moll,
	Tetsuya Takinishi, anton, linux-mmc, chris, andy.green,
	jaswinder.singh, robh+dt, patches, olof

>-----Original Message-----
>From: Arnd Bergmann [mailto:arnd@arndb.de]
>On Wednesday 16 July 2014 17:35:41 Vincent. Yang wrote:
>
>> >
>> >> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
>> >> +{
>> >> +     return F_SDH30_MIN_CLOCK;
>> >> +}
>> >> +
>> >> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
>> >> +{
>> >> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> >> +
>> >> +     if (gpio_is_valid(priv->gpio_select_1v8))
>> >> +             gpio_direction_output(priv->gpio_select_1v8, 1);
>> >> +
>> >> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
>> >> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
>> >> +             mmiowb();
>> >> +     }
>> >
>> >Can you explain the mmiowb call here?
>>
>> This came from the original 3.0 based driver. It's trying to be a
>> write memory barrier. It wants to ensure the clock control change
>> actually happened before the following code.
>> I'll change it to a regular wmb in next version.
>
>Note that with 'readw'/writew', you shouldn't need any extra barriers.
>If sdhci_writew() is readw_relaxed(), wmb() is the correct barrier.

Hi Arnd,
The sdhci_writew() is using writew(), so I shouldn't need any extra
barriers. I'll remove them in next version.
Thanks a lot for your review!


Best regards,
Vincent Yang

>
>> >> +
>> >> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width",
>&bus_width))
>> >{
>> >> +             switch (bus_width) {
>> >> +             case 8:
>> >> +                     dev_info(dev, "Applying 8 bit bus width\n");
>> >> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>> >> +                     break;
>> >> +             case 4:
>> >> +                     dev_info(dev, "Applying 4 bit bus width\n");
>> >> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
>> >> +                     break;
>> >> +             case 1:
>> >> +             default:
>> >> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
>> >> +                     break;
>> >> +             }
>> >> +     }
>> >
>> >This should probably be done in generic sdhci code somewhere. How about
>> >adding it to sdhci_get_of_property instead_
>>
>> I should use generic mmc_of_parse for it. I'll update it in next version.
>
>Ah right, I thought we had this in common code already but couldn't find
>it. Using mmc_of_parse() is definitely the correct solution here.
>
>> >> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
>> >> +     if (!IS_ERR(priv->clk_sd4)) {
>> >> +             ret = clk_prepare_enable(priv->clk_sd4);
>> >> +             if (ret < 0) {
>> >> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
>> >> +                     goto err_clk1;
>> >> +             }
>> >> +     }
>> >> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
>> >> +     if (!IS_ERR(priv->clk_b)) {
>> >> +             ret = clk_prepare_enable(priv->clk_b);
>> >> +             if (ret < 0) {
>> >> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
>> >> +                     goto err_clk2;
>> >> +             }
>> >> +     }
>> >
>> >Please pick clock names that match what some of the other drivers use.
>>
>> I'll use "iface" and "core" for clock names because they match what used
>> in sdhci-msm driver.
>
>Ok, makes sense.
>
>        Arnd

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

* [PATCH 2/8] mmc: sdhci: host: add new f_sdh30
@ 2014-07-16 11:07           ` Vincent.Yang
  0 siblings, 0 replies; 88+ messages in thread
From: Vincent.Yang @ 2014-07-16 11:07 UTC (permalink / raw)
  To: linux-arm-kernel

>-----Original Message-----
>From: Arnd Bergmann [mailto:arnd at arndb.de]
>On Wednesday 16 July 2014 17:35:41 Vincent. Yang wrote:
>
>> >
>> >> +unsigned int sdhci_f_sdh30_get_min_clock(struct sdhci_host *host)
>> >> +{
>> >> +     return F_SDH30_MIN_CLOCK;
>> >> +}
>> >> +
>> >> +void sdhci_f_sdh30_reset(struct sdhci_host *host, u8 mask)
>> >> +{
>> >> +     struct f_sdhost_priv *priv = sdhci_priv(host);
>> >> +
>> >> +     if (gpio_is_valid(priv->gpio_select_1v8))
>> >> +             gpio_direction_output(priv->gpio_select_1v8, 1);
>> >> +
>> >> +     if (sdhci_readw(host, SDHCI_CLOCK_CONTROL) == 0) {
>> >> +             sdhci_writew(host, 0xBC01, SDHCI_CLOCK_CONTROL);
>> >> +             mmiowb();
>> >> +     }
>> >
>> >Can you explain the mmiowb call here?
>>
>> This came from the original 3.0 based driver. It's trying to be a
>> write memory barrier. It wants to ensure the clock control change
>> actually happened before the following code.
>> I'll change it to a regular wmb in next version.
>
>Note that with 'readw'/writew', you shouldn't need any extra barriers.
>If sdhci_writew() is readw_relaxed(), wmb() is the correct barrier.

Hi Arnd,
The sdhci_writew() is using writew(), so I shouldn't need any extra
barriers. I'll remove them in next version.
Thanks a lot for your review!


Best regards,
Vincent Yang

>
>> >> +
>> >> +     if (!of_property_read_u32(pdev->dev.of_node, "bus-width",
>&bus_width))
>> >{
>> >> +             switch (bus_width) {
>> >> +             case 8:
>> >> +                     dev_info(dev, "Applying 8 bit bus width\n");
>> >> +                     host->mmc->caps |= MMC_CAP_8_BIT_DATA;
>> >> +                     break;
>> >> +             case 4:
>> >> +                     dev_info(dev, "Applying 4 bit bus width\n");
>> >> +                     host->mmc->caps |= MMC_CAP_4_BIT_DATA;
>> >> +                     break;
>> >> +             case 1:
>> >> +             default:
>> >> +                     dev_err(dev, "Invalid bus width: %u\n", bus_width);
>> >> +                     break;
>> >> +             }
>> >> +     }
>> >
>> >This should probably be done in generic sdhci code somewhere. How about
>> >adding it to sdhci_get_of_property instead_
>>
>> I should use generic mmc_of_parse for it. I'll update it in next version.
>
>Ah right, I thought we had this in common code already but couldn't find
>it. Using mmc_of_parse() is definitely the correct solution here.
>
>> >> +     priv->clk_sd4 = clk_get(&pdev->dev, "sd_sd4clk");
>> >> +     if (!IS_ERR(priv->clk_sd4)) {
>> >> +             ret = clk_prepare_enable(priv->clk_sd4);
>> >> +             if (ret < 0) {
>> >> +                     dev_err(dev, "Failed to enable sd4 clock: %d\n", ret);
>> >> +                     goto err_clk1;
>> >> +             }
>> >> +     }
>> >> +     priv->clk_b = clk_get(&pdev->dev, "sd_bclk");
>> >> +     if (!IS_ERR(priv->clk_b)) {
>> >> +             ret = clk_prepare_enable(priv->clk_b);
>> >> +             if (ret < 0) {
>> >> +                     dev_err(dev, "Failed to enable clk_b clock: %d\n", ret);
>> >> +                     goto err_clk2;
>> >> +             }
>> >> +     }
>> >
>> >Please pick clock names that match what some of the other drivers use.
>>
>> I'll use "iface" and "core" for clock names because they match what used
>> in sdhci-msm driver.
>
>Ok, makes sense.
>
>        Arnd

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-13  6:32   ` Mollie Wu
@ 2014-07-16 17:37       ` Sudeep Holla
  -1 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-16 17:37 UTC (permalink / raw)
  To: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA
  Cc: Sudeep Holla, andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A,
	jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A,
	linux-lFZ/pmaqli7XmaaqVzeoHQ, arnd-r2nGTMty4D4,
	olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi

Hi Mollie,

On 13/07/14 07:32, Mollie Wu wrote:
> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.

And it looks like this is not Fujitsu proprietary MHU, it's exactly same IP
on JUNO(ARM64 development platform from ARM [1]). I was not sure it's 
standard
IP used on other SoCs too, I too wrote similar driver :(.

Can you please confirm this by reading Peripheral ID(PID: 0xFD0, 0xFE0 -
0xFEC) and Component ID(COMPID: 0xFF0 - 0xFFC). Are they

PID  - 0x04 0x98 0xB0 0x1B 0x00
COMPID - 0x0D 0xF0 0x05 0xB1

If so we have do s/f_mhu/arm_mhu/g :)

> It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
> The MB86S7x communicates over the HighPri-NonSecure channel.
>
> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> ---
>   drivers/mailbox/Kconfig  |   7 ++
>   drivers/mailbox/Makefile |   2 +
>   drivers/mailbox/f_mhu.c  | 227 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 236 insertions(+)
>   create mode 100644 drivers/mailbox/f_mhu.c
>
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index c8b5c13..681aac2 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -6,6 +6,13 @@ menuconfig MAILBOX
>   	  signals. Say Y if your platform supports hardware mailboxes.
>
>   if MAILBOX
> +
> +config MBOX_F_MHU
> +	bool

On Juno, there's nothing that prevents me from compiling this as module.

> +	depends on ARCH_MB86S7X

Definitely not a requirement

> +	help
> +	  Say Y here if you want to use the F_MHU IPCM support.
> +

Also it needs some description.

>   config PL320_MBOX
>   	bool "ARM PL320 Mailbox"
>   	depends on ARM_AMBA
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 2fa343a..3707e93 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -2,6 +2,8 @@
>
>   obj-$(CONFIG_MAILBOX)		+= mailbox.o
>
> +obj-$(CONFIG_MBOX_F_MHU)	+= f_mhu.o
> +
>   obj-$(CONFIG_PL320_MBOX)	+= pl320-ipc.o
>
>   obj-$(CONFIG_OMAP_MBOX)		+= omap-mailbox.o
> diff --git a/drivers/mailbox/f_mhu.c b/drivers/mailbox/f_mhu.c
> new file mode 100644
> index 0000000..cf5d3cd
> --- /dev/null
> +++ b/drivers/mailbox/f_mhu.c
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2013-2014 Fujitsu Semiconductor Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/mailbox_controller.h>
> +#include <linux/platform_device.h>
> +
> +#define INTR_STAT_OFS	0x0
> +#define INTR_SET_OFS	0x8
> +#define INTR_CLR_OFS	0x10
> +
> +#define MHU_SCFG	0x400
> +

Remove this.(secure access only register)

> +struct mhu_link {
> +	unsigned irq;
> +	spinlock_t lock; /* channel regs */
> +	void __iomem *tx_reg;
> +	void __iomem *rx_reg;
> +};
> +
> +struct f_mhu {
> +	void __iomem *base;
> +	struct clk *clk;
> +	struct mhu_link mlink[3];
> +	struct mbox_chan chan[3];
> +	struct mbox_controller mbox;
> +};
> +
> +static irqreturn_t mhu_rx_interrupt(int irq, void *p)
> +{
> +	struct mbox_chan *chan = (struct mbox_chan *)p;
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;

You don't need explicit cast from void pointers

> +	u32 val;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);

Please remove all these debug prints.

> +	/* See NOTE_RX_DONE */
> +	val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
> +	mbox_chan_received_data(chan, (void *)val);
> +
> +	/*
> +	 * It is agreed with the remote firmware that the receiver
> +	 * will clear the STAT register indicating it is ready to
> +	 * receive next data - NOTE_RX_DONE
> +	 */

This note could be added as how this mailbox works in general and
it's not just Rx right ? Even Tx done is based on this logic.
Basically the logic is valid on both directions.

> +	writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static bool mhu_last_tx_done(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +	u32 val;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	spin_lock_irqsave(&mlink->lock, flags);

Why do we need this extra locks here ? mailbox core maintains
per channel lock and uses it for at-least send_data IIRC. And this
function is just reading status do we really need the lock ?

I must be missing something here else  IMO we can get rid of this
extra locks in here.

> +	/* See NOTE_RX_DONE */
> +	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	return (val == 0);
> +}
> +
> +static int mhu_send_data(struct mbox_chan *chan, void *data)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	if (!mhu_last_tx_done(chan)) {
> +		pr_err("%s:%d Shouldn't have seen the day!\n",
> +		       __func__, __LINE__);
> +		return -EBUSY;
> +	}
> +
> +	spin_lock_irqsave(&mlink->lock, flags);
> +	writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mhu_startup(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +	u32 val;
> +	int ret;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	spin_lock_irqsave(&mlink->lock, flags);
> +	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
> +	writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	ret = request_irq(mlink->irq, mhu_rx_interrupt,
> +			  IRQF_SHARED, "mhu_link", chan);

Just a thought: Can this be threaded_irq instead ?
Can move request_irq to probe instead esp. if threaded_irq ?
That provides some flexibility to client's rx_callback.

> +	if (unlikely(ret)) {
> +		pr_err("Unable to aquire IRQ\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mhu_shutdown(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	free_irq(mlink->irq, chan);
> +}
> +
> +static struct mbox_chan_ops mhu_ops = {
> +	.send_data = mhu_send_data,
> +	.startup = mhu_startup,
> +	.shutdown = mhu_shutdown,
> +	.last_tx_done = mhu_last_tx_done,
> +};
> +
> +static int f_mhu_probe(struct platform_device *pdev)
> +{
> +	int i, err;
> +	struct f_mhu *mhu;
> +	struct resource *res;
> +	int mhu_reg[3] = {0x0, 0x20, 0x200};

Probably this gets simplified when you remove secure channel access ?

> +
> +	/* Allocate memory for device */
> +	mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
> +	if (!mhu) {
> +		dev_err(&pdev->dev, "failed to allocate memory.\n");
> +		return -EBUSY;
> +	}
> +
> +	mhu->clk = clk_get(&pdev->dev, "clk");
> +	if (unlikely(IS_ERR(mhu->clk))) {
> +		dev_err(&pdev->dev, "unable to init clock\n");

Don't bail out if there's no clock specified in DT. Clock might not
be a hard requirement.

> +		kfree(mhu);
> +		return -EINVAL;
> +	}
> +	clk_prepare_enable(mhu->clk);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mhu->base = ioremap(res->start, resource_size(res));
> +	if (!mhu->base) {
> +		dev_err(&pdev->dev, "ioremap failed.\n");
> +		kfree(mhu);
> +		return -EBUSY;
> +	}
> +
> +	/* Let UnTrustedOS's access violations don't bother us */
> +	writel_relaxed(0, mhu->base + MHU_SCFG);
> +

Please don't do this. It can't work in non-secure mode. The firmware running
with secure access needs to configure this appropriately.

I might be missing to see, is there a binding document for this mhu ?

> +	for (i = 0; i < 3; i++) {
> +		mhu->chan[i].con_priv = &mhu->mlink[i];
> +		spin_lock_init(&mhu->mlink[i].lock);
> +		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
> +		mhu->mlink[i].irq = res->start;
> +		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
> +		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
> +	}
> +
> +	mhu->mbox.dev = &pdev->dev;
> +	mhu->mbox.chans = &mhu->chan[0];
> +	mhu->mbox.num_chans = 3;

Change this to 2, we shouldn't expose secular channel here as Linux can't
access that anyway.

> +	mhu->mbox.ops = &mhu_ops;
> +	mhu->mbox.txdone_irq = false;
> +	mhu->mbox.txdone_poll = true;
> +	mhu->mbox.txpoll_period = 10;
> +
> +	platform_set_drvdata(pdev, mhu);
> +
> +	err = mbox_controller_register(&mhu->mbox);
> +	if (err) {
> +		dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err);
> +		iounmap(mhu->base);
> +		kfree(mhu);
> +	} else {
> +		dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
> +	}
> +
> +	return 0;
> +}
> +

Also to be module you need add remove.

> +static const struct of_device_id f_mhu_dt_ids[] = {
> +	{ .compatible = "fujitsu,mhu" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
> +
> +static struct platform_driver f_mhu_driver = {
> +	.driver		= {
> +		.name	= "f_mhu",
> +		.owner = THIS_MODULE,
> +		.of_match_table = f_mhu_dt_ids,
> +	},
> +	.probe		= f_mhu_probe,
> +};
> +
> +static int __init f_mhu_init(void)
> +{
> +	return platform_driver_register(&f_mhu_driver);
> +}
> +module_init(f_mhu_init);

This can be module_platform_driver instead.

Regards,
Sudeep

[1] 
http://www.arm.com/products/tools/development-boards/versatile-express/juno-arm-development-platform.php

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-16 17:37       ` Sudeep Holla
  0 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-16 17:37 UTC (permalink / raw)
  To: linux-arm-kernel

Hi Mollie,

On 13/07/14 07:32, Mollie Wu wrote:
> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.

And it looks like this is not Fujitsu proprietary MHU, it's exactly same IP
on JUNO(ARM64 development platform from ARM [1]). I was not sure it's 
standard
IP used on other SoCs too, I too wrote similar driver :(.

Can you please confirm this by reading Peripheral ID(PID: 0xFD0, 0xFE0 -
0xFEC) and Component ID(COMPID: 0xFF0 - 0xFFC). Are they

PID  - 0x04 0x98 0xB0 0x1B 0x00
COMPID - 0x0D 0xF0 0x05 0xB1

If so we have do s/f_mhu/arm_mhu/g :)

> It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
> The MB86S7x communicates over the HighPri-NonSecure channel.
>
> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
> ---
>   drivers/mailbox/Kconfig  |   7 ++
>   drivers/mailbox/Makefile |   2 +
>   drivers/mailbox/f_mhu.c  | 227 +++++++++++++++++++++++++++++++++++++++++++++++
>   3 files changed, 236 insertions(+)
>   create mode 100644 drivers/mailbox/f_mhu.c
>
> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
> index c8b5c13..681aac2 100644
> --- a/drivers/mailbox/Kconfig
> +++ b/drivers/mailbox/Kconfig
> @@ -6,6 +6,13 @@ menuconfig MAILBOX
>   	  signals. Say Y if your platform supports hardware mailboxes.
>
>   if MAILBOX
> +
> +config MBOX_F_MHU
> +	bool

On Juno, there's nothing that prevents me from compiling this as module.

> +	depends on ARCH_MB86S7X

Definitely not a requirement

> +	help
> +	  Say Y here if you want to use the F_MHU IPCM support.
> +

Also it needs some description.

>   config PL320_MBOX
>   	bool "ARM PL320 Mailbox"
>   	depends on ARM_AMBA
> diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile
> index 2fa343a..3707e93 100644
> --- a/drivers/mailbox/Makefile
> +++ b/drivers/mailbox/Makefile
> @@ -2,6 +2,8 @@
>
>   obj-$(CONFIG_MAILBOX)		+= mailbox.o
>
> +obj-$(CONFIG_MBOX_F_MHU)	+= f_mhu.o
> +
>   obj-$(CONFIG_PL320_MBOX)	+= pl320-ipc.o
>
>   obj-$(CONFIG_OMAP_MBOX)		+= omap-mailbox.o
> diff --git a/drivers/mailbox/f_mhu.c b/drivers/mailbox/f_mhu.c
> new file mode 100644
> index 0000000..cf5d3cd
> --- /dev/null
> +++ b/drivers/mailbox/f_mhu.c
> @@ -0,0 +1,227 @@
> +/*
> + * Copyright (C) 2013-2014 Fujitsu Semiconductor Ltd.
> + *
> + * This program is free software: you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License as published by
> + * the Free Software Foundation, version 2 of the License.
> + *
> + * This program is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
> + * GNU General Public License for more details.
> + */
> +
> +#include <linux/interrupt.h>
> +#include <linux/spinlock.h>
> +#include <linux/mutex.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/clk.h>
> +#include <linux/module.h>
> +#include <linux/mailbox_controller.h>
> +#include <linux/platform_device.h>
> +
> +#define INTR_STAT_OFS	0x0
> +#define INTR_SET_OFS	0x8
> +#define INTR_CLR_OFS	0x10
> +
> +#define MHU_SCFG	0x400
> +

Remove this.(secure access only register)

> +struct mhu_link {
> +	unsigned irq;
> +	spinlock_t lock; /* channel regs */
> +	void __iomem *tx_reg;
> +	void __iomem *rx_reg;
> +};
> +
> +struct f_mhu {
> +	void __iomem *base;
> +	struct clk *clk;
> +	struct mhu_link mlink[3];
> +	struct mbox_chan chan[3];
> +	struct mbox_controller mbox;
> +};
> +
> +static irqreturn_t mhu_rx_interrupt(int irq, void *p)
> +{
> +	struct mbox_chan *chan = (struct mbox_chan *)p;
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;

You don't need explicit cast from void pointers

> +	u32 val;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);

Please remove all these debug prints.

> +	/* See NOTE_RX_DONE */
> +	val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
> +	mbox_chan_received_data(chan, (void *)val);
> +
> +	/*
> +	 * It is agreed with the remote firmware that the receiver
> +	 * will clear the STAT register indicating it is ready to
> +	 * receive next data - NOTE_RX_DONE
> +	 */

This note could be added as how this mailbox works in general and
it's not just Rx right ? Even Tx done is based on this logic.
Basically the logic is valid on both directions.

> +	writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
> +
> +	return IRQ_HANDLED;
> +}
> +
> +static bool mhu_last_tx_done(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +	u32 val;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	spin_lock_irqsave(&mlink->lock, flags);

Why do we need this extra locks here ? mailbox core maintains
per channel lock and uses it for at-least send_data IIRC. And this
function is just reading status do we really need the lock ?

I must be missing something here else  IMO we can get rid of this
extra locks in here.

> +	/* See NOTE_RX_DONE */
> +	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	return (val == 0);
> +}
> +
> +static int mhu_send_data(struct mbox_chan *chan, void *data)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	if (!mhu_last_tx_done(chan)) {
> +		pr_err("%s:%d Shouldn't have seen the day!\n",
> +		       __func__, __LINE__);
> +		return -EBUSY;
> +	}
> +
> +	spin_lock_irqsave(&mlink->lock, flags);
> +	writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	return 0;
> +}
> +
> +static int mhu_startup(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +	unsigned long flags;
> +	u32 val;
> +	int ret;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	spin_lock_irqsave(&mlink->lock, flags);
> +	val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
> +	writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
> +	spin_unlock_irqrestore(&mlink->lock, flags);
> +
> +	ret = request_irq(mlink->irq, mhu_rx_interrupt,
> +			  IRQF_SHARED, "mhu_link", chan);

Just a thought: Can this be threaded_irq instead ?
Can move request_irq to probe instead esp. if threaded_irq ?
That provides some flexibility to client's rx_callback.

> +	if (unlikely(ret)) {
> +		pr_err("Unable to aquire IRQ\n");
> +		return ret;
> +	}
> +
> +	return 0;
> +}
> +
> +static void mhu_shutdown(struct mbox_chan *chan)
> +{
> +	struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
> +
> +	pr_debug("%s:%d\n", __func__, __LINE__);
> +	free_irq(mlink->irq, chan);
> +}
> +
> +static struct mbox_chan_ops mhu_ops = {
> +	.send_data = mhu_send_data,
> +	.startup = mhu_startup,
> +	.shutdown = mhu_shutdown,
> +	.last_tx_done = mhu_last_tx_done,
> +};
> +
> +static int f_mhu_probe(struct platform_device *pdev)
> +{
> +	int i, err;
> +	struct f_mhu *mhu;
> +	struct resource *res;
> +	int mhu_reg[3] = {0x0, 0x20, 0x200};

Probably this gets simplified when you remove secure channel access ?

> +
> +	/* Allocate memory for device */
> +	mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
> +	if (!mhu) {
> +		dev_err(&pdev->dev, "failed to allocate memory.\n");
> +		return -EBUSY;
> +	}
> +
> +	mhu->clk = clk_get(&pdev->dev, "clk");
> +	if (unlikely(IS_ERR(mhu->clk))) {
> +		dev_err(&pdev->dev, "unable to init clock\n");

Don't bail out if there's no clock specified in DT. Clock might not
be a hard requirement.

> +		kfree(mhu);
> +		return -EINVAL;
> +	}
> +	clk_prepare_enable(mhu->clk);
> +
> +	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
> +	mhu->base = ioremap(res->start, resource_size(res));
> +	if (!mhu->base) {
> +		dev_err(&pdev->dev, "ioremap failed.\n");
> +		kfree(mhu);
> +		return -EBUSY;
> +	}
> +
> +	/* Let UnTrustedOS's access violations don't bother us */
> +	writel_relaxed(0, mhu->base + MHU_SCFG);
> +

Please don't do this. It can't work in non-secure mode. The firmware running
with secure access needs to configure this appropriately.

I might be missing to see, is there a binding document for this mhu ?

> +	for (i = 0; i < 3; i++) {
> +		mhu->chan[i].con_priv = &mhu->mlink[i];
> +		spin_lock_init(&mhu->mlink[i].lock);
> +		res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
> +		mhu->mlink[i].irq = res->start;
> +		mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
> +		mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
> +	}
> +
> +	mhu->mbox.dev = &pdev->dev;
> +	mhu->mbox.chans = &mhu->chan[0];
> +	mhu->mbox.num_chans = 3;

Change this to 2, we shouldn't expose secular channel here as Linux can't
access that anyway.

> +	mhu->mbox.ops = &mhu_ops;
> +	mhu->mbox.txdone_irq = false;
> +	mhu->mbox.txdone_poll = true;
> +	mhu->mbox.txpoll_period = 10;
> +
> +	platform_set_drvdata(pdev, mhu);
> +
> +	err = mbox_controller_register(&mhu->mbox);
> +	if (err) {
> +		dev_err(&pdev->dev, "Failed to register mailboxes %d\n", err);
> +		iounmap(mhu->base);
> +		kfree(mhu);
> +	} else {
> +		dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
> +	}
> +
> +	return 0;
> +}
> +

Also to be module you need add remove.

> +static const struct of_device_id f_mhu_dt_ids[] = {
> +	{ .compatible = "fujitsu,mhu" },
> +	{ /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
> +
> +static struct platform_driver f_mhu_driver = {
> +	.driver		= {
> +		.name	= "f_mhu",
> +		.owner = THIS_MODULE,
> +		.of_match_table = f_mhu_dt_ids,
> +	},
> +	.probe		= f_mhu_probe,
> +};
> +
> +static int __init f_mhu_init(void)
> +{
> +	return platform_driver_register(&f_mhu_driver);
> +}
> +module_init(f_mhu_init);

This can be module_platform_driver instead.

Regards,
Sudeep

[1] 
http://www.arm.com/products/tools/development-boards/versatile-express/juno-arm-development-platform.php

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-16 17:37       ` Sudeep Holla
@ 2014-07-17  6:25           ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17  6:25 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi

On 16 July 2014 23:07, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
> Hi Mollie,
>
>
> On 13/07/14 07:32, Mollie Wu wrote:
>>
>> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.
>
>
> And it looks like this is not Fujitsu proprietary MHU, it's exactly same IP
> on JUNO(ARM64 development platform from ARM [1]). I was not sure it's
> standard
> IP used on other SoCs too, I too wrote similar driver :(.
>
Same here :)

> Can you please confirm this by reading Peripheral ID(PID: 0xFD0, 0xFE0 -
> 0xFEC) and Component ID(COMPID: 0xFF0 - 0xFFC). Are they
>
> PID  - 0x04 0x98 0xB0 0x1B 0x00
> COMPID - 0x0D 0xF0 0x05 0xB1
>
> If so we have do s/f_mhu/arm_mhu/g :)
>
Yup, we have to.

>
>> It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
>> The MB86S7x communicates over the HighPri-NonSecure channel.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
>> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> ---
>>   drivers/mailbox/Kconfig  |   7 ++
>>   drivers/mailbox/Makefile |   2 +
>>   drivers/mailbox/f_mhu.c  | 227
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 236 insertions(+)
>>   create mode 100644 drivers/mailbox/f_mhu.c
>>
>> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
>> index c8b5c13..681aac2 100644
>> --- a/drivers/mailbox/Kconfig
>> +++ b/drivers/mailbox/Kconfig
>> @@ -6,6 +6,13 @@ menuconfig MAILBOX
>>           signals. Say Y if your platform supports hardware mailboxes.
>>
>>   if MAILBOX
>> +
>> +config MBOX_F_MHU
>> +       bool
>
>
> On Juno, there's nothing that prevents me from compiling this as module.
>
On S7x, even built-in is not early enough for our purposes. But yes,
now that we have company it should be made tristate.

>> +       depends on ARCH_MB86S7X
>
>
> Definitely not a requirement
>
It was, until we found each other ;)

>
>> +       help
>> +         Say Y here if you want to use the F_MHU IPCM support.
>> +
>
>
> Also it needs some description.
>
OK


>> +#define INTR_STAT_OFS  0x0
>> +#define INTR_SET_OFS   0x8
>> +#define INTR_CLR_OFS   0x10
>> +
>> +#define MHU_SCFG       0x400
>> +
>
>
> Remove this.(secure access only register)
>
See later.

>
>> +struct mhu_link {
>> +       unsigned irq;
>> +       spinlock_t lock; /* channel regs */
>> +       void __iomem *tx_reg;
>> +       void __iomem *rx_reg;
>> +};
>> +
>> +struct f_mhu {
>> +       void __iomem *base;
>> +       struct clk *clk;
>> +       struct mhu_link mlink[3];
>> +       struct mbox_chan chan[3];
>> +       struct mbox_controller mbox;
>> +};
>> +
>> +static irqreturn_t mhu_rx_interrupt(int irq, void *p)
>> +{
>> +       struct mbox_chan *chan = (struct mbox_chan *)p;
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>
>
> You don't need explicit cast from void pointers
>
Yup

>
>> +       u32 val;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>
>
> Please remove all these debug prints.
>
These are as good as absent unless DEBUG is defined.

>
>> +       /* See NOTE_RX_DONE */
>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>> +       mbox_chan_received_data(chan, (void *)val);
>> +
>> +       /*
>> +        * It is agreed with the remote firmware that the receiver
>> +        * will clear the STAT register indicating it is ready to
>> +        * receive next data - NOTE_RX_DONE
>> +        */
>
> This note could be added as how this mailbox works in general and
> it's not just Rx right ? Even Tx done is based on this logic.
> Basically the logic is valid on both directions.
>
Yes that is a protocol level agreement. Different f/w may behave differently.


>
>> +       writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static bool mhu_last_tx_done(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +       u32 val;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       spin_lock_irqsave(&mlink->lock, flags);
>
>
> Why do we need this extra locks here ? mailbox core maintains
> per channel lock and uses it for at-least send_data IIRC. And this
> function is just reading status do we really need the lock ?
>
> I must be missing something here else  IMO we can get rid of this
> extra locks in here.
>
Yeah, probably unnecessary.

>> +       /* See NOTE_RX_DONE */
>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       return (val == 0);
>> +}
>> +
>> +static int mhu_send_data(struct mbox_chan *chan, void *data)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       if (!mhu_last_tx_done(chan)) {
>> +               pr_err("%s:%d Shouldn't have seen the day!\n",
>> +                      __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>> +
>> +       spin_lock_irqsave(&mlink->lock, flags);
>> +       writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       return 0;
>> +}
>> +
>> +static int mhu_startup(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +       u32 val;
>> +       int ret;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       spin_lock_irqsave(&mlink->lock, flags);
>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>> +                         IRQF_SHARED, "mhu_link", chan);
>
>
> Just a thought: Can this be threaded_irq instead ?
> Can move request_irq to probe instead esp. if threaded_irq ?
> That provides some flexibility to client's rx_callback.
>
This is controller driver, and can not know which client want
rx_callback in hard-irq context and which in thread_fn context.
Otherwise, request_irq() does evaluate to request_threaded_irq(), if
thats what you mean.
 There might be use-cases like (diagnostic or other) data transfer
over mailbox where we don't wanna increase latency, so we have to
provide a rx_callback in hard-irq context.

>
>> +       if (unlikely(ret)) {
>> +               pr_err("Unable to aquire IRQ\n");
>> +               return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void mhu_shutdown(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       free_irq(mlink->irq, chan);
>> +}
>> +
>> +static struct mbox_chan_ops mhu_ops = {
>> +       .send_data = mhu_send_data,
>> +       .startup = mhu_startup,
>> +       .shutdown = mhu_shutdown,
>> +       .last_tx_done = mhu_last_tx_done,
>> +};
>> +
>> +static int f_mhu_probe(struct platform_device *pdev)
>> +{
>> +       int i, err;
>> +       struct f_mhu *mhu;
>> +       struct resource *res;
>> +       int mhu_reg[3] = {0x0, 0x20, 0x200};
>
>
> Probably this gets simplified when you remove secure channel access ?
>
we don't remove secure channel :)

>
>> +
>> +       /* Allocate memory for device */
>> +       mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
>> +       if (!mhu) {
>> +               dev_err(&pdev->dev, "failed to allocate memory.\n");
>> +               return -EBUSY;
>> +       }
>> +
>> +       mhu->clk = clk_get(&pdev->dev, "clk");
>> +       if (unlikely(IS_ERR(mhu->clk))) {
>> +               dev_err(&pdev->dev, "unable to init clock\n");
>
>
> Don't bail out if there's no clock specified in DT. Clock might not
> be a hard requirement.
>
Hmm.. OK.

>
>> +               kfree(mhu);
>> +               return -EINVAL;
>> +       }
>> +       clk_prepare_enable(mhu->clk);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       mhu->base = ioremap(res->start, resource_size(res));
>> +       if (!mhu->base) {
>> +               dev_err(&pdev->dev, "ioremap failed.\n");
>> +               kfree(mhu);
>> +               return -EBUSY;
>> +       }
>> +
>> +       /* Let UnTrustedOS's access violations don't bother us */
>> +       writel_relaxed(0, mhu->base + MHU_SCFG);
>> +
>
>
> Please don't do this. It can't work in non-secure mode. The firmware running
> with secure access needs to configure this appropriately.
>
ok.. pls see below.

> I might be missing to see, is there a binding document for this mhu ?
>
Yeah we need one.

>
>> +       for (i = 0; i < 3; i++) {
>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>> +               spin_lock_init(&mhu->mlink[i].lock);
>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>> +               mhu->mlink[i].irq = res->start;
>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>> +       }
>> +
>> +       mhu->mbox.dev = &pdev->dev;
>> +       mhu->mbox.chans = &mhu->chan[0];
>> +       mhu->mbox.num_chans = 3;
>
>
> Change this to 2, we shouldn't expose secular channel here as Linux can't
> access that anyway.
>
On the contrary, I think the device driver code should be able to
manage every resource - secure or non-secure. If we remove secure
channel option, what do we do on some other platform that needs it?
And Linux may not always run in non-secure mode.
 So here we populate all channels and let the clients knows which
channel to use via platform specific details - DT. Please note the
driver doesn't touch any secure resource if client doesn't ask it to
(except SCFG for now, which I think should have some S vs NS DT
binding).

>
>> +       mhu->mbox.ops = &mhu_ops;
>> +       mhu->mbox.txdone_irq = false;
>> +       mhu->mbox.txdone_poll = true;
>> +       mhu->mbox.txpoll_period = 10;
>> +
>> +       platform_set_drvdata(pdev, mhu);
>> +
>> +       err = mbox_controller_register(&mhu->mbox);
>> +       if (err) {
>> +               dev_err(&pdev->dev, "Failed to register mailboxes %d\n",
>> err);
>> +               iounmap(mhu->base);
>> +               kfree(mhu);
>> +       } else {
>> +               dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>
>
> Also to be module you need add remove.
>
Yup, OK.

>
>> +static const struct of_device_id f_mhu_dt_ids[] = {
>> +       { .compatible = "fujitsu,mhu" },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
>> +
>> +static struct platform_driver f_mhu_driver = {
>> +       .driver         = {
>> +               .name   = "f_mhu",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = f_mhu_dt_ids,
>> +       },
>> +       .probe          = f_mhu_probe,
>> +};
>> +
>> +static int __init f_mhu_init(void)
>> +{
>> +       return platform_driver_register(&f_mhu_driver);
>> +}
>> +module_init(f_mhu_init);
>
>
> This can be module_platform_driver instead.
>
OK

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17  6:25           ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17  6:25 UTC (permalink / raw)
  To: linux-arm-kernel

On 16 July 2014 23:07, Sudeep Holla <sudeep.holla@arm.com> wrote:
> Hi Mollie,
>
>
> On 13/07/14 07:32, Mollie Wu wrote:
>>
>> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.
>
>
> And it looks like this is not Fujitsu proprietary MHU, it's exactly same IP
> on JUNO(ARM64 development platform from ARM [1]). I was not sure it's
> standard
> IP used on other SoCs too, I too wrote similar driver :(.
>
Same here :)

> Can you please confirm this by reading Peripheral ID(PID: 0xFD0, 0xFE0 -
> 0xFEC) and Component ID(COMPID: 0xFF0 - 0xFFC). Are they
>
> PID  - 0x04 0x98 0xB0 0x1B 0x00
> COMPID - 0x0D 0xF0 0x05 0xB1
>
> If so we have do s/f_mhu/arm_mhu/g :)
>
Yup, we have to.

>
>> It has three channels - LowPri-NonSecure, HighPri-NonSecure and Secure.
>> The MB86S7x communicates over the HighPri-NonSecure channel.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
>> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
>> ---
>>   drivers/mailbox/Kconfig  |   7 ++
>>   drivers/mailbox/Makefile |   2 +
>>   drivers/mailbox/f_mhu.c  | 227
>> +++++++++++++++++++++++++++++++++++++++++++++++
>>   3 files changed, 236 insertions(+)
>>   create mode 100644 drivers/mailbox/f_mhu.c
>>
>> diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig
>> index c8b5c13..681aac2 100644
>> --- a/drivers/mailbox/Kconfig
>> +++ b/drivers/mailbox/Kconfig
>> @@ -6,6 +6,13 @@ menuconfig MAILBOX
>>           signals. Say Y if your platform supports hardware mailboxes.
>>
>>   if MAILBOX
>> +
>> +config MBOX_F_MHU
>> +       bool
>
>
> On Juno, there's nothing that prevents me from compiling this as module.
>
On S7x, even built-in is not early enough for our purposes. But yes,
now that we have company it should be made tristate.

>> +       depends on ARCH_MB86S7X
>
>
> Definitely not a requirement
>
It was, until we found each other ;)

>
>> +       help
>> +         Say Y here if you want to use the F_MHU IPCM support.
>> +
>
>
> Also it needs some description.
>
OK


>> +#define INTR_STAT_OFS  0x0
>> +#define INTR_SET_OFS   0x8
>> +#define INTR_CLR_OFS   0x10
>> +
>> +#define MHU_SCFG       0x400
>> +
>
>
> Remove this.(secure access only register)
>
See later.

>
>> +struct mhu_link {
>> +       unsigned irq;
>> +       spinlock_t lock; /* channel regs */
>> +       void __iomem *tx_reg;
>> +       void __iomem *rx_reg;
>> +};
>> +
>> +struct f_mhu {
>> +       void __iomem *base;
>> +       struct clk *clk;
>> +       struct mhu_link mlink[3];
>> +       struct mbox_chan chan[3];
>> +       struct mbox_controller mbox;
>> +};
>> +
>> +static irqreturn_t mhu_rx_interrupt(int irq, void *p)
>> +{
>> +       struct mbox_chan *chan = (struct mbox_chan *)p;
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>
>
> You don't need explicit cast from void pointers
>
Yup

>
>> +       u32 val;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>
>
> Please remove all these debug prints.
>
These are as good as absent unless DEBUG is defined.

>
>> +       /* See NOTE_RX_DONE */
>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>> +       mbox_chan_received_data(chan, (void *)val);
>> +
>> +       /*
>> +        * It is agreed with the remote firmware that the receiver
>> +        * will clear the STAT register indicating it is ready to
>> +        * receive next data - NOTE_RX_DONE
>> +        */
>
> This note could be added as how this mailbox works in general and
> it's not just Rx right ? Even Tx done is based on this logic.
> Basically the logic is valid on both directions.
>
Yes that is a protocol level agreement. Different f/w may behave differently.


>
>> +       writel_relaxed(val, mlink->rx_reg + INTR_CLR_OFS);
>> +
>> +       return IRQ_HANDLED;
>> +}
>> +
>> +static bool mhu_last_tx_done(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +       u32 val;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       spin_lock_irqsave(&mlink->lock, flags);
>
>
> Why do we need this extra locks here ? mailbox core maintains
> per channel lock and uses it for at-least send_data IIRC. And this
> function is just reading status do we really need the lock ?
>
> I must be missing something here else  IMO we can get rid of this
> extra locks in here.
>
Yeah, probably unnecessary.

>> +       /* See NOTE_RX_DONE */
>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       return (val == 0);
>> +}
>> +
>> +static int mhu_send_data(struct mbox_chan *chan, void *data)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       if (!mhu_last_tx_done(chan)) {
>> +               pr_err("%s:%d Shouldn't have seen the day!\n",
>> +                      __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>> +
>> +       spin_lock_irqsave(&mlink->lock, flags);
>> +       writel_relaxed((u32)data, mlink->tx_reg + INTR_SET_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       return 0;
>> +}
>> +
>> +static int mhu_startup(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +       unsigned long flags;
>> +       u32 val;
>> +       int ret;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       spin_lock_irqsave(&mlink->lock, flags);
>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>> +
>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>> +                         IRQF_SHARED, "mhu_link", chan);
>
>
> Just a thought: Can this be threaded_irq instead ?
> Can move request_irq to probe instead esp. if threaded_irq ?
> That provides some flexibility to client's rx_callback.
>
This is controller driver, and can not know which client want
rx_callback in hard-irq context and which in thread_fn context.
Otherwise, request_irq() does evaluate to request_threaded_irq(), if
thats what you mean.
 There might be use-cases like (diagnostic or other) data transfer
over mailbox where we don't wanna increase latency, so we have to
provide a rx_callback in hard-irq context.

>
>> +       if (unlikely(ret)) {
>> +               pr_err("Unable to aquire IRQ\n");
>> +               return ret;
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void mhu_shutdown(struct mbox_chan *chan)
>> +{
>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>> +
>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>> +       free_irq(mlink->irq, chan);
>> +}
>> +
>> +static struct mbox_chan_ops mhu_ops = {
>> +       .send_data = mhu_send_data,
>> +       .startup = mhu_startup,
>> +       .shutdown = mhu_shutdown,
>> +       .last_tx_done = mhu_last_tx_done,
>> +};
>> +
>> +static int f_mhu_probe(struct platform_device *pdev)
>> +{
>> +       int i, err;
>> +       struct f_mhu *mhu;
>> +       struct resource *res;
>> +       int mhu_reg[3] = {0x0, 0x20, 0x200};
>
>
> Probably this gets simplified when you remove secure channel access ?
>
we don't remove secure channel :)

>
>> +
>> +       /* Allocate memory for device */
>> +       mhu = kzalloc(sizeof(*mhu), GFP_KERNEL);
>> +       if (!mhu) {
>> +               dev_err(&pdev->dev, "failed to allocate memory.\n");
>> +               return -EBUSY;
>> +       }
>> +
>> +       mhu->clk = clk_get(&pdev->dev, "clk");
>> +       if (unlikely(IS_ERR(mhu->clk))) {
>> +               dev_err(&pdev->dev, "unable to init clock\n");
>
>
> Don't bail out if there's no clock specified in DT. Clock might not
> be a hard requirement.
>
Hmm.. OK.

>
>> +               kfree(mhu);
>> +               return -EINVAL;
>> +       }
>> +       clk_prepare_enable(mhu->clk);
>> +
>> +       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
>> +       mhu->base = ioremap(res->start, resource_size(res));
>> +       if (!mhu->base) {
>> +               dev_err(&pdev->dev, "ioremap failed.\n");
>> +               kfree(mhu);
>> +               return -EBUSY;
>> +       }
>> +
>> +       /* Let UnTrustedOS's access violations don't bother us */
>> +       writel_relaxed(0, mhu->base + MHU_SCFG);
>> +
>
>
> Please don't do this. It can't work in non-secure mode. The firmware running
> with secure access needs to configure this appropriately.
>
ok.. pls see below.

> I might be missing to see, is there a binding document for this mhu ?
>
Yeah we need one.

>
>> +       for (i = 0; i < 3; i++) {
>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>> +               spin_lock_init(&mhu->mlink[i].lock);
>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>> +               mhu->mlink[i].irq = res->start;
>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>> +       }
>> +
>> +       mhu->mbox.dev = &pdev->dev;
>> +       mhu->mbox.chans = &mhu->chan[0];
>> +       mhu->mbox.num_chans = 3;
>
>
> Change this to 2, we shouldn't expose secular channel here as Linux can't
> access that anyway.
>
On the contrary, I think the device driver code should be able to
manage every resource - secure or non-secure. If we remove secure
channel option, what do we do on some other platform that needs it?
And Linux may not always run in non-secure mode.
 So here we populate all channels and let the clients knows which
channel to use via platform specific details - DT. Please note the
driver doesn't touch any secure resource if client doesn't ask it to
(except SCFG for now, which I think should have some S vs NS DT
binding).

>
>> +       mhu->mbox.ops = &mhu_ops;
>> +       mhu->mbox.txdone_irq = false;
>> +       mhu->mbox.txdone_poll = true;
>> +       mhu->mbox.txpoll_period = 10;
>> +
>> +       platform_set_drvdata(pdev, mhu);
>> +
>> +       err = mbox_controller_register(&mhu->mbox);
>> +       if (err) {
>> +               dev_err(&pdev->dev, "Failed to register mailboxes %d\n",
>> err);
>> +               iounmap(mhu->base);
>> +               kfree(mhu);
>> +       } else {
>> +               dev_info(&pdev->dev, "Fujitsu MHU Mailbox registered\n");
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>
>
> Also to be module you need add remove.
>
Yup, OK.

>
>> +static const struct of_device_id f_mhu_dt_ids[] = {
>> +       { .compatible = "fujitsu,mhu" },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, f_mhu_dt_ids);
>> +
>> +static struct platform_driver f_mhu_driver = {
>> +       .driver         = {
>> +               .name   = "f_mhu",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = f_mhu_dt_ids,
>> +       },
>> +       .probe          = f_mhu_probe,
>> +};
>> +
>> +static int __init f_mhu_init(void)
>> +{
>> +       return platform_driver_register(&f_mhu_driver);
>> +}
>> +module_init(f_mhu_init);
>
>
> This can be module_platform_driver instead.
>
OK

Thanks
-Jassi

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17  6:25           ` Jassi Brar
@ 2014-07-17 10:31             ` Sudeep Holla
  -1 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 10:31 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Sudeep Holla, Mollie Wu,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi



On 17/07/14 07:25, Jassi Brar wrote:
> On 16 July 2014 23:07, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>> Hi Mollie,
>>
>>
>> On 13/07/14 07:32, Mollie Wu wrote:
>>>
>>> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.

[...]

>>
>>> +       u32 val;
>>> +
>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>
>>
>> Please remove all these debug prints.
>>
> These are as good as absent unless DEBUG is defined.
>

Yes, but with mailbox framework, the controller driver(esp. this one)is
almost like a shim layer. Instead of each driver adding these debugs,
IMO it would be good to have these in the framework.

>>
>>> +       /* See NOTE_RX_DONE */
>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>> +       mbox_chan_received_data(chan, (void *)val);
>>> +
>>> +       /*
>>> +        * It is agreed with the remote firmware that the receiver
>>> +        * will clear the STAT register indicating it is ready to
>>> +        * receive next data - NOTE_RX_DONE
>>> +        */
>>
>> This note could be added as how this mailbox works in general and
>> it's not just Rx right ? Even Tx done is based on this logic.
>> Basically the logic is valid on both directions.
>>
> Yes that is a protocol level agreement. Different f/w may behave differently.
>

Ok, I am not sure if that's entirely true because the MHU spec says it 
drives
the signal using a 32-bit register, with all 32 bits logically ORed 
together.
Usually only Rx signals are wired to interrupts and Tx needs to be polled
but that's entirely implementation choice I believe.

More over if it's protocol level agreement it should not belong here :)

>>> +static int mhu_startup(struct mbox_chan *chan)
>>> +{
>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>> +       unsigned long flags;
>>> +       u32 val;
>>> +       int ret;
>>> +
>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>> +
>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>> +                         IRQF_SHARED, "mhu_link", chan);
>>
>>
>> Just a thought: Can this be threaded_irq instead ?
>> Can move request_irq to probe instead esp. if threaded_irq ?
>> That provides some flexibility to client's rx_callback.
>>
> This is controller driver, and can not know which client want
> rx_callback in hard-irq context and which in thread_fn context.
> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
> thats what you mean.

Agreed but on contrary since MHU involves external firmware(running
on different processor) which might have it's own latency, I prefer
threaded over hard irq. And yes request_irq does evaluate to
request_threaded_irq but with thread_fn = NULL which is not what I want.

>   There might be use-cases like (diagnostic or other) data transfer
> over mailbox where we don't wanna increase latency, so we have to
> provide a rx_callback in hard-irq context.
>

OK

>>
>>> +       for (i = 0; i < 3; i++) {
>>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>>> +               spin_lock_init(&mhu->mlink[i].lock);
>>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>>> +               mhu->mlink[i].irq = res->start;
>>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>>> +       }
>>> +
>>> +       mhu->mbox.dev = &pdev->dev;
>>> +       mhu->mbox.chans = &mhu->chan[0];
>>> +       mhu->mbox.num_chans = 3;
>>
>>
>> Change this to 2, we shouldn't expose secular channel here as Linux can't
>> access that anyway.
>>
> On the contrary, I think the device driver code should be able to
> manage every resource - secure or non-secure. If we remove secure
> channel option, what do we do on some other platform that needs it?
> And Linux may not always run in non-secure mode.

In general secure accesses are avoided these days in Linux as we have no
way to identify it. I agree there are few place where we do access.
Even though I don't like you have secure channel access in Linux, you
have valid reasons. In case you decide to support it, there is some
restrictions in bit 31, though RAZ/WI you need to notify that to protocol
if it tries to access it ?

>   So here we populate all channels and let the clients knows which
> channel to use via platform specific details - DT. Please note the
> driver doesn't touch any secure resource if client doesn't ask it to
> (except SCFG for now, which I think should have some S vs NS DT
> binding).
>

Not sure of this though. We need feedback from DT maintainers.

Regards,
Sudeep

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17 10:31             ` Sudeep Holla
  0 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 10:31 UTC (permalink / raw)
  To: linux-arm-kernel



On 17/07/14 07:25, Jassi Brar wrote:
> On 16 July 2014 23:07, Sudeep Holla <sudeep.holla@arm.com> wrote:
>> Hi Mollie,
>>
>>
>> On 13/07/14 07:32, Mollie Wu wrote:
>>>
>>> Add driver for the proprietary Mailbox controller (f_mhu) in MB86S7x.

[...]

>>
>>> +       u32 val;
>>> +
>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>
>>
>> Please remove all these debug prints.
>>
> These are as good as absent unless DEBUG is defined.
>

Yes, but with mailbox framework, the controller driver(esp. this one)is
almost like a shim layer. Instead of each driver adding these debugs,
IMO it would be good to have these in the framework.

>>
>>> +       /* See NOTE_RX_DONE */
>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>> +       mbox_chan_received_data(chan, (void *)val);
>>> +
>>> +       /*
>>> +        * It is agreed with the remote firmware that the receiver
>>> +        * will clear the STAT register indicating it is ready to
>>> +        * receive next data - NOTE_RX_DONE
>>> +        */
>>
>> This note could be added as how this mailbox works in general and
>> it's not just Rx right ? Even Tx done is based on this logic.
>> Basically the logic is valid on both directions.
>>
> Yes that is a protocol level agreement. Different f/w may behave differently.
>

Ok, I am not sure if that's entirely true because the MHU spec says it 
drives
the signal using a 32-bit register, with all 32 bits logically ORed 
together.
Usually only Rx signals are wired to interrupts and Tx needs to be polled
but that's entirely implementation choice I believe.

More over if it's protocol level agreement it should not belong here :)

>>> +static int mhu_startup(struct mbox_chan *chan)
>>> +{
>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>> +       unsigned long flags;
>>> +       u32 val;
>>> +       int ret;
>>> +
>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>> +
>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>> +                         IRQF_SHARED, "mhu_link", chan);
>>
>>
>> Just a thought: Can this be threaded_irq instead ?
>> Can move request_irq to probe instead esp. if threaded_irq ?
>> That provides some flexibility to client's rx_callback.
>>
> This is controller driver, and can not know which client want
> rx_callback in hard-irq context and which in thread_fn context.
> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
> thats what you mean.

Agreed but on contrary since MHU involves external firmware(running
on different processor) which might have it's own latency, I prefer
threaded over hard irq. And yes request_irq does evaluate to
request_threaded_irq but with thread_fn = NULL which is not what I want.

>   There might be use-cases like (diagnostic or other) data transfer
> over mailbox where we don't wanna increase latency, so we have to
> provide a rx_callback in hard-irq context.
>

OK

>>
>>> +       for (i = 0; i < 3; i++) {
>>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>>> +               spin_lock_init(&mhu->mlink[i].lock);
>>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>>> +               mhu->mlink[i].irq = res->start;
>>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>>> +       }
>>> +
>>> +       mhu->mbox.dev = &pdev->dev;
>>> +       mhu->mbox.chans = &mhu->chan[0];
>>> +       mhu->mbox.num_chans = 3;
>>
>>
>> Change this to 2, we shouldn't expose secular channel here as Linux can't
>> access that anyway.
>>
> On the contrary, I think the device driver code should be able to
> manage every resource - secure or non-secure. If we remove secure
> channel option, what do we do on some other platform that needs it?
> And Linux may not always run in non-secure mode.

In general secure accesses are avoided these days in Linux as we have no
way to identify it. I agree there are few place where we do access.
Even though I don't like you have secure channel access in Linux, you
have valid reasons. In case you decide to support it, there is some
restrictions in bit 31, though RAZ/WI you need to notify that to protocol
if it tries to access it ?

>   So here we populate all channels and let the clients knows which
> channel to use via platform specific details - DT. Please note the
> driver doesn't touch any secure resource if client doesn't ask it to
> (except SCFG for now, which I think should have some S vs NS DT
> binding).
>

Not sure of this though. We need feedback from DT maintainers.

Regards,
Sudeep

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17 10:31             ` Sudeep Holla
@ 2014-07-17 12:56                 ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 12:56 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi

On 17 July 2014 16:01, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
> On 17/07/14 07:25, Jassi Brar wrote:
>>
>>>
>>>> +       u32 val;
>>>> +
>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>
>>>
>>>
>>> Please remove all these debug prints.
>>>
>> These are as good as absent unless DEBUG is defined.
>>
>
> Yes, but with mailbox framework, the controller driver(esp. this one)is
> almost like a shim layer. Instead of each driver adding these debugs,
> IMO it would be good to have these in the framework.
>
OK

>>>
>>>> +       /* See NOTE_RX_DONE */
>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>> +
>>>> +       /*
>>>> +        * It is agreed with the remote firmware that the receiver
>>>> +        * will clear the STAT register indicating it is ready to
>>>> +        * receive next data - NOTE_RX_DONE
>>>> +        */
>>>
>>>
>>> This note could be added as how this mailbox works in general and
>>> it's not just Rx right ? Even Tx done is based on this logic.
>>> Basically the logic is valid on both directions.
>>>
>> Yes that is a protocol level agreement. Different f/w may behave
>> differently.
>>
>
> Ok, I am not sure if that's entirely true because the MHU spec says it
> drives
> the signal using a 32-bit register, with all 32 bits logically ORed
> together.
> Usually only Rx signals are wired to interrupts and Tx needs to be polled
> but that's entirely implementation choice I believe.
>
On my platform, _STAT register only carries the command code but
actual data is exchanged via SharedMemory/SHM. Now we need to know
when the sent data packet (in SHM) has been consumed by the remote -
for which our protocol mandates the remote clear the TX_STAT register.
And vice-versa for RX.

 Some other f/w may choose some other mechanism for TX-Done - say some
ACK bit set or even some time bound guarantee.

> More over if it's protocol level agreement it should not belong here :)
>
My f/w expects the RX_STAT cleared after reading data from SHM. Where
do you think is the right place to clear RX_STAT?

I have said many times in many threads... its the mailbox controller
_and_ the remote f/w that should be seen as one entity. It may not be
possible to write a controller driver that works with any remote
firmware.

>
>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>> +{
>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>> +       unsigned long flags;
>>>> +       u32 val;
>>>> +       int ret;
>>>> +
>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>> +
>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>
>>>
>>>
>>> Just a thought: Can this be threaded_irq instead ?
>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>> That provides some flexibility to client's rx_callback.
>>>
>> This is controller driver, and can not know which client want
>> rx_callback in hard-irq context and which in thread_fn context.
>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>> thats what you mean.
>
> Agreed but on contrary since MHU involves external firmware(running
> on different processor) which might have it's own latency, I prefer
> threaded over hard irq. And yes request_irq does evaluate to
> request_threaded_irq but with thread_fn = NULL which is not what I want.
>
If remote has its own latency, that does not mean we add some more :)

>>   There might be use-cases like (diagnostic or other) data transfer
>> over mailbox where we don't wanna increase latency, so we have to
>> provide a rx_callback in hard-irq context.
>>
> OK
>
Cool.

>
>>>
>>>> +       for (i = 0; i < 3; i++) {
>>>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>>>> +               spin_lock_init(&mhu->mlink[i].lock);
>>>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>>>> +               mhu->mlink[i].irq = res->start;
>>>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>>>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>>>> +       }
>>>> +
>>>> +       mhu->mbox.dev = &pdev->dev;
>>>> +       mhu->mbox.chans = &mhu->chan[0];
>>>> +       mhu->mbox.num_chans = 3;
>>>
>>>
>>>
>>> Change this to 2, we shouldn't expose secular channel here as Linux can't
>>> access that anyway.
>>>
>> On the contrary, I think the device driver code should be able to
>> manage every resource - secure or non-secure. If we remove secure
>> channel option, what do we do on some other platform that needs it?
>> And Linux may not always run in non-secure mode.
>
>
> In general secure accesses are avoided these days in Linux as we have no
> way to identify it. I agree there are few place where we do access.
> Even though I don't like you have secure channel access in Linux, you
> have valid reasons. In case you decide to support it, there is some
> restrictions in bit 31, though RAZ/WI you need to notify that to protocol
> if it tries to access it ?
>
>
>>   So here we populate all channels and let the clients knows which
>> channel to use via platform specific details - DT. Please note the
>> driver doesn't touch any secure resource if client doesn't ask it to
>> (except SCFG for now, which I think should have some S vs NS DT
>> binding).
>>
>
> Not sure of this though. We need feedback from DT maintainers.
>
Yup.


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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17 12:56                 ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 12:56 UTC (permalink / raw)
  To: linux-arm-kernel

On 17 July 2014 16:01, Sudeep Holla <sudeep.holla@arm.com> wrote:
> On 17/07/14 07:25, Jassi Brar wrote:
>>
>>>
>>>> +       u32 val;
>>>> +
>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>
>>>
>>>
>>> Please remove all these debug prints.
>>>
>> These are as good as absent unless DEBUG is defined.
>>
>
> Yes, but with mailbox framework, the controller driver(esp. this one)is
> almost like a shim layer. Instead of each driver adding these debugs,
> IMO it would be good to have these in the framework.
>
OK

>>>
>>>> +       /* See NOTE_RX_DONE */
>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>> +
>>>> +       /*
>>>> +        * It is agreed with the remote firmware that the receiver
>>>> +        * will clear the STAT register indicating it is ready to
>>>> +        * receive next data - NOTE_RX_DONE
>>>> +        */
>>>
>>>
>>> This note could be added as how this mailbox works in general and
>>> it's not just Rx right ? Even Tx done is based on this logic.
>>> Basically the logic is valid on both directions.
>>>
>> Yes that is a protocol level agreement. Different f/w may behave
>> differently.
>>
>
> Ok, I am not sure if that's entirely true because the MHU spec says it
> drives
> the signal using a 32-bit register, with all 32 bits logically ORed
> together.
> Usually only Rx signals are wired to interrupts and Tx needs to be polled
> but that's entirely implementation choice I believe.
>
On my platform, _STAT register only carries the command code but
actual data is exchanged via SharedMemory/SHM. Now we need to know
when the sent data packet (in SHM) has been consumed by the remote -
for which our protocol mandates the remote clear the TX_STAT register.
And vice-versa for RX.

 Some other f/w may choose some other mechanism for TX-Done - say some
ACK bit set or even some time bound guarantee.

> More over if it's protocol level agreement it should not belong here :)
>
My f/w expects the RX_STAT cleared after reading data from SHM. Where
do you think is the right place to clear RX_STAT?

I have said many times in many threads... its the mailbox controller
_and_ the remote f/w that should be seen as one entity. It may not be
possible to write a controller driver that works with any remote
firmware.

>
>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>> +{
>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>> +       unsigned long flags;
>>>> +       u32 val;
>>>> +       int ret;
>>>> +
>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>> +
>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>
>>>
>>>
>>> Just a thought: Can this be threaded_irq instead ?
>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>> That provides some flexibility to client's rx_callback.
>>>
>> This is controller driver, and can not know which client want
>> rx_callback in hard-irq context and which in thread_fn context.
>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>> thats what you mean.
>
> Agreed but on contrary since MHU involves external firmware(running
> on different processor) which might have it's own latency, I prefer
> threaded over hard irq. And yes request_irq does evaluate to
> request_threaded_irq but with thread_fn = NULL which is not what I want.
>
If remote has its own latency, that does not mean we add some more :)

>>   There might be use-cases like (diagnostic or other) data transfer
>> over mailbox where we don't wanna increase latency, so we have to
>> provide a rx_callback in hard-irq context.
>>
> OK
>
Cool.

>
>>>
>>>> +       for (i = 0; i < 3; i++) {
>>>> +               mhu->chan[i].con_priv = &mhu->mlink[i];
>>>> +               spin_lock_init(&mhu->mlink[i].lock);
>>>> +               res = platform_get_resource(pdev, IORESOURCE_IRQ, i);
>>>> +               mhu->mlink[i].irq = res->start;
>>>> +               mhu->mlink[i].rx_reg = mhu->base + mhu_reg[i];
>>>> +               mhu->mlink[i].tx_reg = mhu->mlink[i].rx_reg + 0x100;
>>>> +       }
>>>> +
>>>> +       mhu->mbox.dev = &pdev->dev;
>>>> +       mhu->mbox.chans = &mhu->chan[0];
>>>> +       mhu->mbox.num_chans = 3;
>>>
>>>
>>>
>>> Change this to 2, we shouldn't expose secular channel here as Linux can't
>>> access that anyway.
>>>
>> On the contrary, I think the device driver code should be able to
>> manage every resource - secure or non-secure. If we remove secure
>> channel option, what do we do on some other platform that needs it?
>> And Linux may not always run in non-secure mode.
>
>
> In general secure accesses are avoided these days in Linux as we have no
> way to identify it. I agree there are few place where we do access.
> Even though I don't like you have secure channel access in Linux, you
> have valid reasons. In case you decide to support it, there is some
> restrictions in bit 31, though RAZ/WI you need to notify that to protocol
> if it tries to access it ?
>
>
>>   So here we populate all channels and let the clients knows which
>> channel to use via platform specific details - DT. Please note the
>> driver doesn't touch any secure resource if client doesn't ask it to
>> (except SCFG for now, which I think should have some S vs NS DT
>> binding).
>>
>
> Not sure of this though. We need feedback from DT maintainers.
>
Yup.


Cheers,
Jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-15 20:09             ` Arnd Bergmann
@ 2014-07-17 13:32               ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 13:32 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On 16 July 2014 01:39, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:

>> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
>> >> new file mode 100644
>> >> index 0000000..44f5b0c
>> >> --- /dev/null
>> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
>> >> @@ -0,0 +1,18 @@
>> >> +config ARCH_MB86S7X
>> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
>> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
>> >
>> > Why the LPAE dependency? Is none of the RAM reachable by regular
>> > kernels?
>> >
>> Some devices like PCI, LLI need it and the S70-evb has half of its ram
>> above 4GB.
>> Maybe ARM_LPAE should be selected by inclusion of support for those?
>
> No, you can't select ARM_LPAE because that would break machines that
> do not support it in the same configuration.
>
To be clear, I meant selecting LPAE from config options specific to
S70-evb and PCI on S7x.

> Losing half the RAM or PCI should not be a problem, you'd just run
> with reduced functionality. You wouldn't want to do that in practice,
> but it's different from a hard dependency.
>
Sorry I am not sure what you mean. Is it ok as such?

>> >> +
>> >> +bool __init mb86s7x_smp_init_ops(void)
>> >> +{
>> >> +     struct device_node *node;
>> >> +
>> >> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> >> +     if (node && of_device_is_available(node)) {
>> >> +             mcpm_smp_set_ops();
>> >> +             return true;
>> >> +     }
>> >> +
>> >> +     return false;
>> >> +}
>> >
>> > Can you use the CPU_METHOD_OF_DECLARE() macro to set your
>> > SMP ops instead?
>> >
>> CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
>> smp_ops which are statically defined. We have to call
>> mcpm_smp_set_ops() which does the real job.
>
> Hmm, that seems like a hole in the API. Maybe you can come up with
> a solution for it that doesn't take too much effort. It seems the
> way that MCPM was integrated is suboptimal. We don't have too many
> users yet, can you try turning the logic for MCPM around so it fits
> the CPU_METHOD_OF_DECLARE()?
>
We could cook something up, but as Nico suggested a better place to
set mcpm ops is from where we do other other mcpm setup.

>> >> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
>> >> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
>> >> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
>> >> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
>> >> +
>> >> +static LIST_HEAD(free_xfers);
>> >> +static LIST_HEAD(pending_xfers);
>> >> +static DEFINE_SPINLOCK(fsm_lock);
>> >> +static struct mbox_client mhu_cl;
>> >> +static struct mbox_chan *mhu_chan;
>> >> +static DECLARE_WORK(scb_work, try_xfer);
>> >> +static mhu_handler_t handler[MHU_NUM_CMDS];
>> >> +
>> >> +static enum {
>> >> +     MHU_PARK = 0,
>> >> +     MHU_WRR, /* Waiting to get Remote's Reply */
>> >> +     MHU_WRL, /* Waiting to send Reply */
>> >> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
>> >> +     MHU_INVLD,
>> >> +} fsm_state;
>> >
>> > Ideally, these should all be part of a per-device data structure
>> > referenced from pdev_get_drvdata().
>> >
>> I saw that coming :)
>> We need this driver as early as when timers are populated and then for
>> secondary CPU power control ... when we don't have any platform_device
>> to hook the data on. And I think it is ok because this is the 'server'
>> driver for the platform which by definition won't have another
>> instance.
>
> Then for now put them into one local structure and pass around the
> pointer where you can but access the local one where it's too early.
>
> The effect will be the same, but it's easier to grasp by someone
> who is used to reading regular device drivers. Also add a comment
> in front of the data structure explaining the reasons for having
> a static copy.
>
OK

>> >> +static void got_data(u32 code)
>> >> +{
>> >> +     struct completion *c = NULL;
>> >> +     mhu_handler_t hndlr = NULL;
>> >> +     unsigned long flags;
>> >> +     int ev;
>> >> +
>> >> +     if (code & RESP_BIT)
>> >> +             ev = EV_RR;
>> >> +     else
>> >> +             ev = EV_RC;
>> >> +
>> >> +     spin_lock_irqsave(&fsm_lock, flags);
>> >> +
>> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> >> +             return;
>> >> +     }
>> >> +     fsm_state = mhu_fsm[fsm_state][ev];
>> >> +
>> >> +     if (code & RESP_BIT) {
>> >> +             c = ax->c;
>> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> >> +             list_move(&ax->node, &free_xfers);
>> >> +             ax = NULL;
>> >> +     } else {
>> >> +             /* Find and dispatch relevant registered handler */
>> >> +             if (code < MHU_NUM_CMDS)
>> >> +                     hndlr = handler[code];
>> >> +             if (!hndlr)
>> >> +                     pr_err("No handler for CMD_%u\n", code);
>> >> +     }
>> >> +
>> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> >> +
>> >> +     if (hndlr)
>> >> +             hndlr(code, cmd_from_scb);
>> >> +     if (c)
>> >> +             complete(c);
>> >> +}
>> >> +
>> >> +static void mhu_recv(struct mbox_client *cl, void *data)
>> >> +{
>> >> +     got_data((u32)data);
>> >> +     schedule_work(&scb_work);
>> >> +}
>> >
>> > Why the cast between integer and pointer?
>> >
>> The common mailbox framework passes around pointers to data packets.
>> Its completely between controller and client drivers to decide the
>> format of the packet. In my case the packet is a simple u32 value.
>
> I don't think the mailbox framework should allow that much
> flexibility, because that would break portable drivers that
> rely on a particular behavior from the mailbox provider.
>
> I understand that your driver is not portable to another mailbox
> provider, but it still seems like a mistake in the API that should
> better be fixed sooner than later.
>
Here is what I remember of many discussions on the point over the last year :)
o  We want to support zero-copy receives, which implies the mailbox
framework must simply forward the pointer to "data packet" from
controller to client driver.
o  We could not think of a generic enough structure for 'data packet'
that could fit all protocols. So we assume the data packet format is
an explicit understanding between the controller(+remote) and the
client driver.

Or did I miss your point?


>> >> +                     do {
>> >> +retry:
>> >> +                             /* Wait until we get reply */
>> >> +                             count = 0x1000000;
>> >> +                             do {
>> >> +                                     cpu_relax();
>> >> +                                     val = readl_relaxed(
>> >> +                                             rx_reg + INTR_STAT_OFS);
>> >> +                             } while (--count && !val);
>> >> +
>> >> +                             if (!val) {
>> >> +                                     pr_err("%s:%d SCB didn't reply\n",
>> >> +                                            __func__, __LINE__);
>> >> +                                     goto retry;
>> >
>> > It seems like this loop is unbounded and will just print an error every
>> > 16M loops but never give up or re-enable interrupts if called with
>> > interrupts disabled.
>> >
>> > It should probably be changed to either enforce being called with interrupts
>> > enabled so you can call msleep() inbetween, or you should add error-handling
>> > in case the remote side does not reply.
>> >
>> We can do that for completeness but otherwise my platform is as good
>> as dead if the remote doesn't reply for some reason. And there is no
>> fool-proof way to recover the state-machine from a failed
>> communication.
>
> Do you know how long it takes normally? If you can prove that the
> reply normally comes within a few microseconds, you can instead call
> WARN_ON() once after too much time passes, and then continue polling.
>
> The case to avoid here is just accidentally polling for multiple
> milliseconds with interrupts disabled, which would cause a lot of
> problems.
>
>From a few hundred micro-sec for CPU reset, to potentially tens of
milli-sec for some I2C transaction ... yes we do have for I2C over
mailbox! ;)

Probably bailing out of the loop and returning -ETIMEOUT to the
caller, before a WARN(), is the simplest way to die.



>> >> +struct mb86s7x_peri_clk {
>> >> +     u32 payload_size;
>> >> +     u32 cntrlr;
>> >> +     u32 domain;
>> >> +     u32 port;
>> >> +     u32 en;
>> >> +     u64 freqency;
>> >> +} __packed;
>> >
>> > Just mark the last member by itself __packed. I assume you didn't
>> > actually mean to change the alignment of the data structure to one
>> > byte, but just want to say that the last one is misaligned.
>> >
>> This and others, are data packets that are passed between local and
>> remote via SharedMemory. __packed is only meant to specify that these
>> data structures have no holes in them.
>
> That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> already has no padding on any architecture that is supported by Linux.
> The only reason you need the packing here is because the u64 member is
> unaligned. Note that marking the entire structure as packed means that
> accesses are no longer atomic because the compiler may prefer to do them
> one byte at a time, which can break the protocol on the shared memory
> area.
>
We are not worried about the atomic access because the side sending
the data doesn't touch it until the other side indicates it has
consumed it.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-17 13:32               ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 13:32 UTC (permalink / raw)
  To: linux-arm-kernel

On 16 July 2014 01:39, Arnd Bergmann <arnd@arndb.de> wrote:
> On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:

>> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
>> >> new file mode 100644
>> >> index 0000000..44f5b0c
>> >> --- /dev/null
>> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
>> >> @@ -0,0 +1,18 @@
>> >> +config ARCH_MB86S7X
>> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
>> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
>> >
>> > Why the LPAE dependency? Is none of the RAM reachable by regular
>> > kernels?
>> >
>> Some devices like PCI, LLI need it and the S70-evb has half of its ram
>> above 4GB.
>> Maybe ARM_LPAE should be selected by inclusion of support for those?
>
> No, you can't select ARM_LPAE because that would break machines that
> do not support it in the same configuration.
>
To be clear, I meant selecting LPAE from config options specific to
S70-evb and PCI on S7x.

> Losing half the RAM or PCI should not be a problem, you'd just run
> with reduced functionality. You wouldn't want to do that in practice,
> but it's different from a hard dependency.
>
Sorry I am not sure what you mean. Is it ok as such?

>> >> +
>> >> +bool __init mb86s7x_smp_init_ops(void)
>> >> +{
>> >> +     struct device_node *node;
>> >> +
>> >> +     node = of_find_compatible_node(NULL, NULL, "arm,cci-400");
>> >> +     if (node && of_device_is_available(node)) {
>> >> +             mcpm_smp_set_ops();
>> >> +             return true;
>> >> +     }
>> >> +
>> >> +     return false;
>> >> +}
>> >
>> > Can you use the CPU_METHOD_OF_DECLARE() macro to set your
>> > SMP ops instead?
>> >
>> CPU_METHOD_OF_DECLARE() directly takes smp_ops but here we use mcpm's
>> smp_ops which are statically defined. We have to call
>> mcpm_smp_set_ops() which does the real job.
>
> Hmm, that seems like a hole in the API. Maybe you can come up with
> a solution for it that doesn't take too much effort. It seems the
> way that MCPM was integrated is suboptimal. We don't have too many
> users yet, can you try turning the logic for MCPM around so it fits
> the CPU_METHOD_OF_DECLARE()?
>
We could cook something up, but as Nico suggested a better place to
set mcpm ops is from where we do other other mcpm setup.

>> >> +static void __iomem *cmd_from_scb = MB86S7X_SHM_FROM_SCB_VIRT;
>> >> +static void __iomem *rsp_from_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x100;
>> >> +static void __iomem *cmd_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x200;
>> >> +static void __iomem *rsp_to_scb = MB86S7X_SHM_FROM_SCB_VIRT + 0x300;
>> >> +
>> >> +static LIST_HEAD(free_xfers);
>> >> +static LIST_HEAD(pending_xfers);
>> >> +static DEFINE_SPINLOCK(fsm_lock);
>> >> +static struct mbox_client mhu_cl;
>> >> +static struct mbox_chan *mhu_chan;
>> >> +static DECLARE_WORK(scb_work, try_xfer);
>> >> +static mhu_handler_t handler[MHU_NUM_CMDS];
>> >> +
>> >> +static enum {
>> >> +     MHU_PARK = 0,
>> >> +     MHU_WRR, /* Waiting to get Remote's Reply */
>> >> +     MHU_WRL, /* Waiting to send Reply */
>> >> +     MHU_WRRL, /* WAIT_Ra && WAIT_Rb */
>> >> +     MHU_INVLD,
>> >> +} fsm_state;
>> >
>> > Ideally, these should all be part of a per-device data structure
>> > referenced from pdev_get_drvdata().
>> >
>> I saw that coming :)
>> We need this driver as early as when timers are populated and then for
>> secondary CPU power control ... when we don't have any platform_device
>> to hook the data on. And I think it is ok because this is the 'server'
>> driver for the platform which by definition won't have another
>> instance.
>
> Then for now put them into one local structure and pass around the
> pointer where you can but access the local one where it's too early.
>
> The effect will be the same, but it's easier to grasp by someone
> who is used to reading regular device drivers. Also add a comment
> in front of the data structure explaining the reasons for having
> a static copy.
>
OK

>> >> +static void got_data(u32 code)
>> >> +{
>> >> +     struct completion *c = NULL;
>> >> +     mhu_handler_t hndlr = NULL;
>> >> +     unsigned long flags;
>> >> +     int ev;
>> >> +
>> >> +     if (code & RESP_BIT)
>> >> +             ev = EV_RR;
>> >> +     else
>> >> +             ev = EV_RC;
>> >> +
>> >> +     spin_lock_irqsave(&fsm_lock, flags);
>> >> +
>> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> >> +             return;
>> >> +     }
>> >> +     fsm_state = mhu_fsm[fsm_state][ev];
>> >> +
>> >> +     if (code & RESP_BIT) {
>> >> +             c = ax->c;
>> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> >> +             list_move(&ax->node, &free_xfers);
>> >> +             ax = NULL;
>> >> +     } else {
>> >> +             /* Find and dispatch relevant registered handler */
>> >> +             if (code < MHU_NUM_CMDS)
>> >> +                     hndlr = handler[code];
>> >> +             if (!hndlr)
>> >> +                     pr_err("No handler for CMD_%u\n", code);
>> >> +     }
>> >> +
>> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> >> +
>> >> +     if (hndlr)
>> >> +             hndlr(code, cmd_from_scb);
>> >> +     if (c)
>> >> +             complete(c);
>> >> +}
>> >> +
>> >> +static void mhu_recv(struct mbox_client *cl, void *data)
>> >> +{
>> >> +     got_data((u32)data);
>> >> +     schedule_work(&scb_work);
>> >> +}
>> >
>> > Why the cast between integer and pointer?
>> >
>> The common mailbox framework passes around pointers to data packets.
>> Its completely between controller and client drivers to decide the
>> format of the packet. In my case the packet is a simple u32 value.
>
> I don't think the mailbox framework should allow that much
> flexibility, because that would break portable drivers that
> rely on a particular behavior from the mailbox provider.
>
> I understand that your driver is not portable to another mailbox
> provider, but it still seems like a mistake in the API that should
> better be fixed sooner than later.
>
Here is what I remember of many discussions on the point over the last year :)
o  We want to support zero-copy receives, which implies the mailbox
framework must simply forward the pointer to "data packet" from
controller to client driver.
o  We could not think of a generic enough structure for 'data packet'
that could fit all protocols. So we assume the data packet format is
an explicit understanding between the controller(+remote) and the
client driver.

Or did I miss your point?


>> >> +                     do {
>> >> +retry:
>> >> +                             /* Wait until we get reply */
>> >> +                             count = 0x1000000;
>> >> +                             do {
>> >> +                                     cpu_relax();
>> >> +                                     val = readl_relaxed(
>> >> +                                             rx_reg + INTR_STAT_OFS);
>> >> +                             } while (--count && !val);
>> >> +
>> >> +                             if (!val) {
>> >> +                                     pr_err("%s:%d SCB didn't reply\n",
>> >> +                                            __func__, __LINE__);
>> >> +                                     goto retry;
>> >
>> > It seems like this loop is unbounded and will just print an error every
>> > 16M loops but never give up or re-enable interrupts if called with
>> > interrupts disabled.
>> >
>> > It should probably be changed to either enforce being called with interrupts
>> > enabled so you can call msleep() inbetween, or you should add error-handling
>> > in case the remote side does not reply.
>> >
>> We can do that for completeness but otherwise my platform is as good
>> as dead if the remote doesn't reply for some reason. And there is no
>> fool-proof way to recover the state-machine from a failed
>> communication.
>
> Do you know how long it takes normally? If you can prove that the
> reply normally comes within a few microseconds, you can instead call
> WARN_ON() once after too much time passes, and then continue polling.
>
> The case to avoid here is just accidentally polling for multiple
> milliseconds with interrupts disabled, which would cause a lot of
> problems.
>
>From a few hundred micro-sec for CPU reset, to potentially tens of
milli-sec for some I2C transaction ... yes we do have for I2C over
mailbox! ;)

Probably bailing out of the loop and returning -ETIMEOUT to the
caller, before a WARN(), is the simplest way to die.



>> >> +struct mb86s7x_peri_clk {
>> >> +     u32 payload_size;
>> >> +     u32 cntrlr;
>> >> +     u32 domain;
>> >> +     u32 port;
>> >> +     u32 en;
>> >> +     u64 freqency;
>> >> +} __packed;
>> >
>> > Just mark the last member by itself __packed. I assume you didn't
>> > actually mean to change the alignment of the data structure to one
>> > byte, but just want to say that the last one is misaligned.
>> >
>> This and others, are data packets that are passed between local and
>> remote via SharedMemory. __packed is only meant to specify that these
>> data structures have no holes in them.
>
> That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> already has no padding on any architecture that is supported by Linux.
> The only reason you need the packing here is because the u64 member is
> unaligned. Note that marking the entire structure as packed means that
> accesses are no longer atomic because the compiler may prefer to do them
> one byte at a time, which can break the protocol on the shared memory
> area.
>
We are not worried about the atomic access because the side sending
the data doesn't touch it until the other side indicates it has
consumed it.

thanks
jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-17 13:32               ` Jassi Brar
@ 2014-07-17 13:48                   ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-17 13:48 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:
> On 16 July 2014 01:39, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> > On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:
> 
> >> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> >> >> new file mode 100644
> >> >> index 0000000..44f5b0c
> >> >> --- /dev/null
> >> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
> >> >> @@ -0,0 +1,18 @@
> >> >> +config ARCH_MB86S7X
> >> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> >> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> >> >
> >> > Why the LPAE dependency? Is none of the RAM reachable by regular
> >> > kernels?
> >> >
> >> Some devices like PCI, LLI need it and the S70-evb has half of its ram
> >> above 4GB.
> >> Maybe ARM_LPAE should be selected by inclusion of support for those?
> >
> > No, you can't select ARM_LPAE because that would break machines that
> > do not support it in the same configuration.
> >
> To be clear, I meant selecting LPAE from config options specific to
> S70-evb and PCI on S7x.

I understood that.

> > Losing half the RAM or PCI should not be a problem, you'd just run
> > with reduced functionality. You wouldn't want to do that in practice,
> > but it's different from a hard dependency.
> >
> Sorry I am not sure what you mean. Is it ok as such?

What I mean is that it should be ok to drop the dependency, but you
can't add a 'select ARM_LPAE'. The memory should be handled correctly
already (you will just not see the upper 2GB), while the PCI host
driver should probably be checked for its error handling so it
prints a meaningful error message when it can't reach its MMIO space.


> >> >> +static void got_data(u32 code)
> >> >> +{
> >> >> +     struct completion *c = NULL;
> >> >> +     mhu_handler_t hndlr = NULL;
> >> >> +     unsigned long flags;
> >> >> +     int ev;
> >> >> +
> >> >> +     if (code & RESP_BIT)
> >> >> +             ev = EV_RR;
> >> >> +     else
> >> >> +             ev = EV_RC;
> >> >> +
> >> >> +     spin_lock_irqsave(&fsm_lock, flags);
> >> >> +
> >> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> >> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
> >> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> >> >> +             return;
> >> >> +     }
> >> >> +     fsm_state = mhu_fsm[fsm_state][ev];
> >> >> +
> >> >> +     if (code & RESP_BIT) {
> >> >> +             c = ax->c;
> >> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> >> >> +             list_move(&ax->node, &free_xfers);
> >> >> +             ax = NULL;
> >> >> +     } else {
> >> >> +             /* Find and dispatch relevant registered handler */
> >> >> +             if (code < MHU_NUM_CMDS)
> >> >> +                     hndlr = handler[code];
> >> >> +             if (!hndlr)
> >> >> +                     pr_err("No handler for CMD_%u\n", code);
> >> >> +     }
> >> >> +
> >> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
> >> >> +
> >> >> +     if (hndlr)
> >> >> +             hndlr(code, cmd_from_scb);
> >> >> +     if (c)
> >> >> +             complete(c);
> >> >> +}
> >> >> +
> >> >> +static void mhu_recv(struct mbox_client *cl, void *data)
> >> >> +{
> >> >> +     got_data((u32)data);
> >> >> +     schedule_work(&scb_work);
> >> >> +}
> >> >
> >> > Why the cast between integer and pointer?
> >> >
> >> The common mailbox framework passes around pointers to data packets.
> >> Its completely between controller and client drivers to decide the
> >> format of the packet. In my case the packet is a simple u32 value.
> >
> > I don't think the mailbox framework should allow that much
> > flexibility, because that would break portable drivers that
> > rely on a particular behavior from the mailbox provider.
> >
> > I understand that your driver is not portable to another mailbox
> > provider, but it still seems like a mistake in the API that should
> > better be fixed sooner than later.
> >
> Here is what I remember of many discussions on the point over the last year :)
> o  We want to support zero-copy receives, which implies the mailbox
> framework must simply forward the pointer to "data packet" from
> controller to client driver.
> o  We could not think of a generic enough structure for 'data packet'
> that could fit all protocols. So we assume the data packet format is
> an explicit understanding between the controller(+remote) and the
> client driver.
> 
> Or did I miss your point?

Forwarding a pointer is ok, but then I think you should not overload
the 'void *data' argument. In the case above, you don't actually use
a pointer at all, but instead you use a 'code' that is taken as
an index into an array or a flag.

This is particularly broken when you think about 64-bit machines
on which you cannot cast a pointer to an u32.

What I think you should do is define your own data type for the
mailbox, which would be specific to your mailbox client and change
the code above to

struct mhu_mbox_message {
	u32 code; /* another client could store a pointer here */
};

static void mhu_recv(struct mbox_client *cl, void *data)
{
	struct mhu_mbox_message *msg = data;

	got_data(msg->code);
	schedule_work(&scp_work);
}

You get the cost of another register load here, but that should
be almost free.

There should probably also be some way to ensure that the amount
of data passed in the structure matches between mbox provider
and client.

> >> >> +                     do {
> >> >> +retry:
> >> >> +                             /* Wait until we get reply */
> >> >> +                             count = 0x1000000;
> >> >> +                             do {
> >> >> +                                     cpu_relax();
> >> >> +                                     val = readl_relaxed(
> >> >> +                                             rx_reg + INTR_STAT_OFS);
> >> >> +                             } while (--count && !val);
> >> >> +
> >> >> +                             if (!val) {
> >> >> +                                     pr_err("%s:%d SCB didn't reply\n",
> >> >> +                                            __func__, __LINE__);
> >> >> +                                     goto retry;
> >> >
> >> > It seems like this loop is unbounded and will just print an error every
> >> > 16M loops but never give up or re-enable interrupts if called with
> >> > interrupts disabled.
> >> >
> >> > It should probably be changed to either enforce being called with interrupts
> >> > enabled so you can call msleep() inbetween, or you should add error-handling
> >> > in case the remote side does not reply.
> >> >
> >> We can do that for completeness but otherwise my platform is as good
> >> as dead if the remote doesn't reply for some reason. And there is no
> >> fool-proof way to recover the state-machine from a failed
> >> communication.
> >
> > Do you know how long it takes normally? If you can prove that the
> > reply normally comes within a few microseconds, you can instead call
> > WARN_ON() once after too much time passes, and then continue polling.
> >
> > The case to avoid here is just accidentally polling for multiple
> > milliseconds with interrupts disabled, which would cause a lot of
> > problems.
> >
> From a few hundred micro-sec for CPU reset, to potentially tens of
> milli-sec for some I2C transaction ... yes we do have for I2C over
> mailbox! ;)
> 
> Probably bailing out of the loop and returning -ETIMEOUT to the
> caller, before a WARN(), is the simplest way to die.

If you can have multiple miliseconds here, I think the code should
be changed to run in non-atomic context and use msleep(1) or
usleep_range() as a back-off. Is that possible?

> >> >> +struct mb86s7x_peri_clk {
> >> >> +     u32 payload_size;
> >> >> +     u32 cntrlr;
> >> >> +     u32 domain;
> >> >> +     u32 port;
> >> >> +     u32 en;
> >> >> +     u64 freqency;
> >> >> +} __packed;
> >> >
> >> > Just mark the last member by itself __packed. I assume you didn't
> >> > actually mean to change the alignment of the data structure to one
> >> > byte, but just want to say that the last one is misaligned.
> >> >
> >> This and others, are data packets that are passed between local and
> >> remote via SharedMemory. __packed is only meant to specify that these
> >> data structures have no holes in them.
> >
> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> > already has no padding on any architecture that is supported by Linux.
> > The only reason you need the packing here is because the u64 member is
> > unaligned. Note that marking the entire structure as packed means that
> > accesses are no longer atomic because the compiler may prefer to do them
> > one byte at a time, which can break the protocol on the shared memory
> > area.
> >
> We are not worried about the atomic access because the side sending
> the data doesn't touch it until the other side indicates it has
> consumed it.

It's still wrong though ;-)
	Arnd
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-17 13:48                   ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-17 13:48 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:
> On 16 July 2014 01:39, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Tuesday 15 July 2014 23:07:58 Jassi Brar wrote:
> 
> >> >> diff --git a/arch/arm/mach-mb86s7x/Kconfig b/arch/arm/mach-mb86s7x/Kconfig
> >> >> new file mode 100644
> >> >> index 0000000..44f5b0c
> >> >> --- /dev/null
> >> >> +++ b/arch/arm/mach-mb86s7x/Kconfig
> >> >> @@ -0,0 +1,18 @@
> >> >> +config ARCH_MB86S7X
> >> >> +     bool "Fujitsu MB86S7x platforms" if (ARCH_MULTI_V7 && ARM_LPAE)
> >> >> +     select ARCH_DMA_ADDR_T_64BIT if ARM_LPAE
> >> >
> >> > Why the LPAE dependency? Is none of the RAM reachable by regular
> >> > kernels?
> >> >
> >> Some devices like PCI, LLI need it and the S70-evb has half of its ram
> >> above 4GB.
> >> Maybe ARM_LPAE should be selected by inclusion of support for those?
> >
> > No, you can't select ARM_LPAE because that would break machines that
> > do not support it in the same configuration.
> >
> To be clear, I meant selecting LPAE from config options specific to
> S70-evb and PCI on S7x.

I understood that.

> > Losing half the RAM or PCI should not be a problem, you'd just run
> > with reduced functionality. You wouldn't want to do that in practice,
> > but it's different from a hard dependency.
> >
> Sorry I am not sure what you mean. Is it ok as such?

What I mean is that it should be ok to drop the dependency, but you
can't add a 'select ARM_LPAE'. The memory should be handled correctly
already (you will just not see the upper 2GB), while the PCI host
driver should probably be checked for its error handling so it
prints a meaningful error message when it can't reach its MMIO space.


> >> >> +static void got_data(u32 code)
> >> >> +{
> >> >> +     struct completion *c = NULL;
> >> >> +     mhu_handler_t hndlr = NULL;
> >> >> +     unsigned long flags;
> >> >> +     int ev;
> >> >> +
> >> >> +     if (code & RESP_BIT)
> >> >> +             ev = EV_RR;
> >> >> +     else
> >> >> +             ev = EV_RC;
> >> >> +
> >> >> +     spin_lock_irqsave(&fsm_lock, flags);
> >> >> +
> >> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
> >> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
> >> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
> >> >> +             return;
> >> >> +     }
> >> >> +     fsm_state = mhu_fsm[fsm_state][ev];
> >> >> +
> >> >> +     if (code & RESP_BIT) {
> >> >> +             c = ax->c;
> >> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
> >> >> +             list_move(&ax->node, &free_xfers);
> >> >> +             ax = NULL;
> >> >> +     } else {
> >> >> +             /* Find and dispatch relevant registered handler */
> >> >> +             if (code < MHU_NUM_CMDS)
> >> >> +                     hndlr = handler[code];
> >> >> +             if (!hndlr)
> >> >> +                     pr_err("No handler for CMD_%u\n", code);
> >> >> +     }
> >> >> +
> >> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
> >> >> +
> >> >> +     if (hndlr)
> >> >> +             hndlr(code, cmd_from_scb);
> >> >> +     if (c)
> >> >> +             complete(c);
> >> >> +}
> >> >> +
> >> >> +static void mhu_recv(struct mbox_client *cl, void *data)
> >> >> +{
> >> >> +     got_data((u32)data);
> >> >> +     schedule_work(&scb_work);
> >> >> +}
> >> >
> >> > Why the cast between integer and pointer?
> >> >
> >> The common mailbox framework passes around pointers to data packets.
> >> Its completely between controller and client drivers to decide the
> >> format of the packet. In my case the packet is a simple u32 value.
> >
> > I don't think the mailbox framework should allow that much
> > flexibility, because that would break portable drivers that
> > rely on a particular behavior from the mailbox provider.
> >
> > I understand that your driver is not portable to another mailbox
> > provider, but it still seems like a mistake in the API that should
> > better be fixed sooner than later.
> >
> Here is what I remember of many discussions on the point over the last year :)
> o  We want to support zero-copy receives, which implies the mailbox
> framework must simply forward the pointer to "data packet" from
> controller to client driver.
> o  We could not think of a generic enough structure for 'data packet'
> that could fit all protocols. So we assume the data packet format is
> an explicit understanding between the controller(+remote) and the
> client driver.
> 
> Or did I miss your point?

Forwarding a pointer is ok, but then I think you should not overload
the 'void *data' argument. In the case above, you don't actually use
a pointer at all, but instead you use a 'code' that is taken as
an index into an array or a flag.

This is particularly broken when you think about 64-bit machines
on which you cannot cast a pointer to an u32.

What I think you should do is define your own data type for the
mailbox, which would be specific to your mailbox client and change
the code above to

struct mhu_mbox_message {
	u32 code; /* another client could store a pointer here */
};

static void mhu_recv(struct mbox_client *cl, void *data)
{
	struct mhu_mbox_message *msg = data;

	got_data(msg->code);
	schedule_work(&scp_work);
}

You get the cost of another register load here, but that should
be almost free.

There should probably also be some way to ensure that the amount
of data passed in the structure matches between mbox provider
and client.

> >> >> +                     do {
> >> >> +retry:
> >> >> +                             /* Wait until we get reply */
> >> >> +                             count = 0x1000000;
> >> >> +                             do {
> >> >> +                                     cpu_relax();
> >> >> +                                     val = readl_relaxed(
> >> >> +                                             rx_reg + INTR_STAT_OFS);
> >> >> +                             } while (--count && !val);
> >> >> +
> >> >> +                             if (!val) {
> >> >> +                                     pr_err("%s:%d SCB didn't reply\n",
> >> >> +                                            __func__, __LINE__);
> >> >> +                                     goto retry;
> >> >
> >> > It seems like this loop is unbounded and will just print an error every
> >> > 16M loops but never give up or re-enable interrupts if called with
> >> > interrupts disabled.
> >> >
> >> > It should probably be changed to either enforce being called with interrupts
> >> > enabled so you can call msleep() inbetween, or you should add error-handling
> >> > in case the remote side does not reply.
> >> >
> >> We can do that for completeness but otherwise my platform is as good
> >> as dead if the remote doesn't reply for some reason. And there is no
> >> fool-proof way to recover the state-machine from a failed
> >> communication.
> >
> > Do you know how long it takes normally? If you can prove that the
> > reply normally comes within a few microseconds, you can instead call
> > WARN_ON() once after too much time passes, and then continue polling.
> >
> > The case to avoid here is just accidentally polling for multiple
> > milliseconds with interrupts disabled, which would cause a lot of
> > problems.
> >
> From a few hundred micro-sec for CPU reset, to potentially tens of
> milli-sec for some I2C transaction ... yes we do have for I2C over
> mailbox! ;)
> 
> Probably bailing out of the loop and returning -ETIMEOUT to the
> caller, before a WARN(), is the simplest way to die.

If you can have multiple miliseconds here, I think the code should
be changed to run in non-atomic context and use msleep(1) or
usleep_range() as a back-off. Is that possible?

> >> >> +struct mb86s7x_peri_clk {
> >> >> +     u32 payload_size;
> >> >> +     u32 cntrlr;
> >> >> +     u32 domain;
> >> >> +     u32 port;
> >> >> +     u32 en;
> >> >> +     u64 freqency;
> >> >> +} __packed;
> >> >
> >> > Just mark the last member by itself __packed. I assume you didn't
> >> > actually mean to change the alignment of the data structure to one
> >> > byte, but just want to say that the last one is misaligned.
> >> >
> >> This and others, are data packets that are passed between local and
> >> remote via SharedMemory. __packed is only meant to specify that these
> >> data structures have no holes in them.
> >
> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> > already has no padding on any architecture that is supported by Linux.
> > The only reason you need the packing here is because the u64 member is
> > unaligned. Note that marking the entire structure as packed means that
> > accesses are no longer atomic because the compiler may prefer to do them
> > one byte at a time, which can break the protocol on the shared memory
> > area.
> >
> We are not worried about the atomic access because the side sending
> the data doesn't touch it until the other side indicates it has
> consumed it.

It's still wrong though ;-)
	Arnd

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17 12:56                 ` Jassi Brar
@ 2014-07-17 15:09                     ` Sudeep Holla
  -1 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 15:09 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Sudeep Holla, Mollie Wu,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi



On 17/07/14 13:56, Jassi Brar wrote:
> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>> On 17/07/14 07:25, Jassi Brar wrote:
>>>
>>>>
>>>>> +       u32 val;
>>>>> +
>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>
>>>>
>>>>
>>>> Please remove all these debug prints.
>>>>
>>> These are as good as absent unless DEBUG is defined.
>>>
>>
>> Yes, but with mailbox framework, the controller driver(esp. this one)is
>> almost like a shim layer. Instead of each driver adding these debugs,
>> IMO it would be good to have these in the framework.
>>
> OK
>
>>>>
>>>>> +       /* See NOTE_RX_DONE */
>>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>>> +
>>>>> +       /*
>>>>> +        * It is agreed with the remote firmware that the receiver
>>>>> +        * will clear the STAT register indicating it is ready to
>>>>> +        * receive next data - NOTE_RX_DONE
>>>>> +        */
>>>>
>>>>
>>>> This note could be added as how this mailbox works in general and
>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>> Basically the logic is valid on both directions.
>>>>
>>> Yes that is a protocol level agreement. Different f/w may behave
>>> differently.
>>>
>>
>> Ok, I am not sure if that's entirely true because the MHU spec says it
>> drives
>> the signal using a 32-bit register, with all 32 bits logically ORed
>> together.
>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>> but that's entirely implementation choice I believe.
>>
> On my platform, _STAT register only carries the command code but
> actual data is exchanged via SharedMemory/SHM. Now we need to know
> when the sent data packet (in SHM) has been consumed by the remote -
> for which our protocol mandates the remote clear the TX_STAT register.
> And vice-versa for RX.
>
>   Some other f/w may choose some other mechanism for TX-Done - say some
> ACK bit set or even some time bound guarantee.
>
>> More over if it's protocol level agreement it should not belong here :)
>>
> My f/w expects the RX_STAT cleared after reading data from SHM. Where
> do you think is the right place to clear RX_STAT?
>

I understand that and what I am saying is the MHU is designed like that
and protocol is just using it. There's nothing specific to protocol. Ideally
if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
raises an interrupt to the firmware. In absence of it we need polling that's
what both Linux and firmware does for Tx case.

Even on Juno, it's same. But we should be able to extend it to support Tx
if an implementation supports. Similarly the firmware can make use of the
same when Linux clears Rx(it would be Tx complete/ack for the firmware)

When we need to make this driver work across different firmware, you just
can't rely on the firmware protocol, hence I am asking to drop those
comments.

> I have said many times in many threads... its the mailbox controller
> _and_ the remote f/w that should be seen as one entity. It may not be
> possible to write a controller driver that works with any remote
> firmware.
>

It should be possible if the remote protocol just use the same hardware
feature without any extra software policy at the lowest level(raw Tx and
Rx). I believe that's what we need here if we want this driver to work
on both Juno and your platform. Agree ?

>>
>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>> +{
>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>> +       unsigned long flags;
>>>>> +       u32 val;
>>>>> +       int ret;
>>>>> +
>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>> +
>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>
>>>>
>>>>
>>>> Just a thought: Can this be threaded_irq instead ?
>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>> That provides some flexibility to client's rx_callback.
>>>>
>>> This is controller driver, and can not know which client want
>>> rx_callback in hard-irq context and which in thread_fn context.
>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>> thats what you mean.
>>
>> Agreed but on contrary since MHU involves external firmware(running
>> on different processor) which might have it's own latency, I prefer
>> threaded over hard irq. And yes request_irq does evaluate to
>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>
> If remote has its own latency, that does not mean we add some more :)
>

No what I meant is unless there is a real need to use hard irq, we
should prefer threaded one otherwise. Also by latency I meant what if
the remote firmware misbehaves. In threaded version you have little
more flexibility whereas hard irq is executed with interrupts disabled.
At least the system is responsive and only MHU interrupts are disabled.

Regards,
Sudeep

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17 15:09                     ` Sudeep Holla
  0 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 15:09 UTC (permalink / raw)
  To: linux-arm-kernel



On 17/07/14 13:56, Jassi Brar wrote:
> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla@arm.com> wrote:
>> On 17/07/14 07:25, Jassi Brar wrote:
>>>
>>>>
>>>>> +       u32 val;
>>>>> +
>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>
>>>>
>>>>
>>>> Please remove all these debug prints.
>>>>
>>> These are as good as absent unless DEBUG is defined.
>>>
>>
>> Yes, but with mailbox framework, the controller driver(esp. this one)is
>> almost like a shim layer. Instead of each driver adding these debugs,
>> IMO it would be good to have these in the framework.
>>
> OK
>
>>>>
>>>>> +       /* See NOTE_RX_DONE */
>>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>>> +
>>>>> +       /*
>>>>> +        * It is agreed with the remote firmware that the receiver
>>>>> +        * will clear the STAT register indicating it is ready to
>>>>> +        * receive next data - NOTE_RX_DONE
>>>>> +        */
>>>>
>>>>
>>>> This note could be added as how this mailbox works in general and
>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>> Basically the logic is valid on both directions.
>>>>
>>> Yes that is a protocol level agreement. Different f/w may behave
>>> differently.
>>>
>>
>> Ok, I am not sure if that's entirely true because the MHU spec says it
>> drives
>> the signal using a 32-bit register, with all 32 bits logically ORed
>> together.
>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>> but that's entirely implementation choice I believe.
>>
> On my platform, _STAT register only carries the command code but
> actual data is exchanged via SharedMemory/SHM. Now we need to know
> when the sent data packet (in SHM) has been consumed by the remote -
> for which our protocol mandates the remote clear the TX_STAT register.
> And vice-versa for RX.
>
>   Some other f/w may choose some other mechanism for TX-Done - say some
> ACK bit set or even some time bound guarantee.
>
>> More over if it's protocol level agreement it should not belong here :)
>>
> My f/w expects the RX_STAT cleared after reading data from SHM. Where
> do you think is the right place to clear RX_STAT?
>

I understand that and what I am saying is the MHU is designed like that
and protocol is just using it. There's nothing specific to protocol. Ideally
if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
raises an interrupt to the firmware. In absence of it we need polling that's
what both Linux and firmware does for Tx case.

Even on Juno, it's same. But we should be able to extend it to support Tx
if an implementation supports. Similarly the firmware can make use of the
same when Linux clears Rx(it would be Tx complete/ack for the firmware)

When we need to make this driver work across different firmware, you just
can't rely on the firmware protocol, hence I am asking to drop those
comments.

> I have said many times in many threads... its the mailbox controller
> _and_ the remote f/w that should be seen as one entity. It may not be
> possible to write a controller driver that works with any remote
> firmware.
>

It should be possible if the remote protocol just use the same hardware
feature without any extra software policy at the lowest level(raw Tx and
Rx). I believe that's what we need here if we want this driver to work
on both Juno and your platform. Agree ?

>>
>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>> +{
>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>> +       unsigned long flags;
>>>>> +       u32 val;
>>>>> +       int ret;
>>>>> +
>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>> +
>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>
>>>>
>>>>
>>>> Just a thought: Can this be threaded_irq instead ?
>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>> That provides some flexibility to client's rx_callback.
>>>>
>>> This is controller driver, and can not know which client want
>>> rx_callback in hard-irq context and which in thread_fn context.
>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>> thats what you mean.
>>
>> Agreed but on contrary since MHU involves external firmware(running
>> on different processor) which might have it's own latency, I prefer
>> threaded over hard irq. And yes request_irq does evaluate to
>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>
> If remote has its own latency, that does not mean we add some more :)
>

No what I meant is unless there is a real need to use hard irq, we
should prefer threaded one otherwise. Also by latency I meant what if
the remote firmware misbehaves. In threaded version you have little
more flexibility whereas hard irq is executed with interrupts disabled.
At least the system is responsive and only MHU interrupts are disabled.

Regards,
Sudeep

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-17 13:48                   ` Arnd Bergmann
@ 2014-07-17 16:54                     ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 16:54 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On 17 July 2014 19:18, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:

>
>> > Losing half the RAM or PCI should not be a problem, you'd just run
>> > with reduced functionality. You wouldn't want to do that in practice,
>> > but it's different from a hard dependency.
>> >
>> Sorry I am not sure what you mean. Is it ok as such?
>
> What I mean is that it should be ok to drop the dependency, but you
> can't add a 'select ARM_LPAE'. The memory should be handled correctly
> already (you will just not see the upper 2GB), while the PCI host
> driver should probably be checked for its error handling so it
> prints a meaningful error message when it can't reach its MMIO space.
>
OK, got it.

>
>> >> >> +static void got_data(u32 code)
>> >> >> +{
>> >> >> +     struct completion *c = NULL;
>> >> >> +     mhu_handler_t hndlr = NULL;
>> >> >> +     unsigned long flags;
>> >> >> +     int ev;
>> >> >> +
>> >> >> +     if (code & RESP_BIT)
>> >> >> +             ev = EV_RR;
>> >> >> +     else
>> >> >> +             ev = EV_RC;
>> >> >> +
>> >> >> +     spin_lock_irqsave(&fsm_lock, flags);
>> >> >> +
>> >> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> >> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> >> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> >> >> +             return;
>> >> >> +     }
>> >> >> +     fsm_state = mhu_fsm[fsm_state][ev];
>> >> >> +
>> >> >> +     if (code & RESP_BIT) {
>> >> >> +             c = ax->c;
>> >> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> >> >> +             list_move(&ax->node, &free_xfers);
>> >> >> +             ax = NULL;
>> >> >> +     } else {
>> >> >> +             /* Find and dispatch relevant registered handler */
>> >> >> +             if (code < MHU_NUM_CMDS)
>> >> >> +                     hndlr = handler[code];
>> >> >> +             if (!hndlr)
>> >> >> +                     pr_err("No handler for CMD_%u\n", code);
>> >> >> +     }
>> >> >> +
>> >> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> >> >> +
>> >> >> +     if (hndlr)
>> >> >> +             hndlr(code, cmd_from_scb);
>> >> >> +     if (c)
>> >> >> +             complete(c);
>> >> >> +}
>> >> >> +
>> >> >> +static void mhu_recv(struct mbox_client *cl, void *data)
>> >> >> +{
>> >> >> +     got_data((u32)data);
>> >> >> +     schedule_work(&scb_work);
>> >> >> +}
>> >> >
>> >> > Why the cast between integer and pointer?
>> >> >
>> >> The common mailbox framework passes around pointers to data packets.
>> >> Its completely between controller and client drivers to decide the
>> >> format of the packet. In my case the packet is a simple u32 value.
>> >
>> > I don't think the mailbox framework should allow that much
>> > flexibility, because that would break portable drivers that
>> > rely on a particular behavior from the mailbox provider.
>> >
>> > I understand that your driver is not portable to another mailbox
>> > provider, but it still seems like a mistake in the API that should
>> > better be fixed sooner than later.
>> >
>> Here is what I remember of many discussions on the point over the last year :)
>> o  We want to support zero-copy receives, which implies the mailbox
>> framework must simply forward the pointer to "data packet" from
>> controller to client driver.
>> o  We could not think of a generic enough structure for 'data packet'
>> that could fit all protocols. So we assume the data packet format is
>> an explicit understanding between the controller(+remote) and the
>> client driver.
>>
>> Or did I miss your point?
>
> Forwarding a pointer is ok, but then I think you should not overload
> the 'void *data' argument. In the case above, you don't actually use
> a pointer at all, but instead you use a 'code' that is taken as
> an index into an array or a flag.
>
> This is particularly broken when you think about 64-bit machines
> on which you cannot cast a pointer to an u32.
>
> What I think you should do is define your own data type for the
> mailbox, which would be specific to your mailbox client and change
> the code above to
>
> struct mhu_mbox_message {
>         u32 code; /* another client could store a pointer here */
> };
>
> static void mhu_recv(struct mbox_client *cl, void *data)
> {
>         struct mhu_mbox_message *msg = data;
>
>         got_data(msg->code);
>         schedule_work(&scp_work);
> }
>
> You get the cost of another register load here, but that should
> be almost free.
>
> There should probably also be some way to ensure that the amount
> of data passed in the structure matches between mbox provider
> and client.
>
Ah ok, I am relieved its only for controller-client, and not for the api :)
Yes I will use mhu_mbox_message as you suggest.

>> >> >> +                     do {
>> >> >> +retry:
>> >> >> +                             /* Wait until we get reply */
>> >> >> +                             count = 0x1000000;
>> >> >> +                             do {
>> >> >> +                                     cpu_relax();
>> >> >> +                                     val = readl_relaxed(
>> >> >> +                                             rx_reg + INTR_STAT_OFS);
>> >> >> +                             } while (--count && !val);
>> >> >> +
>> >> >> +                             if (!val) {
>> >> >> +                                     pr_err("%s:%d SCB didn't reply\n",
>> >> >> +                                            __func__, __LINE__);
>> >> >> +                                     goto retry;
>> >> >
>> >> > It seems like this loop is unbounded and will just print an error every
>> >> > 16M loops but never give up or re-enable interrupts if called with
>> >> > interrupts disabled.
>> >> >
>> >> > It should probably be changed to either enforce being called with interrupts
>> >> > enabled so you can call msleep() inbetween, or you should add error-handling
>> >> > in case the remote side does not reply.
>> >> >
>> >> We can do that for completeness but otherwise my platform is as good
>> >> as dead if the remote doesn't reply for some reason. And there is no
>> >> fool-proof way to recover the state-machine from a failed
>> >> communication.
>> >
>> > Do you know how long it takes normally? If you can prove that the
>> > reply normally comes within a few microseconds, you can instead call
>> > WARN_ON() once after too much time passes, and then continue polling.
>> >
>> > The case to avoid here is just accidentally polling for multiple
>> > milliseconds with interrupts disabled, which would cause a lot of
>> > problems.
>> >
>> From a few hundred micro-sec for CPU reset, to potentially tens of
>> milli-sec for some I2C transaction ... yes we do have for I2C over
>> mailbox! ;)
>>
>> Probably bailing out of the loop and returning -ETIMEOUT to the
>> caller, before a WARN(), is the simplest way to die.
>
> If you can have multiple miliseconds here, I think the code should
> be changed to run in non-atomic context and use msleep(1) or
> usleep_range() as a back-off. Is that possible?
>
I don't think we could sleep there but that should be ok because the
code is used only when we don't have mailbox framework ready i.e, in
very early boot before timers are ready and also as late as during
reboot/poweroff. Also I realize long delays like those for I2C would
never use this code - they would always have mailbox usable during
their lifetime.


>> >> >> +struct mb86s7x_peri_clk {
>> >> >> +     u32 payload_size;
>> >> >> +     u32 cntrlr;
>> >> >> +     u32 domain;
>> >> >> +     u32 port;
>> >> >> +     u32 en;
>> >> >> +     u64 freqency;
>> >> >> +} __packed;
>> >> >
>> >> > Just mark the last member by itself __packed. I assume you didn't
>> >> > actually mean to change the alignment of the data structure to one
>> >> > byte, but just want to say that the last one is misaligned.
>> >> >
>> >> This and others, are data packets that are passed between local and
>> >> remote via SharedMemory. __packed is only meant to specify that these
>> >> data structures have no holes in them.
>> >
>> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
>> > already has no padding on any architecture that is supported by Linux.
>> > The only reason you need the packing here is because the u64 member is
>> > unaligned. Note that marking the entire structure as packed means that
>> > accesses are no longer atomic because the compiler may prefer to do them
>> > one byte at a time, which can break the protocol on the shared memory
>> > area.
>> >
>> We are not worried about the atomic access because the side sending
>> the data doesn't touch it until the other side indicates it has
>> consumed it.
>
> It's still wrong though ;-)
>
The remote f/w expects data to be contiguous and we can't assume how
it reads the packet. So our firstmost priority is to have no holes in
the region... like, say, USB descriptors.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-17 16:54                     ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 16:54 UTC (permalink / raw)
  To: linux-arm-kernel

On 17 July 2014 19:18, Arnd Bergmann <arnd@arndb.de> wrote:
> On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:

>
>> > Losing half the RAM or PCI should not be a problem, you'd just run
>> > with reduced functionality. You wouldn't want to do that in practice,
>> > but it's different from a hard dependency.
>> >
>> Sorry I am not sure what you mean. Is it ok as such?
>
> What I mean is that it should be ok to drop the dependency, but you
> can't add a 'select ARM_LPAE'. The memory should be handled correctly
> already (you will just not see the upper 2GB), while the PCI host
> driver should probably be checked for its error handling so it
> prints a meaningful error message when it can't reach its MMIO space.
>
OK, got it.

>
>> >> >> +static void got_data(u32 code)
>> >> >> +{
>> >> >> +     struct completion *c = NULL;
>> >> >> +     mhu_handler_t hndlr = NULL;
>> >> >> +     unsigned long flags;
>> >> >> +     int ev;
>> >> >> +
>> >> >> +     if (code & RESP_BIT)
>> >> >> +             ev = EV_RR;
>> >> >> +     else
>> >> >> +             ev = EV_RC;
>> >> >> +
>> >> >> +     spin_lock_irqsave(&fsm_lock, flags);
>> >> >> +
>> >> >> +     if (mhu_fsm[fsm_state][ev] == MHU_INVLD) {
>> >> >> +             spin_unlock_irqrestore(&fsm_lock, flags);
>> >> >> +             pr_err("State-%d EV-%d FSM Broken!\n", fsm_state, ev);
>> >> >> +             return;
>> >> >> +     }
>> >> >> +     fsm_state = mhu_fsm[fsm_state][ev];
>> >> >> +
>> >> >> +     if (code & RESP_BIT) {
>> >> >> +             c = ax->c;
>> >> >> +             memcpy_fromio(ax->buf, rsp_from_scb, ax->len);
>> >> >> +             list_move(&ax->node, &free_xfers);
>> >> >> +             ax = NULL;
>> >> >> +     } else {
>> >> >> +             /* Find and dispatch relevant registered handler */
>> >> >> +             if (code < MHU_NUM_CMDS)
>> >> >> +                     hndlr = handler[code];
>> >> >> +             if (!hndlr)
>> >> >> +                     pr_err("No handler for CMD_%u\n", code);
>> >> >> +     }
>> >> >> +
>> >> >> +     spin_unlock_irqrestore(&fsm_lock, flags);
>> >> >> +
>> >> >> +     if (hndlr)
>> >> >> +             hndlr(code, cmd_from_scb);
>> >> >> +     if (c)
>> >> >> +             complete(c);
>> >> >> +}
>> >> >> +
>> >> >> +static void mhu_recv(struct mbox_client *cl, void *data)
>> >> >> +{
>> >> >> +     got_data((u32)data);
>> >> >> +     schedule_work(&scb_work);
>> >> >> +}
>> >> >
>> >> > Why the cast between integer and pointer?
>> >> >
>> >> The common mailbox framework passes around pointers to data packets.
>> >> Its completely between controller and client drivers to decide the
>> >> format of the packet. In my case the packet is a simple u32 value.
>> >
>> > I don't think the mailbox framework should allow that much
>> > flexibility, because that would break portable drivers that
>> > rely on a particular behavior from the mailbox provider.
>> >
>> > I understand that your driver is not portable to another mailbox
>> > provider, but it still seems like a mistake in the API that should
>> > better be fixed sooner than later.
>> >
>> Here is what I remember of many discussions on the point over the last year :)
>> o  We want to support zero-copy receives, which implies the mailbox
>> framework must simply forward the pointer to "data packet" from
>> controller to client driver.
>> o  We could not think of a generic enough structure for 'data packet'
>> that could fit all protocols. So we assume the data packet format is
>> an explicit understanding between the controller(+remote) and the
>> client driver.
>>
>> Or did I miss your point?
>
> Forwarding a pointer is ok, but then I think you should not overload
> the 'void *data' argument. In the case above, you don't actually use
> a pointer at all, but instead you use a 'code' that is taken as
> an index into an array or a flag.
>
> This is particularly broken when you think about 64-bit machines
> on which you cannot cast a pointer to an u32.
>
> What I think you should do is define your own data type for the
> mailbox, which would be specific to your mailbox client and change
> the code above to
>
> struct mhu_mbox_message {
>         u32 code; /* another client could store a pointer here */
> };
>
> static void mhu_recv(struct mbox_client *cl, void *data)
> {
>         struct mhu_mbox_message *msg = data;
>
>         got_data(msg->code);
>         schedule_work(&scp_work);
> }
>
> You get the cost of another register load here, but that should
> be almost free.
>
> There should probably also be some way to ensure that the amount
> of data passed in the structure matches between mbox provider
> and client.
>
Ah ok, I am relieved its only for controller-client, and not for the api :)
Yes I will use mhu_mbox_message as you suggest.

>> >> >> +                     do {
>> >> >> +retry:
>> >> >> +                             /* Wait until we get reply */
>> >> >> +                             count = 0x1000000;
>> >> >> +                             do {
>> >> >> +                                     cpu_relax();
>> >> >> +                                     val = readl_relaxed(
>> >> >> +                                             rx_reg + INTR_STAT_OFS);
>> >> >> +                             } while (--count && !val);
>> >> >> +
>> >> >> +                             if (!val) {
>> >> >> +                                     pr_err("%s:%d SCB didn't reply\n",
>> >> >> +                                            __func__, __LINE__);
>> >> >> +                                     goto retry;
>> >> >
>> >> > It seems like this loop is unbounded and will just print an error every
>> >> > 16M loops but never give up or re-enable interrupts if called with
>> >> > interrupts disabled.
>> >> >
>> >> > It should probably be changed to either enforce being called with interrupts
>> >> > enabled so you can call msleep() inbetween, or you should add error-handling
>> >> > in case the remote side does not reply.
>> >> >
>> >> We can do that for completeness but otherwise my platform is as good
>> >> as dead if the remote doesn't reply for some reason. And there is no
>> >> fool-proof way to recover the state-machine from a failed
>> >> communication.
>> >
>> > Do you know how long it takes normally? If you can prove that the
>> > reply normally comes within a few microseconds, you can instead call
>> > WARN_ON() once after too much time passes, and then continue polling.
>> >
>> > The case to avoid here is just accidentally polling for multiple
>> > milliseconds with interrupts disabled, which would cause a lot of
>> > problems.
>> >
>> From a few hundred micro-sec for CPU reset, to potentially tens of
>> milli-sec for some I2C transaction ... yes we do have for I2C over
>> mailbox! ;)
>>
>> Probably bailing out of the loop and returning -ETIMEOUT to the
>> caller, before a WARN(), is the simplest way to die.
>
> If you can have multiple miliseconds here, I think the code should
> be changed to run in non-atomic context and use msleep(1) or
> usleep_range() as a back-off. Is that possible?
>
I don't think we could sleep there but that should be ok because the
code is used only when we don't have mailbox framework ready i.e, in
very early boot before timers are ready and also as late as during
reboot/poweroff. Also I realize long delays like those for I2C would
never use this code - they would always have mailbox usable during
their lifetime.


>> >> >> +struct mb86s7x_peri_clk {
>> >> >> +     u32 payload_size;
>> >> >> +     u32 cntrlr;
>> >> >> +     u32 domain;
>> >> >> +     u32 port;
>> >> >> +     u32 en;
>> >> >> +     u64 freqency;
>> >> >> +} __packed;
>> >> >
>> >> > Just mark the last member by itself __packed. I assume you didn't
>> >> > actually mean to change the alignment of the data structure to one
>> >> > byte, but just want to say that the last one is misaligned.
>> >> >
>> >> This and others, are data packets that are passed between local and
>> >> remote via SharedMemory. __packed is only meant to specify that these
>> >> data structures have no holes in them.
>> >
>> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
>> > already has no padding on any architecture that is supported by Linux.
>> > The only reason you need the packing here is because the u64 member is
>> > unaligned. Note that marking the entire structure as packed means that
>> > accesses are no longer atomic because the compiler may prefer to do them
>> > one byte at a time, which can break the protocol on the shared memory
>> > area.
>> >
>> We are not worried about the atomic access because the side sending
>> the data doesn't touch it until the other side indicates it has
>> consumed it.
>
> It's still wrong though ;-)
>
The remote f/w expects data to be contiguous and we can't assume how
it reads the packet. So our firstmost priority is to have no holes in
the region... like, say, USB descriptors.

Thanks for your patience,
-Jassi

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17 15:09                     ` Sudeep Holla
@ 2014-07-17 17:07                         ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 17:07 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi

On 17 July 2014 20:39, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>
>
> On 17/07/14 13:56, Jassi Brar wrote:
>>
>> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>>>
>>> On 17/07/14 07:25, Jassi Brar wrote:
>>>>
>>>>
>>>>>
>>>>>> +       u32 val;
>>>>>> +
>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Please remove all these debug prints.
>>>>>
>>>> These are as good as absent unless DEBUG is defined.
>>>>
>>>
>>> Yes, but with mailbox framework, the controller driver(esp. this one)is
>>> almost like a shim layer. Instead of each driver adding these debugs,
>>> IMO it would be good to have these in the framework.
>>>
>> OK
>>
>>>>>
>>>>>> +       /* See NOTE_RX_DONE */
>>>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>>>> +
>>>>>> +       /*
>>>>>> +        * It is agreed with the remote firmware that the receiver
>>>>>> +        * will clear the STAT register indicating it is ready to
>>>>>> +        * receive next data - NOTE_RX_DONE
>>>>>> +        */
>>>>>
>>>>>
>>>>>
>>>>> This note could be added as how this mailbox works in general and
>>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>>> Basically the logic is valid on both directions.
>>>>>
>>>> Yes that is a protocol level agreement. Different f/w may behave
>>>> differently.
>>>>
>>>
>>> Ok, I am not sure if that's entirely true because the MHU spec says it
>>> drives
>>> the signal using a 32-bit register, with all 32 bits logically ORed
>>> together.
>>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>>> but that's entirely implementation choice I believe.
>>>
>> On my platform, _STAT register only carries the command code but
>> actual data is exchanged via SharedMemory/SHM. Now we need to know
>> when the sent data packet (in SHM) has been consumed by the remote -
>> for which our protocol mandates the remote clear the TX_STAT register.
>> And vice-versa for RX.
>>
>>   Some other f/w may choose some other mechanism for TX-Done - say some
>> ACK bit set or even some time bound guarantee.
>>
>>> More over if it's protocol level agreement it should not belong here :)
>>>
>> My f/w expects the RX_STAT cleared after reading data from SHM. Where
>> do you think is the right place to clear RX_STAT?
>>
>
> I understand that and what I am saying is the MHU is designed like that
> and protocol is just using it. There's nothing specific to protocol. Ideally
> if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
> raises an interrupt to the firmware. In absence of it we need polling that's
> what both Linux and firmware does for Tx case.
>
> Even on Juno, it's same. But we should be able to extend it to support Tx
> if an implementation supports. Similarly the firmware can make use of the
> same when Linux clears Rx(it would be Tx complete/ack for the firmware)
>
> When we need to make this driver work across different firmware, you just
> can't rely on the firmware protocol, hence I am asking to drop those
> comments.
>
OK, I will remove the comment.

>
>> I have said many times in many threads... its the mailbox controller
>> _and_ the remote f/w that should be seen as one entity. It may not be
>> possible to write a controller driver that works with any remote
>> firmware.
>>
>
> It should be possible if the remote protocol just use the same hardware
> feature without any extra software policy at the lowest level(raw Tx and
> Rx).
>
I wouldn't count on f/w always done the right way. And I am speaking
from my whatever first hand experience :D
And sometimes there may just be bugs that need some quirks at controller level.

> I believe that's what we need here if we want this driver to work
> on both Juno and your platform. Agree ?
>
Does this driver not work for Juno?
If no, may I see your driver and the MHU manual (mine is 90% Japanese)?

>
>>>
>>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>>> +{
>>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>>> +       unsigned long flags;
>>>>>> +       u32 val;
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>>> +
>>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Just a thought: Can this be threaded_irq instead ?
>>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>>> That provides some flexibility to client's rx_callback.
>>>>>
>>>> This is controller driver, and can not know which client want
>>>> rx_callback in hard-irq context and which in thread_fn context.
>>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>>> thats what you mean.
>>>
>>>
>>> Agreed but on contrary since MHU involves external firmware(running
>>> on different processor) which might have it's own latency, I prefer
>>> threaded over hard irq. And yes request_irq does evaluate to
>>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>>
>> If remote has its own latency, that does not mean we add some more :)
>>
>
> No what I meant is unless there is a real need to use hard irq, we
> should prefer threaded one otherwise.
>
And how does controller discern a "real need" from a "soft need" to
use hard irq?
Even if the controller driver pushes data up from a threaded function,
the client can't know the context and can't sleep because the
intermediate API says the rx_callback should be assumed to be atomic.
Again, it maybe more efficient if I see your implementation of the
driver and understand your concerns about mine.

> Also by latency I meant what if
> the remote firmware misbehaves. In threaded version you have little
> more flexibility whereas hard irq is executed with interrupts disabled.
> At least the system is responsive and only MHU interrupts are disabled.
>
If the remote firmware misbehaves, that is a bug of the platform. No
mailbox API could/should account for such malfunctions.

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17 17:07                         ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-17 17:07 UTC (permalink / raw)
  To: linux-arm-kernel

On 17 July 2014 20:39, Sudeep Holla <sudeep.holla@arm.com> wrote:
>
>
> On 17/07/14 13:56, Jassi Brar wrote:
>>
>> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>>
>>> On 17/07/14 07:25, Jassi Brar wrote:
>>>>
>>>>
>>>>>
>>>>>> +       u32 val;
>>>>>> +
>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Please remove all these debug prints.
>>>>>
>>>> These are as good as absent unless DEBUG is defined.
>>>>
>>>
>>> Yes, but with mailbox framework, the controller driver(esp. this one)is
>>> almost like a shim layer. Instead of each driver adding these debugs,
>>> IMO it would be good to have these in the framework.
>>>
>> OK
>>
>>>>>
>>>>>> +       /* See NOTE_RX_DONE */
>>>>>> +       val = readl_relaxed(mlink->rx_reg + INTR_STAT_OFS);
>>>>>> +       mbox_chan_received_data(chan, (void *)val);
>>>>>> +
>>>>>> +       /*
>>>>>> +        * It is agreed with the remote firmware that the receiver
>>>>>> +        * will clear the STAT register indicating it is ready to
>>>>>> +        * receive next data - NOTE_RX_DONE
>>>>>> +        */
>>>>>
>>>>>
>>>>>
>>>>> This note could be added as how this mailbox works in general and
>>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>>> Basically the logic is valid on both directions.
>>>>>
>>>> Yes that is a protocol level agreement. Different f/w may behave
>>>> differently.
>>>>
>>>
>>> Ok, I am not sure if that's entirely true because the MHU spec says it
>>> drives
>>> the signal using a 32-bit register, with all 32 bits logically ORed
>>> together.
>>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>>> but that's entirely implementation choice I believe.
>>>
>> On my platform, _STAT register only carries the command code but
>> actual data is exchanged via SharedMemory/SHM. Now we need to know
>> when the sent data packet (in SHM) has been consumed by the remote -
>> for which our protocol mandates the remote clear the TX_STAT register.
>> And vice-versa for RX.
>>
>>   Some other f/w may choose some other mechanism for TX-Done - say some
>> ACK bit set or even some time bound guarantee.
>>
>>> More over if it's protocol level agreement it should not belong here :)
>>>
>> My f/w expects the RX_STAT cleared after reading data from SHM. Where
>> do you think is the right place to clear RX_STAT?
>>
>
> I understand that and what I am saying is the MHU is designed like that
> and protocol is just using it. There's nothing specific to protocol. Ideally
> if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
> raises an interrupt to the firmware. In absence of it we need polling that's
> what both Linux and firmware does for Tx case.
>
> Even on Juno, it's same. But we should be able to extend it to support Tx
> if an implementation supports. Similarly the firmware can make use of the
> same when Linux clears Rx(it would be Tx complete/ack for the firmware)
>
> When we need to make this driver work across different firmware, you just
> can't rely on the firmware protocol, hence I am asking to drop those
> comments.
>
OK, I will remove the comment.

>
>> I have said many times in many threads... its the mailbox controller
>> _and_ the remote f/w that should be seen as one entity. It may not be
>> possible to write a controller driver that works with any remote
>> firmware.
>>
>
> It should be possible if the remote protocol just use the same hardware
> feature without any extra software policy at the lowest level(raw Tx and
> Rx).
>
I wouldn't count on f/w always done the right way. And I am speaking
from my whatever first hand experience :D
And sometimes there may just be bugs that need some quirks at controller level.

> I believe that's what we need here if we want this driver to work
> on both Juno and your platform. Agree ?
>
Does this driver not work for Juno?
If no, may I see your driver and the MHU manual (mine is 90% Japanese)?

>
>>>
>>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>>> +{
>>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>>> +       unsigned long flags;
>>>>>> +       u32 val;
>>>>>> +       int ret;
>>>>>> +
>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>>> +
>>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>>
>>>>>
>>>>>
>>>>>
>>>>> Just a thought: Can this be threaded_irq instead ?
>>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>>> That provides some flexibility to client's rx_callback.
>>>>>
>>>> This is controller driver, and can not know which client want
>>>> rx_callback in hard-irq context and which in thread_fn context.
>>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>>> thats what you mean.
>>>
>>>
>>> Agreed but on contrary since MHU involves external firmware(running
>>> on different processor) which might have it's own latency, I prefer
>>> threaded over hard irq. And yes request_irq does evaluate to
>>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>>
>> If remote has its own latency, that does not mean we add some more :)
>>
>
> No what I meant is unless there is a real need to use hard irq, we
> should prefer threaded one otherwise.
>
And how does controller discern a "real need" from a "soft need" to
use hard irq?
Even if the controller driver pushes data up from a threaded function,
the client can't know the context and can't sleep because the
intermediate API says the rx_callback should be assumed to be atomic.
Again, it maybe more efficient if I see your implementation of the
driver and understand your concerns about mine.

> Also by latency I meant what if
> the remote firmware misbehaves. In threaded version you have little
> more flexibility whereas hard irq is executed with interrupts disabled.
> At least the system is responsive and only MHU interrupts are disabled.
>
If the remote firmware misbehaves, that is a bug of the platform. No
mailbox API could/should account for such malfunctions.

Thanks
Jassi

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

* Re: [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
  2014-07-17 16:54                     ` Jassi Brar
@ 2014-07-17 17:12                       ` Arnd Bergmann
  -1 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-17 17:12 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Mollie Wu, Devicetree List,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r, Andy Green,
	Olof Johansson, Russell King - ARM Linux, Mark Rutland,
	Rob Herring, Pawel Moll, Patch Tracking, Tetsuya Takinishi

On Thursday 17 July 2014 22:24:43 Jassi Brar wrote:
> On 17 July 2014 19:18, Arnd Bergmann <arnd-r2nGTMty4D4@public.gmane.org> wrote:
> > On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:

> >> From a few hundred micro-sec for CPU reset, to potentially tens of
> >> milli-sec for some I2C transaction ... yes we do have for I2C over
> >> mailbox! ;)
> >>
> >> Probably bailing out of the loop and returning -ETIMEOUT to the
> >> caller, before a WARN(), is the simplest way to die.
> >
> > If you can have multiple miliseconds here, I think the code should
> > be changed to run in non-atomic context and use msleep(1) or
> > usleep_range() as a back-off. Is that possible?
> >
> I don't think we could sleep there but that should be ok because the
> code is used only when we don't have mailbox framework ready i.e, in
> very early boot before timers are ready and also as late as during
> reboot/poweroff. Also I realize long delays like those for I2C would
> never use this code - they would always have mailbox usable during
> their lifetime.

Ok, just add a comment then, and a warning if things take too long.

> >> >> >> +struct mb86s7x_peri_clk {
> >> >> >> +     u32 payload_size;
> >> >> >> +     u32 cntrlr;
> >> >> >> +     u32 domain;
> >> >> >> +     u32 port;
> >> >> >> +     u32 en;
> >> >> >> +     u64 freqency;
> >> >> >> +} __packed;
> >> >> >
> >> >> > Just mark the last member by itself __packed. I assume you didn't
> >> >> > actually mean to change the alignment of the data structure to one
> >> >> > byte, but just want to say that the last one is misaligned.
> >> >> >
> >> >> This and others, are data packets that are passed between local and
> >> >> remote via SharedMemory. __packed is only meant to specify that these
> >> >> data structures have no holes in them.
> >> >
> >> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> >> > already has no padding on any architecture that is supported by Linux.
> >> > The only reason you need the packing here is because the u64 member is
> >> > unaligned. Note that marking the entire structure as packed means that
> >> > accesses are no longer atomic because the compiler may prefer to do them
> >> > one byte at a time, which can break the protocol on the shared memory
> >> > area.
> >> >
> >> We are not worried about the atomic access because the side sending
> >> the data doesn't touch it until the other side indicates it has
> >> consumed it.
> >
> > It's still wrong though ;-)
> >
> The remote f/w expects data to be contiguous and we can't assume how
> it reads the packet. So our firstmost priority is to have no holes in
> the region... like, say, USB descriptors.

The structures being contiguous is guaranteed by the ELF ABI, unless
you have unaligned members.

You can be explicit about it, but then you should also provide a
new minimum alignment (e.g. 4 bytes) for the structure to avoid
having the compiler turn everything into bytewise accesses.

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

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

* [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs
@ 2014-07-17 17:12                       ` Arnd Bergmann
  0 siblings, 0 replies; 88+ messages in thread
From: Arnd Bergmann @ 2014-07-17 17:12 UTC (permalink / raw)
  To: linux-arm-kernel

On Thursday 17 July 2014 22:24:43 Jassi Brar wrote:
> On 17 July 2014 19:18, Arnd Bergmann <arnd@arndb.de> wrote:
> > On Thursday 17 July 2014 19:02:53 Jassi Brar wrote:

> >> From a few hundred micro-sec for CPU reset, to potentially tens of
> >> milli-sec for some I2C transaction ... yes we do have for I2C over
> >> mailbox! ;)
> >>
> >> Probably bailing out of the loop and returning -ETIMEOUT to the
> >> caller, before a WARN(), is the simplest way to die.
> >
> > If you can have multiple miliseconds here, I think the code should
> > be changed to run in non-atomic context and use msleep(1) or
> > usleep_range() as a back-off. Is that possible?
> >
> I don't think we could sleep there but that should be ok because the
> code is used only when we don't have mailbox framework ready i.e, in
> very early boot before timers are ready and also as late as during
> reboot/poweroff. Also I realize long delays like those for I2C would
> never use this code - they would always have mailbox usable during
> their lifetime.

Ok, just add a comment then, and a warning if things take too long.

> >> >> >> +struct mb86s7x_peri_clk {
> >> >> >> +     u32 payload_size;
> >> >> >> +     u32 cntrlr;
> >> >> >> +     u32 domain;
> >> >> >> +     u32 port;
> >> >> >> +     u32 en;
> >> >> >> +     u64 freqency;
> >> >> >> +} __packed;
> >> >> >
> >> >> > Just mark the last member by itself __packed. I assume you didn't
> >> >> > actually mean to change the alignment of the data structure to one
> >> >> > byte, but just want to say that the last one is misaligned.
> >> >> >
> >> >> This and others, are data packets that are passed between local and
> >> >> remote via SharedMemory. __packed is only meant to specify that these
> >> >> data structures have no holes in them.
> >> >
> >> > That would be '__packed __attribute__((aligned(4)))'. A struct of 'u32'
> >> > already has no padding on any architecture that is supported by Linux.
> >> > The only reason you need the packing here is because the u64 member is
> >> > unaligned. Note that marking the entire structure as packed means that
> >> > accesses are no longer atomic because the compiler may prefer to do them
> >> > one byte at a time, which can break the protocol on the shared memory
> >> > area.
> >> >
> >> We are not worried about the atomic access because the side sending
> >> the data doesn't touch it until the other side indicates it has
> >> consumed it.
> >
> > It's still wrong though ;-)
> >
> The remote f/w expects data to be contiguous and we can't assume how
> it reads the packet. So our firstmost priority is to have no holes in
> the region... like, say, USB descriptors.

The structures being contiguous is guaranteed by the ELF ABI, unless
you have unaligned members.

You can be explicit about it, but then you should also provide a
new minimum alignment (e.g. 4 bytes) for the structure to avoid
having the compiler turn everything into bytewise accesses.

	Arnd

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17 17:07                         ` Jassi Brar
@ 2014-07-17 18:51                             ` Sudeep Holla
  -1 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 18:51 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Sudeep Holla, Mollie Wu,
	linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi



On 17/07/14 18:07, Jassi Brar wrote:
> On 17 July 2014 20:39, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>>
>>
>> On 17/07/14 13:56, Jassi Brar wrote:
>>>
>>> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
>>>>

[...]

>>>>>> This note could be added as how this mailbox works in general and
>>>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>>>> Basically the logic is valid on both directions.
>>>>>>
>>>>> Yes that is a protocol level agreement. Different f/w may behave
>>>>> differently.
>>>>>
>>>>
>>>> Ok, I am not sure if that's entirely true because the MHU spec says it
>>>> drives
>>>> the signal using a 32-bit register, with all 32 bits logically ORed
>>>> together.
>>>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>>>> but that's entirely implementation choice I believe.
>>>>
>>> On my platform, _STAT register only carries the command code but
>>> actual data is exchanged via SharedMemory/SHM. Now we need to know
>>> when the sent data packet (in SHM) has been consumed by the remote -
>>> for which our protocol mandates the remote clear the TX_STAT register.
>>> And vice-versa for RX.
>>>
>>>    Some other f/w may choose some other mechanism for TX-Done - say some
>>> ACK bit set or even some time bound guarantee.
>>>
>>>> More over if it's protocol level agreement it should not belong here :)
>>>>
>>> My f/w expects the RX_STAT cleared after reading data from SHM. Where
>>> do you think is the right place to clear RX_STAT?
>>>
>>
>> I understand that and what I am saying is the MHU is designed like that
>> and protocol is just using it. There's nothing specific to protocol. Ideally
>> if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
>> raises an interrupt to the firmware. In absence of it we need polling that's
>> what both Linux and firmware does for Tx case.
>>
>> Even on Juno, it's same. But we should be able to extend it to support Tx
>> if an implementation supports. Similarly the firmware can make use of the
>> same when Linux clears Rx(it would be Tx complete/ack for the firmware)
>>
>> When we need to make this driver work across different firmware, you just
>> can't rely on the firmware protocol, hence I am asking to drop those
>> comments.
>>
> OK, I will remove the comment.
>
>>
>>> I have said many times in many threads... its the mailbox controller
>>> _and_ the remote f/w that should be seen as one entity. It may not be
>>> possible to write a controller driver that works with any remote
>>> firmware.
>>>
>>
>> It should be possible if the remote protocol just use the same hardware
>> feature without any extra software policy at the lowest level(raw Tx and
>> Rx).
>>
> I wouldn't count on f/w always done the right way. And I am speaking
> from my whatever first hand experience :D
> And sometimes there may just be bugs that need some quirks at controller level.
>

Agreed, and I too have similar experience. This is exact reason why I am
urging for threaded irq, unless we have real requirement for hard irq.

>> I believe that's what we need here if we want this driver to work
>> on both Juno and your platform. Agree ?
>>
> Does this driver not work for Juno?

I have not yet tried yet. For sure secure access will explode.

> If no, may I see your driver and the MHU manual (mine is 90% Japanese)?
>

It's quite similar to your one expect few comments which I have made.
Here is the public version of Juno spec[1]. Not sure if it covers MHU in
detail.

>>
>>>>
>>>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>>>> +{
>>>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>>>> +       unsigned long flags;
>>>>>>> +       u32 val;
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>>>> +
>>>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Just a thought: Can this be threaded_irq instead ?
>>>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>>>> That provides some flexibility to client's rx_callback.
>>>>>>
>>>>> This is controller driver, and can not know which client want
>>>>> rx_callback in hard-irq context and which in thread_fn context.
>>>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>>>> thats what you mean.
>>>>
>>>>
>>>> Agreed but on contrary since MHU involves external firmware(running
>>>> on different processor) which might have it's own latency, I prefer
>>>> threaded over hard irq. And yes request_irq does evaluate to
>>>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>>>
>>> If remote has its own latency, that does not mean we add some more :)
>>>
>>
>> No what I meant is unless there is a real need to use hard irq, we
>> should prefer threaded one otherwise.
>>
> And how does controller discern a "real need" from a "soft need" to
> use hard irq?
> Even if the controller driver pushes data up from a threaded function,
> the client can't know the context and can't sleep because the
> intermediate API says the rx_callback should be assumed to be atomic.

Yes I am not arguing on that, it should assume atomic and not sleep.
I am saying we can avoid rx_callback in interrupt context if possible.
I will try to look at the protocol implementation tomorrow.

> Again, it maybe more efficient if I see your implementation of the
> driver and understand your concerns about mine.
>

As I said its almost same as this, except I call mbox_chan_received_data
in irq thread context. I prefer enabling other interrupts while copying
payload data.

>> Also by latency I meant what if
>> the remote firmware misbehaves. In threaded version you have little
>> more flexibility whereas hard irq is executed with interrupts disabled.
>> At least the system is responsive and only MHU interrupts are disabled.
>>
> If the remote firmware misbehaves, that is a bug of the platform. No
> mailbox API could/should account for such malfunctions.
>

No I didn't intend for any mailbox API to account it.

Regards,
Sudeep

[1] http://infocenter.arm.com/help/topic/com.arm.doc.dto0038a/index.html

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-17 18:51                             ` Sudeep Holla
  0 siblings, 0 replies; 88+ messages in thread
From: Sudeep Holla @ 2014-07-17 18:51 UTC (permalink / raw)
  To: linux-arm-kernel



On 17/07/14 18:07, Jassi Brar wrote:
> On 17 July 2014 20:39, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>
>>
>> On 17/07/14 13:56, Jassi Brar wrote:
>>>
>>> On 17 July 2014 16:01, Sudeep Holla <sudeep.holla@arm.com> wrote:
>>>>

[...]

>>>>>> This note could be added as how this mailbox works in general and
>>>>>> it's not just Rx right ? Even Tx done is based on this logic.
>>>>>> Basically the logic is valid on both directions.
>>>>>>
>>>>> Yes that is a protocol level agreement. Different f/w may behave
>>>>> differently.
>>>>>
>>>>
>>>> Ok, I am not sure if that's entirely true because the MHU spec says it
>>>> drives
>>>> the signal using a 32-bit register, with all 32 bits logically ORed
>>>> together.
>>>> Usually only Rx signals are wired to interrupts and Tx needs to be polled
>>>> but that's entirely implementation choice I believe.
>>>>
>>> On my platform, _STAT register only carries the command code but
>>> actual data is exchanged via SharedMemory/SHM. Now we need to know
>>> when the sent data packet (in SHM) has been consumed by the remote -
>>> for which our protocol mandates the remote clear the TX_STAT register.
>>> And vice-versa for RX.
>>>
>>>    Some other f/w may choose some other mechanism for TX-Done - say some
>>> ACK bit set or even some time bound guarantee.
>>>
>>>> More over if it's protocol level agreement it should not belong here :)
>>>>
>>> My f/w expects the RX_STAT cleared after reading data from SHM. Where
>>> do you think is the right place to clear RX_STAT?
>>>
>>
>> I understand that and what I am saying is the MHU is designed like that
>> and protocol is just using it. There's nothing specific to protocol. Ideally
>> if an implementation has both Rx and Tx interrupts, the RX_CLEAR from here
>> raises an interrupt to the firmware. In absence of it we need polling that's
>> what both Linux and firmware does for Tx case.
>>
>> Even on Juno, it's same. But we should be able to extend it to support Tx
>> if an implementation supports. Similarly the firmware can make use of the
>> same when Linux clears Rx(it would be Tx complete/ack for the firmware)
>>
>> When we need to make this driver work across different firmware, you just
>> can't rely on the firmware protocol, hence I am asking to drop those
>> comments.
>>
> OK, I will remove the comment.
>
>>
>>> I have said many times in many threads... its the mailbox controller
>>> _and_ the remote f/w that should be seen as one entity. It may not be
>>> possible to write a controller driver that works with any remote
>>> firmware.
>>>
>>
>> It should be possible if the remote protocol just use the same hardware
>> feature without any extra software policy at the lowest level(raw Tx and
>> Rx).
>>
> I wouldn't count on f/w always done the right way. And I am speaking
> from my whatever first hand experience :D
> And sometimes there may just be bugs that need some quirks at controller level.
>

Agreed, and I too have similar experience. This is exact reason why I am
urging for threaded irq, unless we have real requirement for hard irq.

>> I believe that's what we need here if we want this driver to work
>> on both Juno and your platform. Agree ?
>>
> Does this driver not work for Juno?

I have not yet tried yet. For sure secure access will explode.

> If no, may I see your driver and the MHU manual (mine is 90% Japanese)?
>

It's quite similar to your one expect few comments which I have made.
Here is the public version of Juno spec[1]. Not sure if it covers MHU in
detail.

>>
>>>>
>>>>>>> +static int mhu_startup(struct mbox_chan *chan)
>>>>>>> +{
>>>>>>> +       struct mhu_link *mlink = (struct mhu_link *)chan->con_priv;
>>>>>>> +       unsigned long flags;
>>>>>>> +       u32 val;
>>>>>>> +       int ret;
>>>>>>> +
>>>>>>> +       pr_debug("%s:%d\n", __func__, __LINE__);
>>>>>>> +       spin_lock_irqsave(&mlink->lock, flags);
>>>>>>> +       val = readl_relaxed(mlink->tx_reg + INTR_STAT_OFS);
>>>>>>> +       writel_relaxed(val, mlink->tx_reg + INTR_CLR_OFS);
>>>>>>> +       spin_unlock_irqrestore(&mlink->lock, flags);
>>>>>>> +
>>>>>>> +       ret = request_irq(mlink->irq, mhu_rx_interrupt,
>>>>>>> +                         IRQF_SHARED, "mhu_link", chan);
>>>>>>
>>>>>>
>>>>>>
>>>>>>
>>>>>> Just a thought: Can this be threaded_irq instead ?
>>>>>> Can move request_irq to probe instead esp. if threaded_irq ?
>>>>>> That provides some flexibility to client's rx_callback.
>>>>>>
>>>>> This is controller driver, and can not know which client want
>>>>> rx_callback in hard-irq context and which in thread_fn context.
>>>>> Otherwise, request_irq() does evaluate to request_threaded_irq(), if
>>>>> thats what you mean.
>>>>
>>>>
>>>> Agreed but on contrary since MHU involves external firmware(running
>>>> on different processor) which might have it's own latency, I prefer
>>>> threaded over hard irq. And yes request_irq does evaluate to
>>>> request_threaded_irq but with thread_fn = NULL which is not what I want.
>>>>
>>> If remote has its own latency, that does not mean we add some more :)
>>>
>>
>> No what I meant is unless there is a real need to use hard irq, we
>> should prefer threaded one otherwise.
>>
> And how does controller discern a "real need" from a "soft need" to
> use hard irq?
> Even if the controller driver pushes data up from a threaded function,
> the client can't know the context and can't sleep because the
> intermediate API says the rx_callback should be assumed to be atomic.

Yes I am not arguing on that, it should assume atomic and not sleep.
I am saying we can avoid rx_callback in interrupt context if possible.
I will try to look at the protocol implementation tomorrow.

> Again, it maybe more efficient if I see your implementation of the
> driver and understand your concerns about mine.
>

As I said its almost same as this, except I call mbox_chan_received_data
in irq thread context. I prefer enabling other interrupts while copying
payload data.

>> Also by latency I meant what if
>> the remote firmware misbehaves. In threaded version you have little
>> more flexibility whereas hard irq is executed with interrupts disabled.
>> At least the system is responsive and only MHU interrupts are disabled.
>>
> If the remote firmware misbehaves, that is a bug of the platform. No
> mailbox API could/should account for such malfunctions.
>

No I didn't intend for any mailbox API to account it.

Regards,
Sudeep

[1] http://infocenter.arm.com/help/topic/com.arm.doc.dto0038a/index.html

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

* Re: [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
  2014-07-17 18:51                             ` Sudeep Holla
@ 2014-07-18  9:06                                 ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-18  9:06 UTC (permalink / raw)
  To: Sudeep Holla
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA,
	andy.green-QSEj5FYQhm4dnm+yROfE0A,
	patches-QSEj5FYQhm4dnm+yROfE0A, linux-lFZ/pmaqli7XmaaqVzeoHQ,
	arnd-r2nGTMty4D4, olof-nZhT3qVonbNeoWH0uzbU5w, Mark Rutland,
	robh+dt-DgEjT+Ai2ygdnm+yROfE0A, khilman-QSEj5FYQhm4dnm+yROfE0A,
	broonie-QSEj5FYQhm4dnm+yROfE0A, Pawel Moll, Tetsuya Takinishi

On 18 July 2014 00:21, Sudeep Holla <sudeep.holla-5wv7dgnIgG8@public.gmane.org> wrote:
> On 17/07/14 18:07, Jassi Brar wrote:

>
>>> I believe that's what we need here if we want this driver to work
>>> on both Juno and your platform. Agree ?
>>>
>> Does this driver not work for Juno?
>
>
> I have not yet tried yet. For sure secure access will explode.
>
OK, I will remove setting up SCFG.

>>>
>>> No what I meant is unless there is a real need to use hard irq, we
>>> should prefer threaded one otherwise.
>>>
>> And how does controller discern a "real need" from a "soft need" to
>> use hard irq?
>> Even if the controller driver pushes data up from a threaded function,
>> the client can't know the context and can't sleep because the
>> intermediate API says the rx_callback should be assumed to be atomic.
>
> Yes I am not arguing on that, it should assume atomic and not sleep.
> I am saying we can avoid rx_callback in interrupt context if possible.
> I will try to look at the protocol implementation tomorrow.
>
There is only one way for controller to push data to client... which
is rx_callback() and it specified to be atomic.

>
>> Again, it maybe more efficient if I see your implementation of the
>> driver and understand your concerns about mine.
>>
>
> As I said its almost same as this, except I call mbox_chan_received_data
> in irq thread context. I prefer enabling other interrupts while copying
> payload data.
>
You call mbox_chan_received_data (which does rx_callback) from
threaded handler. If your client only does atomic stuff in
rx_callback(), then you pay for nothing. If your client does sleepable
stuff then, as you agree, that's wrong.

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

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

* [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller
@ 2014-07-18  9:06                                 ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-18  9:06 UTC (permalink / raw)
  To: linux-arm-kernel

On 18 July 2014 00:21, Sudeep Holla <sudeep.holla@arm.com> wrote:
> On 17/07/14 18:07, Jassi Brar wrote:

>
>>> I believe that's what we need here if we want this driver to work
>>> on both Juno and your platform. Agree ?
>>>
>> Does this driver not work for Juno?
>
>
> I have not yet tried yet. For sure secure access will explode.
>
OK, I will remove setting up SCFG.

>>>
>>> No what I meant is unless there is a real need to use hard irq, we
>>> should prefer threaded one otherwise.
>>>
>> And how does controller discern a "real need" from a "soft need" to
>> use hard irq?
>> Even if the controller driver pushes data up from a threaded function,
>> the client can't know the context and can't sleep because the
>> intermediate API says the rx_callback should be assumed to be atomic.
>
> Yes I am not arguing on that, it should assume atomic and not sleep.
> I am saying we can avoid rx_callback in interrupt context if possible.
> I will try to look at the protocol implementation tomorrow.
>
There is only one way for controller to push data to client... which
is rx_callback() and it specified to be atomic.

>
>> Again, it maybe more efficient if I see your implementation of the
>> driver and understand your concerns about mine.
>>
>
> As I said its almost same as this, except I call mbox_chan_received_data
> in irq thread context. I prefer enabling other interrupts while copying
> payload data.
>
You call mbox_chan_received_data (which does rx_callback) from
threaded handler. If your client only does atomic stuff in
rx_callback(), then you pay for nothing. If your client does sleepable
stuff then, as you agree, that's wrong.

cheers
-jassi

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-07-13  6:31   ` Mollie Wu
@ 2014-07-22 16:11       ` Linus Walleij
  -1 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-07-22 16:11 UTC (permalink / raw)
  To: Mollie Wu
  Cc: linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Jaswinder Singh, Russell King - ARM Linux, Arnd Bergmann,
	Olof Johansson, Mark Rutland, Rob Herring, Pawel Moll,
	Tetsuya Takinishi

On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:

> The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
> GPIOs are supported on top of Pinctrl api.
>
> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Cc: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>

This doesn't seem to be just about muxing but a full featured pin control
driver including pin config for electrical portions?

> +++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt

This looks OK.

> diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
> new file mode 100644
> index 0000000..ce2011b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
> @@ -0,0 +1,30 @@
> +Fujitsu MB86S7x Pin Controller
> +------------------------------
> +
> +Required properties:
> +- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
> +- reg: Should contain the register physical address and length for the
> +  pin controller.
> +- #gpio-range-cells: Should be 3
> +
> +Optional subnode-properties:
> +- mb86s7x,function: String specifying the function name.
> +- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
> +    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
> +- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up

For all of these please switch the driver to using generic pin configuration.
We have stopped letting drivers use custom props for these things, even
functions are nowadays standardized to be just the function = "foo";
strings.

Consult
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
for the bindings,

Since the driver selects GENERIC_PINCONF, utilize the core
support for parsing DT info etc for these settings in
drivers/pinctrl/pinconf-generic.c and look how other drivers
exploit these helpers.

> +++ b/drivers/pinctrl/pinctrl-mb86s7x.c
(...)
> +#define PDR(x) (0x0 + x)
> +#define DDR(x) (0x10 + x)
> +#define PFR(x) (0x20 + x)
> +
> +#define CDRV_PDG(x)    (0x0 + ((x) / 16) * 4)
> +#define FORCEZ_PDG(x)  (0x100 + ((x) / 16) * 4)
> +#define PULL_PDG(x)    (0x200 + ((x) / 16) * 4)
> +#define PIN_OFF(x)     (((x) % 16) * 2)

I think the 0x0, 0x10, 0x20, 0x100, 0x200 etc offsets should
be named #defines telling us what kind of registers they will be
hitting.

> +#define PMUX_MB86S70   0
> +#define PMUX_MB86S73   1

More something like MB86S70_ID, MB86S73_ID or something are
better names for this?

> +/* Virtual pin numbers start so that reg offset calculations get simple */
> +enum {
> +       PINS_VIRT = 128,
> +       PINS_PCIE0 = PINS_VIRT,
> +       PINS_HOLE1,
> +       PINS_USB3H0,
> +       PINS_HOLE2,
> +       PINS_USB2H0,
> +       PINS_USB2D0,
> +       PINS_SDH30,
> +       PINS_HOLE3,
> +       PINS_EMMC0,
> +       PINS_HSSPI0,
> +       PINS_GMACD0,
> +       PINS_GMACM0,
> +       PINS_I2S0,
> +       PINS_UART0,
> +       PINS_OTHER0,
> +       PINS_JTAG0,
> +       PINS_PCIE1,
> +       PINS_HOLE4,
> +       PINS_USB3H1,
> +       MB86S70_PMUX_PINS,
> +};
> +
> +struct mb86s70_gpio_chip {
> +       spinlock_t lock; /* for goio regs */
> +       struct clk *clk;
> +       void __iomem *base;
> +       struct gpio_chip gc;
> +};
> +
> +static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
> +{
> +       int ret = pinctrl_request_gpio(gc->base + offset);
> +
> +       if (!ret) {
> +               struct mb86s70_gpio_chip *gchip = container_of(gc,
> +                                               struct mb86s70_gpio_chip, gc);
> +               unsigned long flags;
> +               u32 val;
> +
> +               spin_lock_irqsave(&gchip->lock, flags);
> +               val = readl(gchip->base + PFR(offset / 8 * 4));
> +               val &= ~(1 << (offset % 8)); /* as gpio-port */
> +               writel(val, gchip->base + PFR(offset / 8 * 4));

When PFR is used like this I don't see how those macros are
really helpful. It's just very hard to read:

readl(base + PFR(offset / 8 * 4));

(Whis is already scary unless you know expression evaluation order by heart...)

Expands to

readl(base + 0x20 + (offset / 8 * 4));

Is it possible to just get rid of the PFR macro and create an inline
function like this:

static inline unsigned mb86s70_pfr(unsigned offset)
{
      return 0x20 + (offset / 8 * 4);
}

And the above becomes:

#include <linux/bitops.h>

val = readl(gchip->base + mb86s70_pfr(offset));
val &= ~BIT(offset % 8);
writel(val, gchip->base + mb86s70_pfr(offset));

This way it's less cluttered IMO. But it's not a strong preference.

> +static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
> +                                        unsigned offset, int value)
> +{
> +       struct mb86s70_gpio_chip *gchip =
> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
> +       unsigned long flags;
> +       unsigned char val;
> +
> +       spin_lock_irqsave(&gchip->lock, flags);
> +
> +       val = readl(gchip->base + PDR(offset / 8 * 4));
> +       if (value)
> +               val |= (1 << (offset % 8));
> +       else
> +               val &= ~(1 << (offset % 8));

In this and other places I'd just

val |= BIT(offset % 8); (etc)

> +       writel(val, gchip->base + PDR(offset / 8 * 4));
> +
> +       val = readl(gchip->base + DDR(offset / 8 * 4));
> +       val |= (1 << (offset % 8));
> +       writel(val, gchip->base + DDR(offset / 8 * 4));
> +
> +       spin_unlock_irqrestore(&gchip->lock, flags);
> +
> +       return 0;
> +}

Don't you want to use pinctrl_gpio_direction_output()
and pinctrl_gpio_direction_input() in these?

(Well maybe not.)

> +static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
> +{
> +       struct mb86s70_gpio_chip *gchip =
> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
> +       unsigned char val;
> +
> +       val = readl(gchip->base + PDR(offset / 8 * 4));
> +       val &= (1 << (offset % 8));
> +       return val ? 1 : 0;

I usually just do this:

#include <linux/bitops.h>

return !!(readl(...) & BIT(offset % 8));

This applied in a few places simplifies the code I think.

> +static int mb86s70_gpio_probe(struct platform_device *pdev)
> +{
> +       struct mb86s70_gpio_chip *gchip;
> +       struct resource *res;
> +       int ret;
> +
> +       gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
> +       if (gchip == NULL)
> +               return -ENOMEM;

Just

if (!gchip)

> +static int func_count, grp_count;
> +static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
> +static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;

No static locals please. Add these to struct mb86s70_pmux_chip or
similar state container as they seem to be dynamic. Use
pinctrl_dev_get_drvdata()
to get from struct pinctrl_dev *pctl to the state container.

> +static int mb86s70_pctrl_get_groups_count(struct pinctrl_dev *pctl)
> +{
> +       return grp_count;
> +}
> +
> +static const char *
> +mb86s70_pctrl_get_group_name(struct pinctrl_dev *pctl, unsigned selector)
> +{
> +       return mb86s7x_pgrps[selector].name;
> +}
>
> +static int
> +mb86s70_pctrl_get_group_pins(struct pinctrl_dev *pctl, unsigned selector,
> +                            const unsigned **pins, unsigned *num_pins)
> +{
> +       *pins = mb86s7x_pgrps[selector].pins;
> +       *num_pins = mb86s7x_pgrps[selector].num_pins;
> +
> +       return 0;
> +}

So these should use that method with pinctrl_dev_get_drvdata().

> +static int
> +mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
> +                            struct device_node *node,
> +                            struct pinctrl_map **map, unsigned *num_maps)
> +{

Rewrite this to use pinconf_generic_dt_node_to_map()
and friends. Read other drivers doing this, for example pinctrl-msm.c

> +       ret = of_property_read_string(node, "mb86s7x,function", &function);
> +       if (ret) {
> +               dev_err(pchip->dev,
> +                       "missing mb86s7x,function property in node %s\n",
> +                       node->name);
> +               return -EINVAL;
> +       }

For generic function parsing use just the name "function" rather
than "mb86s7x,function".

> +static const char * const pcie0_groups[] = {"pcie0_grp"};
> +static const char * const usb3h0_groups[] = {"usb3h0_grp"};
> +static const char * const usb2h0_groups[] = {"usb2h0_grp"};
> +static const char * const usb2d0_groups[] = {"usb2d0_grp"};
> +static const char * const sdh30_groups[] = {"sdh30_grp"};
> +static const char * const emmc0_groups[] = {"emmc0_grp"};
> +static const char * const hsspi0_groups[] = {"hsspi0_grp"};
> +static const char * const gmacd0_groups[] = {"gmacd0_grp"};
> +static const char * const gmacm0_groups[] = {"gmacm0_grp"};
> +static const char * const i2s0_groups[] = {"i2s0_grp"};
> +static const char * const other0_groups[] = {"other0_grp"};
> +static const char * const jtag0_groups[] = {"jtag0_grp"};
> +static const char * const pcie1_groups[] = {"pcie1_grp"};
> +static const char * const usb3h1_groups[] = {"usb3h1_grp"};
> +static const char * const extint16_groups[] = {"extint16_grp"};
> +static const char * const extint5_groups[] = {"extint5_grp"};
> +static const char * const tsif0_groups[] = {"tsif0_grp"};
> +static const char * const tsif1_groups[] = {"tsif1_grp"};
> +static const char * const cfg_groups[] = {"cfg_grp"};
> +static const char * const uart0_groups[] = {"uart0_grp"};
> +static const char * const uart1_groups[] = {"uart1_grp"};
> +static const char * const uart2_groups[] = {"uart2_grp"};
> +static const char * const pl244_groups[] = {"pl244_grp"};
> +static const char * const trace_groups[] = {"trace_grp"};
> +static const char * const memcs_groups[] = {"memcs_grp"};
> +static const char * const cap_groups[] = {"cap_grp"};
> +static const char * const smt_groups[] = {"smt_grp"};
> +
> +static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
> +       {
> +               .prog_val = 0x1,
> +               .name = "extint5",
> +               .groups = extint5_groups,
> +               .num_groups = ARRAY_SIZE(extint5_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "pcie0",
> +               .groups = pcie0_groups,
> +               .num_groups = ARRAY_SIZE(pcie0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb3h0",
> +               .groups = usb3h0_groups,
> +               .num_groups = ARRAY_SIZE(usb3h0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb2h0",
> +               .groups = usb2h0_groups,
> +               .num_groups = ARRAY_SIZE(usb2h0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb2d0",
> +               .groups = usb2d0_groups,
> +               .num_groups = ARRAY_SIZE(usb2d0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "sdh30",
> +               .groups = sdh30_groups,
> +               .num_groups = ARRAY_SIZE(sdh30_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "emmc0",
> +               .groups = emmc0_groups,
> +               .num_groups = ARRAY_SIZE(emmc0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "hsspi0",
> +               .groups = hsspi0_groups,
> +               .num_groups = ARRAY_SIZE(hsspi0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "gmacd0",
> +               .groups = gmacd0_groups,
> +               .num_groups = ARRAY_SIZE(gmacd0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "gmacm0",
> +               .groups = gmacm0_groups,
> +               .num_groups = ARRAY_SIZE(gmacm0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "i2s0",
> +               .groups = i2s0_groups,
> +               .num_groups = ARRAY_SIZE(i2s0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "other0",
> +               .groups = other0_groups,
> +               .num_groups = ARRAY_SIZE(other0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "jtag0",
> +               .groups = jtag0_groups,
> +               .num_groups = ARRAY_SIZE(jtag0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "pcie1",
> +               .groups = pcie1_groups,
> +               .num_groups = ARRAY_SIZE(pcie1_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb3h1",
> +               .groups = usb3h1_groups,
> +               .num_groups = ARRAY_SIZE(usb3h1_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "cfg",
> +               .groups = cfg_groups,
> +               .num_groups = ARRAY_SIZE(cfg_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart0",
> +               .groups = uart0_groups,
> +               .num_groups = ARRAY_SIZE(uart0_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart1",
> +               .groups = uart1_groups,
> +               .num_groups = ARRAY_SIZE(uart1_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart2",
> +               .groups = uart2_groups,
> +               .num_groups = ARRAY_SIZE(uart2_groups),
> +       },
> +       {
> +               .prog_val = 0x2,
> +               .name = "trace",
> +               .groups = trace_groups,
> +               .num_groups = ARRAY_SIZE(trace_groups),
> +       },
> +       {
> +               .prog_val = 0x2,
> +               .name = "pl244",
> +               .groups = pl244_groups,
> +               .num_groups = ARRAY_SIZE(pl244_groups),
> +       },
> +       {
> +               .prog_val = 0x3,
> +               .name = "smt",
> +               .groups = smt_groups,
> +               .num_groups = ARRAY_SIZE(smt_groups),
> +       },
> +       {
> +               .prog_val = 0x3,
> +               .name = "memcs",
> +               .groups = memcs_groups,
> +               .num_groups = ARRAY_SIZE(memcs_groups),
> +       },
> +};

Open-coding the function tables like this is certainly OK, but most
drivers will use macros to compress the tables. But I'm happy with
this.

> +static int
> +mb86s70_pmx_get_functions_count(struct pinctrl_dev *pctl)
> +{
> +       return func_count;
> +}
> +
> +static const char *
> +mb86s70_pmx_get_function_name(struct pinctrl_dev *pctl, unsigned selector)
> +{
> +       return mb86s7x_pmx_funcs[selector].name;
> +}
> +
> +static int
> +mb86s70_pmx_get_function_groups(struct pinctrl_dev *pctl,
> +                               unsigned selector, const char * const **groups,
> +                               unsigned * const num_groups)
> +{
> +       *groups = mb86s7x_pmx_funcs[selector].groups;
> +       *num_groups = mb86s7x_pmx_funcs[selector].num_groups;
> +
> +       return 0;
> +}

Again get these things from the state container.

> +static int
> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
> +                  unsigned func_selector, unsigned group_selector)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pins;
> +       int i, j;
> +
> +       if (group_selector >= grp_count) {
> +               pr_err("%s:%d\n", __func__, __LINE__);
> +               return -EINVAL;
> +       }
> +
> +       j = mb86s7x_pgrps[group_selector].num_pins;
> +       pins = mb86s7x_pgrps[group_selector].pins;
> +
> +       spin_lock_irqsave(&pchip->lock, flags);
> +
> +       /* Busy if any pin in the same 'bunch' is taken */
> +       for (i = 0; i < j; i++) {
> +               u32 val;
> +               int p = pins[i] / 4 * 4;
> +
> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
> +                       continue;
> +
> +               val = readl(pchip->base + p);
> +               /* skip if no change needed */
> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
> +                       continue;
> +
> +               if (pchip->pin_busy[p]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p);
> +                       goto busy_exit;
> +               }
> +
> +               if (pchip->pin_busy[p + 1]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+1);
> +                       goto busy_exit;
> +               }

I don't see why you are doing this and keeping track of
pins as "busy" or not. One thing the pin control core does
is to make sure pins do not collide in different use cases,
it seems like you're re-implementing this check again.

> +               if (p == 64)
> +                       continue;

This if-clause seems dubious. At least add a comment as
to what is happening here and why.

> +               if (pchip->pin_busy[p + 2]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+2);
> +                       goto busy_exit;
> +               }
> +
> +               if (pchip->pin_busy[p + 3]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+3);
> +                       goto busy_exit;
> +               }

I don't understand this either.

Why all this fuzz around the four pins from p ... p+3?

> +               continue;
> +busy_exit:
> +               spin_unlock_irqrestore(&pchip->lock, flags);
> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
> +               return -EBUSY;
> +       }

You don't have to have the busy_exit: inside the for-loop right?

Just push it below the return 0; in the end of this function
so you can get rid of the dangling continue;

> +       pr_debug("Going to enable %s on pins -",
> +                mb86s7x_pmx_funcs[func_selector].name);
> +       for (i = 0; i < j; i++) {
> +               int p = pins[i];
> +
> +               pr_debug(" %d", p);
> +               pchip->pin_busy[p] = true;

So I'm questioning this....

> +               if (p < PINS_VIRT) /* Not for virtual pins */

We need an explanation somewhere about what "virtual pins"
means in this driver, I have never seen that before.

> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
> +                              pchip->base + p / 4 * 4);

This may need some static inline like described above to simplify
the code and make it more readable, but I see what is going on.

> +       }
> +
> +       spin_unlock_irqrestore(&pchip->lock, flags);
> +       pr_debug("\n");
> +
> +       return 0;
> +}
> +
> +static void
> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
> +                   unsigned func_selector, unsigned group_selector)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pins;
> +       int i, j;
> +
> +       if (group_selector >= grp_count) {
> +               pr_err("%s:%d\n", __func__, __LINE__);
> +               return;
> +       }
> +
> +       j = mb86s7x_pgrps[group_selector].num_pins;
> +       pins = mb86s7x_pgrps[group_selector].pins;
> +
> +       pr_debug("Going to disable %s on pins -",
> +                mb86s7x_pmx_funcs[func_selector].name);
> +       spin_lock_irqsave(&pchip->lock, flags);
> +       for (i = 0; i < j; i++) {
> +               pr_debug(" %d", pins[i]);
> +               pchip->pin_busy[pins[i]] = false;
> +       }
> +       spin_unlock_irqrestore(&pchip->lock, flags);
> +       pr_debug("\n");
> +}

Remove this function. The .disable member is gone from
struct pinmux_ops, as it was ambiguous, see commit
2243a87d90b42eb38bc281957df3e57c712b5e56
"pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"

> +static int
> +mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
> +                              struct pinctrl_gpio_range *range,
> +                              unsigned pin, bool input)
> +{
> +       struct gpio_chip *gc = range->gc;
> +       struct mb86s70_gpio_chip *gchip = container_of(gc,
> +                                               struct mb86s70_gpio_chip, gc);
> +       unsigned long flags;
> +       u32 off, bit, val;
> +
> +       if (pin >= 64)
> +               return -EINVAL;

This is a bit strange again...

> +       spin_lock_irqsave(&gchip->lock, flags);
> +       bit = (pin - gc->base) % 8;
> +       off = (pin - gc->base) / 8 * 4;
> +       val = readl(gchip->base + DDR(off));
> +       if (input)
> +               val &= ~(1 << bit);
> +       else
> +               val |= (1 << bit);
> +       writel(val, gchip->base + DDR(off));
> +       spin_unlock_irqrestore(&gchip->lock, flags);
> +
> +       return 0;
> +}

So .set_direction is implemented but not called from the
gpiochip functions as pointed out above.

> +static int
> +mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
> +                            unsigned long *config)
> +{
> +       return -EINVAL;
> +}

Don't implement stubs for this, just leave the vtable entry
unassigned, the core will return an error.

However it doesn't seem so hard to implement this
actually: the set code just below does exactly this, retrieves
the right bits and alters them, so why is not get implemented?

> +static int
> +mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
> +                            unsigned long *configs, unsigned num_configs)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pin;
> +       int i, j, p;
> +       u32 val, ds;
> +
> +       p = mb86s7x_pgrps[group].num_pins;
> +       pin = mb86s7x_pgrps[group].pins;
> +
> +       spin_lock_irqsave(&pchip->lock, flags);
> +
> +       for (i = 0; i < num_configs; i++) {
> +               switch (pinconf_to_config_param(configs[i])) {
> +               case PIN_CONFIG_DRIVE_STRENGTH:
> +                       /* Drive Strength should be 2, 4, 6 or 8 mA */
> +                       ds = pinconf_to_config_argument(configs[i]);
> +                       ds = (ds - 2) / 2;

I like this use of SI units, so use the generic bindings too!

(...)
> +       pchip->desc.name = dev_name(&pdev->dev);
> +       pchip->desc.npins = MB86S70_PMUX_PINS;
> +       pchip->desc.pins = pchip->pins;
> +       pchip->desc.pctlops = &mb86s70_pctrl_ops;
> +       pchip->desc.pmxops = &mb86s70_pmx_ops;
> +       pchip->desc.owner = THIS_MODULE;
> +       if (type == PMUX_MB86S73) {
> +               pchip->desc.confops = &mb86s70_conf_ops;
> +               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +               pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
> +               if (IS_ERR(pchip->conf_base))
> +                       return PTR_ERR(pchip->conf_base);
> +               mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
> +               func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
> +               mb86s7x_pgrps = mb86s73_pgrps;
> +               grp_count = ARRAY_SIZE(mb86s73_pgrps);
> +       } else {
> +               mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
> +               func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
> +               mb86s7x_pgrps = mb86s70_pgrps;
> +               grp_count = ARRAY_SIZE(mb86s70_pgrps);
> +       }

As mentioned don't use static locals for these things, add these
to the state container.

> +static const struct of_device_id mb86s70_gpio_dt_ids[] = {
> +       { .compatible = "fujitsu,mb86s7x-gpio" },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);

"fujitsu" does not seem to exist in
Documentation/devicetree/bindings/vendor-prefixes.txt
Do you want to add it?

> +static struct platform_driver mb86s70_gpio_driver = {
> +       .probe     = mb86s70_gpio_probe,
> +       .driver    = {
> +               .name  = "mb86s70-gpio",
> +               .owner = THIS_MODULE,
> +               .of_match_table = mb86s70_gpio_dt_ids,
> +       },
> +};
> +
> +static struct platform_driver mb86s70_pinmux_driver = {
> +       .probe     = mb86s70_pinmux_probe,
> +       .driver    = {
> +               .name  = "mb86s70-pinmux",
> +               .owner = THIS_MODULE,
> +               .of_match_table = mb86s70_pinmux_dt_ids,
> +       },
> +};
> +
> +static int __init mb86s70_pins_init(void)
> +{
> +       int ret;
> +
> +       ret = platform_driver_register(&mb86s70_pinmux_driver);
> +       if (ret)
> +               return ret;
> +
> +       return platform_driver_register(&mb86s70_gpio_driver);
> +}
> +subsys_initcall(mb86s70_pins_init);

It's not really necessary to split this up in two drivers in one
file, it is possible to just use one driver: mb86s70_pinmux_driver,
have just one .probe() function, and register *both* the gpiochip
*and* pin control driver from that probe().

(Well pin control first the gpiochip I guess, but you get the
idea.)

This driver needs some work, but I like the start, please keep at it.

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

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-07-22 16:11       ` Linus Walleij
  0 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-07-22 16:11 UTC (permalink / raw)
  To: linux-arm-kernel

On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:

> The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
> GPIOs are supported on top of Pinctrl api.
>
> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
> Cc: Linus Walleij <linus.walleij@linaro.org>
> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>

This doesn't seem to be just about muxing but a full featured pin control
driver including pin config for electrical portions?

> +++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt

This looks OK.

> diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
> new file mode 100644
> index 0000000..ce2011b
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
> @@ -0,0 +1,30 @@
> +Fujitsu MB86S7x Pin Controller
> +------------------------------
> +
> +Required properties:
> +- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
> +- reg: Should contain the register physical address and length for the
> +  pin controller.
> +- #gpio-range-cells: Should be 3
> +
> +Optional subnode-properties:
> +- mb86s7x,function: String specifying the function name.
> +- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
> +    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
> +- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up

For all of these please switch the driver to using generic pin configuration.
We have stopped letting drivers use custom props for these things, even
functions are nowadays standardized to be just the function = "foo";
strings.

Consult
Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
for the bindings,

Since the driver selects GENERIC_PINCONF, utilize the core
support for parsing DT info etc for these settings in
drivers/pinctrl/pinconf-generic.c and look how other drivers
exploit these helpers.

> +++ b/drivers/pinctrl/pinctrl-mb86s7x.c
(...)
> +#define PDR(x) (0x0 + x)
> +#define DDR(x) (0x10 + x)
> +#define PFR(x) (0x20 + x)
> +
> +#define CDRV_PDG(x)    (0x0 + ((x) / 16) * 4)
> +#define FORCEZ_PDG(x)  (0x100 + ((x) / 16) * 4)
> +#define PULL_PDG(x)    (0x200 + ((x) / 16) * 4)
> +#define PIN_OFF(x)     (((x) % 16) * 2)

I think the 0x0, 0x10, 0x20, 0x100, 0x200 etc offsets should
be named #defines telling us what kind of registers they will be
hitting.

> +#define PMUX_MB86S70   0
> +#define PMUX_MB86S73   1

More something like MB86S70_ID, MB86S73_ID or something are
better names for this?

> +/* Virtual pin numbers start so that reg offset calculations get simple */
> +enum {
> +       PINS_VIRT = 128,
> +       PINS_PCIE0 = PINS_VIRT,
> +       PINS_HOLE1,
> +       PINS_USB3H0,
> +       PINS_HOLE2,
> +       PINS_USB2H0,
> +       PINS_USB2D0,
> +       PINS_SDH30,
> +       PINS_HOLE3,
> +       PINS_EMMC0,
> +       PINS_HSSPI0,
> +       PINS_GMACD0,
> +       PINS_GMACM0,
> +       PINS_I2S0,
> +       PINS_UART0,
> +       PINS_OTHER0,
> +       PINS_JTAG0,
> +       PINS_PCIE1,
> +       PINS_HOLE4,
> +       PINS_USB3H1,
> +       MB86S70_PMUX_PINS,
> +};
> +
> +struct mb86s70_gpio_chip {
> +       spinlock_t lock; /* for goio regs */
> +       struct clk *clk;
> +       void __iomem *base;
> +       struct gpio_chip gc;
> +};
> +
> +static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
> +{
> +       int ret = pinctrl_request_gpio(gc->base + offset);
> +
> +       if (!ret) {
> +               struct mb86s70_gpio_chip *gchip = container_of(gc,
> +                                               struct mb86s70_gpio_chip, gc);
> +               unsigned long flags;
> +               u32 val;
> +
> +               spin_lock_irqsave(&gchip->lock, flags);
> +               val = readl(gchip->base + PFR(offset / 8 * 4));
> +               val &= ~(1 << (offset % 8)); /* as gpio-port */
> +               writel(val, gchip->base + PFR(offset / 8 * 4));

When PFR is used like this I don't see how those macros are
really helpful. It's just very hard to read:

readl(base + PFR(offset / 8 * 4));

(Whis is already scary unless you know expression evaluation order by heart...)

Expands to

readl(base + 0x20 + (offset / 8 * 4));

Is it possible to just get rid of the PFR macro and create an inline
function like this:

static inline unsigned mb86s70_pfr(unsigned offset)
{
      return 0x20 + (offset / 8 * 4);
}

And the above becomes:

#include <linux/bitops.h>

val = readl(gchip->base + mb86s70_pfr(offset));
val &= ~BIT(offset % 8);
writel(val, gchip->base + mb86s70_pfr(offset));

This way it's less cluttered IMO. But it's not a strong preference.

> +static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
> +                                        unsigned offset, int value)
> +{
> +       struct mb86s70_gpio_chip *gchip =
> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
> +       unsigned long flags;
> +       unsigned char val;
> +
> +       spin_lock_irqsave(&gchip->lock, flags);
> +
> +       val = readl(gchip->base + PDR(offset / 8 * 4));
> +       if (value)
> +               val |= (1 << (offset % 8));
> +       else
> +               val &= ~(1 << (offset % 8));

In this and other places I'd just

val |= BIT(offset % 8); (etc)

> +       writel(val, gchip->base + PDR(offset / 8 * 4));
> +
> +       val = readl(gchip->base + DDR(offset / 8 * 4));
> +       val |= (1 << (offset % 8));
> +       writel(val, gchip->base + DDR(offset / 8 * 4));
> +
> +       spin_unlock_irqrestore(&gchip->lock, flags);
> +
> +       return 0;
> +}

Don't you want to use pinctrl_gpio_direction_output()
and pinctrl_gpio_direction_input() in these?

(Well maybe not.)

> +static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
> +{
> +       struct mb86s70_gpio_chip *gchip =
> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
> +       unsigned char val;
> +
> +       val = readl(gchip->base + PDR(offset / 8 * 4));
> +       val &= (1 << (offset % 8));
> +       return val ? 1 : 0;

I usually just do this:

#include <linux/bitops.h>

return !!(readl(...) & BIT(offset % 8));

This applied in a few places simplifies the code I think.

> +static int mb86s70_gpio_probe(struct platform_device *pdev)
> +{
> +       struct mb86s70_gpio_chip *gchip;
> +       struct resource *res;
> +       int ret;
> +
> +       gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
> +       if (gchip == NULL)
> +               return -ENOMEM;

Just

if (!gchip)

> +static int func_count, grp_count;
> +static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
> +static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;

No static locals please. Add these to struct mb86s70_pmux_chip or
similar state container as they seem to be dynamic. Use
pinctrl_dev_get_drvdata()
to get from struct pinctrl_dev *pctl to the state container.

> +static int mb86s70_pctrl_get_groups_count(struct pinctrl_dev *pctl)
> +{
> +       return grp_count;
> +}
> +
> +static const char *
> +mb86s70_pctrl_get_group_name(struct pinctrl_dev *pctl, unsigned selector)
> +{
> +       return mb86s7x_pgrps[selector].name;
> +}
>
> +static int
> +mb86s70_pctrl_get_group_pins(struct pinctrl_dev *pctl, unsigned selector,
> +                            const unsigned **pins, unsigned *num_pins)
> +{
> +       *pins = mb86s7x_pgrps[selector].pins;
> +       *num_pins = mb86s7x_pgrps[selector].num_pins;
> +
> +       return 0;
> +}

So these should use that method with pinctrl_dev_get_drvdata().

> +static int
> +mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
> +                            struct device_node *node,
> +                            struct pinctrl_map **map, unsigned *num_maps)
> +{

Rewrite this to use pinconf_generic_dt_node_to_map()
and friends. Read other drivers doing this, for example pinctrl-msm.c

> +       ret = of_property_read_string(node, "mb86s7x,function", &function);
> +       if (ret) {
> +               dev_err(pchip->dev,
> +                       "missing mb86s7x,function property in node %s\n",
> +                       node->name);
> +               return -EINVAL;
> +       }

For generic function parsing use just the name "function" rather
than "mb86s7x,function".

> +static const char * const pcie0_groups[] = {"pcie0_grp"};
> +static const char * const usb3h0_groups[] = {"usb3h0_grp"};
> +static const char * const usb2h0_groups[] = {"usb2h0_grp"};
> +static const char * const usb2d0_groups[] = {"usb2d0_grp"};
> +static const char * const sdh30_groups[] = {"sdh30_grp"};
> +static const char * const emmc0_groups[] = {"emmc0_grp"};
> +static const char * const hsspi0_groups[] = {"hsspi0_grp"};
> +static const char * const gmacd0_groups[] = {"gmacd0_grp"};
> +static const char * const gmacm0_groups[] = {"gmacm0_grp"};
> +static const char * const i2s0_groups[] = {"i2s0_grp"};
> +static const char * const other0_groups[] = {"other0_grp"};
> +static const char * const jtag0_groups[] = {"jtag0_grp"};
> +static const char * const pcie1_groups[] = {"pcie1_grp"};
> +static const char * const usb3h1_groups[] = {"usb3h1_grp"};
> +static const char * const extint16_groups[] = {"extint16_grp"};
> +static const char * const extint5_groups[] = {"extint5_grp"};
> +static const char * const tsif0_groups[] = {"tsif0_grp"};
> +static const char * const tsif1_groups[] = {"tsif1_grp"};
> +static const char * const cfg_groups[] = {"cfg_grp"};
> +static const char * const uart0_groups[] = {"uart0_grp"};
> +static const char * const uart1_groups[] = {"uart1_grp"};
> +static const char * const uart2_groups[] = {"uart2_grp"};
> +static const char * const pl244_groups[] = {"pl244_grp"};
> +static const char * const trace_groups[] = {"trace_grp"};
> +static const char * const memcs_groups[] = {"memcs_grp"};
> +static const char * const cap_groups[] = {"cap_grp"};
> +static const char * const smt_groups[] = {"smt_grp"};
> +
> +static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
> +       {
> +               .prog_val = 0x1,
> +               .name = "extint5",
> +               .groups = extint5_groups,
> +               .num_groups = ARRAY_SIZE(extint5_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "pcie0",
> +               .groups = pcie0_groups,
> +               .num_groups = ARRAY_SIZE(pcie0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb3h0",
> +               .groups = usb3h0_groups,
> +               .num_groups = ARRAY_SIZE(usb3h0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb2h0",
> +               .groups = usb2h0_groups,
> +               .num_groups = ARRAY_SIZE(usb2h0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb2d0",
> +               .groups = usb2d0_groups,
> +               .num_groups = ARRAY_SIZE(usb2d0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "sdh30",
> +               .groups = sdh30_groups,
> +               .num_groups = ARRAY_SIZE(sdh30_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "emmc0",
> +               .groups = emmc0_groups,
> +               .num_groups = ARRAY_SIZE(emmc0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "hsspi0",
> +               .groups = hsspi0_groups,
> +               .num_groups = ARRAY_SIZE(hsspi0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "gmacd0",
> +               .groups = gmacd0_groups,
> +               .num_groups = ARRAY_SIZE(gmacd0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "gmacm0",
> +               .groups = gmacm0_groups,
> +               .num_groups = ARRAY_SIZE(gmacm0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "i2s0",
> +               .groups = i2s0_groups,
> +               .num_groups = ARRAY_SIZE(i2s0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "other0",
> +               .groups = other0_groups,
> +               .num_groups = ARRAY_SIZE(other0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "jtag0",
> +               .groups = jtag0_groups,
> +               .num_groups = ARRAY_SIZE(jtag0_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "pcie1",
> +               .groups = pcie1_groups,
> +               .num_groups = ARRAY_SIZE(pcie1_groups),
> +       },
> +       {
> +               .prog_val = 0x0,
> +               .name = "usb3h1",
> +               .groups = usb3h1_groups,
> +               .num_groups = ARRAY_SIZE(usb3h1_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "cfg",
> +               .groups = cfg_groups,
> +               .num_groups = ARRAY_SIZE(cfg_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart0",
> +               .groups = uart0_groups,
> +               .num_groups = ARRAY_SIZE(uart0_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart1",
> +               .groups = uart1_groups,
> +               .num_groups = ARRAY_SIZE(uart1_groups),
> +       },
> +       {
> +               .prog_val = 0x1,
> +               .name = "uart2",
> +               .groups = uart2_groups,
> +               .num_groups = ARRAY_SIZE(uart2_groups),
> +       },
> +       {
> +               .prog_val = 0x2,
> +               .name = "trace",
> +               .groups = trace_groups,
> +               .num_groups = ARRAY_SIZE(trace_groups),
> +       },
> +       {
> +               .prog_val = 0x2,
> +               .name = "pl244",
> +               .groups = pl244_groups,
> +               .num_groups = ARRAY_SIZE(pl244_groups),
> +       },
> +       {
> +               .prog_val = 0x3,
> +               .name = "smt",
> +               .groups = smt_groups,
> +               .num_groups = ARRAY_SIZE(smt_groups),
> +       },
> +       {
> +               .prog_val = 0x3,
> +               .name = "memcs",
> +               .groups = memcs_groups,
> +               .num_groups = ARRAY_SIZE(memcs_groups),
> +       },
> +};

Open-coding the function tables like this is certainly OK, but most
drivers will use macros to compress the tables. But I'm happy with
this.

> +static int
> +mb86s70_pmx_get_functions_count(struct pinctrl_dev *pctl)
> +{
> +       return func_count;
> +}
> +
> +static const char *
> +mb86s70_pmx_get_function_name(struct pinctrl_dev *pctl, unsigned selector)
> +{
> +       return mb86s7x_pmx_funcs[selector].name;
> +}
> +
> +static int
> +mb86s70_pmx_get_function_groups(struct pinctrl_dev *pctl,
> +                               unsigned selector, const char * const **groups,
> +                               unsigned * const num_groups)
> +{
> +       *groups = mb86s7x_pmx_funcs[selector].groups;
> +       *num_groups = mb86s7x_pmx_funcs[selector].num_groups;
> +
> +       return 0;
> +}

Again get these things from the state container.

> +static int
> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
> +                  unsigned func_selector, unsigned group_selector)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pins;
> +       int i, j;
> +
> +       if (group_selector >= grp_count) {
> +               pr_err("%s:%d\n", __func__, __LINE__);
> +               return -EINVAL;
> +       }
> +
> +       j = mb86s7x_pgrps[group_selector].num_pins;
> +       pins = mb86s7x_pgrps[group_selector].pins;
> +
> +       spin_lock_irqsave(&pchip->lock, flags);
> +
> +       /* Busy if any pin in the same 'bunch' is taken */
> +       for (i = 0; i < j; i++) {
> +               u32 val;
> +               int p = pins[i] / 4 * 4;
> +
> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
> +                       continue;
> +
> +               val = readl(pchip->base + p);
> +               /* skip if no change needed */
> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
> +                       continue;
> +
> +               if (pchip->pin_busy[p]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p);
> +                       goto busy_exit;
> +               }
> +
> +               if (pchip->pin_busy[p + 1]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+1);
> +                       goto busy_exit;
> +               }

I don't see why you are doing this and keeping track of
pins as "busy" or not. One thing the pin control core does
is to make sure pins do not collide in different use cases,
it seems like you're re-implementing this check again.

> +               if (p == 64)
> +                       continue;

This if-clause seems dubious. At least add a comment as
to what is happening here and why.

> +               if (pchip->pin_busy[p + 2]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+2);
> +                       goto busy_exit;
> +               }
> +
> +               if (pchip->pin_busy[p + 3]) {
> +                       pr_err("%s:%d:%d %d busy\n",
> +                              __func__, __LINE__, pins[i], p+3);
> +                       goto busy_exit;
> +               }

I don't understand this either.

Why all this fuzz around the four pins from p ... p+3?

> +               continue;
> +busy_exit:
> +               spin_unlock_irqrestore(&pchip->lock, flags);
> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
> +               return -EBUSY;
> +       }

You don't have to have the busy_exit: inside the for-loop right?

Just push it below the return 0; in the end of this function
so you can get rid of the dangling continue;

> +       pr_debug("Going to enable %s on pins -",
> +                mb86s7x_pmx_funcs[func_selector].name);
> +       for (i = 0; i < j; i++) {
> +               int p = pins[i];
> +
> +               pr_debug(" %d", p);
> +               pchip->pin_busy[p] = true;

So I'm questioning this....

> +               if (p < PINS_VIRT) /* Not for virtual pins */

We need an explanation somewhere about what "virtual pins"
means in this driver, I have never seen that before.

> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
> +                              pchip->base + p / 4 * 4);

This may need some static inline like described above to simplify
the code and make it more readable, but I see what is going on.

> +       }
> +
> +       spin_unlock_irqrestore(&pchip->lock, flags);
> +       pr_debug("\n");
> +
> +       return 0;
> +}
> +
> +static void
> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
> +                   unsigned func_selector, unsigned group_selector)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pins;
> +       int i, j;
> +
> +       if (group_selector >= grp_count) {
> +               pr_err("%s:%d\n", __func__, __LINE__);
> +               return;
> +       }
> +
> +       j = mb86s7x_pgrps[group_selector].num_pins;
> +       pins = mb86s7x_pgrps[group_selector].pins;
> +
> +       pr_debug("Going to disable %s on pins -",
> +                mb86s7x_pmx_funcs[func_selector].name);
> +       spin_lock_irqsave(&pchip->lock, flags);
> +       for (i = 0; i < j; i++) {
> +               pr_debug(" %d", pins[i]);
> +               pchip->pin_busy[pins[i]] = false;
> +       }
> +       spin_unlock_irqrestore(&pchip->lock, flags);
> +       pr_debug("\n");
> +}

Remove this function. The .disable member is gone from
struct pinmux_ops, as it was ambiguous, see commit
2243a87d90b42eb38bc281957df3e57c712b5e56
"pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"

> +static int
> +mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
> +                              struct pinctrl_gpio_range *range,
> +                              unsigned pin, bool input)
> +{
> +       struct gpio_chip *gc = range->gc;
> +       struct mb86s70_gpio_chip *gchip = container_of(gc,
> +                                               struct mb86s70_gpio_chip, gc);
> +       unsigned long flags;
> +       u32 off, bit, val;
> +
> +       if (pin >= 64)
> +               return -EINVAL;

This is a bit strange again...

> +       spin_lock_irqsave(&gchip->lock, flags);
> +       bit = (pin - gc->base) % 8;
> +       off = (pin - gc->base) / 8 * 4;
> +       val = readl(gchip->base + DDR(off));
> +       if (input)
> +               val &= ~(1 << bit);
> +       else
> +               val |= (1 << bit);
> +       writel(val, gchip->base + DDR(off));
> +       spin_unlock_irqrestore(&gchip->lock, flags);
> +
> +       return 0;
> +}

So .set_direction is implemented but not called from the
gpiochip functions as pointed out above.

> +static int
> +mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
> +                            unsigned long *config)
> +{
> +       return -EINVAL;
> +}

Don't implement stubs for this, just leave the vtable entry
unassigned, the core will return an error.

However it doesn't seem so hard to implement this
actually: the set code just below does exactly this, retrieves
the right bits and alters them, so why is not get implemented?

> +static int
> +mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
> +                            unsigned long *configs, unsigned num_configs)
> +{
> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
> +       unsigned long flags;
> +       const unsigned *pin;
> +       int i, j, p;
> +       u32 val, ds;
> +
> +       p = mb86s7x_pgrps[group].num_pins;
> +       pin = mb86s7x_pgrps[group].pins;
> +
> +       spin_lock_irqsave(&pchip->lock, flags);
> +
> +       for (i = 0; i < num_configs; i++) {
> +               switch (pinconf_to_config_param(configs[i])) {
> +               case PIN_CONFIG_DRIVE_STRENGTH:
> +                       /* Drive Strength should be 2, 4, 6 or 8 mA */
> +                       ds = pinconf_to_config_argument(configs[i]);
> +                       ds = (ds - 2) / 2;

I like this use of SI units, so use the generic bindings too!

(...)
> +       pchip->desc.name = dev_name(&pdev->dev);
> +       pchip->desc.npins = MB86S70_PMUX_PINS;
> +       pchip->desc.pins = pchip->pins;
> +       pchip->desc.pctlops = &mb86s70_pctrl_ops;
> +       pchip->desc.pmxops = &mb86s70_pmx_ops;
> +       pchip->desc.owner = THIS_MODULE;
> +       if (type == PMUX_MB86S73) {
> +               pchip->desc.confops = &mb86s70_conf_ops;
> +               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
> +               pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
> +               if (IS_ERR(pchip->conf_base))
> +                       return PTR_ERR(pchip->conf_base);
> +               mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
> +               func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
> +               mb86s7x_pgrps = mb86s73_pgrps;
> +               grp_count = ARRAY_SIZE(mb86s73_pgrps);
> +       } else {
> +               mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
> +               func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
> +               mb86s7x_pgrps = mb86s70_pgrps;
> +               grp_count = ARRAY_SIZE(mb86s70_pgrps);
> +       }

As mentioned don't use static locals for these things, add these
to the state container.

> +static const struct of_device_id mb86s70_gpio_dt_ids[] = {
> +       { .compatible = "fujitsu,mb86s7x-gpio" },
> +       { /* sentinel */ }
> +};
> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);

"fujitsu" does not seem to exist in
Documentation/devicetree/bindings/vendor-prefixes.txt
Do you want to add it?

> +static struct platform_driver mb86s70_gpio_driver = {
> +       .probe     = mb86s70_gpio_probe,
> +       .driver    = {
> +               .name  = "mb86s70-gpio",
> +               .owner = THIS_MODULE,
> +               .of_match_table = mb86s70_gpio_dt_ids,
> +       },
> +};
> +
> +static struct platform_driver mb86s70_pinmux_driver = {
> +       .probe     = mb86s70_pinmux_probe,
> +       .driver    = {
> +               .name  = "mb86s70-pinmux",
> +               .owner = THIS_MODULE,
> +               .of_match_table = mb86s70_pinmux_dt_ids,
> +       },
> +};
> +
> +static int __init mb86s70_pins_init(void)
> +{
> +       int ret;
> +
> +       ret = platform_driver_register(&mb86s70_pinmux_driver);
> +       if (ret)
> +               return ret;
> +
> +       return platform_driver_register(&mb86s70_gpio_driver);
> +}
> +subsys_initcall(mb86s70_pins_init);

It's not really necessary to split this up in two drivers in one
file, it is possible to just use one driver: mb86s70_pinmux_driver,
have just one .probe() function, and register *both* the gpiochip
*and* pin control driver from that probe().

(Well pin control first the gpiochip I guess, but you get the
idea.)

This driver needs some work, but I like the start, please keep at it.

Yours,
Linus Walleij

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-07-22 16:11       ` Linus Walleij
@ 2014-07-24 18:04           ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-24 18:04 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Russell King - ARM Linux, Arnd Bergmann, Olof Johansson,
	Mark Rutland, Rob Herring, Pawel Moll, Tetsuya Takinishi

On 22 July 2014 21:41, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>
>> The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
>> GPIOs are supported on top of Pinctrl api.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> Cc: Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi-+CUm20s59erQFUHtdCDX3A@public.gmane.org>
>> Signed-off-by: Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
>
> This doesn't seem to be just about muxing but a full featured pin control
> driver including pin config for electrical portions?
>
>> +++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
>
> This looks OK.
>
>> diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
>> new file mode 100644
>> index 0000000..ce2011b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
>> @@ -0,0 +1,30 @@
>> +Fujitsu MB86S7x Pin Controller
>> +------------------------------
>> +
>> +Required properties:
>> +- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
>> +- reg: Should contain the register physical address and length for the
>> +  pin controller.
>> +- #gpio-range-cells: Should be 3
>> +
>> +Optional subnode-properties:
>> +- mb86s7x,function: String specifying the function name.
>> +- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
>> +    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
>> +- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up
>
> For all of these please switch the driver to using generic pin configuration.
> We have stopped letting drivers use custom props for these things, even
> functions are nowadays standardized to be just the function = "foo";
> strings.
>
> Consult
> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
> for the bindings,
>
> Since the driver selects GENERIC_PINCONF, utilize the core
> support for parsing DT info etc for these settings in
> drivers/pinctrl/pinconf-generic.c and look how other drivers
> exploit these helpers.
>
Cool. Its been a while since we had this driver written, so we missed
the latest goodies.

>> +++ b/drivers/pinctrl/pinctrl-mb86s7x.c
> (...)
>> +#define PDR(x) (0x0 + x)
>> +#define DDR(x) (0x10 + x)
>> +#define PFR(x) (0x20 + x)
>> +
>> +#define CDRV_PDG(x)    (0x0 + ((x) / 16) * 4)
>> +#define FORCEZ_PDG(x)  (0x100 + ((x) / 16) * 4)
>> +#define PULL_PDG(x)    (0x200 + ((x) / 16) * 4)
>> +#define PIN_OFF(x)     (((x) % 16) * 2)
>
> I think the 0x0, 0x10, 0x20, 0x100, 0x200 etc offsets should
> be named #defines telling us what kind of registers they will be
> hitting.
>
OK

>> +#define PMUX_MB86S70   0
>> +#define PMUX_MB86S73   1
>
> More something like MB86S70_ID, MB86S73_ID or something are
> better names for this?
>
OK

>> +/* Virtual pin numbers start so that reg offset calculations get simple */
>> +enum {
>> +       PINS_VIRT = 128,
>> +       PINS_PCIE0 = PINS_VIRT,
>> +       PINS_HOLE1,
>> +       PINS_USB3H0,
>> +       PINS_HOLE2,
>> +       PINS_USB2H0,
>> +       PINS_USB2D0,
>> +       PINS_SDH30,
>> +       PINS_HOLE3,
>> +       PINS_EMMC0,
>> +       PINS_HSSPI0,
>> +       PINS_GMACD0,
>> +       PINS_GMACM0,
>> +       PINS_I2S0,
>> +       PINS_UART0,
>> +       PINS_OTHER0,
>> +       PINS_JTAG0,
>> +       PINS_PCIE1,
>> +       PINS_HOLE4,
>> +       PINS_USB3H1,
>> +       MB86S70_PMUX_PINS,
>> +};
>> +
>> +struct mb86s70_gpio_chip {
>> +       spinlock_t lock; /* for goio regs */
>> +       struct clk *clk;
>> +       void __iomem *base;
>> +       struct gpio_chip gc;
>> +};
>> +
>> +static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
>> +{
>> +       int ret = pinctrl_request_gpio(gc->base + offset);
>> +
>> +       if (!ret) {
>> +               struct mb86s70_gpio_chip *gchip = container_of(gc,
>> +                                               struct mb86s70_gpio_chip, gc);
>> +               unsigned long flags;
>> +               u32 val;
>> +
>> +               spin_lock_irqsave(&gchip->lock, flags);
>> +               val = readl(gchip->base + PFR(offset / 8 * 4));
>> +               val &= ~(1 << (offset % 8)); /* as gpio-port */
>> +               writel(val, gchip->base + PFR(offset / 8 * 4));
>
> When PFR is used like this I don't see how those macros are
> really helpful. It's just very hard to read:
>
> readl(base + PFR(offset / 8 * 4));
>
> (Whis is already scary unless you know expression evaluation order by heart...)
>
The maths is *very* internal to the controller design. We shouldn't
need to touch these, since they have been tested to evaluate to right
addresses.

> Expands to
>
> readl(base + 0x20 + (offset / 8 * 4));
>
> Is it possible to just get rid of the PFR macro and create an inline
> function like this:
>
> static inline unsigned mb86s70_pfr(unsigned offset)
> {
>       return 0x20 + (offset / 8 * 4);
> }
>
> And the above becomes:
>
> #include <linux/bitops.h>
>
> val = readl(gchip->base + mb86s70_pfr(offset));
> val &= ~BIT(offset % 8);
> writel(val, gchip->base + mb86s70_pfr(offset));
>
> This way it's less cluttered IMO. But it's not a strong preference.
>
Thanks then.

>> +static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
>> +                                        unsigned offset, int value)
>> +{
>> +       struct mb86s70_gpio_chip *gchip =
>> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
>> +       unsigned long flags;
>> +       unsigned char val;
>> +
>> +       spin_lock_irqsave(&gchip->lock, flags);
>> +
>> +       val = readl(gchip->base + PDR(offset / 8 * 4));
>> +       if (value)
>> +               val |= (1 << (offset % 8));
>> +       else
>> +               val &= ~(1 << (offset % 8));
>
> In this and other places I'd just
>
> val |= BIT(offset % 8); (etc)
>
OK, will use BIT

>> +       writel(val, gchip->base + PDR(offset / 8 * 4));
>> +
>> +       val = readl(gchip->base + DDR(offset / 8 * 4));
>> +       val |= (1 << (offset % 8));
>> +       writel(val, gchip->base + DDR(offset / 8 * 4));
>> +
>> +       spin_unlock_irqrestore(&gchip->lock, flags);
>> +
>> +       return 0;
>> +}
>
> Don't you want to use pinctrl_gpio_direction_output()
> and pinctrl_gpio_direction_input() in these?
>
> (Well maybe not.)
>
Yup, we need not.

>> +static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
>> +{
>> +       struct mb86s70_gpio_chip *gchip =
>> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
>> +       unsigned char val;
>> +
>> +       val = readl(gchip->base + PDR(offset / 8 * 4));
>> +       val &= (1 << (offset % 8));
>> +       return val ? 1 : 0;
>
> I usually just do this:
>
> #include <linux/bitops.h>
>
> return !!(readl(...) & BIT(offset % 8));
>
> This applied in a few places simplifies the code I think.
>
OK

>> +static int mb86s70_gpio_probe(struct platform_device *pdev)
>> +{
>> +       struct mb86s70_gpio_chip *gchip;
>> +       struct resource *res;
>> +       int ret;
>> +
>> +       gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
>> +       if (gchip == NULL)
>> +               return -ENOMEM;
>
> Just
>
> if (!gchip)
>
OK

>> +static int func_count, grp_count;
>> +static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
>> +static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;
>
> No static locals please. Add these to struct mb86s70_pmux_chip or
> similar state container as they seem to be dynamic. Use
> pinctrl_dev_get_drvdata()
> to get from struct pinctrl_dev *pctl to the state container.
>
OK


>> +static int
>> +mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
>> +                            struct device_node *node,
>> +                            struct pinctrl_map **map, unsigned *num_maps)
>> +{
>
> Rewrite this to use pinconf_generic_dt_node_to_map()
> and friends. Read other drivers doing this, for example pinctrl-msm.c
>
OK

>> +       ret = of_property_read_string(node, "mb86s7x,function", &function);
>> +       if (ret) {
>> +               dev_err(pchip->dev,
>> +                       "missing mb86s7x,function property in node %s\n",
>> +                       node->name);
>> +               return -EINVAL;
>> +       }
>
> For generic function parsing use just the name "function" rather
> than "mb86s7x,function".
>
OK

>> +static const char * const pcie0_groups[] = {"pcie0_grp"};
>> +static const char * const usb3h0_groups[] = {"usb3h0_grp"};
>> +static const char * const usb2h0_groups[] = {"usb2h0_grp"};
>> +static const char * const usb2d0_groups[] = {"usb2d0_grp"};
>> +static const char * const sdh30_groups[] = {"sdh30_grp"};
>> +static const char * const emmc0_groups[] = {"emmc0_grp"};
>> +static const char * const hsspi0_groups[] = {"hsspi0_grp"};
>> +static const char * const gmacd0_groups[] = {"gmacd0_grp"};
>> +static const char * const gmacm0_groups[] = {"gmacm0_grp"};
>> +static const char * const i2s0_groups[] = {"i2s0_grp"};
>> +static const char * const other0_groups[] = {"other0_grp"};
>> +static const char * const jtag0_groups[] = {"jtag0_grp"};
>> +static const char * const pcie1_groups[] = {"pcie1_grp"};
>> +static const char * const usb3h1_groups[] = {"usb3h1_grp"};
>> +static const char * const extint16_groups[] = {"extint16_grp"};
>> +static const char * const extint5_groups[] = {"extint5_grp"};
>> +static const char * const tsif0_groups[] = {"tsif0_grp"};
>> +static const char * const tsif1_groups[] = {"tsif1_grp"};
>> +static const char * const cfg_groups[] = {"cfg_grp"};
>> +static const char * const uart0_groups[] = {"uart0_grp"};
>> +static const char * const uart1_groups[] = {"uart1_grp"};
>> +static const char * const uart2_groups[] = {"uart2_grp"};
>> +static const char * const pl244_groups[] = {"pl244_grp"};
>> +static const char * const trace_groups[] = {"trace_grp"};
>> +static const char * const memcs_groups[] = {"memcs_grp"};
>> +static const char * const cap_groups[] = {"cap_grp"};
>> +static const char * const smt_groups[] = {"smt_grp"};
>> +
>> +static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "extint5",
>> +               .groups = extint5_groups,
>> +               .num_groups = ARRAY_SIZE(extint5_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "pcie0",
>> +               .groups = pcie0_groups,
>> +               .num_groups = ARRAY_SIZE(pcie0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb3h0",
>> +               .groups = usb3h0_groups,
>> +               .num_groups = ARRAY_SIZE(usb3h0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb2h0",
>> +               .groups = usb2h0_groups,
>> +               .num_groups = ARRAY_SIZE(usb2h0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb2d0",
>> +               .groups = usb2d0_groups,
>> +               .num_groups = ARRAY_SIZE(usb2d0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "sdh30",
>> +               .groups = sdh30_groups,
>> +               .num_groups = ARRAY_SIZE(sdh30_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "emmc0",
>> +               .groups = emmc0_groups,
>> +               .num_groups = ARRAY_SIZE(emmc0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "hsspi0",
>> +               .groups = hsspi0_groups,
>> +               .num_groups = ARRAY_SIZE(hsspi0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "gmacd0",
>> +               .groups = gmacd0_groups,
>> +               .num_groups = ARRAY_SIZE(gmacd0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "gmacm0",
>> +               .groups = gmacm0_groups,
>> +               .num_groups = ARRAY_SIZE(gmacm0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "i2s0",
>> +               .groups = i2s0_groups,
>> +               .num_groups = ARRAY_SIZE(i2s0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "other0",
>> +               .groups = other0_groups,
>> +               .num_groups = ARRAY_SIZE(other0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "jtag0",
>> +               .groups = jtag0_groups,
>> +               .num_groups = ARRAY_SIZE(jtag0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "pcie1",
>> +               .groups = pcie1_groups,
>> +               .num_groups = ARRAY_SIZE(pcie1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb3h1",
>> +               .groups = usb3h1_groups,
>> +               .num_groups = ARRAY_SIZE(usb3h1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "cfg",
>> +               .groups = cfg_groups,
>> +               .num_groups = ARRAY_SIZE(cfg_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart0",
>> +               .groups = uart0_groups,
>> +               .num_groups = ARRAY_SIZE(uart0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart1",
>> +               .groups = uart1_groups,
>> +               .num_groups = ARRAY_SIZE(uart1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart2",
>> +               .groups = uart2_groups,
>> +               .num_groups = ARRAY_SIZE(uart2_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x2,
>> +               .name = "trace",
>> +               .groups = trace_groups,
>> +               .num_groups = ARRAY_SIZE(trace_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x2,
>> +               .name = "pl244",
>> +               .groups = pl244_groups,
>> +               .num_groups = ARRAY_SIZE(pl244_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x3,
>> +               .name = "smt",
>> +               .groups = smt_groups,
>> +               .num_groups = ARRAY_SIZE(smt_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x3,
>> +               .name = "memcs",
>> +               .groups = memcs_groups,
>> +               .num_groups = ARRAY_SIZE(memcs_groups),
>> +       },
>> +};
>
> Open-coding the function tables like this is certainly OK, but most
> drivers will use macros to compress the tables. But I'm happy with
> this.
>
thnx

>> +static int
>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>> +                  unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       /* Busy if any pin in the same 'bunch' is taken */
>> +       for (i = 0; i < j; i++) {
>> +               u32 val;
>> +               int p = pins[i] / 4 * 4;
>> +
>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>> +                       continue;
>> +
>> +               val = readl(pchip->base + p);
>> +               /* skip if no change needed */
>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>> +                       continue;
>> +
>> +               if (pchip->pin_busy[p]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 1]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+1);
>> +                       goto busy_exit;
>> +               }
>
> I don't see why you are doing this and keeping track of
> pins as "busy" or not. One thing the pin control core does
> is to make sure pins do not collide in different use cases,
> it seems like you're re-implementing this check again.
>
>> +               if (p == 64)
>> +                       continue;
>
> This if-clause seems dubious. At least add a comment as
> to what is happening here and why.
>
>> +               if (pchip->pin_busy[p + 2]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+2);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 3]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+3);
>> +                       goto busy_exit;
>> +               }
>
> I don't understand this either.
>
> Why all this fuzz around the four pins from p ... p+3?
>
Pinctrl exposes each pin individually, but the controller clubs 4 pins
together to work in same mode... so we have to reject any attempt to
change mode of a pin if any other pin in the same group [p,.. p+3] is
already taken by some user and is in a different mode i.e, busy. And
no, we can't expose each group of 4 as a 'virtual' pin because some
groups serve to 2 different IPs.


>> +               continue;
>> +busy_exit:
>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>
> You don't have to have the busy_exit: inside the for-loop right?
>
> Just push it below the return 0; in the end of this function
> so you can get rid of the dangling continue;
>
ok

>> +       pr_debug("Going to enable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       for (i = 0; i < j; i++) {
>> +               int p = pins[i];
>> +
>> +               pr_debug(" %d", p);
>> +               pchip->pin_busy[p] = true;
>
> So I'm questioning this....
>
>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>
> We need an explanation somewhere about what "virtual pins"
> means in this driver, I have never seen that before.
>
Now, these are really 'virtual' :)
The registers for physically individual pins are uniformly spaced and
we can compact the offsets with simple math.
However for some peripherals like PCI, USB etc, the pins are all tied
together as one 'virtual' pin. To reuse the same math to calculate
offsets, we assign virtual numbers to these virtual pins, assuming
'holes' wherever necessary.

>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>> +                              pchip->base + p / 4 * 4);
>
> This may need some static inline like described above to simplify
> the code and make it more readable, but I see what is going on.
>
I wasn't much obsessed with decorating calculations that are very
specific to the controller specs. Control flow, yes needs to be made
obvious to all.

>> +       }
>> +
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static void
>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>> +                   unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       pr_debug("Going to disable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +       for (i = 0; i < j; i++) {
>> +               pr_debug(" %d", pins[i]);
>> +               pchip->pin_busy[pins[i]] = false;
>> +       }
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +}
>
> Remove this function. The .disable member is gone from
> struct pinmux_ops, as it was ambiguous, see commit
> 2243a87d90b42eb38bc281957df3e57c712b5e56
> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>
OK

>> +static int
>> +mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
>> +                              struct pinctrl_gpio_range *range,
>> +                              unsigned pin, bool input)
>> +{
>> +       struct gpio_chip *gc = range->gc;
>> +       struct mb86s70_gpio_chip *gchip = container_of(gc,
>> +                                               struct mb86s70_gpio_chip, gc);
>> +       unsigned long flags;
>> +       u32 off, bit, val;
>> +
>> +       if (pin >= 64)
>> +               return -EINVAL;
>
> This is a bit strange again...
>
As explained above, there are only 64 'real' pins. Above that are
numbers assigned to 'virtual' pins, which we can't set direction of.

>> +       spin_lock_irqsave(&gchip->lock, flags);
>> +       bit = (pin - gc->base) % 8;
>> +       off = (pin - gc->base) / 8 * 4;
>> +       val = readl(gchip->base + DDR(off));
>> +       if (input)
>> +               val &= ~(1 << bit);
>> +       else
>> +               val |= (1 << bit);
>> +       writel(val, gchip->base + DDR(off));
>> +       spin_unlock_irqrestore(&gchip->lock, flags);
>> +
>> +       return 0;
>> +}
>
> So .set_direction is implemented but not called from the
> gpiochip functions as pointed out above.
>
>> +static int
>> +mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
>> +                            unsigned long *config)
>> +{
>> +       return -EINVAL;
>> +}
>
> Don't implement stubs for this, just leave the vtable entry
> unassigned, the core will return an error.
>
> However it doesn't seem so hard to implement this
> actually: the set code just below does exactly this, retrieves
> the right bits and alters them, so why is not get implemented?
>
>> +static int
>> +mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
>> +                            unsigned long *configs, unsigned num_configs)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pin;
>> +       int i, j, p;
>> +       u32 val, ds;
>> +
>> +       p = mb86s7x_pgrps[group].num_pins;
>> +       pin = mb86s7x_pgrps[group].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       for (i = 0; i < num_configs; i++) {
>> +               switch (pinconf_to_config_param(configs[i])) {
>> +               case PIN_CONFIG_DRIVE_STRENGTH:
>> +                       /* Drive Strength should be 2, 4, 6 or 8 mA */
>> +                       ds = pinconf_to_config_argument(configs[i]);
>> +                       ds = (ds - 2) / 2;
>
> I like this use of SI units, so use the generic bindings too!
>
ok

> (...)
>> +       pchip->desc.name = dev_name(&pdev->dev);
>> +       pchip->desc.npins = MB86S70_PMUX_PINS;
>> +       pchip->desc.pins = pchip->pins;
>> +       pchip->desc.pctlops = &mb86s70_pctrl_ops;
>> +       pchip->desc.pmxops = &mb86s70_pmx_ops;
>> +       pchip->desc.owner = THIS_MODULE;
>> +       if (type == PMUX_MB86S73) {
>> +               pchip->desc.confops = &mb86s70_conf_ops;
>> +               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +               pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
>> +               if (IS_ERR(pchip->conf_base))
>> +                       return PTR_ERR(pchip->conf_base);
>> +               mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
>> +               func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
>> +               mb86s7x_pgrps = mb86s73_pgrps;
>> +               grp_count = ARRAY_SIZE(mb86s73_pgrps);
>> +       } else {
>> +               mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
>> +               func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
>> +               mb86s7x_pgrps = mb86s70_pgrps;
>> +               grp_count = ARRAY_SIZE(mb86s70_pgrps);
>> +       }
>
> As mentioned don't use static locals for these things, add these
> to the state container.
>
>> +static const struct of_device_id mb86s70_gpio_dt_ids[] = {
>> +       { .compatible = "fujitsu,mb86s7x-gpio" },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
>
> "fujitsu" does not seem to exist in
> Documentation/devicetree/bindings/vendor-prefixes.txt
> Do you want to add it?
>
Yup, in a later patch of this set :)

>> +static struct platform_driver mb86s70_gpio_driver = {
>> +       .probe     = mb86s70_gpio_probe,
>> +       .driver    = {
>> +               .name  = "mb86s70-gpio",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = mb86s70_gpio_dt_ids,
>> +       },
>> +};
>> +
>> +static struct platform_driver mb86s70_pinmux_driver = {
>> +       .probe     = mb86s70_pinmux_probe,
>> +       .driver    = {
>> +               .name  = "mb86s70-pinmux",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = mb86s70_pinmux_dt_ids,
>> +       },
>> +};
>> +
>> +static int __init mb86s70_pins_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = platform_driver_register(&mb86s70_pinmux_driver);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return platform_driver_register(&mb86s70_gpio_driver);
>> +}
>> +subsys_initcall(mb86s70_pins_init);
>
> It's not really necessary to split this up in two drivers in one
> file, it is possible to just use one driver: mb86s70_pinmux_driver,
> have just one .probe() function, and register *both* the gpiochip
> *and* pin control driver from that probe().
>
> (Well pin control first the gpiochip I guess, but you get the
> idea.)
>
> This driver needs some work, but I like the start, please keep at it.
>
OK, thanks
-jassi
--
To unsubscribe from this list: send the line "unsubscribe devicetree" in
the body of a message to majordomo-u79uwXL29TY76Z2rM5mHXA@public.gmane.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-07-24 18:04           ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-07-24 18:04 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 July 2014 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:
>
>> The mb86s70 and mb86s73 Fujitsu SoCs differ in that the latter provide a pinmux.
>> GPIOs are supported on top of Pinctrl api.
>>
>> Signed-off-by: Jassi Brar <jaswinder.singh@linaro.org>
>> Cc: Linus Walleij <linus.walleij@linaro.org>
>> Signed-off-by: Tetsuya Takinishi <t.takinishi@jp.fujitsu.com>
>> Signed-off-by: Mollie Wu <mollie.wu@linaro.org>
>
> This doesn't seem to be just about muxing but a full featured pin control
> driver including pin config for electrical portions?
>
>> +++ b/Documentation/devicetree/bindings/gpio/fujitsu,mb86s7x-gpio.txt
>
> This looks OK.
>
>> diff --git a/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
>> new file mode 100644
>> index 0000000..ce2011b
>> --- /dev/null
>> +++ b/Documentation/devicetree/bindings/pinctrl/fujitsu,mb86s7x-pinctrl.txt
>> @@ -0,0 +1,30 @@
>> +Fujitsu MB86S7x Pin Controller
>> +------------------------------
>> +
>> +Required properties:
>> +- compatible: Should be "fujitsu,mb86s70-pinctrl" or "fujitsu,mb86s73-pinctrl"
>> +- reg: Should contain the register physical address and length for the
>> +  pin controller.
>> +- #gpio-range-cells: Should be 3
>> +
>> +Optional subnode-properties:
>> +- mb86s7x,function: String specifying the function name.
>> +- mb86s7x,drvst: Drive strength needed for pins to operate in that mode.
>> +    0 (Hi-z), 2mA, 4mA, 6mA or 8mA
>> +- mb86s7x,pullup: Should be 0 for Pull-Down or non-zero for Pull-Up
>
> For all of these please switch the driver to using generic pin configuration.
> We have stopped letting drivers use custom props for these things, even
> functions are nowadays standardized to be just the function = "foo";
> strings.
>
> Consult
> Documentation/devicetree/bindings/pinctrl/pinctrl-bindings.txt
> for the bindings,
>
> Since the driver selects GENERIC_PINCONF, utilize the core
> support for parsing DT info etc for these settings in
> drivers/pinctrl/pinconf-generic.c and look how other drivers
> exploit these helpers.
>
Cool. Its been a while since we had this driver written, so we missed
the latest goodies.

>> +++ b/drivers/pinctrl/pinctrl-mb86s7x.c
> (...)
>> +#define PDR(x) (0x0 + x)
>> +#define DDR(x) (0x10 + x)
>> +#define PFR(x) (0x20 + x)
>> +
>> +#define CDRV_PDG(x)    (0x0 + ((x) / 16) * 4)
>> +#define FORCEZ_PDG(x)  (0x100 + ((x) / 16) * 4)
>> +#define PULL_PDG(x)    (0x200 + ((x) / 16) * 4)
>> +#define PIN_OFF(x)     (((x) % 16) * 2)
>
> I think the 0x0, 0x10, 0x20, 0x100, 0x200 etc offsets should
> be named #defines telling us what kind of registers they will be
> hitting.
>
OK

>> +#define PMUX_MB86S70   0
>> +#define PMUX_MB86S73   1
>
> More something like MB86S70_ID, MB86S73_ID or something are
> better names for this?
>
OK

>> +/* Virtual pin numbers start so that reg offset calculations get simple */
>> +enum {
>> +       PINS_VIRT = 128,
>> +       PINS_PCIE0 = PINS_VIRT,
>> +       PINS_HOLE1,
>> +       PINS_USB3H0,
>> +       PINS_HOLE2,
>> +       PINS_USB2H0,
>> +       PINS_USB2D0,
>> +       PINS_SDH30,
>> +       PINS_HOLE3,
>> +       PINS_EMMC0,
>> +       PINS_HSSPI0,
>> +       PINS_GMACD0,
>> +       PINS_GMACM0,
>> +       PINS_I2S0,
>> +       PINS_UART0,
>> +       PINS_OTHER0,
>> +       PINS_JTAG0,
>> +       PINS_PCIE1,
>> +       PINS_HOLE4,
>> +       PINS_USB3H1,
>> +       MB86S70_PMUX_PINS,
>> +};
>> +
>> +struct mb86s70_gpio_chip {
>> +       spinlock_t lock; /* for goio regs */
>> +       struct clk *clk;
>> +       void __iomem *base;
>> +       struct gpio_chip gc;
>> +};
>> +
>> +static int mb86s70_gpio_request(struct gpio_chip *gc, unsigned offset)
>> +{
>> +       int ret = pinctrl_request_gpio(gc->base + offset);
>> +
>> +       if (!ret) {
>> +               struct mb86s70_gpio_chip *gchip = container_of(gc,
>> +                                               struct mb86s70_gpio_chip, gc);
>> +               unsigned long flags;
>> +               u32 val;
>> +
>> +               spin_lock_irqsave(&gchip->lock, flags);
>> +               val = readl(gchip->base + PFR(offset / 8 * 4));
>> +               val &= ~(1 << (offset % 8)); /* as gpio-port */
>> +               writel(val, gchip->base + PFR(offset / 8 * 4));
>
> When PFR is used like this I don't see how those macros are
> really helpful. It's just very hard to read:
>
> readl(base + PFR(offset / 8 * 4));
>
> (Whis is already scary unless you know expression evaluation order by heart...)
>
The maths is *very* internal to the controller design. We shouldn't
need to touch these, since they have been tested to evaluate to right
addresses.

> Expands to
>
> readl(base + 0x20 + (offset / 8 * 4));
>
> Is it possible to just get rid of the PFR macro and create an inline
> function like this:
>
> static inline unsigned mb86s70_pfr(unsigned offset)
> {
>       return 0x20 + (offset / 8 * 4);
> }
>
> And the above becomes:
>
> #include <linux/bitops.h>
>
> val = readl(gchip->base + mb86s70_pfr(offset));
> val &= ~BIT(offset % 8);
> writel(val, gchip->base + mb86s70_pfr(offset));
>
> This way it's less cluttered IMO. But it's not a strong preference.
>
Thanks then.

>> +static int mb86s70_gpio_direction_output(struct gpio_chip *gc,
>> +                                        unsigned offset, int value)
>> +{
>> +       struct mb86s70_gpio_chip *gchip =
>> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
>> +       unsigned long flags;
>> +       unsigned char val;
>> +
>> +       spin_lock_irqsave(&gchip->lock, flags);
>> +
>> +       val = readl(gchip->base + PDR(offset / 8 * 4));
>> +       if (value)
>> +               val |= (1 << (offset % 8));
>> +       else
>> +               val &= ~(1 << (offset % 8));
>
> In this and other places I'd just
>
> val |= BIT(offset % 8); (etc)
>
OK, will use BIT

>> +       writel(val, gchip->base + PDR(offset / 8 * 4));
>> +
>> +       val = readl(gchip->base + DDR(offset / 8 * 4));
>> +       val |= (1 << (offset % 8));
>> +       writel(val, gchip->base + DDR(offset / 8 * 4));
>> +
>> +       spin_unlock_irqrestore(&gchip->lock, flags);
>> +
>> +       return 0;
>> +}
>
> Don't you want to use pinctrl_gpio_direction_output()
> and pinctrl_gpio_direction_input() in these?
>
> (Well maybe not.)
>
Yup, we need not.

>> +static int mb86s70_gpio_get(struct gpio_chip *gc, unsigned offset)
>> +{
>> +       struct mb86s70_gpio_chip *gchip =
>> +                       container_of(gc, struct mb86s70_gpio_chip, gc);
>> +       unsigned char val;
>> +
>> +       val = readl(gchip->base + PDR(offset / 8 * 4));
>> +       val &= (1 << (offset % 8));
>> +       return val ? 1 : 0;
>
> I usually just do this:
>
> #include <linux/bitops.h>
>
> return !!(readl(...) & BIT(offset % 8));
>
> This applied in a few places simplifies the code I think.
>
OK

>> +static int mb86s70_gpio_probe(struct platform_device *pdev)
>> +{
>> +       struct mb86s70_gpio_chip *gchip;
>> +       struct resource *res;
>> +       int ret;
>> +
>> +       gchip = devm_kzalloc(&pdev->dev, sizeof(*gchip), GFP_KERNEL);
>> +       if (gchip == NULL)
>> +               return -ENOMEM;
>
> Just
>
> if (!gchip)
>
OK

>> +static int func_count, grp_count;
>> +static const struct mb86s70_pmx_grp *mb86s7x_pgrps;
>> +static const struct mb86s70_pmx_function *mb86s7x_pmx_funcs;
>
> No static locals please. Add these to struct mb86s70_pmux_chip or
> similar state container as they seem to be dynamic. Use
> pinctrl_dev_get_drvdata()
> to get from struct pinctrl_dev *pctl to the state container.
>
OK


>> +static int
>> +mb86s70_pctrl_dt_node_to_map(struct pinctrl_dev *pctl,
>> +                            struct device_node *node,
>> +                            struct pinctrl_map **map, unsigned *num_maps)
>> +{
>
> Rewrite this to use pinconf_generic_dt_node_to_map()
> and friends. Read other drivers doing this, for example pinctrl-msm.c
>
OK

>> +       ret = of_property_read_string(node, "mb86s7x,function", &function);
>> +       if (ret) {
>> +               dev_err(pchip->dev,
>> +                       "missing mb86s7x,function property in node %s\n",
>> +                       node->name);
>> +               return -EINVAL;
>> +       }
>
> For generic function parsing use just the name "function" rather
> than "mb86s7x,function".
>
OK

>> +static const char * const pcie0_groups[] = {"pcie0_grp"};
>> +static const char * const usb3h0_groups[] = {"usb3h0_grp"};
>> +static const char * const usb2h0_groups[] = {"usb2h0_grp"};
>> +static const char * const usb2d0_groups[] = {"usb2d0_grp"};
>> +static const char * const sdh30_groups[] = {"sdh30_grp"};
>> +static const char * const emmc0_groups[] = {"emmc0_grp"};
>> +static const char * const hsspi0_groups[] = {"hsspi0_grp"};
>> +static const char * const gmacd0_groups[] = {"gmacd0_grp"};
>> +static const char * const gmacm0_groups[] = {"gmacm0_grp"};
>> +static const char * const i2s0_groups[] = {"i2s0_grp"};
>> +static const char * const other0_groups[] = {"other0_grp"};
>> +static const char * const jtag0_groups[] = {"jtag0_grp"};
>> +static const char * const pcie1_groups[] = {"pcie1_grp"};
>> +static const char * const usb3h1_groups[] = {"usb3h1_grp"};
>> +static const char * const extint16_groups[] = {"extint16_grp"};
>> +static const char * const extint5_groups[] = {"extint5_grp"};
>> +static const char * const tsif0_groups[] = {"tsif0_grp"};
>> +static const char * const tsif1_groups[] = {"tsif1_grp"};
>> +static const char * const cfg_groups[] = {"cfg_grp"};
>> +static const char * const uart0_groups[] = {"uart0_grp"};
>> +static const char * const uart1_groups[] = {"uart1_grp"};
>> +static const char * const uart2_groups[] = {"uart2_grp"};
>> +static const char * const pl244_groups[] = {"pl244_grp"};
>> +static const char * const trace_groups[] = {"trace_grp"};
>> +static const char * const memcs_groups[] = {"memcs_grp"};
>> +static const char * const cap_groups[] = {"cap_grp"};
>> +static const char * const smt_groups[] = {"smt_grp"};
>> +
>> +static const struct mb86s70_pmx_function mb86s73_pmx_funcs[] = {
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "extint5",
>> +               .groups = extint5_groups,
>> +               .num_groups = ARRAY_SIZE(extint5_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "pcie0",
>> +               .groups = pcie0_groups,
>> +               .num_groups = ARRAY_SIZE(pcie0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb3h0",
>> +               .groups = usb3h0_groups,
>> +               .num_groups = ARRAY_SIZE(usb3h0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb2h0",
>> +               .groups = usb2h0_groups,
>> +               .num_groups = ARRAY_SIZE(usb2h0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb2d0",
>> +               .groups = usb2d0_groups,
>> +               .num_groups = ARRAY_SIZE(usb2d0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "sdh30",
>> +               .groups = sdh30_groups,
>> +               .num_groups = ARRAY_SIZE(sdh30_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "emmc0",
>> +               .groups = emmc0_groups,
>> +               .num_groups = ARRAY_SIZE(emmc0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "hsspi0",
>> +               .groups = hsspi0_groups,
>> +               .num_groups = ARRAY_SIZE(hsspi0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "gmacd0",
>> +               .groups = gmacd0_groups,
>> +               .num_groups = ARRAY_SIZE(gmacd0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "gmacm0",
>> +               .groups = gmacm0_groups,
>> +               .num_groups = ARRAY_SIZE(gmacm0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "i2s0",
>> +               .groups = i2s0_groups,
>> +               .num_groups = ARRAY_SIZE(i2s0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "other0",
>> +               .groups = other0_groups,
>> +               .num_groups = ARRAY_SIZE(other0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "jtag0",
>> +               .groups = jtag0_groups,
>> +               .num_groups = ARRAY_SIZE(jtag0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "pcie1",
>> +               .groups = pcie1_groups,
>> +               .num_groups = ARRAY_SIZE(pcie1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x0,
>> +               .name = "usb3h1",
>> +               .groups = usb3h1_groups,
>> +               .num_groups = ARRAY_SIZE(usb3h1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "cfg",
>> +               .groups = cfg_groups,
>> +               .num_groups = ARRAY_SIZE(cfg_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart0",
>> +               .groups = uart0_groups,
>> +               .num_groups = ARRAY_SIZE(uart0_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart1",
>> +               .groups = uart1_groups,
>> +               .num_groups = ARRAY_SIZE(uart1_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x1,
>> +               .name = "uart2",
>> +               .groups = uart2_groups,
>> +               .num_groups = ARRAY_SIZE(uart2_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x2,
>> +               .name = "trace",
>> +               .groups = trace_groups,
>> +               .num_groups = ARRAY_SIZE(trace_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x2,
>> +               .name = "pl244",
>> +               .groups = pl244_groups,
>> +               .num_groups = ARRAY_SIZE(pl244_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x3,
>> +               .name = "smt",
>> +               .groups = smt_groups,
>> +               .num_groups = ARRAY_SIZE(smt_groups),
>> +       },
>> +       {
>> +               .prog_val = 0x3,
>> +               .name = "memcs",
>> +               .groups = memcs_groups,
>> +               .num_groups = ARRAY_SIZE(memcs_groups),
>> +       },
>> +};
>
> Open-coding the function tables like this is certainly OK, but most
> drivers will use macros to compress the tables. But I'm happy with
> this.
>
thnx

>> +static int
>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>> +                  unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       /* Busy if any pin in the same 'bunch' is taken */
>> +       for (i = 0; i < j; i++) {
>> +               u32 val;
>> +               int p = pins[i] / 4 * 4;
>> +
>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>> +                       continue;
>> +
>> +               val = readl(pchip->base + p);
>> +               /* skip if no change needed */
>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>> +                       continue;
>> +
>> +               if (pchip->pin_busy[p]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 1]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+1);
>> +                       goto busy_exit;
>> +               }
>
> I don't see why you are doing this and keeping track of
> pins as "busy" or not. One thing the pin control core does
> is to make sure pins do not collide in different use cases,
> it seems like you're re-implementing this check again.
>
>> +               if (p == 64)
>> +                       continue;
>
> This if-clause seems dubious. At least add a comment as
> to what is happening here and why.
>
>> +               if (pchip->pin_busy[p + 2]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+2);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 3]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+3);
>> +                       goto busy_exit;
>> +               }
>
> I don't understand this either.
>
> Why all this fuzz around the four pins from p ... p+3?
>
Pinctrl exposes each pin individually, but the controller clubs 4 pins
together to work in same mode... so we have to reject any attempt to
change mode of a pin if any other pin in the same group [p,.. p+3] is
already taken by some user and is in a different mode i.e, busy. And
no, we can't expose each group of 4 as a 'virtual' pin because some
groups serve to 2 different IPs.


>> +               continue;
>> +busy_exit:
>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>
> You don't have to have the busy_exit: inside the for-loop right?
>
> Just push it below the return 0; in the end of this function
> so you can get rid of the dangling continue;
>
ok

>> +       pr_debug("Going to enable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       for (i = 0; i < j; i++) {
>> +               int p = pins[i];
>> +
>> +               pr_debug(" %d", p);
>> +               pchip->pin_busy[p] = true;
>
> So I'm questioning this....
>
>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>
> We need an explanation somewhere about what "virtual pins"
> means in this driver, I have never seen that before.
>
Now, these are really 'virtual' :)
The registers for physically individual pins are uniformly spaced and
we can compact the offsets with simple math.
However for some peripherals like PCI, USB etc, the pins are all tied
together as one 'virtual' pin. To reuse the same math to calculate
offsets, we assign virtual numbers to these virtual pins, assuming
'holes' wherever necessary.

>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>> +                              pchip->base + p / 4 * 4);
>
> This may need some static inline like described above to simplify
> the code and make it more readable, but I see what is going on.
>
I wasn't much obsessed with decorating calculations that are very
specific to the controller specs. Control flow, yes needs to be made
obvious to all.

>> +       }
>> +
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static void
>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>> +                   unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       pr_debug("Going to disable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +       for (i = 0; i < j; i++) {
>> +               pr_debug(" %d", pins[i]);
>> +               pchip->pin_busy[pins[i]] = false;
>> +       }
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +}
>
> Remove this function. The .disable member is gone from
> struct pinmux_ops, as it was ambiguous, see commit
> 2243a87d90b42eb38bc281957df3e57c712b5e56
> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>
OK

>> +static int
>> +mb86s70_pmx_gpio_set_direction(struct pinctrl_dev *pctl,
>> +                              struct pinctrl_gpio_range *range,
>> +                              unsigned pin, bool input)
>> +{
>> +       struct gpio_chip *gc = range->gc;
>> +       struct mb86s70_gpio_chip *gchip = container_of(gc,
>> +                                               struct mb86s70_gpio_chip, gc);
>> +       unsigned long flags;
>> +       u32 off, bit, val;
>> +
>> +       if (pin >= 64)
>> +               return -EINVAL;
>
> This is a bit strange again...
>
As explained above, there are only 64 'real' pins. Above that are
numbers assigned to 'virtual' pins, which we can't set direction of.

>> +       spin_lock_irqsave(&gchip->lock, flags);
>> +       bit = (pin - gc->base) % 8;
>> +       off = (pin - gc->base) / 8 * 4;
>> +       val = readl(gchip->base + DDR(off));
>> +       if (input)
>> +               val &= ~(1 << bit);
>> +       else
>> +               val |= (1 << bit);
>> +       writel(val, gchip->base + DDR(off));
>> +       spin_unlock_irqrestore(&gchip->lock, flags);
>> +
>> +       return 0;
>> +}
>
> So .set_direction is implemented but not called from the
> gpiochip functions as pointed out above.
>
>> +static int
>> +mb86s70_pin_config_group_get(struct pinctrl_dev *pctl, unsigned group,
>> +                            unsigned long *config)
>> +{
>> +       return -EINVAL;
>> +}
>
> Don't implement stubs for this, just leave the vtable entry
> unassigned, the core will return an error.
>
> However it doesn't seem so hard to implement this
> actually: the set code just below does exactly this, retrieves
> the right bits and alters them, so why is not get implemented?
>
>> +static int
>> +mb86s70_pin_config_group_set(struct pinctrl_dev *pctl, unsigned group,
>> +                            unsigned long *configs, unsigned num_configs)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pin;
>> +       int i, j, p;
>> +       u32 val, ds;
>> +
>> +       p = mb86s7x_pgrps[group].num_pins;
>> +       pin = mb86s7x_pgrps[group].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       for (i = 0; i < num_configs; i++) {
>> +               switch (pinconf_to_config_param(configs[i])) {
>> +               case PIN_CONFIG_DRIVE_STRENGTH:
>> +                       /* Drive Strength should be 2, 4, 6 or 8 mA */
>> +                       ds = pinconf_to_config_argument(configs[i]);
>> +                       ds = (ds - 2) / 2;
>
> I like this use of SI units, so use the generic bindings too!
>
ok

> (...)
>> +       pchip->desc.name = dev_name(&pdev->dev);
>> +       pchip->desc.npins = MB86S70_PMUX_PINS;
>> +       pchip->desc.pins = pchip->pins;
>> +       pchip->desc.pctlops = &mb86s70_pctrl_ops;
>> +       pchip->desc.pmxops = &mb86s70_pmx_ops;
>> +       pchip->desc.owner = THIS_MODULE;
>> +       if (type == PMUX_MB86S73) {
>> +               pchip->desc.confops = &mb86s70_conf_ops;
>> +               res = platform_get_resource(pdev, IORESOURCE_MEM, 1);
>> +               pchip->conf_base = devm_ioremap_resource(&pdev->dev, res);
>> +               if (IS_ERR(pchip->conf_base))
>> +                       return PTR_ERR(pchip->conf_base);
>> +               mb86s7x_pmx_funcs = mb86s73_pmx_funcs;
>> +               func_count = ARRAY_SIZE(mb86s73_pmx_funcs);
>> +               mb86s7x_pgrps = mb86s73_pgrps;
>> +               grp_count = ARRAY_SIZE(mb86s73_pgrps);
>> +       } else {
>> +               mb86s7x_pmx_funcs = mb86s70_pmx_funcs;
>> +               func_count = ARRAY_SIZE(mb86s70_pmx_funcs);
>> +               mb86s7x_pgrps = mb86s70_pgrps;
>> +               grp_count = ARRAY_SIZE(mb86s70_pgrps);
>> +       }
>
> As mentioned don't use static locals for these things, add these
> to the state container.
>
>> +static const struct of_device_id mb86s70_gpio_dt_ids[] = {
>> +       { .compatible = "fujitsu,mb86s7x-gpio" },
>> +       { /* sentinel */ }
>> +};
>> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
>
> "fujitsu" does not seem to exist in
> Documentation/devicetree/bindings/vendor-prefixes.txt
> Do you want to add it?
>
Yup, in a later patch of this set :)

>> +static struct platform_driver mb86s70_gpio_driver = {
>> +       .probe     = mb86s70_gpio_probe,
>> +       .driver    = {
>> +               .name  = "mb86s70-gpio",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = mb86s70_gpio_dt_ids,
>> +       },
>> +};
>> +
>> +static struct platform_driver mb86s70_pinmux_driver = {
>> +       .probe     = mb86s70_pinmux_probe,
>> +       .driver    = {
>> +               .name  = "mb86s70-pinmux",
>> +               .owner = THIS_MODULE,
>> +               .of_match_table = mb86s70_pinmux_dt_ids,
>> +       },
>> +};
>> +
>> +static int __init mb86s70_pins_init(void)
>> +{
>> +       int ret;
>> +
>> +       ret = platform_driver_register(&mb86s70_pinmux_driver);
>> +       if (ret)
>> +               return ret;
>> +
>> +       return platform_driver_register(&mb86s70_gpio_driver);
>> +}
>> +subsys_initcall(mb86s70_pins_init);
>
> It's not really necessary to split this up in two drivers in one
> file, it is possible to just use one driver: mb86s70_pinmux_driver,
> have just one .probe() function, and register *both* the gpiochip
> *and* pin control driver from that probe().
>
> (Well pin control first the gpiochip I guess, but you get the
> idea.)
>
> This driver needs some work, but I like the start, please keep at it.
>
OK, thanks
-jassi

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-07-24 18:04           ` Jassi Brar
@ 2014-08-08 12:42               ` Linus Walleij
  -1 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-08-08 12:42 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Russell King - ARM Linux, Arnd Bergmann, Olof Johansson,
	Mark Rutland, Rob Herring, Pawel Moll, Tetsuya Takinishi

On Thu, Jul 24, 2014 at 8:04 PM, Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:

>>> +               if (pchip->pin_busy[p + 3]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+3);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't understand this either.
>>
>> Why all this fuzz around the four pins from p ... p+3?
>>
> Pinctrl exposes each pin individually, but the controller clubs 4 pins
> together to work in same mode... so we have to reject any attempt to
> change mode of a pin if any other pin in the same group [p,.. p+3] is
> already taken by some user and is in a different mode i.e, busy. And
> no, we can't expose each group of 4 as a 'virtual' pin because some
> groups serve to 2 different IPs.

OK I get it now I think, seems valid.

>>> +               pr_debug(" %d", p);
>>> +               pchip->pin_busy[p] = true;
>>
>> So I'm questioning this....
>>
>>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>>
>> We need an explanation somewhere about what "virtual pins"
>> means in this driver, I have never seen that before.
>>
> Now, these are really 'virtual' :)
> The registers for physically individual pins are uniformly spaced and
> we can compact the offsets with simple math.
> However for some peripherals like PCI, USB etc, the pins are all tied
> together as one 'virtual' pin. To reuse the same math to calculate
> offsets, we assign virtual numbers to these virtual pins, assuming
> 'holes' wherever necessary.

Aha. OK, well I guess readability is paramount, the scheme
you come up with is likely to stick so make sure that whatever
way it works is very clear and documented so others can follow
the example later on.

>>> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
>>
>> "fujitsu" does not seem to exist in
>> Documentation/devicetree/bindings/vendor-prefixes.txt
>> Do you want to add it?
>>
> Yup, in a later patch of this set :)

OK thanks. Dunno if there is already some v2 in my inbox, I've
got some stuff to process...

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

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-08-08 12:42               ` Linus Walleij
  0 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-08-08 12:42 UTC (permalink / raw)
  To: linux-arm-kernel

On Thu, Jul 24, 2014 at 8:04 PM, Jassi Brar <jaswinder.singh@linaro.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:

>>> +               if (pchip->pin_busy[p + 3]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+3);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't understand this either.
>>
>> Why all this fuzz around the four pins from p ... p+3?
>>
> Pinctrl exposes each pin individually, but the controller clubs 4 pins
> together to work in same mode... so we have to reject any attempt to
> change mode of a pin if any other pin in the same group [p,.. p+3] is
> already taken by some user and is in a different mode i.e, busy. And
> no, we can't expose each group of 4 as a 'virtual' pin because some
> groups serve to 2 different IPs.

OK I get it now I think, seems valid.

>>> +               pr_debug(" %d", p);
>>> +               pchip->pin_busy[p] = true;
>>
>> So I'm questioning this....
>>
>>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>>
>> We need an explanation somewhere about what "virtual pins"
>> means in this driver, I have never seen that before.
>>
> Now, these are really 'virtual' :)
> The registers for physically individual pins are uniformly spaced and
> we can compact the offsets with simple math.
> However for some peripherals like PCI, USB etc, the pins are all tied
> together as one 'virtual' pin. To reuse the same math to calculate
> offsets, we assign virtual numbers to these virtual pins, assuming
> 'holes' wherever necessary.

Aha. OK, well I guess readability is paramount, the scheme
you come up with is likely to stick so make sure that whatever
way it works is very clear and documented so others can follow
the example later on.

>>> +MODULE_DEVICE_TABLE(of, mb86s70_gpio_dt_ids);
>>
>> "fujitsu" does not seem to exist in
>> Documentation/devicetree/bindings/vendor-prefixes.txt
>> Do you want to add it?
>>
> Yup, in a later patch of this set :)

OK thanks. Dunno if there is already some v2 in my inbox, I've
got some stuff to process...

Yours,
Linus Walleij

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-07-22 16:11       ` Linus Walleij
@ 2014-08-22  7:46           ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-08-22  7:46 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Russell King - ARM Linux, Arnd Bergmann, Olof Johansson,
	Mark Rutland, Rob Herring, Pawel Moll, Tetsuya Takinishi,
	fwu-eYqpPyKDWXRBDgjK7y7TUQ, Stephen Warren

On 22 July 2014 21:41, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:

>
>> +static int
>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>> +                  unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       /* Busy if any pin in the same 'bunch' is taken */
>> +       for (i = 0; i < j; i++) {
>> +               u32 val;
>> +               int p = pins[i] / 4 * 4;
>> +
>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>> +                       continue;
>> +
>> +               val = readl(pchip->base + p);
>> +               /* skip if no change needed */
>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>> +                       continue;
>> +
>> +               if (pchip->pin_busy[p]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 1]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+1);
>> +                       goto busy_exit;
>> +               }
>
> I don't see why you are doing this and keeping track of
> pins as "busy" or not. One thing the pin control core does
> is to make sure pins do not collide in different use cases,
> it seems like you're re-implementing this check again.
>
>> +               if (p == 64)
>> +                       continue;
>
> This if-clause seems dubious. At least add a comment as
> to what is happening here and why.
>
>> +               if (pchip->pin_busy[p + 2]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+2);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 3]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+3);
>> +                       goto busy_exit;
>> +               }
>
> I don't understand this either.
>
> Why all this fuzz around the four pins from p ... p+3?
>
>> +               continue;
>> +busy_exit:
>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>
> You don't have to have the busy_exit: inside the for-loop right?
>
> Just push it below the return 0; in the end of this function
> so you can get rid of the dangling continue;
>
>> +       pr_debug("Going to enable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       for (i = 0; i < j; i++) {
>> +               int p = pins[i];
>> +
>> +               pr_debug(" %d", p);
>> +               pchip->pin_busy[p] = true;
>
> So I'm questioning this....
>
>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>
> We need an explanation somewhere about what "virtual pins"
> means in this driver, I have never seen that before.
>
>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>> +                              pchip->base + p / 4 * 4);
>
> This may need some static inline like described above to simplify
> the code and make it more readable, but I see what is going on.
>
>> +       }
>> +
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static void
>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>> +                   unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       pr_debug("Going to disable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +       for (i = 0; i < j; i++) {
>> +               pr_debug(" %d", pins[i]);
>> +               pchip->pin_busy[pins[i]] = false;
>> +       }
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +}
>
> Remove this function. The .disable member is gone from
> struct pinmux_ops, as it was ambiguous, see commit
> 2243a87d90b42eb38bc281957df3e57c712b5e56
> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>
Hmm... I just got bitten by this while updating the patchset. Sorry I
missed the patch review.

The reasons given in changelog are
  (1) 'Fix' desc->mux_usecount
  (2) The .disable callback is not useful for any of the existing platforms.

Well, for (2) I think it is only reasonable for the provider to need
to know when some resource is released by a user just as when it was
requested.
   Even if the core ensures only one user is provided access to a pin,
the controller driver may still need to know when a pin is no more in
use. For ex, within consecutive 4 pins my controller can not enable a
pin for some function if any in the bunch is already
enabled/configured. So I need to know if the pin has been
disabled/released, so I can go ahead enabling the 'neighbor' pin for
some other role.

For reason (1), there should be some way to fix within the core?

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

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-08-22  7:46           ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-08-22  7:46 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 July 2014 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:

>
>> +static int
>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>> +                  unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return -EINVAL;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +
>> +       /* Busy if any pin in the same 'bunch' is taken */
>> +       for (i = 0; i < j; i++) {
>> +               u32 val;
>> +               int p = pins[i] / 4 * 4;
>> +
>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>> +                       continue;
>> +
>> +               val = readl(pchip->base + p);
>> +               /* skip if no change needed */
>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>> +                       continue;
>> +
>> +               if (pchip->pin_busy[p]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 1]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+1);
>> +                       goto busy_exit;
>> +               }
>
> I don't see why you are doing this and keeping track of
> pins as "busy" or not. One thing the pin control core does
> is to make sure pins do not collide in different use cases,
> it seems like you're re-implementing this check again.
>
>> +               if (p == 64)
>> +                       continue;
>
> This if-clause seems dubious. At least add a comment as
> to what is happening here and why.
>
>> +               if (pchip->pin_busy[p + 2]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+2);
>> +                       goto busy_exit;
>> +               }
>> +
>> +               if (pchip->pin_busy[p + 3]) {
>> +                       pr_err("%s:%d:%d %d busy\n",
>> +                              __func__, __LINE__, pins[i], p+3);
>> +                       goto busy_exit;
>> +               }
>
> I don't understand this either.
>
> Why all this fuzz around the four pins from p ... p+3?
>
>> +               continue;
>> +busy_exit:
>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>> +               return -EBUSY;
>> +       }
>
> You don't have to have the busy_exit: inside the for-loop right?
>
> Just push it below the return 0; in the end of this function
> so you can get rid of the dangling continue;
>
>> +       pr_debug("Going to enable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       for (i = 0; i < j; i++) {
>> +               int p = pins[i];
>> +
>> +               pr_debug(" %d", p);
>> +               pchip->pin_busy[p] = true;
>
> So I'm questioning this....
>
>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>
> We need an explanation somewhere about what "virtual pins"
> means in this driver, I have never seen that before.
>
>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>> +                              pchip->base + p / 4 * 4);
>
> This may need some static inline like described above to simplify
> the code and make it more readable, but I see what is going on.
>
>> +       }
>> +
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +
>> +       return 0;
>> +}
>> +
>> +static void
>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>> +                   unsigned func_selector, unsigned group_selector)
>> +{
>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>> +       unsigned long flags;
>> +       const unsigned *pins;
>> +       int i, j;
>> +
>> +       if (group_selector >= grp_count) {
>> +               pr_err("%s:%d\n", __func__, __LINE__);
>> +               return;
>> +       }
>> +
>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>> +       pins = mb86s7x_pgrps[group_selector].pins;
>> +
>> +       pr_debug("Going to disable %s on pins -",
>> +                mb86s7x_pmx_funcs[func_selector].name);
>> +       spin_lock_irqsave(&pchip->lock, flags);
>> +       for (i = 0; i < j; i++) {
>> +               pr_debug(" %d", pins[i]);
>> +               pchip->pin_busy[pins[i]] = false;
>> +       }
>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>> +       pr_debug("\n");
>> +}
>
> Remove this function. The .disable member is gone from
> struct pinmux_ops, as it was ambiguous, see commit
> 2243a87d90b42eb38bc281957df3e57c712b5e56
> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>
Hmm... I just got bitten by this while updating the patchset. Sorry I
missed the patch review.

The reasons given in changelog are
  (1) 'Fix' desc->mux_usecount
  (2) The .disable callback is not useful for any of the existing platforms.

Well, for (2) I think it is only reasonable for the provider to need
to know when some resource is released by a user just as when it was
requested.
   Even if the core ensures only one user is provided access to a pin,
the controller driver may still need to know when a pin is no more in
use. For ex, within consecutive 4 pins my controller can not enable a
pin for some function if any in the bunch is already
enabled/configured. So I need to know if the pin has been
disabled/released, so I can go ahead enabling the 'neighbor' pin for
some other role.

For reason (1), there should be some way to fix within the core?

Regards,
Jassi

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-08-22  7:46           ` Jassi Brar
@ 2014-08-27 16:58               ` Jassi Brar
  -1 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-08-27 16:58 UTC (permalink / raw)
  To: Linus Walleij
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Russell King - ARM Linux, Arnd Bergmann, Olof Johansson,
	Mark Rutland, Rob Herring, Pawel Moll, Tetsuya Takinishi,
	fwu-eYqpPyKDWXRBDgjK7y7TUQ, Stephen Warren

On 22 August 2014 13:16, Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>
>>
>>> +static int
>>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>>> +                  unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +
>>> +       /* Busy if any pin in the same 'bunch' is taken */
>>> +       for (i = 0; i < j; i++) {
>>> +               u32 val;
>>> +               int p = pins[i] / 4 * 4;
>>> +
>>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>>> +                       continue;
>>> +
>>> +               val = readl(pchip->base + p);
>>> +               /* skip if no change needed */
>>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>>> +                       continue;
>>> +
>>> +               if (pchip->pin_busy[p]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p);
>>> +                       goto busy_exit;
>>> +               }
>>> +
>>> +               if (pchip->pin_busy[p + 1]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+1);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't see why you are doing this and keeping track of
>> pins as "busy" or not. One thing the pin control core does
>> is to make sure pins do not collide in different use cases,
>> it seems like you're re-implementing this check again.
>>
>>> +               if (p == 64)
>>> +                       continue;
>>
>> This if-clause seems dubious. At least add a comment as
>> to what is happening here and why.
>>
>>> +               if (pchip->pin_busy[p + 2]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+2);
>>> +                       goto busy_exit;
>>> +               }
>>> +
>>> +               if (pchip->pin_busy[p + 3]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+3);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't understand this either.
>>
>> Why all this fuzz around the four pins from p ... p+3?
>>
>>> +               continue;
>>> +busy_exit:
>>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>>> +               return -EBUSY;
>>> +       }
>>
>> You don't have to have the busy_exit: inside the for-loop right?
>>
>> Just push it below the return 0; in the end of this function
>> so you can get rid of the dangling continue;
>>
>>> +       pr_debug("Going to enable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       for (i = 0; i < j; i++) {
>>> +               int p = pins[i];
>>> +
>>> +               pr_debug(" %d", p);
>>> +               pchip->pin_busy[p] = true;
>>
>> So I'm questioning this....
>>
>>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>>
>> We need an explanation somewhere about what "virtual pins"
>> means in this driver, I have never seen that before.
>>
>>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>>> +                              pchip->base + p / 4 * 4);
>>
>> This may need some static inline like described above to simplify
>> the code and make it more readable, but I see what is going on.
>>
>>> +       }
>>> +
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static void
>>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>>> +                   unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       pr_debug("Going to disable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +       for (i = 0; i < j; i++) {
>>> +               pr_debug(" %d", pins[i]);
>>> +               pchip->pin_busy[pins[i]] = false;
>>> +       }
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +}
>>
>> Remove this function. The .disable member is gone from
>> struct pinmux_ops, as it was ambiguous, see commit
>> 2243a87d90b42eb38bc281957df3e57c712b5e56
>> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>>
> Hmm... I just got bitten by this while updating the patchset. Sorry I
> missed the patch review.
>
> The reasons given in changelog are
>   (1) 'Fix' desc->mux_usecount
>   (2) The .disable callback is not useful for any of the existing platforms.
>
> Well, for (2) I think it is only reasonable for the provider to need
> to know when some resource is released by a user just as when it was
> requested.
>    Even if the core ensures only one user is provided access to a pin,
> the controller driver may still need to know when a pin is no more in
> use. For ex, within consecutive 4 pins my controller can not enable a
> pin for some function if any in the bunch is already
> enabled/configured. So I need to know if the pin has been
> disabled/released, so I can go ahead enabling the 'neighbor' pin for
> some other role.
>
> For reason (1), there should be some way to fix within the core?
>
Polite ping...

I can't find a way around not knowing when a pin is let free. Do we
revert the commit and fix the situation for the author's platform
instead?

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

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-08-27 16:58               ` Jassi Brar
  0 siblings, 0 replies; 88+ messages in thread
From: Jassi Brar @ 2014-08-27 16:58 UTC (permalink / raw)
  To: linux-arm-kernel

On 22 August 2014 13:16, Jassi Brar <jaswinder.singh@linaro.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:
>
>>
>>> +static int
>>> +mb86s70_pmx_enable(struct pinctrl_dev *pctl,
>>> +                  unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return -EINVAL;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +
>>> +       /* Busy if any pin in the same 'bunch' is taken */
>>> +       for (i = 0; i < j; i++) {
>>> +               u32 val;
>>> +               int p = pins[i] / 4 * 4;
>>> +
>>> +               if (pins[i] >= PINS_VIRT) /* Not for virtual pins */
>>> +                       continue;
>>> +
>>> +               val = readl(pchip->base + p);
>>> +               /* skip if no change needed */
>>> +               if (val == mb86s7x_pmx_funcs[func_selector].prog_val)
>>> +                       continue;
>>> +
>>> +               if (pchip->pin_busy[p]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p);
>>> +                       goto busy_exit;
>>> +               }
>>> +
>>> +               if (pchip->pin_busy[p + 1]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+1);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't see why you are doing this and keeping track of
>> pins as "busy" or not. One thing the pin control core does
>> is to make sure pins do not collide in different use cases,
>> it seems like you're re-implementing this check again.
>>
>>> +               if (p == 64)
>>> +                       continue;
>>
>> This if-clause seems dubious. At least add a comment as
>> to what is happening here and why.
>>
>>> +               if (pchip->pin_busy[p + 2]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+2);
>>> +                       goto busy_exit;
>>> +               }
>>> +
>>> +               if (pchip->pin_busy[p + 3]) {
>>> +                       pr_err("%s:%d:%d %d busy\n",
>>> +                              __func__, __LINE__, pins[i], p+3);
>>> +                       goto busy_exit;
>>> +               }
>>
>> I don't understand this either.
>>
>> Why all this fuzz around the four pins from p ... p+3?
>>
>>> +               continue;
>>> +busy_exit:
>>> +               spin_unlock_irqrestore(&pchip->lock, flags);
>>> +               pr_err("%s:%d Take a look!\n", __func__, __LINE__);
>>> +               return -EBUSY;
>>> +       }
>>
>> You don't have to have the busy_exit: inside the for-loop right?
>>
>> Just push it below the return 0; in the end of this function
>> so you can get rid of the dangling continue;
>>
>>> +       pr_debug("Going to enable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       for (i = 0; i < j; i++) {
>>> +               int p = pins[i];
>>> +
>>> +               pr_debug(" %d", p);
>>> +               pchip->pin_busy[p] = true;
>>
>> So I'm questioning this....
>>
>>> +               if (p < PINS_VIRT) /* Not for virtual pins */
>>
>> We need an explanation somewhere about what "virtual pins"
>> means in this driver, I have never seen that before.
>>
>>> +                       writel(mb86s7x_pmx_funcs[func_selector].prog_val,
>>> +                              pchip->base + p / 4 * 4);
>>
>> This may need some static inline like described above to simplify
>> the code and make it more readable, but I see what is going on.
>>
>>> +       }
>>> +
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +
>>> +       return 0;
>>> +}
>>> +
>>> +static void
>>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>>> +                   unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       pr_debug("Going to disable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +       for (i = 0; i < j; i++) {
>>> +               pr_debug(" %d", pins[i]);
>>> +               pchip->pin_busy[pins[i]] = false;
>>> +       }
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +}
>>
>> Remove this function. The .disable member is gone from
>> struct pinmux_ops, as it was ambiguous, see commit
>> 2243a87d90b42eb38bc281957df3e57c712b5e56
>> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>>
> Hmm... I just got bitten by this while updating the patchset. Sorry I
> missed the patch review.
>
> The reasons given in changelog are
>   (1) 'Fix' desc->mux_usecount
>   (2) The .disable callback is not useful for any of the existing platforms.
>
> Well, for (2) I think it is only reasonable for the provider to need
> to know when some resource is released by a user just as when it was
> requested.
>    Even if the core ensures only one user is provided access to a pin,
> the controller driver may still need to know when a pin is no more in
> use. For ex, within consecutive 4 pins my controller can not enable a
> pin for some function if any in the bunch is already
> enabled/configured. So I need to know if the pin has been
> disabled/released, so I can go ahead enabling the 'neighbor' pin for
> some other role.
>
> For reason (1), there should be some way to fix within the core?
>
Polite ping...

I can't find a way around not knowing when a pin is let free. Do we
revert the commit and fix the situation for the author's platform
instead?

Thanks
Jassi

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

* Re: [PATCH 5/8] pinctrl: add driver for MB86S7x
  2014-08-22  7:46           ` Jassi Brar
@ 2014-09-03  9:17               ` Linus Walleij
  -1 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-09-03  9:17 UTC (permalink / raw)
  To: Jassi Brar
  Cc: Mollie Wu, linux-arm-kernel-IAPFreCvJWM7uuMidbF8XUB+6BGkLq7r,
	devicetree-u79uwXL29TY76Z2rM5mHXA, Andy Green, Patch Tracking,
	Russell King - ARM Linux, Arnd Bergmann, Olof Johansson,
	Mark Rutland, Rob Herring, Pawel Moll, Tetsuya Takinishi, FanWu,
	Stephen Warren

On Fri, Aug 22, 2014 at 9:46 AM, Jassi Brar <jaswinder.singh-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org> wrote:

>>> +static void
>>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>>> +                   unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       pr_debug("Going to disable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +       for (i = 0; i < j; i++) {
>>> +               pr_debug(" %d", pins[i]);
>>> +               pchip->pin_busy[pins[i]] = false;
>>> +       }
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +}
>>
>> Remove this function. The .disable member is gone from
>> struct pinmux_ops, as it was ambiguous, see commit
>> 2243a87d90b42eb38bc281957df3e57c712b5e56
>> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>>
> Hmm... I just got bitten by this while updating the patchset. Sorry I
> missed the patch review.
>
> The reasons given in changelog are
>   (1) 'Fix' desc->mux_usecount
>   (2) The .disable callback is not useful for any of the existing platforms.
>
> Well, for (2) I think it is only reasonable for the provider to need
> to know when some resource is released by a user just as when it was
> requested.

Well the .enable() callback is probably badly named. It's not like
it's requesting a resource, it's just setting the hardware into some
defined state. It should maybe be named .set_mux() or something.

The .disable() call is then pointless because the mux is always
set up in some way, there is no "unmuxed" state.

>    Even if the core ensures only one user is provided access to a pin,
> the controller driver may still need to know when a pin is no more in
> use. For ex, within consecutive 4 pins my controller can not enable a
> pin for some function if any in the bunch is already
> enabled/configured. So I need to know if the pin has been
> disabled/released, so I can go ahead enabling the 'neighbor' pin for
> some other role.

But should not that other role of that pin come from some state
transition in the pin control system anyway?

It is of course possible to add an optional .pin_became_free()
callback down to the driver if I can just wrap my head around
the use case.

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

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

* [PATCH 5/8] pinctrl: add driver for MB86S7x
@ 2014-09-03  9:17               ` Linus Walleij
  0 siblings, 0 replies; 88+ messages in thread
From: Linus Walleij @ 2014-09-03  9:17 UTC (permalink / raw)
  To: linux-arm-kernel

On Fri, Aug 22, 2014 at 9:46 AM, Jassi Brar <jaswinder.singh@linaro.org> wrote:
> On 22 July 2014 21:41, Linus Walleij <linus.walleij@linaro.org> wrote:
>> On Sun, Jul 13, 2014 at 8:31 AM, Mollie Wu <mollie.wu@linaro.org> wrote:

>>> +static void
>>> +mb86s70_pmx_disable(struct pinctrl_dev *pctl,
>>> +                   unsigned func_selector, unsigned group_selector)
>>> +{
>>> +       struct mb86s70_pmux_chip *pchip = pinctrl_dev_get_drvdata(pctl);
>>> +       unsigned long flags;
>>> +       const unsigned *pins;
>>> +       int i, j;
>>> +
>>> +       if (group_selector >= grp_count) {
>>> +               pr_err("%s:%d\n", __func__, __LINE__);
>>> +               return;
>>> +       }
>>> +
>>> +       j = mb86s7x_pgrps[group_selector].num_pins;
>>> +       pins = mb86s7x_pgrps[group_selector].pins;
>>> +
>>> +       pr_debug("Going to disable %s on pins -",
>>> +                mb86s7x_pmx_funcs[func_selector].name);
>>> +       spin_lock_irqsave(&pchip->lock, flags);
>>> +       for (i = 0; i < j; i++) {
>>> +               pr_debug(" %d", pins[i]);
>>> +               pchip->pin_busy[pins[i]] = false;
>>> +       }
>>> +       spin_unlock_irqrestore(&pchip->lock, flags);
>>> +       pr_debug("\n");
>>> +}
>>
>> Remove this function. The .disable member is gone from
>> struct pinmux_ops, as it was ambiguous, see commit
>> 2243a87d90b42eb38bc281957df3e57c712b5e56
>> "pinctrl: avoid duplicated calling enable_pinmux_setting for a pin"
>>
> Hmm... I just got bitten by this while updating the patchset. Sorry I
> missed the patch review.
>
> The reasons given in changelog are
>   (1) 'Fix' desc->mux_usecount
>   (2) The .disable callback is not useful for any of the existing platforms.
>
> Well, for (2) I think it is only reasonable for the provider to need
> to know when some resource is released by a user just as when it was
> requested.

Well the .enable() callback is probably badly named. It's not like
it's requesting a resource, it's just setting the hardware into some
defined state. It should maybe be named .set_mux() or something.

The .disable() call is then pointless because the mux is always
set up in some way, there is no "unmuxed" state.

>    Even if the core ensures only one user is provided access to a pin,
> the controller driver may still need to know when a pin is no more in
> use. For ex, within consecutive 4 pins my controller can not enable a
> pin for some function if any in the bunch is already
> enabled/configured. So I need to know if the pin has been
> disabled/released, so I can go ahead enabling the 'neighbor' pin for
> some other role.

But should not that other role of that pin come from some state
transition in the pin control system anyway?

It is of course possible to add an optional .pin_became_free()
callback down to the driver if I can just wrap my head around
the use case.

Yours,
Linus Walleij

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

end of thread, other threads:[~2014-09-03  9:17 UTC | newest]

Thread overview: 88+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
     [not found] <message-id-of-cover-letter>
2014-07-13  6:28 ` [PATCH 1/8] ARM: Add platform support for Fujitsu MB86S7X SoCs Mollie Wu
2014-07-13  6:28   ` Mollie Wu
     [not found]   ` <1405232911-4569-1-git-send-email-mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-07-14 13:33     ` Arnd Bergmann
2014-07-14 13:33       ` Arnd Bergmann
2014-07-15 17:37       ` Jassi Brar
2014-07-15 17:37         ` Jassi Brar
     [not found]         ` <CAJe_Zhc9MVrX_Yi_u91qeSCWdO65MqEro=eQ1U_nWPfzM0ayjw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-15 20:09           ` Arnd Bergmann
2014-07-15 20:09             ` Arnd Bergmann
2014-07-17 13:32             ` Jassi Brar
2014-07-17 13:32               ` Jassi Brar
     [not found]               ` <CAJe_Zhc91g1=CTJUBaFaqk9Np4u4oEqddZEx_zeVxn7EdMEjmw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-17 13:48                 ` Arnd Bergmann
2014-07-17 13:48                   ` Arnd Bergmann
2014-07-17 16:54                   ` Jassi Brar
2014-07-17 16:54                     ` Jassi Brar
2014-07-17 17:12                     ` Arnd Bergmann
2014-07-17 17:12                       ` Arnd Bergmann
2014-07-15 15:11     ` Rob Herring
2014-07-15 15:11       ` Rob Herring
     [not found]       ` <CAL_JsqKvwxjEDZPVwL646PhPKr7oemBzxr6cZ+X=uJQ2u_XZ8g-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-15 16:11         ` Nicolas Pitre
2014-07-15 16:11           ` Nicolas Pitre
2014-07-15 18:03         ` Jassi Brar
2014-07-15 18:03           ` Jassi Brar
2014-07-16  5:52         ` Andy Green
2014-07-16  5:52           ` Andy Green
2014-07-15 17:05   ` Nicolas Pitre
2014-07-15 17:05     ` Nicolas Pitre
     [not found]     ` <alpine.LFD.2.11.1407151214070.3647-fMhRO7WWcppj+hNMo8g0rg@public.gmane.org>
2014-07-15 18:16       ` Jassi Brar
2014-07-15 18:16         ` Jassi Brar
2014-07-13  6:29 ` [PATCH 2/8] mmc: sdhci: host: add new f_sdh30 Mollie Wu
2014-07-13  6:29   ` Mollie Wu
2014-07-14 14:04   ` Arnd Bergmann
2014-07-14 14:04     ` Arnd Bergmann
2014-07-16  9:35     ` Vincent.Yang
2014-07-16  9:35       ` Vincent.Yang
2014-07-16 10:10       ` Arnd Bergmann
2014-07-16 10:10         ` Arnd Bergmann
2014-07-16 11:07         ` Vincent.Yang
2014-07-16 11:07           ` Vincent.Yang
2014-07-13  6:30 ` [PATCH 3/8] mmc: core: add manual resume capability Mollie Wu
2014-07-13  6:30   ` Mollie Wu
2014-07-13  6:30 ` [PATCH 4/8] clk: Add clock driver for mb86s7x Mollie Wu
2014-07-13  6:30   ` Mollie Wu
     [not found]   ` <1405233052-4688-1-git-send-email-mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-07-14 14:08     ` Arnd Bergmann
2014-07-14 14:08       ` Arnd Bergmann
2014-07-16  7:09       ` Jassi Brar
2014-07-16  7:09         ` Jassi Brar
2014-07-13  6:31 ` [PATCH 5/8] pinctrl: add driver for MB86S7x Mollie Wu
2014-07-13  6:31   ` Mollie Wu
     [not found]   ` <1405233067-4725-1-git-send-email-mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-07-22 16:11     ` Linus Walleij
2014-07-22 16:11       ` Linus Walleij
     [not found]       ` <CACRpkdb_NC7j=XkbMPv8YYLSeEd1AOpFqmD8x+ohC7ds3cPCfg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-24 18:04         ` Jassi Brar
2014-07-24 18:04           ` Jassi Brar
     [not found]           ` <CAJe_Zhc2gz8DZjzo-4YAucAZ2P=UamZ2MtTP1fHmxC0vspZJDw-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-08-08 12:42             ` Linus Walleij
2014-08-08 12:42               ` Linus Walleij
2014-08-22  7:46         ` Jassi Brar
2014-08-22  7:46           ` Jassi Brar
     [not found]           ` <CAJe_Zhe2VuzHtkojm9nxvDTjT4AL7Znvma+5T7JTadvFJ1vAJQ-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-08-27 16:58             ` Jassi Brar
2014-08-27 16:58               ` Jassi Brar
2014-09-03  9:17             ` Linus Walleij
2014-09-03  9:17               ` Linus Walleij
2014-07-13  6:31 ` [PATCH 6/8] net: ethernet driver: Fujitsu OGMA Mollie Wu
2014-07-13  6:31   ` Mollie Wu
2014-07-14  9:06   ` Tobias Klauser
2014-07-14  9:06     ` Tobias Klauser
2014-07-14 10:36     ` Andy Green
2014-07-14 10:36       ` Andy Green
2014-07-14 13:50   ` Arnd Bergmann
2014-07-14 13:50     ` Arnd Bergmann
2014-07-14 14:00     ` Andy Green
2014-07-13  6:32 ` [PATCH 7/8] mailbox: f_mhu: add driver for Fujitsu MHU controller Mollie Wu
2014-07-13  6:32   ` Mollie Wu
     [not found]   ` <1405233128-4799-1-git-send-email-mollie.wu-QSEj5FYQhm4dnm+yROfE0A@public.gmane.org>
2014-07-16 17:37     ` Sudeep Holla
2014-07-16 17:37       ` Sudeep Holla
     [not found]       ` <53C6B83D.80602-5wv7dgnIgG8@public.gmane.org>
2014-07-17  6:25         ` Jassi Brar
2014-07-17  6:25           ` Jassi Brar
2014-07-17 10:31           ` Sudeep Holla
2014-07-17 10:31             ` Sudeep Holla
     [not found]             ` <53C7A5F1.30209-5wv7dgnIgG8@public.gmane.org>
2014-07-17 12:56               ` Jassi Brar
2014-07-17 12:56                 ` Jassi Brar
     [not found]                 ` <CAJe_ZhegVczzcRVikt=nWnwU3nGx2G2mNg9nGR3bF4FfHnPeEg-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-17 15:09                   ` Sudeep Holla
2014-07-17 15:09                     ` Sudeep Holla
     [not found]                     ` <53C7E71C.8020501-5wv7dgnIgG8@public.gmane.org>
2014-07-17 17:07                       ` Jassi Brar
2014-07-17 17:07                         ` Jassi Brar
     [not found]                         ` <CAJe_ZhcROjhXebJetyJus43jNKoBU60jXXRLwnHwMdPJ7LPq7Q-JsoAwUIsXosN+BqQ9rBEUg@public.gmane.org>
2014-07-17 18:51                           ` Sudeep Holla
2014-07-17 18:51                             ` Sudeep Holla
     [not found]                             ` <53C81B3E.2020503-5wv7dgnIgG8@public.gmane.org>
2014-07-18  9:06                               ` Jassi Brar
2014-07-18  9:06                                 ` Jassi Brar
2014-07-13  6:32 ` =?y?q?=5BPATCH=208/8=5D=20of=3A=20add=20Fujitsu=20vendor=20prefix?= Mollie Wu

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.