Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH 3/3] EDAC: armada_xp: Add support for more SoCs
From: Chris Packham @ 2018-01-08 22:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108223158.21930-1-chris.packham@alliedtelesis.co.nz>

The Armada 38x and other integrated SoCs use a reduced pin count so the
width of the SDRAM interface is smaller than the Armada XP SoCs. This
means that the definition of "full" and "half" width is reduced from
64/32 to 32/16.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 drivers/edac/armada_xp_edac.c | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/drivers/edac/armada_xp_edac.c b/drivers/edac/armada_xp_edac.c
index cb9173b30aa9..40cef261e61b 100644
--- a/drivers/edac/armada_xp_edac.c
+++ b/drivers/edac/armada_xp_edac.c
@@ -347,6 +347,11 @@ static int armada_xp_mc_edac_probe(struct platform_device *pdev)
 
 	armada_xp_mc_edac_read_config(mci);
 
+	/* These SoCs have a reduced width bus */
+	if (of_machine_is_compatible("marvell,armada380") ||
+	    of_machine_is_compatible("marvell,armadaxp-98dx3236"))
+		drvdata->width /= 2;
+
 	/* configure SBE threshold */
 	/* it seems that SBEs are not captured otherwise */
 	writel(1 << SDRAM_ERR_CTRL_ERR_THR_OFFSET,
-- 
2.15.1

^ permalink raw reply related

* [PATCH 2/3] ARM: dts: mvebu: add sdram controller node to Armada-38x
From: Chris Packham @ 2018-01-08 22:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108223158.21930-1-chris.packham@alliedtelesis.co.nz>

The Armada-38x uses an SDRAM controller that is compatible with the
Armada-XP. The key difference is the width of the bus (XP is 64/32, 38x
is 32/16). The SDRAM controller registers are the same between the two
SoCs.

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 arch/arm/boot/dts/armada-38x.dtsi | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/armada-38x.dtsi b/arch/arm/boot/dts/armada-38x.dtsi
index 00ff549d4e39..6d34c5ec178f 100644
--- a/arch/arm/boot/dts/armada-38x.dtsi
+++ b/arch/arm/boot/dts/armada-38x.dtsi
@@ -138,6 +138,11 @@
 			#size-cells = <1>;
 			ranges = <0 MBUS_ID(0xf0, 0x01) 0 0x100000>;
 
+			sdramc at 1400 {
+				compatible = "marvell,armada-xp-sdram-controller";
+				reg = <0x1400 0x500>;
+			};
+
 			L2: cache-controller at 8000 {
 				compatible = "arm,pl310-cache";
 				reg = <0x8000 0x1000>;
-- 
2.15.1

^ permalink raw reply related

* [PATCH 1/3] ARM: dts: enable L2 cache parity and ecc on db-xc3-24g4xg board
From: Chris Packham @ 2018-01-08 22:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108223158.21930-1-chris.packham@alliedtelesis.co.nz>

Signed-off-by: Chris Packham <chris.packham@alliedtelesis.co.nz>
---
 arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts | 5 +++++
 1 file changed, 5 insertions(+)

diff --git a/arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts b/arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts
index 06fce35d7491..00ca489fc788 100644
--- a/arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts
+++ b/arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts
@@ -70,6 +70,11 @@
 	};
 };
 
+&L2 {
+	arm,parity-enable;
+	marvell,ecc-enable;
+};
+
 &devbus_bootcs {
 	status = "okay";
 
-- 
2.15.1

^ permalink raw reply related

* [PATCH 0/3] EDAC: support for Armada 38x and 98dx3236 SoCs
From: Chris Packham @ 2018-01-08 22:31 UTC (permalink / raw)
  To: linux-arm-kernel

This series applies on top of Jan Lubbe's "EDAC drivers for Armada XP L2 and
DDR" series[1].

The Armada 38x as well as the 98dx3236 and similar switch chips with integrated
CPUs use the same SDRAM controller block as the Armada XP. The key difference
is the width of the DDR interface.

[1] - https://marc.info/?l=linux-edac&m=151030475715706&w=2

Chris Packham (3):
  ARM: dts: enable L2 cache parity and ecc on db-xc3-24g4xg board
  ARM: dts: mvebu: add sdram controller node to Armada-38x
  EDAC: armada_xp: Add support for more SoCs

 arch/arm/boot/dts/armada-38x.dtsi             | 5 +++++
 arch/arm/boot/dts/armada-xp-db-xc3-24g4xg.dts | 5 +++++
 drivers/edac/armada_xp_edac.c                 | 5 +++++
 3 files changed, 15 insertions(+)

-- 
2.15.1

^ permalink raw reply

* [RFC 5/5] pwm: pwm-omap-dmtimer: Add capture functionality
From: Ladislav Michl @ 2018-01-08 22:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108221327.GW3875@atomide.com>

On Mon, Jan 08, 2018 at 02:13:27PM -0800, Tony Lindgren wrote:
> * Ladislav Michl <ladis@linux-mips.org> [180108 22:09]:
> > On Mon, Jan 08, 2018 at 01:59:31PM -0800, Tony Lindgren wrote:
> > > * Ladislav Michl <ladis@linux-mips.org> [180108 15:46]:
> > > > Here it seems hardware can capture both edges, but I do not see a way
> > > > how to tell it I want start from either low to high or high to low
> > > > transition. Clues?
> > > 
> > > At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you
> > > probably know that already..
> > > 
> > > If you're having hard time getting things starting, maybe something
> > > like this helps:
> > > 
> > > stop timer in TCLR register
> > > configure timer in TCLR
> > > write some value to TLDR, maybe 0?
> > > set ST bit in TCLR to start
> > 
> > Let me clarify it a bit more. I have no problem starting timer and capture
> > events. I just didn't find a way how to tell hardware I want to start
> > with for example rising edge, so rising edge goes to TCAR1 and failing edge
> > to TCAR2. Substracting those gives pulse width.
> 
> Oh I see, yeah that would be cool :) Maybe you can first configure
> an interrupt to trigger on rising edge, then configure things for
> falling edge, then subtract..

That will work only for very long periods. I did test where value captured
into TCAR was compared to value read from TCRR. Sometimes those differ by
few hundreds microseconds...

I started this work to overcome GPIO edge interrupts latency, but so far
I'm a bit dissapointed.

(also please read the code, reconfiguration is done to get period and
duty cycle - but in case of duty cycle it is not clear whenever we are
measuring pulse or space length)

	ladis

^ permalink raw reply

* [PATCHv3] dt-bindings: Avago APDS990X light sensor
From: Pavel Machek @ 2018-01-08 22:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180105160137.nr7oqvpbp2oao5fm@rob-hp-laptop>

Hi!

> > From: Filip Matijevi? <filip.matijevic.pz@gmail.com>
> > 
> > This prepares binding for light sensor used in Nokia N9.
> 
> "dt-bindings: ..." is the preferred subject prefix.

Ok.

> > +++ b/Documentation/devicetree/bindings/misc/avago-apds990x.txt
> 
> Put this with other light sensors whether you use IIO or not:
> 
> bindings/iio/light/

Makes sense. Patches to convert to IIO are available.

> > @@ -0,0 +1,41 @@
> > +Avago APDS990X driver
> 
> Bindings aren't drivers.

Ok.

> > +https://docs.broadcom.com/docs/AV02-2867EN
> > +
> > +Required properties:
> > +- compatible: "avago,apds990x"
> > +- reg: address on the I2C bus
> > +- interrupts: external interrupt line number
> > +- vdd-supply: power supply for VDD
> > +- vled-supply: power supply for LEDA
> > +- avago,ga: Glass attenuation
> 
> We already have "upisemi,glass-coef". Can we align on something
> common.

upisemi seems to have different units (1/1000 vs. 1/4096). Do you want
me to do change to avago,glass-coef or ....?

> > +- avago,cf1: Clear channel factor 1
> > +- avago,irf1: IR channel factor 1
> > +- avago,cf2: Clear channel factor 2
> > +- avago,irf2: IR channel factor 2
> 
> Perhaps 2 properties with 2 cells for factor 1 and 2.

This is obviously easier to keep, but I can change.

> > +- avago,df: Device factor
> 
> Units/range for all these?

Fixed point, with 4096 == 1.00.

> > +- avago,pdrive: IR current, one of APDS_IRLED_CURR_XXXmA values
> 
> Don't we have standard current property for LEDs?

We do have "led-max-microamp"; which is not quite what this is. I
guess we can do "led-microamp" if you prefer?

> > +- avago,ppcount: Proximity pulse count
> 
> Is this standard for prox sensors?

I don't know many proximity sensors. Seems like way to raise sensitivity.

Best regards,
								Pavel

-- 
(english) http://www.livejournal.com/~pavelmachek
(cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: Digital signature
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180108/d1bebcf2/attachment.sig>

^ permalink raw reply

* [Intel-wired-lan] [PATCH 01/27] timecounter: Make cyclecounter struct part of timecounter struct
From: Brown, Aaron F @ 2018-01-08 22:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1513323522-15021-2-git-send-email-sagar.a.kamble@intel.com>

> From: Intel-wired-lan [mailto:intel-wired-lan-bounces at osuosl.org] On
> Behalf Of Sagar Arun Kamble
> Sent: Thursday, December 14, 2017 11:38 PM
> To: linux-kernel at vger.kernel.org
> Cc: alsa-devel at alsa-project.org; linux-rdma at vger.kernel.org;
> netdev at vger.kernel.org; Richard Cochran <richardcochran@gmail.com>;
> Stephen Boyd <sboyd@codeaurora.org>; Chris Wilson <chris@chris-
> wilson.co.uk>; John Stultz <john.stultz@linaro.org>; intel-wired-
> lan at lists.osuosl.org; Thomas Gleixner <tglx@linutronix.de>; Kamble, Sagar A
> <sagar.a.kamble@intel.com>; kvmarm at lists.cs.columbia.edu; linux-arm-
> kernel at lists.infradead.org
> Subject: [Intel-wired-lan] [PATCH 01/27] timecounter: Make cyclecounter
> struct part of timecounter struct
> 
> There is no real need for the users of timecounters to define cyclecounter
> and timecounter variables separately. Since timecounter will always be
> based on cyclecounter, have cyclecounter struct as member of timecounter
> struct.
> 
> v2: Rebase.
> 
> Suggested-by: Chris Wilson <chris@chris-wilson.co.uk>
> Signed-off-by: Sagar Arun Kamble <sagar.a.kamble@intel.com>
> Cc: Chris Wilson <chris@chris-wilson.co.uk>
> Cc: Richard Cochran <richardcochran@gmail.com>
> Cc: John Stultz <john.stultz@linaro.org>
> Cc: Thomas Gleixner <tglx@linutronix.de>
> Cc: Stephen Boyd <sboyd@codeaurora.org>
> Cc: linux-kernel at vger.kernel.org
> Cc: linux-arm-kernel at lists.infradead.org
> Cc: netdev at vger.kernel.org
> Cc: intel-wired-lan at lists.osuosl.org
> Cc: linux-rdma at vger.kernel.org
> Cc: alsa-devel at alsa-project.org
> Cc: kvmarm at lists.cs.columbia.edu
> Acked-by: Jeff Kirsher <jeffrey.t.kirsher@intel.com> (Intel drivers)
> ---
>  arch/microblaze/kernel/timer.c                     | 20 ++++++------
>  drivers/clocksource/arm_arch_timer.c               | 19 ++++++------
>  drivers/net/ethernet/amd/xgbe/xgbe-dev.c           |  3 +-
>  drivers/net/ethernet/amd/xgbe/xgbe-ptp.c           |  9 +++---
>  drivers/net/ethernet/amd/xgbe/xgbe.h               |  1 -
>  drivers/net/ethernet/broadcom/bnx2x/bnx2x.h        |  1 -
>  drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c   | 20 ++++++------
>  drivers/net/ethernet/freescale/fec.h               |  1 -
>  drivers/net/ethernet/freescale/fec_ptp.c           | 30 +++++++++---------
>  drivers/net/ethernet/intel/e1000e/e1000.h          |  1 -
>  drivers/net/ethernet/intel/e1000e/netdev.c         | 27 ++++++++--------
>  drivers/net/ethernet/intel/e1000e/ptp.c            |  2 +-
>  drivers/net/ethernet/intel/igb/igb.h               |  1 -
>  drivers/net/ethernet/intel/igb/igb_ptp.c           | 25 ++++++++-------
>  drivers/net/ethernet/intel/ixgbe/ixgbe.h           |  1 -
>  drivers/net/ethernet/intel/ixgbe/ixgbe_ptp.c       | 17 +++++-----
>  drivers/net/ethernet/mellanox/mlx4/en_clock.c      | 28 ++++++++---------
>  drivers/net/ethernet/mellanox/mlx4/mlx4_en.h       |  1 -
>  .../net/ethernet/mellanox/mlx5/core/lib/clock.c    | 34 ++++++++++----------
>  drivers/net/ethernet/qlogic/qede/qede_ptp.c        | 20 ++++++------
>  drivers/net/ethernet/ti/cpts.c                     | 36 ++++++++++++----------
>  drivers/net/ethernet/ti/cpts.h                     |  1 -
>  include/linux/mlx5/driver.h                        |  1 -
>  include/linux/timecounter.h                        |  4 +--
>  include/sound/hdaudio.h                            |  1 -
>  kernel/time/timecounter.c                          | 28 ++++++++---------
>  sound/hda/hdac_stream.c                            |  7 +++--
>  virt/kvm/arm/arch_timer.c                          |  6 ++--
>  28 files changed, 163 insertions(+), 182 deletions(-)
> 

For Intel e1000e and igb drivers:
Tested-by: Aaron Brown <aaron.f.brown@intel.com>

^ permalink raw reply

* [RFC PATCH 2/2] drivers: clk: Add ZynqMP clock driver
From: Jolly Shah @ 2018-01-08 22:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449797-5629-1-git-send-email-jollys@xilinx.com>

This patch adds CCF compliant clock driver for ZynqMP.
Clock driver queries supported clock information from
firmware and regiters pll and output clocks with CCF.

Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
Signed-off-by: Tejas Patel <tejasp@xilinx.com>
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
---
 .../devicetree/bindings/clock/zynq_mpsoc.txt       | 163 +++++
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/zynqmp/Kconfig                         |   8 +
 drivers/clk/zynqmp/Makefile                        |   3 +
 drivers/clk/zynqmp/clk-gate-zynqmp.c               | 158 +++++
 drivers/clk/zynqmp/clk-mux-zynqmp.c                | 190 ++++++
 drivers/clk/zynqmp/clkc.c                          | 707 +++++++++++++++++++++
 drivers/clk/zynqmp/divider.c                       | 239 +++++++
 drivers/clk/zynqmp/pll.c                           | 384 +++++++++++
 include/linux/clk/zynqmp.h                         |  46 ++
 11 files changed, 1900 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
 create mode 100644 drivers/clk/zynqmp/Kconfig
 create mode 100644 drivers/clk/zynqmp/Makefile
 create mode 100644 drivers/clk/zynqmp/clk-gate-zynqmp.c
 create mode 100644 drivers/clk/zynqmp/clk-mux-zynqmp.c
 create mode 100644 drivers/clk/zynqmp/clkc.c
 create mode 100644 drivers/clk/zynqmp/divider.c
 create mode 100644 drivers/clk/zynqmp/pll.c
 create mode 100644 include/linux/clk/zynqmp.h

diff --git a/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
new file mode 100644
index 0000000..9061b57
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
@@ -0,0 +1,163 @@
+Device Tree Clock bindings for the Zynq Ultrascale+ MPSoC
+
+The Zynq Ultrascale+ MPSoC has several different clk providers,
+each with there own bindings.
+The purpose of this document is to document their usage.
+
+See clock_bindings.txt for more information on the generic clock bindings.
+
+== Clock Controller ==
+The clock controller is a logical abstraction of Zynq Ultrascale+ MPSoC clock
+tree. It reads required input clock frequencies from the devicetree and acts
+as clock provider for all clock consumers of PS clocks.
+
+Required properties:
+ - #clock-cells : Must be 1
+ - compatible : "xlnx,zynqmp-clkc"
+ - clocks : list of clock specifiers which are external input clocks to the
+	    given clock controller. Please refer the next section to find
+	    the input clocks for a given controller.
+ - clock-names : list of names of clocks which are exteral input clocks to the
+		 given clock controller. Please refer to the clock bindings
+		 for more details
+
+Input clocks for zynqmp Ultrascale+ clock controller:
+The Zynq UltraScale+ MPSoC has one primary and four alternative reference clock
+inputs.
+These required clock inputs are the
+ - pss_ref_clk (PS reference clock)
+ - video_clk (reference clock for video system )
+ - pss_alt_ref_clk (alternative PS reference clock)
+ - aux_ref_clk
+ - gt_crx_ref_clk (transceiver reference clock)
+
+The following strings are optional parameters to the 'clock-names' property in
+order to provide an optional (E)MIO clock source.
+ - swdt0_ext_clk
+ - swdt1_ext_clk
+ - gem0_emio_clk
+ - gem1_emio_clk
+ - gem2_emio_clk
+ - gem3_emio_clk
+ - mio_clk_XX		# with XX = 00..77
+ - mio_clk_50_or_51	#for the mux clock to gem tsu from 50 or 51
+
+
+Output clocks for zynqmp Ultrascale+ clock controller:
+Output clocks are registered based on clock information received from firmware.
+Output clock indexes are mentioned below:
+
+Clock ID:	Output clock name:
+-------------------------------------
+0		iopll
+1		rpll
+2		apll
+3		dpll
+4		vpll
+5		iopll_to_fpd
+6		rpll_to_fpd
+7		apll_to_lpd
+8		dpll_to_lpd
+9		vpll_to_lpd
+10		acpu
+11		acpu_half
+12		dbf_fpd
+13		dbf_lpd
+14		dbg_trace
+15		dbg_tstmp
+16		dp_video_ref
+17		dp_audio_ref
+18		dp_stc_ref
+19		gdma_ref
+20		dpdma_ref
+21		ddr_ref
+22		sata_ref
+23		pcie_ref
+24		gpu_ref
+25		gpu_pp0_ref
+26		gpu_pp1_ref
+27		topsw_main
+28		topsw_lsbus
+29		gtgref0_ref
+30		lpd_switch
+31		lpd_lsbus
+32		usb0_bus_ref
+33		usb1_bus_ref
+34		usb3_dual_ref
+35		usb0
+36		usb1
+37		cpu_r5
+38		cpu_r5_core
+39		csu_spb
+40		csu_pll
+41		pcap
+42		iou_switch
+43		gem_tsu_ref
+44		gem_tsu
+45		gem0_ref
+46		gem1_ref
+47		gem2_ref
+48		gem3_ref
+49		gem0_tx
+50		gem1_tx
+51		gem2_tx
+52		gem3_tx
+53		qspi_ref
+54		sdio0_ref
+55		sdio1_ref
+56		uart0_ref
+57		uart1_ref
+58		spi0_ref
+59		spi1_ref
+60		nand_ref
+61		i2c0_ref
+62		i2c1_ref
+63		can0_ref
+64		can1_ref
+65		can0
+66		can1
+67		dll_ref
+68		adma_ref
+69		timestamp_ref
+70		ams_ref
+71		pl0_ref
+72		pl1_ref
+73		pl2_ref
+74		pl3_ref
+75		wdt
+76		iopll_int
+77		iopll_pre_src
+78		iopll_half
+79		iopll_int_mux
+80		iopll_post_src
+81		rpll_int
+82		rpll_pre_src
+83		rpll_half
+84		rpll_int_mux
+85		rpll_post_src
+86		apll_int
+87		apll_pre_src
+88		apll_half
+89		apll_int_mux
+90		apll_post_src
+91		dpll_int
+92		dpll_pre_src
+93		dpll_half
+94		dpll_int_mux
+95		dpll_post_src
+96		vpll_int
+97		vpll_pre_src
+98		vpll_half
+99		vpll_int_mux
+100		vpll_post_src
+101		can0_mio
+102		can1_mio
+
+Example:
+
+clkc: clkc at ff5e0020 {
+	#clock-cells = <1>;
+	compatible = "xlnx,zynqmp-clkc";
+	clocks = <&pss_ref_clk>, <&video_clk>, <&pss_alt_ref_clk>, <&aux_ref_clk>, <&gt_crx_ref_clk>;
+	clock-names = "pss_ref_clk", "video_clk", "pss_alt_ref_clk","aux_ref_clk", "gt_crx_ref_clk"
+};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index 1c4e1aa..526f4f5 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -239,6 +239,7 @@ source "drivers/clk/samsung/Kconfig"
 source "drivers/clk/sunxi-ng/Kconfig"
 source "drivers/clk/tegra/Kconfig"
 source "drivers/clk/ti/Kconfig"
+source "drivers/clk/zynqmp/Kconfig"
 source "drivers/clk/uniphier/Kconfig"
 
 endmenu
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index f7f761b..d7328b4 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -98,3 +98,4 @@ obj-$(CONFIG_X86)			+= x86/
 endif
 obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
+obj-$(CONFIG_COMMON_CLK_ZYNQMP)         += zynqmp/
diff --git a/drivers/clk/zynqmp/Kconfig b/drivers/clk/zynqmp/Kconfig
new file mode 100644
index 0000000..a6d54e9
--- /dev/null
+++ b/drivers/clk/zynqmp/Kconfig
@@ -0,0 +1,8 @@
+config COMMON_CLK_ZYNQMP
+	bool "Support for Xilinx ZynqMP Ultrascale+ clock controllers"
+	depends on OF
+	depends on ARCH_ZYNQMP || COMPILE_TEST
+	help
+	  Support for the Zynqmp Ultrascale clock controller.
+	  It has a dependency on the PMU firmware.
+	  Say Y if you want to support clock support
diff --git a/drivers/clk/zynqmp/Makefile b/drivers/clk/zynqmp/Makefile
new file mode 100644
index 0000000..7d50f7a
--- /dev/null
+++ b/drivers/clk/zynqmp/Makefile
@@ -0,0 +1,3 @@
+# Zynq Ultrascale+ MPSoC clock specific Makefile
+
+obj-$(CONFIG_ARCH_ZYNQMP)	+= pll.o clk-gate-zynqmp.o divider.o clk-mux-zynqmp.o clkc.o
diff --git a/drivers/clk/zynqmp/clk-gate-zynqmp.c b/drivers/clk/zynqmp/clk-gate-zynqmp.c
new file mode 100644
index 0000000..45eeed8
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-gate-zynqmp.c
@@ -0,0 +1,158 @@
+/*
+ * Zynq UltraScale+ MPSoC clock controller
+ *
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Gated clock implementation
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk/zynqmp.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+
+/**
+ * struct clk_gate - gating clock
+ *
+ * @hw:	handle between common and hardware-specific interfaces
+ * @flags:	hardware-specific flags
+ * @clk_id:	Id of clock
+ */
+struct zynqmp_clk_gate {
+	struct clk_hw hw;
+	u8 flags;
+	u32 clk_id;
+};
+
+#define to_zynqmp_clk_gate(_hw) container_of(_hw, struct zynqmp_clk_gate, hw)
+
+/**
+ * zynqmp_clk_gate_enable - Enable clock
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * Return: 0 always
+ */
+static int zynqmp_clk_gate_enable(struct clk_hw *hw)
+{
+	struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = gate->clk_id;
+	int ret = 0;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_enable)
+		return -ENXIO;
+
+	ret = eemi_ops->clock_enable(clk_id);
+
+	if (ret)
+		pr_warn_once("%s() clock enabled failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return 0;
+}
+
+/*
+ * zynqmp_clk_gate_disable - Disable clock
+ * @hw: handle between common and hardware-specific interfaces
+ */
+static void zynqmp_clk_gate_disable(struct clk_hw *hw)
+{
+	struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = gate->clk_id;
+	int ret = 0;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_disable)
+		return;
+
+	ret = eemi_ops->clock_disable(clk_id);
+
+	if (ret)
+		pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+}
+
+/**
+ * zynqmp_clk_gate_is_enable - Check clock state
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * Return: 1 if enabled
+ *         0 if disabled
+ */
+static int zynqmp_clk_gate_is_enabled(struct clk_hw *hw)
+{
+	struct zynqmp_clk_gate *gate = to_zynqmp_clk_gate(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = gate->clk_id;
+	int state, ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getstate)
+		return 0;
+
+	ret = eemi_ops->clock_getstate(clk_id, &state);
+	if (ret)
+		pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return state ? 1 : 0;
+}
+
+const struct clk_ops zynqmp_clk_gate_ops = {
+	.enable = zynqmp_clk_gate_enable,
+	.disable = zynqmp_clk_gate_disable,
+	.is_enabled = zynqmp_clk_gate_is_enabled,
+};
+EXPORT_SYMBOL_GPL(zynqmp_clk_gate_ops);
+
+/**
+ * zynqmp_clk_register_gate - register a gate clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @clk_id: Id of this clock
+ * @parents: name of this clock's parents
+ * @num_parents: number of parents
+ * @flags: framework-specific flags for this clock
+ * @clk_gate_flags: gate-specific flags for this clock
+ *
+ * Return: clock handle of the registered clock gate
+ */
+struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
+				     u32 clk_id, const char * const *parents,
+				     u8 num_parents, unsigned long flags,
+				     u8 clk_gate_flags)
+{
+	struct zynqmp_clk_gate *gate;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the gate */
+	gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+	if (!gate)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &zynqmp_clk_gate_ops;
+	init.flags = flags;
+	init.parent_names = parents;
+	init.num_parents = num_parents;
+
+	/* struct clk_gate assignments */
+	gate->flags = clk_gate_flags;
+	gate->hw.init = &init;
+	gate->clk_id = clk_id;
+
+	clk = clk_register(dev, &gate->hw);
+
+	if (IS_ERR(clk))
+		kfree(gate);
+
+	return clk;
+}
diff --git a/drivers/clk/zynqmp/clk-mux-zynqmp.c b/drivers/clk/zynqmp/clk-mux-zynqmp.c
new file mode 100644
index 0000000..ee36244
--- /dev/null
+++ b/drivers/clk/zynqmp/clk-mux-zynqmp.c
@@ -0,0 +1,190 @@
+/*
+ * Zynq UltraScale+ MPSoC mux
+ *
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/clk/zynqmp.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+
+/*
+ * DOC: basic adjustable multiplexer clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is only affected by parent switching.  No clk_set_rate support
+ * parent - parent is adjustable through clk_set_parent
+ */
+
+/**
+ * struct zynqmp_clk_mux - multiplexer clock
+ *
+ * @hw: handle between common and hardware-specific interfaces
+ * @flags: hardware-specific flags
+ * @clk_id: Id of clock
+ */
+struct zynqmp_clk_mux {
+	struct clk_hw hw;
+	u8 flags;
+	u32 clk_id;
+};
+
+#define to_zynqmp_clk_mux(_hw) container_of(_hw, struct zynqmp_clk_mux, hw)
+
+/**
+ * zynqmp_clk_mux_get_parent - Get parent of clock
+ * @hw: handle between common and hardware-specific interfaces
+ *
+ * Return: Parent index
+ */
+static u8 zynqmp_clk_mux_get_parent(struct clk_hw *hw)
+{
+	struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = mux->clk_id;
+	u32 val;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getparent)
+		return -ENXIO;
+
+	ret = eemi_ops->clock_getparent(clk_id, &val);
+
+	if (ret)
+		pr_warn_once("%s() getparent failed for clock: %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	if (val && (mux->flags & CLK_MUX_INDEX_BIT))
+		val = ffs(val) - 1;
+
+	if (val && (mux->flags & CLK_MUX_INDEX_ONE))
+		val--;
+
+	return val;
+}
+
+/**
+ * zynqmp_clk_mux_set_parent - Set parent of clock
+ * @hw: handle between common and hardware-specific interfaces
+ * @index: Parent index
+ *
+ * Return: 0 always
+ */
+static int zynqmp_clk_mux_set_parent(struct clk_hw *hw, u8 index)
+{
+	struct zynqmp_clk_mux *mux = to_zynqmp_clk_mux(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = mux->clk_id;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_setparent)
+		return -ENXIO;
+
+	if (mux->flags & CLK_MUX_INDEX_BIT)
+		index = 1 << index;
+
+	if (mux->flags & CLK_MUX_INDEX_ONE)
+		index++;
+
+	ret = eemi_ops->clock_setparent(clk_id, index);
+
+	if (ret)
+		pr_warn_once("%s() set parent failed for clock: %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return 0;
+}
+
+const struct clk_ops zynqmp_clk_mux_ops = {
+	.get_parent = zynqmp_clk_mux_get_parent,
+	.set_parent = zynqmp_clk_mux_set_parent,
+	.determine_rate = __clk_mux_determine_rate,
+};
+EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ops);
+
+const struct clk_ops zynqmp_clk_mux_ro_ops = {
+	.get_parent = zynqmp_clk_mux_get_parent,
+};
+EXPORT_SYMBOL_GPL(zynqmp_clk_mux_ro_ops);
+
+/**
+ * zynqmp_clk_register_mux_table - register a mux table with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @clk_id: Id of this clock
+ * @parent_names: name of this clock's parents
+ * @num_parents: number of parents
+ * @flags: framework-specific flags for this clock
+ * @clk_mux_flags: mux-specific flags for this clock
+ *
+ * Return: clock handle of the registered clock mux
+ */
+struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
+					  u32 clk_id,
+					  const char * const *parent_names,
+					  u8 num_parents,
+					  unsigned long flags,
+					  u8 clk_mux_flags)
+{
+	struct zynqmp_clk_mux *mux;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the mux */
+	mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+	if (!mux)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	if (clk_mux_flags & CLK_MUX_READ_ONLY)
+		init.ops = &zynqmp_clk_mux_ro_ops;
+	else
+		init.ops = &zynqmp_clk_mux_ops;
+	init.flags = flags;
+	init.parent_names = parent_names;
+	init.num_parents = num_parents;
+
+	/* struct clk_mux assignments */
+	mux->flags = clk_mux_flags;
+	mux->hw.init = &init;
+	mux->clk_id = clk_id;
+
+	clk = clk_register(dev, &mux->hw);
+
+	if (IS_ERR(clk))
+		kfree(mux);
+
+	return clk;
+}
+EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux_table);
+
+/**
+ * zynqmp_clk_register_mux - register a mux clock with the clock framework
+ * @dev: device that is registering this clock
+ * @name: name of this clock
+ * @clk_id: Id of this clock
+ * @parent_names: name of this clock's parents
+ * @num_parents: number of parents
+ * @flags: framework-specific flags for this clock
+ * @clk_mux_flags: mux-specific flags for this clock
+ *
+ * Return: clock handle of the registered clock mux
+ */
+struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
+				    u32 clk_id, const char **parent_names,
+				    u8 num_parents, unsigned long flags,
+				    u8 clk_mux_flags)
+{
+	return zynqmp_clk_register_mux_table(dev, name, clk_id, parent_names,
+					     num_parents, flags, clk_mux_flags);
+}
+EXPORT_SYMBOL_GPL(zynqmp_clk_register_mux);
diff --git a/drivers/clk/zynqmp/clkc.c b/drivers/clk/zynqmp/clkc.c
new file mode 100644
index 0000000..36bf1c1
--- /dev/null
+++ b/drivers/clk/zynqmp/clkc.c
@@ -0,0 +1,707 @@
+/*
+ * Zynq UltraScale+ MPSoC clock controller
+ *
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Based on drivers/clk/zynq/clkc.c
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/zynqmp.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+
+#define MAX_PARENT			100
+#define MAX_NODES			6
+#define MAX_NAME_LEN			50
+#define MAX_CLOCK			300
+
+#define CLK_INIT_ENABLE_SHIFT           1
+#define CLK_TYPE_SHIFT                  2
+
+#define PM_API_PAYLOAD_LEN		3
+
+#define NA_PARENT			-1
+#define DUMMY_PARENT			-2
+
+#define CLK_TYPE_FIELD_LEN		4
+#define CLK_TOPOLOGY_NODE_OFFSET	16
+#define NODES_PER_RESP			3
+
+#define CLK_TYPE_FIELD_MASK		0xF
+#define CLK_FLAG_FIELD_SHIFT		8
+#define CLK_FLAG_FIELD_MASK		0x3FFF
+#define CLK_TYPE_FLAG_FIELD_SHIFT	24
+#define CLK_TYPE_FLAG_FIELD_MASK	0xFF
+
+#define CLK_PARENTS_ID_LEN              16
+#define CLK_PARENTS_ID_MASK		0xFFFF
+
+/* Flags for parents */
+#define PARENT_CLK_SELF			0
+#define PARENT_CLK_NODE1		1
+#define PARENT_CLK_NODE2		2
+#define PARENT_CLK_NODE3		3
+#define PARENT_CLK_NODE4		4
+#define PARENT_CLK_EXTERNAL		5
+
+#define END_OF_CLK_NAME			"END_OF_CLK"
+#define RESERVED_CLK_NAME		""
+
+#define CLK_VALID_MASK			0x1
+#define CLK_INIT_ENABLE_MASK		(0x1 << CLK_INIT_ENABLE_SHIFT)
+
+enum clk_type {
+	CLK_TYPE_OUTPUT,
+	CLK_TYPE_EXTERNAL,
+};
+
+/**
+ * struct clock_parent - Structure for parent of clock
+ * @id:	Parent clock ID
+ * @flag: Parent flags
+ */
+struct clock_parent {
+	char name[MAX_NAME_LEN];
+	int id;
+	u32 flag;
+};
+
+/**
+ * struct clock_topology - Structure for topology of clock
+ * @type: Type of topology
+ * @flag: Topology flags
+ * @type_flag: Topology type specific flag
+ */
+struct clock_topology {
+	u32 type;
+	u32 flag;
+	u32 type_flag;
+};
+
+/**
+ * struct zynqmp_clock - Structure for clock
+ * @clk_name: Clock name
+ * @valid: Validity flag of clock
+ * @init_enable: init_enable flag of clock
+ * @topology: structure of topology of clock
+ * @num_node: Number of nodes present in topology
+ * @parent: structure of parent of clock
+ * @num_parents: Number of parents of clock
+ * @type: Type of clock
+ */
+struct zynqmp_clock {
+	char clk_name[MAX_NAME_LEN];
+	u32 valid;
+	u32 init_enable;
+	enum clk_type type;
+	struct clock_topology node[MAX_NODES];
+	u32 num_nodes;
+	struct clock_parent parent[MAX_PARENT];
+	u32 num_parents;
+};
+
+static const char clk_type_postfix[][10] = {
+	[TYPE_INVALID] = "",
+	[TYPE_MUX] = "_mux",
+	[TYPE_GATE] = "",
+	[TYPE_DIV1] = "_div1",
+	[TYPE_DIV2] = "_div2",
+	[TYPE_FIXEDFACTOR] = "_ff",
+	[TYPE_PLL] = ""
+};
+
+static struct zynqmp_clock clock[MAX_CLOCK];
+static struct clk_onecell_data zynqmp_clk_data;
+static struct clk *zynqmp_clks[MAX_CLOCK];
+static unsigned int clock_max_idx;
+static const struct zynqmp_eemi_ops *eemi_ops;
+
+/**
+ * is_valid_clock - Check whether clock is valid or not
+ * @clk_id: Clock Index
+ * @valid: 1: if clock is valid
+ *         0: invalid clock
+ *
+ * Return: 0 Success
+ *         Error code: Failure
+ */
+static int is_valid_clock(u32 clk_id, u32 *valid)
+{
+	if (clk_id < 0 || clk_id > clock_max_idx)
+		return -ENODEV;
+
+	*valid = clock[clk_id].valid;
+
+	return *valid ? 0 : -EINVAL;
+}
+
+/**
+ * zynqmp_get_clock_name - Get name of clock from clock index
+ * @clk_id: Clock index
+ * @clk_name: Name of clock
+ *
+ * Return: 0: Success
+ *         Error code: failure
+ */
+static int zynqmp_get_clock_name(u32 clk_id, char *clk_name)
+{
+	int ret;
+	u32 valid;
+
+	ret = is_valid_clock(clk_id, &valid);
+	if (!ret && valid) {
+		strncpy(clk_name, clock[clk_id].clk_name, MAX_NAME_LEN);
+		return 0;
+	} else {
+		return ret;
+	}
+}
+
+/**
+ * get_clock_type - Get type of clock
+ * @clk_id: Clock Index
+ * @type: Clock type: CLK_TYPE_OUTPUT or CLK_TYPE_EXTERNAL
+ *
+ * Return: 0: Success
+ *         Error code: failure
+ */
+static int get_clock_type(u32 clk_id, u32 *type)
+{
+	int ret;
+	u32 valid;
+
+	ret = is_valid_clock(clk_id, &valid);
+	if (!ret && valid) {
+		*type = clock[clk_id].type;
+		return 0;
+	} else {
+		return ret;
+	}
+}
+
+/**
+ * zynqmp_pm_clock_get_name - Get the name of clock for given id
+ * @clock_id: ID of the clock to be queried
+ * @name: Name of given clock
+ *
+ * This function is used to get name of clock specified by given
+ * clock ID.
+ *
+ * Return: Returns status, in case of error name would be 0.
+ */
+static int zynqmp_pm_clock_get_name(u32 clock_id, char *name)
+{
+	struct zynqmp_pm_query_data qdata = {0};
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	qdata.qid = PM_QID_CLOCK_GET_NAME;
+	qdata.arg1 = clock_id;
+
+	eemi_ops->query_data(qdata, ret_payload);
+	memcpy(name, ret_payload, CLK_GET_NAME_RESP_LEN);
+
+	return 0;
+}
+
+/**
+ * zynqmp_pm_clock_get_topology - Get the topology of clock for given id
+ * @clock_id: ID of the clock to be queried
+ * @index: Node index of clock topology
+ * @topology: Buffer to store nodes in topology and flags
+ *
+ * This function is used to get topology information for the clock
+ * specified by given clock ID.
+ *
+ * This API will return 3 node of topology with a single response. To get
+ * other nodes, master should call same API in loop with new
+ * index till error is returned. E.g First call should have
+ * index 0 which will return nodes 0,1 and 2. Next call, index
+ * should be 3 which will return nodes 3,4 and 5 and so on.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_get_topology(u32 clock_id, u32 index, u32 *topology)
+{
+	struct zynqmp_pm_query_data qdata = {0};
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	qdata.qid = PM_QID_CLOCK_GET_TOPOLOGY;
+	qdata.arg1 = clock_id;
+	qdata.arg2 = index;
+
+	eemi_ops->query_data(qdata, ret_payload);
+	memcpy(topology, &ret_payload[1], CLK_GET_TOPOLOGY_RESP_WORDS * 4);
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_get_fixedfactor_params - Get the clock's fixed factor
+ *					    parameters for fixed clock
+ * @clock_id: Clock ID
+ * @mul: Multiplication value
+ * @div: Divisor value
+ *
+ * This function is used to get fixed factor parameers for the fixed
+ * clock. This API is application only for the fixed clock.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_get_fixedfactor_params(u32 clock_id,
+						  u32 *mul,
+						  u32 *div)
+{
+	struct zynqmp_pm_query_data qdata = {0};
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	qdata.qid = PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS;
+	qdata.arg1 = clock_id;
+
+	eemi_ops->query_data(qdata, ret_payload);
+	*mul = ret_payload[1];
+	*div = ret_payload[2];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_get_parents - Get the first 3 parents of clock for given id
+ * @clock_id: Clock ID
+ * @index: Parent index
+ * @parents: 3 parents of the given clock
+ *
+ * This function is used to get 3 parents for the clock specified by
+ * given clock ID.
+ *
+ * This API will return 3 parents with a single response. To get
+ * other parents, master should call same API in loop with new
+ * parent index till error is returned. E.g First call should have
+ * index 0 which will return parents 0,1 and 2. Next call, index
+ * should be 3 which will return parent 3,4 and 5 and so on.
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_get_parents(u32 clock_id, u32 index, u32 *parents)
+{
+	struct zynqmp_pm_query_data qdata = {0};
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	qdata.qid = PM_QID_CLOCK_GET_PARENTS;
+	qdata.arg1 = clock_id;
+	qdata.arg2 = index;
+
+	eemi_ops->query_data(qdata, ret_payload);
+	memcpy(parents, &ret_payload[1], CLK_GET_PARENTS_RESP_WORDS * 4);
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_get_attributes - Get the attributes of clock for given id
+ * @clock_id: Clock ID
+ * @attributes: Clock attributes
+ *
+ * This function is used to get clock's attributes(e.g. valid, clock type, etc).
+ *
+ * Return: Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_get_attributes(u32 clock_id, u32 *attr)
+{
+	struct zynqmp_pm_query_data qdata = {0};
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	qdata.qid = PM_QID_CLOCK_GET_ATTRIBUTES;
+	qdata.arg1 = clock_id;
+
+	eemi_ops->query_data(qdata, ret_payload);
+	memcpy(attr, &ret_payload[1], CLK_GET_ATTR_RESP_WORDS * 4);
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * clock_get_topology: Get topology of clock from firmware using PM_API
+ * @clk_id: Clock Index
+ * @clk_topology: Structure of clock topology
+ * @num_nodes: number of nodes
+ *
+ * Return: 0: Success
+ *         Error Code: Failure
+ */
+static int clock_get_topology(u32 clk_id, struct clock_topology *clk_topology,
+			      u32 *num_nodes)
+{
+	int j, k = 0, ret;
+	u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
+
+	*num_nodes = 0;
+	for (j = 0; j <= MAX_NODES; j += 3) {
+		ret = zynqmp_pm_clock_get_topology(clk_id, j, pm_resp);
+		if (ret)
+			return ret;
+		for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
+			if (!(pm_resp[k] & CLK_TYPE_FIELD_MASK))
+				goto done;
+			clk_topology[*num_nodes].type = pm_resp[k] &
+							CLK_TYPE_FIELD_MASK;
+			clk_topology[*num_nodes].flag =
+					(pm_resp[k] >> CLK_FLAG_FIELD_SHIFT) &
+					CLK_FLAG_FIELD_MASK;
+			clk_topology[*num_nodes].type_flag =
+				(pm_resp[k] >> CLK_TYPE_FLAG_FIELD_SHIFT) &
+				CLK_TYPE_FLAG_FIELD_MASK;
+			(*num_nodes)++;
+		}
+	}
+done:
+	return 0;
+}
+
+/**
+ * clock_get_parents: Get parents info from firmware using PM_API
+ * @clk_id: Clock Index
+ * @parent: Structure of parent information
+ * @num_parents: Total number of parents
+ *
+ * Return: 0: Success
+ *         Error code: Failure
+ */
+static int clock_get_parents(u32 clk_id, struct clock_parent *parents,
+			     u32 *num_parents)
+{
+	int j = 0, k, ret, total_parents = 0;
+	u32 pm_resp[PM_API_PAYLOAD_LEN] = {0};
+
+	do {
+		/* Get parents from firmware */
+		ret = zynqmp_pm_clock_get_parents(clk_id, j, pm_resp);
+		if (ret)
+			return ret;
+
+		for (k = 0; k < PM_API_PAYLOAD_LEN; k++) {
+			if (pm_resp[k] == (u32)NA_PARENT) {
+				*num_parents = total_parents;
+				return 0;
+			}
+
+			parents[k + j].id = pm_resp[k] & CLK_PARENTS_ID_MASK;
+			if (parents[k + j].id == DUMMY_PARENT) {
+				strncpy(parents[k + j].name,
+					"dummy_name", MAX_NAME_LEN);
+				parents[k + j].flag = 0;
+			} else {
+				parents[k + j].flag = pm_resp[k] >>
+							CLK_PARENTS_ID_LEN;
+				if (zynqmp_get_clock_name(parents[k + j].id,
+							  parents[k + j].name))
+					continue;
+			}
+			total_parents++;
+		}
+		j += PM_API_PAYLOAD_LEN;
+	} while (total_parents <= MAX_PARENT);
+	return 0;
+}
+
+/**
+ * get_parent_list: Create list of parents name
+ * @np:		Device node
+ * @clk_id: Clock Index
+ * @parent_list List of parent's name
+ * @num_parents: Total number of parents
+ *
+ # Return: 0: Success
+ *         Error code: Failure
+ */
+static int get_parent_list(struct device_node *np, u32 clk_id,
+			   const char **parent_list, u32 *num_parents)
+{
+	int i = 0, ret;
+	u32 total_parents = clock[clk_id].num_parents;
+	struct clock_topology *clk_nodes;
+	struct clock_parent *parents;
+
+	clk_nodes = clock[clk_id].node;
+	parents = clock[clk_id].parent;
+
+	for (i = 0; i < total_parents; i++) {
+		if (!parents[i].flag) {
+			parent_list[i] = parents[i].name;
+		} else if (parents[i].flag == PARENT_CLK_EXTERNAL) {
+			ret = of_property_match_string(np, "clock-names",
+						       parents[i].name);
+			if (ret < 0)
+				strncpy(parents[i].name,
+					"dummy_name", MAX_NAME_LEN);
+			parent_list[i] = parents[i].name;
+		} else {
+			strcat(parents[i].name,
+			       clk_type_postfix[clk_nodes[parents[i].flag - 1].
+			       type]);
+			parent_list[i] = parents[i].name;
+		}
+	}
+
+	*num_parents = total_parents;
+	return 0;
+}
+
+/**
+ * zynqmp_register_clk_topology: Register clock topology
+ * @clk_id: Clock Index
+ * @clk_name: Clock Name
+ * @num_parents: Total number of parents
+ * @parent_names: List of parents name
+ *
+ * Return: 0: Success
+ *         Error code: Failure
+ */
+static struct clk *zynqmp_register_clk_topology(int clk_id, char *clk_name,
+						int num_parents,
+						const char **parent_names)
+{
+	int j, ret;
+	u32 num_nodes, mult, div;
+	char *clk_out = NULL;
+	struct clock_topology *nodes;
+	struct clk *clk = NULL;
+
+	nodes = clock[clk_id].node;
+	num_nodes = clock[clk_id].num_nodes;
+
+	for (j = 0; j < num_nodes; j++) {
+		if (j != (num_nodes - 1)) {
+			clk_out = kasprintf(GFP_KERNEL, "%s%s", clk_name,
+					    clk_type_postfix[nodes[j].type]);
+		} else {
+			clk_out = kasprintf(GFP_KERNEL, "%s", clk_name);
+		}
+
+		switch (nodes[j].type) {
+		case TYPE_MUX:
+			clk = zynqmp_clk_register_mux(NULL, clk_out,
+						      clk_id, parent_names,
+						      num_parents,
+						      nodes[j].flag,
+						      nodes[j].type_flag);
+			break;
+		case TYPE_PLL:
+			clk = clk_register_zynqmp_pll(clk_out, clk_id,
+						      parent_names, 1,
+						      nodes[j].flag);
+			break;
+		case TYPE_FIXEDFACTOR:
+			ret = zynqmp_pm_clock_get_fixedfactor_params(clk_id,
+								     &mult,
+								     &div);
+			clk = clk_register_fixed_factor(NULL, clk_out,
+							parent_names[0],
+							nodes[j].flag, mult,
+							div);
+			break;
+		case TYPE_DIV1:
+		case TYPE_DIV2:
+			clk = zynqmp_clk_register_divider(NULL, clk_out, clk_id,
+							  nodes[j].type,
+							  parent_names, 1,
+							  nodes[j].flag,
+							  nodes[j].type_flag);
+			break;
+		case TYPE_GATE:
+			clk = zynqmp_clk_register_gate(NULL, clk_out, clk_id,
+						       parent_names, 1,
+						       nodes[j].flag,
+						       nodes[j].type_flag);
+			break;
+		default:
+			pr_err("%s() Unknown topology for %s\n",
+			       __func__, clk_out);
+			break;
+		}
+		if (IS_ERR(clk))
+			pr_warn_once("%s() %s register fail with %ld\n",
+				     __func__, clk_name, PTR_ERR(clk));
+
+		parent_names[0] = clk_out;
+	}
+	kfree(clk_out);
+	return clk;
+}
+
+/**
+ * zynqmp_register_clocks: Register clocks
+ * @np: Device node
+ *
+ * Return: 0: Success
+ *         Error code: failure
+ */
+static int zynqmp_register_clocks(struct device_node *np)
+{
+	int ret;
+	u32 i, total_parents = 0, type = 0;
+	const char *parent_names[MAX_PARENT];
+
+	for (i = 0; i < clock_max_idx; i++) {
+		char clk_name[MAX_NAME_LEN];
+
+		/* get clock name, continue to next clock if name not found */
+		if (zynqmp_get_clock_name(i, clk_name))
+			continue;
+
+		/* Check if clock is valid and output clock.
+		 * Do not regiter invalid or external clock.
+		 */
+		ret = get_clock_type(i, &type);
+		if (ret || type != CLK_TYPE_OUTPUT)
+			continue;
+
+		/* Get parents of clock*/
+		if (get_parent_list(np, i, parent_names, &total_parents)) {
+			WARN_ONCE(1, "No parents found for %s\n",
+				  clock[i].clk_name);
+			continue;
+		}
+
+		zynqmp_clks[i] = zynqmp_register_clk_topology(i, clk_name,
+							      total_parents,
+							      parent_names);
+
+		/* Enable clock if init_enable flag is 1 */
+		if (clock[i].init_enable)
+			clk_prepare_enable(zynqmp_clks[i]);
+	}
+
+	for (i = 0; i < clock_max_idx; i++) {
+		if (IS_ERR(zynqmp_clks[i])) {
+			pr_err("Zynq Ultrascale+ MPSoC clk %s: register failed with %ld\n",
+			       clock[i].clk_name, PTR_ERR(zynqmp_clks[i]));
+			WARN_ON(1);
+		}
+	}
+	return 0;
+}
+
+/**
+ * zynqmp_get_clock_info - Get clock information from firmware using PM_API
+ */
+static void zynqmp_get_clock_info(void)
+{
+	int i, ret;
+	u32 attr, type = 0;
+
+	memset(clock, 0, sizeof(clock));
+	for (i = 0; i < MAX_CLOCK; i++) {
+		zynqmp_pm_clock_get_name(i, clock[i].clk_name);
+		if (!strncmp(clock[i].clk_name, END_OF_CLK_NAME,
+			     MAX_NAME_LEN)) {
+			clock_max_idx = i;
+			break;
+		} else if (!strncmp(clock[i].clk_name, RESERVED_CLK_NAME,
+				    MAX_NAME_LEN)) {
+			continue;
+		}
+
+		ret = zynqmp_pm_clock_get_attributes(i, &attr);
+		if (ret)
+			continue;
+
+		clock[i].valid = attr & CLK_VALID_MASK;
+		clock[i].init_enable = !!(attr & CLK_INIT_ENABLE_MASK);
+		clock[i].type = attr >> CLK_TYPE_SHIFT ? CLK_TYPE_EXTERNAL :
+							CLK_TYPE_OUTPUT;
+	}
+
+	/* Get topology of all clock */
+	for (i = 0; i < clock_max_idx; i++) {
+		ret = get_clock_type(i, &type);
+		if (ret || type != CLK_TYPE_OUTPUT)
+			continue;
+
+		ret = clock_get_topology(i, clock[i].node, &clock[i].num_nodes);
+		if (ret)
+			continue;
+
+		ret = clock_get_parents(i, clock[i].parent,
+					&clock[i].num_parents);
+		if (ret)
+			continue;
+	}
+}
+
+/**
+ * zynqmp_clk_setup -  Setup the clock framework and register clocks
+ * @np: Device node
+ */
+static void __init zynqmp_clk_setup(struct device_node *np)
+{
+	int idx;
+
+	idx = of_property_match_string(np, "clock-names", "pss_ref_clk");
+	if (idx < 0) {
+		pr_err("pss_ref_clk not provided\n");
+		return;
+	}
+	idx = of_property_match_string(np, "clock-names", "video_clk");
+	if (idx < 0) {
+		pr_err("video_clk not provided\n");
+		return;
+	}
+	idx = of_property_match_string(np, "clock-names", "pss_alt_ref_clk");
+	if (idx < 0) {
+		pr_err("pss_alt_ref_clk not provided\n");
+		return;
+	}
+	idx = of_property_match_string(np, "clock-names", "aux_ref_clk");
+	if (idx < 0) {
+		pr_err("aux_ref_clk not provided\n");
+		return;
+	}
+	idx = of_property_match_string(np, "clock-names", "gt_crx_ref_clk");
+	if (idx < 0) {
+		pr_err("aux_ref_clk not provided\n");
+		return;
+	}
+
+	zynqmp_get_clock_info();
+	zynqmp_register_clocks(np);
+
+	zynqmp_clk_data.clks = zynqmp_clks;
+	zynqmp_clk_data.clk_num = clock_max_idx;
+	of_clk_add_provider(np, of_clk_src_onecell_get, &zynqmp_clk_data);
+}
+
+/**
+ * zynqmp_clock_init -  Initialize zynqmp clocks
+ *
+ * Return: 0 always
+ */
+static int __init zynqmp_clock_init(void)
+{
+	struct device_node *np;
+
+	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-clkc");
+	if (!np) {
+		pr_err("%s: clkc node not found\n", __func__);
+		of_node_put(np);
+		return 0;
+	}
+
+	eemi_ops = get_eemi_ops();
+	if (!eemi_ops || !eemi_ops->query_data) {
+		pr_err("%s: clk data not found\n", __func__);
+		of_node_put(np);
+		return 0;
+	}
+
+	zynqmp_clk_setup(np);
+
+	return 0;
+}
+arch_initcall(zynqmp_clock_init);
diff --git a/drivers/clk/zynqmp/divider.c b/drivers/clk/zynqmp/divider.c
new file mode 100644
index 0000000..1a1473c
--- /dev/null
+++ b/drivers/clk/zynqmp/divider.c
@@ -0,0 +1,239 @@
+/*
+ * Zynq UltraScale+ MPSoC Divider support
+ *
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ *
+ * Adjustable divider clock implementation
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/clk/zynqmp.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/err.h>
+#include <linux/string.h>
+#include <linux/log2.h>
+
+/*
+ * DOC: basic adjustable divider clock that cannot gate
+ *
+ * Traits of this clock:
+ * prepare - clk_prepare only ensures that parents are prepared
+ * enable - clk_enable only ensures that parents are enabled
+ * rate - rate is adjustable.  clk->rate = ceiling(parent->rate / divisor)
+ * parent - fixed parent.  No clk_set_parent support
+ */
+
+#define to_zynqmp_clk_divider(_hw)		\
+	container_of(_hw, struct zynqmp_clk_divider, hw)
+
+/**
+ * struct zynqmp_clk_divider - adjustable divider clock
+ *
+ * @hw:	handle between common and hardware-specific interfaces
+ * @flags: Hardware specific flags
+ * @clk_id: Id of clock
+ * @div_type: divisor type (TYPE_DIV1 or TYPE_DIV2)
+ */
+struct zynqmp_clk_divider {
+	struct clk_hw hw;
+	u8 flags;
+	u32 clk_id;
+	u32 div_type;
+};
+
+static int zynqmp_divider_get_val(unsigned long parent_rate, unsigned long rate)
+{
+	return DIV_ROUND_UP_ULL((u64)parent_rate, rate);
+}
+
+static unsigned long zynqmp_clk_divider_recalc_rate(struct clk_hw *hw,
+						    unsigned long parent_rate)
+{
+	struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = divider->clk_id;
+	u32 div_type = divider->div_type;
+	u32 div, value;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getdivider)
+		return -ENXIO;
+
+	ret = eemi_ops->clock_getdivider(clk_id, &div);
+
+	if (ret)
+		pr_warn_once("%s() get divider failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	if (div_type == TYPE_DIV1)
+		value = div & 0xFFFF;
+	else
+		value = (div >> 16) & 0xFFFF;
+
+	return zynqmp_divider_get_val((u64)parent_rate, value);
+}
+
+static long zynqmp_clk_divider_round_rate(struct clk_hw *hw,
+					  unsigned long rate,
+					  unsigned long *prate)
+{
+	struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = divider->clk_id;
+	u32 div_type = divider->div_type;
+	u32 bestdiv;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getdivider)
+		return -ENXIO;
+
+	/* if read only, just return current value */
+	if (divider->flags & CLK_DIVIDER_READ_ONLY) {
+		ret = eemi_ops->clock_getdivider(clk_id, &bestdiv);
+
+		if (ret)
+			pr_warn_once("%s() get divider failed for %s, ret = %d\n",
+				     __func__, clk_name, ret);
+		if (div_type == TYPE_DIV1)
+			bestdiv = bestdiv & 0xFFFF;
+		else
+			bestdiv  = (bestdiv >> 16) & 0xFFFF;
+
+		return DIV_ROUND_UP_ULL((u64)*prate, bestdiv);
+	}
+
+	bestdiv = zynqmp_divider_get_val(*prate, rate);
+
+	if ((clk_hw_get_flags(hw) & CLK_SET_RATE_PARENT) &&
+	    ((clk_hw_get_flags(hw) & CLK_FRAC)))
+		bestdiv = rate % *prate ? 1 : bestdiv;
+	*prate = rate * bestdiv;
+
+	return rate;
+}
+
+/**
+ * zynqmp_clk_divider_set_rate - Set rate of divider clock
+ * @hw:	handle between common and hardware-specific interfaces
+ * @rate: rate of clock to be set
+ * @parent_rate: rate of parent clock
+ *
+ * Return: 0 always
+ */
+static int zynqmp_clk_divider_set_rate(struct clk_hw *hw, unsigned long rate,
+				       unsigned long parent_rate)
+{
+	struct zynqmp_clk_divider *divider = to_zynqmp_clk_divider(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = divider->clk_id;
+	u32 div_type = divider->div_type;
+	u32 value, div;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_setdivider)
+		return -ENXIO;
+
+	value = zynqmp_divider_get_val(parent_rate, rate);
+	if (div_type == TYPE_DIV1) {
+		div = value & 0xFFFF;
+		div |= ((u16)-1) << 16;
+	} else {
+		div = ((u16)-1);
+		div |= value << 16;
+	}
+
+	ret = eemi_ops->clock_setdivider(clk_id, div);
+
+	if (ret)
+		pr_warn_once("%s() set divider failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return 0;
+}
+
+static const struct clk_ops zynqmp_clk_divider_ops = {
+	.recalc_rate = zynqmp_clk_divider_recalc_rate,
+	.round_rate = zynqmp_clk_divider_round_rate,
+	.set_rate = zynqmp_clk_divider_set_rate,
+};
+
+/**
+ * _register_divider - register a divider clock
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @clk_id: Id of clock
+ * @div_type: Type of divisor
+ * @parents: name of clock's parents
+ * @num_parents: number of parents
+ * @flags: framework-specific flags
+ * @clk_divider_flags: divider-specific flags for this clock
+ *
+ * Return: handle to registered clock divider
+ */
+static struct clk *_register_divider(struct device *dev, const char *name,
+				     u32 clk_id, u32 div_type,
+				     const char * const *parents,
+				     u8 num_parents, unsigned long flags,
+				     u8 clk_divider_flags)
+{
+	struct zynqmp_clk_divider *div;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	/* allocate the divider */
+	div = kzalloc(sizeof(*div), GFP_KERNEL);
+	if (!div)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &zynqmp_clk_divider_ops;
+	init.flags = flags;
+	init.parent_names = parents;
+	init.num_parents = num_parents;
+
+	/* struct clk_divider assignments */
+	div->flags = clk_divider_flags;
+	div->hw.init = &init;
+	div->clk_id = clk_id;
+	div->div_type = div_type;
+
+	/* register the clock */
+	clk = clk_register(dev, &div->hw);
+
+	if (IS_ERR(clk))
+		kfree(div);
+
+	return clk;
+}
+
+/**
+ * zynqmp_clk_register_divider - register a divider clock
+ * @dev: device registering this clock
+ * @name: name of this clock
+ * @clk_id: Id of clock
+ * @div_type: Type of divisor
+ * @parents: name of clock's parents
+ * @num_parents: number of parents
+ * @flags: framework-specific flags
+ * @clk_divider_flags: divider-specific flags for this clock
+ *
+ * Return: handle to registered clock divider
+ */
+struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
+					u32 clk_id, u32 div_type,
+					const char * const *parents,
+					u8 num_parents, unsigned long flags,
+					u8 clk_divider_flags)
+{
+	return _register_divider(dev, name, clk_id, div_type, parents,
+				 num_parents, flags, clk_divider_flags);
+}
+EXPORT_SYMBOL_GPL(zynqmp_clk_register_divider);
diff --git a/drivers/clk/zynqmp/pll.c b/drivers/clk/zynqmp/pll.c
new file mode 100644
index 0000000..75def21
--- /dev/null
+++ b/drivers/clk/zynqmp/pll.c
@@ -0,0 +1,384 @@
+/*
+ * Zynq UltraScale+ MPSoC PLL driver
+ *
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+#include <linux/clk.h>
+#include <linux/clk/zynqmp.h>
+#include <linux/clk-provider.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+
+/**
+ * struct zynqmp_pll - Structure for PLL clock
+ * @hw:		Handle between common and hardware-specific interfaces
+ * @clk_id:	PLL clock ID
+ */
+struct zynqmp_pll {
+	struct clk_hw hw;
+	u32 clk_id;
+};
+
+#define to_zynqmp_pll(_hw)	container_of(_hw, struct zynqmp_pll, hw)
+
+/* Register bitfield defines */
+#define PLLCTRL_FBDIV_MASK	0x7f00
+#define PLLCTRL_FBDIV_SHIFT	8
+#define PLLCTRL_BP_MASK		BIT(3)
+#define PLLCTRL_DIV2_MASK	BIT(16)
+#define PLLCTRL_RESET_MASK	1
+#define PLLCTRL_RESET_VAL	1
+#define PLL_STATUS_LOCKED	1
+#define PLLCTRL_RESET_SHIFT	0
+#define PLLCTRL_DIV2_SHIFT	16
+
+#define PLL_FBDIV_MIN	25
+#define PLL_FBDIV_MAX	125
+
+#define PS_PLL_VCO_MIN 1500000000
+#define PS_PLL_VCO_MAX 3000000000UL
+
+enum pll_mode {
+	PLL_MODE_INT,
+	PLL_MODE_FRAC,
+};
+
+#define FRAC_OFFSET 0x8
+#define PLLFCFG_FRAC_EN	BIT(31)
+#define FRAC_DIV  0x10000  /* 2^16 */
+
+/**
+ * pll_get_mode - Get mode of PLL
+ * @hw: Handle between common and hardware-specific interfaces
+ *
+ * Return: Mode of PLL
+ */
+static inline enum pll_mode pll_get_mode(struct clk_hw *hw)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	u32 clk_id = clk->clk_id;
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->ioctl)
+		return -ENXIO;
+
+	ret = eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_MODE, clk_id, 0,
+			      ret_payload);
+	if (ret)
+		pr_warn_once("%s() PLL get frac mode failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return ret_payload[1];
+}
+
+/**
+ * pll_set_mode - Set the PLL mode
+ * @hw:		Handle between common and hardware-specific interfaces
+ * @on:		Flag to determine the mode
+ */
+static inline void pll_set_mode(struct clk_hw *hw, bool on)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	u32 clk_id = clk->clk_id;
+	const char *clk_name = clk_hw_get_name(hw);
+	int ret;
+	u32 mode;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->ioctl) {
+		pr_warn_once("eemi_ops not found\n");
+		return;
+	}
+
+	if (on)
+		mode = PLL_MODE_FRAC;
+	else
+		mode = PLL_MODE_INT;
+
+	ret = eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_MODE, clk_id, mode, NULL);
+	if (ret)
+		pr_warn_once("%s() PLL set frac mode failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+}
+
+/**
+ * zynqmp_pll_round_rate - Round a clock frequency
+ * @hw:		Handle between common and hardware-specific interfaces
+ * @rate:	Desired clock frequency
+ * @prate:	Clock frequency of parent clock
+ *
+ * Return:	Frequency closest to @rate the hardware can generate
+ */
+static long zynqmp_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+				  unsigned long *prate)
+{
+	u32 fbdiv;
+	long rate_div, f;
+
+	/* Enable the fractional mode if needed */
+	rate_div = ((rate * FRAC_DIV) / *prate);
+	f = rate_div % FRAC_DIV;
+	pll_set_mode(hw, !!f);
+
+	if (pll_get_mode(hw) == PLL_MODE_FRAC) {
+		if (rate > PS_PLL_VCO_MAX) {
+			fbdiv = rate / PS_PLL_VCO_MAX;
+			rate = rate / (fbdiv + 1);
+		}
+		if (rate < PS_PLL_VCO_MIN) {
+			fbdiv = DIV_ROUND_UP(PS_PLL_VCO_MIN, rate);
+			rate = rate * fbdiv;
+		}
+		return rate;
+	}
+
+	fbdiv = DIV_ROUND_CLOSEST(rate, *prate);
+	fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
+	return *prate * fbdiv;
+}
+
+/**
+ * zynqmp_pll_recalc_rate - Recalculate clock frequency
+ * @hw:			Handle between common and hardware-specific interfaces
+ * @parent_rate:	Clock frequency of parent clock
+ * Return:		Current clock frequency
+ */
+static unsigned long zynqmp_pll_recalc_rate(struct clk_hw *hw,
+					    unsigned long parent_rate)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	u32 clk_id = clk->clk_id;
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 fbdiv, data;
+	unsigned long rate, frac;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getdivider)
+		return 0;
+
+	/*
+	 * makes probably sense to redundantly save fbdiv in the struct
+	 * zynqmp_pll to save the IO access.
+	 */
+	ret = eemi_ops->clock_getdivider(clk_id, &fbdiv);
+	if (ret)
+		pr_warn_once("%s() get divider failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	rate =  parent_rate * fbdiv;
+	if (pll_get_mode(hw) == PLL_MODE_FRAC) {
+		eemi_ops->ioctl(0, IOCTL_GET_PLL_FRAC_DATA, clk_id, 0,
+				ret_payload);
+		data = ret_payload[1];
+		frac = (parent_rate * data) / FRAC_DIV;
+		rate = rate + frac;
+	}
+
+	return rate;
+}
+
+/**
+ * zynqmp_pll_set_rate - Set rate of PLL
+ * @hw:			Handle between common and hardware-specific interfaces
+ * @rate:		Frequency of clock to be set
+ * @parent_rate:	Clock frequency of parent clock
+ */
+static int zynqmp_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			       unsigned long parent_rate)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	u32 clk_id = clk->clk_id;
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 fbdiv, data;
+	long rate_div, frac, m, f;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_setdivider)
+		return -ENXIO;
+
+	if (pll_get_mode(hw) == PLL_MODE_FRAC) {
+		unsigned int children;
+
+		/*
+		 * We're running on a ZynqMP compatible machine, make sure the
+		 * VPLL only has one child.
+		 */
+		children = clk_get_children("vpll");
+
+		/* Account for vpll_to_lpd and dp_video_ref */
+		if (children > 2)
+			WARN(1, "Two devices are using vpll which is forbidden\n");
+
+		rate_div = ((rate * FRAC_DIV) / parent_rate);
+		m = rate_div / FRAC_DIV;
+		f = rate_div % FRAC_DIV;
+		m = clamp_t(u32, m, (PLL_FBDIV_MIN), (PLL_FBDIV_MAX));
+		rate = parent_rate * m;
+		frac = (parent_rate * f) / FRAC_DIV;
+
+		ret = eemi_ops->clock_setdivider(clk_id, m);
+		if (ret)
+			pr_warn_once("%s() set divider failed for %s, ret = %d\n",
+				     __func__, clk_name, ret);
+
+		data = (FRAC_DIV * f) / FRAC_DIV;
+		eemi_ops->ioctl(0, IOCTL_SET_PLL_FRAC_DATA, clk_id, data, NULL);
+
+		return (rate + frac);
+	}
+
+	fbdiv = DIV_ROUND_CLOSEST(rate, parent_rate);
+	fbdiv = clamp_t(u32, fbdiv, PLL_FBDIV_MIN, PLL_FBDIV_MAX);
+	ret = eemi_ops->clock_setdivider(clk_id, fbdiv);
+	if (ret)
+		pr_warn_once("%s() set divider failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return parent_rate * fbdiv;
+}
+
+/**
+ * zynqmp_pll_is_enabled - Check if a clock is enabled
+ * @hw:		Handle between common and hardware-specific interfaces
+ *
+ * Return:	1 if the clock is enabled, 0 otherwise
+ */
+static int zynqmp_pll_is_enabled(struct clk_hw *hw)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = clk->clk_id;
+	unsigned int state;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_getstate)
+		return 0;
+
+	ret = eemi_ops->clock_getstate(clk_id, &state);
+	if (ret)
+		pr_warn_once("%s() clock get state failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return state ? 1 : 0;
+}
+
+/**
+ * zynqmp_pll_enable - Enable clock
+ * @hw:		Handle between common and hardware-specific interfaces
+ *
+ * Return:	0 always
+ */
+static int zynqmp_pll_enable(struct clk_hw *hw)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = clk->clk_id;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_enable)
+		return 0;
+
+	if (zynqmp_pll_is_enabled(hw))
+		return 0;
+
+	pr_info("PLL: enable\n");
+
+	ret = eemi_ops->clock_enable(clk_id);
+	if (ret)
+		pr_warn_once("%s() clock enable failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+
+	return 0;
+}
+
+/**
+ * zynqmp_pll_disable - Disable clock
+ * @hw:		Handle between common and hardware-specific interfaces
+ *
+ */
+static void zynqmp_pll_disable(struct clk_hw *hw)
+{
+	struct zynqmp_pll *clk = to_zynqmp_pll(hw);
+	const char *clk_name = clk_hw_get_name(hw);
+	u32 clk_id = clk->clk_id;
+	int ret;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->clock_disable)
+		return;
+
+	if (!zynqmp_pll_is_enabled(hw))
+		return;
+
+	pr_info("PLL: shutdown\n");
+
+	ret = eemi_ops->clock_disable(clk_id);
+	if (ret)
+		pr_warn_once("%s() clock disable failed for %s, ret = %d\n",
+			     __func__, clk_name, ret);
+}
+
+static const struct clk_ops zynqmp_pll_ops = {
+	.enable = zynqmp_pll_enable,
+	.disable = zynqmp_pll_disable,
+	.is_enabled = zynqmp_pll_is_enabled,
+	.round_rate = zynqmp_pll_round_rate,
+	.recalc_rate = zynqmp_pll_recalc_rate,
+	.set_rate = zynqmp_pll_set_rate,
+};
+
+/**
+ * clk_register_zynqmp_pll - Register PLL with the clock framework
+ * @name:	PLL name
+ * @flag:	PLL flags
+ * @parents:	Parent clock names
+ * @num_parents:Number of parents
+ * @pll_ctrl:	Pointer to PLL control register
+ * @pll_status:	Pointer to PLL status register
+ * @lock_index:	Bit index to this PLL's lock status bit in @pll_status
+ *
+ * Return:	Handle to the registered clock
+ */
+struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
+				    const char * const *parents,
+				    u8 num_parents, unsigned long flag)
+{
+	struct zynqmp_pll *pll;
+	struct clk *clk;
+	struct clk_init_data init;
+	int status;
+
+	init.name = name;
+	init.ops = &zynqmp_pll_ops;
+	init.flags = flag;
+	init.parent_names = parents;
+	init.num_parents = num_parents;
+
+	pll = kmalloc(sizeof(*pll), GFP_KERNEL);
+	if (!pll)
+		return ERR_PTR(-ENOMEM);
+
+	/* Populate the struct */
+	pll->hw.init = &init;
+	pll->clk_id = clk_id;
+
+	clk = clk_register(NULL, &pll->hw);
+	if (WARN_ON(IS_ERR(clk)))
+		kfree(pll);
+
+	status = clk_set_rate_range(clk, PS_PLL_VCO_MIN, PS_PLL_VCO_MAX);
+	if (status < 0)
+		pr_err("%s:ERROR clk_set_rate_range failed %d\n", name, status);
+
+	return clk;
+}
diff --git a/include/linux/clk/zynqmp.h b/include/linux/clk/zynqmp.h
new file mode 100644
index 0000000..024ebf8
--- /dev/null
+++ b/include/linux/clk/zynqmp.h
@@ -0,0 +1,46 @@
+/*
+ *  Copyright (C) 2016-2017 Xilinx
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#ifndef __LINUX_CLK_ZYNQMP_H_
+#define __LINUX_CLK_ZYNQMP_H_
+
+#include <linux/spinlock.h>
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+
+#define CLK_FRAC	BIT(13) /* has a fractional parent */
+
+struct device;
+
+struct clk *clk_register_zynqmp_pll(const char *name, u32 clk_id,
+				    const char * const *parent, u8 num_parents,
+				    unsigned long flag);
+
+struct clk *zynqmp_clk_register_gate(struct device *dev, const char *name,
+				     u32 clk_id,
+				     const char * const *parent_name,
+				     u8 num_parents, unsigned long flags,
+				     u8 clk_gate_flags);
+
+struct clk *zynqmp_clk_register_divider(struct device *dev, const char *name,
+					u32 clk_id, u32 div_type,
+					const char * const *parent_name,
+					u8 num_parents,
+					unsigned long flags,
+					u8 clk_divider_flags);
+
+struct clk *zynqmp_clk_register_mux(struct device *dev, const char *name,
+				    u32 clk_id,
+				    const char **parent_names,
+				    u8 num_parents, unsigned long flags,
+				    u8 clk_mux_flags);
+
+struct clk *zynqmp_clk_register_mux_table(struct device *dev, const char *name,
+					  u32 clk_id,
+					  const char * const *parent_names,
+					  u8 num_parents, unsigned long flags,
+					  u8 clk_mux_flags);
+
+#endif
-- 
2.7.4

^ permalink raw reply related

* [RFC PATCH 1/2] drivers: clk: Add clk_get_children support
From: Jolly Shah @ 2018-01-08 22:16 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515449797-5629-1-git-send-email-jollys@xilinx.com>

This API helps to determine the users for any clock.

Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Tejas Patel <tejasp@xilinx.com>
Signed-off-by: Shubhrajyoti Datta <shubhrajyoti.datta@xilinx.com>
---
 drivers/clk/clk.c            | 28 ++++++++++++++++++++++++++++
 include/linux/clk-provider.h |  1 +
 2 files changed, 29 insertions(+)

diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index b56c11f..7488787 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -258,6 +258,34 @@ struct clk_hw *clk_hw_get_parent(const struct clk_hw *hw)
 }
 EXPORT_SYMBOL_GPL(clk_hw_get_parent);
 
+static unsigned int sibling;
+
+static void clk_show_subtree(struct clk_core *c,
+			     int level)
+{
+	struct clk_core *child;
+
+	if (!c)
+		return;
+
+	if (level == 1)
+		sibling++;
+
+	hlist_for_each_entry(child, &c->children, child_node)
+		clk_show_subtree(child, level + 1);
+}
+
+unsigned int clk_get_children(char *name)
+{
+	struct clk_core *core;
+	struct clk *pclk = __clk_lookup(name);
+
+	sibling = 0;
+	core = pclk->core;
+	clk_show_subtree(core, 0);
+	return sibling;
+}
+
 static struct clk_core *__clk_lookup_subtree(const char *name,
 					     struct clk_core *core)
 {
diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h
index 7c925e6..8191a32a 100644
--- a/include/linux/clk-provider.h
+++ b/include/linux/clk-provider.h
@@ -743,6 +743,7 @@ unsigned int __clk_get_enable_count(struct clk *clk);
 unsigned long clk_hw_get_rate(const struct clk_hw *hw);
 unsigned long __clk_get_flags(struct clk *clk);
 unsigned long clk_hw_get_flags(const struct clk_hw *hw);
+unsigned int clk_get_children(char *name);
 bool clk_hw_is_prepared(const struct clk_hw *hw);
 bool clk_hw_is_enabled(const struct clk_hw *hw);
 bool __clk_is_enabled(struct clk *clk);
-- 
2.7.4

^ permalink raw reply related

* [RFC PATCH 0/2] drivers: clk: Add ZynqMP clock driver support
From: Jolly Shah @ 2018-01-08 22:16 UTC (permalink / raw)
  To: linux-arm-kernel

Add clock driver for ZynqMP

Jolly Shah (2):
  drivers: clk: Add clk_get_children support
  drivers: clk: Add ZynqMP clock driver

 .../devicetree/bindings/clock/zynq_mpsoc.txt       | 163 +++++
 drivers/clk/Kconfig                                |   1 +
 drivers/clk/Makefile                               |   1 +
 drivers/clk/clk.c                                  |  28 +
 drivers/clk/zynqmp/Kconfig                         |   8 +
 drivers/clk/zynqmp/Makefile                        |   3 +
 drivers/clk/zynqmp/clk-gate-zynqmp.c               | 158 +++++
 drivers/clk/zynqmp/clk-mux-zynqmp.c                | 190 ++++++
 drivers/clk/zynqmp/clkc.c                          | 707 +++++++++++++++++++++
 drivers/clk/zynqmp/divider.c                       | 239 +++++++
 drivers/clk/zynqmp/pll.c                           | 384 +++++++++++
 include/linux/clk-provider.h                       |   1 +
 include/linux/clk/zynqmp.h                         |  46 ++
 13 files changed, 1929 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/clock/zynq_mpsoc.txt
 create mode 100644 drivers/clk/zynqmp/Kconfig
 create mode 100644 drivers/clk/zynqmp/Makefile
 create mode 100644 drivers/clk/zynqmp/clk-gate-zynqmp.c
 create mode 100644 drivers/clk/zynqmp/clk-mux-zynqmp.c
 create mode 100644 drivers/clk/zynqmp/clkc.c
 create mode 100644 drivers/clk/zynqmp/divider.c
 create mode 100644 drivers/clk/zynqmp/pll.c
 create mode 100644 include/linux/clk/zynqmp.h

-- 
2.7.4

^ permalink raw reply

* [RFC 5/5] pwm: pwm-omap-dmtimer: Add capture functionality
From: Tony Lindgren @ 2018-01-08 22:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108220602.GA17615@lenoch>

* Ladislav Michl <ladis@linux-mips.org> [180108 22:09]:
> On Mon, Jan 08, 2018 at 01:59:31PM -0800, Tony Lindgren wrote:
> > * Ladislav Michl <ladis@linux-mips.org> [180108 15:46]:
> > > Here it seems hardware can capture both edges, but I do not see a way
> > > how to tell it I want start from either low to high or high to low
> > > transition. Clues?
> > 
> > At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you
> > probably know that already..
> > 
> > If you're having hard time getting things starting, maybe something
> > like this helps:
> > 
> > stop timer in TCLR register
> > configure timer in TCLR
> > write some value to TLDR, maybe 0?
> > set ST bit in TCLR to start
> 
> Let me clarify it a bit more. I have no problem starting timer and capture
> events. I just didn't find a way how to tell hardware I want to start
> with for example rising edge, so rising edge goes to TCAR1 and failing edge
> to TCAR2. Substracting those gives pulse width.

Oh I see, yeah that would be cool :) Maybe you can first configure
an interrupt to trigger on rising edge, then configure things for
falling edge, then subtract..

Regards,

Tony

^ permalink raw reply

* [RFC PATCH] drivers: soc: xilinx: Add ZynqMP power domain driver
From: Jolly Shah @ 2018-01-08 22:12 UTC (permalink / raw)
  To: linux-arm-kernel

The zynqmp-genpd driver communicates the usage requirements
for logical power domains / devices to the platform FW.
FW is responsible for choosing appropriate power states,
taking Linux' usage information into account.

Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
---
 .../devicetree/bindings/power/zynqmp-genpd.txt     |  46 +++
 drivers/soc/xilinx/zynqmp/Kconfig                  |  10 +-
 drivers/soc/xilinx/zynqmp/Makefile                 |   1 +
 drivers/soc/xilinx/zynqmp/pm_domains.c             | 343 +++++++++++++++++++++
 4 files changed, 399 insertions(+), 1 deletion(-)
 create mode 100644 Documentation/devicetree/bindings/power/zynqmp-genpd.txt
 create mode 100644 drivers/soc/xilinx/zynqmp/pm_domains.c

diff --git a/Documentation/devicetree/bindings/power/zynqmp-genpd.txt b/Documentation/devicetree/bindings/power/zynqmp-genpd.txt
new file mode 100644
index 0000000..25f9711
--- /dev/null
+++ b/Documentation/devicetree/bindings/power/zynqmp-genpd.txt
@@ -0,0 +1,46 @@
+Device Tree bindings for Xilinx Zynq MPSoC PM domains
+
+The binding for zynqmp-genpd follow the common generic PM domain binding[1].
+
+[1] Documentation/devicetree/bindings/power/power_domain.txt
+
+== Zynq MPSoC Generic PM Domain Node ==
+
+Required properties:
+ - compatible: Must be: "xlnx,zynqmp-genpd"
+
+This node contains a number of subnodes, each representing a single PM domain
+that PM domain consumer devices reference.
+
+== PM Domain Nodes ==
+
+Required properties:
+ - #power-domain-cells: Number of cells in a PM domain specifier. Must be 0.
+ - pd-id: List of domain identifiers of as defined by platform firmware. These
+	  identifiers are passed to the PM firmware.
+
+Example:
+	zynqmp-genpd {
+		compatible = "xlnx,zynqmp-genpd";
+
+		pd_usb0: pd-usb0 {
+			pd-id = <22>;
+			#power-domain-cells = <0>;
+		};
+
+		pd_sata: pd-sata {
+			pd-id = <25>;
+			#power-domain-cells = <0>;
+		};
+
+		pd_gpu: pd-gpu {
+			pd-id = <58 20 21>;
+			#power-domain-cells = <0x0>;
+		};
+	};
+
+	sata0: ahci at SATA_AHCI_HBA {
+		...
+		power-domains = <&pd_sata>;
+		...
+	};
diff --git a/drivers/soc/xilinx/zynqmp/Kconfig b/drivers/soc/xilinx/zynqmp/Kconfig
index d3c784d..545a66c 100644
--- a/drivers/soc/xilinx/zynqmp/Kconfig
+++ b/drivers/soc/xilinx/zynqmp/Kconfig
@@ -4,7 +4,6 @@
 menu "Zynq MPSoC SoC Drivers"
 	depends on ARCH_ZYNQMP
 
-
 config ZYNQMP_PM
 	bool "Enable Xilinx Zynq MPSoC Power Management"
 	depends on PM
@@ -12,4 +11,13 @@ config ZYNQMP_PM
 	  Say yes to enable power management support for
 	  ZyqnMP SoC. In doubt, say N.
 
+config ZYNQMP_PM_DOMAINS
+	bool "Enable Zynq MPSoC generic PM domains"
+	default y
+	depends on PM
+	select PM_GENERIC_DOMAINS
+	help
+	  Say yes to enable device power management through PM domains
+	  In doubt, say N
+
 endmenu
diff --git a/drivers/soc/xilinx/zynqmp/Makefile b/drivers/soc/xilinx/zynqmp/Makefile
index 98034f7..3e530be 100644
--- a/drivers/soc/xilinx/zynqmp/Makefile
+++ b/drivers/soc/xilinx/zynqmp/Makefile
@@ -1 +1,2 @@
 obj-$(CONFIG_ZYNQMP_PM) += pm.o
+obj-$(CONFIG_ZYNQMP_PM_DOMAINS) += pm_domains.o
diff --git a/drivers/soc/xilinx/zynqmp/pm_domains.c b/drivers/soc/xilinx/zynqmp/pm_domains.c
new file mode 100644
index 0000000..2ab3829
--- /dev/null
+++ b/drivers/soc/xilinx/zynqmp/pm_domains.c
@@ -0,0 +1,343 @@
+/*
+ * ZynqMP Generic PM domain support
+ *
+ *  Copyright (C) 2014-2017 Xilinx, Inc.
+ *
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:     GPL-2.0+
+ */
+
+#include <linux/err.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/platform_device.h>
+#include <linux/pm_domain.h>
+#include <linux/slab.h>
+#include <linux/list.h>
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+
+#define DRIVER_NAME "zynqmp_gpd"
+
+/* Flag stating if PM nodes mapped to the PM domain has been requested */
+#define ZYNQMP_PM_DOMAIN_REQUESTED	BIT(0)
+
+/**
+ * struct zynqmp_pm_domain - Wrapper around struct generic_pm_domain
+ * @gpd:		Generic power domain
+ * @dev_list:		List of devices belong to power domain
+ * @node_ids:		PM node IDs corresponding to device(s) inside PM domain
+ * @node_id_num:	Number of PM node IDs
+ * @flags:		ZynqMP PM domain flags
+ */
+struct zynqmp_pm_domain {
+	struct generic_pm_domain gpd;
+	struct list_head dev_list;
+	u32 *node_ids;
+	int node_id_num;
+	u8 flags;
+};
+
+/*
+ * struct zynqmp_domain_device - Device node present in power domain
+ * @dev: Device
+ * &list: List member for the devices in domain list
+ */
+struct zynqmp_domain_device {
+	struct device *dev;
+	struct list_head list;
+};
+
+/**
+ * zynqmp_gpd_is_active_wakeup_path - Check if device is in wakeup source path
+ * @dev: Device to check for wakeup source path
+ * @not_used: Data member (not required)
+ *
+ * This function is checks device's child hierarchy and checks if any device is
+ * set as wakeup source.
+ *
+ * Return:	1 if device is in wakeup source path else 0.
+ */
+static int zynqmp_gpd_is_active_wakeup_path(struct device *dev, void *not_used)
+{
+	int may_wakeup;
+
+	may_wakeup = device_may_wakeup(dev);
+	if (may_wakeup)
+		return may_wakeup;
+
+	return device_for_each_child(dev, NULL,
+			zynqmp_gpd_is_active_wakeup_path);
+}
+
+/**
+ * zynqmp_gpd_power_on - Power on PM domain
+ * @domain:	Generic PM domain
+ *
+ * This function is called before devices inside a PM domain are resumed, to
+ * power on PM domain.
+ *
+ * Return:	0 on success, error code otherwise.
+ */
+static int zynqmp_gpd_power_on(struct generic_pm_domain *domain)
+{
+	int i, status = 0;
+	struct zynqmp_pm_domain *pd;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->set_requirement)
+		return status;
+
+	pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+	for (i = 0; i < pd->node_id_num; i++) {
+		status =
+		   eemi_ops->set_requirement(pd->node_ids[i],
+					     ZYNQMP_PM_CAPABILITY_ACCESS,
+					     ZYNQMP_PM_MAX_QOS,
+					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+		if (status)
+			break;
+	}
+	return status;
+}
+
+/**
+ * zynqmp_gpd_power_off - Power off PM domain
+ * @domain:	Generic PM domain
+ *
+ * This function is called after devices inside a PM domain are suspended, to
+ * power off PM domain.
+ *
+ * Return:	0 on success, error code otherwise.
+ */
+static int zynqmp_gpd_power_off(struct generic_pm_domain *domain)
+{
+	int i, status = 0;
+	struct zynqmp_pm_domain *pd;
+	struct zynqmp_domain_device *zdev, *tmp;
+	u32 capabilities = 0;
+	bool may_wakeup = 0;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->set_requirement)
+		return status;
+
+	pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+	/* If domain is already released there is nothing to be done */
+	if (!(pd->flags & ZYNQMP_PM_DOMAIN_REQUESTED))
+		return 0;
+
+	list_for_each_entry_safe(zdev, tmp, &pd->dev_list, list) {
+		/* If device is in wakeup path, set capability to WAKEUP */
+		may_wakeup = zynqmp_gpd_is_active_wakeup_path(zdev->dev, NULL);
+		if (may_wakeup) {
+			dev_dbg(zdev->dev, "device is in wakeup path in %s\n",
+				domain->name);
+			capabilities = ZYNQMP_PM_CAPABILITY_WAKEUP;
+			break;
+		}
+	}
+
+	for (i = pd->node_id_num - 1; i >= 0; i--) {
+		status = eemi_ops->set_requirement(pd->node_ids[i],
+						   capabilities, 0,
+						   ZYNQMP_PM_REQUEST_ACK_NO);
+		/**
+		 * If powering down of any node inside this domain fails,
+		 * report and return the error
+		 */
+		if (status) {
+			pr_err("%s error %d, node %u\n", __func__, status,
+			       pd->node_ids[i]);
+			return status;
+		}
+	}
+
+	return status;
+}
+
+/**
+ * zynqmp_gpd_attach_dev - Attach device to the PM domain
+ * @domain:	Generic PM domain
+ * @dev:	Device to attach
+ *
+ * Return:	0 on success, error code otherwise.
+ */
+static int zynqmp_gpd_attach_dev(struct generic_pm_domain *domain,
+				 struct device *dev)
+{
+	int i, status;
+	struct zynqmp_pm_domain *pd;
+	struct zynqmp_domain_device *zdev;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->request_node)
+		return -ENXIO;
+
+	pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+	zdev = devm_kzalloc(dev, sizeof(*zdev), GFP_KERNEL);
+	if (!zdev)
+		return -ENOMEM;
+
+	zdev->dev = dev;
+	list_add(&zdev->list, &pd->dev_list);
+
+	/* If this is not the first device to attach there is nothing to do */
+	if (domain->device_count)
+		return 0;
+
+	for (i = 0; i < pd->node_id_num; i++) {
+		status = eemi_ops->request_node(pd->node_ids[i], 0, 0,
+						ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+		/* If requesting a node fails print and return the error */
+		if (status) {
+			pr_err("%s error %d, node %u\n", __func__, status,
+			       pd->node_ids[i]);
+			list_del(&zdev->list);
+			zdev->dev = NULL;
+			devm_kfree(dev, zdev);
+			return status;
+		}
+	}
+
+	pd->flags |= ZYNQMP_PM_DOMAIN_REQUESTED;
+
+	return 0;
+}
+
+/**
+ * zynqmp_gpd_detach_dev - Detach device from the PM domain
+ * @domain:	Generic PM domain
+ * @dev:	Device to detach
+ */
+static void zynqmp_gpd_detach_dev(struct generic_pm_domain *domain,
+				  struct device *dev)
+{
+	int i, status;
+	struct zynqmp_pm_domain *pd;
+	struct zynqmp_domain_device *zdev, *tmp;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->release_node)
+		return;
+
+	pd = container_of(domain, struct zynqmp_pm_domain, gpd);
+
+	list_for_each_entry_safe(zdev, tmp, &pd->dev_list, list)
+		if (zdev->dev == dev) {
+			list_del(&zdev->list);
+			zdev->dev = NULL;
+			devm_kfree(dev, zdev);
+		}
+
+	/* If this is not the last device to detach there is nothing to do */
+	if (domain->device_count)
+		return;
+
+	for (i = 0; i < pd->node_id_num; i++) {
+		status = eemi_ops->release_node(pd->node_ids[i]);
+		/* If releasing a node fails print the error and return */
+		if (status) {
+			pr_err("%s error %d, node %u\n", __func__, status,
+			       pd->node_ids[i]);
+			return;
+		}
+	}
+
+	pd->flags &= ~ZYNQMP_PM_DOMAIN_REQUESTED;
+}
+
+/**
+ * zynqmp_gpd_probe - Initialize ZynqMP specific PM domains
+ * @pdev:	Platform device pointer
+ *
+ * Description:	This function populates struct zynqmp_pm_domain for each PM
+ * domain and initalizes generic PM domain. If the "pd-id" DT property
+ * of a certain domain is missing or invalid, that domain will be skipped.
+ *
+ * Return:	0 on success, error code otherwise.
+ */
+static int __init zynqmp_gpd_probe(struct platform_device *pdev)
+{
+	int ret;
+	struct device_node *child_err, *child, *np = pdev->dev.of_node;
+
+	for_each_child_of_node(np, child) {
+		struct zynqmp_pm_domain *pd;
+
+		pd = devm_kzalloc(&pdev->dev, sizeof(*pd), GFP_KERNEL);
+		if (!pd) {
+			ret = -ENOMEM;
+			goto err_cleanup;
+		}
+
+		ret = of_property_count_u32_elems(child, "pd-id");
+		if (ret <= 0)
+			goto err_cleanup;
+
+		pd->node_id_num = ret;
+		pd->node_ids = devm_kcalloc(&pdev->dev, ret,
+					    sizeof(*pd->node_ids), GFP_KERNEL);
+		if (!pd->node_ids) {
+			ret = -ENOMEM;
+			goto err_cleanup;
+		}
+
+		ret = of_property_read_u32_array(child, "pd-id", pd->node_ids,
+						 pd->node_id_num);
+		if (ret)
+			goto err_cleanup;
+
+		pd->gpd.name = kstrdup(child->name, GFP_KERNEL);
+		pd->gpd.power_off = zynqmp_gpd_power_off;
+		pd->gpd.power_on = zynqmp_gpd_power_on;
+		pd->gpd.attach_dev = zynqmp_gpd_attach_dev;
+		pd->gpd.detach_dev = zynqmp_gpd_detach_dev;
+
+		/* Mark all PM domains as initially powered off */
+		pm_genpd_init(&pd->gpd, NULL, true);
+
+		ret = of_genpd_add_provider_simple(child, &pd->gpd);
+		if (ret)
+			goto err_cleanup;
+
+		INIT_LIST_HEAD(&pd->dev_list);
+	}
+
+	return 0;
+
+err_cleanup:
+	child_err = child;
+	for_each_child_of_node(np, child) {
+		if (child == child_err)
+			break;
+		of_genpd_del_provider(child);
+	}
+
+	return ret;
+}
+
+static const struct of_device_id zynqmp_gpd_of_match[] = {
+	{ .compatible = "xlnx,zynqmp-genpd" },
+	{},
+};
+
+MODULE_DEVICE_TABLE(of, zynqmp_gpd_of_match);
+
+static struct platform_driver zynqmp_gpd_platform_driver = {
+	.driver	= {
+		.name = DRIVER_NAME,
+		.of_match_table = zynqmp_gpd_of_match,
+	},
+};
+
+static __init int zynqmp_gpd_init(void)
+{
+	return platform_driver_probe(&zynqmp_gpd_platform_driver,
+				     zynqmp_gpd_probe);
+}
+subsys_initcall(zynqmp_gpd_init);
-- 
2.7.4

^ permalink raw reply related

* [RFC PATCH] drivers: soc: xilinx: Add ZynqMP PM driver
From: Jolly Shah @ 2018-01-08 22:10 UTC (permalink / raw)
  To: linux-arm-kernel

Add ZynqMP PM driver. PM driver provides power management
support for ZynqMP.

Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
---
 .../bindings/soc/xilinx/xlnx,zynqmp-pm.txt         |  15 ++
 drivers/soc/Kconfig                                |   1 +
 drivers/soc/Makefile                               |   1 +
 drivers/soc/xilinx/Kconfig                         |   4 +
 drivers/soc/xilinx/Makefile                        |   4 +
 drivers/soc/xilinx/zynqmp/Kconfig                  |  15 ++
 drivers/soc/xilinx/zynqmp/Makefile                 |   1 +
 drivers/soc/xilinx/zynqmp/pm.c                     | 265 +++++++++++++++++++++
 8 files changed, 306 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt
 create mode 100644 drivers/soc/xilinx/Kconfig
 create mode 100644 drivers/soc/xilinx/Makefile
 create mode 100644 drivers/soc/xilinx/zynqmp/Kconfig
 create mode 100644 drivers/soc/xilinx/zynqmp/Makefile
 create mode 100644 drivers/soc/xilinx/zynqmp/pm.c

diff --git a/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt b/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt
new file mode 100644
index 0000000..9cfb40d
--- /dev/null
+++ b/Documentation/devicetree/bindings/soc/xilinx/xlnx,zynqmp-pm.txt
@@ -0,0 +1,15 @@
+Xilinx Zynq MPSoC Power Management Device Tree Bindings
+
+The zynqmp-pm node describes the power management configurations.
+
+Required properties:
+ - compatible		: Must contain:  "xlnx,zynqmp-pm"
+ - interrupt-parent	: Interrupt controller the interrupt is routed through
+ - interrupts		: Interrupt specifier
+
+Examples:
+	zynqmp-firmware {
+		compatible = "xlnx,zynqmp-pm";
+		interrupt-parent = <&gic>;
+		interrupts = <0 35 4>;
+	};
diff --git a/drivers/soc/Kconfig b/drivers/soc/Kconfig
index fc9e980..c07b4a8 100644
--- a/drivers/soc/Kconfig
+++ b/drivers/soc/Kconfig
@@ -16,6 +16,7 @@ source "drivers/soc/tegra/Kconfig"
 source "drivers/soc/ti/Kconfig"
 source "drivers/soc/ux500/Kconfig"
 source "drivers/soc/versatile/Kconfig"
+source "drivers/soc/xilinx/Kconfig"
 source "drivers/soc/zte/Kconfig"
 
 endmenu
diff --git a/drivers/soc/Makefile b/drivers/soc/Makefile
index deecb16..abb019a 100644
--- a/drivers/soc/Makefile
+++ b/drivers/soc/Makefile
@@ -19,6 +19,7 @@ obj-$(CONFIG_ARCH_ROCKCHIP)	+= rockchip/
 obj-$(CONFIG_SOC_SAMSUNG)	+= samsung/
 obj-$(CONFIG_ARCH_SUNXI)	+= sunxi/
 obj-$(CONFIG_ARCH_TEGRA)	+= tegra/
+obj-$(CONFIG_ARCH_ZYNQMP)	+= xilinx/
 obj-$(CONFIG_SOC_TI)		+= ti/
 obj-$(CONFIG_ARCH_U8500)	+= ux500/
 obj-$(CONFIG_PLAT_VERSATILE)	+= versatile/
diff --git a/drivers/soc/xilinx/Kconfig b/drivers/soc/xilinx/Kconfig
new file mode 100644
index 0000000..190add7
--- /dev/null
+++ b/drivers/soc/xilinx/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Kconfig for Xilinx SoCs
+
+source "drivers/soc/xilinx/zynqmp/Kconfig"
diff --git a/drivers/soc/xilinx/Makefile b/drivers/soc/xilinx/Makefile
new file mode 100644
index 0000000..bc9d560
--- /dev/null
+++ b/drivers/soc/xilinx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Makefile for Xilinx SoCs
+
+obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
diff --git a/drivers/soc/xilinx/zynqmp/Kconfig b/drivers/soc/xilinx/zynqmp/Kconfig
new file mode 100644
index 0000000..d3c784d
--- /dev/null
+++ b/drivers/soc/xilinx/zynqmp/Kconfig
@@ -0,0 +1,15 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Kconfig for Xilinx zynqmp SoC
+#
+menu "Zynq MPSoC SoC Drivers"
+	depends on ARCH_ZYNQMP
+
+
+config ZYNQMP_PM
+	bool "Enable Xilinx Zynq MPSoC Power Management"
+	depends on PM
+	help
+	  Say yes to enable power management support for
+	  ZyqnMP SoC. In doubt, say N.
+
+endmenu
diff --git a/drivers/soc/xilinx/zynqmp/Makefile b/drivers/soc/xilinx/zynqmp/Makefile
new file mode 100644
index 0000000..98034f7
--- /dev/null
+++ b/drivers/soc/xilinx/zynqmp/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_ZYNQMP_PM) += pm.o
diff --git a/drivers/soc/xilinx/zynqmp/pm.c b/drivers/soc/xilinx/zynqmp/pm.c
new file mode 100644
index 0000000..7178fb5
--- /dev/null
+++ b/drivers/soc/xilinx/zynqmp/pm.c
@@ -0,0 +1,265 @@
+/*
+ * Xilinx Zynq MPSoC Power Management
+ *
+ *  Copyright (C) 2014-2017 Xilinx, Inc.
+ *
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/compiler.h>
+#include <linux/arm-smccc.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/platform_device.h>
+#include <linux/debugfs.h>
+#include <linux/reboot.h>
+#include <linux/suspend.h>
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+
+#define DRIVER_NAME	"zynqmp_pm"
+
+/**
+ * struct zynqmp_pm_work_struct - Wrapper for struct work_struct
+ * @callback_work:	Work structure
+ * @args:		Callback arguments
+ */
+struct zynqmp_pm_work_struct {
+	struct work_struct callback_work;
+	u32 args[CB_ARG_CNT];
+};
+
+static struct zynqmp_pm_work_struct *zynqmp_pm_init_suspend_work;
+
+enum pm_suspend_mode {
+	PM_SUSPEND_MODE_STD,
+	PM_SUSPEND_MODE_POWER_OFF,
+};
+
+#define PM_SUSPEND_MODE_FIRST	PM_SUSPEND_MODE_STD
+
+static const char *const suspend_modes[] = {
+	[PM_SUSPEND_MODE_STD] = "standard",
+	[PM_SUSPEND_MODE_POWER_OFF] = "power-off",
+};
+
+static enum pm_suspend_mode suspend_mode = PM_SUSPEND_MODE_STD;
+
+enum pm_api_cb_id {
+	PM_INIT_SUSPEND_CB = 30,
+	PM_ACKNOWLEDGE_CB,
+	PM_NOTIFY_CB,
+};
+
+static irqreturn_t zynqmp_pm_isr(int irq, void *data)
+{
+	u32 payload[CB_PAYLOAD_SIZE];
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->get_callback_data)
+		return IRQ_NONE;
+
+	eemi_ops->get_callback_data(payload);
+
+	if (!payload[0])
+		return IRQ_NONE;
+
+	/* First element is callback API ID, others are callback arguments */
+	if (payload[0] == PM_INIT_SUSPEND_CB) {
+		if (work_pending(&zynqmp_pm_init_suspend_work->callback_work))
+			goto done;
+
+		/* Copy callback arguments into work's structure */
+		memcpy(zynqmp_pm_init_suspend_work->args, &payload[1],
+		       sizeof(zynqmp_pm_init_suspend_work->args));
+
+		queue_work(system_unbound_wq,
+			   &zynqmp_pm_init_suspend_work->callback_work);
+	}
+
+done:
+	return IRQ_HANDLED;
+}
+
+static const struct of_device_id pm_of_match[] = {
+	{ .compatible = "xlnx,zynqmp-pm", },
+	{ /* end of table */ },
+};
+
+MODULE_DEVICE_TABLE(of, pm_of_match);
+
+/**
+ * zynqmp_pm_init_suspend_work_fn - Initialize suspend
+ * @work:	Pointer to work_struct
+ *
+ * Bottom-half of PM callback IRQ handler.
+ */
+static void zynqmp_pm_init_suspend_work_fn(struct work_struct *work)
+{
+	struct zynqmp_pm_work_struct *pm_work =
+		container_of(work, struct zynqmp_pm_work_struct, callback_work);
+
+	if (pm_work->args[0] == ZYNQMP_PM_SUSPEND_REASON_SYSTEM_SHUTDOWN) {
+		orderly_poweroff(true);
+	} else if (pm_work->args[0] ==
+		   ZYNQMP_PM_SUSPEND_REASON_POWER_UNIT_REQUEST) {
+		pm_suspend(PM_SUSPEND_MEM);
+	} else {
+		pr_err("%s Unsupported InitSuspendCb reason code %d.\n"
+		       , __func__, pm_work->args[0]);
+	}
+}
+
+static ssize_t suspend_mode_show(struct device *dev,
+				 struct device_attribute *attr, char *buf)
+{
+	char *s = buf;
+	int md;
+
+	for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++)
+		if (suspend_modes[md]) {
+			if (md == suspend_mode)
+				s += sprintf(s, "[%s] ", suspend_modes[md]);
+			else
+				s += sprintf(s, "%s ", suspend_modes[md]);
+		}
+
+	/* Convert last space to newline */
+	if (s != buf)
+		*(s - 1) = '\n';
+	return (s - buf);
+}
+
+static ssize_t suspend_mode_store(struct device *dev,
+				  struct device_attribute *attr,
+				  const char *buf, size_t count)
+{
+	int md, ret = -EINVAL;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->set_suspend_mode)
+		return ret;
+
+	for (md = PM_SUSPEND_MODE_FIRST; md < ARRAY_SIZE(suspend_modes); md++)
+		if (suspend_modes[md] &&
+		    sysfs_streq(suspend_modes[md], buf)) {
+			ret = 0;
+			break;
+		}
+
+	if (!ret && md != suspend_mode) {
+		ret = eemi_ops->set_suspend_mode(md);
+		if (likely(!ret))
+			suspend_mode = md;
+	}
+
+	return ret ? ret : count;
+}
+
+static DEVICE_ATTR_RW(suspend_mode);
+
+/**
+ * zynqmp_pm_sysfs_init - Initialize PM driver sysfs interface
+ * @dev:	Pointer to device structure
+ *
+ * Return:	0 on success, negative error code otherwise
+ */
+static int zynqmp_pm_sysfs_init(struct device *dev)
+{
+	return sysfs_create_file(&dev->kobj, &dev_attr_suspend_mode.attr);
+}
+
+/**
+ * zynqmp_pm_probe - Probe existence of the PMU Firmware
+ *			and initialize debugfs interface
+ *
+ * @pdev:	Pointer to the platform_device structure
+ *
+ * Return:	Returns 0 on success
+ *		Negative error code otherwise
+ */
+static int zynqmp_pm_probe(struct platform_device *pdev)
+{
+	int ret, irq;
+	u32 pm_api_version;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->get_api_version)
+		return -ENXIO;
+
+	eemi_ops->get_api_version(&pm_api_version);
+
+	/* Check PM API version number */
+	if (pm_api_version != ZYNQMP_PM_VERSION)
+		return -ENODEV;
+
+	irq = platform_get_irq(pdev, 0);
+	if (irq <= 0)
+		return -ENXIO;
+
+	ret = request_irq(irq, zynqmp_pm_isr, IRQF_SHARED, DRIVER_NAME, pdev);
+	if (ret) {
+		dev_err(&pdev->dev, "request_irq '%d' failed with %d\n",
+			irq, ret);
+		return ret;
+	}
+
+	zynqmp_pm_init_suspend_work =
+		devm_kzalloc(&pdev->dev, sizeof(struct zynqmp_pm_work_struct),
+			     GFP_KERNEL);
+	if (!zynqmp_pm_init_suspend_work) {
+		ret = -ENOMEM;
+		goto error;
+	}
+
+	INIT_WORK(&zynqmp_pm_init_suspend_work->callback_work,
+		  zynqmp_pm_init_suspend_work_fn);
+
+	ret = zynqmp_pm_sysfs_init(&pdev->dev);
+	if (ret) {
+		dev_err(&pdev->dev, "unable to initialize sysfs interface\n");
+		goto error;
+	}
+
+	dev_info(&pdev->dev, "Power management API v%d.%d\n",
+		 ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
+
+	return 0;
+
+error:
+	free_irq(irq, 0);
+	return ret;
+}
+
+static struct platform_driver zynqmp_pm_platform_driver = {
+	.probe   = zynqmp_pm_probe,
+	.driver  = {
+			.name             = DRIVER_NAME,
+			.of_match_table   = pm_of_match,
+		   },
+};
+builtin_platform_driver(zynqmp_pm_platform_driver);
+
+/**
+ * zynqmp_pm_init - Notify PM firmware that initialization is completed
+ *
+ * Return:	Status returned from the PM firmware
+ */
+static int __init zynqmp_pm_init(void)
+{
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->init_finalize)
+		return -ENXIO;
+
+	return eemi_ops->init_finalize();
+}
+
+late_initcall_sync(zynqmp_pm_init);
-- 
2.7.4

^ permalink raw reply related

* [PATCH] drivers: firmware: xilinx: Add ZynqMP firmware driver
From: Jolly Shah @ 2018-01-08 22:07 UTC (permalink / raw)
  To: linux-arm-kernel

This patch is adding communication layer with firmware.
Firmware driver provides an interface to firmware APIs.
Interface APIs can be used by any driver to communicate to
PMUFW(Platform Management Unit). All requests go through ATF.
Firmware-debug provides debugfs interface to all APIs.
Firmware-ggs provides read/write interface to
global storage registers.

Signed-off-by: Jolly Shah <jollys@xilinx.com>
Signed-off-by: Rajan Vaja <rajanv@xilinx.com>
---
 .../firmware/xilinx/xlnx,zynqmp-firmware.txt       |   16 +
 arch/arm64/Kconfig.platforms                       |    1 +
 drivers/firmware/Kconfig                           |    1 +
 drivers/firmware/Makefile                          |    1 +
 drivers/firmware/xilinx/Kconfig                    |    4 +
 drivers/firmware/xilinx/Makefile                   |    4 +
 drivers/firmware/xilinx/zynqmp/Kconfig             |   23 +
 drivers/firmware/xilinx/zynqmp/Makefile            |    5 +
 drivers/firmware/xilinx/zynqmp/firmware-debug.c    |  540 +++++++++++
 drivers/firmware/xilinx/zynqmp/firmware-ggs.c      |  298 ++++++
 drivers/firmware/xilinx/zynqmp/firmware.c          | 1024 ++++++++++++++++++++
 .../linux/firmware/xilinx/zynqmp/firmware-debug.h  |   32 +
 include/linux/firmware/xilinx/zynqmp/firmware.h    |  573 +++++++++++
 13 files changed, 2522 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
 create mode 100644 drivers/firmware/xilinx/Kconfig
 create mode 100644 drivers/firmware/xilinx/Makefile
 create mode 100644 drivers/firmware/xilinx/zynqmp/Kconfig
 create mode 100644 drivers/firmware/xilinx/zynqmp/Makefile
 create mode 100644 drivers/firmware/xilinx/zynqmp/firmware-debug.c
 create mode 100644 drivers/firmware/xilinx/zynqmp/firmware-ggs.c
 create mode 100644 drivers/firmware/xilinx/zynqmp/firmware.c
 create mode 100644 include/linux/firmware/xilinx/zynqmp/firmware-debug.h
 create mode 100644 include/linux/firmware/xilinx/zynqmp/firmware.h

diff --git a/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
new file mode 100644
index 0000000..ace111c
--- /dev/null
+++ b/Documentation/devicetree/bindings/firmware/xilinx/xlnx,zynqmp-firmware.txt
@@ -0,0 +1,16 @@
+Xilinx Zynq MPSoC Firmware Device Tree Bindings
+
+The zynqmp-firmware node describes the interface to platform firmware.
+
+Required properties:
+ - compatible:	Must contain:  "xlnx,zynqmp-firmware"
+ - method:	The method of calling the PM-API firmware layer.
+		Permitted values are:
+		 - "smc" : To be used in configurations without a hypervisor
+		 - "hvc" : To be used when hypervisor is present
+
+Examples:
+	firmware: firmware {
+		compatible = "xlnx,zynqmp-firmware";
+		method = "smc";
+	};
diff --git a/arch/arm64/Kconfig.platforms b/arch/arm64/Kconfig.platforms
index 2401373..3dd3ae9 100644
--- a/arch/arm64/Kconfig.platforms
+++ b/arch/arm64/Kconfig.platforms
@@ -273,6 +273,7 @@ config ARCH_ZX
 
 config ARCH_ZYNQMP
 	bool "Xilinx ZynqMP Family"
+	select ZYNQMP_FIRMWARE
 	help
 	  This enables support for Xilinx ZynqMP Family
 
diff --git a/drivers/firmware/Kconfig b/drivers/firmware/Kconfig
index fa87a055..18fc2a8 100644
--- a/drivers/firmware/Kconfig
+++ b/drivers/firmware/Kconfig
@@ -249,5 +249,6 @@ source "drivers/firmware/google/Kconfig"
 source "drivers/firmware/efi/Kconfig"
 source "drivers/firmware/meson/Kconfig"
 source "drivers/firmware/tegra/Kconfig"
+source "drivers/firmware/xilinx/Kconfig"
 
 endmenu
diff --git a/drivers/firmware/Makefile b/drivers/firmware/Makefile
index feaa890..43a24b5 100644
--- a/drivers/firmware/Makefile
+++ b/drivers/firmware/Makefile
@@ -30,3 +30,4 @@ obj-$(CONFIG_GOOGLE_FIRMWARE)	+= google/
 obj-$(CONFIG_EFI)		+= efi/
 obj-$(CONFIG_UEFI_CPER)		+= efi/
 obj-y				+= tegra/
+obj-y				+= xilinx/
diff --git a/drivers/firmware/xilinx/Kconfig b/drivers/firmware/xilinx/Kconfig
new file mode 100644
index 0000000..dd3cddb
--- /dev/null
+++ b/drivers/firmware/xilinx/Kconfig
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Kconfig for Xilinx firmwares
+
+source "drivers/firmware/xilinx/zynqmp/Kconfig"
diff --git a/drivers/firmware/xilinx/Makefile b/drivers/firmware/xilinx/Makefile
new file mode 100644
index 0000000..aba1f86
--- /dev/null
+++ b/drivers/firmware/xilinx/Makefile
@@ -0,0 +1,4 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Makefile for Xilinx firmwares
+
+obj-$(CONFIG_ARCH_ZYNQMP) += zynqmp/
diff --git a/drivers/firmware/xilinx/zynqmp/Kconfig b/drivers/firmware/xilinx/zynqmp/Kconfig
new file mode 100644
index 0000000..1f815e0
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp/Kconfig
@@ -0,0 +1,23 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Kconfig for Xilinx zynqmp firmware
+
+menu "Zynq MPSoC Firmware Drivers"
+	depends on ARCH_ZYNQMP
+
+config ZYNQMP_FIRMWARE
+	bool "Enable Xilinx Zynq MPSoC firmware interface"
+	help
+	  Firmware interface driver is used by different to
+	  communicate with the firmware for various platform
+	  management services.
+	  Say yes to enable zynqmp firmware interface driver.
+	  In doubt, say N
+
+config ZYNQMP_FIRMWARE_DEBUG
+	bool "Enable Xilinx Zynq MPSoC firmware debug APIs"
+	depends on ARCH_ZYNQMP && DEBUG_FS
+	help
+	  Say yes to enable zynqmp firmware interface debug APIs.
+	  In doubt, say N
+
+endmenu
diff --git a/drivers/firmware/xilinx/zynqmp/Makefile b/drivers/firmware/xilinx/zynqmp/Makefile
new file mode 100644
index 0000000..97086b5
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp/Makefile
@@ -0,0 +1,5 @@
+# SPDX-License-Identifier:	GPL-2.0+
+# Makefile for Xilinx firmwares
+
+obj-$(CONFIG_ZYNQMP_FIRMWARE) += firmware.o firmware-ggs.o
+obj-$(CONFIG_ZYNQMP_FIRMWARE_DEBUG) += firmware-debug.o
diff --git a/drivers/firmware/xilinx/zynqmp/firmware-debug.c b/drivers/firmware/xilinx/zynqmp/firmware-debug.c
new file mode 100644
index 0000000..83b1c45
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp/firmware-debug.c
@@ -0,0 +1,540 @@
+/*
+ * Xilinx Zynq MPSoC Firmware layer for debugfs APIs
+ *
+ *  Copyright (C) 2014-2017 Xilinx, Inc.
+ *
+ *  Michal Simek <michal.simek@xilinx.com>
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/compiler.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/debugfs.h>
+#include <linux/uaccess.h>
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+#include <linux/firmware/xilinx/zynqmp/firmware-debug.h>
+
+#define DRIVER_NAME	"zynqmp-firmware"
+
+/**
+ * zynqmp_pm_self_suspend - PM call for master to suspend itself
+ * @node:	Node ID of the master or subsystem
+ * @latency:	Requested maximum wakeup latency (not supported)
+ * @state:	Requested state (not supported)
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+int zynqmp_pm_self_suspend(const u32 node,
+			   const u32 latency,
+			   const u32 state)
+{
+	return invoke_pm_fn(SELF_SUSPEND, node, latency, state, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_abort_suspend - PM call to announce that a prior suspend request
+ *				is to be aborted.
+ * @reason:	Reason for the abort
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+int zynqmp_pm_abort_suspend(const enum zynqmp_pm_abort_reason reason)
+{
+	return invoke_pm_fn(ABORT_SUSPEND, reason, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_register_notifier - Register the PU to be notified of PM events
+ * @node:	Node ID of the slave
+ * @event:	The event to be notified about
+ * @wake:	Wake up on event
+ * @enable:	Enable or disable the notifier
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+int zynqmp_pm_register_notifier(const u32 node, const u32 event,
+				const u32 wake, const u32 enable)
+{
+	return invoke_pm_fn(REGISTER_NOTIFIER, node, event,
+			    wake, enable, NULL);
+}
+
+/**
+ * zynqmp_pm_argument_value - Extract argument value from a PM-API request
+ * @arg:	Entered PM-API argument in string format
+ *
+ * Return:	Argument value in unsigned integer format on success
+ *		0 otherwise
+ */
+static u64 zynqmp_pm_argument_value(char *arg)
+{
+	u64 value;
+
+	if (!arg)
+		return 0;
+
+	if (!kstrtou64(arg, 0, &value))
+		return value;
+
+	return 0;
+}
+
+static struct dentry *zynqmp_pm_debugfs_dir;
+static struct dentry *zynqmp_pm_debugfs_power;
+static struct dentry *zynqmp_pm_debugfs_api_version;
+
+/**
+ * zynqmp_pm_debugfs_api_write - debugfs write function
+ * @file:	User file structure
+ * @ptr:	User entered PM-API string
+ * @len:	Length of the userspace buffer
+ * @off:	Offset within the file
+ *
+ * Return:	Number of bytes copied if PM-API request succeeds,
+ *		the corresponding error code otherwise
+ *
+ * Used for triggering pm api functions by writing
+ * echo <pm_api_id>    > /sys/kernel/debug/zynqmp_pm/power or
+ * echo <pm_api_name>  > /sys/kernel/debug/zynqmp_pm/power
+ */
+static ssize_t zynqmp_pm_debugfs_api_write(struct file *file,
+					   const char __user *ptr, size_t len,
+					   loff_t *off)
+{
+	char *kern_buff, *tmp_buff;
+	char *pm_api_req;
+	u32 pm_id = 0;
+	u64 pm_api_arg[4];
+	/* Return values from PM APIs calls */
+	u32 pm_api_ret[4] = {0, 0, 0, 0};
+	u32 pm_api_version;
+
+	int ret;
+	int i = 0;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops)
+		return -ENXIO;
+
+	if (*off != 0 || len <= 0)
+		return -EINVAL;
+
+	kern_buff = kzalloc(len, GFP_KERNEL);
+	if (!kern_buff)
+		return -ENOMEM;
+	tmp_buff = kern_buff;
+
+	while (i < ARRAY_SIZE(pm_api_arg))
+		pm_api_arg[i++] = 0;
+
+	ret = strncpy_from_user(kern_buff, ptr, len);
+	if (ret < 0) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	/* Read the API name from a user request */
+	pm_api_req = strsep(&kern_buff, " ");
+
+	if (strncasecmp(pm_api_req, "REQUEST_SUSPEND", 15) == 0)
+		pm_id = REQUEST_SUSPEND;
+	else if (strncasecmp(pm_api_req, "SELF_SUSPEND", 12) == 0)
+		pm_id = SELF_SUSPEND;
+	else if (strncasecmp(pm_api_req, "FORCE_POWERDOWN", 15) == 0)
+		pm_id = FORCE_POWERDOWN;
+	else if (strncasecmp(pm_api_req, "ABORT_SUSPEND", 13) == 0)
+		pm_id = ABORT_SUSPEND;
+	else if (strncasecmp(pm_api_req, "REQUEST_WAKEUP", 14) == 0)
+		pm_id = REQUEST_WAKEUP;
+	else if (strncasecmp(pm_api_req, "SET_WAKEUP_SOURCE", 17) == 0)
+		pm_id = SET_WAKEUP_SOURCE;
+	else if (strncasecmp(pm_api_req, "SYSTEM_SHUTDOWN", 15) == 0)
+		pm_id = SYSTEM_SHUTDOWN;
+	else if (strncasecmp(pm_api_req, "REQUEST_NODE", 12) == 0)
+		pm_id = REQUEST_NODE;
+	else if (strncasecmp(pm_api_req, "RELEASE_NODE", 12) == 0)
+		pm_id = RELEASE_NODE;
+	else if (strncasecmp(pm_api_req, "SET_REQUIREMENT", 15) == 0)
+		pm_id = SET_REQUIREMENT;
+	else if (strncasecmp(pm_api_req, "SET_MAX_LATENCY", 15) == 0)
+		pm_id = SET_MAX_LATENCY;
+	else if (strncasecmp(pm_api_req, "GET_API_VERSION", 15) == 0)
+		pm_id = GET_API_VERSION;
+	else if (strncasecmp(pm_api_req, "SET_CONFIGURATION", 17) == 0)
+		pm_id = SET_CONFIGURATION;
+	else if (strncasecmp(pm_api_req, "GET_NODE_STATUS", 15) == 0)
+		pm_id = GET_NODE_STATUS;
+	else if (strncasecmp(pm_api_req,
+			     "GET_OPERATING_CHARACTERISTIC", 28) == 0)
+		pm_id = GET_OPERATING_CHARACTERISTIC;
+	else if (strncasecmp(pm_api_req, "REGISTER_NOTIFIER", 17) == 0)
+		pm_id = REGISTER_NOTIFIER;
+	else if (strncasecmp(pm_api_req, "RESET_ASSERT", 12) == 0)
+		pm_id = RESET_ASSERT;
+	else if (strncasecmp(pm_api_req, "RESET_GET_STATUS", 16) == 0)
+		pm_id = RESET_GET_STATUS;
+	else if (strncasecmp(pm_api_req, "MMIO_READ", 9) == 0)
+		pm_id = MMIO_READ;
+	else if (strncasecmp(pm_api_req, "MMIO_WRITE", 10) == 0)
+		pm_id = MMIO_WRITE;
+	else if (strncasecmp(pm_api_req, "GET_CHIPID", 9) == 0)
+		pm_id = GET_CHIPID;
+	else if (strncasecmp(pm_api_req, "PINCTRL_GET_FUNCTION", 21) == 0)
+		pm_id = PINCTRL_GET_FUNCTION;
+	else if (strncasecmp(pm_api_req, "PINCTRL_SET_FUNCTION", 21) == 0)
+		pm_id = PINCTRL_SET_FUNCTION;
+	else if (strncasecmp(pm_api_req,
+			     "PINCTRL_CONFIG_PARAM_GET", 25) == 0)
+		pm_id = PINCTRL_CONFIG_PARAM_GET;
+	else if (strncasecmp(pm_api_req,
+			     "PINCTRL_CONFIG_PARAM_SET", 25) == 0)
+		pm_id = PINCTRL_CONFIG_PARAM_SET;
+	else if (strncasecmp(pm_api_req, "IOCTL", 6) == 0)
+		pm_id = IOCTL;
+	else if (strncasecmp(pm_api_req, "CLOCK_ENABLE", 12) == 0)
+		pm_id = CLOCK_ENABLE;
+	else if (strncasecmp(pm_api_req, "CLOCK_DISABLE", 13) == 0)
+		pm_id = CLOCK_DISABLE;
+	else if (strncasecmp(pm_api_req, "CLOCK_GETSTATE", 14) == 0)
+		pm_id = CLOCK_GETSTATE;
+	else if (strncasecmp(pm_api_req, "CLOCK_SETDIVIDER", 16) == 0)
+		pm_id = CLOCK_SETDIVIDER;
+	else if (strncasecmp(pm_api_req, "CLOCK_GETDIVIDER", 16) == 0)
+		pm_id = CLOCK_GETDIVIDER;
+	else if (strncasecmp(pm_api_req, "CLOCK_SETRATE", 13) == 0)
+		pm_id = CLOCK_SETRATE;
+	else if (strncasecmp(pm_api_req, "CLOCK_GETRATE", 13) == 0)
+		pm_id = CLOCK_GETRATE;
+	else if (strncasecmp(pm_api_req, "CLOCK_SETPARENT", 15) == 0)
+		pm_id = CLOCK_SETPARENT;
+	else if (strncasecmp(pm_api_req, "CLOCK_GETPARENT", 15) == 0)
+		pm_id = CLOCK_GETPARENT;
+	else if (strncasecmp(pm_api_req, "QUERY_DATA", 22) == 0)
+		pm_id = QUERY_DATA;
+
+	/* If no name was entered look for PM-API ID instead */
+	else if (kstrtouint(pm_api_req, 10, &pm_id))
+		ret = -EINVAL;
+
+	/* Read node_id and arguments from the PM-API request */
+	i = 0;
+	pm_api_req = strsep(&kern_buff, " ");
+	while ((i < ARRAY_SIZE(pm_api_arg)) && pm_api_req) {
+		pm_api_arg[i++] = zynqmp_pm_argument_value(pm_api_req);
+		pm_api_req = strsep(&kern_buff, " ");
+	}
+
+	switch (pm_id) {
+	case GET_API_VERSION:
+		eemi_ops->get_api_version(&pm_api_version);
+		pr_info("%s PM-API Version = %d.%d\n", __func__,
+			pm_api_version >> 16, pm_api_version & 0xffff);
+		break;
+	case REQUEST_SUSPEND:
+		ret = eemi_ops->request_suspend(pm_api_arg[0],
+						pm_api_arg[1] ? pm_api_arg[1] :
+						ZYNQMP_PM_REQUEST_ACK_NO,
+						pm_api_arg[2] ? pm_api_arg[2] :
+						ZYNQMP_PM_MAX_LATENCY, 0);
+		break;
+	case SELF_SUSPEND:
+		ret = zynqmp_pm_self_suspend(pm_api_arg[0],
+					     pm_api_arg[1] ? pm_api_arg[1] :
+					     ZYNQMP_PM_MAX_LATENCY, 0);
+		break;
+	case FORCE_POWERDOWN:
+		ret = eemi_ops->force_powerdown(pm_api_arg[0],
+						pm_api_arg[1] ? pm_api_arg[1] :
+						ZYNQMP_PM_REQUEST_ACK_NO);
+		break;
+	case ABORT_SUSPEND:
+		ret = zynqmp_pm_abort_suspend(pm_api_arg[0] ? pm_api_arg[0] :
+					      ZYNQMP_PM_ABORT_REASON_UNKNOWN);
+		break;
+	case REQUEST_WAKEUP:
+		ret = eemi_ops->request_wakeup(pm_api_arg[0],
+					       pm_api_arg[1], pm_api_arg[2],
+					       pm_api_arg[3] ? pm_api_arg[3] :
+					       ZYNQMP_PM_REQUEST_ACK_NO);
+		break;
+	case SET_WAKEUP_SOURCE:
+		ret = eemi_ops->set_wakeup_source(pm_api_arg[0], pm_api_arg[1],
+						  pm_api_arg[2]);
+		break;
+	case SYSTEM_SHUTDOWN:
+		ret = eemi_ops->system_shutdown(pm_api_arg[0], pm_api_arg[1]);
+		break;
+	case REQUEST_NODE:
+		ret = eemi_ops->request_node(pm_api_arg[0],
+					     pm_api_arg[1] ? pm_api_arg[1] :
+					     ZYNQMP_PM_CAPABILITY_ACCESS,
+					     pm_api_arg[2] ? pm_api_arg[2] : 0,
+					     pm_api_arg[3] ? pm_api_arg[3] :
+					     ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+		break;
+	case RELEASE_NODE:
+		ret = eemi_ops->release_node(pm_api_arg[0]);
+		break;
+	case SET_REQUIREMENT:
+		ret = eemi_ops->set_requirement(pm_api_arg[0],
+						pm_api_arg[1] ? pm_api_arg[1] :
+						ZYNQMP_PM_CAPABILITY_CONTEXT,
+						pm_api_arg[2] ?
+						pm_api_arg[2] : 0,
+						pm_api_arg[3] ? pm_api_arg[3] :
+						ZYNQMP_PM_REQUEST_ACK_BLOCKING);
+		break;
+	case SET_MAX_LATENCY:
+		ret = eemi_ops->set_max_latency(pm_api_arg[0],
+						pm_api_arg[1] ? pm_api_arg[1] :
+						ZYNQMP_PM_MAX_LATENCY);
+		break;
+	case SET_CONFIGURATION:
+		ret = eemi_ops->set_configuration(pm_api_arg[0]);
+		break;
+	case GET_NODE_STATUS:
+		ret = eemi_ops->get_node_status(pm_api_arg[0],
+						&pm_api_ret[0],
+						&pm_api_ret[1],
+						&pm_api_ret[2]);
+		if (!ret)
+			pr_info("GET_NODE_STATUS:\n\tNodeId: %llu\n\tStatus: %u\n\tRequirements: %u\n\tUsage: %u\n",
+				pm_api_arg[0], pm_api_ret[0],
+				pm_api_ret[1], pm_api_ret[2]);
+		break;
+	case GET_OPERATING_CHARACTERISTIC:
+		ret = eemi_ops->get_operating_characteristic(pm_api_arg[0],
+				pm_api_arg[1] ? pm_api_arg[1] :
+				ZYNQMP_PM_OPERATING_CHARACTERISTIC_POWER,
+				&pm_api_ret[0]);
+		if (!ret)
+			pr_info("GET_OPERATING_CHARACTERISTIC:\n\tNodeId: %llu\n\tType: %llu\n\tResult: %u\n",
+				pm_api_arg[0], pm_api_arg[1], pm_api_ret[0]);
+		break;
+	case REGISTER_NOTIFIER:
+		ret = zynqmp_pm_register_notifier(pm_api_arg[0],
+						  pm_api_arg[1] ?
+						  pm_api_arg[1] : 0,
+						  pm_api_arg[2] ?
+						  pm_api_arg[2] : 0,
+						  pm_api_arg[3] ?
+						  pm_api_arg[3] : 0);
+		break;
+	case RESET_ASSERT:
+		ret = eemi_ops->reset_assert(pm_api_arg[0], pm_api_arg[1]);
+		break;
+	case RESET_GET_STATUS:
+		ret = eemi_ops->reset_get_status(pm_api_arg[0], &pm_api_ret[0]);
+		pr_info("%s Reset status: %u\n", __func__, pm_api_ret[0]);
+		break;
+	case GET_CHIPID:
+		ret = eemi_ops->get_chipid(&pm_api_ret[0], &pm_api_ret[1]);
+		pr_info("%s idcode: %#x, version:%#x\n",
+			__func__, pm_api_ret[0], pm_api_ret[1]);
+		break;
+	case PINCTRL_GET_FUNCTION:
+		ret = eemi_ops->pinctrl_get_function(pm_api_arg[0],
+						     &pm_api_ret[0]);
+		pr_info("%s Current set function for the pin: %u\n",
+			__func__, pm_api_ret[0]);
+		break;
+	case PINCTRL_SET_FUNCTION:
+		ret = eemi_ops->pinctrl_set_function(pm_api_arg[0],
+						     pm_api_arg[1]);
+		break;
+	case PINCTRL_CONFIG_PARAM_GET:
+		ret = eemi_ops->pinctrl_get_config(pm_api_arg[0], pm_api_arg[1],
+						   &pm_api_ret[0]);
+		pr_info("%s pin: %llu, param: %llu, value: %u\n",
+			__func__, pm_api_arg[0], pm_api_arg[1],
+			pm_api_ret[0]);
+		break;
+	case PINCTRL_CONFIG_PARAM_SET:
+		ret = eemi_ops->pinctrl_set_config(pm_api_arg[0],
+						   pm_api_arg[1],
+						   pm_api_arg[2]);
+		break;
+	case IOCTL:
+		ret = eemi_ops->ioctl(pm_api_arg[0], pm_api_arg[1],
+				      pm_api_arg[2], pm_api_arg[3],
+				      &pm_api_ret[0]);
+		if (pm_api_arg[1] == IOCTL_GET_RPU_OPER_MODE ||
+		    pm_api_arg[1] == IOCTL_GET_PLL_FRAC_MODE ||
+		    pm_api_arg[1] == IOCTL_GET_PLL_FRAC_DATA ||
+		    pm_api_arg[1] == IOCTL_READ_GGS ||
+		    pm_api_arg[1] == IOCTL_READ_PGGS)
+			pr_info("%s Value: %u\n",
+				__func__, pm_api_ret[1]);
+		break;
+	case CLOCK_ENABLE:
+		ret = eemi_ops->clock_enable(pm_api_arg[0]);
+		break;
+	case CLOCK_DISABLE:
+		ret = eemi_ops->clock_disable(pm_api_arg[0]);
+		break;
+	case CLOCK_GETSTATE:
+		ret = eemi_ops->clock_getstate(pm_api_arg[0], &pm_api_ret[0]);
+		pr_info("%s state: %u\n", __func__, pm_api_ret[0]);
+		break;
+	case CLOCK_SETDIVIDER:
+		ret = eemi_ops->clock_setdivider(pm_api_arg[0], pm_api_arg[1]);
+		break;
+	case CLOCK_GETDIVIDER:
+		ret = eemi_ops->clock_getdivider(pm_api_arg[0], &pm_api_ret[0]);
+		pr_info("%s Divider Value: %d\n", __func__, pm_api_ret[0]);
+		break;
+	case CLOCK_SETRATE:
+		ret = eemi_ops->clock_setrate(pm_api_arg[0], pm_api_arg[1]);
+		break;
+	case CLOCK_GETRATE:
+		ret = eemi_ops->clock_getrate(pm_api_arg[0], &pm_api_ret[0]);
+		pr_info("%s Rate Value: %u\n", __func__, pm_api_ret[0]);
+		break;
+	case CLOCK_SETPARENT:
+		ret = eemi_ops->clock_setparent(pm_api_arg[0], pm_api_arg[1]);
+		break;
+	case CLOCK_GETPARENT:
+		ret = eemi_ops->clock_getparent(pm_api_arg[0], &pm_api_ret[0]);
+		pr_info("%s Parent Index: %u\n", __func__, pm_api_ret[0]);
+		break;
+	case QUERY_DATA:
+	{
+		struct zynqmp_pm_query_data qdata = {0};
+
+		qdata.qid = pm_api_arg[0];
+		qdata.arg1 = pm_api_arg[1];
+		qdata.arg2 = pm_api_arg[2];
+		qdata.arg3 = pm_api_arg[3];
+
+		ret = eemi_ops->query_data(qdata, pm_api_ret);
+
+		pr_info("%s: data[0] = 0x%08x\n", __func__, pm_api_ret[0]);
+		pr_info("%s: data[1] = 0x%08x\n", __func__, pm_api_ret[1]);
+		pr_info("%s: data[2] = 0x%08x\n", __func__, pm_api_ret[2]);
+		pr_info("%s: data[3] = 0x%08x\n", __func__, pm_api_ret[3]);
+		break;
+	}
+	default:
+		pr_err("%s Unsupported PM-API request\n", __func__);
+		ret = -EINVAL;
+	}
+
+err:
+	kfree(tmp_buff);
+	if (ret)
+		return ret;
+
+	return len;
+}
+
+/**
+ * zynqmp_pm_debugfs_api_version_read - debugfs read function
+ * @file:	User file structure
+ * @ptr:	Requested pm_api_version string
+ * @len:	Length of the userspace buffer
+ * @off:	Offset within the file
+ *
+ * Return:	Length of the version string on success
+ *		-EFAULT otherwise
+ *
+ * Used to display the pm api version.
+ * cat /sys/kernel/debug/zynqmp_pm/pm_api_version
+ */
+static ssize_t zynqmp_pm_debugfs_api_version_read(struct file *file,
+						  char __user *ptr, size_t len,
+						  loff_t *off)
+{
+	char *kern_buff;
+	int ret;
+	int kern_buff_len;
+	u32 pm_api_version;
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->get_api_version)
+		return -ENXIO;
+
+	if (len <= 0)
+		return -EINVAL;
+
+	if (*off != 0)
+		return 0;
+
+	kern_buff = kzalloc(len, GFP_KERNEL);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	eemi_ops->get_api_version(&pm_api_version);
+	sprintf(kern_buff, "PM-API Version = %d.%d\n",
+		pm_api_version >> 16, pm_api_version & 0xffff);
+	kern_buff_len = strlen(kern_buff) + 1;
+
+	if (len > kern_buff_len)
+		len = kern_buff_len;
+	ret = copy_to_user(ptr, kern_buff, len);
+
+	kfree(kern_buff);
+	if (ret)
+		return -EFAULT;
+
+	*off = len + 1;
+
+	return len;
+}
+
+/* Setup debugfs fops */
+static const struct file_operations fops_zynqmp_pm_dbgfs = {
+	.owner  =	THIS_MODULE,
+	.write  =	zynqmp_pm_debugfs_api_write,
+	.read   =	zynqmp_pm_debugfs_api_version_read,
+};
+
+/**
+ * zynqmp_pm_api_debugfs_init - Initialize debugfs interface
+ *
+ * Return:      Returns 0 on success
+ *		Corresponding error code otherwise
+ */
+int zynqmp_pm_api_debugfs_init(void)
+{
+	int err;
+
+	/* Initialize debugfs interface */
+	zynqmp_pm_debugfs_dir = debugfs_create_dir(DRIVER_NAME, NULL);
+	if (!zynqmp_pm_debugfs_dir) {
+		pr_err("debugfs_create_dir failed\n");
+		return -ENODEV;
+	}
+
+	zynqmp_pm_debugfs_power =
+		debugfs_create_file("pm", 0220,
+				    zynqmp_pm_debugfs_dir, NULL,
+				    &fops_zynqmp_pm_dbgfs);
+	if (!zynqmp_pm_debugfs_power) {
+		pr_err("debugfs_create_file power failed\n");
+		err = -ENODEV;
+		goto err_dbgfs;
+	}
+
+	zynqmp_pm_debugfs_api_version =
+		debugfs_create_file("api_version", 0444,
+				    zynqmp_pm_debugfs_dir, NULL,
+				    &fops_zynqmp_pm_dbgfs);
+	if (!zynqmp_pm_debugfs_api_version) {
+		pr_err("debugfs_create_file api_version failed\n");
+		err = -ENODEV;
+		goto err_dbgfs;
+	}
+
+	return 0;
+
+err_dbgfs:
+	debugfs_remove_recursive(zynqmp_pm_debugfs_dir);
+	zynqmp_pm_debugfs_dir = NULL;
+
+	return err;
+}
diff --git a/drivers/firmware/xilinx/zynqmp/firmware-ggs.c b/drivers/firmware/xilinx/zynqmp/firmware-ggs.c
new file mode 100644
index 0000000..feb6148
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp/firmware-ggs.c
@@ -0,0 +1,298 @@
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ *  Copyright (C) 2014-2017 Xilinx, Inc.
+ *
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/compiler.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/uaccess.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+
+static ssize_t read_register(char *buf, u32 ioctl_id, u32 reg)
+{
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->ioctl)
+		return 0;
+
+	ret = eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload);
+	if (ret)
+		return ret;
+
+	return snprintf(buf, PAGE_SIZE, "0x%x\n", ret_payload[1]);
+}
+
+static ssize_t write_register(const char *buf, size_t count,
+			      u32 ioctl_id, u32 reg)
+{
+	char *kern_buff;
+	char *inbuf;
+	char *tok;
+	long mask;
+	long value;
+	int ret;
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+	const struct zynqmp_eemi_ops *eemi_ops = get_eemi_ops();
+
+	if (!eemi_ops || !eemi_ops->ioctl)
+		return -EFAULT;
+
+	kern_buff = kzalloc(count, GFP_KERNEL);
+	if (!kern_buff)
+		return -ENOMEM;
+
+	ret = strlcpy(kern_buff, buf, count);
+	if (ret < 0) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	inbuf = kern_buff;
+
+	/* Read the write mask */
+	tok = strsep(&inbuf, " ");
+	if (!tok) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	ret = kstrtol(tok, 16, &mask);
+	if (ret) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	/* Read the write value */
+	tok = strsep(&inbuf, " ");
+	if (!tok) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	ret = kstrtol(tok, 16, &value);
+	if (ret) {
+		ret = -EFAULT;
+		goto err;
+	}
+
+	ret = eemi_ops->ioctl(0, ioctl_id, reg, 0, ret_payload);
+	if (ret) {
+		ret = -EFAULT;
+		goto err;
+	}
+	ret_payload[1] &= ~mask;
+	value &= mask;
+	value |= ret_payload[1];
+
+	ret = eemi_ops->ioctl(0, ioctl_id, reg, value, NULL);
+	if (ret)
+		ret = -EFAULT;
+
+err:
+	kfree(kern_buff);
+	if (ret)
+		return ret;
+
+	return count;
+}
+
+/**
+ * ggs_show - Show global general storage (ggs) sysfs attribute
+ * @dev: Device structure
+ * @attr: Device attribute structure
+ * @buf: Requested available shutdown_scope attributes string
+ * @reg: Register number
+ *
+ * Return:Number of bytes printed into the buffer.
+ *
+ * Helper function for viewing a ggs register value.
+ *
+ * User-space interface for viewing the content of the ggs0 register.
+ * cat /sys/devices/platform/firmware/ggs0
+ */
+static ssize_t ggs_show(struct device *dev,
+			struct device_attribute *attr,
+			char *buf,
+			u32 reg)
+{
+	return read_register(buf, IOCTL_READ_GGS, reg);
+}
+
+/**
+ * ggs_store - Store global general storage (ggs) sysfs attribute
+ * @dev: Device structure
+ * @attr: Device attribute structure
+ * @buf: User entered shutdown_scope attribute string
+ * @count: Size of buf
+ * @reg: Register number
+ *
+ * Return: count argument if request succeeds, the corresponding
+ * error code otherwise
+ *
+ * Helper function for storing a ggs register value.
+ *
+ * For example, the user-space interface for storing a value to the
+ * ggs0 register:
+ * echo 0xFFFFFFFF 0x1234ABCD > /sys/devices/platform/firmware/ggs0
+ */
+static ssize_t ggs_store(struct device *dev,
+			 struct device_attribute *attr,
+			 const char *buf,
+			 size_t count,
+			 u32 reg)
+{
+	if (!dev || !attr || !buf || !count || reg >= GSS_NUM_REGS)
+		return -EINVAL;
+
+	return write_register(buf, count, IOCTL_WRITE_GGS, reg);
+}
+
+/* GGS register show functions */
+#define GGS0_SHOW(N) \
+	ssize_t ggs##N##_show(struct device *dev, \
+			 struct device_attribute *attr, \
+			 char *buf) \
+	{ \
+		return ggs_show(dev, attr, buf, N); \
+	}
+
+static GGS0_SHOW(0);
+static GGS0_SHOW(1);
+static GGS0_SHOW(2);
+static GGS0_SHOW(3);
+
+/* GGS register store function */
+#define GGS0_STORE(N) \
+	ssize_t ggs##N##_store(struct device *dev, \
+				   struct device_attribute *attr, \
+				   const char *buf, \
+				   size_t count) \
+	{ \
+		return ggs_store(dev, attr, buf, count, N); \
+	}
+
+static GGS0_STORE(0);
+static GGS0_STORE(1);
+static GGS0_STORE(2);
+static GGS0_STORE(3);
+
+/* GGS register device attributes */
+static DEVICE_ATTR_RW(ggs0);
+static DEVICE_ATTR_RW(ggs1);
+static DEVICE_ATTR_RW(ggs2);
+static DEVICE_ATTR_RW(ggs3);
+
+#define CREATE_GGS_DEVICE(dev, N) \
+do { \
+	if (device_create_file(dev, &dev_attr_ggs##N)) \
+		dev_err(dev, "unable to create ggs%d attribute\n", N); \
+} while (0)
+
+/**
+ * pggs_show - Show persistent global general storage (pggs) sysfs attribute
+ * @dev: Device structure
+ * @attr: Device attribute structure
+ * @buf: Requested available shutdown_scope attributes string
+ * @reg: Register number
+ *
+ * Return:Number of bytes printed into the buffer.
+ *
+ * Helper function for viewing a pggs register value.
+ */
+static ssize_t pggs_show(struct device *dev,
+			 struct device_attribute *attr,
+			 char *buf,
+			 u32 reg)
+{
+	return read_register(buf, IOCTL_READ_GGS, reg);
+}
+
+/**
+ * pggs_store - Store persistent global general storage (pggs) sysfs attribute
+ * @dev: Device structure
+ * @attr: Device attribute structure
+ * @buf: User entered shutdown_scope attribute string
+ * @count: Size of buf
+ * @reg: Register number
+ *
+ * Return: count argument if request succeeds, the corresponding
+ * error code otherwise
+ *
+ * Helper function for storing a pggs register value.
+ */
+static ssize_t pggs_store(struct device *dev,
+			  struct device_attribute *attr,
+			  const char *buf,
+			  size_t count,
+			  u32 reg)
+{
+	return write_register(buf, count, IOCTL_WRITE_PGGS, reg);
+}
+
+#define PGGS0_SHOW(N) \
+	ssize_t pggs##N##_show(struct device *dev, \
+			 struct device_attribute *attr, \
+			 char *buf) \
+	{ \
+		return pggs_show(dev, attr, buf, N); \
+	}
+
+/* PGGS register show functions */
+static PGGS0_SHOW(0);
+static PGGS0_SHOW(1);
+static PGGS0_SHOW(2);
+static PGGS0_SHOW(3);
+
+#define PGGS0_STORE(N) \
+	ssize_t pggs##N##_store(struct device *dev, \
+				   struct device_attribute *attr, \
+				   const char *buf, \
+				   size_t count) \
+	{ \
+		return pggs_store(dev, attr, buf, count, N); \
+	}
+
+/* PGGS register store functions */
+static PGGS0_STORE(0);
+static PGGS0_STORE(1);
+static PGGS0_STORE(2);
+static PGGS0_STORE(3);
+
+/* PGGS register device attributes */
+static DEVICE_ATTR_RW(pggs0);
+static DEVICE_ATTR_RW(pggs1);
+static DEVICE_ATTR_RW(pggs2);
+static DEVICE_ATTR_RW(pggs3);
+
+#define CREATE_PGGS_DEVICE(dev, N) \
+do { \
+	if (device_create_file(dev, &dev_attr_pggs##N)) \
+		dev_err(dev, "unable to create pggs%d attribute\n", N); \
+} while (0)
+
+void zynqmp_pm_ggs_init(struct device *dev)
+{
+	/* Create Global General Storage register. */
+	CREATE_GGS_DEVICE(dev, 0);
+	CREATE_GGS_DEVICE(dev, 1);
+	CREATE_GGS_DEVICE(dev, 2);
+	CREATE_GGS_DEVICE(dev, 3);
+
+	/* Create Persistent Global General Storage register. */
+	CREATE_PGGS_DEVICE(dev, 0);
+	CREATE_PGGS_DEVICE(dev, 1);
+	CREATE_PGGS_DEVICE(dev, 2);
+	CREATE_PGGS_DEVICE(dev, 3);
+}
diff --git a/drivers/firmware/xilinx/zynqmp/firmware.c b/drivers/firmware/xilinx/zynqmp/firmware.c
new file mode 100644
index 0000000..edce5eb
--- /dev/null
+++ b/drivers/firmware/xilinx/zynqmp/firmware.c
@@ -0,0 +1,1024 @@
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ *  Copyright (C) 2014-2017 Xilinx, Inc.
+ *
+ *  Michal Simek <michal.simek@xilinx.com>
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#include <linux/compiler.h>
+#include <linux/arm-smccc.h>
+#include <linux/of.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/uaccess.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/platform_device.h>
+
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+#include <linux/firmware/xilinx/zynqmp/firmware-debug.h>
+
+#define DRIVER_NAME	"zynqmp_firmware"
+
+/**
+ * zynqmp_pm_ret_code - Convert PMU-FW error codes to Linux error codes
+ * @ret_status:		PMUFW return code
+ *
+ * Return:		corresponding Linux error code
+ */
+int zynqmp_pm_ret_code(u32 ret_status)
+{
+	switch (ret_status) {
+	case XST_PM_SUCCESS:
+	case XST_PM_DOUBLE_REQ:
+		return 0;
+	case XST_PM_NO_ACCESS:
+		return -EACCES;
+	case XST_PM_ABORT_SUSPEND:
+		return -ECANCELED;
+	case XST_PM_INTERNAL:
+	case XST_PM_CONFLICT:
+	case XST_PM_INVALID_NODE:
+	default:
+		return -EINVAL;
+	}
+}
+
+static noinline int do_fw_call_fail(u64 arg0, u64 arg1, u64 arg2,
+				    u32 *ret_payload)
+{
+	return -ENODEV;
+}
+
+/*
+ * PM function call wrapper
+ * Invoke do_fw_call_smc or do_fw_call_hvc, depending on the configuration
+ */
+static int (*do_fw_call)(u64, u64, u64, u32 *ret_payload) = do_fw_call_fail;
+
+/**
+ * do_fw_call_smc - Call system-level power management layer (SMC)
+ * @arg0:		Argument 0 to SMC call
+ * @arg1:		Argument 1 to SMC call
+ * @arg2:		Argument 2 to SMC call
+ * @ret_payload:	Returned value array
+ *
+ * Return:		Returns status, either success or error+reason
+ *
+ * Invoke power management function via SMC call (no hypervisor present)
+ */
+static noinline int do_fw_call_smc(u64 arg0, u64 arg1, u64 arg2,
+				   u32 *ret_payload)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_smc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
+
+	if (ret_payload) {
+		ret_payload[0] = lower_32_bits(res.a0);
+		ret_payload[1] = upper_32_bits(res.a0);
+		ret_payload[2] = lower_32_bits(res.a1);
+		ret_payload[3] = upper_32_bits(res.a1);
+		ret_payload[4] = lower_32_bits(res.a2);
+	}
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
+}
+
+/**
+ * do_fw_call_hvc - Call system-level power management layer (HVC)
+ * @arg0:		Argument 0 to HVC call
+ * @arg1:		Argument 1 to HVC call
+ * @arg2:		Argument 2 to HVC call
+ * @ret_payload:	Returned value array
+ *
+ * Return:		Returns status, either success or error+reason
+ *
+ * Invoke power management function via HVC
+ * HVC-based for communication through hypervisor
+ * (no direct communication with ATF)
+ */
+static noinline int do_fw_call_hvc(u64 arg0, u64 arg1, u64 arg2,
+				   u32 *ret_payload)
+{
+	struct arm_smccc_res res;
+
+	arm_smccc_hvc(arg0, arg1, arg2, 0, 0, 0, 0, 0, &res);
+
+	if (ret_payload) {
+		ret_payload[0] = lower_32_bits(res.a0);
+		ret_payload[1] = upper_32_bits(res.a0);
+		ret_payload[2] = lower_32_bits(res.a1);
+		ret_payload[3] = upper_32_bits(res.a1);
+		ret_payload[4] = lower_32_bits(res.a2);
+	}
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)res.a0);
+}
+
+/**
+ * invoke_pm_fn - Invoke the system-level power management layer caller
+ *			function depending on the configuration
+ * @pm_api_id:         Requested PM-API call
+ * @arg0:              Argument 0 to requested PM-API call
+ * @arg1:              Argument 1 to requested PM-API call
+ * @arg2:              Argument 2 to requested PM-API call
+ * @arg3:              Argument 3 to requested PM-API call
+ * @ret_payload:       Returned value array
+ *
+ * Return:             Returns status, either success or error+reason
+ *
+ * Invoke power management function for SMC or HVC call, depending on
+ * configuration
+ * Following SMC Calling Convention (SMCCC) for SMC64:
+ * Pm Function Identifier,
+ * PM_SIP_SVC + PM_API_ID =
+ *     ((SMC_TYPE_FAST << FUNCID_TYPE_SHIFT)
+ *     ((SMC_64) << FUNCID_CC_SHIFT)
+ *     ((SIP_START) << FUNCID_OEN_SHIFT)
+ *     ((PM_API_ID) & FUNCID_NUM_MASK))
+ *
+ * PM_SIP_SVC  - Registered ZynqMP SIP Service Call
+ * PM_API_ID   - Power Management API ID
+ */
+int invoke_pm_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3,
+		 u32 *ret_payload)
+{
+	/*
+	 * Added SIP service call Function Identifier
+	 * Make sure to stay in x0 register
+	 */
+	u64 smc_arg[4];
+
+	smc_arg[0] = PM_SIP_SVC | pm_api_id;
+	smc_arg[1] = ((u64)arg1 << 32) | arg0;
+	smc_arg[2] = ((u64)arg3 << 32) | arg2;
+
+	return do_fw_call(smc_arg[0], smc_arg[1], smc_arg[2], ret_payload);
+}
+
+static u32 pm_api_version;
+
+/**
+ * zynqmp_pm_get_api_version - Get version number of PMU PM firmware
+ * @version:	Returned version value
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_get_api_version(u32 *version)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!version)
+		return zynqmp_pm_ret_code(XST_PM_CONFLICT);
+
+	/* Check is PM API version already verified */
+	if (pm_api_version > 0) {
+		*version = pm_api_version;
+		return XST_PM_SUCCESS;
+	}
+	invoke_pm_fn(GET_API_VERSION, 0, 0, 0, 0, ret_payload);
+	*version = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_get_chipid - Get silicon ID registers
+ * @idcode:	IDCODE register
+ * @version:	version register
+ *
+ * Return:	Returns the status of the operation and the idcode and version
+ *		registers in @idcode and @version.
+ */
+static int zynqmp_pm_get_chipid(u32 *idcode, u32 *version)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!idcode || !version)
+		return -EINVAL;
+
+	invoke_pm_fn(GET_CHIPID, 0, 0, 0, 0, ret_payload);
+	*idcode = ret_payload[1];
+	*version = ret_payload[2];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * get_set_conduit_method - Choose SMC or HVC based communication
+ * @np:	Pointer to the device_node structure
+ *
+ * Use SMC or HVC-based functions to communicate with EL2/EL3
+ */
+static int get_set_conduit_method(struct device_node *np)
+{
+	const char *method;
+
+	if (of_property_read_string(np, "method", &method)) {
+		pr_warn("%s missing \"method\" property\n", __func__);
+		return -ENXIO;
+	}
+
+	if (!strcmp("hvc", method)) {
+		do_fw_call = do_fw_call_hvc;
+	} else if (!strcmp("smc", method)) {
+		do_fw_call = do_fw_call_smc;
+	} else {
+		pr_warn("%s Invalid \"method\" property: %s\n",
+			__func__, method);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+/**
+ * zynqmp_pm_reset_assert - Request setting of reset (1 - assert, 0 - release)
+ * @reset:		Reset to be configured
+ * @assert_flag:	Flag stating should reset be asserted (1) or
+ *			released (0)
+ *
+ * Return:		Returns status, either success or error+reason
+ */
+static int zynqmp_pm_reset_assert(const enum zynqmp_pm_reset reset,
+			   const enum zynqmp_pm_reset_action assert_flag)
+{
+	return invoke_pm_fn(RESET_ASSERT, reset, assert_flag, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_reset_get_status - Get status of the reset
+ * @reset:	Reset whose status should be returned
+ * @status:	Returned status
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_reset_get_status(const enum zynqmp_pm_reset reset,
+				      u32 *status)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!status)
+		return zynqmp_pm_ret_code(XST_PM_CONFLICT);
+
+	invoke_pm_fn(RESET_GET_STATUS, reset, 0, 0, 0, ret_payload);
+	*status = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_fpga_load - Perform the fpga load
+ * @address:    Address to write to
+ * @size:       pl bitstream size
+ * @flags:
+ *	BIT(0) - Bit-stream type.
+ *		 0 - Full Bit-stream.
+ *		 1 - Partial Bit-stream.
+ *	BIT(1) - Authentication.
+ *		 1 - Enable.
+ *		 0 - Disable.
+ *	BIT(2) - Encryption.
+ *		 1 - Enable.
+ *		 0 - Disable.
+ * NOTE -
+ *	The current implementation supports only Full Bit-stream.
+ *
+ * This function provides access to xilfpga library to transfer
+ * the required bitstream into PL.
+ *
+ * Return:      Returns status, either success or error+reason
+ */
+static int zynqmp_pm_fpga_load(const u64 address, const u32 size,
+			       const u32 flags)
+{
+	return invoke_pm_fn(FPGA_LOAD, (u32)address,
+			    ((u32)(address >> 32)), size, flags, NULL);
+}
+
+/**
+ * zynqmp_pm_fpga_get_status - Read value from PCAP status register
+ * @value:      Value to read
+ *
+ *This function provides access to the xilfpga library to get
+ *the PCAP status
+ *
+ * Return:      Returns status, either success or error+reason
+ */
+static int zynqmp_pm_fpga_get_status(u32 *value)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!value)
+		return -EINVAL;
+
+	invoke_pm_fn(FPGA_GET_STATUS, 0, 0, 0, 0, ret_payload);
+	*value = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_request_suspend - PM call to request for another PU or subsystem to
+ *					be suspended gracefully.
+ * @node:	Node ID of the targeted PU or subsystem
+ * @ack:	Flag to specify whether acknowledge is requested
+ * @latency:	Requested wakeup latency (not supported)
+ * @state:	Requested state (not supported)
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_request_suspend(const u32 node,
+			      const enum zynqmp_pm_request_ack ack,
+			      const u32 latency,
+			      const u32 state)
+{
+	return invoke_pm_fn(REQUEST_SUSPEND, node, ack,
+			    latency, state, NULL);
+}
+
+/**
+ * zynqmp_pm_force_powerdown - PM call to request for another PU or subsystem to
+ *				be powered down forcefully
+ * @target:	Node ID of the targeted PU or subsystem
+ * @ack:	Flag to specify whether acknowledge is requested
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_force_powerdown(const u32 target,
+				     const enum zynqmp_pm_request_ack ack)
+{
+	return invoke_pm_fn(FORCE_POWERDOWN, target, ack, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_request_wakeup - PM call to wake up selected master or subsystem
+ * @node:	Node ID of the master or subsystem
+ * @set_addr:	Specifies whether the address argument is relevant
+ * @address:	Address from which to resume when woken up
+ * @ack:	Flag to specify whether acknowledge requested
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_request_wakeup(const u32 node,
+				    const bool set_addr,
+				    const u64 address,
+				    const enum zynqmp_pm_request_ack ack)
+{
+	/* set_addr flag is encoded into 1st bit of address */
+	return invoke_pm_fn(REQUEST_WAKEUP, node, address | set_addr,
+			    address >> 32, ack, NULL);
+}
+
+/**
+ * zynqmp_pm_set_wakeup_source - PM call to specify the wakeup source
+ *					while suspended
+ * @target:	Node ID of the targeted PU or subsystem
+ * @wakeup_node:Node ID of the wakeup peripheral
+ * @enable:	Enable or disable the specified peripheral as wake source
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_wakeup_source(const u32 target,
+				       const u32 wakeup_node,
+				       const u32 enable)
+{
+	return invoke_pm_fn(SET_WAKEUP_SOURCE, target,
+			    wakeup_node, enable, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_system_shutdown - PM call to request a system shutdown or restart
+ * @type:	Shutdown or restart? 0 for shutdown, 1 for restart
+ * @subtype:	Specifies which system should be restarted or shut down
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_system_shutdown(const u32 type, const u32 subtype)
+{
+	return invoke_pm_fn(SYSTEM_SHUTDOWN, type, subtype, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_request_node - PM call to request a node with specific capabilities
+ * @node:		Node ID of the slave
+ * @capabilities:	Requested capabilities of the slave
+ * @qos:		Quality of service (not supported)
+ * @ack:		Flag to specify whether acknowledge is requested
+ *
+ * Return:		Returns status, either success or error+reason
+ */
+static int zynqmp_pm_request_node(const u32 node, const u32 capabilities,
+				  const u32 qos,
+				  const enum zynqmp_pm_request_ack ack)
+{
+	return invoke_pm_fn(REQUEST_NODE, node, capabilities,
+			    qos, ack, NULL);
+}
+
+/**
+ * zynqmp_pm_release_node - PM call to release a node
+ * @node:	Node ID of the slave
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_release_node(const u32 node)
+{
+	return invoke_pm_fn(RELEASE_NODE, node, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_set_requirement - PM call to set requirement for PM slaves
+ * @node:		Node ID of the slave
+ * @capabilities:	Requested capabilities of the slave
+ * @qos:		Quality of service (not supported)
+ * @ack:		Flag to specify whether acknowledge is requested
+ *
+ * This API function is to be used for slaves a PU already has requested
+ *
+ * Return:		Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_requirement(const u32 node, const u32 capabilities,
+				     const u32 qos,
+				     const enum zynqmp_pm_request_ack ack)
+{
+	return invoke_pm_fn(SET_REQUIREMENT, node, capabilities,
+			    qos, ack, NULL);
+}
+
+/**
+ * zynqmp_pm_set_max_latency - PM call to set wakeup latency requirements
+ * @node:	Node ID of the slave
+ * @latency:	Requested maximum wakeup latency
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_max_latency(const u32 node, const u32 latency)
+{
+	return invoke_pm_fn(SET_MAX_LATENCY, node, latency, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_set_configuration - PM call to set system configuration
+ * @physical_addr:	Physical 32-bit address of data structure in memory
+ *
+ * Return:		Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_configuration(const u32 physical_addr)
+{
+	return invoke_pm_fn(SET_CONFIGURATION, physical_addr, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_get_node_status - PM call to request a node's current power state
+ * @node:		ID of the component or sub-system in question
+ * @status:		Current operating state of the requested node
+ * @requirements:	Current requirements asserted on the node,
+ *			used for slave nodes only.
+ * @usage:		Usage information, used for slave nodes only:
+ *			0 - No master is currently using the node
+ *			1 - Only requesting master is currently using the node
+ *			2 - Only other masters are currently using the node
+ *			3 - Both the current and at least one other master
+ *			is currently using the node
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_get_node_status(const u32 node, u32 *const status,
+				     u32 *const requirements, u32 *const usage)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!status)
+		return -EINVAL;
+
+	invoke_pm_fn(GET_NODE_STATUS, node, 0, 0, 0, ret_payload);
+	if (ret_payload[0] == XST_PM_SUCCESS) {
+		*status = ret_payload[1];
+		if (requirements)
+			*requirements = ret_payload[2];
+		if (usage)
+			*usage = ret_payload[3];
+	}
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_get_operating_characteristic - PM call to request operating
+ *						characteristic information
+ * @node:	Node ID of the slave
+ * @type:	Type of the operating characteristic requested
+ * @result:	Used to return the requsted operating characteristic
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_get_operating_characteristic(const u32 node,
+						const enum zynqmp_pm_opchar_type
+						type, u32 *const result)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!result)
+		return -EINVAL;
+
+	invoke_pm_fn(GET_OPERATING_CHARACTERISTIC,
+		     node, type, 0, 0, ret_payload);
+	if (ret_payload[0] == XST_PM_SUCCESS)
+		*result = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_init_finalize - PM call to informi firmware that the caller master
+ *				has initialized its own power management
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_init_finalize(void)
+{
+	return invoke_pm_fn(PM_INIT_FINALIZE, 0, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_get_callback_data - Get callback data from firmware
+ * @buf:	Buffer to store payload data
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_get_callback_data(u32 *buf)
+{
+	return invoke_pm_fn(GET_CALLBACK_DATA, 0, 0, 0, 0, buf);
+}
+
+/**
+ * zynqmp_pm_set_suspend_mode	- Set system suspend mode
+ *
+ * @mode:	Mode to set for system suspend
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_set_suspend_mode(u32 mode)
+{
+	return invoke_pm_fn(SET_SUSPEND_MODE, mode, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_sha_hash - Access the SHA engine to calculate the hash
+ * @address:	Address of the data/ Address of output buffer where
+ *		hash should be stored.
+ * @size:	Size of the data.
+ * @flags:
+ *	BIT(0) - Sha3 init (Here address and size inputs can be NULL)
+ *	BIT(1) - Sha3 update (address should holds the )
+ *	BIT(2) - Sha3 final (address should hold the address of
+ *		 buffer to store hash)
+ *
+ * Return:	Returns status, either success or error code.
+ */
+static int zynqmp_pm_sha_hash(const u64 address, const u32 size,
+			      const u32 flags)
+{
+	u32 lower_32_bits = (u32)address;
+	u32 upper_32_bits = (u32)(address >> 32);
+
+	return invoke_pm_fn(SECURE_SHA, upper_32_bits, lower_32_bits,
+			    size, flags, NULL);
+}
+
+/**
+ * zynqmp_pm_rsa - Access RSA hardware to encrypt/decrypt the data with RSA.
+ * @address:	Address of the data
+ * @size:	Size of the data.
+ * @flags:
+ *		BIT(0) - Encryption/Decryption
+ *			 0 - RSA decryption with private key
+ *			 1 - RSA encryption with public key.
+ *
+ * Return:	Returns status, either success or error code.
+ */
+static int zynqmp_pm_rsa(const u64 address, const u32 size, const u32 flags)
+{
+	u32 lower_32_bits = (u32)address;
+	u32 upper_32_bits = (u32)(address >> 32);
+
+	return invoke_pm_fn(SECURE_RSA, upper_32_bits, lower_32_bits,
+			    size, flags, NULL);
+}
+
+/**
+ * zynqmp_pm_pinctrl_request - Request Pin from firmware
+ * @pin:	Pin number to request
+ *
+ * This function requests pin from firmware.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_pinctrl_request(const u32 pin)
+{
+	return invoke_pm_fn(PINCTRL_REQUEST, pin, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_pinctrl_release - Inform firmware that Pin control is released
+ * @pin:	Pin number to release
+ *
+ * This function release pin from firmware.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_pinctrl_release(const u32 pin)
+{
+	return invoke_pm_fn(PINCTRL_RELEASE, pin, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_pinctrl_get_function() - Read function id set for the given pin
+ * @pin:	Pin number
+ * @node:	Buffer to store node ID matching current function
+ *
+ * This function provides the function currently set for the given pin.
+ *
+ * Return:	Returns status, either success or error+reason
+ */
+static int zynqmp_pm_pinctrl_get_function(const u32 pin, u32 *node)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!node)
+		return -EINVAL;
+
+	invoke_pm_fn(PINCTRL_GET_FUNCTION, pin, 0, 0, 0, ret_payload);
+	*node = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_pinctrl_set_function - Set requested function for the pin
+ * @pin:	Pin number
+ * @node:	Node ID mapped with the requested function
+ *
+ * This function sets requested function for the given pin.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_pinctrl_set_function(const u32 pin, const u32 node)
+{
+	return invoke_pm_fn(PINCTRL_SET_FUNCTION, pin, node, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_pinctrl_get_config - Get configuration parameter for the pin
+ * @pin:	Pin number
+ * @param:	Parameter to get
+ * @value:	Buffer to store parameter value
+ *
+ * This function gets requested configuration parameter for the given pin.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_pinctrl_get_config(const u32 pin, const u32 param,
+					u32 *value)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	if (!value)
+		return -EINVAL;
+
+	invoke_pm_fn(PINCTRL_CONFIG_PARAM_GET, pin,
+		     param, 0, 0, ret_payload);
+	*value = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_pinctrl_set_config - Set configuration parameter for the pin
+ * @pin:	Pin number
+ * @param:	Parameter to set
+ * @value:	Parameter value to set
+ *
+ * This function sets requested configuration parameter for the given pin.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_pinctrl_set_config(const u32 pin, const u32 param,
+					u32 value)
+{
+	return invoke_pm_fn(PINCTRL_CONFIG_PARAM_SET, pin,
+			    param, value, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_ioctl - PM IOCTL API for device control and configs
+ * @node_id:	Node ID of the device
+ * @ioctl_id:	ID of the requested IOCTL
+ * @arg1:	Argument 1 to requested IOCTL call
+ * @arg2:	Argument 2 to requested IOCTL call
+ * @out:	Returned output value
+ *
+ * This function calls IOCTL to firmware for device control and configuration.
+ */
+static int zynqmp_pm_ioctl(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2,
+			   u32 *out)
+{
+	return invoke_pm_fn(IOCTL, node_id, ioctl_id, arg1, arg2, out);
+}
+
+static int zynqmp_pm_query_data(struct zynqmp_pm_query_data qdata, u32 *out)
+{
+	return invoke_pm_fn(QUERY_DATA, qdata.qid, qdata.arg1,
+			    qdata.arg2, qdata.arg3, out);
+}
+
+/**
+ * zynqmp_pm_clock_enable - Enable the clock for given id
+ * @clock_id:	ID of the clock to be enabled
+ *
+ * This function is used by master to enable the clock
+ * including peripherals and PLL clocks.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_enable(u32 clock_id)
+{
+	return invoke_pm_fn(CLOCK_ENABLE, clock_id, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_clock_disable - Disable the clock for given id
+ * @clock_id:	ID of the clock to be disable
+ *
+ * This function is used by master to disable the clock
+ * including peripherals and PLL clocks.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_disable(u32 clock_id)
+{
+	return invoke_pm_fn(CLOCK_DISABLE, clock_id, 0, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_clock_getstate - Get the clock state for given id
+ * @clock_id:	ID of the clock to be queried
+ * @state:	1/0 (Enabled/Disabled)
+ *
+ * This function is used by master to get the state of clock
+ * including peripherals and PLL clocks.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_getstate(u32 clock_id, u32 *state)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	invoke_pm_fn(CLOCK_GETSTATE, clock_id, 0, 0, 0, ret_payload);
+	*state = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_setdivider - Set the clock divider for given id
+ * @clock_id:	ID of the clock
+ * @div_type:	TYPE_DIV1: div1
+ *		TYPE_DIV2: div2
+ * @divider:	divider value.
+ *
+ * This function is used by master to set divider for any clock
+ * to achieve desired rate.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_setdivider(u32 clock_id, u32 divider)
+{
+	return invoke_pm_fn(CLOCK_SETDIVIDER, clock_id, divider, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_clock_getdivider - Get the clock divider for given id
+ * @clock_id:	ID of the clock
+ * @div_type:	TYPE_DIV1: div1
+ *		TYPE_DIV2: div2
+ * @divider:	divider value.
+ *
+ * This function is used by master to get divider values
+ * for any clock.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_getdivider(u32 clock_id, u32 *divider)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	invoke_pm_fn(CLOCK_GETDIVIDER, clock_id, 0, 0, 0, ret_payload);
+	*divider = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_setrate - Set the clock rate for given id
+ * @clock_id:	ID of the clock
+ * @rate:	rate value in hz
+ *
+ * This function is used by master to set rate for any clock.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_setrate(u32 clock_id, u32 rate)
+{
+	return invoke_pm_fn(CLOCK_SETRATE, clock_id, rate, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_clock_getrate - Get the clock rate for given id
+ * @clock_id:	ID of the clock
+ * @rate:	rate value in hz
+ *
+ * This function is used by master to get rate
+ * for any clock.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_getrate(u32 clock_id, u32 *rate)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	invoke_pm_fn(CLOCK_GETRATE, clock_id, 0, 0, 0, ret_payload);
+	*rate = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+/**
+ * zynqmp_pm_clock_setparent - Set the clock parent for given id
+ * @clock_id:	ID of the clock
+ * @parent_id:	parent id
+ *
+ * This function is used by master to set parent for any clock.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_setparent(u32 clock_id, u32 parent_id)
+{
+	return invoke_pm_fn(CLOCK_SETPARENT, clock_id, parent_id, 0, 0, NULL);
+}
+
+/**
+ * zynqmp_pm_clock_getparent - Get the clock parent for given id
+ * @clock_id:	ID of the clock
+ * @parent_id:	parent id
+ *
+ * This function is used by master to get parent index
+ * for any clock.
+ *
+ * Return:	Returns status, either success or error+reason.
+ */
+static int zynqmp_pm_clock_getparent(u32 clock_id, u32 *parent_id)
+{
+	u32 ret_payload[PAYLOAD_ARG_CNT];
+
+	invoke_pm_fn(CLOCK_GETPARENT, clock_id, 0, 0, 0, ret_payload);
+	*parent_id = ret_payload[1];
+
+	return zynqmp_pm_ret_code((enum pm_ret_status)ret_payload[0]);
+}
+
+static const struct zynqmp_eemi_ops eemi_ops  = {
+	.get_api_version = zynqmp_pm_get_api_version,
+	.get_chipid = zynqmp_pm_get_chipid,
+	.reset_assert = zynqmp_pm_reset_assert,
+	.reset_get_status = zynqmp_pm_reset_get_status,
+	.fpga_load = zynqmp_pm_fpga_load,
+	.fpga_get_status = zynqmp_pm_fpga_get_status,
+	.sha_hash = zynqmp_pm_sha_hash,
+	.rsa = zynqmp_pm_rsa,
+	.request_suspend = zynqmp_pm_request_suspend,
+	.force_powerdown = zynqmp_pm_force_powerdown,
+	.request_wakeup = zynqmp_pm_request_wakeup,
+	.set_wakeup_source = zynqmp_pm_set_wakeup_source,
+	.system_shutdown = zynqmp_pm_system_shutdown,
+	.request_node = zynqmp_pm_request_node,
+	.release_node = zynqmp_pm_release_node,
+	.set_requirement = zynqmp_pm_set_requirement,
+	.set_max_latency = zynqmp_pm_set_max_latency,
+	.set_configuration = zynqmp_pm_set_configuration,
+	.get_node_status = zynqmp_pm_get_node_status,
+	.get_operating_characteristic = zynqmp_pm_get_operating_characteristic,
+	.init_finalize = zynqmp_pm_init_finalize,
+	.get_callback_data = zynqmp_pm_get_callback_data,
+	.set_suspend_mode = zynqmp_pm_set_suspend_mode,
+	.ioctl = zynqmp_pm_ioctl,
+	.query_data = zynqmp_pm_query_data,
+	.pinctrl_request = zynqmp_pm_pinctrl_request,
+	.pinctrl_release = zynqmp_pm_pinctrl_release,
+	.pinctrl_get_function = zynqmp_pm_pinctrl_get_function,
+	.pinctrl_set_function = zynqmp_pm_pinctrl_set_function,
+	.pinctrl_get_config = zynqmp_pm_pinctrl_get_config,
+	.pinctrl_set_config = zynqmp_pm_pinctrl_set_config,
+	.clock_enable = zynqmp_pm_clock_enable,
+	.clock_disable = zynqmp_pm_clock_disable,
+	.clock_getstate = zynqmp_pm_clock_getstate,
+	.clock_setdivider = zynqmp_pm_clock_setdivider,
+	.clock_getdivider = zynqmp_pm_clock_getdivider,
+	.clock_setrate = zynqmp_pm_clock_setrate,
+	.clock_getrate = zynqmp_pm_clock_getrate,
+	.clock_setparent = zynqmp_pm_clock_setparent,
+	.clock_getparent = zynqmp_pm_clock_getparent,
+};
+
+/**
+ * get_eemi_ops	- Get eemi ops functions
+ *
+ * Return:	- pointer of eemi_ops structure
+ */
+const struct zynqmp_eemi_ops *get_eemi_ops(void)
+{
+	return &eemi_ops;
+}
+EXPORT_SYMBOL_GPL(get_eemi_ops);
+
+static int __init zynqmp_plat_init(void)
+{
+	struct device_node *np;
+	int ret = 0;
+
+	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp");
+	if (!np)
+		return 0;
+	of_node_put(np);
+
+	/* We're running on a ZynqMP machine, the PM node is mandatory. */
+	np = of_find_compatible_node(NULL, NULL, "xlnx,zynqmp-firmware");
+	if (!np) {
+		pr_warn("%s: pm node not found\n", __func__);
+		return -ENXIO;
+	}
+
+	ret = get_set_conduit_method(np);
+	if (ret) {
+		of_node_put(np);
+		return ret;
+	}
+
+	/* Check PM API version number */
+	zynqmp_pm_get_api_version(&pm_api_version);
+	if (pm_api_version != ZYNQMP_PM_VERSION) {
+		panic("%s power management API version error. Expected: v%d.%d - Found: v%d.%d\n",
+		      __func__,
+		      ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR,
+		      pm_api_version >> 16, pm_api_version & 0xffff);
+	}
+
+	pr_info("%s Power management API v%d.%d\n", __func__,
+		ZYNQMP_PM_VERSION_MAJOR, ZYNQMP_PM_VERSION_MINOR);
+
+	of_node_put(np);
+
+	return ret;
+}
+
+static const struct of_device_id firmware_of_match[] = {
+	{ .compatible = "xlnx,zynqmp-firmware", },
+	{ /* end of table */ },
+};
+
+MODULE_DEVICE_TABLE(of, firmware_of_match);
+
+static int zynqmp_firmware_probe(struct platform_device *pdev)
+{
+	int ret;
+
+	ret = zynqmp_pm_api_debugfs_init();
+	if (ret) {
+		pr_err("%s() debugfs init fail with error %d\n", __func__, ret);
+		return ret;
+	}
+
+	zynqmp_pm_ggs_init(&pdev->dev);
+
+	return ret;
+}
+
+static struct platform_driver zynqmp_firmware_platform_driver = {
+	.probe   = zynqmp_firmware_probe,
+	.driver  = {
+			.name             = DRIVER_NAME,
+			.of_match_table   = firmware_of_match,
+		   },
+};
+builtin_platform_driver(zynqmp_firmware_platform_driver);
+
+early_initcall(zynqmp_plat_init);
diff --git a/include/linux/firmware/xilinx/zynqmp/firmware-debug.h b/include/linux/firmware/xilinx/zynqmp/firmware-debug.h
new file mode 100644
index 0000000..a388621
--- /dev/null
+++ b/include/linux/firmware/xilinx/zynqmp/firmware-debug.h
@@ -0,0 +1,32 @@
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ *  Copyright (C) 2014-2017 Xilinx
+ *
+ *  Michal Simek <michal.simek@xilinx.com>
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __SOC_ZYNQMP_FIRMWARE_DEBUG_H__
+#define __SOC_ZYNQMP_FIRMWARE_DEBUG_H__
+
+#include <linux/firmware/xilinx/zynqmp/firmware.h>
+
+int zynqmp_pm_self_suspend(const u32 node,
+			   const u32 latency,
+			   const u32 state);
+int zynqmp_pm_abort_suspend(const enum zynqmp_pm_abort_reason reason);
+int zynqmp_pm_register_notifier(const u32 node, const u32 event,
+				const u32 wake, const u32 enable);
+
+#if IS_REACHABLE(CONFIG_ZYNQMP_FIRMWARE_DEBUG)
+int zynqmp_pm_api_debugfs_init(void);
+#else
+static inline int zynqmp_pm_api_debugfs_init(void) { return 0; }
+#endif
+
+#endif /* __SOC_ZYNQMP_FIRMWARE_DEBUG_H__ */
diff --git a/include/linux/firmware/xilinx/zynqmp/firmware.h b/include/linux/firmware/xilinx/zynqmp/firmware.h
new file mode 100644
index 0000000..2088b15
--- /dev/null
+++ b/include/linux/firmware/xilinx/zynqmp/firmware.h
@@ -0,0 +1,573 @@
+/*
+ * Xilinx Zynq MPSoC Firmware layer
+ *
+ *  Copyright (C) 2014-2017 Xilinx
+ *
+ *  Michal Simek <michal.simek@xilinx.com>
+ *  Davorin Mista <davorin.mista@aggios.com>
+ *  Jolly Shah <jollys@xilinx.com>
+ *  Rajan Vaja <rajanv@xilinx.com>
+ *
+ * SPDX-License-Identifier:	GPL-2.0+
+ */
+
+#ifndef __SOC_ZYNQMP_FIRMWARE_H__
+#define __SOC_ZYNQMP_FIRMWARE_H__
+
+#include <linux/device.h>
+
+#define ZYNQMP_PM_VERSION_MAJOR	1
+#define ZYNQMP_PM_VERSION_MINOR	0
+
+#define ZYNQMP_PM_VERSION	((ZYNQMP_PM_VERSION_MAJOR << 16) | \
+					ZYNQMP_PM_VERSION_MINOR)
+
+#define ZYNQMP_PM_MAX_LATENCY	(~0U)
+#define ZYNQMP_PM_MAX_QOS	100U
+
+/* SMC SIP service Call Function Identifier Prefix */
+#define PM_SIP_SVC	0xC2000000
+#define GET_CALLBACK_DATA 0xa01
+#define SET_SUSPEND_MODE  0xa02
+
+/* Number of 32bits values in payload */
+#define PAYLOAD_ARG_CNT	5U
+
+/* Number of arguments for a callback */
+#define CB_ARG_CNT	4
+
+/* Payload size (consists of callback API ID + arguments) */
+#define CB_PAYLOAD_SIZE	(CB_ARG_CNT + 1)
+
+/* Global general storage register base address */
+#define GGS_BASEADDR	(0xFFD80030U)
+#define GSS_NUM_REGS	(4)
+
+/* Persistent global general storage register base address */
+#define PGGS_BASEADDR	(0xFFD80050U)
+#define PGSS_NUM_REGS	(4)
+
+/* Capabilities for RAM */
+#define	ZYNQMP_PM_CAPABILITY_ACCESS	0x1U
+#define	ZYNQMP_PM_CAPABILITY_CONTEXT	0x2U
+#define	ZYNQMP_PM_CAPABILITY_WAKEUP	0x4U
+#define	ZYNQMP_PM_CAPABILITY_POWER	0x8U
+
+/* Clock APIs payload parameters */
+#define CLK_GET_NAME_RESP_LEN				16
+#define CLK_GET_TOPOLOGY_RESP_WORDS			3
+#define CLK_GET_FIXEDFACTOR_RESP_WORDS			2
+#define CLK_GET_PARENTS_RESP_WORDS			3
+#define CLK_GET_ATTR_RESP_WORDS				1
+
+enum pm_api_id {
+	/* Miscellaneous API functions: */
+	GET_API_VERSION = 1,
+	SET_CONFIGURATION,
+	GET_NODE_STATUS,
+	GET_OPERATING_CHARACTERISTIC,
+	REGISTER_NOTIFIER,
+	/* API for suspending of PUs: */
+	REQUEST_SUSPEND,
+	SELF_SUSPEND,
+	FORCE_POWERDOWN,
+	ABORT_SUSPEND,
+	REQUEST_WAKEUP,
+	SET_WAKEUP_SOURCE,
+	SYSTEM_SHUTDOWN,
+	/* API for managing PM slaves: */
+	REQUEST_NODE,
+	RELEASE_NODE,
+	SET_REQUIREMENT,
+	SET_MAX_LATENCY,
+	/* Direct control API functions: */
+	RESET_ASSERT,
+	RESET_GET_STATUS,
+	MMIO_WRITE,
+	MMIO_READ,
+	PM_INIT_FINALIZE,
+	FPGA_LOAD,
+	FPGA_GET_STATUS,
+	GET_CHIPID,
+	/* ID 25 is been used by U-boot to process secure boot images */
+	/* Secure library generic API functions */
+	SECURE_SHA = 26,
+	SECURE_RSA,
+	/* Pin control API functions */
+	PINCTRL_REQUEST,
+	PINCTRL_RELEASE,
+	PINCTRL_GET_FUNCTION,
+	PINCTRL_SET_FUNCTION,
+	PINCTRL_CONFIG_PARAM_GET,
+	PINCTRL_CONFIG_PARAM_SET,
+	/* PM IOCTL API */
+	IOCTL,
+	/* API to query information from firmware */
+	QUERY_DATA,
+	/* Clock control API functions */
+	CLOCK_ENABLE,
+	CLOCK_DISABLE,
+	CLOCK_GETSTATE,
+	CLOCK_SETDIVIDER,
+	CLOCK_GETDIVIDER,
+	CLOCK_SETRATE,
+	CLOCK_GETRATE,
+	CLOCK_SETPARENT,
+	CLOCK_GETPARENT,
+};
+
+/* PMU-FW return status codes */
+enum pm_ret_status {
+	XST_PM_SUCCESS = 0,
+	XST_PM_INTERNAL	= 2000,
+	XST_PM_CONFLICT,
+	XST_PM_NO_ACCESS,
+	XST_PM_INVALID_NODE,
+	XST_PM_DOUBLE_REQ,
+	XST_PM_ABORT_SUSPEND,
+};
+
+enum zynqmp_pm_reset_action {
+	PM_RESET_ACTION_RELEASE,
+	PM_RESET_ACTION_ASSERT,
+	PM_RESET_ACTION_PULSE,
+};
+
+enum zynqmp_pm_reset {
+	ZYNQMP_PM_RESET_START = 999,
+	ZYNQMP_PM_RESET_PCIE_CFG,
+	ZYNQMP_PM_RESET_PCIE_BRIDGE,
+	ZYNQMP_PM_RESET_PCIE_CTRL,
+	ZYNQMP_PM_RESET_DP,
+	ZYNQMP_PM_RESET_SWDT_CRF,
+	ZYNQMP_PM_RESET_AFI_FM5,
+	ZYNQMP_PM_RESET_AFI_FM4,
+	ZYNQMP_PM_RESET_AFI_FM3,
+	ZYNQMP_PM_RESET_AFI_FM2,
+	ZYNQMP_PM_RESET_AFI_FM1,
+	ZYNQMP_PM_RESET_AFI_FM0,
+	ZYNQMP_PM_RESET_GDMA,
+	ZYNQMP_PM_RESET_GPU_PP1,
+	ZYNQMP_PM_RESET_GPU_PP0,
+	ZYNQMP_PM_RESET_GPU,
+	ZYNQMP_PM_RESET_GT,
+	ZYNQMP_PM_RESET_SATA,
+	ZYNQMP_PM_RESET_ACPU3_PWRON,
+	ZYNQMP_PM_RESET_ACPU2_PWRON,
+	ZYNQMP_PM_RESET_ACPU1_PWRON,
+	ZYNQMP_PM_RESET_ACPU0_PWRON,
+	ZYNQMP_PM_RESET_APU_L2,
+	ZYNQMP_PM_RESET_ACPU3,
+	ZYNQMP_PM_RESET_ACPU2,
+	ZYNQMP_PM_RESET_ACPU1,
+	ZYNQMP_PM_RESET_ACPU0,
+	ZYNQMP_PM_RESET_DDR,
+	ZYNQMP_PM_RESET_APM_FPD,
+	ZYNQMP_PM_RESET_SOFT,
+	ZYNQMP_PM_RESET_GEM0,
+	ZYNQMP_PM_RESET_GEM1,
+	ZYNQMP_PM_RESET_GEM2,
+	ZYNQMP_PM_RESET_GEM3,
+	ZYNQMP_PM_RESET_QSPI,
+	ZYNQMP_PM_RESET_UART0,
+	ZYNQMP_PM_RESET_UART1,
+	ZYNQMP_PM_RESET_SPI0,
+	ZYNQMP_PM_RESET_SPI1,
+	ZYNQMP_PM_RESET_SDIO0,
+	ZYNQMP_PM_RESET_SDIO1,
+	ZYNQMP_PM_RESET_CAN0,
+	ZYNQMP_PM_RESET_CAN1,
+	ZYNQMP_PM_RESET_I2C0,
+	ZYNQMP_PM_RESET_I2C1,
+	ZYNQMP_PM_RESET_TTC0,
+	ZYNQMP_PM_RESET_TTC1,
+	ZYNQMP_PM_RESET_TTC2,
+	ZYNQMP_PM_RESET_TTC3,
+	ZYNQMP_PM_RESET_SWDT_CRL,
+	ZYNQMP_PM_RESET_NAND,
+	ZYNQMP_PM_RESET_ADMA,
+	ZYNQMP_PM_RESET_GPIO,
+	ZYNQMP_PM_RESET_IOU_CC,
+	ZYNQMP_PM_RESET_TIMESTAMP,
+	ZYNQMP_PM_RESET_RPU_R50,
+	ZYNQMP_PM_RESET_RPU_R51,
+	ZYNQMP_PM_RESET_RPU_AMBA,
+	ZYNQMP_PM_RESET_OCM,
+	ZYNQMP_PM_RESET_RPU_PGE,
+	ZYNQMP_PM_RESET_USB0_CORERESET,
+	ZYNQMP_PM_RESET_USB1_CORERESET,
+	ZYNQMP_PM_RESET_USB0_HIBERRESET,
+	ZYNQMP_PM_RESET_USB1_HIBERRESET,
+	ZYNQMP_PM_RESET_USB0_APB,
+	ZYNQMP_PM_RESET_USB1_APB,
+	ZYNQMP_PM_RESET_IPI,
+	ZYNQMP_PM_RESET_APM_LPD,
+	ZYNQMP_PM_RESET_RTC,
+	ZYNQMP_PM_RESET_SYSMON,
+	ZYNQMP_PM_RESET_AFI_FM6,
+	ZYNQMP_PM_RESET_LPD_SWDT,
+	ZYNQMP_PM_RESET_FPD,
+	ZYNQMP_PM_RESET_RPU_DBG1,
+	ZYNQMP_PM_RESET_RPU_DBG0,
+	ZYNQMP_PM_RESET_DBG_LPD,
+	ZYNQMP_PM_RESET_DBG_FPD,
+	ZYNQMP_PM_RESET_APLL,
+	ZYNQMP_PM_RESET_DPLL,
+	ZYNQMP_PM_RESET_VPLL,
+	ZYNQMP_PM_RESET_IOPLL,
+	ZYNQMP_PM_RESET_RPLL,
+	ZYNQMP_PM_RESET_GPO3_PL_0,
+	ZYNQMP_PM_RESET_GPO3_PL_1,
+	ZYNQMP_PM_RESET_GPO3_PL_2,
+	ZYNQMP_PM_RESET_GPO3_PL_3,
+	ZYNQMP_PM_RESET_GPO3_PL_4,
+	ZYNQMP_PM_RESET_GPO3_PL_5,
+	ZYNQMP_PM_RESET_GPO3_PL_6,
+	ZYNQMP_PM_RESET_GPO3_PL_7,
+	ZYNQMP_PM_RESET_GPO3_PL_8,
+	ZYNQMP_PM_RESET_GPO3_PL_9,
+	ZYNQMP_PM_RESET_GPO3_PL_10,
+	ZYNQMP_PM_RESET_GPO3_PL_11,
+	ZYNQMP_PM_RESET_GPO3_PL_12,
+	ZYNQMP_PM_RESET_GPO3_PL_13,
+	ZYNQMP_PM_RESET_GPO3_PL_14,
+	ZYNQMP_PM_RESET_GPO3_PL_15,
+	ZYNQMP_PM_RESET_GPO3_PL_16,
+	ZYNQMP_PM_RESET_GPO3_PL_17,
+	ZYNQMP_PM_RESET_GPO3_PL_18,
+	ZYNQMP_PM_RESET_GPO3_PL_19,
+	ZYNQMP_PM_RESET_GPO3_PL_20,
+	ZYNQMP_PM_RESET_GPO3_PL_21,
+	ZYNQMP_PM_RESET_GPO3_PL_22,
+	ZYNQMP_PM_RESET_GPO3_PL_23,
+	ZYNQMP_PM_RESET_GPO3_PL_24,
+	ZYNQMP_PM_RESET_GPO3_PL_25,
+	ZYNQMP_PM_RESET_GPO3_PL_26,
+	ZYNQMP_PM_RESET_GPO3_PL_27,
+	ZYNQMP_PM_RESET_GPO3_PL_28,
+	ZYNQMP_PM_RESET_GPO3_PL_29,
+	ZYNQMP_PM_RESET_GPO3_PL_30,
+	ZYNQMP_PM_RESET_GPO3_PL_31,
+	ZYNQMP_PM_RESET_RPU_LS,
+	ZYNQMP_PM_RESET_PS_ONLY,
+	ZYNQMP_PM_RESET_PL,
+	ZYNQMP_PM_RESET_END
+};
+
+enum zynqmp_pm_request_ack {
+	ZYNQMP_PM_REQUEST_ACK_NO = 1,
+	ZYNQMP_PM_REQUEST_ACK_BLOCKING,
+	ZYNQMP_PM_REQUEST_ACK_NON_BLOCKING,
+};
+
+enum zynqmp_pm_abort_reason {
+	ZYNQMP_PM_ABORT_REASON_WAKEUP_EVENT = 100,
+	ZYNQMP_PM_ABORT_REASON_POWER_UNIT_BUSY,
+	ZYNQMP_PM_ABORT_REASON_NO_POWERDOWN,
+	ZYNQMP_PM_ABORT_REASON_UNKNOWN,
+};
+
+enum zynqmp_pm_suspend_reason {
+	ZYNQMP_PM_SUSPEND_REASON_POWER_UNIT_REQUEST = 201,
+	ZYNQMP_PM_SUSPEND_REASON_ALERT,
+	ZYNQMP_PM_SUSPEND_REASON_SYSTEM_SHUTDOWN,
+};
+
+enum zynqmp_pm_ram_state {
+	ZYNQMP_PM_RAM_STATE_OFF = 1,
+	ZYNQMP_PM_RAM_STATE_RETENTION,
+	ZYNQMP_PM_RAM_STATE_ON,
+};
+
+enum zynqmp_pm_opchar_type {
+	ZYNQMP_PM_OPERATING_CHARACTERISTIC_POWER = 1,
+	ZYNQMP_PM_OPERATING_CHARACTERISTIC_ENERGY,
+	ZYNQMP_PM_OPERATING_CHARACTERISTIC_TEMPERATURE,
+};
+
+enum pm_node_id {
+	NODE_UNKNOWN = 0,
+	NODE_APU,
+	NODE_APU_0,
+	NODE_APU_1,
+	NODE_APU_2,
+	NODE_APU_3,
+	NODE_RPU,
+	NODE_RPU_0,
+	NODE_RPU_1,
+	NODE_PLD,
+	NODE_FPD,
+	NODE_OCM_BANK_0,
+	NODE_OCM_BANK_1,
+	NODE_OCM_BANK_2,
+	NODE_OCM_BANK_3,
+	NODE_TCM_0_A,
+	NODE_TCM_0_B,
+	NODE_TCM_1_A,
+	NODE_TCM_1_B,
+	NODE_L2,
+	NODE_GPU_PP_0,
+	NODE_GPU_PP_1,
+	NODE_USB_0,
+	NODE_USB_1,
+	NODE_TTC_0,
+	NODE_TTC_1,
+	NODE_TTC_2,
+	NODE_TTC_3,
+	NODE_SATA,
+	NODE_ETH_0,
+	NODE_ETH_1,
+	NODE_ETH_2,
+	NODE_ETH_3,
+	NODE_UART_0,
+	NODE_UART_1,
+	NODE_SPI_0,
+	NODE_SPI_1,
+	NODE_I2C_0,
+	NODE_I2C_1,
+	NODE_SD_0,
+	NODE_SD_1,
+	NODE_DP,
+	NODE_GDMA,
+	NODE_ADMA,
+	NODE_NAND,
+	NODE_QSPI,
+	NODE_GPIO,
+	NODE_CAN_0,
+	NODE_CAN_1,
+	NODE_EXTERN,
+	NODE_APLL,
+	NODE_VPLL,
+	NODE_DPLL,
+	NODE_RPLL,
+	NODE_IOPLL,
+	NODE_DDR,
+	NODE_IPI_APU,
+	NODE_IPI_RPU_0,
+	NODE_GPU,
+	NODE_PCIE,
+	NODE_PCAP,
+	NODE_RTC,
+	NODE_LPD,
+	NODE_VCU,
+	NODE_IPI_RPU_1,
+	NODE_IPI_PL_0,
+	NODE_IPI_PL_1,
+	NODE_IPI_PL_2,
+	NODE_IPI_PL_3,
+	NODE_PL,
+	NODE_GEM_TSU,
+	NODE_SWDT_0,
+	NODE_SWDT_1,
+	NODE_CSU,
+	NODE_PJTAG,
+	NODE_TRACE,
+	NODE_TESTSCAN,
+	NODE_PMU,
+	NODE_MAX,
+};
+
+enum pm_pinctrl_config_param {
+	PM_PINCTRL_CONFIG_SLEW_RATE,
+	PM_PINCTRL_CONFIG_BIAS_STATUS,
+	PM_PINCTRL_CONFIG_PULL_CTRL,
+	PM_PINCTRL_CONFIG_SCHMITT_CMOS,
+	PM_PINCTRL_CONFIG_DRIVE_STRENGTH,
+	PM_PINCTRL_CONFIG_VOLTAGE_STATUS,
+	PM_PINCTRL_CONFIG_MAX,
+};
+
+enum pm_pinctrl_slew_rate {
+	PM_PINCTRL_SLEW_RATE_FAST,
+	PM_PINCTRL_SLEW_RATE_SLOW,
+};
+
+enum pm_pinctrl_bias_status {
+	PM_PINCTRL_BIAS_DISABLE,
+	PM_PINCTRL_BIAS_ENABLE,
+};
+
+enum pm_pinctrl_pull_ctrl {
+	PM_PINCTRL_BIAS_PULL_DOWN,
+	PM_PINCTRL_BIAS_PULL_UP,
+};
+
+enum pm_pinctrl_schmitt_cmos {
+	PM_PINCTRL_INPUT_TYPE_CMOS,
+	PM_PINCTRL_INPUT_TYPE_SCHMITT,
+};
+
+enum pm_pinctrl_drive_strength {
+	PM_PINCTRL_DRIVE_STRENGTH_2MA,
+	PM_PINCTRL_DRIVE_STRENGTH_4MA,
+	PM_PINCTRL_DRIVE_STRENGTH_8MA,
+	PM_PINCTRL_DRIVE_STRENGTH_12MA,
+};
+
+enum pm_ioctl_id {
+	IOCTL_GET_RPU_OPER_MODE,
+	IOCTL_SET_RPU_OPER_MODE,
+	IOCTL_RPU_BOOT_ADDR_CONFIG,
+	IOCTL_TCM_COMB_CONFIG,
+	IOCTL_SET_TAPDELAY_BYPASS,
+	IOCTL_SET_SGMII_MODE,
+	IOCTL_SD_DLL_RESET,
+	IOCTL_SET_SD_TAPDELAY,
+	/* Ioctl for clock driver */
+	IOCTL_SET_PLL_FRAC_MODE,
+	IOCTL_GET_PLL_FRAC_MODE,
+	IOCTL_SET_PLL_FRAC_DATA,
+	IOCTL_GET_PLL_FRAC_DATA,
+	IOCTL_WRITE_GGS,
+	IOCTL_READ_GGS,
+	IOCTL_WRITE_PGGS,
+	IOCTL_READ_PGGS,
+};
+
+enum rpu_oper_mode {
+	PM_RPU_MODE_LOCKSTEP,
+	PM_RPU_MODE_SPLIT,
+};
+
+enum rpu_boot_mem {
+	PM_RPU_BOOTMEM_LOVEC,
+	PM_RPU_BOOTMEM_HIVEC,
+};
+
+enum rpu_tcm_comb {
+	PM_RPU_TCM_SPLIT,
+	PM_RPU_TCM_COMB,
+};
+
+enum tap_delay_signal_type {
+	PM_TAPDELAY_NAND_DQS_IN,
+	PM_TAPDELAY_NAND_DQS_OUT,
+	PM_TAPDELAY_QSPI,
+	PM_TAPDELAY_MAX,
+};
+
+enum tap_delay_bypass_ctrl {
+	PM_TAPDELAY_BYPASS_DISABLE,
+	PM_TAPDELAY_BYPASS_ENABLE,
+};
+
+enum sgmii_mode {
+	PM_SGMII_DISABLE,
+	PM_SGMII_ENABLE,
+};
+
+enum tap_delay_type {
+	PM_TAPDELAY_INPUT,
+	PM_TAPDELAY_OUTPUT,
+};
+
+enum dll_reset_type {
+	PM_DLL_RESET_ASSERT,
+	PM_DLL_RESET_RELEASE,
+	PM_DLL_RESET_PULSE,
+};
+
+enum topology_type {
+	TYPE_INVALID,
+	TYPE_MUX,
+	TYPE_PLL,
+	TYPE_FIXEDFACTOR,
+	TYPE_DIV1,
+	TYPE_DIV2,
+	TYPE_GATE,
+};
+
+enum pm_query_id {
+	PM_QID_INVALID,
+	PM_QID_CLOCK_GET_NAME,
+	PM_QID_CLOCK_GET_TOPOLOGY,
+	PM_QID_CLOCK_GET_FIXEDFACTOR_PARAMS,
+	PM_QID_CLOCK_GET_PARENTS,
+	PM_QID_CLOCK_GET_ATTRIBUTES,
+};
+
+struct zynqmp_pm_query_data {
+	u32 qid;
+	u32 arg1;
+	u32 arg2;
+	u32 arg3;
+};
+
+struct zynqmp_eemi_ops {
+	int (*get_api_version)(u32 *version);
+	int (*get_chipid)(u32 *idcode, u32 *version);
+	int (*reset_assert)(const enum zynqmp_pm_reset reset,
+			    const enum zynqmp_pm_reset_action assert_flag);
+	int (*reset_get_status)(const enum zynqmp_pm_reset reset, u32 *status);
+	int (*fpga_load)(const u64 address, const u32 size, const u32 flags);
+	int (*fpga_get_status)(u32 *value);
+	int (*sha_hash)(const u64 address, const u32 size, const u32 flags);
+	int (*rsa)(const u64 address, const u32 size, const u32 flags);
+	int (*request_suspend)(const u32 node,
+			       const enum zynqmp_pm_request_ack ack,
+			       const u32 latency,
+			       const u32 state);
+	int (*force_powerdown)(const u32 target,
+			       const enum zynqmp_pm_request_ack ack);
+	int (*request_wakeup)(const u32 node,
+			      const bool set_addr,
+			      const u64 address,
+			      const enum zynqmp_pm_request_ack ack);
+	int (*set_wakeup_source)(const u32 target,
+				 const u32 wakeup_node,
+				 const u32 enable);
+	int (*system_shutdown)(const u32 type, const u32 subtype);
+	int (*request_node)(const u32 node,
+			    const u32 capabilities,
+			    const u32 qos,
+			    const enum zynqmp_pm_request_ack ack);
+	int (*release_node)(const u32 node);
+	int (*set_requirement)(const u32 node,
+			       const u32 capabilities,
+			       const u32 qos,
+			       const enum zynqmp_pm_request_ack ack);
+	int (*set_max_latency)(const u32 node, const u32 latency);
+	int (*set_configuration)(const u32 physical_addr);
+	int (*get_node_status)(const u32 node, u32 *const status,
+			       u32 *const requirements, u32 *const usage);
+	int (*get_operating_characteristic)(const u32 node,
+					    const enum zynqmp_pm_opchar_type
+					    type, u32 *const result);
+	int (*init_finalize)(void);
+	int (*get_callback_data)(u32 *buf);
+	int (*set_suspend_mode)(u32 mode);
+	int (*ioctl)(u32 node_id, u32 ioctl_id, u32 arg1, u32 arg2, u32 *out);
+	int (*query_data)(struct zynqmp_pm_query_data qdata, u32 *out);
+	int (*pinctrl_request)(const u32 pin);
+	int (*pinctrl_release)(const u32 pin);
+	int (*pinctrl_get_function)(const u32 pin, u32 *node);
+	int (*pinctrl_set_function)(const u32 pin, const u32 node);
+	int (*pinctrl_get_config)(const u32 pin, const u32 param, u32 *value);
+	int (*pinctrl_set_config)(const u32 pin, const u32 param, u32 value);
+	int (*clock_enable)(u32 clock_id);
+	int (*clock_disable)(u32 clock_id);
+	int (*clock_getstate)(u32 clock_id, u32 *state);
+	int (*clock_setdivider)(u32 clock_id, u32 divider);
+	int (*clock_getdivider)(u32 clock_id, u32 *divider);
+	int (*clock_setrate)(u32 clock_id, u32 rate);
+	int (*clock_getrate)(u32 clock_id, u32 *rate);
+	int (*clock_setparent)(u32 clock_id, u32 parent_id);
+	int (*clock_getparent)(u32 clock_id, u32 *parent_id);
+};
+
+/*
+ * Internal functions
+ */
+int invoke_pm_fn(u32 pm_api_id, u32 arg0, u32 arg1, u32 arg2, u32 arg3,
+		 u32 *ret_payload);
+int zynqmp_pm_ret_code(u32 ret_status);
+
+void zynqmp_pm_ggs_init(struct device *dev);
+
+#if IS_REACHABLE(CONFIG_ARCH_ZYNQMP)
+const struct zynqmp_eemi_ops *get_eemi_ops(void);
+#else
+static inline struct zynqmp_eemi_ops *get_eemi_ops(void) { return NULL; }
+#endif
+
+#endif /* __SOC_ZYNQMP_FIRMWARE_H__ */
-- 
2.7.4

^ permalink raw reply related

* [RFC 5/5] pwm: pwm-omap-dmtimer: Add capture functionality
From: Ladislav Michl @ 2018-01-08 22:06 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108215931.GV3875@atomide.com>

On Mon, Jan 08, 2018 at 01:59:31PM -0800, Tony Lindgren wrote:
> * Ladislav Michl <ladis@linux-mips.org> [180108 15:46]:
> > Here it seems hardware can capture both edges, but I do not see a way
> > how to tell it I want start from either low to high or high to low
> > transition. Clues?
> 
> At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you
> probably know that already..
> 
> If you're having hard time getting things starting, maybe something
> like this helps:
> 
> stop timer in TCLR register
> configure timer in TCLR
> write some value to TLDR, maybe 0?
> set ST bit in TCLR to start

Let me clarify it a bit more. I have no problem starting timer and capture
events. I just didn't find a way how to tell hardware I want to start
with for example rising edge, so rising edge goes to TCAR1 and failing edge
to TCAR2. Substracting those gives pulse width.

	ladis

^ permalink raw reply

* [PATCH v3 7/7] EDAC: Add driver for the Marvell Armada XP SDRAM and L2 cache ECC
From: Chris Packham @ 2018-01-08 22:00 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20171110090308.21562-8-jlu@pengutronix.de>

Hi Jan,

On 10/11/17 22:03, Jan Luebbe wrote:
> Add support for the ECC functionality as found in the DDR RAM and L2
> cache controllers on the MV78230/MV78x60 SoCs. This driver has been
> tested on the MV78460 (on a custom board with a DDR3 ECC DIMM).
> 
> Signed-off-by: Jan Luebbe <jlu@pengutronix.de>

What is the current state of this? I see there were some comments from 
Borislav that need addressing.

I've got my patches for Armada-380 and 98dx3236 ready. They're fairly 
minor so I might send them anyway just to get any feedback. They 
probably could be incorporated into a v4 series if you are willing to 
carry them.

^ permalink raw reply

* [RFC 5/5] pwm: pwm-omap-dmtimer: Add capture functionality
From: Tony Lindgren @ 2018-01-08 21:59 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108154336.GE4077@lenoch>

* Ladislav Michl <ladis@linux-mips.org> [180108 15:46]:
> Here it seems hardware can capture both edges, but I do not see a way
> how to tell it I want start from either low to high or high to low
> transition. Clues?

At least dm3730 TRM documents TCM bits [9:8] for TCLR, but you
probably know that already..

If you're having hard time getting things starting, maybe something
like this helps:

stop timer in TCLR register
configure timer in TCLR
write some value to TLDR, maybe 0?
set ST bit in TCLR to start

Regards,

Tony

^ permalink raw reply

* [PATCH 0/5] Add capture functionality to OMAP pwm driver
From: Ladislav Michl @ 2018-01-08 21:57 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108215123.GU3875@atomide.com>

On Mon, Jan 08, 2018 at 01:51:23PM -0800, Tony Lindgren wrote:
> * Ladislav Michl <ladis@linux-mips.org> [180108 15:42]:
> > Tony, I wanted to do something easy to understand as a base for
> > event capture interrupt, but either I do not understand hardware,
> > or it indeed cannot do pulse capture.
> 
> Hmm OK. Presumably it works as it's been around for a long time,
> I thought any edge trigger will just sample the current counter
> value and that's it..

Yes, indeed, _any_ edge will just sample the current counter value.
So what can hardware say about duty cycle of this signal?
  +-+        +-+
__| |________| |___

Depending time capture was started it will either trigger on first
falling or first rising edge.


	ladis

^ permalink raw reply

* [PATCH 0/5] Add capture functionality to OMAP pwm driver
From: Tony Lindgren @ 2018-01-08 21:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108153926.GA3916@lenoch>

* Ladislav Michl <ladis@linux-mips.org> [180108 15:42]:
> Tony, I wanted to do something easy to understand as a base for
> event capture interrupt, but either I do not understand hardware,
> or it indeed cannot do pulse capture.

Hmm OK. Presumably it works as it's been around for a long time,
I thought any edge trigger will just sample the current counter
value and that's it..

Regards,

Tony

^ permalink raw reply

* [PATCH] arm64: Implement branch predictor hardening for Falkor
From: Shanker Donthineni @ 2018-01-08 21:44 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <01fe1f41-ac45-0c19-a502-77b915c48fcf@codeaurora.org>

Hi Will/Catalin,

Please drop https://git.kernel.org/pub/scm/linux/kernel/git/arm64/linux.git/commit/?h=kpti&id=79ad24ef6c260efa0614896b15e67f4829448e32 in which you've removed FALKOR MIDR change. I've posted
v2 patch series including typo fix & FALKOR MIDR patch which is already available in upstream v4.15-rc7
branch. Please merge v2 patch.

On 01/08/2018 01:10 PM, Shanker Donthineni wrote:
> Hi Will,
> 
> On 01/08/2018 12:44 PM, Will Deacon wrote:
>> On Mon, Jan 08, 2018 at 05:09:33PM +0000, Will Deacon wrote:
>>> On Fri, Jan 05, 2018 at 02:28:59PM -0600, Shanker Donthineni wrote:
>>>> Falkor is susceptible to branch predictor aliasing and can
>>>> theoretically be attacked by malicious code. This patch
>>>> implements a mitigation for these attacks, preventing any
>>>> malicious entries from affecting other victim contexts.
>>>
>>> Thanks, Shanker. I'll pick this up (fixing the typo pointed out by Drew).
>>
>> Note that MIDR_FALKOR doesn't exist in mainline, so I had to drop those
>> changes too. See the kpti branch for details.
>>
> 
> The FALKOR MIDR patch is already available in the upstream kernel v4.15-rc7
>     https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/arch/arm64?h=v4.15-rc7&id=c622cc013cece073722592cff1ac6643a33b1622
> 
> If you want I can resend the above patch in v2 series including typo fix.
> 
>> If you'd like anything else done here, please send additional patches to me
>> and Catalin that we can apply on top of what we currently have. Note that
>> I'm in the air tomorrow, so won't be picking up email.
>>
>> Cheers,
>>
>> Will
>>
>> _______________________________________________
>> linux-arm-kernel mailing list
>> linux-arm-kernel at lists.infradead.org
>> http://lists.infradead.org/mailman/listinfo/linux-arm-kernel
>>
> 

-- 
Shanker Donthineni
Qualcomm Datacenter Technologies, Inc. as an affiliate of Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply

* [PATCH v2 2/2] arm64: Implement branch predictor hardening for Falkor
From: Shanker Donthineni @ 2018-01-08 21:31 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515447068-20977-1-git-send-email-shankerd@codeaurora.org>

Falkor is susceptible to branch predictor aliasing and can
theoretically be attacked by malicious code. This patch
implements a mitigation for these attacks, preventing any
malicious entries from affecting other victim contexts.

Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org>
---
Changes since v1:
  Corrected typo to fix the compilation errors if HARDEN_BRANCH_PREDICTOR=n

This patch requires FALKOR MIDR which is available in upstream v4.15-rc7
  https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/arch/arm64?h=v4.15-rc7&id=c622cc013cece073722592cff1ac6643a33b1622 ans also
  attached this v2 patch series.

 arch/arm64/include/asm/cpucaps.h |  3 ++-
 arch/arm64/include/asm/kvm_asm.h |  2 ++
 arch/arm64/kernel/bpi.S          |  8 +++++++
 arch/arm64/kernel/cpu_errata.c   | 49 ++++++++++++++++++++++++++++++++++++++--
 arch/arm64/kvm/hyp/entry.S       | 12 ++++++++++
 arch/arm64/kvm/hyp/switch.c      | 10 ++++++++
 6 files changed, 81 insertions(+), 3 deletions(-)

diff --git a/arch/arm64/include/asm/cpucaps.h b/arch/arm64/include/asm/cpucaps.h
index 51616e7..7049b48 100644
--- a/arch/arm64/include/asm/cpucaps.h
+++ b/arch/arm64/include/asm/cpucaps.h
@@ -43,7 +43,8 @@
 #define ARM64_SVE				22
 #define ARM64_UNMAP_KERNEL_AT_EL0		23
 #define ARM64_HARDEN_BRANCH_PREDICTOR		24
+#define ARM64_HARDEN_BP_POST_GUEST_EXIT		25
 
-#define ARM64_NCAPS				25
+#define ARM64_NCAPS				26
 
 #endif /* __ASM_CPUCAPS_H */
diff --git a/arch/arm64/include/asm/kvm_asm.h b/arch/arm64/include/asm/kvm_asm.h
index ab4d0a9..24961b7 100644
--- a/arch/arm64/include/asm/kvm_asm.h
+++ b/arch/arm64/include/asm/kvm_asm.h
@@ -68,6 +68,8 @@
 
 extern u32 __init_stage2_translation(void);
 
+extern void __qcom_hyp_sanitize_btac_predictors(void);
+
 #endif
 
 #endif /* __ARM_KVM_ASM_H__ */
diff --git a/arch/arm64/kernel/bpi.S b/arch/arm64/kernel/bpi.S
index 2b10d52..44ffcda 100644
--- a/arch/arm64/kernel/bpi.S
+++ b/arch/arm64/kernel/bpi.S
@@ -77,3 +77,11 @@ ENTRY(__psci_hyp_bp_inval_start)
 	ldp	x2, x3, [sp], #16
 	ldp	x0, x1, [sp], #16
 ENTRY(__psci_hyp_bp_inval_end)
+
+ENTRY(__qcom_hyp_sanitize_link_stack_start)
+	stp     x29, x30, [sp, #-16]!
+	.rept	16
+	bl	. + 4
+	.endr
+	ldp	x29, x30, [sp], #16
+ENTRY(__qcom_hyp_sanitize_link_stack_end)
diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
index cb0fb37..9ee9d2e 100644
--- a/arch/arm64/kernel/cpu_errata.c
+++ b/arch/arm64/kernel/cpu_errata.c
@@ -54,6 +54,8 @@ static int cpu_enable_trap_ctr_access(void *__unused)
 
 #ifdef CONFIG_KVM
 extern char __psci_hyp_bp_inval_start[], __psci_hyp_bp_inval_end[];
+extern char __qcom_hyp_sanitize_link_stack_start[];
+extern char __qcom_hyp_sanitize_link_stack_end[];
 
 static void __copy_hyp_vect_bpi(int slot, const char *hyp_vecs_start,
 				const char *hyp_vecs_end)
@@ -96,8 +98,10 @@ static void __install_bp_hardening_cb(bp_hardening_cb_t fn,
 	spin_unlock(&bp_lock);
 }
 #else
-#define __psci_hyp_bp_inval_start	NULL
-#define __psci_hyp_bp_inval_end		NULL
+#define __psci_hyp_bp_inval_start	   	NULL
+#define __psci_hyp_bp_inval_end			NULL
+#define __qcom_hyp_sanitize_link_stack_start	NULL
+#define __qcom_hyp_sanitize_link_stack_end	NULL
 
 static void __install_bp_hardening_cb(bp_hardening_cb_t fn,
 				      const char *hyp_vecs_start,
@@ -138,6 +142,29 @@ static int enable_psci_bp_hardening(void *data)
 
 	return 0;
 }
+
+static void qcom_link_stack_sanitization(void)
+{
+	u64 tmp;
+
+	asm volatile("mov	%0, x30		\n"
+		     ".rept	16		\n"
+		     "bl	. + 4		\n"
+		     ".endr			\n"
+		     "mov	x30, %0		\n"
+		     : "=&r" (tmp));
+}
+
+static int qcom_enable_link_stack_sanitization(void *data)
+{
+	const struct arm64_cpu_capabilities *entry = data;
+
+	install_bp_hardening_cb(entry, qcom_link_stack_sanitization,
+				__qcom_hyp_sanitize_link_stack_start,
+				__qcom_hyp_sanitize_link_stack_end);
+
+	return 0;
+}
 #endif	/* CONFIG_HARDEN_BRANCH_PREDICTOR */
 
 #define MIDR_RANGE(model, min, max) \
@@ -302,6 +329,24 @@ static int enable_psci_bp_hardening(void *data)
 		MIDR_ALL_VERSIONS(MIDR_CORTEX_A75),
 		.enable = enable_psci_bp_hardening,
 	},
+	{
+		.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+		MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1),
+		.enable = qcom_enable_link_stack_sanitization,
+	},
+	{
+		.capability = ARM64_HARDEN_BRANCH_PREDICTOR,
+		MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR),
+		.enable = qcom_enable_link_stack_sanitization,
+	},
+	{
+		.capability = ARM64_HARDEN_BP_POST_GUEST_EXIT,
+		MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR_V1),
+	},
+	{
+		.capability = ARM64_HARDEN_BP_POST_GUEST_EXIT,
+		MIDR_ALL_VERSIONS(MIDR_QCOM_FALKOR),
+	},
 #endif
 	{
 	}
diff --git a/arch/arm64/kvm/hyp/entry.S b/arch/arm64/kvm/hyp/entry.S
index 12ee62d..9c45c6a 100644
--- a/arch/arm64/kvm/hyp/entry.S
+++ b/arch/arm64/kvm/hyp/entry.S
@@ -196,3 +196,15 @@ alternative_endif
 
 	eret
 ENDPROC(__fpsimd_guest_restore)
+
+ENTRY(__qcom_hyp_sanitize_btac_predictors)
+	/**
+	 * Call SMC64 with Silicon provider serviceID 23<<8 (0xc2001700)
+	 * 0xC2000000-0xC200FFFF: assigned to SiP Service Calls
+	 * b15-b0: contains SiP functionID
+	 */
+	movz    x0, #0x1700
+	movk    x0, #0xc200, lsl #16
+	smc     #0
+	ret
+ENDPROC(__qcom_hyp_sanitize_btac_predictors)
diff --git a/arch/arm64/kvm/hyp/switch.c b/arch/arm64/kvm/hyp/switch.c
index 4d273f6..7e37379 100644
--- a/arch/arm64/kvm/hyp/switch.c
+++ b/arch/arm64/kvm/hyp/switch.c
@@ -406,6 +406,16 @@ int __hyp_text __kvm_vcpu_run(struct kvm_vcpu *vcpu)
 		/* 0 falls through to be handled out of EL2 */
 	}
 
+	if (cpus_have_const_cap(ARM64_HARDEN_BP_POST_GUEST_EXIT)) {
+		u32 midr = read_cpuid_id();
+
+		/* Apply BTAC predictors mitigation to all Falkor chips */
+		if (((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR) ||
+		    ((midr & MIDR_CPU_MODEL_MASK) == MIDR_QCOM_FALKOR_V1)) {
+			__qcom_hyp_sanitize_btac_predictors();
+		}
+	}
+
 	fp_enabled = __fpsimd_enabled();
 
 	__sysreg_save_guest_state(guest_ctxt);
-- 
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH v2 1/2] arm64: Define cputype macros for Falkor CPU
From: Shanker Donthineni @ 2018-01-08 21:31 UTC (permalink / raw)
  To: linux-arm-kernel

Add cputype definition macros for Qualcomm Datacenter Technologies
Falkor CPU in cputype.h. It's unfortunate that the first revision
of the Falkor CPU used the wrong part number 0x800, got fixed in v2
chip with part number 0xC00, and would be used the same value for
future revisions.

Signed-off-by: Shanker Donthineni <shankerd@codeaurora.org>
Signed-off-by: Will Deacon <will.deacon@arm.com>
---
This patch is availble at https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/arch/arm64?h=v4.15-rc7&id=c622cc013cece073722592cff1ac6643a33b1622

 arch/arm64/include/asm/cputype.h | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/arch/arm64/include/asm/cputype.h b/arch/arm64/include/asm/cputype.h
index 84385b9..424ca71d 100644
--- a/arch/arm64/include/asm/cputype.h
+++ b/arch/arm64/include/asm/cputype.h
@@ -93,6 +93,7 @@
 #define BRCM_CPU_PART_VULCAN		0x516
 
 #define QCOM_CPU_PART_FALKOR_V1		0x800
+#define QCOM_CPU_PART_FALKOR		0xC00
 
 #define MIDR_CORTEX_A53 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A53)
 #define MIDR_CORTEX_A57 MIDR_CPU_MODEL(ARM_CPU_IMP_ARM, ARM_CPU_PART_CORTEX_A57)
@@ -103,6 +104,7 @@
 #define MIDR_THUNDERX_81XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_81XX)
 #define MIDR_THUNDERX_83XX MIDR_CPU_MODEL(ARM_CPU_IMP_CAVIUM, CAVIUM_CPU_PART_THUNDERX_83XX)
 #define MIDR_QCOM_FALKOR_V1 MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR_V1)
+#define MIDR_QCOM_FALKOR MIDR_CPU_MODEL(ARM_CPU_IMP_QCOM, QCOM_CPU_PART_FALKOR)
 
 #ifndef __ASSEMBLY__
 
-- 
Qualcomm Datacenter Technologies, Inc. on behalf of the Qualcomm Technologies, Inc.
Qualcomm Technologies, Inc. is a member of the Code Aurora Forum, a Linux Foundation Collaborative Project.

^ permalink raw reply related

* [PATCH 0/3] ARM branch predictor hardening
From: Marc Zyngier @ 2018-01-08 21:28 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <608da127-fffa-0782-7baf-7edfe189abdc@gmail.com>

On Sat, 06 Jan 2018 18:50:41 +0000,
Florian Fainelli wrote:

Hi Florian,

> Le 01/06/18 ? 04:09, Marc Zyngier a ?crit?:
> > This small series implements some basic BP hardening by invalidating
> > the BTB on CPUs that are known to be susceptible to aliasing attacks.
> > 
> > These patches are closely modelled against what we do on arm64,
> > although simpler as we can rely on an architected instruction to
> > perform the invalidation.
> > 
> > The first patch reuses the Cortex-A8 BTB invalidation in switch_mm and
> > generalises it to be used on all affected CPUs. The second perform the
> > same invalidation on fatal signal delivery. The last one nukes it on
> > guest exit, and results in some major surgery (kudos to Dimitris
> > Papastamos who came up with the magic vector decoding sequence).
> > 
> > Note that that M-class CPUs are not affected and for R-class cores,
> > the mitigation doesn't make much sense since we do not enforce
> > user/kernel isolation.
> 
> Broadcom's Brahma-B15 CPUs are also affected, I can either send an
> incremental patch on top of this series once it lands in, or since it
> looks like you are going to respin a v2, feel free to incorporate the
> changes I sent as replies to patch 1 and 2.

I've re-spun the series, as there was quite a few issues with the
first one. Could you please try and respin your B15 patches on top?

> What about P4JB and Krait, should they also be covered?

I have no idea. I only know of the ARM cores. Other implementation
will have to check whether they are affected or not.

> Even though I am assuming -stable maintainers will quickly pick
> those changes, should there be an explicit mention of CVE-2017-5715?

I have no plans for these patches to be merged immediately. We're
targeting the arm64 patches at v4.16, and I don't expect the 32bit
patches to be any different.

As for the CVE mention, I'm not really bothered (yet another number
soup). Everybody knows what we're talking about, these days...

Thanks,

	M.

^ permalink raw reply

* [PATCH] soc: imx: gpc: de-register power domains only if initialized
From: Stefan Agner @ 2018-01-08 21:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515408692.12538.10.camel@pengutronix.de>

On 2018-01-08 11:51, Lucas Stach wrote:
> Am Montag, den 08.01.2018, 18:28 +0800 schrieb Dong Aisheng:
>> On Sun, Jan 07, 2018 at 02:49:05PM +0100, Stefan Agner wrote:
>> > If power domain information are missing in the device tree, no
>> > power domains get initialized. However, imx_gpc_remove tries to
>> > remove power domains always in the old DT binding case. Only
>> > remove power domains when imx_gpc_probe initialized them in
>> > first place.
>> >
>> > Fixes: 721cabf6c660 ("soc: imx: move PGC handling to a new GPC
>> > driver")
>> > Cc: Lucas Stach <l.stach@pengutronix.de>
>> > Signed-off-by: Stefan Agner <stefan@agner.ch>
>> > ---
>> > ?drivers/soc/imx/gpc.c | 10 +++++++++-
>> > ?1 file changed, 9 insertions(+), 1 deletion(-)
>> >
>> > diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c
>> > index 53f7275d6cbd..62bb724726d9 100644
>> > --- a/drivers/soc/imx/gpc.c
>> > +++ b/drivers/soc/imx/gpc.c
>> > @@ -470,13 +470,21 @@ static int imx_gpc_probe(struct
>> > platform_device *pdev)
>> > ?
>> > ?static int imx_gpc_remove(struct platform_device *pdev)
>> > ?{
>>
>> What's the original purpose of imx_gpc_remove?
>> ARM power domain can't be removed.
> 
> Why? As long as it stays powered on there is not reason why we wouldn't
> be able to remove the driver.
> 

Is it really safe to make assumptions of the hardware state when drivers
get removed? At least some drivers disable the hardware on remove (e.g.
i.MX SPI driver).

>> And why current imx_gpc_remove only remove domains for old DT but not
>> for new ones?
> 
> With the new binding the power domains will be removed by the sub-
> drivers for the domains.
> 
>> How about make it un-removable?
>> e.g.
> 
> I don't see why this would be a good idea. Once more device-dependency
> handling is in place we might need to unbind the power domains when the
> regulator driver for the domain is unbound. Do you intend to make them
> non-removable, too?

I think it would be preferable to keep the ability to remote the driver.

However, I noticed that even with this fix, with device trees which do
use the power domains capabilities (e.g. i.MX6DL) it leads to a stack
trace when using DEBUG_TEST_DRIVER_REMOVE=y, see:
https://marc.info/?l=linux-arm-kernel&m=151544599904423&w=4

--
Stefan

> 
> Regards,
> Lucas
> 
>> diff --git a/drivers/soc/imx/gpc.c b/drivers/soc/imx/gpc.c
>> index 47e7aa9..7fc6737 100644
>> --- a/drivers/soc/imx/gpc.c
>> +++ b/drivers/soc/imx/gpc.c
>> @@ -454,36 +454,17 @@ static int imx_gpc_probe(struct platform_device
>> *pdev)
>> ????????return 0;
>> ?}
>> ?
>> -static int imx_gpc_remove(struct platform_device *pdev)
>> -{
>> -???????int ret;
>> -
>> -???????/*
>> -????????* If the old DT binding is used the toplevel driver needs to
>> -????????* de-register the power domains
>> -????????*/
>> -???????if (!of_get_child_by_name(pdev->dev.of_node, "pgc")) {
>> -???????????????of_genpd_del_provider(pdev->dev.of_node);
>> -
>> -???????????????ret =
>> pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
>> -???????????????if (ret)
>> -???????????????????????return ret;
>> -???????????????imx_pgc_put_clocks(&imx_gpc_domains[GPC_PGC_DOMAIN_PU
>> ]);
>> -
>> -???????????????ret =
>> pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_ARM].base);
>> -???????????????if (ret)
>> -???????????????????????return ret;
>> -???????}
>> -
>> -???????return 0;
>> -}
>> -
>> ?static struct platform_driver imx_gpc_driver = {
>> ????????.driver = {
>> ????????????????.name = "imx-gpc",
>> ????????????????.of_match_table = imx_gpc_dt_ids,
>> +????????????????/*
>> +?????????????????* We can't forcibly eject devices form power
>> domain,
>> +?????????????????* so we can't really remove power domains once they
>> +?????????????????* were added.
>> +?????????????????*/
>> +????????????????.suppress_bind_attrs = true,
>> ????????},
>> ????????.probe = imx_gpc_probe,
>> -???????.remove = imx_gpc_remove,
>> ?};
>> ?builtin_platform_driver(imx_gpc_driver)
>>
>> Regards
>> Dong Aisheng
>>
>> > +	struct device_node *pgc_node;
>> > ?	int ret;
>> > ?
>> > +	pgc_node = of_get_child_by_name(pdev->dev.of_node, "pgc");
>> > +
>> > +	/* bail out if DT too old and doesn't provide the
>> > necessary info */
>> > +	if (!of_property_read_bool(pdev->dev.of_node, "#power-
>> > domain-cells") &&
>> > +	????!pgc_node)
>> > +		return 0;
>> > +
>> > ?	/*
>> > ?	?* If the old DT binding is used the toplevel driver needs
>> > to
>> > ?	?* de-register the power domains
>> > ?	?*/
>> > -	if (!of_get_child_by_name(pdev->dev.of_node, "pgc")) {
>> > +	if (!pgc_node) {
>> > ?		of_genpd_del_provider(pdev->dev.of_node);
>> > ?
>> > ?		ret =
>> > pm_genpd_remove(&imx_gpc_domains[GPC_PGC_DOMAIN_PU].base);
>> > --?
>> > 2.15.1
>> >

^ permalink raw reply

* soc: imx: gpcv2: removing and probing fails
From: Stefan Agner @ 2018-01-08 21:12 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAHQ1cqEutLV7wRWq3FdU1CyercLiiDgNL0trSuV5p-x7eGYFVg@mail.gmail.com>

On 2018-01-08 07:24, Andrey Smirnov wrote:
> On Sun, Jan 7, 2018 at 4:22 PM, Andrey Smirnov <andrew.smirnov@gmail.com> wrote:
>> On Sun, Jan 7, 2018 at 2:48 AM, Stefan Agner <stefan@agner.ch> wrote:
>>> Hi Andrew,
>>>
>>> I noticed that the driver fails when removing and probing again. As far
>>> as I can see due to duplicate add of the platform devices.
>>>
>>> As far as I can tell the driver should register the remove callback and
>>> do a platform_device_unregister on the newly created platform devices.
>>> However, as far as I can tell we don't hold on to a reference to them...
>>> I guess we could keep references in imx_gpcv2_probe, but maybe there is
>>> an easier way?
>>
>> Stefan:
>>
>> Good catch and sorry for the inconvenience. I just spent a little bit
>> of time repro-ing this and it looks like there are two separate bugs,
>> actually. First one, as you correctly pointed out, is due to
>> re-registration of pm-domain platform drivers. That, however, should
>> only result in a WARNING and a failed driver probing, not in a killed
>> init due to BUG. So the second one, that BUG message in the stack
>> trace, is due to the fact that I incorrectly provide statically
>> allocated data via dev.platform_data and it ends up being kfree'd in
>> platform_device_release().
>>
>> IMHO, this driver isn't really meant to be removed, so the simplest
>> solution to the first problem would be to specify
>> "imx_gpc_driver.driver.suppress_bind_attrs = true" and remove any
>> option to remove the driver, but I don't know if that's acceptable or
>> not.
>>
>> Shawn, would the above be acceptable upstream?
>>
>> Solution for bug #2 is trivial and I'll send patches for both once we
>> agree how to fix #1.
>>
>> Thanks,
>> Andrey Smirnov
>>
>> P.S: Also, since I based my code on gpc.c, I suspect that driver will
>> have exactly the same problem (I'll do some experiments to confirm)
> 
> Done with experiments. Same problem happens with gpc.c as well.

Yeah gpc.c also has another problem:
https://patchwork.kernel.org/patch/10148315/

But, yeah, you are right, even with that patch applied and when using
CONFIG_DEBUG_TEST_DRIVER_REMOVE=y it seems to show the problem as well.

I like the config symbol to test my own drivers, its just unfortunate
when mainline blows by default... But then, maybe it is the reason why
that config symbol got a UNSTABLE flag.

The full splash:
[    0.697548] ------------[ cut here ]------------
[    0.702212] WARNING: CPU: 0 PID: 1 at fs/sysfs/dir.c:31
sysfs_warn_dup+0x64/0x74
[    0.709695] sysfs: cannot create duplicate filename
'/devices/soc0/soc/2000000.aips-bus/20dc000.gpc/imx-pgc-power-domain.0'
[    0.720887] Modules linked in:
[    0.723965] CPU: 0 PID: 1 Comm: swapper/0 Not tainted
4.15.0-rc3-00061-g38f99f72e8f3-dirty #246
[    0.732694] Hardware name: Freescale i.MX6 Quad/DualLite (Device
Tree)
[    0.739263] [<8010f61c>] (unwind_backtrace) from [<8010b984>]
(show_stack+0x10/0x14)
[    0.747045] [<8010b984>] (show_stack) from [<80832024>]
(dump_stack+0x88/0x9c)
[    0.754308] [<80832024>] (dump_stack) from [<8011e7ac>]
(__warn+0xdc/0xf4)
[    0.761214] [<8011e7ac>] (__warn) from [<8011e7fc>]
(warn_slowpath_fmt+0x38/0x48)
[    0.768732] [<8011e7fc>] (warn_slowpath_fmt) from [<802768b0>]
(sysfs_warn_dup+0x64/0x74)
[    0.776950] [<802768b0>] (sysfs_warn_dup) from [<80276988>]
(sysfs_create_dir_ns+0x84/0x90)
[    0.785339] [<80276988>] (sysfs_create_dir_ns) from [<80836560>]
(kobject_add_internal+0xb4/0x30c)
[    0.794337] [<80836560>] (kobject_add_internal) from [<80836804>]
(kobject_add+0x4c/0x9c)
[    0.802554] [<80836804>] (kobject_add) from [<8050c030>]
(device_add+0xe0/0x594)
[    0.809991] [<8050c030>] (device_add) from [<805103d8>]
(platform_device_add+0x110/0x224)
[    0.818208] [<805103d8>] (platform_device_add) from [<8049bb94>]
(imx_gpc_probe+0x184/0x380)
[    0.826685] [<8049bb94>] (imx_gpc_probe) from [<805105c4>]
(platform_drv_probe+0x50/0xac)
[    0.834901] [<805105c4>] (platform_drv_probe) from [<8050ec0c>]
(driver_probe_device+0x1b4/0x3c8)
[    0.843810] [<8050ec0c>] (driver_probe_device) from [<8050eec4>]
(__driver_attach+0xa4/0xa8)
[    0.852284] [<8050eec4>] (__driver_attach) from [<8050cfe8>]
(bus_for_each_dev+0x4c/0x9c)
[    0.860495] [<8050cfe8>] (bus_for_each_dev) from [<8050e1a0>]
(bus_add_driver+0x188/0x20c)
[    0.868795] [<8050e1a0>] (bus_add_driver) from [<8050f7b4>]
(driver_register+0x78/0xf4)
[    0.876835] [<8050f7b4>] (driver_register) from [<80101b00>]
(do_one_initcall+0x44/0x168)
[    0.885049] [<80101b00>] (do_one_initcall) from [<80c00db8>]
(kernel_init_freeable+0x14c/0x1d8)
[    0.893789] [<80c00db8>] (kernel_init_freeable) from [<80844538>]
(kernel_init+0x8/0x10c)
[    0.902006] [<80844538>] (kernel_init) from [<80107a28>]
(ret_from_fork+0x14/0x2c)
[    0.909639] ---[ end trace ade27083f156a989 ]---
[    0.914277] ------------[ cut here ]------------
[    0.918934] WARNING: CPU: 0 PID: 1 at lib/kobject.c:240
kobject_add_internal+0x278/0x30c
[    0.927073] kobject_add_internal failed for imx-pgc-power-domain.0
with -EEXIST, don't try to register things with the same name in the
same directory.
[    0.940690] Modules linked in:
[    0.943763] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W       
4.15.0-rc3-00061-g38f99f72e8f3-dirty #246
[    0.953796] Hardware name: Freescale i.MX6 Quad/DualLite (Device
Tree)
[    0.960357] [<8010f61c>] (unwind_backtrace) from [<8010b984>]
(show_stack+0x10/0x14)
[    0.968136] [<8010b984>] (show_stack) from [<80832024>]
(dump_stack+0x88/0x9c)
[    0.975394] [<80832024>] (dump_stack) from [<8011e7ac>]
(__warn+0xdc/0xf4)
[    0.982298] [<8011e7ac>] (__warn) from [<8011e7fc>]
(warn_slowpath_fmt+0x38/0x48)
[    0.989815] [<8011e7fc>] (warn_slowpath_fmt) from [<80836724>]
(kobject_add_internal+0x278/0x30c)
[    0.998725] [<80836724>] (kobject_add_internal) from [<80836804>]
(kobject_add+0x4c/0x9c)
[    1.006938] [<80836804>] (kobject_add) from [<8050c030>]
(device_add+0xe0/0x594)
[    1.014370] [<8050c030>] (device_add) from [<805103d8>]
(platform_device_add+0x110/0x224)
[    1.022583] [<805103d8>] (platform_device_add) from [<8049bb94>]
(imx_gpc_probe+0x184/0x380)
[    1.031057] [<8049bb94>] (imx_gpc_probe) from [<805105c4>]
(platform_drv_probe+0x50/0xac)
[    1.039269] [<805105c4>] (platform_drv_probe) from [<8050ec0c>]
(driver_probe_device+0x1b4/0x3c8)
[    1.048176] [<8050ec0c>] (driver_probe_device) from [<8050eec4>]
(__driver_attach+0xa4/0xa8)
[    1.056647] [<8050eec4>] (__driver_attach) from [<8050cfe8>]
(bus_for_each_dev+0x4c/0x9c)
[    1.064857] [<8050cfe8>] (bus_for_each_dev) from [<8050e1a0>]
(bus_add_driver+0x188/0x20c)
[    1.073153] [<8050e1a0>] (bus_add_driver) from [<8050f7b4>]
(driver_register+0x78/0xf4)
[    1.081191] [<8050f7b4>] (driver_register) from [<80101b00>]
(do_one_initcall+0x44/0x168)
[    1.089404] [<80101b00>] (do_one_initcall) from [<80c00db8>]
(kernel_init_freeable+0x14c/0x1d8)
[    1.098141] [<80c00db8>] (kernel_init_freeable) from [<80844538>]
(kernel_init+0x8/0x10c)
[    1.106355] [<80844538>] (kernel_init) from [<80107a28>]
(ret_from_fork+0x14/0x2c)
[    1.113979] ---[ end trace ade27083f156a98a ]---
[    1.118637] ------------[ cut here ]------------
[    1.123275] Kernel BUG at c46b9f56 [verbose debug info unavailable]
[    1.129564] Internal error: Oops - BUG: 0 [#1] SMP ARM
[    1.134719] Modules linked in:
[    1.137791] CPU: 0 PID: 1 Comm: swapper/0 Tainted: G        W       
4.15.0-rc3-00061-g38f99f72e8f3-dirty #246
[    1.147823] Hardware name: Freescale i.MX6 Quad/DualLite (Device
Tree)
[    1.154375] PC is at kfree+0xfc/0x140
[    1.158054] LR is at platform_device_release+0x10/0x34
[    1.163209] pc : [<801fd744>]    lr : [<805101b0>]    psr: 40000053
[    1.169495] sp : 84057de8  ip : 00000000  fp : 842e9e00
[    1.174736] r10: 842e9800  r9 : 80d23968  r8 : 80aa8a40
[    1.179980] r7 : 80952bac  r6 : 00000000  r5 : 842e9810  r4 :
842e9810
[    1.186529] r3 : 87dd2474  r2 : 87dd2460  r1 : a0000053  r0 :
80d23a20
[    1.193080] Flags: nZcv  IRQs on  FIQs off  Mode SVC_32  ISA ARM 
Segment none
[    1.200328] Control: 10c5387d  Table: 10004059  DAC: 00000051
[    1.206093] Process swapper/0 (pid: 1, stack limit = 0x86b82e40)
[    1.212122] Stack: (0x84057de8 to 0x84058000)
[    1.216499] 7de0:                   842e9810 842e9818 842e9810
842e9810 842e9810 00000000
[    1.224710] 7e00: 80952bac 805101b0 842e9818 80509eac 842e9818
80d2a784 84251480 80835f38
[    1.232919] 7e20: 87da98f4 80952bac 87da9810 ffffffef 87da98f4
8049bd2c 00000000 00000000
[    1.241128] 7e40: 84155a10 00000042 842500c0 00000000 80b410d8
84155a10 fffffffe 80d239d8
[    1.249337] 7e60: fffffdfb 80d239d8 80daefe8 00000000 00000000
805105c4 84155a10 00000000
[    1.257548] 7e80: 80daefe4 00000000 80d239d8 8050ec0c 87da95f0
00000000 000000d7 84155a10
[    1.265756] 7ea0: 80d239d8 84155a44 00000000 000000d7 80c5b83c
80c6c57c 00000000 8050eec4
[    1.273965] 7ec0: 00000000 80d239d8 8050ee20 8050cfe8 84050358
8414f6b4 80d239d8 842b4f00
[    1.282174] 7ee0: 80d2ab60 8050e1a0 80afef10 80c35700 80d239d8
80d239d8 00000000 80c35ecc
[    1.290384] 7f00: 80d58680 8050f7b4 ffffe000 00000000 80c35ecc
80101b00 80b94c44 000000d7
[    1.298595] 7f20: 00000000 8013a458 00000000 80b1dd5c 00000006
00000006 80aadf4c 00000000
[    1.306803] 7f40: 80ab7130 80aadfc0 87fffaee 87fffaf4 00000000
00000007 80d58680 80c5b830
[    1.315012] 7f60: 00000007 80d58680 80c5b834 80d58680 000000d7
80c00db8 00000006 00000006
[    1.323221] 7f80: 00000000 80c005b0 00000000 80844530 00000000
00000000 00000000 00000000
[    1.331430] 7fa0: 00000000 80844538 00000000 80107a28 00000000
00000000 00000000 00000000
[    1.339639] 7fc0: 00000000 00000000 00000000 00000000 00000000
00000000 00000000 00000000
[    1.347848] 7fe0: 00000000 00000000 00000000 00000000 00000013
00000000 3fbfd7ff fed779df
[    1.356064] [<801fd744>] (kfree) from [<805101b0>]
(platform_device_release+0x10/0x34)
[    1.364019] [<805101b0>] (platform_device_release) from [<80509eac>]
(device_release+0x2c/0x90)
[    1.372759] [<80509eac>] (device_release) from [<80835f38>]
(kobject_put+0x94/0xe4)
[    1.380447] [<80835f38>] (kobject_put) from [<8049bd2c>]
(imx_gpc_probe+0x31c/0x380)
[    1.388224] [<8049bd2c>] (imx_gpc_probe) from [<805105c4>]
(platform_drv_probe+0x50/0xac)
[    1.396435] [<805105c4>] (platform_drv_probe) from [<8050ec0c>]
(driver_probe_device+0x1b4/0x3c8)
[    1.405345] [<8050ec0c>] (driver_probe_device) from [<8050eec4>]
(__driver_attach+0xa4/0xa8)
[    1.413815] [<8050eec4>] (__driver_attach) from [<8050cfe8>]
(bus_for_each_dev+0x4c/0x9c)
[    1.422024] [<8050cfe8>] (bus_for_each_dev) from [<8050e1a0>]
(bus_add_driver+0x188/0x20c)
[    1.430320] [<8050e1a0>] (bus_add_driver) from [<8050f7b4>]
(driver_register+0x78/0xf4)
[    1.438357] [<8050f7b4>] (driver_register) from [<80101b00>]
(do_one_initcall+0x44/0x168)
[    1.446568] [<80101b00>] (do_one_initcall) from [<80c00db8>]
(kernel_init_freeable+0x14c/0x1d8)
[    1.455305] [<80c00db8>] (kernel_init_freeable) from [<80844538>]
(kernel_init+0x8/0x10c)
[    1.463518] [<80844538>] (kernel_init) from [<80107a28>]
(ret_from_fork+0x14/0x2c)
[    1.471118] Code: 1a000003 e5923014 e3130001 1a000000 (e7f001f2)
[    1.477236] ---[ end trace ade27083f156a98b ]---
[    1.482097] Kernel panic - not syncing: Attempted to kill init!
exitcode=0x0000000b
[    1.482097]
[    1.491281] ---[ end Kernel panic - not syncing: Attempted to kill
init! exitcode=0x0000000b
[    1.491281]

--
Stefan

^ permalink raw reply


This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox