diff mbox

[v4,5/7] clk: zx: add clock support to zx296702

Message ID 1430212691-16009-6-git-send-email-jun.nie@linaro.org (mailing list archive)
State New, archived
Headers show

Commit Message

Jun Nie April 28, 2015, 9:18 a.m. UTC
It adds a clock driver for zx296702 SoC to register the clock tree to
Common Clock Framework.  All the clocks of bus topology and some the
peripheral clocks are ready with this commit. Some missing leaf clocks
for peripherals will be added later when needed.

Signed-off-by: Jun Nie <jun.nie@linaro.org>
---
 drivers/clk/Makefile                       |   1 +
 drivers/clk/zte/Makefile                   |   2 +
 drivers/clk/zte/clk-pll.c                  | 180 ++++++++
 drivers/clk/zte/clk-zx296702.c             | 658 +++++++++++++++++++++++++++++
 drivers/clk/zte/clk.h                      |  30 ++
 include/dt-bindings/clock/zx296702-clock.h | 162 +++++++
 6 files changed, 1033 insertions(+)
 create mode 100644 drivers/clk/zte/Makefile
 create mode 100644 drivers/clk/zte/clk-pll.c
 create mode 100644 drivers/clk/zte/clk-zx296702.c
 create mode 100644 drivers/clk/zte/clk.h
 create mode 100644 include/dt-bindings/clock/zx296702-clock.h

Comments

Arnd Bergmann May 15, 2015, 8:32 p.m. UTC | #1
On Tuesday 28 April 2015 17:18:09 Jun Nie wrote:
> diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
> new file mode 100644
> index 0000000..c5e7ec2
> --- /dev/null
> +++ b/include/dt-bindings/clock/zx296702-clock.h
> @@ -0,0 +1,162 @@
> +/*
> + * Copyright 2014 Linaro Ltd.
> + * Copyright (C) 2014 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
> +#define __DT_BINDINGS_CLOCK_ZX296702_H
> +
> +#define ZX296702_OSC                           0
> +#define ZX296702_PLL_A9                                1
> +#define ZX296702_PLL_A9_350M                   2
> +#define ZX296702_PLL_MAC_1000M                 3
> +#define ZX296702_PLL_MAC_333M                  4
> +#define ZX296702_PLL_MM0_1188M                 5
> +#define ZX296702_PLL_MM0_396M                  6
> +#define ZX296702_PLL_MM0_198M                  7
> +#define ZX296702_PLL_MM1_108M                  8
> +#define ZX296702_PLL_MM1_72M                   9
> 

No objections for this driver going through Mike's tree, but as I said
if you want the dts file to be merged in the same merge window, it would
be best to avoid the header file.

	Arnd
Shawn Guo May 16, 2015, 1:07 p.m. UTC | #2
On Fri, May 15, 2015 at 10:32:14PM +0200, Arnd Bergmann wrote:
> On Tuesday 28 April 2015 17:18:09 Jun Nie wrote:
> > diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
> > new file mode 100644
> > index 0000000..c5e7ec2
> > --- /dev/null
> > +++ b/include/dt-bindings/clock/zx296702-clock.h
> > @@ -0,0 +1,162 @@
> > +/*
> > + * Copyright 2014 Linaro Ltd.
> > + * Copyright (C) 2014 ZTE Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
> > +#define __DT_BINDINGS_CLOCK_ZX296702_H
> > +
> > +#define ZX296702_OSC                           0
> > +#define ZX296702_PLL_A9                                1
> > +#define ZX296702_PLL_A9_350M                   2
> > +#define ZX296702_PLL_MAC_1000M                 3
> > +#define ZX296702_PLL_MAC_333M                  4
> > +#define ZX296702_PLL_MM0_1188M                 5
> > +#define ZX296702_PLL_MM0_396M                  6
> > +#define ZX296702_PLL_MM0_198M                  7
> > +#define ZX296702_PLL_MM1_108M                  8
> > +#define ZX296702_PLL_MM1_72M                   9
> > 
> 
> No objections for this driver going through Mike's tree, but as I said
> if you want the dts file to be merged in the same merge window, it would
> be best to avoid the header file.

Alternatively, we can ask an ACK from Mike or Stephen on this patch, so
that we can merge the whole series through arm-soc tree.  As this patch
adds a SoC specific clock driver and it's quite self-contained and
shouldn't cause non-trivial merge conflicts, Mike/Stephen may be happy
to give an ACK on it.

Shawn
Arnd Bergmann May 16, 2015, 7:13 p.m. UTC | #3
On Saturday 16 May 2015 21:07:57 Shawn Guo wrote:
> On Fri, May 15, 2015 at 10:32:14PM +0200, Arnd Bergmann wrote:
> > On Tuesday 28 April 2015 17:18:09 Jun Nie wrote:
> > > diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
> > > new file mode 100644
> > > index 0000000..c5e7ec2
> > > --- /dev/null
> > > +++ b/include/dt-bindings/clock/zx296702-clock.h
> > > @@ -0,0 +1,162 @@
> > > +/*
> > > + * Copyright 2014 Linaro Ltd.
> > > + * Copyright (C) 2014 ZTE Corporation.
> > > + *
> > > + * This program is free software; you can redistribute it and/or modify
> > > + * it under the terms of the GNU General Public License version 2 as
> > > + * published by the Free Software Foundation.
> > > + */
> > > +
> > > +#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
> > > +#define __DT_BINDINGS_CLOCK_ZX296702_H
> > > +
> > > +#define ZX296702_OSC                           0
> > > +#define ZX296702_PLL_A9                                1
> > > +#define ZX296702_PLL_A9_350M                   2
> > > +#define ZX296702_PLL_MAC_1000M                 3
> > > +#define ZX296702_PLL_MAC_333M                  4
> > > +#define ZX296702_PLL_MM0_1188M                 5
> > > +#define ZX296702_PLL_MM0_396M                  6
> > > +#define ZX296702_PLL_MM0_198M                  7
> > > +#define ZX296702_PLL_MM1_108M                  8
> > > +#define ZX296702_PLL_MM1_72M                   9
> > > 
> > 
> > No objections for this driver going through Mike's tree, but as I said
> > if you want the dts file to be merged in the same merge window, it would
> > be best to avoid the header file.
> 
> Alternatively, we can ask an ACK from Mike or Stephen on this patch, so
> that we can merge the whole series through arm-soc tree.  As this patch
> adds a SoC specific clock driver and it's quite self-contained and
> shouldn't cause non-trivial merge conflicts, Mike/Stephen may be happy
> to give an ACK on it.
> 

Yes, that would work too.

	Arnd
Jun Nie May 18, 2015, 1:18 a.m. UTC | #4
B.R.
Jun


2015-04-28 17:18 GMT+08:00 Jun Nie <jun.nie@linaro.org>:
> It adds a clock driver for zx296702 SoC to register the clock tree to
> Common Clock Framework.  All the clocks of bus topology and some the
> peripheral clocks are ready with this commit. Some missing leaf clocks
> for peripherals will be added later when needed.
>
Mike & Stephen,

Could you help review and ack this patch? So that this patch set can
be merged together in Arnd side.

Jun

> Signed-off-by: Jun Nie <jun.nie@linaro.org>
> ---
>  drivers/clk/Makefile                       |   1 +
>  drivers/clk/zte/Makefile                   |   2 +
>  drivers/clk/zte/clk-pll.c                  | 180 ++++++++
>  drivers/clk/zte/clk-zx296702.c             | 658 +++++++++++++++++++++++++++++
>  drivers/clk/zte/clk.h                      |  30 ++
>  include/dt-bindings/clock/zx296702-clock.h | 162 +++++++
>  6 files changed, 1033 insertions(+)
>  create mode 100644 drivers/clk/zte/Makefile
>  create mode 100644 drivers/clk/zte/clk-pll.c
>  create mode 100644 drivers/clk/zte/clk-zx296702.c
>  create mode 100644 drivers/clk/zte/clk.h
>  create mode 100644 include/dt-bindings/clock/zx296702-clock.h
>
> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
> index 3d00c25..f4c68be 100644
> --- a/drivers/clk/Makefile
> +++ b/drivers/clk/Makefile
> @@ -72,4 +72,5 @@ obj-$(CONFIG_ARCH_OMAP2PLUS)          += ti/
>  obj-$(CONFIG_ARCH_U8500)               += ux500/
>  obj-$(CONFIG_COMMON_CLK_VERSATILE)     += versatile/
>  obj-$(CONFIG_X86)                      += x86/
> +obj-$(CONFIG_ARCH_ZX)                  += zte/
>  obj-$(CONFIG_ARCH_ZYNQ)                        += zynq/
> diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
> new file mode 100644
> index 0000000..95b707c
> --- /dev/null
> +++ b/drivers/clk/zte/Makefile
> @@ -0,0 +1,2 @@
> +obj-y := clk-pll.o
> +obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o
> diff --git a/drivers/clk/zte/clk-pll.c b/drivers/clk/zte/clk-pll.c
> new file mode 100644
> index 0000000..f0ff0cb
> --- /dev/null
> +++ b/drivers/clk/zte/clk-pll.c
> @@ -0,0 +1,180 @@
> +/*
> + * Copyright 2014 Linaro Ltd.
> + * Copyright (C) 2014 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/err.h>
> +#include <linux/io.h>
> +#include <linux/slab.h>
> +#include <linux/spinlock.h>
> +
> +#include "clk.h"
> +
> +#define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw)
> +
> +#define CFG0_CFG1_OFFSET 4
> +#define LOCK_FLAG BIT(30)
> +#define POWER_DOWN BIT(31)
> +
> +static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
> +{
> +       const struct zx_pll_config *config = zx_pll->lookup_table;
> +       int i;
> +
> +       for (i = 0; i < zx_pll->count; i++) {
> +               if (config[i].rate > rate)
> +                       return i > 0 ? i - 1 : 0;
> +
> +               if (config[i].rate == rate)
> +                       return i;
> +       }
> +
> +       return i - 1;
> +}
> +
> +static int hw_to_idx(struct clk_zx_pll *zx_pll)
> +{
> +       const struct zx_pll_config *config = zx_pll->lookup_table;
> +       u32 hw_cfg0, hw_cfg1;
> +       int i;
> +
> +       hw_cfg0 = readl_relaxed(zx_pll->reg_base);
> +       hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
> +
> +       /* For matching the value in lookup table */
> +       hw_cfg0 &= ~LOCK_FLAG;
> +       hw_cfg0 |= POWER_DOWN;
> +
> +       for (i = 0; i < zx_pll->count; i++) {
> +               if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
> +                       return i;
> +       }
> +
> +       return -1;
> +}
> +
> +static unsigned long zx_pll_recalc_rate(struct clk_hw *hw,
> +                                       unsigned long parent_rate)
> +{
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       int idx;
> +
> +       idx = hw_to_idx(zx_pll);
> +       if (unlikely(idx == -1))
> +               return 0;
> +
> +       return zx_pll->lookup_table[idx].rate;
> +}
> +
> +static long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
> +                             unsigned long *prate)
> +{
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       int idx;
> +
> +       idx = rate_to_idx(zx_pll, rate);
> +
> +       return zx_pll->lookup_table[idx].rate;
> +}
> +
> +static int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
> +                          unsigned long parent_rate)
> +{
> +       /* Assume current cpu is not running on current PLL */
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       const struct zx_pll_config *config;
> +       int idx;
> +
> +       idx = rate_to_idx(zx_pll, rate);
> +       config = &zx_pll->lookup_table[idx];
> +
> +       writel_relaxed(config->cfg0, zx_pll->reg_base);
> +       writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET);
> +
> +       return 0;
> +}
> +
> +static int zx_pll_enable(struct clk_hw *hw)
> +{
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       unsigned long timeout = jiffies + msecs_to_jiffies(500);
> +       u32 reg;
> +
> +       reg = readl_relaxed(zx_pll->reg_base);
> +       writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
> +       while (!(readl_relaxed(zx_pll->reg_base) & LOCK_FLAG)) {
> +               if (time_after(jiffies, timeout)) {
> +                       pr_err("clk %s enable timeout\n",
> +                               __clk_get_name(hw->clk));
> +                       break;
> +               }
> +       }
> +
> +       return 0;
> +}
> +
> +static void zx_pll_disable(struct clk_hw *hw)
> +{
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       u32 reg;
> +
> +       reg = readl_relaxed(zx_pll->reg_base);
> +       writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base);
> +}
> +
> +static int zx_pll_is_enabled(struct clk_hw *hw)
> +{
> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> +       u32 reg;
> +
> +       reg = readl_relaxed(zx_pll->reg_base);
> +
> +       return !(reg & POWER_DOWN);
> +}
> +
> +static const struct clk_ops zx_pll_ops = {
> +       .recalc_rate = zx_pll_recalc_rate,
> +       .round_rate = zx_pll_round_rate,
> +       .set_rate = zx_pll_set_rate,
> +       .enable = zx_pll_enable,
> +       .disable = zx_pll_disable,
> +       .is_enabled = zx_pll_is_enabled,
> +};
> +
> +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
> +       unsigned long flags, void __iomem *reg_base,
> +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock)
> +{
> +       struct clk_zx_pll *zx_pll;
> +       struct clk *clk;
> +       struct clk_init_data init;
> +
> +       zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
> +       if (!zx_pll)
> +               return ERR_PTR(-ENOMEM);
> +
> +       init.name = name;
> +       init.ops = &zx_pll_ops;
> +       init.flags = flags;
> +       init.parent_names = (parent_name ? &parent_name : NULL);
> +       init.num_parents = (parent_name ? 1 : 0);
> +
> +       zx_pll->reg_base = reg_base;
> +       zx_pll->lookup_table = lookup_table;
> +       zx_pll->count = count;
> +       zx_pll->lock = lock;
> +       zx_pll->hw.init = &init;
> +
> +       clk = clk_register(NULL, &zx_pll->hw);
> +
> +       if (IS_ERR(clk))
> +               kfree(zx_pll);
> +
> +       return clk;
> +}
> diff --git a/drivers/clk/zte/clk-zx296702.c b/drivers/clk/zte/clk-zx296702.c
> new file mode 100644
> index 0000000..5f4d7ed
> --- /dev/null
> +++ b/drivers/clk/zte/clk-zx296702.c
> @@ -0,0 +1,658 @@
> +/*
> + * Copyright 2014 Linaro Ltd.
> + * Copyright (C) 2014 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#include <linux/clk.h>
> +#include <linux/clk-provider.h>
> +#include <linux/of_address.h>
> +#include <dt-bindings/clock/zx296702-clock.h>
> +#include "clk.h"
> +
> +static DEFINE_SPINLOCK(reg_lock);
> +
> +static void __iomem *topcrm_base;
> +static void __iomem *lsp0crpm_base;
> +static void __iomem *lsp1crpm_base;
> +
> +static struct clk *topclk[ZX296702_TOPCLK_END];
> +static struct clk *lsp0clk[ZX296702_LSP0CLK_END];
> +static struct clk *lsp1clk[ZX296702_LSP1CLK_END];
> +
> +static struct clk_onecell_data topclk_data;
> +static struct clk_onecell_data lsp0clk_data;
> +static struct clk_onecell_data lsp1clk_data;
> +
> +#define CLK_MUX                        (topcrm_base + 0x04)
> +#define CLK_DIV                        (topcrm_base + 0x08)
> +#define CLK_EN0                        (topcrm_base + 0x0c)
> +#define CLK_EN1                        (topcrm_base + 0x10)
> +#define VOU_LOCAL_CLKEN                (topcrm_base + 0x68)
> +#define VOU_LOCAL_CLKSEL       (topcrm_base + 0x70)
> +#define VOU_LOCAL_DIV2_SET     (topcrm_base + 0x74)
> +#define CLK_MUX1               (topcrm_base + 0x8c)
> +
> +#define CLK_SDMMC1             (lsp0crpm_base + 0x0c)
> +
> +#define CLK_UART0              (lsp1crpm_base + 0x20)
> +#define CLK_UART1              (lsp1crpm_base + 0x24)
> +#define CLK_SDMMC0             (lsp1crpm_base + 0x2c)
> +
> +static const struct zx_pll_config pll_a9_config[] = {
> +       { .rate = 700000000, .cfg0 = 0x800405d1, .cfg1 = 0x04555555 },
> +       { .rate = 800000000, .cfg0 = 0x80040691, .cfg1 = 0x04aaaaaa },
> +       { .rate = 900000000, .cfg0 = 0x80040791, .cfg1 = 0x04000000 },
> +       { .rate = 1000000000, .cfg0 = 0x80040851, .cfg1 = 0x04555555 },
> +       { .rate = 1100000000, .cfg0 = 0x80040911, .cfg1 = 0x04aaaaaa },
> +       { .rate = 1200000000, .cfg0 = 0x80040a11, .cfg1 = 0x04000000 },
> +};
> +
> +static const struct clk_div_table main_hlk_div[] = {
> +       { .val = 1, .div = 2, },
> +       { .val = 3, .div = 4, },
> +       { /* sentinel */ }
> +};
> +
> +static const struct clk_div_table a9_as1_aclk_divider[] = {
> +       { .val = 0, .div = 1, },
> +       { .val = 1, .div = 2, },
> +       { .val = 3, .div = 4, },
> +       { /* sentinel */ }
> +};
> +
> +static const struct clk_div_table sec_wclk_divider[] = {
> +       { .val = 0, .div = 1, },
> +       { .val = 1, .div = 2, },
> +       { .val = 3, .div = 4, },
> +       { .val = 5, .div = 6, },
> +       { .val = 7, .div = 8, },
> +       { /* sentinel */ }
> +};
> +
> +static const char const *matrix_aclk_sel[] = {
> +       "pll_mm0_198M",
> +       "osc",
> +       "clk_148M5",
> +       "pll_lsp_104M",
> +};
> +
> +static const char const *a9_wclk_sel[] = {
> +       "pll_a9",
> +       "osc",
> +       "clk_500",
> +       "clk_250",
> +};
> +
> +static const char const *a9_as1_aclk_sel[] = {
> +       "clk_250",
> +       "osc",
> +       "pll_mm0_396M",
> +       "pll_mac_333M",
> +};
> +
> +static const char const *a9_trace_clkin_sel[] = {
> +       "clk_74M25",
> +       "pll_mm1_108M",
> +       "clk_125",
> +       "clk_148M5",
> +};
> +
> +static const char const *decppu_aclk_sel[] = {
> +       "clk_250",
> +       "pll_mm0_198M",
> +       "pll_lsp_104M",
> +       "pll_audio_294M912",
> +};
> +
> +static const char const *vou_main_wclk_sel[] = {
> +       "clk_148M5",
> +       "clk_74M25",
> +       "clk_27",
> +       "pll_mm1_54M",
> +};
> +
> +static const char const *vou_scaler_wclk_sel[] = {
> +       "clk_250",
> +       "pll_mac_333M",
> +       "pll_audio_294M912",
> +       "pll_mm0_198M",
> +};
> +
> +static const char const *r2d_wclk_sel[] = {
> +       "pll_audio_294M912",
> +       "pll_mac_333M",
> +       "pll_a9_350M",
> +       "pll_mm0_396M",
> +};
> +
> +static const char const *ddr_wclk_sel[] = {
> +       "pll_mac_333M",
> +       "pll_ddr_266M",
> +       "pll_audio_294M912",
> +       "pll_mm0_198M",
> +};
> +
> +static const char const *nand_wclk_sel[] = {
> +       "pll_lsp_104M",
> +       "osc",
> +};
> +
> +static const char const *lsp_26_wclk_sel[] = {
> +       "pll_lsp_26M",
> +       "osc",
> +};
> +
> +static const char const *vl0_sel[] = {
> +       "vou_main_channel_div",
> +       "vou_aux_channel_div",
> +};
> +
> +static const char const *hdmi_sel[] = {
> +       "vou_main_channel_wclk",
> +       "vou_aux_channel_wclk",
> +};
> +
> +static const char const *sdmmc0_wclk_sel[] = {
> +       "lsp1_104M_wclk",
> +       "lsp1_26M_wclk",
> +};
> +
> +static const char const *sdmmc1_wclk_sel[] = {
> +       "lsp0_104M_wclk",
> +       "lsp0_26M_wclk",
> +};
> +
> +static const char const *uart_wclk_sel[] = {
> +       "lsp1_104M_wclk",
> +       "lsp1_26M_wclk",
> +};
> +
> +static inline struct clk *zx_divtbl(const char *name, const char *parent,
> +                                   void __iomem *reg, u8 shift, u8 width,
> +                                   const struct clk_div_table *table)
> +{
> +       return clk_register_divider_table(NULL, name, parent, 0, reg, shift,
> +                                         width, 0, table, &reg_lock);
> +}
> +
> +static inline struct clk *zx_div(const char *name, const char *parent,
> +                                void __iomem *reg, u8 shift, u8 width)
> +{
> +       return clk_register_divider(NULL, name, parent, 0,
> +                                   reg, shift, width, 0, &reg_lock);
> +}
> +
> +static inline struct clk *zx_mux(const char *name, const char **parents,
> +               int num_parents, void __iomem *reg, u8 shift, u8 width)
> +{
> +       return clk_register_mux(NULL, name, parents, num_parents,
> +                               0, reg, shift, width, 0, &reg_lock);
> +}
> +
> +static inline struct clk *zx_gate(const char *name, const char *parent,
> +                                 void __iomem *reg, u8 shift)
> +{
> +       return clk_register_gate(NULL, name, parent, CLK_IGNORE_UNUSED,
> +                                reg, shift, 0, &reg_lock);
> +}
> +
> +static void __init zx296702_top_clocks_init(struct device_node *np)
> +{
> +       struct clk **clk = topclk;
> +       int i;
> +
> +       topcrm_base = of_iomap(np, 0);
> +       WARN_ON(!topcrm_base);
> +
> +       clk[ZX296702_OSC] =
> +               clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT,
> +                               30000000);
> +       clk[ZX296702_PLL_A9] =
> +               clk_register_zx_pll("pll_a9", "osc", 0, topcrm_base
> +                               + 0x01c, pll_a9_config,
> +                               ARRAY_SIZE(pll_a9_config), &reg_lock);
> +
> +       /* TODO: pll_a9_350M look like changeble follow a9 pll */
> +       clk[ZX296702_PLL_A9_350M] =
> +               clk_register_fixed_rate(NULL, "pll_a9_350M", "osc", 0,
> +                               350000000);
> +       clk[ZX296702_PLL_MAC_1000M] =
> +               clk_register_fixed_rate(NULL, "pll_mac_1000M", "osc", 0,
> +                               1000000000);
> +       clk[ZX296702_PLL_MAC_333M] =
> +               clk_register_fixed_rate(NULL, "pll_mac_333M",    "osc", 0,
> +                               333000000);
> +       clk[ZX296702_PLL_MM0_1188M] =
> +               clk_register_fixed_rate(NULL, "pll_mm0_1188M", "osc", 0,
> +                               1188000000);
> +       clk[ZX296702_PLL_MM0_396M] =
> +               clk_register_fixed_rate(NULL, "pll_mm0_396M",  "osc", 0,
> +                               396000000);
> +       clk[ZX296702_PLL_MM0_198M] =
> +               clk_register_fixed_rate(NULL, "pll_mm0_198M",  "osc", 0,
> +                               198000000);
> +       clk[ZX296702_PLL_MM1_108M] =
> +               clk_register_fixed_rate(NULL, "pll_mm1_108M",  "osc", 0,
> +                               108000000);
> +       clk[ZX296702_PLL_MM1_72M] =
> +               clk_register_fixed_rate(NULL, "pll_mm1_72M",     "osc", 0,
> +                               72000000);
> +       clk[ZX296702_PLL_MM1_54M] =
> +               clk_register_fixed_rate(NULL, "pll_mm1_54M",     "osc", 0,
> +                               54000000);
> +       clk[ZX296702_PLL_LSP_104M] =
> +               clk_register_fixed_rate(NULL, "pll_lsp_104M",  "osc", 0,
> +                               104000000);
> +       clk[ZX296702_PLL_LSP_26M] =
> +               clk_register_fixed_rate(NULL, "pll_lsp_26M",     "osc", 0,
> +                               26000000);
> +       clk[ZX296702_PLL_DDR_266M] =
> +               clk_register_fixed_rate(NULL, "pll_ddr_266M",    "osc", 0,
> +                               266000000);
> +       clk[ZX296702_PLL_AUDIO_294M912] =
> +               clk_register_fixed_rate(NULL, "pll_audio_294M912", "osc", 0,
> +                               294912000);
> +
> +       /* bus clock */
> +       clk[ZX296702_MATRIX_ACLK] =
> +               zx_mux("matrix_aclk", matrix_aclk_sel,
> +                               ARRAY_SIZE(matrix_aclk_sel), CLK_MUX, 2, 2);
> +       clk[ZX296702_MAIN_HCLK] =
> +               zx_divtbl("main_hclk", "matrix_aclk", CLK_DIV, 0, 2,
> +                               main_hlk_div);
> +       clk[ZX296702_MAIN_PCLK] =
> +               zx_divtbl("main_pclk", "matrix_aclk", CLK_DIV, 2, 2,
> +                               main_hlk_div);
> +
> +       /* cpu clock */
> +       clk[ZX296702_CLK_500] =
> +               clk_register_fixed_factor(NULL, "clk_500", "pll_mac_1000M", 0,
> +                               1, 2);
> +       clk[ZX296702_CLK_250] =
> +               clk_register_fixed_factor(NULL, "clk_250", "pll_mac_1000M", 0,
> +                               1, 4);
> +       clk[ZX296702_CLK_125] =
> +               clk_register_fixed_factor(NULL, "clk_125", "clk_250", 0, 1, 2);
> +       clk[ZX296702_CLK_148M5] =
> +               clk_register_fixed_factor(NULL, "clk_148M5", "pll_mm0_1188M", 0,
> +                               1, 8);
> +       clk[ZX296702_CLK_74M25] =
> +               clk_register_fixed_factor(NULL, "clk_74M25", "pll_mm0_1188M", 0,
> +                               1, 16);
> +       clk[ZX296702_A9_WCLK] =
> +               zx_mux("a9_wclk", a9_wclk_sel, ARRAY_SIZE(a9_wclk_sel), CLK_MUX,
> +                               0, 2);
> +       clk[ZX296702_A9_AS1_ACLK_MUX] =
> +               zx_mux("a9_as1_aclk_mux", a9_as1_aclk_sel,
> +                               ARRAY_SIZE(a9_as1_aclk_sel), CLK_MUX, 4, 2);
> +       clk[ZX296702_A9_TRACE_CLKIN_MUX] =
> +               zx_mux("a9_trace_clkin_mux", a9_trace_clkin_sel,
> +                               ARRAY_SIZE(a9_trace_clkin_sel), CLK_MUX1, 0, 2);
> +       clk[ZX296702_A9_AS1_ACLK_DIV] =
> +               zx_divtbl("a9_as1_aclk_div", "a9_as1_aclk_mux", CLK_DIV, 4, 2,
> +                               a9_as1_aclk_divider);
> +
> +       /* multi-media clock */
> +       clk[ZX296702_CLK_2] =
> +               clk_register_fixed_factor(NULL, "clk_2", "pll_mm1_72M", 0,
> +                               1, 36);
> +       clk[ZX296702_CLK_27] =
> +               clk_register_fixed_factor(NULL, "clk_27", "pll_mm1_54M", 0,
> +                               1, 2);
> +       clk[ZX296702_DECPPU_ACLK_MUX] =
> +               zx_mux("decppu_aclk_mux", decppu_aclk_sel,
> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 6, 2);
> +       clk[ZX296702_PPU_ACLK_MUX] =
> +               zx_mux("ppu_aclk_mux", decppu_aclk_sel,
> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 8, 2);
> +       clk[ZX296702_MALI400_ACLK_MUX] =
> +               zx_mux("mali400_aclk_mux", decppu_aclk_sel,
> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 12, 2);
> +       clk[ZX296702_VOU_ACLK_MUX] =
> +               zx_mux("vou_aclk_mux", decppu_aclk_sel,
> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 10, 2);
> +       clk[ZX296702_VOU_MAIN_WCLK_MUX] =
> +               zx_mux("vou_main_wclk_mux", vou_main_wclk_sel,
> +                               ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 14, 2);
> +       clk[ZX296702_VOU_AUX_WCLK_MUX] =
> +               zx_mux("vou_aux_wclk_mux", vou_main_wclk_sel,
> +                               ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 16, 2);
> +       clk[ZX296702_VOU_SCALER_WCLK_MUX] =
> +               zx_mux("vou_scaler_wclk_mux", vou_scaler_wclk_sel,
> +                               ARRAY_SIZE(vou_scaler_wclk_sel), CLK_MUX,
> +                               18, 2);
> +       clk[ZX296702_R2D_ACLK_MUX] =
> +               zx_mux("r2d_aclk_mux", decppu_aclk_sel,
> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 20, 2);
> +       clk[ZX296702_R2D_WCLK_MUX] =
> +               zx_mux("r2d_wclk_mux", r2d_wclk_sel,
> +                               ARRAY_SIZE(r2d_wclk_sel), CLK_MUX, 22, 2);
> +
> +       /* other clock */
> +       clk[ZX296702_CLK_50] =
> +               clk_register_fixed_factor(NULL, "clk_50", "pll_mac_1000M",
> +                               0, 1, 20);
> +       clk[ZX296702_CLK_25] =
> +               clk_register_fixed_factor(NULL, "clk_25", "pll_mac_1000M",
> +                               0, 1, 40);
> +       clk[ZX296702_CLK_12] =
> +               clk_register_fixed_factor(NULL, "clk_12", "pll_mm1_72M",
> +                               0, 1, 6);
> +       clk[ZX296702_CLK_16M384] =
> +               clk_register_fixed_factor(NULL, "clk_16M384",
> +                               "pll_audio_294M912", 0, 1, 18);
> +       clk[ZX296702_CLK_32K768] =
> +               clk_register_fixed_factor(NULL, "clk_32K768", "clk_16M384",
> +                               0, 1, 500);
> +       clk[ZX296702_SEC_WCLK_DIV] =
> +               zx_divtbl("sec_wclk_div", "pll_lsp_104M", CLK_DIV, 6, 3,
> +                               sec_wclk_divider);
> +       clk[ZX296702_DDR_WCLK_MUX] =
> +               zx_mux("ddr_wclk_mux", ddr_wclk_sel,
> +                               ARRAY_SIZE(ddr_wclk_sel), CLK_MUX, 24, 2);
> +       clk[ZX296702_NAND_WCLK_MUX] =
> +               zx_mux("nand_wclk_mux", nand_wclk_sel,
> +                               ARRAY_SIZE(nand_wclk_sel), CLK_MUX, 24, 2);
> +       clk[ZX296702_LSP_26_WCLK_MUX] =
> +               zx_mux("lsp_26_wclk_mux", lsp_26_wclk_sel,
> +                               ARRAY_SIZE(lsp_26_wclk_sel), CLK_MUX, 27, 1);
> +
> +       /* gates */
> +       clk[ZX296702_A9_AS0_ACLK] =
> +               zx_gate("a9_as0_aclk",  "matrix_aclk",          CLK_EN0, 0);
> +       clk[ZX296702_A9_AS1_ACLK] =
> +               zx_gate("a9_as1_aclk",  "a9_as1_aclk_div",      CLK_EN0, 1);
> +       clk[ZX296702_A9_TRACE_CLKIN] =
> +               zx_gate("a9_trace_clkin", "a9_trace_clkin_mux", CLK_EN0, 2);
> +       clk[ZX296702_DECPPU_AXI_M_ACLK] =
> +               zx_gate("decppu_axi_m_aclk", "decppu_aclk_mux", CLK_EN0, 3);
> +       clk[ZX296702_DECPPU_AHB_S_HCLK] =
> +               zx_gate("decppu_ahb_s_hclk",    "main_hclk",    CLK_EN0, 4);
> +       clk[ZX296702_PPU_AXI_M_ACLK] =
> +               zx_gate("ppu_axi_m_aclk",       "ppu_aclk_mux", CLK_EN0, 5);
> +       clk[ZX296702_PPU_AHB_S_HCLK] =
> +               zx_gate("ppu_ahb_s_hclk",       "main_hclk",    CLK_EN0, 6);
> +       clk[ZX296702_VOU_AXI_M_ACLK] =
> +               zx_gate("vou_axi_m_aclk",       "vou_aclk_mux", CLK_EN0, 7);
> +       clk[ZX296702_VOU_APB_PCLK] =
> +               zx_gate("vou_apb_pclk", "main_pclk",            CLK_EN0, 8);
> +       clk[ZX296702_VOU_MAIN_CHANNEL_WCLK] =
> +               zx_gate("vou_main_channel_wclk", "vou_main_wclk_mux",
> +                               CLK_EN0, 9);
> +       clk[ZX296702_VOU_AUX_CHANNEL_WCLK] =
> +               zx_gate("vou_aux_channel_wclk", "vou_aux_wclk_mux",
> +                               CLK_EN0, 10);
> +       clk[ZX296702_VOU_HDMI_OSCLK_CEC] =
> +               zx_gate("vou_hdmi_osclk_cec", "clk_2",          CLK_EN0, 11);
> +       clk[ZX296702_VOU_SCALER_WCLK] =
> +               zx_gate("vou_scaler_wclk", "vou_scaler_wclk_mux", CLK_EN0, 12);
> +       clk[ZX296702_MALI400_AXI_M_ACLK] =
> +               zx_gate("mali400_axi_m_aclk", "mali400_aclk_mux", CLK_EN0, 13);
> +       clk[ZX296702_MALI400_APB_PCLK] =
> +               zx_gate("mali400_apb_pclk",     "main_pclk",    CLK_EN0, 14);
> +       clk[ZX296702_R2D_WCLK] =
> +               zx_gate("r2d_wclk",             "r2d_wclk_mux", CLK_EN0, 15);
> +       clk[ZX296702_R2D_AXI_M_ACLK] =
> +               zx_gate("r2d_axi_m_aclk",       "r2d_aclk_mux", CLK_EN0, 16);
> +       clk[ZX296702_R2D_AHB_HCLK] =
> +               zx_gate("r2d_ahb_hclk",         "main_hclk",    CLK_EN0, 17);
> +       clk[ZX296702_DDR3_AXI_S0_ACLK] =
> +               zx_gate("ddr3_axi_s0_aclk",     "matrix_aclk",  CLK_EN0, 18);
> +       clk[ZX296702_DDR3_APB_PCLK] =
> +               zx_gate("ddr3_apb_pclk",        "main_pclk",    CLK_EN0, 19);
> +       clk[ZX296702_DDR3_WCLK] =
> +               zx_gate("ddr3_wclk",            "ddr_wclk_mux", CLK_EN0, 20);
> +       clk[ZX296702_USB20_0_AHB_HCLK] =
> +               zx_gate("usb20_0_ahb_hclk",     "main_hclk",    CLK_EN0, 21);
> +       clk[ZX296702_USB20_0_EXTREFCLK] =
> +               zx_gate("usb20_0_extrefclk",    "clk_12",       CLK_EN0, 22);
> +       clk[ZX296702_USB20_1_AHB_HCLK] =
> +               zx_gate("usb20_1_ahb_hclk",     "main_hclk",    CLK_EN0, 23);
> +       clk[ZX296702_USB20_1_EXTREFCLK] =
> +               zx_gate("usb20_1_extrefclk",    "clk_12",       CLK_EN0, 24);
> +       clk[ZX296702_USB20_2_AHB_HCLK] =
> +               zx_gate("usb20_2_ahb_hclk",     "main_hclk",    CLK_EN0, 25);
> +       clk[ZX296702_USB20_2_EXTREFCLK] =
> +               zx_gate("usb20_2_extrefclk",    "clk_12",       CLK_EN0, 26);
> +       clk[ZX296702_GMAC_AXI_M_ACLK] =
> +               zx_gate("gmac_axi_m_aclk",      "matrix_aclk",  CLK_EN0, 27);
> +       clk[ZX296702_GMAC_APB_PCLK] =
> +               zx_gate("gmac_apb_pclk",        "main_pclk",    CLK_EN0, 28);
> +       clk[ZX296702_GMAC_125_CLKIN] =
> +               zx_gate("gmac_125_clkin",       "clk_125",      CLK_EN0, 29);
> +       clk[ZX296702_GMAC_RMII_CLKIN] =
> +               zx_gate("gmac_rmii_clkin",      "clk_50",       CLK_EN0, 30);
> +       clk[ZX296702_GMAC_25M_CLK] =
> +               zx_gate("gmac_25M_clk",         "clk_25",       CLK_EN0, 31);
> +       clk[ZX296702_NANDFLASH_AHB_HCLK] =
> +               zx_gate("nandflash_ahb_hclk", "main_hclk",      CLK_EN1, 0);
> +       clk[ZX296702_NANDFLASH_WCLK] =
> +               zx_gate("nandflash_wclk",     "nand_wclk_mux",  CLK_EN1, 1);
> +       clk[ZX296702_LSP0_APB_PCLK] =
> +               zx_gate("lsp0_apb_pclk",        "main_pclk",    CLK_EN1, 2);
> +       clk[ZX296702_LSP0_AHB_HCLK] =
> +               zx_gate("lsp0_ahb_hclk",        "main_hclk",    CLK_EN1, 3);
> +       clk[ZX296702_LSP0_26M_WCLK] =
> +               zx_gate("lsp0_26M_wclk",   "lsp_26_wclk_mux",   CLK_EN1, 4);
> +       clk[ZX296702_LSP0_104M_WCLK] =
> +               zx_gate("lsp0_104M_wclk",       "pll_lsp_104M", CLK_EN1, 5);
> +       clk[ZX296702_LSP0_16M384_WCLK] =
> +               zx_gate("lsp0_16M384_wclk",     "clk_16M384",   CLK_EN1, 6);
> +       clk[ZX296702_LSP1_APB_PCLK] =
> +               zx_gate("lsp1_apb_pclk",        "main_pclk",    CLK_EN1, 7);
> +       /* FIXME: wclk enable bit is bit8. We hack it as reserved 31 for
> +        * UART does not work after parent clk is disabled/enabled */
> +       clk[ZX296702_LSP1_26M_WCLK] =
> +               zx_gate("lsp1_26M_wclk",     "lsp_26_wclk_mux", CLK_EN1, 31);
> +       clk[ZX296702_LSP1_104M_WCLK] =
> +               zx_gate("lsp1_104M_wclk",    "pll_lsp_104M",    CLK_EN1, 9);
> +       clk[ZX296702_LSP1_32K_CLK] =
> +               zx_gate("lsp1_32K_clk", "clk_32K768",           CLK_EN1, 10);
> +       clk[ZX296702_AON_HCLK] =
> +               zx_gate("aon_hclk",             "main_hclk",    CLK_EN1, 11);
> +       clk[ZX296702_SYS_CTRL_PCLK] =
> +               zx_gate("sys_ctrl_pclk",        "main_pclk",    CLK_EN1, 12);
> +       clk[ZX296702_DMA_PCLK] =
> +               zx_gate("dma_pclk",             "main_pclk",    CLK_EN1, 13);
> +       clk[ZX296702_DMA_ACLK] =
> +               zx_gate("dma_aclk",             "matrix_aclk",  CLK_EN1, 14);
> +       clk[ZX296702_SEC_HCLK] =
> +               zx_gate("sec_hclk",             "main_hclk",    CLK_EN1, 15);
> +       clk[ZX296702_AES_WCLK] =
> +               zx_gate("aes_wclk",             "sec_wclk_div", CLK_EN1, 16);
> +       clk[ZX296702_DES_WCLK] =
> +               zx_gate("des_wclk",             "sec_wclk_div", CLK_EN1, 17);
> +       clk[ZX296702_IRAM_ACLK] =
> +               zx_gate("iram_aclk",            "matrix_aclk",  CLK_EN1, 18);
> +       clk[ZX296702_IROM_ACLK] =
> +               zx_gate("irom_aclk",            "matrix_aclk",  CLK_EN1, 19);
> +       clk[ZX296702_BOOT_CTRL_HCLK] =
> +               zx_gate("boot_ctrl_hclk",       "main_hclk",    CLK_EN1, 20);
> +       clk[ZX296702_EFUSE_CLK_30] =
> +               zx_gate("efuse_clk_30", "osc",                  CLK_EN1, 21);
> +
> +       /* TODO: add VOU Local clocks */
> +       clk[ZX296702_VOU_MAIN_CHANNEL_DIV] =
> +               zx_div("vou_main_channel_div", "vou_main_channel_wclk",
> +                               VOU_LOCAL_DIV2_SET, 1, 1);
> +       clk[ZX296702_VOU_AUX_CHANNEL_DIV] =
> +               zx_div("vou_aux_channel_div", "vou_aux_channel_wclk",
> +                               VOU_LOCAL_DIV2_SET, 0, 1);
> +       clk[ZX296702_VOU_TV_ENC_HD_DIV] =
> +               zx_div("vou_tv_enc_hd_div", "vou_tv_enc_hd_mux",
> +                               VOU_LOCAL_DIV2_SET, 3, 1);
> +       clk[ZX296702_VOU_TV_ENC_SD_DIV] =
> +               zx_div("vou_tv_enc_sd_div", "vou_tv_enc_sd_mux",
> +                               VOU_LOCAL_DIV2_SET, 2, 1);
> +       clk[ZX296702_VL0_MUX] =
> +               zx_mux("vl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 8, 1);
> +       clk[ZX296702_VL1_MUX] =
> +               zx_mux("vl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 9, 1);
> +       clk[ZX296702_VL2_MUX] =
> +               zx_mux("vl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 10, 1);
> +       clk[ZX296702_GL0_MUX] =
> +               zx_mux("gl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 5, 1);
> +       clk[ZX296702_GL1_MUX] =
> +               zx_mux("gl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 6, 1);
> +       clk[ZX296702_GL2_MUX] =
> +               zx_mux("gl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 7, 1);
> +       clk[ZX296702_WB_MUX] =
> +               zx_mux("wb_mux",  vl0_sel, ARRAY_SIZE(vl0_sel),
> +                               VOU_LOCAL_CLKSEL, 11, 1);
> +       clk[ZX296702_HDMI_MUX] =
> +               zx_mux("hdmi_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
> +                               VOU_LOCAL_CLKSEL, 4, 1);
> +       clk[ZX296702_VOU_TV_ENC_HD_MUX] =
> +               zx_mux("vou_tv_enc_hd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
> +                               VOU_LOCAL_CLKSEL, 3, 1);
> +       clk[ZX296702_VOU_TV_ENC_SD_MUX] =
> +               zx_mux("vou_tv_enc_sd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
> +                               VOU_LOCAL_CLKSEL, 2, 1);
> +       clk[ZX296702_VL0_CLK] =
> +               zx_gate("vl0_clk", "vl0_mux", VOU_LOCAL_CLKEN, 8);
> +       clk[ZX296702_VL1_CLK] =
> +               zx_gate("vl1_clk", "vl1_mux", VOU_LOCAL_CLKEN, 9);
> +       clk[ZX296702_VL2_CLK] =
> +               zx_gate("vl2_clk", "vl2_mux", VOU_LOCAL_CLKEN, 10);
> +       clk[ZX296702_GL0_CLK] =
> +               zx_gate("gl0_clk", "gl0_mux", VOU_LOCAL_CLKEN, 5);
> +       clk[ZX296702_GL1_CLK] =
> +               zx_gate("gl1_clk", "gl1_mux", VOU_LOCAL_CLKEN, 6);
> +       clk[ZX296702_GL2_CLK] =
> +               zx_gate("gl2_clk", "gl2_mux", VOU_LOCAL_CLKEN, 7);
> +       clk[ZX296702_WB_CLK] =
> +               zx_gate("wb_clk", "wb_mux", VOU_LOCAL_CLKEN, 11);
> +       clk[ZX296702_CL_CLK] =
> +               zx_gate("cl_clk", "vou_main_channel_div", VOU_LOCAL_CLKEN, 12);
> +       clk[ZX296702_MAIN_MIX_CLK] =
> +               zx_gate("main_mix_clk", "vou_main_channel_div",
> +                               VOU_LOCAL_CLKEN, 4);
> +       clk[ZX296702_AUX_MIX_CLK] =
> +               zx_gate("aux_mix_clk", "vou_aux_channel_div",
> +                               VOU_LOCAL_CLKEN, 3);
> +       clk[ZX296702_HDMI_CLK] =
> +               zx_gate("hdmi_clk", "hdmi_mux", VOU_LOCAL_CLKEN, 2);
> +       clk[ZX296702_VOU_TV_ENC_HD_DAC_CLK] =
> +               zx_gate("vou_tv_enc_hd_dac_clk", "vou_tv_enc_hd_div",
> +                               VOU_LOCAL_CLKEN, 1);
> +       clk[ZX296702_VOU_TV_ENC_SD_DAC_CLK] =
> +               zx_gate("vou_tv_enc_sd_dac_clk", "vou_tv_enc_sd_div",
> +                               VOU_LOCAL_CLKEN, 0);
> +
> +       /* CA9 PERIPHCLK = a9_wclk / 2 */
> +       clk[ZX296702_A9_PERIPHCLK] =
> +               clk_register_fixed_factor(NULL, "a9_periphclk", "a9_wclk",
> +                               0, 1, 2);
> +
> +       for (i = 0; i < ARRAY_SIZE(topclk); i++) {
> +               if (IS_ERR(clk[i])) {
> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
> +                               i, PTR_ERR(clk[i]));
> +                       return;
> +               }
> +       }
> +
> +       topclk_data.clks = topclk;
> +       topclk_data.clk_num = ARRAY_SIZE(topclk);
> +       of_clk_add_provider(np, of_clk_src_onecell_get, &topclk_data);
> +}
> +CLK_OF_DECLARE(zx296702_top_clk, "zte,zx296702-topcrm-clk",
> +               zx296702_top_clocks_init);
> +
> +static void __init zx296702_lsp0_clocks_init(struct device_node *np)
> +{
> +       struct clk **clk = lsp0clk;
> +       int i;
> +
> +       lsp0crpm_base = of_iomap(np, 0);
> +       WARN_ON(!lsp0crpm_base);
> +
> +       /* SDMMC1 */
> +       clk[ZX296702_SDMMC1_WCLK_MUX] =
> +               zx_mux("sdmmc1_wclk_mux", sdmmc1_wclk_sel,
> +                               ARRAY_SIZE(sdmmc1_wclk_sel), CLK_SDMMC1, 4, 1);
> +       clk[ZX296702_SDMMC1_WCLK_DIV] =
> +               zx_div("sdmmc1_wclk_div", "sdmmc1_wclk_mux", CLK_SDMMC1, 12, 4);
> +       clk[ZX296702_SDMMC1_WCLK] =
> +               zx_gate("sdmmc1_wclk", "sdmmc1_wclk_div", CLK_SDMMC1, 1);
> +       clk[ZX296702_SDMMC1_PCLK] =
> +               zx_gate("sdmmc1_pclk", "lsp1_apb_pclk", CLK_SDMMC1, 0);
> +
> +       for (i = 0; i < ARRAY_SIZE(lsp0clk); i++) {
> +               if (IS_ERR(clk[i])) {
> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
> +                               i, PTR_ERR(clk[i]));
> +                       return;
> +               }
> +       }
> +
> +       lsp0clk_data.clks = lsp0clk;
> +       lsp0clk_data.clk_num = ARRAY_SIZE(lsp0clk);
> +       of_clk_add_provider(np, of_clk_src_onecell_get, &lsp0clk_data);
> +}
> +CLK_OF_DECLARE(zx296702_lsp0_clk, "zte,zx296702-lsp0crpm-clk",
> +               zx296702_lsp0_clocks_init);
> +
> +static void __init zx296702_lsp1_clocks_init(struct device_node *np)
> +{
> +       struct clk **clk = lsp1clk;
> +       int i;
> +
> +       lsp1crpm_base = of_iomap(np, 0);
> +       WARN_ON(!lsp1crpm_base);
> +
> +       /* UART0 */
> +       clk[ZX296702_UART0_WCLK_MUX] =
> +               zx_mux("uart0_wclk_mux", uart_wclk_sel,
> +                               ARRAY_SIZE(uart_wclk_sel), CLK_UART0, 4, 1);
> +       /* FIXME: uart wclk enable bit is bit1 in. We hack it as reserved 31 for
> +        * UART does not work after parent clk is disabled/enabled */
> +       clk[ZX296702_UART0_WCLK] =
> +               zx_gate("uart0_wclk", "uart0_wclk_mux", CLK_UART0, 31);
> +       clk[ZX296702_UART0_PCLK] =
> +               zx_gate("uart0_pclk", "lsp1_apb_pclk", CLK_UART0, 0);
> +
> +       /* UART1 */
> +       clk[ZX296702_UART1_WCLK_MUX] =
> +               zx_mux("uart1_wclk_mux", uart_wclk_sel,
> +                               ARRAY_SIZE(uart_wclk_sel), CLK_UART1, 4, 1);
> +       clk[ZX296702_UART1_WCLK] =
> +               zx_gate("uart1_wclk", "uart1_wclk_mux", CLK_UART1, 1);
> +       clk[ZX296702_UART1_PCLK] =
> +               zx_gate("uart1_pclk", "lsp1_apb_pclk", CLK_UART1, 0);
> +
> +       /* SDMMC0 */
> +       clk[ZX296702_SDMMC0_WCLK_MUX] =
> +               zx_mux("sdmmc0_wclk_mux", sdmmc0_wclk_sel,
> +                               ARRAY_SIZE(sdmmc0_wclk_sel), CLK_SDMMC0, 4, 1);
> +       clk[ZX296702_SDMMC0_WCLK_DIV] =
> +               zx_div("sdmmc0_wclk_div", "sdmmc0_wclk_mux", CLK_SDMMC0, 12, 4);
> +       clk[ZX296702_SDMMC0_WCLK] =
> +               zx_gate("sdmmc0_wclk", "sdmmc0_wclk_div", CLK_SDMMC0, 1);
> +       clk[ZX296702_SDMMC0_PCLK] =
> +               zx_gate("sdmmc0_pclk", "lsp1_apb_pclk", CLK_SDMMC0, 0);
> +
> +       for (i = 0; i < ARRAY_SIZE(lsp1clk); i++) {
> +               if (IS_ERR(clk[i])) {
> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
> +                               i, PTR_ERR(clk[i]));
> +                       return;
> +               }
> +       }
> +
> +       lsp1clk_data.clks = lsp1clk;
> +       lsp1clk_data.clk_num = ARRAY_SIZE(lsp1clk);
> +       of_clk_add_provider(np, of_clk_src_onecell_get, &lsp1clk_data);
> +}
> +CLK_OF_DECLARE(zx296702_lsp1_clk, "zte,zx296702-lsp1crpm-clk",
> +               zx296702_lsp1_clocks_init);
> diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
> new file mode 100644
> index 0000000..419d93b
> --- /dev/null
> +++ b/drivers/clk/zte/clk.h
> @@ -0,0 +1,30 @@
> +/*
> + * Copyright 2014 Linaro Ltd.
> + * Copyright (C) 2014 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __ZTE_CLK_H
> +#define __ZTE_CLK_H
> +
> +struct zx_pll_config {
> +       unsigned long rate;
> +       u32 cfg0;
> +       u32 cfg1;
> +};
> +
> +struct clk_zx_pll {
> +       struct clk_hw hw;
> +       void __iomem *reg_base;
> +       const struct zx_pll_config *lookup_table; /* order by rate asc */
> +       int count;
> +       spinlock_t *lock;
> +};
> +
> +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
> +       unsigned long flags, void __iomem *reg_base,
> +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
> +#endif
> diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
> new file mode 100644
> index 0000000..c5e7ec2
> --- /dev/null
> +++ b/include/dt-bindings/clock/zx296702-clock.h
> @@ -0,0 +1,162 @@
> +/*
> + * Copyright 2014 Linaro Ltd.
> + * Copyright (C) 2014 ZTE Corporation.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 as
> + * published by the Free Software Foundation.
> + */
> +
> +#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
> +#define __DT_BINDINGS_CLOCK_ZX296702_H
> +
> +#define ZX296702_OSC                           0
> +#define ZX296702_PLL_A9                                1
> +#define ZX296702_PLL_A9_350M                   2
> +#define ZX296702_PLL_MAC_1000M                 3
> +#define ZX296702_PLL_MAC_333M                  4
> +#define ZX296702_PLL_MM0_1188M                 5
> +#define ZX296702_PLL_MM0_396M                  6
> +#define ZX296702_PLL_MM0_198M                  7
> +#define ZX296702_PLL_MM1_108M                  8
> +#define ZX296702_PLL_MM1_72M                   9
> +#define ZX296702_PLL_MM1_54M                   10
> +#define ZX296702_PLL_LSP_104M                  11
> +#define ZX296702_PLL_LSP_26M                   12
> +#define ZX296702_PLL_AUDIO_294M912             13
> +#define ZX296702_PLL_DDR_266M                  14
> +#define ZX296702_CLK_148M5                     15
> +#define ZX296702_MATRIX_ACLK                   16
> +#define ZX296702_MAIN_HCLK                     17
> +#define ZX296702_MAIN_PCLK                     18
> +#define ZX296702_CLK_500                       19
> +#define ZX296702_CLK_250                       20
> +#define ZX296702_CLK_125                       21
> +#define ZX296702_CLK_74M25                     22
> +#define ZX296702_A9_WCLK                       23
> +#define ZX296702_A9_AS1_ACLK_MUX               24
> +#define ZX296702_A9_TRACE_CLKIN_MUX            25
> +#define ZX296702_A9_AS1_ACLK_DIV               26
> +#define ZX296702_CLK_2                         27
> +#define ZX296702_CLK_27                                28
> +#define ZX296702_DECPPU_ACLK_MUX               29
> +#define ZX296702_PPU_ACLK_MUX                  30
> +#define ZX296702_MALI400_ACLK_MUX              31
> +#define ZX296702_VOU_ACLK_MUX                  32
> +#define ZX296702_VOU_MAIN_WCLK_MUX             33
> +#define ZX296702_VOU_AUX_WCLK_MUX              34
> +#define ZX296702_VOU_SCALER_WCLK_MUX           35
> +#define ZX296702_R2D_ACLK_MUX                  36
> +#define ZX296702_R2D_WCLK_MUX                  37
> +#define ZX296702_CLK_50                                38
> +#define ZX296702_CLK_25                                39
> +#define ZX296702_CLK_12                                40
> +#define ZX296702_CLK_16M384                    41
> +#define ZX296702_CLK_32K768                    42
> +#define ZX296702_SEC_WCLK_DIV                  43
> +#define ZX296702_DDR_WCLK_MUX                  44
> +#define ZX296702_NAND_WCLK_MUX                 45
> +#define ZX296702_LSP_26_WCLK_MUX               46
> +#define ZX296702_A9_AS0_ACLK                   47
> +#define ZX296702_A9_AS1_ACLK                   48
> +#define ZX296702_A9_TRACE_CLKIN                        49
> +#define ZX296702_DECPPU_AXI_M_ACLK             50
> +#define ZX296702_DECPPU_AHB_S_HCLK             51
> +#define ZX296702_PPU_AXI_M_ACLK                        52
> +#define ZX296702_PPU_AHB_S_HCLK                        53
> +#define ZX296702_VOU_AXI_M_ACLK                        54
> +#define ZX296702_VOU_APB_PCLK                  55
> +#define ZX296702_VOU_MAIN_CHANNEL_WCLK         56
> +#define ZX296702_VOU_AUX_CHANNEL_WCLK          57
> +#define ZX296702_VOU_HDMI_OSCLK_CEC            58
> +#define ZX296702_VOU_SCALER_WCLK               59
> +#define ZX296702_MALI400_AXI_M_ACLK            60
> +#define ZX296702_MALI400_APB_PCLK              61
> +#define ZX296702_R2D_WCLK                      62
> +#define ZX296702_R2D_AXI_M_ACLK                        63
> +#define ZX296702_R2D_AHB_HCLK                  64
> +#define ZX296702_DDR3_AXI_S0_ACLK              65
> +#define ZX296702_DDR3_APB_PCLK                 66
> +#define ZX296702_DDR3_WCLK                     67
> +#define ZX296702_USB20_0_AHB_HCLK              68
> +#define ZX296702_USB20_0_EXTREFCLK             69
> +#define ZX296702_USB20_1_AHB_HCLK              70
> +#define ZX296702_USB20_1_EXTREFCLK             71
> +#define ZX296702_USB20_2_AHB_HCLK              72
> +#define ZX296702_USB20_2_EXTREFCLK             73
> +#define ZX296702_GMAC_AXI_M_ACLK               74
> +#define ZX296702_GMAC_APB_PCLK                 75
> +#define ZX296702_GMAC_125_CLKIN                        76
> +#define ZX296702_GMAC_RMII_CLKIN               77
> +#define ZX296702_GMAC_25M_CLK                  78
> +#define ZX296702_NANDFLASH_AHB_HCLK            79
> +#define ZX296702_NANDFLASH_WCLK                        80
> +#define ZX296702_LSP0_APB_PCLK                 81
> +#define ZX296702_LSP0_AHB_HCLK                 82
> +#define ZX296702_LSP0_26M_WCLK                 83
> +#define ZX296702_LSP0_104M_WCLK                        84
> +#define ZX296702_LSP0_16M384_WCLK              85
> +#define ZX296702_LSP1_APB_PCLK                 86
> +#define ZX296702_LSP1_26M_WCLK                 87
> +#define ZX296702_LSP1_104M_WCLK                        88
> +#define ZX296702_LSP1_32K_CLK                  89
> +#define ZX296702_AON_HCLK                      90
> +#define ZX296702_SYS_CTRL_PCLK                 91
> +#define ZX296702_DMA_PCLK                      92
> +#define ZX296702_DMA_ACLK                      93
> +#define ZX296702_SEC_HCLK                      94
> +#define ZX296702_AES_WCLK                      95
> +#define ZX296702_DES_WCLK                      96
> +#define ZX296702_IRAM_ACLK                     97
> +#define ZX296702_IROM_ACLK                     98
> +#define ZX296702_BOOT_CTRL_HCLK                        99
> +#define ZX296702_EFUSE_CLK_30                  100
> +#define ZX296702_VOU_MAIN_CHANNEL_DIV          101
> +#define ZX296702_VOU_AUX_CHANNEL_DIV           102
> +#define ZX296702_VOU_TV_ENC_HD_DIV             103
> +#define ZX296702_VOU_TV_ENC_SD_DIV             104
> +#define ZX296702_VL0_MUX                       105
> +#define ZX296702_VL1_MUX                       106
> +#define ZX296702_VL2_MUX                       107
> +#define ZX296702_GL0_MUX                       108
> +#define ZX296702_GL1_MUX                       109
> +#define ZX296702_GL2_MUX                       110
> +#define ZX296702_WB_MUX                                111
> +#define ZX296702_HDMI_MUX                      112
> +#define ZX296702_VOU_TV_ENC_HD_MUX             113
> +#define ZX296702_VOU_TV_ENC_SD_MUX             114
> +#define ZX296702_VL0_CLK                       115
> +#define ZX296702_VL1_CLK                       116
> +#define ZX296702_VL2_CLK                       117
> +#define ZX296702_GL0_CLK                       118
> +#define ZX296702_GL1_CLK                       119
> +#define ZX296702_GL2_CLK                       120
> +#define ZX296702_WB_CLK                                121
> +#define ZX296702_CL_CLK                                122
> +#define ZX296702_MAIN_MIX_CLK                  123
> +#define ZX296702_AUX_MIX_CLK                   124
> +#define ZX296702_HDMI_CLK                      125
> +#define ZX296702_VOU_TV_ENC_HD_DAC_CLK         126
> +#define ZX296702_VOU_TV_ENC_SD_DAC_CLK         127
> +#define ZX296702_A9_PERIPHCLK                  128
> +#define ZX296702_TOPCLK_END                    129
> +
> +#define ZX296702_SDMMC1_WCLK_MUX               0
> +#define ZX296702_SDMMC1_WCLK_DIV               1
> +#define ZX296702_SDMMC1_WCLK                   2
> +#define ZX296702_SDMMC1_PCLK                   3
> +#define ZX296702_LSP0CLK_END                   4
> +
> +#define ZX296702_UART0_WCLK_MUX                        0
> +#define ZX296702_UART0_WCLK                    1
> +#define ZX296702_UART0_PCLK                    2
> +#define ZX296702_UART1_WCLK_MUX                        3
> +#define ZX296702_UART1_WCLK                    4
> +#define ZX296702_UART1_PCLK                    5
> +#define ZX296702_SDMMC0_WCLK_MUX               6
> +#define ZX296702_SDMMC0_WCLK_DIV               7
> +#define ZX296702_SDMMC0_WCLK                   8
> +#define ZX296702_SDMMC0_PCLK                   9
> +#define ZX296702_LSP1CLK_END                   10
> +
> +#endif /* __DT_BINDINGS_CLOCK_ZX296702_H */
> --
> 1.9.1
>
Jun Nie May 25, 2015, 10:04 a.m. UTC | #5
> 2015-04-28 17:18 GMT+08:00 Jun Nie <jun.nie@linaro.org>:
>> It adds a clock driver for zx296702 SoC to register the clock tree to
>> Common Clock Framework.  All the clocks of bus topology and some the
>> peripheral clocks are ready with this commit. Some missing leaf clocks
>> for peripherals will be added later when needed.
>>
> Mike & Stephen,
>
> Could you help review and ack this patch? So that this patch set can
> be merged together in Arnd side.
>
Hi Mike,

    Could you help on this? Thanks ^_^

Jun

> Jun
>
>> Signed-off-by: Jun Nie <jun.nie@linaro.org>
>> ---
>>  drivers/clk/Makefile                       |   1 +
>>  drivers/clk/zte/Makefile                   |   2 +
>>  drivers/clk/zte/clk-pll.c                  | 180 ++++++++
>>  drivers/clk/zte/clk-zx296702.c             | 658 +++++++++++++++++++++++++++++
>>  drivers/clk/zte/clk.h                      |  30 ++
>>  include/dt-bindings/clock/zx296702-clock.h | 162 +++++++
>>  6 files changed, 1033 insertions(+)
>>  create mode 100644 drivers/clk/zte/Makefile
>>  create mode 100644 drivers/clk/zte/clk-pll.c
>>  create mode 100644 drivers/clk/zte/clk-zx296702.c
>>  create mode 100644 drivers/clk/zte/clk.h
>>  create mode 100644 include/dt-bindings/clock/zx296702-clock.h
>>
>> diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
>> index 3d00c25..f4c68be 100644
>> --- a/drivers/clk/Makefile
>> +++ b/drivers/clk/Makefile
>> @@ -72,4 +72,5 @@ obj-$(CONFIG_ARCH_OMAP2PLUS)          += ti/
>>  obj-$(CONFIG_ARCH_U8500)               += ux500/
>>  obj-$(CONFIG_COMMON_CLK_VERSATILE)     += versatile/
>>  obj-$(CONFIG_X86)                      += x86/
>> +obj-$(CONFIG_ARCH_ZX)                  += zte/
>>  obj-$(CONFIG_ARCH_ZYNQ)                        += zynq/
>> diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
>> new file mode 100644
>> index 0000000..95b707c
>> --- /dev/null
>> +++ b/drivers/clk/zte/Makefile
>> @@ -0,0 +1,2 @@
>> +obj-y := clk-pll.o
>> +obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o
>> diff --git a/drivers/clk/zte/clk-pll.c b/drivers/clk/zte/clk-pll.c
>> new file mode 100644
>> index 0000000..f0ff0cb
>> --- /dev/null
>> +++ b/drivers/clk/zte/clk-pll.c
>> @@ -0,0 +1,180 @@
>> +/*
>> + * Copyright 2014 Linaro Ltd.
>> + * Copyright (C) 2014 ZTE Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/err.h>
>> +#include <linux/io.h>
>> +#include <linux/slab.h>
>> +#include <linux/spinlock.h>
>> +
>> +#include "clk.h"
>> +
>> +#define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw)
>> +
>> +#define CFG0_CFG1_OFFSET 4
>> +#define LOCK_FLAG BIT(30)
>> +#define POWER_DOWN BIT(31)
>> +
>> +static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
>> +{
>> +       const struct zx_pll_config *config = zx_pll->lookup_table;
>> +       int i;
>> +
>> +       for (i = 0; i < zx_pll->count; i++) {
>> +               if (config[i].rate > rate)
>> +                       return i > 0 ? i - 1 : 0;
>> +
>> +               if (config[i].rate == rate)
>> +                       return i;
>> +       }
>> +
>> +       return i - 1;
>> +}
>> +
>> +static int hw_to_idx(struct clk_zx_pll *zx_pll)
>> +{
>> +       const struct zx_pll_config *config = zx_pll->lookup_table;
>> +       u32 hw_cfg0, hw_cfg1;
>> +       int i;
>> +
>> +       hw_cfg0 = readl_relaxed(zx_pll->reg_base);
>> +       hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
>> +
>> +       /* For matching the value in lookup table */
>> +       hw_cfg0 &= ~LOCK_FLAG;
>> +       hw_cfg0 |= POWER_DOWN;
>> +
>> +       for (i = 0; i < zx_pll->count; i++) {
>> +               if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
>> +                       return i;
>> +       }
>> +
>> +       return -1;
>> +}
>> +
>> +static unsigned long zx_pll_recalc_rate(struct clk_hw *hw,
>> +                                       unsigned long parent_rate)
>> +{
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       int idx;
>> +
>> +       idx = hw_to_idx(zx_pll);
>> +       if (unlikely(idx == -1))
>> +               return 0;
>> +
>> +       return zx_pll->lookup_table[idx].rate;
>> +}
>> +
>> +static long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
>> +                             unsigned long *prate)
>> +{
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       int idx;
>> +
>> +       idx = rate_to_idx(zx_pll, rate);
>> +
>> +       return zx_pll->lookup_table[idx].rate;
>> +}
>> +
>> +static int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
>> +                          unsigned long parent_rate)
>> +{
>> +       /* Assume current cpu is not running on current PLL */
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       const struct zx_pll_config *config;
>> +       int idx;
>> +
>> +       idx = rate_to_idx(zx_pll, rate);
>> +       config = &zx_pll->lookup_table[idx];
>> +
>> +       writel_relaxed(config->cfg0, zx_pll->reg_base);
>> +       writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET);
>> +
>> +       return 0;
>> +}
>> +
>> +static int zx_pll_enable(struct clk_hw *hw)
>> +{
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       unsigned long timeout = jiffies + msecs_to_jiffies(500);
>> +       u32 reg;
>> +
>> +       reg = readl_relaxed(zx_pll->reg_base);
>> +       writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
>> +       while (!(readl_relaxed(zx_pll->reg_base) & LOCK_FLAG)) {
>> +               if (time_after(jiffies, timeout)) {
>> +                       pr_err("clk %s enable timeout\n",
>> +                               __clk_get_name(hw->clk));
>> +                       break;
>> +               }
>> +       }
>> +
>> +       return 0;
>> +}
>> +
>> +static void zx_pll_disable(struct clk_hw *hw)
>> +{
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       u32 reg;
>> +
>> +       reg = readl_relaxed(zx_pll->reg_base);
>> +       writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base);
>> +}
>> +
>> +static int zx_pll_is_enabled(struct clk_hw *hw)
>> +{
>> +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
>> +       u32 reg;
>> +
>> +       reg = readl_relaxed(zx_pll->reg_base);
>> +
>> +       return !(reg & POWER_DOWN);
>> +}
>> +
>> +static const struct clk_ops zx_pll_ops = {
>> +       .recalc_rate = zx_pll_recalc_rate,
>> +       .round_rate = zx_pll_round_rate,
>> +       .set_rate = zx_pll_set_rate,
>> +       .enable = zx_pll_enable,
>> +       .disable = zx_pll_disable,
>> +       .is_enabled = zx_pll_is_enabled,
>> +};
>> +
>> +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
>> +       unsigned long flags, void __iomem *reg_base,
>> +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock)
>> +{
>> +       struct clk_zx_pll *zx_pll;
>> +       struct clk *clk;
>> +       struct clk_init_data init;
>> +
>> +       zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
>> +       if (!zx_pll)
>> +               return ERR_PTR(-ENOMEM);
>> +
>> +       init.name = name;
>> +       init.ops = &zx_pll_ops;
>> +       init.flags = flags;
>> +       init.parent_names = (parent_name ? &parent_name : NULL);
>> +       init.num_parents = (parent_name ? 1 : 0);
>> +
>> +       zx_pll->reg_base = reg_base;
>> +       zx_pll->lookup_table = lookup_table;
>> +       zx_pll->count = count;
>> +       zx_pll->lock = lock;
>> +       zx_pll->hw.init = &init;
>> +
>> +       clk = clk_register(NULL, &zx_pll->hw);
>> +
>> +       if (IS_ERR(clk))
>> +               kfree(zx_pll);
>> +
>> +       return clk;
>> +}
>> diff --git a/drivers/clk/zte/clk-zx296702.c b/drivers/clk/zte/clk-zx296702.c
>> new file mode 100644
>> index 0000000..5f4d7ed
>> --- /dev/null
>> +++ b/drivers/clk/zte/clk-zx296702.c
>> @@ -0,0 +1,658 @@
>> +/*
>> + * Copyright 2014 Linaro Ltd.
>> + * Copyright (C) 2014 ZTE Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#include <linux/clk.h>
>> +#include <linux/clk-provider.h>
>> +#include <linux/of_address.h>
>> +#include <dt-bindings/clock/zx296702-clock.h>
>> +#include "clk.h"
>> +
>> +static DEFINE_SPINLOCK(reg_lock);
>> +
>> +static void __iomem *topcrm_base;
>> +static void __iomem *lsp0crpm_base;
>> +static void __iomem *lsp1crpm_base;
>> +
>> +static struct clk *topclk[ZX296702_TOPCLK_END];
>> +static struct clk *lsp0clk[ZX296702_LSP0CLK_END];
>> +static struct clk *lsp1clk[ZX296702_LSP1CLK_END];
>> +
>> +static struct clk_onecell_data topclk_data;
>> +static struct clk_onecell_data lsp0clk_data;
>> +static struct clk_onecell_data lsp1clk_data;
>> +
>> +#define CLK_MUX                        (topcrm_base + 0x04)
>> +#define CLK_DIV                        (topcrm_base + 0x08)
>> +#define CLK_EN0                        (topcrm_base + 0x0c)
>> +#define CLK_EN1                        (topcrm_base + 0x10)
>> +#define VOU_LOCAL_CLKEN                (topcrm_base + 0x68)
>> +#define VOU_LOCAL_CLKSEL       (topcrm_base + 0x70)
>> +#define VOU_LOCAL_DIV2_SET     (topcrm_base + 0x74)
>> +#define CLK_MUX1               (topcrm_base + 0x8c)
>> +
>> +#define CLK_SDMMC1             (lsp0crpm_base + 0x0c)
>> +
>> +#define CLK_UART0              (lsp1crpm_base + 0x20)
>> +#define CLK_UART1              (lsp1crpm_base + 0x24)
>> +#define CLK_SDMMC0             (lsp1crpm_base + 0x2c)
>> +
>> +static const struct zx_pll_config pll_a9_config[] = {
>> +       { .rate = 700000000, .cfg0 = 0x800405d1, .cfg1 = 0x04555555 },
>> +       { .rate = 800000000, .cfg0 = 0x80040691, .cfg1 = 0x04aaaaaa },
>> +       { .rate = 900000000, .cfg0 = 0x80040791, .cfg1 = 0x04000000 },
>> +       { .rate = 1000000000, .cfg0 = 0x80040851, .cfg1 = 0x04555555 },
>> +       { .rate = 1100000000, .cfg0 = 0x80040911, .cfg1 = 0x04aaaaaa },
>> +       { .rate = 1200000000, .cfg0 = 0x80040a11, .cfg1 = 0x04000000 },
>> +};
>> +
>> +static const struct clk_div_table main_hlk_div[] = {
>> +       { .val = 1, .div = 2, },
>> +       { .val = 3, .div = 4, },
>> +       { /* sentinel */ }
>> +};
>> +
>> +static const struct clk_div_table a9_as1_aclk_divider[] = {
>> +       { .val = 0, .div = 1, },
>> +       { .val = 1, .div = 2, },
>> +       { .val = 3, .div = 4, },
>> +       { /* sentinel */ }
>> +};
>> +
>> +static const struct clk_div_table sec_wclk_divider[] = {
>> +       { .val = 0, .div = 1, },
>> +       { .val = 1, .div = 2, },
>> +       { .val = 3, .div = 4, },
>> +       { .val = 5, .div = 6, },
>> +       { .val = 7, .div = 8, },
>> +       { /* sentinel */ }
>> +};
>> +
>> +static const char const *matrix_aclk_sel[] = {
>> +       "pll_mm0_198M",
>> +       "osc",
>> +       "clk_148M5",
>> +       "pll_lsp_104M",
>> +};
>> +
>> +static const char const *a9_wclk_sel[] = {
>> +       "pll_a9",
>> +       "osc",
>> +       "clk_500",
>> +       "clk_250",
>> +};
>> +
>> +static const char const *a9_as1_aclk_sel[] = {
>> +       "clk_250",
>> +       "osc",
>> +       "pll_mm0_396M",
>> +       "pll_mac_333M",
>> +};
>> +
>> +static const char const *a9_trace_clkin_sel[] = {
>> +       "clk_74M25",
>> +       "pll_mm1_108M",
>> +       "clk_125",
>> +       "clk_148M5",
>> +};
>> +
>> +static const char const *decppu_aclk_sel[] = {
>> +       "clk_250",
>> +       "pll_mm0_198M",
>> +       "pll_lsp_104M",
>> +       "pll_audio_294M912",
>> +};
>> +
>> +static const char const *vou_main_wclk_sel[] = {
>> +       "clk_148M5",
>> +       "clk_74M25",
>> +       "clk_27",
>> +       "pll_mm1_54M",
>> +};
>> +
>> +static const char const *vou_scaler_wclk_sel[] = {
>> +       "clk_250",
>> +       "pll_mac_333M",
>> +       "pll_audio_294M912",
>> +       "pll_mm0_198M",
>> +};
>> +
>> +static const char const *r2d_wclk_sel[] = {
>> +       "pll_audio_294M912",
>> +       "pll_mac_333M",
>> +       "pll_a9_350M",
>> +       "pll_mm0_396M",
>> +};
>> +
>> +static const char const *ddr_wclk_sel[] = {
>> +       "pll_mac_333M",
>> +       "pll_ddr_266M",
>> +       "pll_audio_294M912",
>> +       "pll_mm0_198M",
>> +};
>> +
>> +static const char const *nand_wclk_sel[] = {
>> +       "pll_lsp_104M",
>> +       "osc",
>> +};
>> +
>> +static const char const *lsp_26_wclk_sel[] = {
>> +       "pll_lsp_26M",
>> +       "osc",
>> +};
>> +
>> +static const char const *vl0_sel[] = {
>> +       "vou_main_channel_div",
>> +       "vou_aux_channel_div",
>> +};
>> +
>> +static const char const *hdmi_sel[] = {
>> +       "vou_main_channel_wclk",
>> +       "vou_aux_channel_wclk",
>> +};
>> +
>> +static const char const *sdmmc0_wclk_sel[] = {
>> +       "lsp1_104M_wclk",
>> +       "lsp1_26M_wclk",
>> +};
>> +
>> +static const char const *sdmmc1_wclk_sel[] = {
>> +       "lsp0_104M_wclk",
>> +       "lsp0_26M_wclk",
>> +};
>> +
>> +static const char const *uart_wclk_sel[] = {
>> +       "lsp1_104M_wclk",
>> +       "lsp1_26M_wclk",
>> +};
>> +
>> +static inline struct clk *zx_divtbl(const char *name, const char *parent,
>> +                                   void __iomem *reg, u8 shift, u8 width,
>> +                                   const struct clk_div_table *table)
>> +{
>> +       return clk_register_divider_table(NULL, name, parent, 0, reg, shift,
>> +                                         width, 0, table, &reg_lock);
>> +}
>> +
>> +static inline struct clk *zx_div(const char *name, const char *parent,
>> +                                void __iomem *reg, u8 shift, u8 width)
>> +{
>> +       return clk_register_divider(NULL, name, parent, 0,
>> +                                   reg, shift, width, 0, &reg_lock);
>> +}
>> +
>> +static inline struct clk *zx_mux(const char *name, const char **parents,
>> +               int num_parents, void __iomem *reg, u8 shift, u8 width)
>> +{
>> +       return clk_register_mux(NULL, name, parents, num_parents,
>> +                               0, reg, shift, width, 0, &reg_lock);
>> +}
>> +
>> +static inline struct clk *zx_gate(const char *name, const char *parent,
>> +                                 void __iomem *reg, u8 shift)
>> +{
>> +       return clk_register_gate(NULL, name, parent, CLK_IGNORE_UNUSED,
>> +                                reg, shift, 0, &reg_lock);
>> +}
>> +
>> +static void __init zx296702_top_clocks_init(struct device_node *np)
>> +{
>> +       struct clk **clk = topclk;
>> +       int i;
>> +
>> +       topcrm_base = of_iomap(np, 0);
>> +       WARN_ON(!topcrm_base);
>> +
>> +       clk[ZX296702_OSC] =
>> +               clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT,
>> +                               30000000);
>> +       clk[ZX296702_PLL_A9] =
>> +               clk_register_zx_pll("pll_a9", "osc", 0, topcrm_base
>> +                               + 0x01c, pll_a9_config,
>> +                               ARRAY_SIZE(pll_a9_config), &reg_lock);
>> +
>> +       /* TODO: pll_a9_350M look like changeble follow a9 pll */
>> +       clk[ZX296702_PLL_A9_350M] =
>> +               clk_register_fixed_rate(NULL, "pll_a9_350M", "osc", 0,
>> +                               350000000);
>> +       clk[ZX296702_PLL_MAC_1000M] =
>> +               clk_register_fixed_rate(NULL, "pll_mac_1000M", "osc", 0,
>> +                               1000000000);
>> +       clk[ZX296702_PLL_MAC_333M] =
>> +               clk_register_fixed_rate(NULL, "pll_mac_333M",    "osc", 0,
>> +                               333000000);
>> +       clk[ZX296702_PLL_MM0_1188M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm0_1188M", "osc", 0,
>> +                               1188000000);
>> +       clk[ZX296702_PLL_MM0_396M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm0_396M",  "osc", 0,
>> +                               396000000);
>> +       clk[ZX296702_PLL_MM0_198M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm0_198M",  "osc", 0,
>> +                               198000000);
>> +       clk[ZX296702_PLL_MM1_108M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm1_108M",  "osc", 0,
>> +                               108000000);
>> +       clk[ZX296702_PLL_MM1_72M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm1_72M",     "osc", 0,
>> +                               72000000);
>> +       clk[ZX296702_PLL_MM1_54M] =
>> +               clk_register_fixed_rate(NULL, "pll_mm1_54M",     "osc", 0,
>> +                               54000000);
>> +       clk[ZX296702_PLL_LSP_104M] =
>> +               clk_register_fixed_rate(NULL, "pll_lsp_104M",  "osc", 0,
>> +                               104000000);
>> +       clk[ZX296702_PLL_LSP_26M] =
>> +               clk_register_fixed_rate(NULL, "pll_lsp_26M",     "osc", 0,
>> +                               26000000);
>> +       clk[ZX296702_PLL_DDR_266M] =
>> +               clk_register_fixed_rate(NULL, "pll_ddr_266M",    "osc", 0,
>> +                               266000000);
>> +       clk[ZX296702_PLL_AUDIO_294M912] =
>> +               clk_register_fixed_rate(NULL, "pll_audio_294M912", "osc", 0,
>> +                               294912000);
>> +
>> +       /* bus clock */
>> +       clk[ZX296702_MATRIX_ACLK] =
>> +               zx_mux("matrix_aclk", matrix_aclk_sel,
>> +                               ARRAY_SIZE(matrix_aclk_sel), CLK_MUX, 2, 2);
>> +       clk[ZX296702_MAIN_HCLK] =
>> +               zx_divtbl("main_hclk", "matrix_aclk", CLK_DIV, 0, 2,
>> +                               main_hlk_div);
>> +       clk[ZX296702_MAIN_PCLK] =
>> +               zx_divtbl("main_pclk", "matrix_aclk", CLK_DIV, 2, 2,
>> +                               main_hlk_div);
>> +
>> +       /* cpu clock */
>> +       clk[ZX296702_CLK_500] =
>> +               clk_register_fixed_factor(NULL, "clk_500", "pll_mac_1000M", 0,
>> +                               1, 2);
>> +       clk[ZX296702_CLK_250] =
>> +               clk_register_fixed_factor(NULL, "clk_250", "pll_mac_1000M", 0,
>> +                               1, 4);
>> +       clk[ZX296702_CLK_125] =
>> +               clk_register_fixed_factor(NULL, "clk_125", "clk_250", 0, 1, 2);
>> +       clk[ZX296702_CLK_148M5] =
>> +               clk_register_fixed_factor(NULL, "clk_148M5", "pll_mm0_1188M", 0,
>> +                               1, 8);
>> +       clk[ZX296702_CLK_74M25] =
>> +               clk_register_fixed_factor(NULL, "clk_74M25", "pll_mm0_1188M", 0,
>> +                               1, 16);
>> +       clk[ZX296702_A9_WCLK] =
>> +               zx_mux("a9_wclk", a9_wclk_sel, ARRAY_SIZE(a9_wclk_sel), CLK_MUX,
>> +                               0, 2);
>> +       clk[ZX296702_A9_AS1_ACLK_MUX] =
>> +               zx_mux("a9_as1_aclk_mux", a9_as1_aclk_sel,
>> +                               ARRAY_SIZE(a9_as1_aclk_sel), CLK_MUX, 4, 2);
>> +       clk[ZX296702_A9_TRACE_CLKIN_MUX] =
>> +               zx_mux("a9_trace_clkin_mux", a9_trace_clkin_sel,
>> +                               ARRAY_SIZE(a9_trace_clkin_sel), CLK_MUX1, 0, 2);
>> +       clk[ZX296702_A9_AS1_ACLK_DIV] =
>> +               zx_divtbl("a9_as1_aclk_div", "a9_as1_aclk_mux", CLK_DIV, 4, 2,
>> +                               a9_as1_aclk_divider);
>> +
>> +       /* multi-media clock */
>> +       clk[ZX296702_CLK_2] =
>> +               clk_register_fixed_factor(NULL, "clk_2", "pll_mm1_72M", 0,
>> +                               1, 36);
>> +       clk[ZX296702_CLK_27] =
>> +               clk_register_fixed_factor(NULL, "clk_27", "pll_mm1_54M", 0,
>> +                               1, 2);
>> +       clk[ZX296702_DECPPU_ACLK_MUX] =
>> +               zx_mux("decppu_aclk_mux", decppu_aclk_sel,
>> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 6, 2);
>> +       clk[ZX296702_PPU_ACLK_MUX] =
>> +               zx_mux("ppu_aclk_mux", decppu_aclk_sel,
>> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 8, 2);
>> +       clk[ZX296702_MALI400_ACLK_MUX] =
>> +               zx_mux("mali400_aclk_mux", decppu_aclk_sel,
>> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 12, 2);
>> +       clk[ZX296702_VOU_ACLK_MUX] =
>> +               zx_mux("vou_aclk_mux", decppu_aclk_sel,
>> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 10, 2);
>> +       clk[ZX296702_VOU_MAIN_WCLK_MUX] =
>> +               zx_mux("vou_main_wclk_mux", vou_main_wclk_sel,
>> +                               ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 14, 2);
>> +       clk[ZX296702_VOU_AUX_WCLK_MUX] =
>> +               zx_mux("vou_aux_wclk_mux", vou_main_wclk_sel,
>> +                               ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 16, 2);
>> +       clk[ZX296702_VOU_SCALER_WCLK_MUX] =
>> +               zx_mux("vou_scaler_wclk_mux", vou_scaler_wclk_sel,
>> +                               ARRAY_SIZE(vou_scaler_wclk_sel), CLK_MUX,
>> +                               18, 2);
>> +       clk[ZX296702_R2D_ACLK_MUX] =
>> +               zx_mux("r2d_aclk_mux", decppu_aclk_sel,
>> +                               ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 20, 2);
>> +       clk[ZX296702_R2D_WCLK_MUX] =
>> +               zx_mux("r2d_wclk_mux", r2d_wclk_sel,
>> +                               ARRAY_SIZE(r2d_wclk_sel), CLK_MUX, 22, 2);
>> +
>> +       /* other clock */
>> +       clk[ZX296702_CLK_50] =
>> +               clk_register_fixed_factor(NULL, "clk_50", "pll_mac_1000M",
>> +                               0, 1, 20);
>> +       clk[ZX296702_CLK_25] =
>> +               clk_register_fixed_factor(NULL, "clk_25", "pll_mac_1000M",
>> +                               0, 1, 40);
>> +       clk[ZX296702_CLK_12] =
>> +               clk_register_fixed_factor(NULL, "clk_12", "pll_mm1_72M",
>> +                               0, 1, 6);
>> +       clk[ZX296702_CLK_16M384] =
>> +               clk_register_fixed_factor(NULL, "clk_16M384",
>> +                               "pll_audio_294M912", 0, 1, 18);
>> +       clk[ZX296702_CLK_32K768] =
>> +               clk_register_fixed_factor(NULL, "clk_32K768", "clk_16M384",
>> +                               0, 1, 500);
>> +       clk[ZX296702_SEC_WCLK_DIV] =
>> +               zx_divtbl("sec_wclk_div", "pll_lsp_104M", CLK_DIV, 6, 3,
>> +                               sec_wclk_divider);
>> +       clk[ZX296702_DDR_WCLK_MUX] =
>> +               zx_mux("ddr_wclk_mux", ddr_wclk_sel,
>> +                               ARRAY_SIZE(ddr_wclk_sel), CLK_MUX, 24, 2);
>> +       clk[ZX296702_NAND_WCLK_MUX] =
>> +               zx_mux("nand_wclk_mux", nand_wclk_sel,
>> +                               ARRAY_SIZE(nand_wclk_sel), CLK_MUX, 24, 2);
>> +       clk[ZX296702_LSP_26_WCLK_MUX] =
>> +               zx_mux("lsp_26_wclk_mux", lsp_26_wclk_sel,
>> +                               ARRAY_SIZE(lsp_26_wclk_sel), CLK_MUX, 27, 1);
>> +
>> +       /* gates */
>> +       clk[ZX296702_A9_AS0_ACLK] =
>> +               zx_gate("a9_as0_aclk",  "matrix_aclk",          CLK_EN0, 0);
>> +       clk[ZX296702_A9_AS1_ACLK] =
>> +               zx_gate("a9_as1_aclk",  "a9_as1_aclk_div",      CLK_EN0, 1);
>> +       clk[ZX296702_A9_TRACE_CLKIN] =
>> +               zx_gate("a9_trace_clkin", "a9_trace_clkin_mux", CLK_EN0, 2);
>> +       clk[ZX296702_DECPPU_AXI_M_ACLK] =
>> +               zx_gate("decppu_axi_m_aclk", "decppu_aclk_mux", CLK_EN0, 3);
>> +       clk[ZX296702_DECPPU_AHB_S_HCLK] =
>> +               zx_gate("decppu_ahb_s_hclk",    "main_hclk",    CLK_EN0, 4);
>> +       clk[ZX296702_PPU_AXI_M_ACLK] =
>> +               zx_gate("ppu_axi_m_aclk",       "ppu_aclk_mux", CLK_EN0, 5);
>> +       clk[ZX296702_PPU_AHB_S_HCLK] =
>> +               zx_gate("ppu_ahb_s_hclk",       "main_hclk",    CLK_EN0, 6);
>> +       clk[ZX296702_VOU_AXI_M_ACLK] =
>> +               zx_gate("vou_axi_m_aclk",       "vou_aclk_mux", CLK_EN0, 7);
>> +       clk[ZX296702_VOU_APB_PCLK] =
>> +               zx_gate("vou_apb_pclk", "main_pclk",            CLK_EN0, 8);
>> +       clk[ZX296702_VOU_MAIN_CHANNEL_WCLK] =
>> +               zx_gate("vou_main_channel_wclk", "vou_main_wclk_mux",
>> +                               CLK_EN0, 9);
>> +       clk[ZX296702_VOU_AUX_CHANNEL_WCLK] =
>> +               zx_gate("vou_aux_channel_wclk", "vou_aux_wclk_mux",
>> +                               CLK_EN0, 10);
>> +       clk[ZX296702_VOU_HDMI_OSCLK_CEC] =
>> +               zx_gate("vou_hdmi_osclk_cec", "clk_2",          CLK_EN0, 11);
>> +       clk[ZX296702_VOU_SCALER_WCLK] =
>> +               zx_gate("vou_scaler_wclk", "vou_scaler_wclk_mux", CLK_EN0, 12);
>> +       clk[ZX296702_MALI400_AXI_M_ACLK] =
>> +               zx_gate("mali400_axi_m_aclk", "mali400_aclk_mux", CLK_EN0, 13);
>> +       clk[ZX296702_MALI400_APB_PCLK] =
>> +               zx_gate("mali400_apb_pclk",     "main_pclk",    CLK_EN0, 14);
>> +       clk[ZX296702_R2D_WCLK] =
>> +               zx_gate("r2d_wclk",             "r2d_wclk_mux", CLK_EN0, 15);
>> +       clk[ZX296702_R2D_AXI_M_ACLK] =
>> +               zx_gate("r2d_axi_m_aclk",       "r2d_aclk_mux", CLK_EN0, 16);
>> +       clk[ZX296702_R2D_AHB_HCLK] =
>> +               zx_gate("r2d_ahb_hclk",         "main_hclk",    CLK_EN0, 17);
>> +       clk[ZX296702_DDR3_AXI_S0_ACLK] =
>> +               zx_gate("ddr3_axi_s0_aclk",     "matrix_aclk",  CLK_EN0, 18);
>> +       clk[ZX296702_DDR3_APB_PCLK] =
>> +               zx_gate("ddr3_apb_pclk",        "main_pclk",    CLK_EN0, 19);
>> +       clk[ZX296702_DDR3_WCLK] =
>> +               zx_gate("ddr3_wclk",            "ddr_wclk_mux", CLK_EN0, 20);
>> +       clk[ZX296702_USB20_0_AHB_HCLK] =
>> +               zx_gate("usb20_0_ahb_hclk",     "main_hclk",    CLK_EN0, 21);
>> +       clk[ZX296702_USB20_0_EXTREFCLK] =
>> +               zx_gate("usb20_0_extrefclk",    "clk_12",       CLK_EN0, 22);
>> +       clk[ZX296702_USB20_1_AHB_HCLK] =
>> +               zx_gate("usb20_1_ahb_hclk",     "main_hclk",    CLK_EN0, 23);
>> +       clk[ZX296702_USB20_1_EXTREFCLK] =
>> +               zx_gate("usb20_1_extrefclk",    "clk_12",       CLK_EN0, 24);
>> +       clk[ZX296702_USB20_2_AHB_HCLK] =
>> +               zx_gate("usb20_2_ahb_hclk",     "main_hclk",    CLK_EN0, 25);
>> +       clk[ZX296702_USB20_2_EXTREFCLK] =
>> +               zx_gate("usb20_2_extrefclk",    "clk_12",       CLK_EN0, 26);
>> +       clk[ZX296702_GMAC_AXI_M_ACLK] =
>> +               zx_gate("gmac_axi_m_aclk",      "matrix_aclk",  CLK_EN0, 27);
>> +       clk[ZX296702_GMAC_APB_PCLK] =
>> +               zx_gate("gmac_apb_pclk",        "main_pclk",    CLK_EN0, 28);
>> +       clk[ZX296702_GMAC_125_CLKIN] =
>> +               zx_gate("gmac_125_clkin",       "clk_125",      CLK_EN0, 29);
>> +       clk[ZX296702_GMAC_RMII_CLKIN] =
>> +               zx_gate("gmac_rmii_clkin",      "clk_50",       CLK_EN0, 30);
>> +       clk[ZX296702_GMAC_25M_CLK] =
>> +               zx_gate("gmac_25M_clk",         "clk_25",       CLK_EN0, 31);
>> +       clk[ZX296702_NANDFLASH_AHB_HCLK] =
>> +               zx_gate("nandflash_ahb_hclk", "main_hclk",      CLK_EN1, 0);
>> +       clk[ZX296702_NANDFLASH_WCLK] =
>> +               zx_gate("nandflash_wclk",     "nand_wclk_mux",  CLK_EN1, 1);
>> +       clk[ZX296702_LSP0_APB_PCLK] =
>> +               zx_gate("lsp0_apb_pclk",        "main_pclk",    CLK_EN1, 2);
>> +       clk[ZX296702_LSP0_AHB_HCLK] =
>> +               zx_gate("lsp0_ahb_hclk",        "main_hclk",    CLK_EN1, 3);
>> +       clk[ZX296702_LSP0_26M_WCLK] =
>> +               zx_gate("lsp0_26M_wclk",   "lsp_26_wclk_mux",   CLK_EN1, 4);
>> +       clk[ZX296702_LSP0_104M_WCLK] =
>> +               zx_gate("lsp0_104M_wclk",       "pll_lsp_104M", CLK_EN1, 5);
>> +       clk[ZX296702_LSP0_16M384_WCLK] =
>> +               zx_gate("lsp0_16M384_wclk",     "clk_16M384",   CLK_EN1, 6);
>> +       clk[ZX296702_LSP1_APB_PCLK] =
>> +               zx_gate("lsp1_apb_pclk",        "main_pclk",    CLK_EN1, 7);
>> +       /* FIXME: wclk enable bit is bit8. We hack it as reserved 31 for
>> +        * UART does not work after parent clk is disabled/enabled */
>> +       clk[ZX296702_LSP1_26M_WCLK] =
>> +               zx_gate("lsp1_26M_wclk",     "lsp_26_wclk_mux", CLK_EN1, 31);
>> +       clk[ZX296702_LSP1_104M_WCLK] =
>> +               zx_gate("lsp1_104M_wclk",    "pll_lsp_104M",    CLK_EN1, 9);
>> +       clk[ZX296702_LSP1_32K_CLK] =
>> +               zx_gate("lsp1_32K_clk", "clk_32K768",           CLK_EN1, 10);
>> +       clk[ZX296702_AON_HCLK] =
>> +               zx_gate("aon_hclk",             "main_hclk",    CLK_EN1, 11);
>> +       clk[ZX296702_SYS_CTRL_PCLK] =
>> +               zx_gate("sys_ctrl_pclk",        "main_pclk",    CLK_EN1, 12);
>> +       clk[ZX296702_DMA_PCLK] =
>> +               zx_gate("dma_pclk",             "main_pclk",    CLK_EN1, 13);
>> +       clk[ZX296702_DMA_ACLK] =
>> +               zx_gate("dma_aclk",             "matrix_aclk",  CLK_EN1, 14);
>> +       clk[ZX296702_SEC_HCLK] =
>> +               zx_gate("sec_hclk",             "main_hclk",    CLK_EN1, 15);
>> +       clk[ZX296702_AES_WCLK] =
>> +               zx_gate("aes_wclk",             "sec_wclk_div", CLK_EN1, 16);
>> +       clk[ZX296702_DES_WCLK] =
>> +               zx_gate("des_wclk",             "sec_wclk_div", CLK_EN1, 17);
>> +       clk[ZX296702_IRAM_ACLK] =
>> +               zx_gate("iram_aclk",            "matrix_aclk",  CLK_EN1, 18);
>> +       clk[ZX296702_IROM_ACLK] =
>> +               zx_gate("irom_aclk",            "matrix_aclk",  CLK_EN1, 19);
>> +       clk[ZX296702_BOOT_CTRL_HCLK] =
>> +               zx_gate("boot_ctrl_hclk",       "main_hclk",    CLK_EN1, 20);
>> +       clk[ZX296702_EFUSE_CLK_30] =
>> +               zx_gate("efuse_clk_30", "osc",                  CLK_EN1, 21);
>> +
>> +       /* TODO: add VOU Local clocks */
>> +       clk[ZX296702_VOU_MAIN_CHANNEL_DIV] =
>> +               zx_div("vou_main_channel_div", "vou_main_channel_wclk",
>> +                               VOU_LOCAL_DIV2_SET, 1, 1);
>> +       clk[ZX296702_VOU_AUX_CHANNEL_DIV] =
>> +               zx_div("vou_aux_channel_div", "vou_aux_channel_wclk",
>> +                               VOU_LOCAL_DIV2_SET, 0, 1);
>> +       clk[ZX296702_VOU_TV_ENC_HD_DIV] =
>> +               zx_div("vou_tv_enc_hd_div", "vou_tv_enc_hd_mux",
>> +                               VOU_LOCAL_DIV2_SET, 3, 1);
>> +       clk[ZX296702_VOU_TV_ENC_SD_DIV] =
>> +               zx_div("vou_tv_enc_sd_div", "vou_tv_enc_sd_mux",
>> +                               VOU_LOCAL_DIV2_SET, 2, 1);
>> +       clk[ZX296702_VL0_MUX] =
>> +               zx_mux("vl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 8, 1);
>> +       clk[ZX296702_VL1_MUX] =
>> +               zx_mux("vl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 9, 1);
>> +       clk[ZX296702_VL2_MUX] =
>> +               zx_mux("vl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 10, 1);
>> +       clk[ZX296702_GL0_MUX] =
>> +               zx_mux("gl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 5, 1);
>> +       clk[ZX296702_GL1_MUX] =
>> +               zx_mux("gl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 6, 1);
>> +       clk[ZX296702_GL2_MUX] =
>> +               zx_mux("gl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 7, 1);
>> +       clk[ZX296702_WB_MUX] =
>> +               zx_mux("wb_mux",  vl0_sel, ARRAY_SIZE(vl0_sel),
>> +                               VOU_LOCAL_CLKSEL, 11, 1);
>> +       clk[ZX296702_HDMI_MUX] =
>> +               zx_mux("hdmi_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
>> +                               VOU_LOCAL_CLKSEL, 4, 1);
>> +       clk[ZX296702_VOU_TV_ENC_HD_MUX] =
>> +               zx_mux("vou_tv_enc_hd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
>> +                               VOU_LOCAL_CLKSEL, 3, 1);
>> +       clk[ZX296702_VOU_TV_ENC_SD_MUX] =
>> +               zx_mux("vou_tv_enc_sd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
>> +                               VOU_LOCAL_CLKSEL, 2, 1);
>> +       clk[ZX296702_VL0_CLK] =
>> +               zx_gate("vl0_clk", "vl0_mux", VOU_LOCAL_CLKEN, 8);
>> +       clk[ZX296702_VL1_CLK] =
>> +               zx_gate("vl1_clk", "vl1_mux", VOU_LOCAL_CLKEN, 9);
>> +       clk[ZX296702_VL2_CLK] =
>> +               zx_gate("vl2_clk", "vl2_mux", VOU_LOCAL_CLKEN, 10);
>> +       clk[ZX296702_GL0_CLK] =
>> +               zx_gate("gl0_clk", "gl0_mux", VOU_LOCAL_CLKEN, 5);
>> +       clk[ZX296702_GL1_CLK] =
>> +               zx_gate("gl1_clk", "gl1_mux", VOU_LOCAL_CLKEN, 6);
>> +       clk[ZX296702_GL2_CLK] =
>> +               zx_gate("gl2_clk", "gl2_mux", VOU_LOCAL_CLKEN, 7);
>> +       clk[ZX296702_WB_CLK] =
>> +               zx_gate("wb_clk", "wb_mux", VOU_LOCAL_CLKEN, 11);
>> +       clk[ZX296702_CL_CLK] =
>> +               zx_gate("cl_clk", "vou_main_channel_div", VOU_LOCAL_CLKEN, 12);
>> +       clk[ZX296702_MAIN_MIX_CLK] =
>> +               zx_gate("main_mix_clk", "vou_main_channel_div",
>> +                               VOU_LOCAL_CLKEN, 4);
>> +       clk[ZX296702_AUX_MIX_CLK] =
>> +               zx_gate("aux_mix_clk", "vou_aux_channel_div",
>> +                               VOU_LOCAL_CLKEN, 3);
>> +       clk[ZX296702_HDMI_CLK] =
>> +               zx_gate("hdmi_clk", "hdmi_mux", VOU_LOCAL_CLKEN, 2);
>> +       clk[ZX296702_VOU_TV_ENC_HD_DAC_CLK] =
>> +               zx_gate("vou_tv_enc_hd_dac_clk", "vou_tv_enc_hd_div",
>> +                               VOU_LOCAL_CLKEN, 1);
>> +       clk[ZX296702_VOU_TV_ENC_SD_DAC_CLK] =
>> +               zx_gate("vou_tv_enc_sd_dac_clk", "vou_tv_enc_sd_div",
>> +                               VOU_LOCAL_CLKEN, 0);
>> +
>> +       /* CA9 PERIPHCLK = a9_wclk / 2 */
>> +       clk[ZX296702_A9_PERIPHCLK] =
>> +               clk_register_fixed_factor(NULL, "a9_periphclk", "a9_wclk",
>> +                               0, 1, 2);
>> +
>> +       for (i = 0; i < ARRAY_SIZE(topclk); i++) {
>> +               if (IS_ERR(clk[i])) {
>> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
>> +                               i, PTR_ERR(clk[i]));
>> +                       return;
>> +               }
>> +       }
>> +
>> +       topclk_data.clks = topclk;
>> +       topclk_data.clk_num = ARRAY_SIZE(topclk);
>> +       of_clk_add_provider(np, of_clk_src_onecell_get, &topclk_data);
>> +}
>> +CLK_OF_DECLARE(zx296702_top_clk, "zte,zx296702-topcrm-clk",
>> +               zx296702_top_clocks_init);
>> +
>> +static void __init zx296702_lsp0_clocks_init(struct device_node *np)
>> +{
>> +       struct clk **clk = lsp0clk;
>> +       int i;
>> +
>> +       lsp0crpm_base = of_iomap(np, 0);
>> +       WARN_ON(!lsp0crpm_base);
>> +
>> +       /* SDMMC1 */
>> +       clk[ZX296702_SDMMC1_WCLK_MUX] =
>> +               zx_mux("sdmmc1_wclk_mux", sdmmc1_wclk_sel,
>> +                               ARRAY_SIZE(sdmmc1_wclk_sel), CLK_SDMMC1, 4, 1);
>> +       clk[ZX296702_SDMMC1_WCLK_DIV] =
>> +               zx_div("sdmmc1_wclk_div", "sdmmc1_wclk_mux", CLK_SDMMC1, 12, 4);
>> +       clk[ZX296702_SDMMC1_WCLK] =
>> +               zx_gate("sdmmc1_wclk", "sdmmc1_wclk_div", CLK_SDMMC1, 1);
>> +       clk[ZX296702_SDMMC1_PCLK] =
>> +               zx_gate("sdmmc1_pclk", "lsp1_apb_pclk", CLK_SDMMC1, 0);
>> +
>> +       for (i = 0; i < ARRAY_SIZE(lsp0clk); i++) {
>> +               if (IS_ERR(clk[i])) {
>> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
>> +                               i, PTR_ERR(clk[i]));
>> +                       return;
>> +               }
>> +       }
>> +
>> +       lsp0clk_data.clks = lsp0clk;
>> +       lsp0clk_data.clk_num = ARRAY_SIZE(lsp0clk);
>> +       of_clk_add_provider(np, of_clk_src_onecell_get, &lsp0clk_data);
>> +}
>> +CLK_OF_DECLARE(zx296702_lsp0_clk, "zte,zx296702-lsp0crpm-clk",
>> +               zx296702_lsp0_clocks_init);
>> +
>> +static void __init zx296702_lsp1_clocks_init(struct device_node *np)
>> +{
>> +       struct clk **clk = lsp1clk;
>> +       int i;
>> +
>> +       lsp1crpm_base = of_iomap(np, 0);
>> +       WARN_ON(!lsp1crpm_base);
>> +
>> +       /* UART0 */
>> +       clk[ZX296702_UART0_WCLK_MUX] =
>> +               zx_mux("uart0_wclk_mux", uart_wclk_sel,
>> +                               ARRAY_SIZE(uart_wclk_sel), CLK_UART0, 4, 1);
>> +       /* FIXME: uart wclk enable bit is bit1 in. We hack it as reserved 31 for
>> +        * UART does not work after parent clk is disabled/enabled */
>> +       clk[ZX296702_UART0_WCLK] =
>> +               zx_gate("uart0_wclk", "uart0_wclk_mux", CLK_UART0, 31);
>> +       clk[ZX296702_UART0_PCLK] =
>> +               zx_gate("uart0_pclk", "lsp1_apb_pclk", CLK_UART0, 0);
>> +
>> +       /* UART1 */
>> +       clk[ZX296702_UART1_WCLK_MUX] =
>> +               zx_mux("uart1_wclk_mux", uart_wclk_sel,
>> +                               ARRAY_SIZE(uart_wclk_sel), CLK_UART1, 4, 1);
>> +       clk[ZX296702_UART1_WCLK] =
>> +               zx_gate("uart1_wclk", "uart1_wclk_mux", CLK_UART1, 1);
>> +       clk[ZX296702_UART1_PCLK] =
>> +               zx_gate("uart1_pclk", "lsp1_apb_pclk", CLK_UART1, 0);
>> +
>> +       /* SDMMC0 */
>> +       clk[ZX296702_SDMMC0_WCLK_MUX] =
>> +               zx_mux("sdmmc0_wclk_mux", sdmmc0_wclk_sel,
>> +                               ARRAY_SIZE(sdmmc0_wclk_sel), CLK_SDMMC0, 4, 1);
>> +       clk[ZX296702_SDMMC0_WCLK_DIV] =
>> +               zx_div("sdmmc0_wclk_div", "sdmmc0_wclk_mux", CLK_SDMMC0, 12, 4);
>> +       clk[ZX296702_SDMMC0_WCLK] =
>> +               zx_gate("sdmmc0_wclk", "sdmmc0_wclk_div", CLK_SDMMC0, 1);
>> +       clk[ZX296702_SDMMC0_PCLK] =
>> +               zx_gate("sdmmc0_pclk", "lsp1_apb_pclk", CLK_SDMMC0, 0);
>> +
>> +       for (i = 0; i < ARRAY_SIZE(lsp1clk); i++) {
>> +               if (IS_ERR(clk[i])) {
>> +                       pr_err("zx296702 clk %d: register failed with %ld\n",
>> +                               i, PTR_ERR(clk[i]));
>> +                       return;
>> +               }
>> +       }
>> +
>> +       lsp1clk_data.clks = lsp1clk;
>> +       lsp1clk_data.clk_num = ARRAY_SIZE(lsp1clk);
>> +       of_clk_add_provider(np, of_clk_src_onecell_get, &lsp1clk_data);
>> +}
>> +CLK_OF_DECLARE(zx296702_lsp1_clk, "zte,zx296702-lsp1crpm-clk",
>> +               zx296702_lsp1_clocks_init);
>> diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
>> new file mode 100644
>> index 0000000..419d93b
>> --- /dev/null
>> +++ b/drivers/clk/zte/clk.h
>> @@ -0,0 +1,30 @@
>> +/*
>> + * Copyright 2014 Linaro Ltd.
>> + * Copyright (C) 2014 ZTE Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __ZTE_CLK_H
>> +#define __ZTE_CLK_H
>> +
>> +struct zx_pll_config {
>> +       unsigned long rate;
>> +       u32 cfg0;
>> +       u32 cfg1;
>> +};
>> +
>> +struct clk_zx_pll {
>> +       struct clk_hw hw;
>> +       void __iomem *reg_base;
>> +       const struct zx_pll_config *lookup_table; /* order by rate asc */
>> +       int count;
>> +       spinlock_t *lock;
>> +};
>> +
>> +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
>> +       unsigned long flags, void __iomem *reg_base,
>> +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
>> +#endif
>> diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
>> new file mode 100644
>> index 0000000..c5e7ec2
>> --- /dev/null
>> +++ b/include/dt-bindings/clock/zx296702-clock.h
>> @@ -0,0 +1,162 @@
>> +/*
>> + * Copyright 2014 Linaro Ltd.
>> + * Copyright (C) 2014 ZTE Corporation.
>> + *
>> + * This program is free software; you can redistribute it and/or modify
>> + * it under the terms of the GNU General Public License version 2 as
>> + * published by the Free Software Foundation.
>> + */
>> +
>> +#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
>> +#define __DT_BINDINGS_CLOCK_ZX296702_H
>> +
>> +#define ZX296702_OSC                           0
>> +#define ZX296702_PLL_A9                                1
>> +#define ZX296702_PLL_A9_350M                   2
>> +#define ZX296702_PLL_MAC_1000M                 3
>> +#define ZX296702_PLL_MAC_333M                  4
>> +#define ZX296702_PLL_MM0_1188M                 5
>> +#define ZX296702_PLL_MM0_396M                  6
>> +#define ZX296702_PLL_MM0_198M                  7
>> +#define ZX296702_PLL_MM1_108M                  8
>> +#define ZX296702_PLL_MM1_72M                   9
>> +#define ZX296702_PLL_MM1_54M                   10
>> +#define ZX296702_PLL_LSP_104M                  11
>> +#define ZX296702_PLL_LSP_26M                   12
>> +#define ZX296702_PLL_AUDIO_294M912             13
>> +#define ZX296702_PLL_DDR_266M                  14
>> +#define ZX296702_CLK_148M5                     15
>> +#define ZX296702_MATRIX_ACLK                   16
>> +#define ZX296702_MAIN_HCLK                     17
>> +#define ZX296702_MAIN_PCLK                     18
>> +#define ZX296702_CLK_500                       19
>> +#define ZX296702_CLK_250                       20
>> +#define ZX296702_CLK_125                       21
>> +#define ZX296702_CLK_74M25                     22
>> +#define ZX296702_A9_WCLK                       23
>> +#define ZX296702_A9_AS1_ACLK_MUX               24
>> +#define ZX296702_A9_TRACE_CLKIN_MUX            25
>> +#define ZX296702_A9_AS1_ACLK_DIV               26
>> +#define ZX296702_CLK_2                         27
>> +#define ZX296702_CLK_27                                28
>> +#define ZX296702_DECPPU_ACLK_MUX               29
>> +#define ZX296702_PPU_ACLK_MUX                  30
>> +#define ZX296702_MALI400_ACLK_MUX              31
>> +#define ZX296702_VOU_ACLK_MUX                  32
>> +#define ZX296702_VOU_MAIN_WCLK_MUX             33
>> +#define ZX296702_VOU_AUX_WCLK_MUX              34
>> +#define ZX296702_VOU_SCALER_WCLK_MUX           35
>> +#define ZX296702_R2D_ACLK_MUX                  36
>> +#define ZX296702_R2D_WCLK_MUX                  37
>> +#define ZX296702_CLK_50                                38
>> +#define ZX296702_CLK_25                                39
>> +#define ZX296702_CLK_12                                40
>> +#define ZX296702_CLK_16M384                    41
>> +#define ZX296702_CLK_32K768                    42
>> +#define ZX296702_SEC_WCLK_DIV                  43
>> +#define ZX296702_DDR_WCLK_MUX                  44
>> +#define ZX296702_NAND_WCLK_MUX                 45
>> +#define ZX296702_LSP_26_WCLK_MUX               46
>> +#define ZX296702_A9_AS0_ACLK                   47
>> +#define ZX296702_A9_AS1_ACLK                   48
>> +#define ZX296702_A9_TRACE_CLKIN                        49
>> +#define ZX296702_DECPPU_AXI_M_ACLK             50
>> +#define ZX296702_DECPPU_AHB_S_HCLK             51
>> +#define ZX296702_PPU_AXI_M_ACLK                        52
>> +#define ZX296702_PPU_AHB_S_HCLK                        53
>> +#define ZX296702_VOU_AXI_M_ACLK                        54
>> +#define ZX296702_VOU_APB_PCLK                  55
>> +#define ZX296702_VOU_MAIN_CHANNEL_WCLK         56
>> +#define ZX296702_VOU_AUX_CHANNEL_WCLK          57
>> +#define ZX296702_VOU_HDMI_OSCLK_CEC            58
>> +#define ZX296702_VOU_SCALER_WCLK               59
>> +#define ZX296702_MALI400_AXI_M_ACLK            60
>> +#define ZX296702_MALI400_APB_PCLK              61
>> +#define ZX296702_R2D_WCLK                      62
>> +#define ZX296702_R2D_AXI_M_ACLK                        63
>> +#define ZX296702_R2D_AHB_HCLK                  64
>> +#define ZX296702_DDR3_AXI_S0_ACLK              65
>> +#define ZX296702_DDR3_APB_PCLK                 66
>> +#define ZX296702_DDR3_WCLK                     67
>> +#define ZX296702_USB20_0_AHB_HCLK              68
>> +#define ZX296702_USB20_0_EXTREFCLK             69
>> +#define ZX296702_USB20_1_AHB_HCLK              70
>> +#define ZX296702_USB20_1_EXTREFCLK             71
>> +#define ZX296702_USB20_2_AHB_HCLK              72
>> +#define ZX296702_USB20_2_EXTREFCLK             73
>> +#define ZX296702_GMAC_AXI_M_ACLK               74
>> +#define ZX296702_GMAC_APB_PCLK                 75
>> +#define ZX296702_GMAC_125_CLKIN                        76
>> +#define ZX296702_GMAC_RMII_CLKIN               77
>> +#define ZX296702_GMAC_25M_CLK                  78
>> +#define ZX296702_NANDFLASH_AHB_HCLK            79
>> +#define ZX296702_NANDFLASH_WCLK                        80
>> +#define ZX296702_LSP0_APB_PCLK                 81
>> +#define ZX296702_LSP0_AHB_HCLK                 82
>> +#define ZX296702_LSP0_26M_WCLK                 83
>> +#define ZX296702_LSP0_104M_WCLK                        84
>> +#define ZX296702_LSP0_16M384_WCLK              85
>> +#define ZX296702_LSP1_APB_PCLK                 86
>> +#define ZX296702_LSP1_26M_WCLK                 87
>> +#define ZX296702_LSP1_104M_WCLK                        88
>> +#define ZX296702_LSP1_32K_CLK                  89
>> +#define ZX296702_AON_HCLK                      90
>> +#define ZX296702_SYS_CTRL_PCLK                 91
>> +#define ZX296702_DMA_PCLK                      92
>> +#define ZX296702_DMA_ACLK                      93
>> +#define ZX296702_SEC_HCLK                      94
>> +#define ZX296702_AES_WCLK                      95
>> +#define ZX296702_DES_WCLK                      96
>> +#define ZX296702_IRAM_ACLK                     97
>> +#define ZX296702_IROM_ACLK                     98
>> +#define ZX296702_BOOT_CTRL_HCLK                        99
>> +#define ZX296702_EFUSE_CLK_30                  100
>> +#define ZX296702_VOU_MAIN_CHANNEL_DIV          101
>> +#define ZX296702_VOU_AUX_CHANNEL_DIV           102
>> +#define ZX296702_VOU_TV_ENC_HD_DIV             103
>> +#define ZX296702_VOU_TV_ENC_SD_DIV             104
>> +#define ZX296702_VL0_MUX                       105
>> +#define ZX296702_VL1_MUX                       106
>> +#define ZX296702_VL2_MUX                       107
>> +#define ZX296702_GL0_MUX                       108
>> +#define ZX296702_GL1_MUX                       109
>> +#define ZX296702_GL2_MUX                       110
>> +#define ZX296702_WB_MUX                                111
>> +#define ZX296702_HDMI_MUX                      112
>> +#define ZX296702_VOU_TV_ENC_HD_MUX             113
>> +#define ZX296702_VOU_TV_ENC_SD_MUX             114
>> +#define ZX296702_VL0_CLK                       115
>> +#define ZX296702_VL1_CLK                       116
>> +#define ZX296702_VL2_CLK                       117
>> +#define ZX296702_GL0_CLK                       118
>> +#define ZX296702_GL1_CLK                       119
>> +#define ZX296702_GL2_CLK                       120
>> +#define ZX296702_WB_CLK                                121
>> +#define ZX296702_CL_CLK                                122
>> +#define ZX296702_MAIN_MIX_CLK                  123
>> +#define ZX296702_AUX_MIX_CLK                   124
>> +#define ZX296702_HDMI_CLK                      125
>> +#define ZX296702_VOU_TV_ENC_HD_DAC_CLK         126
>> +#define ZX296702_VOU_TV_ENC_SD_DAC_CLK         127
>> +#define ZX296702_A9_PERIPHCLK                  128
>> +#define ZX296702_TOPCLK_END                    129
>> +
>> +#define ZX296702_SDMMC1_WCLK_MUX               0
>> +#define ZX296702_SDMMC1_WCLK_DIV               1
>> +#define ZX296702_SDMMC1_WCLK                   2
>> +#define ZX296702_SDMMC1_PCLK                   3
>> +#define ZX296702_LSP0CLK_END                   4
>> +
>> +#define ZX296702_UART0_WCLK_MUX                        0
>> +#define ZX296702_UART0_WCLK                    1
>> +#define ZX296702_UART0_PCLK                    2
>> +#define ZX296702_UART1_WCLK_MUX                        3
>> +#define ZX296702_UART1_WCLK                    4
>> +#define ZX296702_UART1_PCLK                    5
>> +#define ZX296702_SDMMC0_WCLK_MUX               6
>> +#define ZX296702_SDMMC0_WCLK_DIV               7
>> +#define ZX296702_SDMMC0_WCLK                   8
>> +#define ZX296702_SDMMC0_PCLK                   9
>> +#define ZX296702_LSP1CLK_END                   10
>> +
>> +#endif /* __DT_BINDINGS_CLOCK_ZX296702_H */
>> --
>> 1.9.1
>>
Stephen Boyd May 28, 2015, 10:16 p.m. UTC | #6
On 05/18, Jun Nie wrote:
> 2015-04-28 17:18 GMT+08:00 Jun Nie <jun.nie@linaro.org>:
> > It adds a clock driver for zx296702 SoC to register the clock tree to
> > Common Clock Framework.  All the clocks of bus topology and some the
> > peripheral clocks are ready with this commit. Some missing leaf clocks
> > for peripherals will be added later when needed.
> >
> Mike & Stephen,
> 
> Could you help review and ack this patch? So that this patch set can
> be merged together in Arnd side.

A bit late, but here it goes!

> > diff --git a/drivers/clk/zte/clk-pll.c b/drivers/clk/zte/clk-pll.c
> > new file mode 100644
> > index 0000000..f0ff0cb
> > --- /dev/null
> > +++ b/drivers/clk/zte/clk-pll.c
> > @@ -0,0 +1,180 @@
> > +
> > +static int zx_pll_enable(struct clk_hw *hw)
> > +{
> > +       struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
> > +       unsigned long timeout = jiffies + msecs_to_jiffies(500);
> > +       u32 reg;
> > +
> > +       reg = readl_relaxed(zx_pll->reg_base);
> > +       writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
> > +       while (!(readl_relaxed(zx_pll->reg_base) & LOCK_FLAG)) {
> > +               if (time_after(jiffies, timeout)) {

How does this work? zx_pll_enable() will be called with
interrupts disabled so it's possible that jiffies will never
increment while we're inside this loop. For polling bits we have
iopoll.h now, so maybe you can use that?

> > +                       pr_err("clk %s enable timeout\n",
> > +                               __clk_get_name(hw->clk));
> > +                       break;
> > +               }
> > +       }
> > +
> > +       return 0;
> > +}
> > +
[..]
> > +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
> > +       unsigned long flags, void __iomem *reg_base,
> > +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock)
> > +{
> > +       struct clk_zx_pll *zx_pll;
> > +       struct clk *clk;
> > +       struct clk_init_data init;
> > +
> > +       zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
> > +       if (!zx_pll)
> > +               return ERR_PTR(-ENOMEM);
> > +
> > +       init.name = name;
> > +       init.ops = &zx_pll_ops;
> > +       init.flags = flags;
> > +       init.parent_names = (parent_name ? &parent_name : NULL);
> > +       init.num_parents = (parent_name ? 1 : 0);

Useless parentheses.

> > +
> > +       zx_pll->reg_base = reg_base;
> > +       zx_pll->lookup_table = lookup_table;
> > +       zx_pll->count = count;
> > +       zx_pll->lock = lock;
> > +       zx_pll->hw.init = &init;
> > +
> > +       clk = clk_register(NULL, &zx_pll->hw);
> > +
> > +       if (IS_ERR(clk))

Why the extra newline?

> > +               kfree(zx_pll);
> > +
> > +       return clk;
> > +}
> > diff --git a/drivers/clk/zte/clk-zx296702.c b/drivers/clk/zte/clk-zx296702.c
> > new file mode 100644
> > index 0000000..5f4d7ed
> > --- /dev/null
> > +++ b/drivers/clk/zte/clk-zx296702.c
> > @@ -0,0 +1,658 @@
> > +/*
> > + * Copyright 2014 Linaro Ltd.
> > + * Copyright (C) 2014 ZTE Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#include <linux/clk.h>

Do you need this include? Applies to all files in this patch.

> > +#include <linux/clk-provider.h>
> > +#include <linux/of_address.h>
> > +#include <dt-bindings/clock/zx296702-clock.h>
> > +#include "clk.h"
> > +
> > +static DEFINE_SPINLOCK(reg_lock);
> > +
> > +static void __iomem *topcrm_base;
> > +static void __iomem *lsp0crpm_base;
> > +static void __iomem *lsp1crpm_base;
> > +
> > +static struct clk *topclk[ZX296702_TOPCLK_END];
> > +static struct clk *lsp0clk[ZX296702_LSP0CLK_END];
> > +static struct clk *lsp1clk[ZX296702_LSP1CLK_END];
> > +
> > +static struct clk_onecell_data topclk_data;
> > +static struct clk_onecell_data lsp0clk_data;
> > +static struct clk_onecell_data lsp1clk_data;
> > +
> > +#define CLK_MUX                        (topcrm_base + 0x04)
> > +#define CLK_DIV                        (topcrm_base + 0x08)
> > +#define CLK_EN0                        (topcrm_base + 0x0c)
> > +#define CLK_EN1                        (topcrm_base + 0x10)
> > +#define VOU_LOCAL_CLKEN                (topcrm_base + 0x68)
> > +#define VOU_LOCAL_CLKSEL       (topcrm_base + 0x70)
> > +#define VOU_LOCAL_DIV2_SET     (topcrm_base + 0x74)
> > +#define CLK_MUX1               (topcrm_base + 0x8c)
> > +
> > +#define CLK_SDMMC1             (lsp0crpm_base + 0x0c)
> > +
> > +#define CLK_UART0              (lsp1crpm_base + 0x20)
> > +#define CLK_UART1              (lsp1crpm_base + 0x24)
> > +#define CLK_SDMMC0             (lsp1crpm_base + 0x2c)
> > +
> > +static const struct zx_pll_config pll_a9_config[] = {
> > +       { .rate = 700000000, .cfg0 = 0x800405d1, .cfg1 = 0x04555555 },
> > +       { .rate = 800000000, .cfg0 = 0x80040691, .cfg1 = 0x04aaaaaa },
> > +       { .rate = 900000000, .cfg0 = 0x80040791, .cfg1 = 0x04000000 },
> > +       { .rate = 1000000000, .cfg0 = 0x80040851, .cfg1 = 0x04555555 },
> > +       { .rate = 1100000000, .cfg0 = 0x80040911, .cfg1 = 0x04aaaaaa },
> > +       { .rate = 1200000000, .cfg0 = 0x80040a11, .cfg1 = 0x04000000 },
> > +};
> > +
> > +static const struct clk_div_table main_hlk_div[] = {
> > +       { .val = 1, .div = 2, },
> > +       { .val = 3, .div = 4, },
> > +       { /* sentinel */ }
> > +};
> > +
> > +static const struct clk_div_table a9_as1_aclk_divider[] = {
> > +       { .val = 0, .div = 1, },
> > +       { .val = 1, .div = 2, },
> > +       { .val = 3, .div = 4, },
> > +       { /* sentinel */ }
> > +};
> > +
> > +static const struct clk_div_table sec_wclk_divider[] = {
> > +       { .val = 0, .div = 1, },
> > +       { .val = 1, .div = 2, },
> > +       { .val = 3, .div = 4, },
> > +       { .val = 5, .div = 6, },
> > +       { .val = 7, .div = 8, },
> > +       { /* sentinel */ }
> > +};
> > +
> > +static const char const *matrix_aclk_sel[] = {
> > +       "pll_mm0_198M",
> > +       "osc",
> > +       "clk_148M5",
> > +       "pll_lsp_104M",
> > +};
> > +
> > +static const char const *a9_wclk_sel[] = {
> > +       "pll_a9",
> > +       "osc",
> > +       "clk_500",
> > +       "clk_250",
> > +};
> > +
> > +static const char const *a9_as1_aclk_sel[] = {
> > +       "clk_250",
> > +       "osc",
> > +       "pll_mm0_396M",
> > +       "pll_mac_333M",
> > +};
> > +
> > +static const char const *a9_trace_clkin_sel[] = {
> > +       "clk_74M25",
> > +       "pll_mm1_108M",
> > +       "clk_125",
> > +       "clk_148M5",
> > +};
> > +
> > +static const char const *decppu_aclk_sel[] = {

const char const doesn't make any sense. Did you mean const char
* const instead? If so, you'll need the patch in clk-next that
* allows that to work.

> > +       "clk_250",
> > +       "pll_mm0_198M",
> > +       "pll_lsp_104M",
> > +       "pll_audio_294M912",
> > +};
> > +
> > +static const char const *vou_main_wclk_sel[] = {
> > +       "clk_148M5",
> > +       "clk_74M25",
[..]
> > diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
> > new file mode 100644
> > index 0000000..419d93b
> > --- /dev/null
> > +++ b/drivers/clk/zte/clk.h
> > @@ -0,0 +1,30 @@
> > +/*
> > + * Copyright 2014 Linaro Ltd.
> > + * Copyright (C) 2014 ZTE Corporation.
> > + *
> > + * This program is free software; you can redistribute it and/or modify
> > + * it under the terms of the GNU General Public License version 2 as
> > + * published by the Free Software Foundation.
> > + */
> > +
> > +#ifndef __ZTE_CLK_H
> > +#define __ZTE_CLK_H

#include <linux/clk-provider.h> for clk_hw usage?
#include <linux/spinlock.h> for spinlock_t usage?

> > +
> > +struct zx_pll_config {
> > +       unsigned long rate;
> > +       u32 cfg0;
> > +       u32 cfg1;
> > +};
> > +
> > +struct clk_zx_pll {
> > +       struct clk_hw hw;
> > +       void __iomem *reg_base;
> > +       const struct zx_pll_config *lookup_table; /* order by rate asc */
> > +       int count;
> > +       spinlock_t *lock;
> > +};
> > +
> > +struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
> > +       unsigned long flags, void __iomem *reg_base,
> > +       const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
> > +#endif
diff mbox

Patch

diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile
index 3d00c25..f4c68be 100644
--- a/drivers/clk/Makefile
+++ b/drivers/clk/Makefile
@@ -72,4 +72,5 @@  obj-$(CONFIG_ARCH_OMAP2PLUS)		+= ti/
 obj-$(CONFIG_ARCH_U8500)		+= ux500/
 obj-$(CONFIG_COMMON_CLK_VERSATILE)	+= versatile/
 obj-$(CONFIG_X86)			+= x86/
+obj-$(CONFIG_ARCH_ZX)			+= zte/
 obj-$(CONFIG_ARCH_ZYNQ)			+= zynq/
diff --git a/drivers/clk/zte/Makefile b/drivers/clk/zte/Makefile
new file mode 100644
index 0000000..95b707c
--- /dev/null
+++ b/drivers/clk/zte/Makefile
@@ -0,0 +1,2 @@ 
+obj-y := clk-pll.o
+obj-$(CONFIG_SOC_ZX296702) += clk-zx296702.o
diff --git a/drivers/clk/zte/clk-pll.c b/drivers/clk/zte/clk-pll.c
new file mode 100644
index 0000000..f0ff0cb
--- /dev/null
+++ b/drivers/clk/zte/clk-pll.c
@@ -0,0 +1,180 @@ 
+/*
+ * Copyright 2014 Linaro Ltd.
+ * Copyright (C) 2014 ZTE Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+
+#include "clk.h"
+
+#define to_clk_zx_pll(_hw) container_of(_hw, struct clk_zx_pll, hw)
+
+#define CFG0_CFG1_OFFSET 4
+#define LOCK_FLAG BIT(30)
+#define POWER_DOWN BIT(31)
+
+static int rate_to_idx(struct clk_zx_pll *zx_pll, unsigned long rate)
+{
+	const struct zx_pll_config *config = zx_pll->lookup_table;
+	int i;
+
+	for (i = 0; i < zx_pll->count; i++) {
+		if (config[i].rate > rate)
+			return i > 0 ? i - 1 : 0;
+
+		if (config[i].rate == rate)
+			return i;
+	}
+
+	return i - 1;
+}
+
+static int hw_to_idx(struct clk_zx_pll *zx_pll)
+{
+	const struct zx_pll_config *config = zx_pll->lookup_table;
+	u32 hw_cfg0, hw_cfg1;
+	int i;
+
+	hw_cfg0 = readl_relaxed(zx_pll->reg_base);
+	hw_cfg1 = readl_relaxed(zx_pll->reg_base + CFG0_CFG1_OFFSET);
+
+	/* For matching the value in lookup table */
+	hw_cfg0 &= ~LOCK_FLAG;
+	hw_cfg0 |= POWER_DOWN;
+
+	for (i = 0; i < zx_pll->count; i++) {
+		if (hw_cfg0 == config[i].cfg0 && hw_cfg1 == config[i].cfg1)
+			return i;
+	}
+
+	return -1;
+}
+
+static unsigned long zx_pll_recalc_rate(struct clk_hw *hw,
+					unsigned long parent_rate)
+{
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	int idx;
+
+	idx = hw_to_idx(zx_pll);
+	if (unlikely(idx == -1))
+		return 0;
+
+	return zx_pll->lookup_table[idx].rate;
+}
+
+static long zx_pll_round_rate(struct clk_hw *hw, unsigned long rate,
+			      unsigned long *prate)
+{
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	int idx;
+
+	idx = rate_to_idx(zx_pll, rate);
+
+	return zx_pll->lookup_table[idx].rate;
+}
+
+static int zx_pll_set_rate(struct clk_hw *hw, unsigned long rate,
+			   unsigned long parent_rate)
+{
+	/* Assume current cpu is not running on current PLL */
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	const struct zx_pll_config *config;
+	int idx;
+
+	idx = rate_to_idx(zx_pll, rate);
+	config = &zx_pll->lookup_table[idx];
+
+	writel_relaxed(config->cfg0, zx_pll->reg_base);
+	writel_relaxed(config->cfg1, zx_pll->reg_base + CFG0_CFG1_OFFSET);
+
+	return 0;
+}
+
+static int zx_pll_enable(struct clk_hw *hw)
+{
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	unsigned long timeout = jiffies + msecs_to_jiffies(500);
+	u32 reg;
+
+	reg = readl_relaxed(zx_pll->reg_base);
+	writel_relaxed(reg & ~POWER_DOWN, zx_pll->reg_base);
+	while (!(readl_relaxed(zx_pll->reg_base) & LOCK_FLAG)) {
+		if (time_after(jiffies, timeout)) {
+			pr_err("clk %s enable timeout\n",
+				__clk_get_name(hw->clk));
+			break;
+		}
+	}
+
+	return 0;
+}
+
+static void zx_pll_disable(struct clk_hw *hw)
+{
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	u32 reg;
+
+	reg = readl_relaxed(zx_pll->reg_base);
+	writel_relaxed(reg | POWER_DOWN, zx_pll->reg_base);
+}
+
+static int zx_pll_is_enabled(struct clk_hw *hw)
+{
+	struct clk_zx_pll *zx_pll = to_clk_zx_pll(hw);
+	u32 reg;
+
+	reg = readl_relaxed(zx_pll->reg_base);
+
+	return !(reg & POWER_DOWN);
+}
+
+static const struct clk_ops zx_pll_ops = {
+	.recalc_rate = zx_pll_recalc_rate,
+	.round_rate = zx_pll_round_rate,
+	.set_rate = zx_pll_set_rate,
+	.enable = zx_pll_enable,
+	.disable = zx_pll_disable,
+	.is_enabled = zx_pll_is_enabled,
+};
+
+struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
+	unsigned long flags, void __iomem *reg_base,
+	const struct zx_pll_config *lookup_table, int count, spinlock_t *lock)
+{
+	struct clk_zx_pll *zx_pll;
+	struct clk *clk;
+	struct clk_init_data init;
+
+	zx_pll = kzalloc(sizeof(*zx_pll), GFP_KERNEL);
+	if (!zx_pll)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = name;
+	init.ops = &zx_pll_ops;
+	init.flags = flags;
+	init.parent_names = (parent_name ? &parent_name : NULL);
+	init.num_parents = (parent_name ? 1 : 0);
+
+	zx_pll->reg_base = reg_base;
+	zx_pll->lookup_table = lookup_table;
+	zx_pll->count = count;
+	zx_pll->lock = lock;
+	zx_pll->hw.init = &init;
+
+	clk = clk_register(NULL, &zx_pll->hw);
+
+	if (IS_ERR(clk))
+		kfree(zx_pll);
+
+	return clk;
+}
diff --git a/drivers/clk/zte/clk-zx296702.c b/drivers/clk/zte/clk-zx296702.c
new file mode 100644
index 0000000..5f4d7ed
--- /dev/null
+++ b/drivers/clk/zte/clk-zx296702.c
@@ -0,0 +1,658 @@ 
+/*
+ * Copyright 2014 Linaro Ltd.
+ * Copyright (C) 2014 ZTE Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/zx296702-clock.h>
+#include "clk.h"
+
+static DEFINE_SPINLOCK(reg_lock);
+
+static void __iomem *topcrm_base;
+static void __iomem *lsp0crpm_base;
+static void __iomem *lsp1crpm_base;
+
+static struct clk *topclk[ZX296702_TOPCLK_END];
+static struct clk *lsp0clk[ZX296702_LSP0CLK_END];
+static struct clk *lsp1clk[ZX296702_LSP1CLK_END];
+
+static struct clk_onecell_data topclk_data;
+static struct clk_onecell_data lsp0clk_data;
+static struct clk_onecell_data lsp1clk_data;
+
+#define CLK_MUX			(topcrm_base + 0x04)
+#define CLK_DIV			(topcrm_base + 0x08)
+#define CLK_EN0			(topcrm_base + 0x0c)
+#define CLK_EN1			(topcrm_base + 0x10)
+#define VOU_LOCAL_CLKEN		(topcrm_base + 0x68)
+#define VOU_LOCAL_CLKSEL	(topcrm_base + 0x70)
+#define VOU_LOCAL_DIV2_SET	(topcrm_base + 0x74)
+#define CLK_MUX1		(topcrm_base + 0x8c)
+
+#define CLK_SDMMC1		(lsp0crpm_base + 0x0c)
+
+#define CLK_UART0		(lsp1crpm_base + 0x20)
+#define CLK_UART1		(lsp1crpm_base + 0x24)
+#define CLK_SDMMC0		(lsp1crpm_base + 0x2c)
+
+static const struct zx_pll_config pll_a9_config[] = {
+	{ .rate = 700000000, .cfg0 = 0x800405d1, .cfg1 = 0x04555555 },
+	{ .rate = 800000000, .cfg0 = 0x80040691, .cfg1 = 0x04aaaaaa },
+	{ .rate = 900000000, .cfg0 = 0x80040791, .cfg1 = 0x04000000 },
+	{ .rate = 1000000000, .cfg0 = 0x80040851, .cfg1 = 0x04555555 },
+	{ .rate = 1100000000, .cfg0 = 0x80040911, .cfg1 = 0x04aaaaaa },
+	{ .rate = 1200000000, .cfg0 = 0x80040a11, .cfg1 = 0x04000000 },
+};
+
+static const struct clk_div_table main_hlk_div[] = {
+	{ .val = 1, .div = 2, },
+	{ .val = 3, .div = 4, },
+	{ /* sentinel */ }
+};
+
+static const struct clk_div_table a9_as1_aclk_divider[] = {
+	{ .val = 0, .div = 1, },
+	{ .val = 1, .div = 2, },
+	{ .val = 3, .div = 4, },
+	{ /* sentinel */ }
+};
+
+static const struct clk_div_table sec_wclk_divider[] = {
+	{ .val = 0, .div = 1, },
+	{ .val = 1, .div = 2, },
+	{ .val = 3, .div = 4, },
+	{ .val = 5, .div = 6, },
+	{ .val = 7, .div = 8, },
+	{ /* sentinel */ }
+};
+
+static const char const *matrix_aclk_sel[] = {
+	"pll_mm0_198M",
+	"osc",
+	"clk_148M5",
+	"pll_lsp_104M",
+};
+
+static const char const *a9_wclk_sel[] = {
+	"pll_a9",
+	"osc",
+	"clk_500",
+	"clk_250",
+};
+
+static const char const *a9_as1_aclk_sel[] = {
+	"clk_250",
+	"osc",
+	"pll_mm0_396M",
+	"pll_mac_333M",
+};
+
+static const char const *a9_trace_clkin_sel[] = {
+	"clk_74M25",
+	"pll_mm1_108M",
+	"clk_125",
+	"clk_148M5",
+};
+
+static const char const *decppu_aclk_sel[] = {
+	"clk_250",
+	"pll_mm0_198M",
+	"pll_lsp_104M",
+	"pll_audio_294M912",
+};
+
+static const char const *vou_main_wclk_sel[] = {
+	"clk_148M5",
+	"clk_74M25",
+	"clk_27",
+	"pll_mm1_54M",
+};
+
+static const char const *vou_scaler_wclk_sel[] = {
+	"clk_250",
+	"pll_mac_333M",
+	"pll_audio_294M912",
+	"pll_mm0_198M",
+};
+
+static const char const *r2d_wclk_sel[] = {
+	"pll_audio_294M912",
+	"pll_mac_333M",
+	"pll_a9_350M",
+	"pll_mm0_396M",
+};
+
+static const char const *ddr_wclk_sel[] = {
+	"pll_mac_333M",
+	"pll_ddr_266M",
+	"pll_audio_294M912",
+	"pll_mm0_198M",
+};
+
+static const char const *nand_wclk_sel[] = {
+	"pll_lsp_104M",
+	"osc",
+};
+
+static const char const *lsp_26_wclk_sel[] = {
+	"pll_lsp_26M",
+	"osc",
+};
+
+static const char const *vl0_sel[] = {
+	"vou_main_channel_div",
+	"vou_aux_channel_div",
+};
+
+static const char const *hdmi_sel[] = {
+	"vou_main_channel_wclk",
+	"vou_aux_channel_wclk",
+};
+
+static const char const *sdmmc0_wclk_sel[] = {
+	"lsp1_104M_wclk",
+	"lsp1_26M_wclk",
+};
+
+static const char const *sdmmc1_wclk_sel[] = {
+	"lsp0_104M_wclk",
+	"lsp0_26M_wclk",
+};
+
+static const char const *uart_wclk_sel[] = {
+	"lsp1_104M_wclk",
+	"lsp1_26M_wclk",
+};
+
+static inline struct clk *zx_divtbl(const char *name, const char *parent,
+				    void __iomem *reg, u8 shift, u8 width,
+				    const struct clk_div_table *table)
+{
+	return clk_register_divider_table(NULL, name, parent, 0, reg, shift,
+					  width, 0, table, &reg_lock);
+}
+
+static inline struct clk *zx_div(const char *name, const char *parent,
+				 void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_register_divider(NULL, name, parent, 0,
+				    reg, shift, width, 0, &reg_lock);
+}
+
+static inline struct clk *zx_mux(const char *name, const char **parents,
+		int num_parents, void __iomem *reg, u8 shift, u8 width)
+{
+	return clk_register_mux(NULL, name, parents, num_parents,
+				0, reg, shift, width, 0, &reg_lock);
+}
+
+static inline struct clk *zx_gate(const char *name, const char *parent,
+				  void __iomem *reg, u8 shift)
+{
+	return clk_register_gate(NULL, name, parent, CLK_IGNORE_UNUSED,
+				 reg, shift, 0, &reg_lock);
+}
+
+static void __init zx296702_top_clocks_init(struct device_node *np)
+{
+	struct clk **clk = topclk;
+	int i;
+
+	topcrm_base = of_iomap(np, 0);
+	WARN_ON(!topcrm_base);
+
+	clk[ZX296702_OSC] =
+		clk_register_fixed_rate(NULL, "osc", NULL, CLK_IS_ROOT,
+				30000000);
+	clk[ZX296702_PLL_A9] =
+		clk_register_zx_pll("pll_a9", "osc", 0, topcrm_base
+				+ 0x01c, pll_a9_config,
+				ARRAY_SIZE(pll_a9_config), &reg_lock);
+
+	/* TODO: pll_a9_350M look like changeble follow a9 pll */
+	clk[ZX296702_PLL_A9_350M] =
+		clk_register_fixed_rate(NULL, "pll_a9_350M", "osc", 0,
+				350000000);
+	clk[ZX296702_PLL_MAC_1000M] =
+		clk_register_fixed_rate(NULL, "pll_mac_1000M", "osc", 0,
+				1000000000);
+	clk[ZX296702_PLL_MAC_333M] =
+		clk_register_fixed_rate(NULL, "pll_mac_333M",	 "osc", 0,
+				333000000);
+	clk[ZX296702_PLL_MM0_1188M] =
+		clk_register_fixed_rate(NULL, "pll_mm0_1188M", "osc", 0,
+				1188000000);
+	clk[ZX296702_PLL_MM0_396M] =
+		clk_register_fixed_rate(NULL, "pll_mm0_396M",  "osc", 0,
+				396000000);
+	clk[ZX296702_PLL_MM0_198M] =
+		clk_register_fixed_rate(NULL, "pll_mm0_198M",  "osc", 0,
+				198000000);
+	clk[ZX296702_PLL_MM1_108M] =
+		clk_register_fixed_rate(NULL, "pll_mm1_108M",  "osc", 0,
+				108000000);
+	clk[ZX296702_PLL_MM1_72M] =
+		clk_register_fixed_rate(NULL, "pll_mm1_72M",	 "osc", 0,
+				72000000);
+	clk[ZX296702_PLL_MM1_54M] =
+		clk_register_fixed_rate(NULL, "pll_mm1_54M",	 "osc", 0,
+				54000000);
+	clk[ZX296702_PLL_LSP_104M] =
+		clk_register_fixed_rate(NULL, "pll_lsp_104M",  "osc", 0,
+				104000000);
+	clk[ZX296702_PLL_LSP_26M] =
+		clk_register_fixed_rate(NULL, "pll_lsp_26M",	 "osc", 0,
+				26000000);
+	clk[ZX296702_PLL_DDR_266M] =
+		clk_register_fixed_rate(NULL, "pll_ddr_266M",	 "osc", 0,
+				266000000);
+	clk[ZX296702_PLL_AUDIO_294M912] =
+		clk_register_fixed_rate(NULL, "pll_audio_294M912", "osc", 0,
+				294912000);
+
+	/* bus clock */
+	clk[ZX296702_MATRIX_ACLK] =
+		zx_mux("matrix_aclk", matrix_aclk_sel,
+				ARRAY_SIZE(matrix_aclk_sel), CLK_MUX, 2, 2);
+	clk[ZX296702_MAIN_HCLK] =
+		zx_divtbl("main_hclk", "matrix_aclk", CLK_DIV, 0, 2,
+				main_hlk_div);
+	clk[ZX296702_MAIN_PCLK] =
+		zx_divtbl("main_pclk", "matrix_aclk", CLK_DIV, 2, 2,
+				main_hlk_div);
+
+	/* cpu clock */
+	clk[ZX296702_CLK_500] =
+		clk_register_fixed_factor(NULL, "clk_500", "pll_mac_1000M", 0,
+				1, 2);
+	clk[ZX296702_CLK_250] =
+		clk_register_fixed_factor(NULL, "clk_250", "pll_mac_1000M", 0,
+				1, 4);
+	clk[ZX296702_CLK_125] =
+		clk_register_fixed_factor(NULL, "clk_125", "clk_250", 0, 1, 2);
+	clk[ZX296702_CLK_148M5] =
+		clk_register_fixed_factor(NULL, "clk_148M5", "pll_mm0_1188M", 0,
+				1, 8);
+	clk[ZX296702_CLK_74M25] =
+		clk_register_fixed_factor(NULL, "clk_74M25", "pll_mm0_1188M", 0,
+				1, 16);
+	clk[ZX296702_A9_WCLK] =
+		zx_mux("a9_wclk", a9_wclk_sel, ARRAY_SIZE(a9_wclk_sel), CLK_MUX,
+				0, 2);
+	clk[ZX296702_A9_AS1_ACLK_MUX] =
+		zx_mux("a9_as1_aclk_mux", a9_as1_aclk_sel,
+				ARRAY_SIZE(a9_as1_aclk_sel), CLK_MUX, 4, 2);
+	clk[ZX296702_A9_TRACE_CLKIN_MUX] =
+		zx_mux("a9_trace_clkin_mux", a9_trace_clkin_sel,
+				ARRAY_SIZE(a9_trace_clkin_sel), CLK_MUX1, 0, 2);
+	clk[ZX296702_A9_AS1_ACLK_DIV] =
+		zx_divtbl("a9_as1_aclk_div", "a9_as1_aclk_mux", CLK_DIV, 4, 2,
+				a9_as1_aclk_divider);
+
+	/* multi-media clock */
+	clk[ZX296702_CLK_2] =
+		clk_register_fixed_factor(NULL, "clk_2", "pll_mm1_72M", 0,
+				1, 36);
+	clk[ZX296702_CLK_27] =
+		clk_register_fixed_factor(NULL, "clk_27", "pll_mm1_54M", 0,
+				1, 2);
+	clk[ZX296702_DECPPU_ACLK_MUX] =
+		zx_mux("decppu_aclk_mux", decppu_aclk_sel,
+				ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 6, 2);
+	clk[ZX296702_PPU_ACLK_MUX] =
+		zx_mux("ppu_aclk_mux", decppu_aclk_sel,
+				ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 8, 2);
+	clk[ZX296702_MALI400_ACLK_MUX] =
+		zx_mux("mali400_aclk_mux", decppu_aclk_sel,
+				ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 12, 2);
+	clk[ZX296702_VOU_ACLK_MUX] =
+		zx_mux("vou_aclk_mux", decppu_aclk_sel,
+				ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 10, 2);
+	clk[ZX296702_VOU_MAIN_WCLK_MUX] =
+		zx_mux("vou_main_wclk_mux", vou_main_wclk_sel,
+				ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 14, 2);
+	clk[ZX296702_VOU_AUX_WCLK_MUX] =
+		zx_mux("vou_aux_wclk_mux", vou_main_wclk_sel,
+				ARRAY_SIZE(vou_main_wclk_sel), CLK_MUX, 16, 2);
+	clk[ZX296702_VOU_SCALER_WCLK_MUX] =
+		zx_mux("vou_scaler_wclk_mux", vou_scaler_wclk_sel,
+				ARRAY_SIZE(vou_scaler_wclk_sel), CLK_MUX,
+				18, 2);
+	clk[ZX296702_R2D_ACLK_MUX] =
+		zx_mux("r2d_aclk_mux", decppu_aclk_sel,
+				ARRAY_SIZE(decppu_aclk_sel), CLK_MUX, 20, 2);
+	clk[ZX296702_R2D_WCLK_MUX] =
+		zx_mux("r2d_wclk_mux", r2d_wclk_sel,
+				ARRAY_SIZE(r2d_wclk_sel), CLK_MUX, 22, 2);
+
+	/* other clock */
+	clk[ZX296702_CLK_50] =
+		clk_register_fixed_factor(NULL, "clk_50", "pll_mac_1000M",
+				0, 1, 20);
+	clk[ZX296702_CLK_25] =
+		clk_register_fixed_factor(NULL, "clk_25", "pll_mac_1000M",
+				0, 1, 40);
+	clk[ZX296702_CLK_12] =
+		clk_register_fixed_factor(NULL, "clk_12", "pll_mm1_72M",
+				0, 1, 6);
+	clk[ZX296702_CLK_16M384] =
+		clk_register_fixed_factor(NULL, "clk_16M384",
+				"pll_audio_294M912", 0, 1, 18);
+	clk[ZX296702_CLK_32K768] =
+		clk_register_fixed_factor(NULL, "clk_32K768", "clk_16M384",
+				0, 1, 500);
+	clk[ZX296702_SEC_WCLK_DIV] =
+		zx_divtbl("sec_wclk_div", "pll_lsp_104M", CLK_DIV, 6, 3,
+				sec_wclk_divider);
+	clk[ZX296702_DDR_WCLK_MUX] =
+		zx_mux("ddr_wclk_mux", ddr_wclk_sel,
+				ARRAY_SIZE(ddr_wclk_sel), CLK_MUX, 24, 2);
+	clk[ZX296702_NAND_WCLK_MUX] =
+		zx_mux("nand_wclk_mux", nand_wclk_sel,
+				ARRAY_SIZE(nand_wclk_sel), CLK_MUX, 24, 2);
+	clk[ZX296702_LSP_26_WCLK_MUX] =
+		zx_mux("lsp_26_wclk_mux", lsp_26_wclk_sel,
+				ARRAY_SIZE(lsp_26_wclk_sel), CLK_MUX, 27, 1);
+
+	/* gates */
+	clk[ZX296702_A9_AS0_ACLK] =
+		zx_gate("a9_as0_aclk",	"matrix_aclk",		CLK_EN0, 0);
+	clk[ZX296702_A9_AS1_ACLK] =
+		zx_gate("a9_as1_aclk",	"a9_as1_aclk_div",	CLK_EN0, 1);
+	clk[ZX296702_A9_TRACE_CLKIN] =
+		zx_gate("a9_trace_clkin", "a9_trace_clkin_mux",	CLK_EN0, 2);
+	clk[ZX296702_DECPPU_AXI_M_ACLK] =
+		zx_gate("decppu_axi_m_aclk", "decppu_aclk_mux", CLK_EN0, 3);
+	clk[ZX296702_DECPPU_AHB_S_HCLK] =
+		zx_gate("decppu_ahb_s_hclk",	"main_hclk",	CLK_EN0, 4);
+	clk[ZX296702_PPU_AXI_M_ACLK] =
+		zx_gate("ppu_axi_m_aclk",	"ppu_aclk_mux",	CLK_EN0, 5);
+	clk[ZX296702_PPU_AHB_S_HCLK] =
+		zx_gate("ppu_ahb_s_hclk",	"main_hclk",	CLK_EN0, 6);
+	clk[ZX296702_VOU_AXI_M_ACLK] =
+		zx_gate("vou_axi_m_aclk",	"vou_aclk_mux",	CLK_EN0, 7);
+	clk[ZX296702_VOU_APB_PCLK] =
+		zx_gate("vou_apb_pclk",	"main_pclk",		CLK_EN0, 8);
+	clk[ZX296702_VOU_MAIN_CHANNEL_WCLK] =
+		zx_gate("vou_main_channel_wclk", "vou_main_wclk_mux",
+				CLK_EN0, 9);
+	clk[ZX296702_VOU_AUX_CHANNEL_WCLK] =
+		zx_gate("vou_aux_channel_wclk", "vou_aux_wclk_mux",
+				CLK_EN0, 10);
+	clk[ZX296702_VOU_HDMI_OSCLK_CEC] =
+		zx_gate("vou_hdmi_osclk_cec", "clk_2",		CLK_EN0, 11);
+	clk[ZX296702_VOU_SCALER_WCLK] =
+		zx_gate("vou_scaler_wclk", "vou_scaler_wclk_mux", CLK_EN0, 12);
+	clk[ZX296702_MALI400_AXI_M_ACLK] =
+		zx_gate("mali400_axi_m_aclk", "mali400_aclk_mux", CLK_EN0, 13);
+	clk[ZX296702_MALI400_APB_PCLK] =
+		zx_gate("mali400_apb_pclk",	"main_pclk",	CLK_EN0, 14);
+	clk[ZX296702_R2D_WCLK] =
+		zx_gate("r2d_wclk",		"r2d_wclk_mux",	CLK_EN0, 15);
+	clk[ZX296702_R2D_AXI_M_ACLK] =
+		zx_gate("r2d_axi_m_aclk",	"r2d_aclk_mux",	CLK_EN0, 16);
+	clk[ZX296702_R2D_AHB_HCLK] =
+		zx_gate("r2d_ahb_hclk",		"main_hclk",	CLK_EN0, 17);
+	clk[ZX296702_DDR3_AXI_S0_ACLK] =
+		zx_gate("ddr3_axi_s0_aclk",	"matrix_aclk",	CLK_EN0, 18);
+	clk[ZX296702_DDR3_APB_PCLK] =
+		zx_gate("ddr3_apb_pclk",	"main_pclk",	CLK_EN0, 19);
+	clk[ZX296702_DDR3_WCLK] =
+		zx_gate("ddr3_wclk",		"ddr_wclk_mux",	CLK_EN0, 20);
+	clk[ZX296702_USB20_0_AHB_HCLK] =
+		zx_gate("usb20_0_ahb_hclk",	"main_hclk",	CLK_EN0, 21);
+	clk[ZX296702_USB20_0_EXTREFCLK] =
+		zx_gate("usb20_0_extrefclk",	"clk_12",	CLK_EN0, 22);
+	clk[ZX296702_USB20_1_AHB_HCLK] =
+		zx_gate("usb20_1_ahb_hclk",	"main_hclk",	CLK_EN0, 23);
+	clk[ZX296702_USB20_1_EXTREFCLK] =
+		zx_gate("usb20_1_extrefclk",	"clk_12",	CLK_EN0, 24);
+	clk[ZX296702_USB20_2_AHB_HCLK] =
+		zx_gate("usb20_2_ahb_hclk",	"main_hclk",	CLK_EN0, 25);
+	clk[ZX296702_USB20_2_EXTREFCLK] =
+		zx_gate("usb20_2_extrefclk",	"clk_12",	CLK_EN0, 26);
+	clk[ZX296702_GMAC_AXI_M_ACLK] =
+		zx_gate("gmac_axi_m_aclk",	"matrix_aclk",	CLK_EN0, 27);
+	clk[ZX296702_GMAC_APB_PCLK] =
+		zx_gate("gmac_apb_pclk",	"main_pclk",	CLK_EN0, 28);
+	clk[ZX296702_GMAC_125_CLKIN] =
+		zx_gate("gmac_125_clkin",	"clk_125",	CLK_EN0, 29);
+	clk[ZX296702_GMAC_RMII_CLKIN] =
+		zx_gate("gmac_rmii_clkin",	"clk_50",	CLK_EN0, 30);
+	clk[ZX296702_GMAC_25M_CLK] =
+		zx_gate("gmac_25M_clk",		"clk_25",	CLK_EN0, 31);
+	clk[ZX296702_NANDFLASH_AHB_HCLK] =
+		zx_gate("nandflash_ahb_hclk", "main_hclk",	CLK_EN1, 0);
+	clk[ZX296702_NANDFLASH_WCLK] =
+		zx_gate("nandflash_wclk",     "nand_wclk_mux",	CLK_EN1, 1);
+	clk[ZX296702_LSP0_APB_PCLK] =
+		zx_gate("lsp0_apb_pclk",	"main_pclk",	CLK_EN1, 2);
+	clk[ZX296702_LSP0_AHB_HCLK] =
+		zx_gate("lsp0_ahb_hclk",	"main_hclk",	CLK_EN1, 3);
+	clk[ZX296702_LSP0_26M_WCLK] =
+		zx_gate("lsp0_26M_wclk",   "lsp_26_wclk_mux",	CLK_EN1, 4);
+	clk[ZX296702_LSP0_104M_WCLK] =
+		zx_gate("lsp0_104M_wclk",	"pll_lsp_104M",	CLK_EN1, 5);
+	clk[ZX296702_LSP0_16M384_WCLK] =
+		zx_gate("lsp0_16M384_wclk",	"clk_16M384",	CLK_EN1, 6);
+	clk[ZX296702_LSP1_APB_PCLK] =
+		zx_gate("lsp1_apb_pclk",	"main_pclk",	CLK_EN1, 7);
+	/* FIXME: wclk enable bit is bit8. We hack it as reserved 31 for
+	 * UART does not work after parent clk is disabled/enabled */
+	clk[ZX296702_LSP1_26M_WCLK] =
+		zx_gate("lsp1_26M_wclk",     "lsp_26_wclk_mux",	CLK_EN1, 31);
+	clk[ZX296702_LSP1_104M_WCLK] =
+		zx_gate("lsp1_104M_wclk",    "pll_lsp_104M",	CLK_EN1, 9);
+	clk[ZX296702_LSP1_32K_CLK] =
+		zx_gate("lsp1_32K_clk",	"clk_32K768",		CLK_EN1, 10);
+	clk[ZX296702_AON_HCLK] =
+		zx_gate("aon_hclk",		"main_hclk",	CLK_EN1, 11);
+	clk[ZX296702_SYS_CTRL_PCLK] =
+		zx_gate("sys_ctrl_pclk",	"main_pclk",	CLK_EN1, 12);
+	clk[ZX296702_DMA_PCLK] =
+		zx_gate("dma_pclk",		"main_pclk",	CLK_EN1, 13);
+	clk[ZX296702_DMA_ACLK] =
+		zx_gate("dma_aclk",		"matrix_aclk",	CLK_EN1, 14);
+	clk[ZX296702_SEC_HCLK] =
+		zx_gate("sec_hclk",		"main_hclk",	CLK_EN1, 15);
+	clk[ZX296702_AES_WCLK] =
+		zx_gate("aes_wclk",		"sec_wclk_div",	CLK_EN1, 16);
+	clk[ZX296702_DES_WCLK] =
+		zx_gate("des_wclk",		"sec_wclk_div",	CLK_EN1, 17);
+	clk[ZX296702_IRAM_ACLK] =
+		zx_gate("iram_aclk",		"matrix_aclk",	CLK_EN1, 18);
+	clk[ZX296702_IROM_ACLK] =
+		zx_gate("irom_aclk",		"matrix_aclk",	CLK_EN1, 19);
+	clk[ZX296702_BOOT_CTRL_HCLK] =
+		zx_gate("boot_ctrl_hclk",	"main_hclk",	CLK_EN1, 20);
+	clk[ZX296702_EFUSE_CLK_30] =
+		zx_gate("efuse_clk_30",	"osc",			CLK_EN1, 21);
+
+	/* TODO: add VOU Local clocks */
+	clk[ZX296702_VOU_MAIN_CHANNEL_DIV] =
+		zx_div("vou_main_channel_div", "vou_main_channel_wclk",
+				VOU_LOCAL_DIV2_SET, 1, 1);
+	clk[ZX296702_VOU_AUX_CHANNEL_DIV] =
+		zx_div("vou_aux_channel_div", "vou_aux_channel_wclk",
+				VOU_LOCAL_DIV2_SET, 0, 1);
+	clk[ZX296702_VOU_TV_ENC_HD_DIV] =
+		zx_div("vou_tv_enc_hd_div", "vou_tv_enc_hd_mux",
+				VOU_LOCAL_DIV2_SET, 3, 1);
+	clk[ZX296702_VOU_TV_ENC_SD_DIV] =
+		zx_div("vou_tv_enc_sd_div", "vou_tv_enc_sd_mux",
+				VOU_LOCAL_DIV2_SET, 2, 1);
+	clk[ZX296702_VL0_MUX] =
+		zx_mux("vl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 8, 1);
+	clk[ZX296702_VL1_MUX] =
+		zx_mux("vl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 9, 1);
+	clk[ZX296702_VL2_MUX] =
+		zx_mux("vl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 10, 1);
+	clk[ZX296702_GL0_MUX] =
+		zx_mux("gl0_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 5, 1);
+	clk[ZX296702_GL1_MUX] =
+		zx_mux("gl1_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 6, 1);
+	clk[ZX296702_GL2_MUX] =
+		zx_mux("gl2_mux", vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 7, 1);
+	clk[ZX296702_WB_MUX] =
+		zx_mux("wb_mux",  vl0_sel, ARRAY_SIZE(vl0_sel),
+				VOU_LOCAL_CLKSEL, 11, 1);
+	clk[ZX296702_HDMI_MUX] =
+		zx_mux("hdmi_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
+				VOU_LOCAL_CLKSEL, 4, 1);
+	clk[ZX296702_VOU_TV_ENC_HD_MUX] =
+		zx_mux("vou_tv_enc_hd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
+				VOU_LOCAL_CLKSEL, 3, 1);
+	clk[ZX296702_VOU_TV_ENC_SD_MUX] =
+		zx_mux("vou_tv_enc_sd_mux", hdmi_sel, ARRAY_SIZE(hdmi_sel),
+				VOU_LOCAL_CLKSEL, 2, 1);
+	clk[ZX296702_VL0_CLK] =
+		zx_gate("vl0_clk", "vl0_mux", VOU_LOCAL_CLKEN, 8);
+	clk[ZX296702_VL1_CLK] =
+		zx_gate("vl1_clk", "vl1_mux", VOU_LOCAL_CLKEN, 9);
+	clk[ZX296702_VL2_CLK] =
+		zx_gate("vl2_clk", "vl2_mux", VOU_LOCAL_CLKEN, 10);
+	clk[ZX296702_GL0_CLK] =
+		zx_gate("gl0_clk", "gl0_mux", VOU_LOCAL_CLKEN, 5);
+	clk[ZX296702_GL1_CLK] =
+		zx_gate("gl1_clk", "gl1_mux", VOU_LOCAL_CLKEN, 6);
+	clk[ZX296702_GL2_CLK] =
+		zx_gate("gl2_clk", "gl2_mux", VOU_LOCAL_CLKEN, 7);
+	clk[ZX296702_WB_CLK] =
+		zx_gate("wb_clk", "wb_mux", VOU_LOCAL_CLKEN, 11);
+	clk[ZX296702_CL_CLK] =
+		zx_gate("cl_clk", "vou_main_channel_div", VOU_LOCAL_CLKEN, 12);
+	clk[ZX296702_MAIN_MIX_CLK] =
+		zx_gate("main_mix_clk", "vou_main_channel_div",
+				VOU_LOCAL_CLKEN, 4);
+	clk[ZX296702_AUX_MIX_CLK] =
+		zx_gate("aux_mix_clk", "vou_aux_channel_div",
+				VOU_LOCAL_CLKEN, 3);
+	clk[ZX296702_HDMI_CLK] =
+		zx_gate("hdmi_clk", "hdmi_mux", VOU_LOCAL_CLKEN, 2);
+	clk[ZX296702_VOU_TV_ENC_HD_DAC_CLK] =
+		zx_gate("vou_tv_enc_hd_dac_clk", "vou_tv_enc_hd_div",
+				VOU_LOCAL_CLKEN, 1);
+	clk[ZX296702_VOU_TV_ENC_SD_DAC_CLK] =
+		zx_gate("vou_tv_enc_sd_dac_clk", "vou_tv_enc_sd_div",
+				VOU_LOCAL_CLKEN, 0);
+
+	/* CA9 PERIPHCLK = a9_wclk / 2 */
+	clk[ZX296702_A9_PERIPHCLK] =
+		clk_register_fixed_factor(NULL, "a9_periphclk", "a9_wclk",
+				0, 1, 2);
+
+	for (i = 0; i < ARRAY_SIZE(topclk); i++) {
+		if (IS_ERR(clk[i])) {
+			pr_err("zx296702 clk %d: register failed with %ld\n",
+				i, PTR_ERR(clk[i]));
+			return;
+		}
+	}
+
+	topclk_data.clks = topclk;
+	topclk_data.clk_num = ARRAY_SIZE(topclk);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &topclk_data);
+}
+CLK_OF_DECLARE(zx296702_top_clk, "zte,zx296702-topcrm-clk",
+		zx296702_top_clocks_init);
+
+static void __init zx296702_lsp0_clocks_init(struct device_node *np)
+{
+	struct clk **clk = lsp0clk;
+	int i;
+
+	lsp0crpm_base = of_iomap(np, 0);
+	WARN_ON(!lsp0crpm_base);
+
+	/* SDMMC1 */
+	clk[ZX296702_SDMMC1_WCLK_MUX] =
+		zx_mux("sdmmc1_wclk_mux", sdmmc1_wclk_sel,
+				ARRAY_SIZE(sdmmc1_wclk_sel), CLK_SDMMC1, 4, 1);
+	clk[ZX296702_SDMMC1_WCLK_DIV] =
+		zx_div("sdmmc1_wclk_div", "sdmmc1_wclk_mux", CLK_SDMMC1, 12, 4);
+	clk[ZX296702_SDMMC1_WCLK] =
+		zx_gate("sdmmc1_wclk", "sdmmc1_wclk_div", CLK_SDMMC1, 1);
+	clk[ZX296702_SDMMC1_PCLK] =
+		zx_gate("sdmmc1_pclk", "lsp1_apb_pclk", CLK_SDMMC1, 0);
+
+	for (i = 0; i < ARRAY_SIZE(lsp0clk); i++) {
+		if (IS_ERR(clk[i])) {
+			pr_err("zx296702 clk %d: register failed with %ld\n",
+				i, PTR_ERR(clk[i]));
+			return;
+		}
+	}
+
+	lsp0clk_data.clks = lsp0clk;
+	lsp0clk_data.clk_num = ARRAY_SIZE(lsp0clk);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &lsp0clk_data);
+}
+CLK_OF_DECLARE(zx296702_lsp0_clk, "zte,zx296702-lsp0crpm-clk",
+		zx296702_lsp0_clocks_init);
+
+static void __init zx296702_lsp1_clocks_init(struct device_node *np)
+{
+	struct clk **clk = lsp1clk;
+	int i;
+
+	lsp1crpm_base = of_iomap(np, 0);
+	WARN_ON(!lsp1crpm_base);
+
+	/* UART0 */
+	clk[ZX296702_UART0_WCLK_MUX] =
+		zx_mux("uart0_wclk_mux", uart_wclk_sel,
+				ARRAY_SIZE(uart_wclk_sel), CLK_UART0, 4, 1);
+	/* FIXME: uart wclk enable bit is bit1 in. We hack it as reserved 31 for
+	 * UART does not work after parent clk is disabled/enabled */
+	clk[ZX296702_UART0_WCLK] =
+		zx_gate("uart0_wclk", "uart0_wclk_mux", CLK_UART0, 31);
+	clk[ZX296702_UART0_PCLK] =
+		zx_gate("uart0_pclk", "lsp1_apb_pclk", CLK_UART0, 0);
+
+	/* UART1 */
+	clk[ZX296702_UART1_WCLK_MUX] =
+		zx_mux("uart1_wclk_mux", uart_wclk_sel,
+				ARRAY_SIZE(uart_wclk_sel), CLK_UART1, 4, 1);
+	clk[ZX296702_UART1_WCLK] =
+		zx_gate("uart1_wclk", "uart1_wclk_mux", CLK_UART1, 1);
+	clk[ZX296702_UART1_PCLK] =
+		zx_gate("uart1_pclk", "lsp1_apb_pclk", CLK_UART1, 0);
+
+	/* SDMMC0 */
+	clk[ZX296702_SDMMC0_WCLK_MUX] =
+		zx_mux("sdmmc0_wclk_mux", sdmmc0_wclk_sel,
+				ARRAY_SIZE(sdmmc0_wclk_sel), CLK_SDMMC0, 4, 1);
+	clk[ZX296702_SDMMC0_WCLK_DIV] =
+		zx_div("sdmmc0_wclk_div", "sdmmc0_wclk_mux", CLK_SDMMC0, 12, 4);
+	clk[ZX296702_SDMMC0_WCLK] =
+		zx_gate("sdmmc0_wclk", "sdmmc0_wclk_div", CLK_SDMMC0, 1);
+	clk[ZX296702_SDMMC0_PCLK] =
+		zx_gate("sdmmc0_pclk", "lsp1_apb_pclk", CLK_SDMMC0, 0);
+
+	for (i = 0; i < ARRAY_SIZE(lsp1clk); i++) {
+		if (IS_ERR(clk[i])) {
+			pr_err("zx296702 clk %d: register failed with %ld\n",
+				i, PTR_ERR(clk[i]));
+			return;
+		}
+	}
+
+	lsp1clk_data.clks = lsp1clk;
+	lsp1clk_data.clk_num = ARRAY_SIZE(lsp1clk);
+	of_clk_add_provider(np, of_clk_src_onecell_get, &lsp1clk_data);
+}
+CLK_OF_DECLARE(zx296702_lsp1_clk, "zte,zx296702-lsp1crpm-clk",
+		zx296702_lsp1_clocks_init);
diff --git a/drivers/clk/zte/clk.h b/drivers/clk/zte/clk.h
new file mode 100644
index 0000000..419d93b
--- /dev/null
+++ b/drivers/clk/zte/clk.h
@@ -0,0 +1,30 @@ 
+/*
+ * Copyright 2014 Linaro Ltd.
+ * Copyright (C) 2014 ZTE Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __ZTE_CLK_H
+#define __ZTE_CLK_H
+
+struct zx_pll_config {
+	unsigned long rate;
+	u32 cfg0;
+	u32 cfg1;
+};
+
+struct clk_zx_pll {
+	struct clk_hw hw;
+	void __iomem *reg_base;
+	const struct zx_pll_config *lookup_table; /* order by rate asc */
+	int count;
+	spinlock_t *lock;
+};
+
+struct clk *clk_register_zx_pll(const char *name, const char *parent_name,
+	unsigned long flags, void __iomem *reg_base,
+	const struct zx_pll_config *lookup_table, int count, spinlock_t *lock);
+#endif
diff --git a/include/dt-bindings/clock/zx296702-clock.h b/include/dt-bindings/clock/zx296702-clock.h
new file mode 100644
index 0000000..c5e7ec2
--- /dev/null
+++ b/include/dt-bindings/clock/zx296702-clock.h
@@ -0,0 +1,162 @@ 
+/*
+ * Copyright 2014 Linaro Ltd.
+ * Copyright (C) 2014 ZTE Corporation.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __DT_BINDINGS_CLOCK_ZX296702_H
+#define __DT_BINDINGS_CLOCK_ZX296702_H
+
+#define ZX296702_OSC				0
+#define ZX296702_PLL_A9				1
+#define ZX296702_PLL_A9_350M			2
+#define ZX296702_PLL_MAC_1000M			3
+#define ZX296702_PLL_MAC_333M			4
+#define ZX296702_PLL_MM0_1188M			5
+#define ZX296702_PLL_MM0_396M			6
+#define ZX296702_PLL_MM0_198M			7
+#define ZX296702_PLL_MM1_108M			8
+#define ZX296702_PLL_MM1_72M			9
+#define ZX296702_PLL_MM1_54M			10
+#define ZX296702_PLL_LSP_104M			11
+#define ZX296702_PLL_LSP_26M			12
+#define ZX296702_PLL_AUDIO_294M912		13
+#define ZX296702_PLL_DDR_266M			14
+#define ZX296702_CLK_148M5			15
+#define ZX296702_MATRIX_ACLK			16
+#define ZX296702_MAIN_HCLK			17
+#define ZX296702_MAIN_PCLK			18
+#define ZX296702_CLK_500			19
+#define ZX296702_CLK_250			20
+#define ZX296702_CLK_125			21
+#define ZX296702_CLK_74M25			22
+#define ZX296702_A9_WCLK			23
+#define ZX296702_A9_AS1_ACLK_MUX		24
+#define ZX296702_A9_TRACE_CLKIN_MUX		25
+#define ZX296702_A9_AS1_ACLK_DIV		26
+#define ZX296702_CLK_2				27
+#define ZX296702_CLK_27				28
+#define ZX296702_DECPPU_ACLK_MUX		29
+#define ZX296702_PPU_ACLK_MUX			30
+#define ZX296702_MALI400_ACLK_MUX		31
+#define ZX296702_VOU_ACLK_MUX			32
+#define ZX296702_VOU_MAIN_WCLK_MUX		33
+#define ZX296702_VOU_AUX_WCLK_MUX		34
+#define ZX296702_VOU_SCALER_WCLK_MUX		35
+#define ZX296702_R2D_ACLK_MUX			36
+#define ZX296702_R2D_WCLK_MUX			37
+#define ZX296702_CLK_50				38
+#define ZX296702_CLK_25				39
+#define ZX296702_CLK_12				40
+#define ZX296702_CLK_16M384			41
+#define ZX296702_CLK_32K768			42
+#define ZX296702_SEC_WCLK_DIV			43
+#define ZX296702_DDR_WCLK_MUX			44
+#define ZX296702_NAND_WCLK_MUX			45
+#define ZX296702_LSP_26_WCLK_MUX		46
+#define ZX296702_A9_AS0_ACLK			47
+#define ZX296702_A9_AS1_ACLK			48
+#define ZX296702_A9_TRACE_CLKIN			49
+#define ZX296702_DECPPU_AXI_M_ACLK		50
+#define ZX296702_DECPPU_AHB_S_HCLK		51
+#define ZX296702_PPU_AXI_M_ACLK			52
+#define ZX296702_PPU_AHB_S_HCLK			53
+#define ZX296702_VOU_AXI_M_ACLK			54
+#define ZX296702_VOU_APB_PCLK			55
+#define ZX296702_VOU_MAIN_CHANNEL_WCLK		56
+#define ZX296702_VOU_AUX_CHANNEL_WCLK		57
+#define ZX296702_VOU_HDMI_OSCLK_CEC		58
+#define ZX296702_VOU_SCALER_WCLK		59
+#define ZX296702_MALI400_AXI_M_ACLK		60
+#define ZX296702_MALI400_APB_PCLK		61
+#define ZX296702_R2D_WCLK			62
+#define ZX296702_R2D_AXI_M_ACLK			63
+#define ZX296702_R2D_AHB_HCLK			64
+#define ZX296702_DDR3_AXI_S0_ACLK		65
+#define ZX296702_DDR3_APB_PCLK			66
+#define ZX296702_DDR3_WCLK			67
+#define ZX296702_USB20_0_AHB_HCLK		68
+#define ZX296702_USB20_0_EXTREFCLK		69
+#define ZX296702_USB20_1_AHB_HCLK		70
+#define ZX296702_USB20_1_EXTREFCLK		71
+#define ZX296702_USB20_2_AHB_HCLK		72
+#define ZX296702_USB20_2_EXTREFCLK		73
+#define ZX296702_GMAC_AXI_M_ACLK		74
+#define ZX296702_GMAC_APB_PCLK			75
+#define ZX296702_GMAC_125_CLKIN			76
+#define ZX296702_GMAC_RMII_CLKIN		77
+#define ZX296702_GMAC_25M_CLK			78
+#define ZX296702_NANDFLASH_AHB_HCLK		79
+#define ZX296702_NANDFLASH_WCLK			80
+#define ZX296702_LSP0_APB_PCLK			81
+#define ZX296702_LSP0_AHB_HCLK			82
+#define ZX296702_LSP0_26M_WCLK			83
+#define ZX296702_LSP0_104M_WCLK			84
+#define ZX296702_LSP0_16M384_WCLK		85
+#define ZX296702_LSP1_APB_PCLK			86
+#define ZX296702_LSP1_26M_WCLK			87
+#define ZX296702_LSP1_104M_WCLK			88
+#define ZX296702_LSP1_32K_CLK			89
+#define ZX296702_AON_HCLK			90
+#define ZX296702_SYS_CTRL_PCLK			91
+#define ZX296702_DMA_PCLK			92
+#define ZX296702_DMA_ACLK			93
+#define ZX296702_SEC_HCLK			94
+#define ZX296702_AES_WCLK			95
+#define ZX296702_DES_WCLK			96
+#define ZX296702_IRAM_ACLK			97
+#define ZX296702_IROM_ACLK			98
+#define ZX296702_BOOT_CTRL_HCLK			99
+#define ZX296702_EFUSE_CLK_30			100
+#define ZX296702_VOU_MAIN_CHANNEL_DIV		101
+#define ZX296702_VOU_AUX_CHANNEL_DIV		102
+#define ZX296702_VOU_TV_ENC_HD_DIV		103
+#define ZX296702_VOU_TV_ENC_SD_DIV		104
+#define ZX296702_VL0_MUX			105
+#define ZX296702_VL1_MUX			106
+#define ZX296702_VL2_MUX			107
+#define ZX296702_GL0_MUX			108
+#define ZX296702_GL1_MUX			109
+#define ZX296702_GL2_MUX			110
+#define ZX296702_WB_MUX				111
+#define ZX296702_HDMI_MUX			112
+#define ZX296702_VOU_TV_ENC_HD_MUX		113
+#define ZX296702_VOU_TV_ENC_SD_MUX		114
+#define ZX296702_VL0_CLK			115
+#define ZX296702_VL1_CLK			116
+#define ZX296702_VL2_CLK			117
+#define ZX296702_GL0_CLK			118
+#define ZX296702_GL1_CLK			119
+#define ZX296702_GL2_CLK			120
+#define ZX296702_WB_CLK				121
+#define ZX296702_CL_CLK				122
+#define ZX296702_MAIN_MIX_CLK			123
+#define ZX296702_AUX_MIX_CLK			124
+#define ZX296702_HDMI_CLK			125
+#define ZX296702_VOU_TV_ENC_HD_DAC_CLK		126
+#define ZX296702_VOU_TV_ENC_SD_DAC_CLK		127
+#define ZX296702_A9_PERIPHCLK			128
+#define ZX296702_TOPCLK_END			129
+
+#define ZX296702_SDMMC1_WCLK_MUX		0
+#define ZX296702_SDMMC1_WCLK_DIV		1
+#define ZX296702_SDMMC1_WCLK			2
+#define ZX296702_SDMMC1_PCLK			3
+#define ZX296702_LSP0CLK_END			4
+
+#define ZX296702_UART0_WCLK_MUX			0
+#define ZX296702_UART0_WCLK			1
+#define ZX296702_UART0_PCLK			2
+#define ZX296702_UART1_WCLK_MUX			3
+#define ZX296702_UART1_WCLK			4
+#define ZX296702_UART1_PCLK			5
+#define ZX296702_SDMMC0_WCLK_MUX		6
+#define ZX296702_SDMMC0_WCLK_DIV		7
+#define ZX296702_SDMMC0_WCLK			8
+#define ZX296702_SDMMC0_PCLK			9
+#define ZX296702_LSP1CLK_END			10
+
+#endif /* __DT_BINDINGS_CLOCK_ZX296702_H */