aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-08-22 09:12:54 +0800
committerGuodong Xu <guodong.xu@linaro.org>2013-08-22 09:12:54 +0800
commit098c1cfef2050654a43b88de13f4446020b60316 (patch)
tree90ffe5115f234e8b207a22fca5025642144057cb
parent71e1951dd152bbae4f78167b49c5ed76151d5188 (diff)
parentae90e250147423c60915b9499b29358a4bdcbac5 (diff)
Merge commit 'tracking-hilt-s40-soc-clock-08212013' into integration-hilt-lsk-androidintegration-hilt-lsk-android-0822
Conflicts: drivers/clk/hisilicon/Makefile Signed-off-by: Guodong Xu <guodong.xu@linaro.org>
-rw-r--r--Documentation/devicetree/bindings/clock/hi3716.txt121
-rw-r--r--arch/arm/boot/dts/Makefile3
-rw-r--r--arch/arm/boot/dts/hi3716-dkb.dts56
-rw-r--r--arch/arm/boot/dts/hi3716.dtsi256
-rw-r--r--arch/arm/mach-hs/hs-dt.c1
-rw-r--r--drivers/clk/clk.c34
-rw-r--r--drivers/clk/hisilicon/Makefile2
-rw-r--r--drivers/clk/hisilicon/clk-hi3716.c268
8 files changed, 739 insertions, 2 deletions
diff --git a/Documentation/devicetree/bindings/clock/hi3716.txt b/Documentation/devicetree/bindings/clock/hi3716.txt
new file mode 100644
index 000000000000..60af61e1a413
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/hi3716.txt
@@ -0,0 +1,121 @@
+Device Tree Clock bindings for hi3716
+
+This binding uses the common clock binding[1].
+
+[1] Documentation/devicetree/bindings/clock/clock-bindings.txt
+
+Clock control register
+Required properties:
+- compatible : "hisilicon,clkbase"
+- reg : Address and size of clkbase.
+
+Device Clocks
+
+Device clocks are required to have one or both of the following sets of
+properties:
+
+
+Gated device clocks:
+
+Required properties:
+- compatible : "hisilicon,hi3716-clk-gate"
+- gate-reg : shall be the register offset from clkbase and enable bit, reset bit
+- clock-output-names : shall be reference name
+
+
+Mux device clocks:
+
+Required properties:
+- compatible : "hisilicon,hi3716-clk-mux"
+- mux-reg : shall be the register offset from clkbase and mux_shift mux_width
+- mux-table : shall be reg value to be choose clocks accordingly
+- clock-output-names : shall be reference name
+
+
+Fixed divisor device clocks:
+
+Required properties:
+- compatible : "hisilicon,hi3716-fixed-divider"
+- div-table : shall be divisor sequence
+- clock-output-names : shall be reference name according to divisor
+
+
+Fixed pll clocks:
+
+Required properties:
+- compatible : "hisilicon,hi3716-fixed-pll"
+- clock-frequency : shall be output clock frequence sequence
+- clock-output-names : shall be reference name according to clock-frequnce
+
+For example:
+ clkbase@f8a22000 {
+ compatible = "hisilicon,clkbase";
+ reg = <0xf8a22000 0x1000>;
+ };
+
+ osc24m: osc {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "osc24mhz";
+ };
+
+ bpll: bpll {
+ compatible = "hisilicon,hi3716-fixed-pll";
+ #clock-cells = <1>;
+ clocks = <&osc24m>;
+ clock-frequency = <1200000000>,
+ <600000000>,
+ <300000000>,
+ <200000000>,
+ <150000000>;
+ clock-output-names = "bpll_fout0",
+ "bpll_fout1",
+ "bpll_fout2",
+ "bpll_fout3",
+ "bpll_fout4";
+ };
+
+ bpll_fout0_div: bpll_fout0_div {/* 1200Mhz */
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <1>;
+ clocks = <&bpll 0>;
+ div-table = <3 14 25 50>;
+ clock-output-names = "fout0_400m", "fout0_86m",
+ "fout0_48m", "fout0_24m";
+ };
+
+ bpll_fout3_div: bpll_fout3_div {
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <1>;
+ clocks = <&bpll 3>;
+ div-table = <2 4 5 8>;
+ clock-output-names = "fout3_100m", "fout3_50m",
+ "fout3_40m", "fout3_25m";
+ };
+
+ clk_sfc_mux: clk_sfc_mux {
+ compatible = "hisilicon,hi3716-clk-mux";
+ #clock-cells = <0>;
+ /* clks: 24M 75M 100M 150M 200M */
+ clocks = <&osc24m>, <&bpll_fout2_div>,
+ <&bpll_fout3_div 0>, <&bpll 4>, <&bpll 3>;
+
+ /* offset mux_shift mux_width */
+ mux-reg = <0x5c 8 3>;
+ /* mux reg value to choose clks */
+ mux-table = <0 7 6 4 5>;
+
+ clock-output-names = "sfc_mux";
+ };
+
+ clk_sfc: clk_sfc {
+ compatible = "hisilicon,hi3716-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&clk_sfc_mux>;
+
+ /* offset, enable, reset */
+ gate-reg = <0x5c 0 4>;
+
+ clock-output-names = "sfc";
+ };
diff --git a/arch/arm/boot/dts/Makefile b/arch/arm/boot/dts/Makefile
index 57cb00ce5d75..c1f09712080e 100644
--- a/arch/arm/boot/dts/Makefile
+++ b/arch/arm/boot/dts/Makefile
@@ -60,7 +60,8 @@ dtb-$(CONFIG_ARCH_EXYNOS) += exynos4210-origen.dtb \
exynos5440-ssdk5440.dtb
dtb-$(CONFIG_ARCH_HIGHBANK) += highbank.dtb \
ecx-2000.dtb
-dtb-$(CONFIG_ARCH_HS) += hi4511.dtb
+dtb-$(CONFIG_ARCH_HS) += hi4511.dtb \
+ hi3716-dkb.dtb
dtb-$(CONFIG_ARCH_INTEGRATOR) += integratorap.dtb \
integratorcp.dtb
dtb-$(CONFIG_ARCH_LPC32XX) += ea3250.dtb phy3250.dtb
diff --git a/arch/arm/boot/dts/hi3716-dkb.dts b/arch/arm/boot/dts/hi3716-dkb.dts
new file mode 100644
index 000000000000..91beec5f236f
--- /dev/null
+++ b/arch/arm/boot/dts/hi3716-dkb.dts
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+
+/dts-v1/;
+/include/ "hi3716.dtsi"
+
+/ {
+ model = "Hisilicon Hi3716 Development Board";
+ compatible = "hisilicon,hi3716";
+
+ chosen {
+ bootargs = "console=ttyAMA0,115200";
+ };
+
+ cpus {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ cpu@0 {
+ compatible = "arm,cortex-a9";
+ device_type = "cpu";
+ reg = <0>;
+ next-level-cache = <&l2>;
+ };
+
+ cpu@1 {
+ compatible = "arm,cortex-a9";
+ device_type = "cpu";
+ reg = <1>;
+ next-level-cache = <&l2>;
+ };
+ };
+
+ memory {
+ device_type = "memory";
+ reg = <0x00000000 0x80000000>;
+ };
+
+ soc {
+ amba {
+ timer0: timer@f8002000 {
+ status = "okay";
+ };
+
+ uart0: uart@f8b00000 {
+ status = "okay";
+ };
+ };
+ };
+};
diff --git a/arch/arm/boot/dts/hi3716.dtsi b/arch/arm/boot/dts/hi3716.dtsi
new file mode 100644
index 000000000000..5eea43c055f5
--- /dev/null
+++ b/arch/arm/boot/dts/hi3716.dtsi
@@ -0,0 +1,256 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * 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
+ * publishhed by the Free Software Foundation.
+ */
+
+/include/ "skeleton.dtsi"
+
+/ {
+ aliases {
+ serial0 = &uart0;
+ };
+
+ gic: interrupt-controller@fc001000 {
+ compatible = "arm,cortex-a9-gic";
+ #interrupt-cells = <3>;
+ #address-cells = <0>;
+ interrupt-controller;
+ /* gic dist base, gic cpu base */
+ reg = <0xf8a01000 0x1000>, <0xf8a00100 0x100>;
+ };
+
+ soc {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "simple-bus";
+ device_type = "soc";
+ interrupt-parent = <&gic>;
+ ranges;
+
+ amba {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ compatible = "arm,amba-bus";
+ ranges;
+
+ timer0: timer@f8002000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xf8002000 0x1000>;
+ /* timer00 & timer01 */
+ interrupts = <0 24 4>;
+ clocks = <&osc24m>;
+ status = "disabled";
+ };
+
+ timer1: timer@f8a29000 {
+ /*
+ * Only used in NORMAL state, not available ins
+ * SLOW or DOZE state.
+ * The rate is fixed in 24MHz.
+ */
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xf8a29000 0x1000>;
+ /* timer10 & timer11 */
+ interrupts = <0 25 4>;
+ clocks = <&osc24m>;
+ status = "disabled";
+ };
+
+ timer2: timer@f8a2a000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xf8a2a000 0x1000>;
+ /* timer20 & timer21 */
+ interrupts = <0 26 4>;
+ clocks = <&osc24m>;
+ status = "disabled";
+ };
+
+ timer3: timer@f8a2b000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xf8a2b000 0x1000>;
+ /* timer30 & timer31 */
+ interrupts = <0 27 4>;
+ clocks = <&osc24m>;
+ status = "disabled";
+ };
+
+ timer4: timer@f8a81000 {
+ compatible = "arm,sp804", "arm,primecell";
+ reg = <0xf8a81000 0x1000>;
+ /* timer30 & timer31 */
+ interrupts = <0 28 4>;
+ clocks = <&osc24m>;
+ status = "disabled";
+ };
+
+ uart0: uart@f8b00000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0xf8b00000 0x1000>;
+ interrupts = <0 49 4>;
+ clocks = <&bpll_fout0_div 1>;
+ clock-names = "apb_pclk";
+ status = "disabled";
+ };
+
+ uart1: uart@f8006000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0xf8006000 0x1000>;
+ interrupts = <0 50 4>;
+ clocks = <&bpll_fout0_div 1>;
+ clock-names = "apb_pclk";
+ status = "disabled";
+ };
+
+ uart2: uart@f8b02000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0xf8b02000 0x1000>;
+ interrupts = <0 51 4>;
+ clocks = <&bpll_fout0_div 1>;
+ clock-names = "apb_pclk";
+ status = "disabled";
+ };
+
+ uart3: uart@f8b03000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0xf8b03000 0x1000>;
+ interrupts = <0 52 4>;
+ clocks = <&bpll_fout0_div 1>;
+ clock-names = "apb_pclk";
+ status = "disabled";
+ };
+
+ uart4: uart@f8b04000 {
+ compatible = "arm,pl011", "arm,primecell";
+ reg = <0xf8b04000 0x1000>;
+ interrupts = <0 53 4>;
+ clocks = <&bpll_fout0_div 1>;
+ clock-names = "apb_pclk";
+ status = "disabled";
+ };
+ };
+
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <0>;
+
+ osc24m: osc {
+ compatible = "fixed-clock";
+ #clock-cells = <0>;
+ clock-frequency = <24000000>;
+ clock-output-names = "osc24mhz";
+ };
+
+ bpll: bpll {
+ compatible = "hisilicon,hi3716-fixed-pll";
+ #clock-cells = <1>;
+ clocks = <&osc24m>;
+ clock-frequency = <1200000000>,
+ <600000000>,
+ <300000000>,
+ <200000000>,
+ <150000000>;
+ clock-output-names = "bpll_fout0",
+ "bpll_fout1",
+ "bpll_fout2",
+ "bpll_fout3",
+ "bpll_fout4";
+ };
+
+ bpll_fout0_div: bpll_fout0_div {/* 1200Mhz */
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <1>;
+ clocks = <&bpll 0>;
+ div-table = <3 14 25 50>;
+ clock-output-names = "fout0_400m", "fout0_86m",
+ "fout0_48m", "fout0_24m";
+ };
+
+ bpll_fout1_div: bpll_fout1_div {
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <0>;
+ clocks = <&bpll 1>;
+ div-table = <10>;
+ clock-output-names = "fout1_60m";
+ };
+
+ bpll_fout2_div: bpll_fout2_div {
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <0>;
+ clocks = <&bpll 2>;
+ div-table = <4>;
+ clock-output-names = "fout2_75m";
+ };
+
+ bpll_fout3_div: bpll_fout3_div {
+ compatible = "hisilicon,hi3716-fixed-divider";
+ #clock-cells = <1>;
+ clocks = <&bpll 3>;
+ div-table = <2 4 5 8>;
+ clock-output-names = "fout3_100m", "fout3_50m",
+ "fout3_40m", "fout3_25m";
+ };
+
+ clk_sfc_mux: clk_sfc_mux {
+ compatible = "hisilicon,hi3716-clk-mux";
+ #clock-cells = <0>;
+ /* clks: 24M 75M 100M 150M 200M */
+ clocks = <&osc24m>, <&bpll_fout2_div>,
+ <&bpll_fout3_div 0>, <&bpll 4>, <&bpll 3>;
+
+ /* offset mux_shift mux_width */
+ mux-reg = <0x5c 8 3>;
+ /* mux reg value to choose clks */
+ mux-table = <0 7 6 4 5>;
+
+ clock-output-names = "sfc_mux";
+ };
+
+ clk_sfc: clk_sfc {
+ compatible = "hisilicon,hi3716-clk-gate";
+ #clock-cells = <0>;
+ clocks = <&clk_sfc_mux>;
+
+ /* offset, enable, reset */
+ gate-reg = <0x5c 0 4>;
+
+ clock-output-names = "sfc";
+ };
+ };
+
+ local_timer@f8a00600 {
+ compatible = "arm,cortex-a9-twd-timer";
+ reg = <0xf8a00600 0x20>;
+ interrupts = <1 13 0xf01>;
+ };
+
+ l2: l2-cache {
+ compatible = "arm,pl310-cache";
+ reg = <0xf8a10000 0x100000>;
+ interrupts = <0 15 4>;
+ cache-unified;
+ cache-level = <2>;
+ hisilicon,l2cache-aux = <0x00050000 0xfff0ffff>;
+ };
+
+ sctrl@f8000000 {
+ compatible = "hisilicon,sctrl";
+ reg = <0xf8000000 0x1000>;
+ smp_reg = <0xc0>;
+ reboot_reg = <0x4>;
+ };
+
+ clkbase@f8a22000 {
+ compatible = "hisilicon,clkbase";
+ reg = <0xf8a22000 0x1000>;
+ };
+
+ cpuctrl@f8a22000 {
+ compatible = "hisilicon,cpuctrl";
+ reg = <0xf8a22000 0x2000>;
+ };
+ };
+};
diff --git a/arch/arm/mach-hs/hs-dt.c b/arch/arm/mach-hs/hs-dt.c
index c3e0636a513e..774ace66cc22 100644
--- a/arch/arm/mach-hs/hs-dt.c
+++ b/arch/arm/mach-hs/hs-dt.c
@@ -66,6 +66,7 @@ static void __init hs_init(void)
static const char *hs_compat[] __initdata = {
"hisilicon,hi3620-hi4511",
+ "hisilicon,hi3716",
NULL,
};
diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c
index 373cd545b969..6b582f730a05 100644
--- a/drivers/clk/clk.c
+++ b/drivers/clk/clk.c
@@ -226,6 +226,35 @@ static const struct file_operations clk_dump_fops = {
.release = single_release,
};
+#ifdef DEBUG
+static int clk_rate_fops_get(void *data, u64 *rate)
+{
+ struct clk *clk = data;
+
+ *rate = clk->rate;
+
+ return 0;
+};
+
+static int clk_rate_fops_set(void *data, u64 rate)
+{
+ struct clk *clk = data;
+ int ret = 0;
+
+ ret = clk_prepare_enable(clk);
+ if (ret)
+ goto out;
+ clk_set_rate(clk, rate);
+ clk_disable_unprepare(clk);
+
+out:
+ return ret;
+};
+
+DEFINE_SIMPLE_ATTRIBUTE(clk_rate_fops, clk_rate_fops_get,
+ clk_rate_fops_set, "%llu\n");
+#endif
+
/* caller must hold prepare_lock */
static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
{
@@ -243,8 +272,13 @@ static int clk_debug_create_one(struct clk *clk, struct dentry *pdentry)
clk->dentry = d;
+#ifdef DEBUG
+ d = debugfs_create_file("clk_rate", S_IWUSR | S_IRUGO, clk->dentry,
+ clk, &clk_rate_fops);
+#else
d = debugfs_create_u32("clk_rate", S_IRUGO, clk->dentry,
(u32 *)&clk->rate);
+#endif
if (!d)
goto err_out;
diff --git a/drivers/clk/hisilicon/Makefile b/drivers/clk/hisilicon/Makefile
index cd97fda64891..e25405e712d9 100644
--- a/drivers/clk/hisilicon/Makefile
+++ b/drivers/clk/hisilicon/Makefile
@@ -1,2 +1,2 @@
-obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o
+obj-$(CONFIG_HI3xxx_CLK_CORE) += clk-hi3xxx.o clk-hi3716.o
obj-$(CONFIG_HI3620_CLK_MIPI_DSI) += clk-hi3620-dsi.o
diff --git a/drivers/clk/hisilicon/clk-hi3716.c b/drivers/clk/hisilicon/clk-hi3716.c
new file mode 100644
index 000000000000..887ffe90fba5
--- /dev/null
+++ b/drivers/clk/hisilicon/clk-hi3716.c
@@ -0,0 +1,268 @@
+/*
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2013 Hisilicon Limited.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/clk-provider.h>
+#include <linux/clkdev.h>
+#include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <linux/slab.h>
+
+static DEFINE_SPINLOCK(_lock);
+
+static void __iomem *clk_base;
+
+struct hi3716_clk {
+ struct clk_gate gate;
+ void __iomem *reg;
+ u8 reset_bit;
+};
+
+#define MAX_NUMS 10
+
+static struct hi3716_clk *to_clk_hi3716(struct clk_hw *hw)
+{
+ return container_of(hw, struct hi3716_clk, gate.hw);
+}
+
+static void __init hi3716_map_io(void)
+{
+ struct device_node *node;
+
+ if (clk_base)
+ return;
+
+ node = of_find_compatible_node(NULL, NULL, "hisilicon,clkbase");
+ if (node)
+ clk_base = of_iomap(node, 0);
+ WARN_ON(!clk_base);
+}
+
+static int hi3716_clkgate_prepare(struct clk_hw *hw)
+{
+ struct hi3716_clk *clk = to_clk_hi3716(hw);
+ unsigned long flags = 0;
+ u32 reg;
+
+ spin_lock_irqsave(&_lock, flags);
+
+ reg = readl_relaxed(clk->reg);
+ reg &= ~BIT(clk->reset_bit);
+ writel_relaxed(reg, clk->reg);
+
+ spin_unlock_irqrestore(&_lock, flags);
+
+ return 0;
+}
+
+static void hi3716_clkgate_unprepare(struct clk_hw *hw)
+{
+ struct hi3716_clk *clk = to_clk_hi3716(hw);
+ unsigned long flags = 0;
+ u32 reg;
+
+ spin_lock_irqsave(&_lock, flags);
+
+ reg = readl_relaxed(clk->reg);
+ reg |= BIT(clk->reset_bit);
+ writel_relaxed(reg, clk->reg);
+
+ spin_unlock_irqrestore(&_lock, flags);
+}
+
+static struct clk_ops hi3716_clkgate_ops = {
+ .prepare = hi3716_clkgate_prepare,
+ .unprepare = hi3716_clkgate_unprepare,
+};
+
+void __init hi3716_clkgate_setup(struct device_node *node)
+{
+ struct clk *clk;
+ struct hi3716_clk *p_clk;
+ struct clk_init_data init;
+ const char *parent_name;
+ u32 array[3]; /* reg, enable_bit, reset_bit */
+ int err;
+
+ hi3716_map_io();
+ err = of_property_read_u32_array(node, "gate-reg", &array[0], 3);
+ if (WARN_ON(err))
+ return;
+
+ err = of_property_read_string(node, "clock-output-names", &init.name);
+ if (WARN_ON(err))
+ return;
+
+ p_clk = kzalloc(sizeof(*p_clk), GFP_KERNEL);
+ if (WARN_ON(!p_clk))
+ return;
+
+ hi3716_clkgate_ops.enable = clk_gate_ops.enable;
+ hi3716_clkgate_ops.disable = clk_gate_ops.disable;
+ hi3716_clkgate_ops.is_enabled = clk_gate_ops.is_enabled;
+
+ init.ops = &hi3716_clkgate_ops;
+ init.flags = CLK_SET_RATE_PARENT;
+ parent_name = of_clk_get_parent_name(node, 0);
+ init.parent_names = &parent_name;
+ init.num_parents = 1;
+
+ p_clk->reg = p_clk->gate.reg = clk_base + array[0];
+ p_clk->gate.bit_idx = array[1];
+ p_clk->gate.flags = 0;
+ p_clk->gate.lock = &_lock;
+ p_clk->gate.hw.init = &init;
+ p_clk->reset_bit = array[2];
+
+ clk = clk_register(NULL, &p_clk->gate.hw);
+ if (WARN_ON(IS_ERR(clk))) {
+ kfree(p_clk);
+ return;
+ }
+
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+}
+
+static void __init hi3716_clkmux_setup(struct device_node *node)
+{
+ int num = 0, err;
+ void __iomem *reg;
+ unsigned int shift, width;
+ u32 array[3]; /* reg, mux_shift, mux_width */
+ u32 *table = NULL;
+ const char *clk_name = node->name;
+ const char *parents[MAX_NUMS];
+ struct clk *clk;
+
+ hi3716_map_io();
+ err = of_property_read_string(node, "clock-output-names", &clk_name);
+ if (WARN_ON(err))
+ return;
+
+ err = of_property_read_u32_array(node, "mux-reg", &array[0], 3);
+ if (WARN_ON(err))
+ return;
+
+ reg = clk_base + array[0];
+ shift = array[1];
+ width = array[2];
+
+ while ((num < MAX_NUMS) &&
+ ((parents[num] = of_clk_get_parent_name(node, num)) != NULL))
+ num++;
+ if (!num)
+ return;
+
+ table = kzalloc(sizeof(u32 *) * num, GFP_KERNEL);
+ if (WARN_ON(!table))
+ return;
+
+ err = of_property_read_u32_array(node, "mux-table", table, num);
+ if (WARN_ON(err))
+ goto err;
+
+ clk = clk_register_mux_table(NULL, clk_name, parents, num,
+ CLK_SET_RATE_PARENT, reg, shift, BIT(width) - 1,
+ 0, table, &_lock);
+ if (WARN_ON(IS_ERR(clk)))
+ goto err;
+
+ clk_register_clkdev(clk, clk_name, NULL);
+ of_clk_add_provider(node, of_clk_src_simple_get, clk);
+ return;
+
+err:
+ kfree(table);
+ return;
+}
+
+static void __init hi3716_fixed_pll_setup(struct device_node *node)
+{
+ const char *clk_name, *parent_name;
+ struct clk *clks[MAX_NUMS];
+ u32 rate[MAX_NUMS];
+ struct clk_onecell_data *clk_data;
+ int i, err, nums = 0;
+
+ nums = of_property_count_strings(node, "clock-output-names");
+ if (WARN_ON((nums < 0) || (nums > MAX_NUMS)))
+ return;
+
+ err = of_property_read_u32_array(node, "clock-frequency",
+ &rate[0], nums);
+ WARN_ON(err);
+
+ parent_name = of_clk_get_parent_name(node, 0);
+
+ for (i = 0; i < nums; i++) {
+ err = of_property_read_string_index(node, "clock-output-names",
+ i, &clk_name);
+ WARN_ON(err);
+
+ clks[i] = clk_register_fixed_rate(NULL, clk_name,
+ parent_name, 0, rate[i]);
+ WARN_ON(IS_ERR(clks[i]));
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data) + nums * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ memcpy(&clk_data[1], clks, nums * sizeof(struct clk *));
+ clk_data->clks = (struct clk **)&clk_data[1];
+ clk_data->clk_num = nums;
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+void __init hi3716_fixed_divider_setup(struct device_node *node)
+{
+ const char *clk_parent;
+ const char *clk_name;
+ u32 div[MAX_NUMS];
+ struct clk *clks[MAX_NUMS];
+ struct clk_onecell_data *clk_data;
+ int err, i, nums = 0;
+
+ clk_parent = of_clk_get_parent_name(node, 0);
+
+ nums = of_property_count_strings(node, "clock-output-names");
+ if (WARN_ON((nums < 0) || (nums > MAX_NUMS)))
+ return;
+
+ err = of_property_read_u32_array(node, "div-table", &div[0], nums);
+ WARN_ON(err);
+
+ for (i = 0; i < nums; i++) {
+ err = of_property_read_string_index(node,
+ "clock-output-names", i, &clk_name);
+ WARN_ON(err);
+
+ clks[i] = clk_register_fixed_factor(NULL, clk_name,
+ clk_parent, CLK_SET_RATE_PARENT, 1, div[i]);
+ WARN_ON(IS_ERR(clks[i]));
+ }
+
+ clk_data = kzalloc(sizeof(*clk_data) + nums * sizeof(struct clk *),
+ GFP_KERNEL);
+ if (WARN_ON(!clk_data))
+ return;
+
+ memcpy(&clk_data[1], clks, nums * sizeof(struct clk *));
+ clk_data->clks = (struct clk **)&clk_data[1];
+ clk_data->clk_num = nums;
+ of_clk_add_provider(node, of_clk_src_onecell_get, clk_data);
+}
+
+CLK_OF_DECLARE(hi3716_fixed_rate, "fixed-clock", of_fixed_clk_setup)
+CLK_OF_DECLARE(hi3716_fixed_pll, "hisilicon,hi3716-fixed-pll", hi3716_fixed_pll_setup)
+CLK_OF_DECLARE(hi3716_divider, "hisilicon,hi3716-fixed-divider", hi3716_fixed_divider_setup)
+CLK_OF_DECLARE(hi3716_mux, "hisilicon,hi3716-clk-mux", hi3716_clkmux_setup)
+CLK_OF_DECLARE(hi3716_gate, "hisilicon,hi3716-clk-gate", hi3716_clkgate_setup)