Merge git://git.denx.de/u-boot-uniphier
diff --git a/MAINTAINERS b/MAINTAINERS
index e1227f8..e950267 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -65,6 +65,14 @@
 L:	uboot-snps-arc@synopsys.com
 F:	drivers/gpio/hsdk-creg-gpio.c
 
+ARC HSDK CGU CLOCK
+M:	Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+S:	Maintained
+L:	uboot-snps-arc@synopsys.com
+F:	drivers/clk/clk-hsdk-cgu.c
+F:	include/dt-bindings/clock/snps,hsdk-cgu.h
+F:	doc/device-tree-bindings/clock/snps,hsdk-cgu.txt
+
 ARM
 M:	Albert Aribaud <albert.u.boot@aribaud.net>
 S:	Maintained
diff --git a/arch/arc/include/asm/arcregs.h b/arch/arc/include/asm/arcregs.h
index 54a9b00..ba1f7ba 100644
--- a/arch/arc/include/asm/arcregs.h
+++ b/arch/arc/include/asm/arcregs.h
@@ -27,6 +27,12 @@
 #define ARC_AUX_IC_PTAG		0x1E
 #endif
 #define ARC_BCR_IC_BUILD	0x77
+#define AUX_AUX_CACHE_LIMIT		0x5D
+#define ARC_AUX_NON_VOLATILE_LIMIT	0x5E
+
+/* ICCM and DCCM auxiliary registers */
+#define ARC_AUX_DCCM_BASE	0x18	/* DCCM Base Addr ARCv2 */
+#define ARC_AUX_ICCM_BASE	0x208	/* ICCM Base Addr ARCv2 */
 
 /* Timer related auxiliary registers */
 #define ARC_AUX_TIMER0_CNT	0x21	/* Timer 0 count */
@@ -72,6 +78,9 @@
 /* gcc builtin sr needs reg param to be long immediate */
 #define write_aux_reg(reg_immed, val)		\
 		__builtin_arc_sr((unsigned int)val, reg_immed)
+
+/* ARCNUM [15:8] - field to identify each core in a multi-core system */
+#define CPU_ID_GET()	((read_aux_reg(ARC_AUX_IDENTITY) & 0xFF00) >> 8)
 #endif /* __ASSEMBLY__ */
 
 #endif /* _ASM_ARC_ARCREGS_H */
diff --git a/arch/arc/include/asm/gpio.h b/arch/arc/include/asm/gpio.h
new file mode 100644
index 0000000..306ab4c
--- /dev/null
+++ b/arch/arc/include/asm/gpio.h
@@ -0,0 +1 @@
+#include <asm-generic/gpio.h>
diff --git a/arch/arc/lib/cache.c b/arch/arc/lib/cache.c
index d8741fe..1073e15 100644
--- a/arch/arc/lib/cache.c
+++ b/arch/arc/lib/cache.c
@@ -32,15 +32,15 @@
  * relocation but will be used after being zeroed.
  */
 int l1_line_sz __section(".data");
-int dcache_exists __section(".data");
-int icache_exists __section(".data");
+bool dcache_exists __section(".data") = false;
+bool icache_exists __section(".data") = false;
 
 #define CACHE_LINE_MASK		(~(l1_line_sz - 1))
 
 #ifdef CONFIG_ISA_ARCV2
 int slc_line_sz __section(".data");
-int slc_exists __section(".data");
-int ioc_exists __section(".data");
+bool slc_exists __section(".data") = false;
+bool ioc_exists __section(".data") = false;
 
 static unsigned int __before_slc_op(const int op)
 {
@@ -152,7 +152,7 @@
 	sbcr.word = read_aux_reg(ARC_BCR_SLC);
 	if (sbcr.fields.ver) {
 		slc_cfg.word = read_aux_reg(ARC_AUX_SLC_CONFIG);
-		slc_exists = 1;
+		slc_exists = true;
 		slc_line_sz = (slc_cfg.fields.lsz == 0) ? 128 : 64;
 	}
 
@@ -169,7 +169,7 @@
 
 	cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
 	if (cbcr.fields.c)
-		ioc_exists = 1;
+		ioc_exists = true;
 }
 #endif
 
@@ -190,7 +190,7 @@
 
 	ibcr.word = read_aux_reg(ARC_BCR_IC_BUILD);
 	if (ibcr.fields.ver) {
-		icache_exists = 1;
+		icache_exists = true;
 		l1_line_sz = ic_line_sz = 8 << ibcr.fields.line_len;
 		if (!ic_line_sz)
 			panic("Instruction exists but line length is 0\n");
@@ -198,7 +198,7 @@
 
 	dbcr.word = read_aux_reg(ARC_BCR_DC_BUILD);
 	if (dbcr.fields.ver){
-		dcache_exists = 1;
+		dcache_exists = true;
 		l1_line_sz = dc_line_sz = 16 << dbcr.fields.line_len;
 		if (!dc_line_sz)
 			panic("Data cache exists but line length is 0\n");
diff --git a/board/synopsys/hsdk/hsdk.c b/board/synopsys/hsdk/hsdk.c
index 7b56255..7641978 100644
--- a/board/synopsys/hsdk/hsdk.c
+++ b/board/synopsys/hsdk/hsdk.c
@@ -26,6 +26,10 @@
 	return 0;
 }
 
+#define SDIO_BASE              (ARC_PERIPHERAL_BASE + 0xA000)
+#define SDIO_UHS_REG_EXT       (SDIO_BASE + 0x108)
+#define SDIO_UHS_REG_EXT_DIV_2 (2 << 30)
+
 int board_mmc_init(bd_t *bis)
 {
 	struct dwmci_host *host = NULL;
@@ -36,12 +40,18 @@
 		return 1;
 	}
 
+	/*
+	 * Switch SDIO external ciu clock divider from default div-by-8 to
+	 * minimum possible div-by-2.
+	 */
+	writel(SDIO_UHS_REG_EXT_DIV_2, (void __iomem *) SDIO_UHS_REG_EXT);
+
 	memset(host, 0, sizeof(struct dwmci_host));
 	host->name = "Synopsys Mobile storage";
 	host->ioaddr = (void *)ARC_DWMMC_BASE;
 	host->buswidth = 4;
 	host->dev_index = 0;
-	host->bus_hz = 100000000;
+	host->bus_hz = 50000000;
 
 	add_dwmci(host, host->bus_hz / 2, 400000);
 
diff --git a/doc/device-tree-bindings/clock/snps,hsdk-cgu.txt b/doc/device-tree-bindings/clock/snps,hsdk-cgu.txt
new file mode 100644
index 0000000..82fe1dd
--- /dev/null
+++ b/doc/device-tree-bindings/clock/snps,hsdk-cgu.txt
@@ -0,0 +1,35 @@
+* Synopsys HSDK clock generation unit
+
+The Synopsys HSDK clock controller generates and supplies clock to various
+controllers and peripherals within the SoC.
+
+Required Properties:
+
+- compatible: should be "snps,hsdk-cgu-clock"
+- reg: the pair of physical base address and length of clock generation unit
+  memory mapped region and creg arc core divider memory mapped region.
+- #clock-cells: should be 1.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. All available clocks are defined as
+preprocessor macros in the dt-bindings/clock/snps,hsdk-cgu.h headers and can be
+used in device tree sources.
+
+Example: Clock controller node:
+
+	cgu_clk: cgu-clk@f0000000 {
+		compatible = "snps,hsdk-cgu-clock";
+		reg = <0xf0000000 0x1000>, <0xf00014B8 0x4>;
+		#clock-cells = <1>;
+	};
+
+Example: UART controller node that consumes the clock generated by the clock
+controller:
+
+	uart0: serial0@f0005000 {
+		compatible = "snps,dw-apb-uart";
+		reg = <0xf0005000 0x1000>;
+		reg-shift = <2>;
+		reg-io-width = <4>;
+		clocks = <&cgu_clk CLK_SYS_UART_REF>;
+	};
diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig
index f6644ee..cdfa052 100644
--- a/drivers/clk/Kconfig
+++ b/drivers/clk/Kconfig
@@ -54,6 +54,12 @@
 	  This clock driver adds support for RCC clock management
 	  for STM32F4 and STM32F7 SoCs.
 
+config CLK_HSDK
+	bool "Enable cgu clock driver for HSDK"
+	depends on CLK
+	help
+	  Enable this to support the cgu clocks on Synopsys ARC HSDK
+
 config CLK_ZYNQ
 	bool "Enable clock driver support for Zynq"
 	depends on CLK && ARCH_ZYNQ
diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index bcc8f82..876c2b8 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -20,6 +20,7 @@
 obj-$(CONFIG_CLK_AT91) += at91/
 obj-$(CONFIG_CLK_BCM6345) += clk_bcm6345.o
 obj-$(CONFIG_CLK_BOSTON) += clk_boston.o
+obj-$(CONFIG_CLK_HSDK) += clk-hsdk-cgu.o
 obj-$(CONFIG_ARCH_ASPEED) += aspeed/
 obj-$(CONFIG_CLK_STM32F) += clk_stm32f.o
 obj-$(CONFIG_STM32H7) += clk_stm32h7.o
diff --git a/drivers/clk/clk-hsdk-cgu.c b/drivers/clk/clk-hsdk-cgu.c
new file mode 100644
index 0000000..c80f90e
--- /dev/null
+++ b/drivers/clk/clk-hsdk-cgu.c
@@ -0,0 +1,564 @@
+/*
+ * Synopsys HSDK SDP CGU clock driver
+ *
+ * Copyright (C) 2017 Synopsys
+ * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <common.h>
+#include <clk-uclass.h>
+#include <div64.h>
+#include <dm.h>
+#include <linux/io.h>
+
+/*
+ * Synopsys ARC HSDK clock tree.
+ *
+ *   ------------------
+ *   | 33.33 MHz xtal |
+ *   ------------------
+ *            |
+ *            |   -----------
+ *            |-->| ARC PLL |
+ *            |   -----------
+ *            |        |
+ *            |        |-->|CGU_ARC_IDIV|----------->
+ *            |        |-->|CREG_CORE_IF_DIV|------->
+ *            |
+ *            |   --------------
+ *            |-->| SYSTEM PLL |
+ *            |   --------------
+ *            |        |
+ *            |        |-->|CGU_SYS_IDIV_APB|------->
+ *            |        |-->|CGU_SYS_IDIV_AXI|------->
+ *            |        |-->|CGU_SYS_IDIV_*|--------->
+ *            |        |-->|CGU_SYS_IDIV_EBI_REF|--->
+ *            |
+ *            |   --------------
+ *            |-->| TUNNEL PLL |
+ *            |   --------------
+ *            |        |
+ *            |        |-->|CGU_TUN_IDIV|----------->
+ *            |
+ *            |   ------------
+ *            |-->| HDMI PLL |
+ *            |   ------------
+ *            |        |
+ *            |        |-->|CGU_HDMI_IDIV_APB|------>
+ *            |
+ *            |   -----------
+ *            |-->| DDR PLL |
+ *                -----------
+ *                     |
+ *                     |---------------------------->
+ */
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define CGU_ARC_IDIV		0x080
+#define CGU_TUN_IDIV		0x380
+#define CGU_HDMI_IDIV_APB	0x480
+#define CGU_SYS_IDIV_APB	0x180
+#define CGU_SYS_IDIV_AXI	0x190
+#define CGU_SYS_IDIV_ETH	0x1A0
+#define CGU_SYS_IDIV_USB	0x1B0
+#define CGU_SYS_IDIV_SDIO	0x1C0
+#define CGU_SYS_IDIV_HDMI	0x1D0
+#define CGU_SYS_IDIV_GFX_CORE	0x1E0
+#define CGU_SYS_IDIV_GFX_DMA	0x1F0
+#define CGU_SYS_IDIV_GFX_CFG	0x200
+#define CGU_SYS_IDIV_DMAC_CORE	0x210
+#define CGU_SYS_IDIV_DMAC_CFG	0x220
+#define CGU_SYS_IDIV_SDIO_REF	0x230
+#define CGU_SYS_IDIV_SPI_REF	0x240
+#define CGU_SYS_IDIV_I2C_REF	0x250
+#define CGU_SYS_IDIV_UART_REF	0x260
+#define CGU_SYS_IDIV_EBI_REF	0x270
+
+#define CGU_IDIV_MASK		0xFF /* All idiv have 8 significant bits */
+
+#define CGU_ARC_PLL		0x0
+#define CGU_SYS_PLL		0x10
+#define CGU_DDR_PLL		0x20
+#define CGU_TUN_PLL		0x30
+#define CGU_HDMI_PLL		0x40
+
+#define CGU_PLL_CTRL		0x000 /* ARC PLL control register */
+#define CGU_PLL_STATUS		0x004 /* ARC PLL status register */
+#define CGU_PLL_FMEAS		0x008 /* ARC PLL frequency measurement register */
+#define CGU_PLL_MON		0x00C /* ARC PLL monitor register */
+
+#define CGU_PLL_CTRL_ODIV_SHIFT		2
+#define CGU_PLL_CTRL_IDIV_SHIFT		4
+#define CGU_PLL_CTRL_FBDIV_SHIFT	9
+#define CGU_PLL_CTRL_BAND_SHIFT		20
+
+#define CGU_PLL_CTRL_ODIV_MASK		GENMASK(3, CGU_PLL_CTRL_ODIV_SHIFT)
+#define CGU_PLL_CTRL_IDIV_MASK		GENMASK(8, CGU_PLL_CTRL_IDIV_SHIFT)
+#define CGU_PLL_CTRL_FBDIV_MASK		GENMASK(15, CGU_PLL_CTRL_FBDIV_SHIFT)
+
+#define CGU_PLL_CTRL_PD			BIT(0)
+#define CGU_PLL_CTRL_BYPASS		BIT(1)
+
+#define CGU_PLL_STATUS_LOCK		BIT(0)
+#define CGU_PLL_STATUS_ERR		BIT(1)
+
+#define HSDK_PLL_MAX_LOCK_TIME		100 /* 100 us */
+
+#define CREG_CORE_IF_DIV		0x000 /* ARC CORE interface divider */
+#define CORE_IF_CLK_THRESHOLD_HZ	500000000
+#define CREG_CORE_IF_CLK_DIV_1		0x0
+#define CREG_CORE_IF_CLK_DIV_2		0x1
+
+#define PARENT_RATE			33333333 /* fixed clock - xtal */
+#define CGU_MAX_CLOCKS			24
+
+struct hsdk_pll_cfg {
+	u32 rate;
+	u32 idiv;
+	u32 fbdiv;
+	u32 odiv;
+	u32 band;
+};
+
+static const struct hsdk_pll_cfg asdt_pll_cfg[] = {
+	{ 100000000,  0, 11, 3, 0 },
+	{ 125000000,  0, 14, 3, 0 },
+	{ 133000000,  0, 15, 3, 0 },
+	{ 150000000,  0, 17, 3, 0 },
+	{ 200000000,  1, 47, 3, 0 },
+	{ 233000000,  1, 27, 2, 0 },
+	{ 300000000,  1, 35, 2, 0 },
+	{ 333000000,  1, 39, 2, 0 },
+	{ 400000000,  1, 47, 2, 0 },
+	{ 500000000,  0, 14, 1, 0 },
+	{ 600000000,  0, 17, 1, 0 },
+	{ 700000000,  0, 20, 1, 0 },
+	{ 800000000,  0, 23, 1, 0 },
+	{ 900000000,  1, 26, 0, 0 },
+	{ 1000000000, 1, 29, 0, 0 },
+	{ 1100000000, 1, 32, 0, 0 },
+	{ 1200000000, 1, 35, 0, 0 },
+	{ 1300000000, 1, 38, 0, 0 },
+	{ 1400000000, 1, 41, 0, 0 },
+	{ 1500000000, 1, 44, 0, 0 },
+	{ 1600000000, 1, 47, 0, 0 },
+	{}
+};
+
+static const struct hsdk_pll_cfg hdmi_pll_cfg[] = {
+	{ 297000000,  0, 21, 2, 0 },
+	{ 540000000,  0, 19, 1, 0 },
+	{ 594000000,  0, 21, 1, 0 },
+	{}
+};
+
+struct hsdk_cgu_clk {
+	/* CGU block register */
+	void __iomem *cgu_regs;
+	/* CREG block register */
+	void __iomem *creg_regs;
+
+	/* PLLs registers */
+	void __iomem *regs;
+	/* PLLs special registers */
+	void __iomem *spec_regs;
+	/* PLLs devdata */
+	const struct hsdk_pll_devdata *pll_devdata;
+
+	/* Dividers registers */
+	void __iomem *idiv_regs;
+};
+
+struct hsdk_pll_devdata {
+	const struct hsdk_pll_cfg *pll_cfg;
+	int (*update_rate)(struct hsdk_cgu_clk *clk, unsigned long rate,
+			   const struct hsdk_pll_cfg *cfg);
+};
+
+static int hsdk_pll_core_update_rate(struct hsdk_cgu_clk *, unsigned long,
+				     const struct hsdk_pll_cfg *);
+static int hsdk_pll_comm_update_rate(struct hsdk_cgu_clk *, unsigned long,
+				     const struct hsdk_pll_cfg *);
+
+static const struct hsdk_pll_devdata core_pll_dat = {
+	.pll_cfg = asdt_pll_cfg,
+	.update_rate = hsdk_pll_core_update_rate,
+};
+
+static const struct hsdk_pll_devdata sdt_pll_dat = {
+	.pll_cfg = asdt_pll_cfg,
+	.update_rate = hsdk_pll_comm_update_rate,
+};
+
+static const struct hsdk_pll_devdata hdmi_pll_dat = {
+	.pll_cfg = hdmi_pll_cfg,
+	.update_rate = hsdk_pll_comm_update_rate,
+};
+
+static ulong idiv_set(struct clk *, ulong);
+static ulong idiv_get(struct clk *);
+static int idiv_off(struct clk *);
+static ulong pll_set(struct clk *, ulong);
+static ulong pll_get(struct clk *);
+
+struct hsdk_cgu_clock_map {
+	u32 cgu_pll_oft;
+	u32 creg_div_oft;
+	u32 cgu_div_oft;
+	const struct hsdk_pll_devdata *pll_devdata;
+	ulong (*get_rate)(struct clk *clk);
+	ulong (*set_rate)(struct clk *clk, ulong rate);
+	int (*disable)(struct clk *clk);
+};
+
+static const struct hsdk_cgu_clock_map clock_map[] = {
+	{ CGU_ARC_PLL, 0, 0, &core_pll_dat, pll_get, pll_set, NULL },
+	{ CGU_ARC_PLL, 0, CGU_ARC_IDIV, &core_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_DDR_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
+	{ CGU_SYS_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_APB, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_AXI, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_ETH, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_USB, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_SDIO, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_HDMI, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_GFX_CORE, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_GFX_DMA, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_GFX_CFG, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_DMAC_CORE, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_DMAC_CFG, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_SDIO_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_SPI_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_I2C_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_UART_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_SYS_PLL, 0, CGU_SYS_IDIV_EBI_REF, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_TUN_PLL, 0, 0, &sdt_pll_dat, pll_get, pll_set, NULL },
+	{ CGU_TUN_PLL, 0, CGU_TUN_IDIV, &sdt_pll_dat, idiv_get, idiv_set, idiv_off },
+	{ CGU_HDMI_PLL, 0, 0, &hdmi_pll_dat, pll_get, pll_set, NULL },
+	{ CGU_HDMI_PLL, 0, CGU_HDMI_IDIV_APB, &hdmi_pll_dat, idiv_get, idiv_set, idiv_off }
+};
+
+static inline void hsdk_idiv_write(struct hsdk_cgu_clk *clk, u32 val)
+{
+	iowrite32(val, clk->idiv_regs);
+}
+
+static inline u32 hsdk_idiv_read(struct hsdk_cgu_clk *clk)
+{
+	return ioread32(clk->idiv_regs);
+}
+
+static inline void hsdk_pll_write(struct hsdk_cgu_clk *clk, u32 reg, u32 val)
+{
+	iowrite32(val, clk->regs + reg);
+}
+
+static inline u32 hsdk_pll_read(struct hsdk_cgu_clk *clk, u32 reg)
+{
+	return ioread32(clk->regs + reg);
+}
+
+static inline void hsdk_pll_spcwrite(struct hsdk_cgu_clk *clk, u32 reg, u32 val)
+{
+	iowrite32(val, clk->spec_regs + reg);
+}
+
+static inline u32 hsdk_pll_spcread(struct hsdk_cgu_clk *clk, u32 reg)
+{
+	return ioread32(clk->spec_regs + reg);
+}
+
+static inline void hsdk_pll_set_cfg(struct hsdk_cgu_clk *clk,
+				    const struct hsdk_pll_cfg *cfg)
+{
+	u32 val = 0;
+
+	/* Powerdown and Bypass bits should be cleared */
+	val |= cfg->idiv << CGU_PLL_CTRL_IDIV_SHIFT;
+	val |= cfg->fbdiv << CGU_PLL_CTRL_FBDIV_SHIFT;
+	val |= cfg->odiv << CGU_PLL_CTRL_ODIV_SHIFT;
+	val |= cfg->band << CGU_PLL_CTRL_BAND_SHIFT;
+
+	pr_debug("write configurarion: %#x\n", val);
+
+	hsdk_pll_write(clk, CGU_PLL_CTRL, val);
+}
+
+static inline bool hsdk_pll_is_locked(struct hsdk_cgu_clk *clk)
+{
+	return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_LOCK);
+}
+
+static inline bool hsdk_pll_is_err(struct hsdk_cgu_clk *clk)
+{
+	return !!(hsdk_pll_read(clk, CGU_PLL_STATUS) & CGU_PLL_STATUS_ERR);
+}
+
+static ulong pll_get(struct clk *sclk)
+{
+	u32 val;
+	u64 rate;
+	u32 idiv, fbdiv, odiv;
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+
+	val = hsdk_pll_read(clk, CGU_PLL_CTRL);
+
+	pr_debug("current configurarion: %#x\n", val);
+
+	/* Check if PLL is disabled */
+	if (val & CGU_PLL_CTRL_PD)
+		return 0;
+
+	/* Check if PLL is bypassed */
+	if (val & CGU_PLL_CTRL_BYPASS)
+		return PARENT_RATE;
+
+	/* input divider = reg.idiv + 1 */
+	idiv = 1 + ((val & CGU_PLL_CTRL_IDIV_MASK) >> CGU_PLL_CTRL_IDIV_SHIFT);
+	/* fb divider = 2*(reg.fbdiv + 1) */
+	fbdiv = 2 * (1 + ((val & CGU_PLL_CTRL_FBDIV_MASK) >> CGU_PLL_CTRL_FBDIV_SHIFT));
+	/* output divider = 2^(reg.odiv) */
+	odiv = 1 << ((val & CGU_PLL_CTRL_ODIV_MASK) >> CGU_PLL_CTRL_ODIV_SHIFT);
+
+	rate = (u64)PARENT_RATE * fbdiv;
+	do_div(rate, idiv * odiv);
+
+	return rate;
+}
+
+static unsigned long hsdk_pll_round_rate(struct clk *sclk, unsigned long rate)
+{
+	int i;
+	unsigned long best_rate;
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg;
+
+	if (pll_cfg[0].rate == 0)
+		return -EINVAL;
+
+	best_rate = pll_cfg[0].rate;
+
+	for (i = 1; pll_cfg[i].rate != 0; i++) {
+		if (abs(rate - pll_cfg[i].rate) < abs(rate - best_rate))
+			best_rate = pll_cfg[i].rate;
+	}
+
+	pr_debug("chosen best rate: %lu\n", best_rate);
+
+	return best_rate;
+}
+
+static int hsdk_pll_comm_update_rate(struct hsdk_cgu_clk *clk,
+				     unsigned long rate,
+				     const struct hsdk_pll_cfg *cfg)
+{
+	hsdk_pll_set_cfg(clk, cfg);
+
+	/*
+	 * Wait until CGU relocks and check error status.
+	 * If after timeout CGU is unlocked yet return error.
+	 */
+	udelay(HSDK_PLL_MAX_LOCK_TIME);
+	if (!hsdk_pll_is_locked(clk))
+		return -ETIMEDOUT;
+
+	if (hsdk_pll_is_err(clk))
+		return -EINVAL;
+
+	return 0;
+}
+
+static int hsdk_pll_core_update_rate(struct hsdk_cgu_clk *clk,
+				     unsigned long rate,
+				     const struct hsdk_pll_cfg *cfg)
+{
+	/*
+	 * When core clock exceeds 500MHz, the divider for the interface
+	 * clock must be programmed to div-by-2.
+	 */
+	if (rate > CORE_IF_CLK_THRESHOLD_HZ)
+		hsdk_pll_spcwrite(clk, CREG_CORE_IF_DIV, CREG_CORE_IF_CLK_DIV_2);
+
+	hsdk_pll_set_cfg(clk, cfg);
+
+	/*
+	 * Wait until CGU relocks and check error status.
+	 * If after timeout CGU is unlocked yet return error.
+	 */
+	udelay(HSDK_PLL_MAX_LOCK_TIME);
+	if (!hsdk_pll_is_locked(clk))
+		return -ETIMEDOUT;
+
+	if (hsdk_pll_is_err(clk))
+		return -EINVAL;
+
+	/*
+	 * Program divider to div-by-1 if we succesfuly set core clock below
+	 * 500MHz threshold.
+	 */
+	if (rate <= CORE_IF_CLK_THRESHOLD_HZ)
+		hsdk_pll_spcwrite(clk, CREG_CORE_IF_DIV, CREG_CORE_IF_CLK_DIV_1);
+
+	return 0;
+}
+
+static ulong pll_set(struct clk *sclk, ulong rate)
+{
+	int i;
+	unsigned long best_rate;
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	const struct hsdk_pll_cfg *pll_cfg = clk->pll_devdata->pll_cfg;
+
+	best_rate = hsdk_pll_round_rate(sclk, rate);
+
+	for (i = 0; pll_cfg[i].rate != 0; i++) {
+		if (pll_cfg[i].rate == best_rate) {
+			return clk->pll_devdata->update_rate(clk, best_rate,
+							     &pll_cfg[i]);
+		}
+	}
+
+	pr_err("invalid rate=%ld, parent_rate=%d\n", best_rate, PARENT_RATE);
+
+	return -EINVAL;
+}
+
+static int idiv_off(struct clk *sclk)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+
+	hsdk_idiv_write(clk, 0);
+
+	return 0;
+}
+
+static ulong idiv_get(struct clk *sclk)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	ulong parent_rate = pll_get(sclk);
+	u32 div_factor = hsdk_idiv_read(clk);
+
+	div_factor &= CGU_IDIV_MASK;
+
+	pr_debug("current configurarion: %#x (%d)\n", div_factor, div_factor);
+
+	if (div_factor == 0)
+		return 0;
+
+	return parent_rate / div_factor;
+}
+
+static ulong idiv_set(struct clk *sclk, ulong rate)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+	ulong parent_rate = pll_get(sclk);
+	u32 div_factor;
+
+	div_factor = parent_rate / rate;
+	if (abs(rate - parent_rate / (div_factor + 1)) <=
+	    abs(rate - parent_rate / div_factor)) {
+		div_factor += 1;
+	}
+
+	if (div_factor & ~CGU_IDIV_MASK) {
+		pr_err("invalid rate=%ld, parent_rate=%ld, div=%d: max divider valie is%d\n",
+		       rate, parent_rate, div_factor, CGU_IDIV_MASK);
+
+		div_factor = CGU_IDIV_MASK;
+	}
+
+	if (div_factor == 0) {
+		pr_err("invalid rate=%ld, parent_rate=%ld, div=%d: min divider valie is 1\n",
+		       rate, parent_rate, div_factor);
+
+		div_factor = 1;
+	}
+
+	hsdk_idiv_write(clk, div_factor);
+
+	return 0;
+}
+
+static int hsdk_prepare_clock_tree_branch(struct clk *sclk)
+{
+	struct hsdk_cgu_clk *clk = dev_get_priv(sclk->dev);
+
+	if (sclk->id >= CGU_MAX_CLOCKS)
+		return -EINVAL;
+
+	clk->pll_devdata = clock_map[sclk->id].pll_devdata;
+	clk->regs = clk->cgu_regs + clock_map[sclk->id].cgu_pll_oft;
+	clk->spec_regs = clk->creg_regs + clock_map[sclk->id].creg_div_oft;
+	clk->idiv_regs = clk->cgu_regs + clock_map[sclk->id].cgu_div_oft;
+
+	return 0;
+}
+
+static ulong hsdk_cgu_get_rate(struct clk *sclk)
+{
+	if (hsdk_prepare_clock_tree_branch(sclk))
+		return -EINVAL;
+
+	return clock_map[sclk->id].get_rate(sclk);
+}
+
+static ulong hsdk_cgu_set_rate(struct clk *sclk, ulong rate)
+{
+	if (hsdk_prepare_clock_tree_branch(sclk))
+		return -EINVAL;
+
+	return clock_map[sclk->id].set_rate(sclk, rate);
+}
+
+static int hsdk_cgu_disable(struct clk *sclk)
+{
+	if (hsdk_prepare_clock_tree_branch(sclk))
+		return -EINVAL;
+
+	if (clock_map[sclk->id].disable)
+		return clock_map[sclk->id].disable(sclk);
+
+	return -ENOTSUPP;
+}
+
+static const struct clk_ops hsdk_cgu_ops = {
+	.set_rate = hsdk_cgu_set_rate,
+	.get_rate = hsdk_cgu_get_rate,
+	.disable = hsdk_cgu_disable,
+};
+
+static int hsdk_cgu_clk_probe(struct udevice *dev)
+{
+	struct hsdk_cgu_clk *pll_clk = dev_get_priv(dev);
+
+	BUILD_BUG_ON(ARRAY_SIZE(clock_map) != CGU_MAX_CLOCKS);
+
+	pll_clk->cgu_regs = (void __iomem *)devfdt_get_addr_index(dev, 0);
+	if (!pll_clk->cgu_regs)
+		return -EINVAL;
+
+	pll_clk->creg_regs = (void __iomem *)devfdt_get_addr_index(dev, 1);
+	if (!pll_clk->creg_regs)
+		return -EINVAL;
+
+	return 0;
+}
+
+static const struct udevice_id hsdk_cgu_clk_id[] = {
+	{ .compatible = "snps,hsdk-cgu-clock" },
+	{ }
+};
+
+U_BOOT_DRIVER(hsdk_cgu_clk) = {
+	.name = "hsdk-cgu-clk",
+	.id = UCLASS_CLK,
+	.of_match = hsdk_cgu_clk_id,
+	.probe = hsdk_cgu_clk_probe,
+	.platdata_auto_alloc_size = sizeof(struct hsdk_cgu_clk),
+	.ops = &hsdk_cgu_ops,
+};
diff --git a/include/dt-bindings/clock/snps,hsdk-cgu.h b/include/dt-bindings/clock/snps,hsdk-cgu.h
new file mode 100644
index 0000000..813ab71
--- /dev/null
+++ b/include/dt-bindings/clock/snps,hsdk-cgu.h
@@ -0,0 +1,40 @@
+/*
+ * Synopsys HSDK SDP CGU clock driver dts bindings
+ *
+ * Copyright (C) 2017 Synopsys
+ * Author: Eugeniy Paltsev <Eugeniy.Paltsev@synopsys.com>
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2. This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#ifndef __DT_BINDINGS_CLK_HSDK_CGU_H_
+#define __DT_BINDINGS_CLK_HSDK_CGU_H_
+
+#define CLK_ARC_PLL		0
+#define CLK_ARC			1
+#define CLK_DDR_PLL		2
+#define CLK_SYS_PLL		3
+#define CLK_SYS_APB		4
+#define CLK_SYS_AXI		5
+#define CLK_SYS_ETH		6
+#define CLK_SYS_USB		7
+#define CLK_SYS_SDIO		8
+#define CLK_SYS_HDMI		9
+#define CLK_SYS_GFX_CORE	10
+#define CLK_SYS_GFX_DMA		11
+#define CLK_SYS_GFX_CFG		12
+#define CLK_SYS_DMAC_CORE	13
+#define CLK_SYS_DMAC_CFG	14
+#define CLK_SYS_SDIO_REF	15
+#define CLK_SYS_SPI_REF		16
+#define CLK_SYS_I2C_REF		17
+#define CLK_SYS_UART_REF	18
+#define CLK_SYS_EBI_REF		19
+#define CLK_TUN_PLL		20
+#define CLK_TUN			21
+#define CLK_HDMI_PLL		22
+#define CLK_HDMI		23
+
+#endif /* __DT_BINDINGS_CLK_HSDK_CGU_H_ */