]> git.ipfire.org Git - people/ms/u-boot.git/commitdiff
Merge git://git.denx.de/u-boot-arc
authorTom Rini <trini@konsulko.com>
Mon, 11 Dec 2017 22:05:43 +0000 (17:05 -0500)
committerTom Rini <trini@konsulko.com>
Mon, 11 Dec 2017 22:05:43 +0000 (17:05 -0500)
MAINTAINERS
arch/arc/include/asm/arcregs.h
arch/arc/include/asm/gpio.h [new file with mode: 0644]
arch/arc/lib/cache.c
board/synopsys/hsdk/hsdk.c
doc/device-tree-bindings/clock/snps,hsdk-cgu.txt [new file with mode: 0644]
drivers/clk/Kconfig
drivers/clk/Makefile
drivers/clk/clk-hsdk-cgu.c [new file with mode: 0644]
include/dt-bindings/clock/snps,hsdk-cgu.h [new file with mode: 0644]

index e1227f847cbb9470f4e349b9792b54f87394996f..e950267494a3c608b7f541dfe0b9f622295dcaf3 100644 (file)
@@ -65,6 +65,14 @@ S:   Maintained
 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
index 54a9b00d4c24cb3c0bc83d2a62631b8ad54ed28e..ba1f7bac77c77bd5e8d83469e0758bae25847d15 100644 (file)
 #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 (file)
index 0000000..306ab4c
--- /dev/null
@@ -0,0 +1 @@
+#include <asm-generic/gpio.h>
index d8741fe959c3fc7ddec39ecaf6bfaa7443cb8d38..1073e1570fa7906ac106bb32a5311d7f99054baf 100644 (file)
  * 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 @@ static void read_decode_cache_bcr_arcv2(void)
        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 @@ static void read_decode_cache_bcr_arcv2(void)
 
        cbcr.word = read_aux_reg(ARC_BCR_CLUSTER);
        if (cbcr.fields.c)
-               ioc_exists = 1;
+               ioc_exists = true;
 }
 #endif
 
@@ -190,7 +190,7 @@ void read_decode_cache_bcr(void)
 
        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 @@ void read_decode_cache_bcr(void)
 
        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");
index 7b562556e6bcd9469185fbaa5182752f10a36939..7641978a7b13d7b5bd17cccca06b640499c8b49e 100644 (file)
@@ -26,6 +26,10 @@ int board_early_init_f(void)
        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 @@ int board_mmc_init(bd_t *bis)
                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 (file)
index 0000000..82fe1dd
--- /dev/null
@@ -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>;
+       };
index f6644ee6d16f881be63a6f9661f15a25152942e0..cdfa052c16a9717a97134ecd927b71a20a7ba177 100644 (file)
@@ -54,6 +54,12 @@ config CLK_STM32F
          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
index bcc8f82fb65db2c3e12df6331a2810876971b13d..876c2b816f193ca7e0aa5935bda67cb8f6017395 100644 (file)
@@ -20,6 +20,7 @@ obj-$(CONFIG_CLK_EXYNOS) += exynos/
 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 (file)
index 0000000..c80f90e
--- /dev/null
@@ -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 (file)
index 0000000..813ab71
--- /dev/null
@@ -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_ */