Linux-ARM-Kernel Archive on lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH V2 2/2] ARM: dts: imx7s: add snvs rtc clock
From: Anson Huang @ 2018-01-09  2:51 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515466276-5541-1-git-send-email-Anson.Huang@nxp.com>

Add i.MX7 SNVS RTC clock.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
changes since v1:
	update snvs lp rtc binding-doc for clock info.
 Documentation/devicetree/bindings/crypto/fsl-sec4.txt | 16 ++++++++++++++++
 arch/arm/boot/dts/imx7s.dtsi                          |  2 ++
 2 files changed, 18 insertions(+)

diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
index 76aec8a..da66ac4 100644
--- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
+++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt
@@ -415,12 +415,26 @@ Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node
 	value type: <u32>
 	Definition: LP register offset. default it is 0x34.
 
+   - clocks
+      Usage: required if SNVS LP RTC requires explicit enablement of clocks
+      Value type: <prop_encoded-array>
+      Definition:  A list of phandle and clock specifier pairs describing
+          the clocks required for enabling and disabling SNVS LP RTC.
+
+   - clock-names
+      Usage: required if SNVS LP RTC requires explicit enablement of clocks
+      Value type: <string>
+      Definition: A list of clock name strings in the same order as the
+          clocks property.
+
 EXAMPLE
 	sec_mon_rtc_lp at 1 {
 		compatible = "fsl,sec-v4.0-mon-rtc-lp";
 		interrupts = <93 2>;
 		regmap = <&snvs>;
 		offset = <0x34>;
+		clocks = <&clks IMX7D_SNVS_CLK>;
+		clock-names = "snvs-rtc";
 	};
 
 =====================================================================
@@ -543,6 +557,8 @@ FULL EXAMPLE
 			regmap = <&sec_mon>;
 			offset = <0x34>;
 			interrupts = <93 2>;
+			clocks = <&clks IMX7D_SNVS_CLK>;
+			clock-names = "snvs-rtc";
 		};
 
 		snvs-pwrkey at 020cc000 {
diff --git a/arch/arm/boot/dts/imx7s.dtsi b/arch/arm/boot/dts/imx7s.dtsi
index 9aa2bb9..02baf42 100644
--- a/arch/arm/boot/dts/imx7s.dtsi
+++ b/arch/arm/boot/dts/imx7s.dtsi
@@ -551,6 +551,8 @@
 					offset = <0x34>;
 					interrupts = <GIC_SPI 19 IRQ_TYPE_LEVEL_HIGH>,
 						     <GIC_SPI 20 IRQ_TYPE_LEVEL_HIGH>;
+					clocks = <&clks IMX7D_SNVS_CLK>;
+					clock-names = "snvs-rtc";
 				};
 
 				snvs_poweroff: snvs-poweroff {
-- 
1.9.1

^ permalink raw reply related

* [PATCH V2 1/2] clk: imx: imx7d: add the snvs clock
From: Anson Huang @ 2018-01-09  2:51 UTC (permalink / raw)
  To: linux-arm-kernel

According to the i.MX7D Reference Manual,
SNVS block has a clock gate, accessing SNVS block
would need this clock gate to be enabled, add it
into clock tree so that SNVS module driver can
operate this clock gate.

Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
---
no changes since v1.
 drivers/clk/imx/clk-imx7d.c             | 1 +
 include/dt-bindings/clock/imx7d-clock.h | 3 ++-
 2 files changed, 3 insertions(+), 1 deletion(-)

diff --git a/drivers/clk/imx/clk-imx7d.c b/drivers/clk/imx/clk-imx7d.c
index 80dc211..f34f1ec 100644
--- a/drivers/clk/imx/clk-imx7d.c
+++ b/drivers/clk/imx/clk-imx7d.c
@@ -795,6 +795,7 @@ static void __init imx7d_clocks_init(struct device_node *ccm_node)
 	clks[IMX7D_DRAM_PHYM_ALT_ROOT_CLK] = imx_clk_gate4("dram_phym_alt_root_clk", "dram_phym_alt_post_div", base + 0x4130, 0);
 	clks[IMX7D_DRAM_ALT_ROOT_CLK] = imx_clk_gate4("dram_alt_root_clk", "dram_alt_post_div", base + 0x4130, 0);
 	clks[IMX7D_OCOTP_CLK] = imx_clk_gate4("ocotp_clk", "ipg_root_clk", base + 0x4230, 0);
+	clks[IMX7D_SNVS_CLK] = imx_clk_gate4("snvs_clk", "ipg_root_clk", base + 0x4250, 0);
 	clks[IMX7D_USB_HSIC_ROOT_CLK] = imx_clk_gate4("usb_hsic_root_clk", "usb_hsic_post_div", base + 0x4420, 0);
 	clks[IMX7D_SDMA_CORE_CLK] = imx_clk_gate4("sdma_root_clk", "ahb_root_clk", base + 0x4480, 0);
 	clks[IMX7D_PCIE_CTRL_ROOT_CLK] = imx_clk_gate4("pcie_ctrl_root_clk", "pcie_ctrl_post_div", base + 0x4600, 0);
diff --git a/include/dt-bindings/clock/imx7d-clock.h b/include/dt-bindings/clock/imx7d-clock.h
index e2f99ae..dc51904 100644
--- a/include/dt-bindings/clock/imx7d-clock.h
+++ b/include/dt-bindings/clock/imx7d-clock.h
@@ -452,5 +452,6 @@
 #define IMX7D_OCOTP_CLK			439
 #define IMX7D_NAND_RAWNAND_CLK		440
 #define IMX7D_NAND_USDHC_BUS_RAWNAND_CLK 441
-#define IMX7D_CLK_END			442
+#define IMX7D_SNVS_CLK			442
+#define IMX7D_CLK_END			443
 #endif /* __DT_BINDINGS_CLOCK_IMX7D_H */
-- 
1.9.1

^ permalink raw reply related

* [PATCH 2/2] arm64: Branch predictor hardening for Cavium ThunderX2
From: Jayachandran C @ 2018-01-09  2:26 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108172341.GV25869@arm.com>

On Mon, Jan 08, 2018 at 05:23:41PM +0000, Will Deacon wrote:
> On Mon, Jan 08, 2018 at 09:19:43AM -0800, Jayachandran C wrote:
> > On Mon, Jan 08, 2018 at 04:46:52PM +0000, Will Deacon wrote:
> > > On Sun, Jan 07, 2018 at 10:53:36PM -0800, Jayachandran C wrote:
> > > > Use PSCI based mitigation for speculative execution attacks targeting
> > > > the branch predictor. The approach is similar to the one used for
> > > > Cortex-A CPUs, but in case of ThunderX2 we add another SMC call to
> > > > test if the firmware supports the capability.
> > > > 
> > > > If the secure firmware has been updated with the mitigation code to
> > > > invalidate the branch target buffer, we use the PSCI version call to
> > > > invoke it.
> > > > 
> > > > Signed-off-by: Jayachandran C <jnair@caviumnetworks.com>
> > > > ---
> > > >  arch/arm64/kernel/cpu_errata.c | 38 ++++++++++++++++++++++++++++++++++++++
> > > >  1 file changed, 38 insertions(+)
> > > > 
> > > > diff --git a/arch/arm64/kernel/cpu_errata.c b/arch/arm64/kernel/cpu_errata.c
> > > > index cb0fb37..abceb5d 100644
> > > > --- a/arch/arm64/kernel/cpu_errata.c
> > > > +++ b/arch/arm64/kernel/cpu_errata.c
> > > > @@ -124,6 +124,7 @@ static void  install_bp_hardening_cb(const struct arm64_cpu_capabilities *entry,
> > > >  	__install_bp_hardening_cb(fn, hyp_vecs_start, hyp_vecs_end);
> > > >  }
> > > >  
> > > > +#include <linux/arm-smccc.h>
> > > >  #include <linux/psci.h>
> > > >  
> > > >  static int enable_psci_bp_hardening(void *data)
> > > > @@ -138,6 +139,33 @@ static int enable_psci_bp_hardening(void *data)
> > > >  
> > > >  	return 0;
> > > >  }
> > > > +
> > > > +#define CAVIUM_TX2_SIP_SMC_CALL		0xC200FF00
> > > > +#define CAVIUM_TX2_BTB_HARDEN_CAP	0xB0A0
> > > > +
> > > > +static int enable_tx2_psci_bp_hardening(void *data)
> > > > +{
> > > > +	const struct arm64_cpu_capabilities *entry = data;
> > > > +	struct arm_smccc_res res;
> > > > +
> > > > +	if (!entry->matches(entry, SCOPE_LOCAL_CPU))
> > > > +		return;
> > > > +
> > > > +	arm_smccc_smc(CAVIUM_TX2_SIP_SMC_CALL, CAVIUM_TX2_BTB_HARDEN_CAP, 0, 0, 0, 0, 0, 0, &res);
> > > 
> > > One thing to be aware of here is that if somebody configures qemu to emulate
> > > a TX2, this may actually disappear into EL3 and not return. You're better
> > > off sticking with PSCI GET_VERSION in terms of portability, but it's your
> > > call -- I'd expect you to deal with any breakage reports on the list due
> > > to the SMC above. Fair?
> > 
> > I don't like having a custom SMC here either. But Overloading PSCI get version
> > is the problem as I wrote earlier - there is no way to check if the firmware
> > implements BTB hardening with overloading. There is a good chance that users
> > with old firmware will just fail without any warning.
> 
> That's true, but there is precedent for this elsewhere. For example, CPU
> errata that require a firmware change are often not probable. Also, your SMC
> call won't always work (see the qemu comment below). Note that I'm not
> saying I won't take this code, just that you need to be aware of what
> you're doing.
> 
> > Is there a reason for overloading PSCI get version? Allocating a new standard
> > SMC number would make checking for existance and usage much simpler.
> 
> PSCI get version is what we have today. We're working on extending PSCI to
> allocate a new standard SMC number, but we need something that can be used
> with existing firmware too and standardisation doesn't happen overnight.

Can you hold this patchset until the SMC number is published? Otherwise we
will end up with two incompatible interfaces, and the mess of supporting
both.

Or if there is a plan standardize this later, I can pickup a vendor specific
SMC for now, and switch over to the standard one later. Any suggestions here?

JC.

^ permalink raw reply

* [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
From: Anson Huang @ 2018-01-09  2:20 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109021704.GG32635@b29396-OptiPlex-7040>



Best Regards!
Anson Huang


> -----Original Message-----
> From: Dong Aisheng [mailto:dongas86 at gmail.com]
> Sent: 2018-01-09 10:17 AM
> To: Anson Huang <anson.huang@nxp.com>
> Cc: linux-arm-kernel at lists.infradead.org; linux-clk at vger.kernel.org; linux-
> kernel at vger.kernel.org; devicetree at vger.kernel.org; dl-linux-imx <linux-
> imx at nxp.com>; shawnguo at kernel.org; kernel at pengutronix.de; Fabio
> Estevam <fabio.estevam@nxp.com>; mturquette at baylibre.com;
> sboyd at codeaurora.org; robh+dt at kernel.org; mark.rutland at arm.com;
> stefan at agner.ch; linux at armlinux.org.uk
> Subject: Re: [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
> 
> On Tue, Jan 09, 2018 at 01:43:25AM +0000, Anson Huang wrote:
> >
> >
> > Best Regards!
> > Anson Huang
> >
> >
> > > -----Original Message-----
> > > From: Dong Aisheng [mailto:dongas86 at gmail.com]
> > > Sent: 2018-01-08 6:39 PM
> > > To: Anson Huang <anson.huang@nxp.com>
> > > Cc: linux-arm-kernel at lists.infradead.org; linux-clk at vger.kernel.org;
> > > linux- kernel at vger.kernel.org; devicetree at vger.kernel.org;
> > > dl-linux-imx <linux- imx@nxp.com>; shawnguo at kernel.org;
> > > kernel at pengutronix.de; Fabio Estevam <fabio.estevam@nxp.com>;
> > > mturquette at baylibre.com; sboyd at codeaurora.org; robh+dt at kernel.org;
> > > mark.rutland at arm.com; stefan at agner.ch; linux at armlinux.org.uk
> > > Subject: Re: [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
> > >
> > > On Thu, Jan 04, 2018 at 11:06:42PM +0800, Anson Huang wrote:
> > > > Add i.MX7 SNVS RTC clock.
> > > >
> > > > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > >
> > > If this clock IMX7D_SNVS_CLK is used for all SNVS sub modules, then
> > > snvs_poweroff and snvs_pwrkey should also rely on it, right?
> > >
> > > How about add them all in one time?
> > >
> > > BTW, syscon-poweroff seems still not introduce clock support and
> > > fsl,sec-v4.0- pwrkey also does not handle clock, they may need to be added
> later.
> > >
> > > Regards
> > > Dong Aisheng
> >
> > Yes, I thought about it before sending patch, but currently the
> > poweroff and pwrkey driver do NOT handle clocks, so I plan to add them later,
> including handling clocks in driver and dtb.
> >
> 
> SNVS RTC binding doc does not claim a optional clock support, you probably
> may need update the binding doc as well.
> 
> BTW, Should mx6 platforms need the same fixes?
> If yes, we'd better fix them all.

I.MX6 platforms has no such SNVS CCGR clock gate, no need to change.
OK, I will update the SNVS RTC binding doc and send out a V2 patch set.

Anson.

> 
> Regards
> Dong Aisheng
> 
> > Anson
> >
> > >
> > > > ---
> > > >  arch/arm/boot/dts/imx7s.dtsi | 2 ++
> > > >  1 file changed, 2 insertions(+)
> > > >
> > > > diff --git a/arch/arm/boot/dts/imx7s.dtsi
> > > > b/arch/arm/boot/dts/imx7s.dtsi index e718fd2..f9b97f3 100644
> > > > --- a/arch/arm/boot/dts/imx7s.dtsi
> > > > +++ b/arch/arm/boot/dts/imx7s.dtsi
> > > > @@ -534,6 +534,8 @@
> > > >  					offset = <0x34>;
> > > >  					interrupts = <GIC_SPI 19
> > > IRQ_TYPE_LEVEL_HIGH>,
> > > >  						     <GIC_SPI 20
> > > IRQ_TYPE_LEVEL_HIGH>;
> > > > +					clocks = <&clks IMX7D_SNVS_CLK>;
> > > > +					clock-names = "snvs-rtc";
> > > >  				};
> > > >
> > > >  				snvs_poweroff: snvs-poweroff {
> > > > --
> > > > 1.9.1
> > > >
> > > > --
> > > > To unsubscribe from this list: send the line "unsubscribe linux-clk"
> > > > in the body of a message to majordomo at vger.kernel.org More
> > > > majordomo info at
> > > > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2F
> > > > vger
> > > > .kernel.org%2Fmajordomo-
> > > info.html&data=02%7C01%7CAnson.Huang%40nxp.com
> > > > %7Cd10ff23b822b4cd16c2708d5568417c6%7C686ea1d3bc2b4c6fa92cd99c5
> c30
> > > 1635
> > > > %7C0%7C0%7C636510047707248763&sdata=FMx22hfKxs7YA%2F1zCVeYC
> nVX
> > > 9Ck4MmB9
> > > > GuolA51V2eg%3D&reserved=0

^ permalink raw reply

* [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
From: Dong Aisheng @ 2018-01-09  2:17 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <AM3PR04MB131588945BE9C5504C6EC0E8F5100@AM3PR04MB1315.eurprd04.prod.outlook.com>

On Tue, Jan 09, 2018 at 01:43:25AM +0000, Anson Huang wrote:
> 
> 
> Best Regards!
> Anson Huang
> 
> 
> > -----Original Message-----
> > From: Dong Aisheng [mailto:dongas86 at gmail.com]
> > Sent: 2018-01-08 6:39 PM
> > To: Anson Huang <anson.huang@nxp.com>
> > Cc: linux-arm-kernel at lists.infradead.org; linux-clk at vger.kernel.org; linux-
> > kernel at vger.kernel.org; devicetree at vger.kernel.org; dl-linux-imx <linux-
> > imx at nxp.com>; shawnguo at kernel.org; kernel at pengutronix.de; Fabio
> > Estevam <fabio.estevam@nxp.com>; mturquette at baylibre.com;
> > sboyd at codeaurora.org; robh+dt at kernel.org; mark.rutland at arm.com;
> > stefan at agner.ch; linux at armlinux.org.uk
> > Subject: Re: [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
> > 
> > On Thu, Jan 04, 2018 at 11:06:42PM +0800, Anson Huang wrote:
> > > Add i.MX7 SNVS RTC clock.
> > >
> > > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> > 
> > If this clock IMX7D_SNVS_CLK is used for all SNVS sub modules, then
> > snvs_poweroff and snvs_pwrkey should also rely on it, right?
> > 
> > How about add them all in one time?
> > 
> > BTW, syscon-poweroff seems still not introduce clock support and fsl,sec-v4.0-
> > pwrkey also does not handle clock, they may need to be added later.
> > 
> > Regards
> > Dong Aisheng
> 
> Yes, I thought about it before sending patch, but currently the poweroff and pwrkey driver do NOT
> handle clocks, so I plan to add them later, including handling clocks in driver and dtb.
> 

SNVS RTC binding doc does not claim a optional clock support, you probably may
need update the binding doc as well.

BTW, Should mx6 platforms need the same fixes?
If yes, we'd better fix them all.

Regards
Dong Aisheng

> Anson
> 
> > 
> > > ---
> > >  arch/arm/boot/dts/imx7s.dtsi | 2 ++
> > >  1 file changed, 2 insertions(+)
> > >
> > > diff --git a/arch/arm/boot/dts/imx7s.dtsi
> > > b/arch/arm/boot/dts/imx7s.dtsi index e718fd2..f9b97f3 100644
> > > --- a/arch/arm/boot/dts/imx7s.dtsi
> > > +++ b/arch/arm/boot/dts/imx7s.dtsi
> > > @@ -534,6 +534,8 @@
> > >  					offset = <0x34>;
> > >  					interrupts = <GIC_SPI 19
> > IRQ_TYPE_LEVEL_HIGH>,
> > >  						     <GIC_SPI 20
> > IRQ_TYPE_LEVEL_HIGH>;
> > > +					clocks = <&clks IMX7D_SNVS_CLK>;
> > > +					clock-names = "snvs-rtc";
> > >  				};
> > >
> > >  				snvs_poweroff: snvs-poweroff {
> > > --
> > > 1.9.1
> > >
> > > --
> > > To unsubscribe from this list: send the line "unsubscribe linux-clk"
> > > in the body of a message to majordomo at vger.kernel.org More majordomo
> > > info at
> > > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fvger
> > > .kernel.org%2Fmajordomo-
> > info.html&data=02%7C01%7CAnson.Huang%40nxp.com
> > > %7Cd10ff23b822b4cd16c2708d5568417c6%7C686ea1d3bc2b4c6fa92cd99c5c30
> > 1635
> > > %7C0%7C0%7C636510047707248763&sdata=FMx22hfKxs7YA%2F1zCVeYCnVX
> > 9Ck4MmB9
> > > GuolA51V2eg%3D&reserved=0

^ permalink raw reply

* [PATCH v3 3/3] drm/sun4i: hdmi: Add missing rate halving check in sun4i_tmds_determine_rate
From: Jonathan Liu @ 2018-01-09  2:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109020323.11852-1-net147@gmail.com>

It was only checking the divider when determing the closest match if
it could not match the requested rate exactly.

For a projector connected to an Olimex A20-OLinuXino-LIME using HDMI
with a native resolution of 1280x800 and pixel clock of 83.5 MHz, this
resulted in 1280x800 mode not being available and the following in dmesg
when the kernel is booted with drm.debug=0x3e:
[drm:drm_mode_debug_printmodeline] Modeline 37:"1280x800" 60 83500 1280 1352 1480 1680 800 810 816 831 0x48 0x5
[drm:drm_mode_prune_invalid] Not using 1280x800 mode: NOCLOCK

Fixes: 9c5681011a0c ("drm/sun4i: Add HDMI support")
Signed-off-by: Jonathan Liu <net147@gmail.com>
---
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
index 88eeeaf34638..3ecffa52c814 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -102,9 +102,12 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw,
 					goto out;
 				}
 
-				if (!best_parent || abs(rate - rounded / i) <
-				    abs(rate - best_parent / best_div)) {
+				if (!best_parent ||
+				    abs(rate - rounded / i / j) <
+				    abs(rate - best_parent / best_half /
+					best_div)) {
 					best_parent = rounded;
+					best_half = i;
 					best_div = j;
 				}
 			}
-- 
2.15.1

^ permalink raw reply related

* [PATCH v3 2/3] drm/sun4i: hdmi: Fix incorrect assignment in sun4i_tmds_determine_rate
From: Jonathan Liu @ 2018-01-09  2:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109020323.11852-1-net147@gmail.com>

best_div is set to i which corresponds to rate halving when it should be
set to j which corresponds to the divider.

Fixes: 9c5681011a0c ("drm/sun4i: Add HDMI support")
Signed-off-by: Jonathan Liu <net147@gmail.com>
---
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
index 4d235e5ea31c..88eeeaf34638 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -105,7 +105,7 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw,
 				if (!best_parent || abs(rate - rounded / i) <
 				    abs(rate - best_parent / best_div)) {
 					best_parent = rounded;
-					best_div = i;
+					best_div = j;
 				}
 			}
 		}
-- 
2.15.1

^ permalink raw reply related

* [PATCH v3 1/3] drm/sun4i: hdmi: Check for unset best_parent in sun4i_tmds_determine_rate
From: Jonathan Liu @ 2018-01-09  2:03 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180109020323.11852-1-net147@gmail.com>

It is possible that if there is no exact rate match and
"rounded = clk_hw_round_rate(parent, ideal)" gives high enough values
(e.g. if rounded is 2 * ideal) that the condition
"abs(rate - rounded / i) < abs(rate - best_parent / best_div)" is never
met and best_parent is never set. This results in req->rate and
req->best_parent_rate being assigned 0.

To avoid this, we set best_parent to the first calculated rate if it is
unset. The sun4i_tmds_calc_divider function already has a similar check.

Fixes: 9c5681011a0c ("drm/sun4i: Add HDMI support")
Signed-off-by: Jonathan Liu <net147@gmail.com>
---
 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
index dc332ea56f6c..4d235e5ea31c 100644
--- a/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
+++ b/drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c
@@ -102,7 +102,7 @@ static int sun4i_tmds_determine_rate(struct clk_hw *hw,
 					goto out;
 				}
 
-				if (abs(rate - rounded / i) <
+				if (!best_parent || abs(rate - rounded / i) <
 				    abs(rate - best_parent / best_div)) {
 					best_parent = rounded;
 					best_div = i;
-- 
2.15.1

^ permalink raw reply related

* [PATCH v3 0/3] drm/sun4i: hdmi: Fix sun4i_tmds_determine_rate
From: Jonathan Liu @ 2018-01-09  2:03 UTC (permalink / raw)
  To: linux-arm-kernel

This patchset fixes several issues in sun4i_tmds_determine_rate that I
discovered while trying to get a projector connected to an Olimex
A20-OLinuXino-LIME using HDMI with a native resolution of 1280x800 and
pixel clock of 83.5 MHz to display at its native resolution.

Changes for v3:
- Improve commit message for unset best_parent

Changes for v2:
- Split into separate patches for each issue
- Add details to commit message for reproducing issue

Jonathan Liu (3):
  drm/sun4i: hdmi: Check for unset best_parent in
    sun4i_tmds_determine_rate
  drm/sun4i: hdmi: Fix incorrect assignment in sun4i_tmds_determine_rate
  drm/sun4i: hdmi: Add missing rate halving check in
    sun4i_tmds_determine_rate

 drivers/gpu/drm/sun4i/sun4i_hdmi_tmds_clk.c | 9 ++++++---
 1 file changed, 6 insertions(+), 3 deletions(-)

-- 
2.15.1

^ permalink raw reply

* [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
From: Anson Huang @ 2018-01-09  1:43 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180108103915.GD32635@b29396-OptiPlex-7040>



Best Regards!
Anson Huang


> -----Original Message-----
> From: Dong Aisheng [mailto:dongas86 at gmail.com]
> Sent: 2018-01-08 6:39 PM
> To: Anson Huang <anson.huang@nxp.com>
> Cc: linux-arm-kernel at lists.infradead.org; linux-clk at vger.kernel.org; linux-
> kernel at vger.kernel.org; devicetree at vger.kernel.org; dl-linux-imx <linux-
> imx at nxp.com>; shawnguo at kernel.org; kernel at pengutronix.de; Fabio
> Estevam <fabio.estevam@nxp.com>; mturquette at baylibre.com;
> sboyd at codeaurora.org; robh+dt at kernel.org; mark.rutland at arm.com;
> stefan at agner.ch; linux at armlinux.org.uk
> Subject: Re: [PATCH 2/2] ARM: dts: imx7s: add snvs rtc clock
> 
> On Thu, Jan 04, 2018 at 11:06:42PM +0800, Anson Huang wrote:
> > Add i.MX7 SNVS RTC clock.
> >
> > Signed-off-by: Anson Huang <Anson.Huang@nxp.com>
> 
> If this clock IMX7D_SNVS_CLK is used for all SNVS sub modules, then
> snvs_poweroff and snvs_pwrkey should also rely on it, right?
> 
> How about add them all in one time?
> 
> BTW, syscon-poweroff seems still not introduce clock support and fsl,sec-v4.0-
> pwrkey also does not handle clock, they may need to be added later.
> 
> Regards
> Dong Aisheng

Yes, I thought about it before sending patch, but currently the poweroff and pwrkey driver do NOT
handle clocks, so I plan to add them later, including handling clocks in driver and dtb.

Anson

> 
> > ---
> >  arch/arm/boot/dts/imx7s.dtsi | 2 ++
> >  1 file changed, 2 insertions(+)
> >
> > diff --git a/arch/arm/boot/dts/imx7s.dtsi
> > b/arch/arm/boot/dts/imx7s.dtsi index e718fd2..f9b97f3 100644
> > --- a/arch/arm/boot/dts/imx7s.dtsi
> > +++ b/arch/arm/boot/dts/imx7s.dtsi
> > @@ -534,6 +534,8 @@
> >  					offset = <0x34>;
> >  					interrupts = <GIC_SPI 19
> IRQ_TYPE_LEVEL_HIGH>,
> >  						     <GIC_SPI 20
> IRQ_TYPE_LEVEL_HIGH>;
> > +					clocks = <&clks IMX7D_SNVS_CLK>;
> > +					clock-names = "snvs-rtc";
> >  				};
> >
> >  				snvs_poweroff: snvs-poweroff {
> > --
> > 1.9.1
> >
> > --
> > To unsubscribe from this list: send the line "unsubscribe linux-clk"
> > in the body of a message to majordomo at vger.kernel.org More majordomo
> > info at
> > https://emea01.safelinks.protection.outlook.com/?url=http%3A%2F%2Fvger
> > .kernel.org%2Fmajordomo-
> info.html&data=02%7C01%7CAnson.Huang%40nxp.com
> > %7Cd10ff23b822b4cd16c2708d5568417c6%7C686ea1d3bc2b4c6fa92cd99c5c30
> 1635
> > %7C0%7C0%7C636510047707248763&sdata=FMx22hfKxs7YA%2F1zCVeYCnVX
> 9Ck4MmB9
> > GuolA51V2eg%3D&reserved=0

^ permalink raw reply

* [PATCH] arm: omap2: timer: fix a kmemleak caused in omap_get_timer_dt
From: qhou @ 2018-01-09  1:42 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <201801090643.8ChygjAc%fengguang.wu@intel.com>



On 2018?01?09? 07:13, kbuild test robot wrote:
> Hi Qi,
>
> Thank you for the patch! Perhaps something to improve:
>
> [auto build test WARNING on omap/for-next]
> [also build test WARNING on v4.15-rc7 next-20180108]
> [if your patch is applied to the wrong git tree, please drop us a note to help improve the system]
>
> url:    https://github.com/0day-ci/linux/commits/Qi-Hou/arm-omap2-timer-fix-a-kmemleak-caused-in-omap_get_timer_dt/20180109-033316
> base:   https://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git for-next
> config: arm-omap2plus_defconfig (attached as .config)
> compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
> reproduce:
>          wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
>          chmod +x ~/bin/make.cross
>          # save the attached .config to linux build tree
>          make.cross ARCH=arm
>
> All warnings (new ones prefixed by >>):
>
>     arch/arm/mach-omap2/timer.c: In function 'omap_get_timer_dt':
> >> arch/arm/mach-omap2/timer.c:205:12: warning: return makes pointer from integer without a cast [-Wint-conversion]
>          return -ENOMEM;

Yes, it's problem.

I will correct it to "return NULL;".

As omap_get_timer_dt() is called in early time during booting up kernel, 
that allocating a little size of memory should

always be alright. Return NULL just in case that it fails.

--
best regards,
Qi Hou
>                 ^
>
> vim +205 arch/arm/mach-omap2/timer.c
>
>     170	
>     171	/**
>     172	 * omap_get_timer_dt - get a timer using device-tree
>     173	 * @match	- device-tree match structure for matching a device type
>     174	 * @property	- optional timer property to match
>     175	 *
>     176	 * Helper function to get a timer during early boot using device-tree for use
>     177	 * as kernel system timer. Optionally, the property argument can be used to
>     178	 * select a timer with a specific property. Once a timer is found then mark
>     179	 * the timer node in device-tree as disabled, to prevent the kernel from
>     180	 * registering this timer as a platform device and so no one else can use it.
>     181	 */
>     182	static struct device_node * __init omap_get_timer_dt(const struct of_device_id *match,
>     183							     const char *property)
>     184	{
>     185		struct device_node *np;
>     186	
>     187		for_each_matching_node(np, match) {
>     188			if (!of_device_is_available(np))
>     189				continue;
>     190	
>     191			if (property && !of_get_property(np, property, NULL))
>     192				continue;
>     193	
>     194			if (!property && (of_get_property(np, "ti,timer-alwon", NULL) ||
>     195					  of_get_property(np, "ti,timer-dsp", NULL) ||
>     196					  of_get_property(np, "ti,timer-pwm", NULL) ||
>     197					  of_get_property(np, "ti,timer-secure", NULL)))
>     198				continue;
>     199	
>     200			if (!of_device_is_compatible(np, "ti,omap-counter32k")) {
>     201				struct property *prop;
>     202	
>     203				prop = kzalloc(sizeof(*prop), GFP_KERNEL);
>     204				if (!prop)
>   > 205					return -ENOMEM;

Correct "return -ENOMEM" to "return NULL;" in case that it fails to 
allocate a new memory block.

--
Qi Hou
>     206				prop->name = "status";
>     207				prop->length = sizeof("disabled");
>     208				prop->value = "disabled";
>     209				of_add_property(np, prop);
>     210			}
>     211			return np;
>     212		}
>     213	
>     214		return NULL;
>     215	}
>     216	
>
> ---
> 0-DAY kernel test infrastructure                Open Source Technology Center
> https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

-- 
Best regards,
Qi Hou
Phone number: +86-10-8477-8608
Address: Floor 15, Building B, Wangjing Plaza, No.9 Zhong-Huan Nanlu, Chaoyang District

^ permalink raw reply

* [PATCH] ARM: dts: qcom: Add initial DTS file for Samsung Galaxy S5 phone
From: Daniele Debernardi @ 2018-01-09  1:15 UTC (permalink / raw)
  To: linux-arm-kernel

This DTS has support for the Samsung Galaxy S5 (codenamed klte).
Initial version have support just for serial console.

Cc: Andy Gross <andy.gross@linaro.org>
Cc: Bjorn Andersson <bjorn.andersson@linaro.org>
Cc: David Brown <david.brown@linaro.org>
Cc: Rob Herring <robh+dt@kernel.org>
Cc: Mark Rutland <mark.rutland@arm.com>
Cc: Russell King <linux@armlinux.org.uk>
Cc: devicetree at vger.kernel.org
Cc: linux-arm-kernel at lists.infradead.org
Cc: linux-arm-msm at vger.kernel.org
Signed-off-by: Daniele Debernardi <drebrez@gmail.com>
---
 arch/arm/boot/dts/Makefile                      |  1 +
 arch/arm/boot/dts/qcom-msm8974-samsung-klte.dts | 24 ++++++++++++++++++++++++
 2 files changed, 25 insertions(+)
 create mode 100644 arch/arm/boot/dts/qcom-msm8974-samsung-klte.dts

diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index d0381e9..abb0ab1 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -729,6 +729,7 @@ dtb-$(CONFIG_ARCH_QCOM) += \
 	qcom-msm8960-cdp.dtb \
 	qcom-msm8974-fairphone-fp2.dtb \
 	qcom-msm8974-lge-nexus5-hammerhead.dtb \
+	qcom-msm8974-samsung-klte.dtb \
 	qcom-msm8974-sony-xperia-castor.dtb \
 	qcom-msm8974-sony-xperia-honami.dtb \
 	qcom-mdm9615-wp8548-mangoh-green.dtb
diff --git a/arch/arm/boot/dts/qcom-msm8974-samsung-klte.dts b/arch/arm/boot/dts/qcom-msm8974-samsung-klte.dts
new file mode 100644
index 0000000..eaa1001
--- /dev/null
+++ b/arch/arm/boot/dts/qcom-msm8974-samsung-klte.dts
@@ -0,0 +1,24 @@
+// SPDX-License-Identifier: GPL-2.0
+#include "qcom-msm8974pro.dtsi"
+#include "qcom-pm8841.dtsi"
+#include "qcom-pm8941.dtsi"
+
+/ {
+	model = "Samsung Galaxy S5";
+	compatible = "samsung,klte", "qcom,msm8974";
+
+	aliases {
+		serial0 = &blsp1_uart1;
+	};
+
+	chosen {
+		stdout-path = "serial0:115200n8";
+	};
+};
+
+&soc {
+	serial at f991e000 {
+		status = "ok";
+	};
+
+};
-- 
2.7.4

^ permalink raw reply related

* [PATCH] ASoC: rockchip: i2s: Support mono capture
From: Matthias Kaehlcke @ 2018-01-09  0:01 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <20180105221242.22083-1-mka@chromium.org>

El Fri, Jan 05, 2018 at 02:12:42PM -0800 Matthias Kaehlcke ha dit:

> The Rockchip I2S controller only allows to configure even numbers of
> capture channels. It is still possible to capture monophonic audio by
> using dual-channel mode and ignoring the 'data' from the second
> channel.

Due to different issues in our audio recording path I couldn't test
this properly, and only looked at the .wav data in audacity, which
appeared to look ok. After putting some of the missing pieces
together and I noticed that mono capture doesn't work as intended,
the recorded audio is slowed down. I think this is because the I2S
controller keeps generating a two-channel stream, however the sound
system interprets it as a monophonic stream because of the channel
configuration.

Not sure if there is a clean solution for this if we can't tell the
hardware to generate a single-channel stream. Any suggestions or do
we have to revert the patch?

Sorry about the mess :(

> ---
>  sound/soc/rockchip/rockchip_i2s.c | 5 +++--
>  1 file changed, 3 insertions(+), 2 deletions(-)
> 
> diff --git a/sound/soc/rockchip/rockchip_i2s.c b/sound/soc/rockchip/rockchip_i2s.c
> index 908211e1d6fc..cc22ab3d10dd 100644
> --- a/sound/soc/rockchip/rockchip_i2s.c
> +++ b/sound/soc/rockchip/rockchip_i2s.c
> @@ -328,6 +328,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
>  		val |= I2S_CHN_4;
>  		break;
>  	case 2:
> +	case 1:
>  		val |= I2S_CHN_2;
>  		break;
>  	default:
> @@ -460,7 +461,7 @@ static struct snd_soc_dai_driver rockchip_i2s_dai = {
>  	},
>  	.capture = {
>  		.stream_name = "Capture",
> -		.channels_min = 2,
> +		.channels_min = 1,
>  		.channels_max = 2,
>  		.rates = SNDRV_PCM_RATE_8000_192000,
>  		.formats = (SNDRV_PCM_FMTBIT_S8 |
> @@ -654,7 +655,7 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
>  	}
>  
>  	if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
> -		if (val >= 2 && val <= 8)
> +		if (val >= 1 && val <= 8)
>  			soc_dai->capture.channels_max = val;
>  	}
>  

^ permalink raw reply

* [RFC patch] ioremap: don't set up huge I/O mappings when p4d/pud/pmd is zero
From: Kani, Toshi @ 2018-01-08 23:36 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <e0fa1b52-86f5-687e-46b3-78ddd03565d8@huawei.com>

On Sat, 2018-01-06 at 17:46 +0800, Hanjun Guo wrote:
> On 2018/1/6 6:15, Kani, Toshi wrote:
> > On Thu, 2017-12-28 at 19:24 +0800, Hanjun Guo wrote:
> > > From: Hanjun Guo <hanjun.guo@linaro.org>
> > > 
> > > When we using iounmap() to free the 4K mapping, it just clear the PTEs
> > > but leave P4D/PUD/PMD unchanged, also will not free the memory of page
> > > tables.
> > > 
> > > This will cause issues on ARM64 platform (not sure if other archs have
> > > the same issue) for this case:
> > > 
> > > 1. ioremap a 4K size, valid page table will build,
> > > 2. iounmap it, pte0 will set to 0;
> > > 3. ioremap the same address with 2M size, pgd/pmd is unchanged,
> > >    then set the a new value for pmd;
> > > 4. pte0 is leaked;
> > > 5. CPU may meet exception because the old pmd is still in TLB,
> > >    which will lead to kernel panic.
> > > 
> > > Fix it by skip setting up the huge I/O mappings when p4d/pud/pmd is
> > > zero.
> > 
> > Hi Hanjun,
> > 
> > I tested the above steps on my x86 box, but was not able to reproduce
> > your kernel panic.  On x86, a 4K vaddr gets allocated from a small
> > fragmented free range, whereas a 2MB vaddr is from a larger free range. 
> > Their addrs have different alignments (4KB & 2MB) as well.  So, the
> > steps did not lead to use a same pmd entry.
> 
> Thanks for the testing, I can only reproduce this on my ARM64 platform
> which the CPU will cache the PMD in TLB, from my knowledge, only Cortex-A75
> will do this, so ARM64 platforms which are not A75 based can't be reproduced
> either.
> 
> Catalin, Will, I can reproduce this issue in about 3 minutes with following
> simplified test case [1], and can trigger panic as [2], could you take a look
> as well?

Yes, the test case looks good to me. (nit - it should check if vir_addr
is not NULL.)

> > However, I agree that zero'd pte entries will be leaked when a pmd map
> > is set if they are present under the pmd.
> 
> Thanks for the confirm.
> 
> > 
> > I also tested your patch on my x86 box.  Unfortunately, it effectively
> > disabled 2MB mappings.  While a 2MB vaddr gets allocated from a larger
> > free range, it sill comes from a free range covered by zero'd pte
> > entries.  So, it ends up with 4KB mappings with your changes.
> > 
> > I think we need to come up with other approach.
> 
> Yes, As I said in my patch, this is just RFC, comments are welcomed :)

I am wondering if we can follow the same approach in
arch/x86/mm/pageattr.c.  Like the ioremap case, populate_pmd() does not
check if there is a pte table under the pmd.  But its free function,
unmap_pte_range() calls try_to_free_pte_page() so that a pte table is
freed when all pte entries are zero'd.  It then calls pmd_clear().
iounmap()'s free function, vunmap_pte_range() does not free up a pte
table even if all pte entries are zero'd.

Thanks,
-Toshi

^ permalink raw reply

* [PATCH] arm: omap2: timer: fix a kmemleak caused in omap_get_timer_dt
From: kbuild test robot @ 2018-01-08 23:13 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <1515373733-3658-1-git-send-email-qi.hou@windriver.com>

Hi Qi,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on omap/for-next]
[also build test WARNING on v4.15-rc7 next-20180108]
[if your patch is applied to the wrong git tree, please drop us a note to help improve the system]

url:    https://github.com/0day-ci/linux/commits/Qi-Hou/arm-omap2-timer-fix-a-kmemleak-caused-in-omap_get_timer_dt/20180109-033316
base:   https://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap.git for-next
config: arm-omap2plus_defconfig (attached as .config)
compiler: arm-linux-gnueabi-gcc (Debian 7.2.0-11) 7.2.0
reproduce:
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # save the attached .config to linux build tree
        make.cross ARCH=arm 

All warnings (new ones prefixed by >>):

   arch/arm/mach-omap2/timer.c: In function 'omap_get_timer_dt':
>> arch/arm/mach-omap2/timer.c:205:12: warning: return makes pointer from integer without a cast [-Wint-conversion]
        return -ENOMEM;
               ^

vim +205 arch/arm/mach-omap2/timer.c

   170	
   171	/**
   172	 * omap_get_timer_dt - get a timer using device-tree
   173	 * @match	- device-tree match structure for matching a device type
   174	 * @property	- optional timer property to match
   175	 *
   176	 * Helper function to get a timer during early boot using device-tree for use
   177	 * as kernel system timer. Optionally, the property argument can be used to
   178	 * select a timer with a specific property. Once a timer is found then mark
   179	 * the timer node in device-tree as disabled, to prevent the kernel from
   180	 * registering this timer as a platform device and so no one else can use it.
   181	 */
   182	static struct device_node * __init omap_get_timer_dt(const struct of_device_id *match,
   183							     const char *property)
   184	{
   185		struct device_node *np;
   186	
   187		for_each_matching_node(np, match) {
   188			if (!of_device_is_available(np))
   189				continue;
   190	
   191			if (property && !of_get_property(np, property, NULL))
   192				continue;
   193	
   194			if (!property && (of_get_property(np, "ti,timer-alwon", NULL) ||
   195					  of_get_property(np, "ti,timer-dsp", NULL) ||
   196					  of_get_property(np, "ti,timer-pwm", NULL) ||
   197					  of_get_property(np, "ti,timer-secure", NULL)))
   198				continue;
   199	
   200			if (!of_device_is_compatible(np, "ti,omap-counter32k")) {
   201				struct property *prop;
   202	
   203				prop = kzalloc(sizeof(*prop), GFP_KERNEL);
   204				if (!prop)
 > 205					return -ENOMEM;
   206				prop->name = "status";
   207				prop->length = sizeof("disabled");
   208				prop->value = "disabled";
   209				of_add_property(np, prop);
   210			}
   211			return np;
   212		}
   213	
   214		return NULL;
   215	}
   216	

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation
-------------- next part --------------
A non-text attachment was scrubbed...
Name: .config.gz
Type: application/gzip
Size: 33066 bytes
Desc: not available
URL: <http://lists.infradead.org/pipermail/linux-arm-kernel/attachments/20180109/116fcc7a/attachment-0001.gz>

^ permalink raw reply

* [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


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