* [PATCH v3 00/11] Add real clock support for Rockchip's RK3188
@ 2014-05-23 19:32 Heiko Stübner
2014-05-23 19:33 ` [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent Heiko Stübner
` (10 more replies)
0 siblings, 11 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:32 UTC (permalink / raw)
To: linux-arm-kernel
This series add a clock driver infrastructure for Rockchip SoCs in
general and clock-definitions for the RK3188 in particular.
The previous attempt to define parts separately in the devicetree did
not really fit with the clock structure, which became apparent with more
knowledge about the clock tree.
The whole structure should support Rockchip SoCs at least down to
the RK28xx (ARM9) which all share a very similar setup of their clock
controllers in PLL, divider and gate handling as well as the included
softreset parts.
A big change is the move from declaring all mux/dividers/gates individually
to declaring them as composite clock branches, making the code better
readable and making it possible to keep clocks together in a more natural
order.
This series depends on the patch adding the CLK_DIVIDER_READ_ONLY flag
Thomas Abraham reposted today.
The changes under drivers/clk apply cleanly to clk-next but the changes
in arm/boot/dts require patches from arm-soc, so in the unlikely case of
this making it in before the merge window the dts patches should go
through arm-soc (or should do so anyway, as they do not directly depend
on the drivers/clk changes)
@Max: due to the quite deep changes I didn't keep your tags, so re-tag
please if you feel they're still appropriate :-)
changes since v2:
- drop the special cpuclk type until coordinated range changes
matured, as mentioned in the Samsung cpufreq thread.
- add the patch from Boris BREZILLON that reenables correct rate
calculations in composite clocks
- change pll handling to better handle the pll output mux and also
the enabling/disabling of the pll clock
- change core code to handle composite clock branches instead of
individual basic clock definitions
- use the newly defined GRF syscon instead of mapping a grf register
individually
changes since v1:
- adapt to apply on current clk-next branch
- add saradc clock
- add rk3188a cru, which has a slightly different handling of one
pll value (bwadj)
Boris BREZILLON (1):
clk: composite: support determine_rate using rate_ops->round_rate +
mux_ops->set_parent
Heiko Stuebner (10):
clk: composite: allow read-only clocks
clk: rockchip: add basic infrastructure for clock branches
clk: rockchip: add clock type for pll clocks and pll used on rk3066
clk: rockchip: add reset controller
dt-bindings: add documentation for rk3188 clock and reset unit
clk: rockchip: add clock driver for rk3188 clocks
ARM: rockchip: Select ARCH_HAS_RESET_CONTROLLER
ARM: dts: rk3188: add cru node and update device clocks to use it
ARM: dts: rockchip: move rk3188 core input clocks into main dtsi
ARM: dts: rockchip: remove the now obsolete rk3188-clocks.dtsi
.../bindings/clock/rockchip,rk3188-cru.txt | 59 +++
arch/arm/boot/dts/rk3188-clocks.dtsi | 289 -----------
arch/arm/boot/dts/rk3188.dtsi | 59 ++-
arch/arm/mach-rockchip/Kconfig | 1 +
drivers/clk/clk-composite.c | 60 ++-
drivers/clk/rockchip/Makefile | 5 +
drivers/clk/rockchip/clk-pll.c | 430 ++++++++++++++++
drivers/clk/rockchip/clk-rk3188.c | 561 +++++++++++++++++++++
drivers/clk/rockchip/clk.c | 238 +++++++++
drivers/clk/rockchip/clk.h | 339 +++++++++++++
drivers/clk/rockchip/softrst.c | 115 +++++
include/dt-bindings/clock/rk3188-cru.h | 78 +++
12 files changed, 1933 insertions(+), 301 deletions(-)
create mode 100644 Documentation/devicetree/bindings/clock/rockchip,rk3188-cru.txt
delete mode 100644 arch/arm/boot/dts/rk3188-clocks.dtsi
create mode 100644 drivers/clk/rockchip/clk-pll.c
create mode 100644 drivers/clk/rockchip/clk-rk3188.c
create mode 100644 drivers/clk/rockchip/clk.c
create mode 100644 drivers/clk/rockchip/clk.h
create mode 100644 drivers/clk/rockchip/softrst.c
create mode 100644 include/dt-bindings/clock/rk3188-cru.h
--
1.9.0
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
@ 2014-05-23 19:33 ` Heiko Stübner
2014-05-26 9:24 ` Boris BREZILLON
2014-05-23 19:34 ` [PATCH v3 02/11] clk: composite: allow read-only clocks Heiko Stübner
` (9 subsequent siblings)
10 siblings, 1 reply; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:33 UTC (permalink / raw)
To: linux-arm-kernel
From: Boris BREZILLON <b.brezillon@overkiz.com>
In case the rate_hw does not implement determine_rate, but only round_rate
we fallback to best_parent selection if mux_hw is present and support
reparenting.
Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
This also fixes a rate calculation problem when using the standard div and
mux ops, as in this case currently only the mux->determine_rate is used
in the composite rate calculation.
[fixed the output to actually return a rate instead of the diff]
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/clk-composite.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 50 insertions(+), 1 deletion(-)
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 57a078e..0a4cd21 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -64,11 +64,59 @@ static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
const struct clk_ops *mux_ops = composite->mux_ops;
struct clk_hw *rate_hw = composite->rate_hw;
struct clk_hw *mux_hw = composite->mux_hw;
+ struct clk *parent;
+ unsigned long parent_rate;
+ long tmp_rate, best_rate;
+ unsigned long rate_diff;
+ unsigned long best_rate_diff = ULONG_MAX;
+ int i;
if (rate_hw && rate_ops && rate_ops->determine_rate) {
rate_hw->clk = hw->clk;
return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
best_parent_p);
+ } else if (rate_hw && rate_ops && rate_ops->round_rate &&
+ mux_hw && mux_ops && mux_ops->set_parent) {
+ *best_parent_p = NULL;
+
+ if (__clk_get_flags(hw->clk) & CLK_SET_RATE_NO_REPARENT) {
+ *best_parent_p = clk_get_parent(mux_hw->clk);
+ *best_parent_rate = __clk_get_rate(*best_parent_p);
+
+ return rate_ops->round_rate(rate_hw, rate,
+ best_parent_rate);
+ }
+
+ for (i = 0; i < __clk_get_num_parents(mux_hw->clk); i++) {
+ parent = clk_get_parent_by_index(mux_hw->clk, i);
+ if (!parent)
+ continue;
+
+ parent_rate = __clk_get_rate(parent);
+
+ tmp_rate = rate_ops->round_rate(rate_hw, rate,
+ &parent_rate);
+ if (tmp_rate < 0)
+ continue;
+
+ if (tmp_rate < rate)
+ rate_diff = rate - tmp_rate;
+ else
+ rate_diff = tmp_rate - rate;
+
+ if (!rate_diff || !*best_parent_p
+ || best_rate_diff > rate_diff) {
+ *best_parent_p = parent;
+ *best_parent_rate = parent_rate;
+ best_rate_diff = rate_diff;
+ best_rate = tmp_rate;
+ }
+
+ if (!rate_diff)
+ return rate;
+ }
+
+ return best_rate;
} else if (mux_hw && mux_ops && mux_ops->determine_rate) {
mux_hw->clk = hw->clk;
return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
@@ -196,7 +244,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
composite->rate_hw = rate_hw;
composite->rate_ops = rate_ops;
clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
- if (rate_ops->determine_rate)
+ if (rate_ops->determine_rate ||
+ (rate_ops->round_rate && clk_composite_ops->set_parent))
clk_composite_ops->determine_rate = clk_composite_determine_rate;
}
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 02/11] clk: composite: allow read-only clocks
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
2014-05-23 19:33 ` [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent Heiko Stübner
@ 2014-05-23 19:34 ` Heiko Stübner
2014-05-23 19:34 ` [PATCH v3 03/11] clk: rockchip: add basic infrastructure for clock branches Heiko Stübner
` (8 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:34 UTC (permalink / raw)
To: linux-arm-kernel
This allows readl-only composite clocks by making mux_ops->set_parent and
divider_ops->round_rate/set_rate optional.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/clk-composite.c | 9 +++------
1 file changed, 3 insertions(+), 6 deletions(-)
diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
index 0a4cd21..046d3f6 100644
--- a/drivers/clk/clk-composite.c
+++ b/drivers/clk/clk-composite.c
@@ -210,7 +210,7 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
clk_composite_ops = &composite->ops;
if (mux_hw && mux_ops) {
- if (!mux_ops->get_parent || !mux_ops->set_parent) {
+ if (!mux_ops->get_parent) {
clk = ERR_PTR(-EINVAL);
goto err;
}
@@ -218,7 +218,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
composite->mux_hw = mux_hw;
composite->mux_ops = mux_ops;
clk_composite_ops->get_parent = clk_composite_get_parent;
- clk_composite_ops->set_parent = clk_composite_set_parent;
+ if (mux_ops->set_parent)
+ clk_composite_ops->set_parent = clk_composite_set_parent;
if (mux_ops->determine_rate)
clk_composite_ops->determine_rate = clk_composite_determine_rate;
}
@@ -235,10 +236,6 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
if (rate_ops->set_rate) {
clk_composite_ops->set_rate = clk_composite_set_rate;
}
- } else {
- WARN(rate_ops->set_rate,
- "%s: missing round_rate op is required\n",
- __func__);
}
composite->rate_hw = rate_hw;
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 03/11] clk: rockchip: add basic infrastructure for clock branches
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
2014-05-23 19:33 ` [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent Heiko Stübner
2014-05-23 19:34 ` [PATCH v3 02/11] clk: composite: allow read-only clocks Heiko Stübner
@ 2014-05-23 19:34 ` Heiko Stübner
2014-05-23 19:35 ` [PATCH v3 04/11] clk: rockchip: add clock type for pll clocks and pll used on rk3066 Heiko Stübner
` (7 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:34 UTC (permalink / raw)
To: linux-arm-kernel
This adds infrastructure for registering clock branches. On Rockchip SoCs
most clock branches are a combination of mux,divider and gate components,
thus a composite clock is used when appropriate.
Clock branches are supposed to be declared in an array using the COMPOSITE*
or MUX, etc makros defined in the header and then registered using
rockchip_clk_register_branches.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/rockchip/Makefile | 1 +
drivers/clk/rockchip/clk.c | 203 ++++++++++++++++++++++++++++++++++
drivers/clk/rockchip/clk.h | 251 ++++++++++++++++++++++++++++++++++++++++++
3 files changed, 455 insertions(+)
create mode 100644 drivers/clk/rockchip/clk.c
create mode 100644 drivers/clk/rockchip/clk.h
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 8d3aefa..0068a8b 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -3,3 +3,4 @@
#
obj-y += clk-rockchip.o
+obj-y += clk.o
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
new file mode 100644
index 0000000..b01cac7
--- /dev/null
+++ b/drivers/clk/rockchip/clk.c
@@ -0,0 +1,203 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on
+ *
+ * samsung/clk.c
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include "clk.h"
+
+/**
+ * Register a clock branch.
+ * Most clock branches have a form like
+ *
+ * src1 --|--\
+ * |M |--[GATE]-[DIV]-
+ * src2 --|--/
+ *
+ * sometimes without one of those components.
+ */
+struct clk *rockchip_clk_register_branch(const char *name,
+ const char **parent_names, u8 num_parents, void __iomem *base,
+ int muxdiv_offset, u8 mux_shift, u8 mux_width, u8 mux_flags,
+ u8 div_shift, u8 div_width, u8 div_flags,
+ struct clk_div_table *div_table, int gate_offset,
+ u8 gate_shift, u8 gate_flags, unsigned long flags,
+ spinlock_t *lock)
+{
+ struct clk *clk;
+ struct clk_mux *mux = NULL;
+ struct clk_gate *gate = NULL;
+ struct clk_divider *div = NULL;
+ const struct clk_ops *mux_ops = NULL, *div_ops = NULL,
+ *gate_ops = NULL;
+
+ if (num_parents > 1) {
+ mux = kzalloc(sizeof(*mux), GFP_KERNEL);
+ if (!mux)
+ return ERR_PTR(-ENOMEM);
+
+ mux->reg = base + muxdiv_offset;
+ mux->shift = mux_shift;
+ mux->mask = BIT(mux_width) - 1;
+ mux->flags = mux_flags;
+ mux->lock = lock;
+ mux_ops = (mux_flags & CLK_MUX_READ_ONLY) ? &clk_mux_ro_ops
+ : &clk_mux_ops;
+ }
+
+ if (gate_offset >= 0) {
+ gate = kzalloc(sizeof(*gate), GFP_KERNEL);
+ if (!gate)
+ return ERR_PTR(-ENOMEM);
+
+ gate->flags = gate_flags;
+ gate->reg = base + gate_offset;
+ gate->bit_idx = gate_shift;
+ gate->lock = lock;
+ gate_ops = &clk_gate_ops;
+ }
+
+ if (div_width > 0) {
+ div = kzalloc(sizeof(*div), GFP_KERNEL);
+ if (!div)
+ return ERR_PTR(-ENOMEM);
+
+ div->flags = div_flags;
+ div->reg = base + muxdiv_offset;
+ div->shift = div_shift;
+ div->width = div_width;
+ div->lock = lock;
+ div->table = div_table;
+ div_ops = (div_flags & CLK_DIVIDER_READ_ONLY)
+ ? &clk_divider_ro_ops
+ : &clk_divider_ops;
+ }
+
+ clk = clk_register_composite(NULL, name, parent_names, num_parents,
+ mux ? &mux->hw : NULL, mux_ops,
+ div ? &div->hw : NULL, div_ops,
+ gate ? &gate->hw : NULL, gate_ops,
+ flags);
+
+ return clk;
+}
+
+static DEFINE_SPINLOCK(clk_lock);
+static struct clk **clk_table;
+static void __iomem *reg_base;
+static struct clk_onecell_data clk_data;
+
+void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
+ unsigned long nr_clks)
+{
+ reg_base = base;
+
+ clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
+ if (!clk_table)
+ pr_err("%s: could not allocate clock lookup table\n", __func__);
+
+ clk_data.clks = clk_table;
+ clk_data.clk_num = nr_clks;
+ of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
+}
+
+void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
+{
+ if (clk_table && id)
+ clk_table[id] = clk;
+}
+
+void __init rockchip_clk_register_branches(
+ struct rockchip_clk_branch *list,
+ unsigned int nr_clk)
+{
+ struct clk *clk;
+ unsigned int idx;
+ unsigned long flags;
+
+ for (idx = 0; idx < nr_clk; idx++, list++) {
+ flags = list->flags;
+
+ /* catch simple muxes */
+ switch (list->branch_type) {
+ case branch_mux:
+ clk = clk_register_mux(NULL, list->name,
+ list->parent_names, list->num_parents,
+ flags, reg_base + list->muxdiv_offset,
+ list->mux_shift, list->mux_width,
+ list->mux_flags, &clk_lock);
+ break;
+ case branch_divider:
+ if (list->div_table)
+ clk = clk_register_divider_table(NULL,
+ list->name, list->parent_names[0],
+ flags, reg_base + list->muxdiv_offset,
+ list->div_shift, list->div_width,
+ list->div_flags, list->div_table,
+ &clk_lock);
+ else
+ clk = clk_register_divider(NULL, list->name,
+ list->parent_names[0], flags,
+ reg_base + list->muxdiv_offset,
+ list->div_shift, list->div_width,
+ list->div_flags, &clk_lock);
+ break;
+ case branch_fraction_divider:
+ case branch_muxgrf:
+ /* unimplemented */
+ continue;
+ break;
+ case branch_gate:
+ flags |= CLK_SET_RATE_PARENT;
+
+ /* keep all gates untouched for now */
+ flags |= CLK_IGNORE_UNUSED;
+
+ clk = clk_register_gate(NULL, list->name,
+ list->parent_names[0], flags,
+ reg_base + list->gate_offset,
+ list->gate_shift, list->gate_flags, &clk_lock);
+ break;
+ case branch_composite:
+ /* keep all gates untouched for now */
+ flags |= CLK_IGNORE_UNUSED;
+
+ clk = rockchip_clk_register_branch(list->name,
+ list->parent_names, list->num_parents,
+ reg_base, list->muxdiv_offset, list->mux_shift,
+ list->mux_width, list->mux_flags,
+ list->div_shift, list->div_width,
+ list->div_flags, list->div_table,
+ list->gate_offset, list->gate_shift,
+ list->gate_flags, flags, &clk_lock);
+ break;
+ }
+
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s: %ld\n",
+ __func__, list->name, PTR_ERR(clk));
+ continue;
+ }
+
+ rockchip_clk_add_lookup(clk, list->id);
+ }
+}
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
new file mode 100644
index 0000000..a77bd74
--- /dev/null
+++ b/drivers/clk/rockchip/clk.h
@@ -0,0 +1,251 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * based on
+ *
+ * samsung/clk.h
+ * Copyright (c) 2013 Samsung Electronics Co., Ltd.
+ * Copyright (c) 2013 Linaro Ltd.
+ * Author: Thomas Abraham <thomas.ab@samsung.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#ifndef CLK_ROCKCHIP_CLK_H
+#define CLK_ROCKCHIP_CLK_H
+
+#include <linux/io.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+
+#define HIWORD_UPDATE(val, mask, shift) \
+ ((val) << (shift) | (mask) << ((shift) + 16))
+
+/* register positions shared by RK2928, RK3066 and RK3188 */
+#define RK2928_PLL_CON(x) (x * 0x4)
+#define RK2928_MODE_CON 0x40
+#define RK2928_CLKSEL_CON(x) (x * 0x4 + 0x44)
+#define RK2928_CLKGATE_CON(x) (x * 0x4 + 0xd0)
+#define RK2928_GLB_SRST_FST 0x100
+#define RK2928_GLB_SRST_SND 0x104
+#define RK2928_SOFTRST_CON(x) (x * 0x4 + 0x110)
+#define RK2928_MISC_CON 0x134
+
+#define PNAME(x) static const char *x[] __initconst
+
+enum rockchip_clk_branch_type {
+ branch_composite,
+ branch_mux,
+ branch_muxgrf,
+ branch_divider,
+ branch_fraction_divider,
+ branch_gate,
+};
+
+struct rockchip_clk_branch {
+ unsigned int id;
+ enum rockchip_clk_branch_type branch_type;
+ const char *name;
+ const char **parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ int muxdiv_offset;
+ u8 mux_shift;
+ u8 mux_width;
+ u8 mux_flags;
+ u8 div_shift;
+ u8 div_width;
+ u8 div_flags;
+ struct clk_div_table *div_table;
+ int gate_offset;
+ u8 gate_shift;
+ u8 gate_flags;
+};
+
+#define COMPOSITE(_id, cname, pnames, f, mo, ms, mw, mf, ds, dw,\
+ df, go, gs, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_composite, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .mux_shift = ms, \
+ .mux_width = mw, \
+ .mux_flags = mf, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
+#define COMPOSITE_NOMUX(_id, cname, pname, f, mo, ds, dw, df, \
+ go, gs, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_composite, \
+ .name = cname, \
+ .parent_names = (const char *[]){ pname }, \
+ .num_parents = 1, \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
+#define COMPOSITE_NOMUX_DIVTBL(_id, cname, pname, f, mo, ds, dw,\
+ df, dt, go, gs, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_composite, \
+ .name = cname, \
+ .parent_names = (const char *[]){ pname }, \
+ .num_parents = 1, \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .div_table = dt, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
+#define COMPOSITE_NODIV(_id, cname, pnames, f, mo, ms, mw, mf, \
+ go, gs, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_composite, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .mux_shift = ms, \
+ .mux_width = mw, \
+ .mux_flags = mf, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
+#define COMPOSITE_NOGATE(_id, cname, pnames, f, mo, ms, mw, mf, \
+ ds, dw, df) \
+ { \
+ .id = _id, \
+ .branch_type = branch_composite, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .mux_shift = ms, \
+ .mux_width = mw, \
+ .mux_flags = mf, \
+ .div_shift = ds, \
+ .div_width = dw, \
+ .div_flags = df, \
+ .gate_offset = -1, \
+ }
+
+#define COMPOSITE_FRAC(_id, cname, pname, f, mo, df, go, gs, gf)\
+ { \
+ .id = _id, \
+ .branch_type = branch_fraction_divider, \
+ .name = cname, \
+ .parent_names = (const char *[]){ pname }, \
+ .num_parents = 1, \
+ .flags = f, \
+ .muxdiv_offset = mo, \
+ .div_shift = 16, \
+ .div_width = 16, \
+ .div_flags = df, \
+ .gate_offset = go, \
+ .gate_shift = gs, \
+ .gate_flags = gf, \
+ }
+
+#define MUX(_id, cname, pnames, f, o, s, w, mf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_mux, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = o, \
+ .mux_shift = s, \
+ .mux_width = w, \
+ .mux_flags = mf, \
+ .gate_offset = -1, \
+ }
+
+#define MUXGRF(_id, cname, pnames, f, o, s, w, mf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_muxgrf, \
+ .name = cname, \
+ .parent_names = pnames, \
+ .num_parents = ARRAY_SIZE(pnames), \
+ .flags = f, \
+ .muxdiv_offset = o, \
+ .mux_shift = s, \
+ .mux_width = w, \
+ .mux_flags = mf, \
+ .gate_offset = -1, \
+ }
+
+#define DIV(_id, cname, pname, f, o, s, w, df) \
+ { \
+ .id = _id, \
+ .branch_type = branch_divider, \
+ .name = cname, \
+ .parent_names = (const char *[]){ pname }, \
+ .num_parents = 1, \
+ .flags = f, \
+ .muxdiv_offset = o, \
+ .div_shift = s, \
+ .div_width = w, \
+ .div_flags = df, \
+ .gate_offset = -1, \
+ }
+
+#define GATE(_id, cname, pname, f, o, b, gf) \
+ { \
+ .id = _id, \
+ .branch_type = branch_gate, \
+ .name = cname, \
+ .parent_names = (const char *[]){ pname }, \
+ .num_parents = 1, \
+ .flags = f, \
+ .gate_offset = o, \
+ .gate_shift = b, \
+ .gate_flags = gf, \
+ }
+
+
+void rockchip_clk_init(struct device_node *np, void __iomem *base,
+ unsigned long nr_clks);
+void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
+void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
+ unsigned int nr_clk);
+
+#endif
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 04/11] clk: rockchip: add clock type for pll clocks and pll used on rk3066
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (2 preceding siblings ...)
2014-05-23 19:34 ` [PATCH v3 03/11] clk: rockchip: add basic infrastructure for clock branches Heiko Stübner
@ 2014-05-23 19:35 ` Heiko Stübner
2014-05-23 19:36 ` [PATCH v3 05/11] clk: rockchip: add reset controller Heiko Stübner
` (6 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:35 UTC (permalink / raw)
To: linux-arm-kernel
All known Rockchip SoCs down to the RK28xx (ARM9) use a similar pattern to
handle their plls:
|--\
xin32k ----------------|mux\
xin24m -----| pll |----|pll|--- pll output
\---------------|src/
|--/
The pll output is sourced from 1 of 3 sources, the actual pll being one of
them. To change the pll frequency it is imperative to remux it to another
source beforehand. This is done by adding a clock-listener to the pll that
handles the remuxing before and after the rate change.
The output mux is implemented as a separate clock to make use of already
existing common-clock features for disabling the pll if one of the other
two sources is used.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/rockchip/Makefile | 1 +
drivers/clk/rockchip/clk-pll.c | 430 +++++++++++++++++++++++++++++++++++++++++
drivers/clk/rockchip/clk.c | 35 ++++
drivers/clk/rockchip/clk.h | 74 +++++++
4 files changed, 540 insertions(+)
create mode 100644 drivers/clk/rockchip/clk-pll.c
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 0068a8b..2cb9164 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -4,3 +4,4 @@
obj-y += clk-rockchip.o
obj-y += clk.o
+obj-y += clk-pll.o
diff --git a/drivers/clk/rockchip/clk-pll.c b/drivers/clk/rockchip/clk-pll.c
new file mode 100644
index 0000000..78e20be8
--- /dev/null
+++ b/drivers/clk/rockchip/clk-pll.c
@@ -0,0 +1,430 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <asm/div64.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
+#include <linux/regmap.h>
+#include "clk.h"
+
+#define PLL_MODE_MASK 0x3
+#define PLL_MODE_SLOW 0x0
+#define PLL_MODE_NORM 0x1
+#define PLL_MODE_DEEP 0x2
+
+struct rockchip_clk_pll {
+ struct clk_hw hw;
+
+ struct clk_mux pll_mux;
+ const struct clk_ops *pll_mux_ops;
+
+ struct notifier_block clk_nb;
+ bool rate_change_remuxed;
+
+ void __iomem *reg_base;
+ int lock_offset;
+ unsigned int lock_shift;
+ enum rockchip_pll_type type;
+ const struct rockchip_pll_rate_table *rate_table;
+ unsigned int rate_count;
+ spinlock_t *lock;
+};
+
+#define to_rockchip_clk_pll(_hw) container_of(_hw, struct rockchip_clk_pll, hw)
+#define to_rockchip_clk_pll_nb(nb) \
+ container_of(nb, struct rockchip_clk_pll, clk_nb)
+
+static const struct rockchip_pll_rate_table *rockchip_get_pll_settings(
+ struct rockchip_clk_pll *pll, unsigned long rate)
+{
+ const struct rockchip_pll_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ for (i = 0; i < pll->rate_count; i++) {
+ if (rate == rate_table[i].rate)
+ return &rate_table[i];
+ }
+
+ return NULL;
+}
+
+static long rockchip_pll_round_rate(struct clk_hw *hw,
+ unsigned long drate, unsigned long *prate)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ const struct rockchip_pll_rate_table *rate_table = pll->rate_table;
+ int i;
+
+ /* Assumming rate_table is in descending order */
+ for (i = 0; i < pll->rate_count; i++) {
+ if (drate >= rate_table[i].rate)
+ return rate_table[i].rate;
+ }
+
+ /* return minimum supported value */
+ return rate_table[i - 1].rate;
+}
+
+/*
+ * Wait for the pll to reach the locked state.
+ * The calling set_rate function is responsible for making sure the
+ * grf regmap is available.
+ */
+static int rockchip_pll_wait_lock(struct rockchip_clk_pll *pll)
+{
+ struct regmap *grf = rockchip_clk_get_grf();
+ unsigned int val;
+ int delay = 24000000, ret;
+
+ while (delay > 0) {
+ ret = regmap_read(grf, pll->lock_offset, &val);
+ if (ret) {
+ pr_err("%s: failed to read pll lock status: %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ if (val & BIT(pll->lock_shift))
+ return 0;
+ delay--;
+ }
+
+ pr_err("%s: timeout waiting for pll to lock\n", __func__);
+ return -ETIMEDOUT;
+}
+
+/**
+ * Set pll mux when changing the pll rate.
+ * This makes sure to move the pll mux away from the actual pll before
+ * changing its rate and back to the original parent after the change.
+ */
+static int rockchip_pll_notifier_cb(struct notifier_block *nb,
+ unsigned long event, void *data)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll_nb(nb);
+ struct clk_mux *pll_mux = &pll->pll_mux;
+ const struct clk_ops *pll_mux_ops = pll->pll_mux_ops;
+ int cur_parent;
+
+ switch (event) {
+ case PRE_RATE_CHANGE:
+ cur_parent = pll_mux_ops->get_parent(&pll_mux->hw);
+ if (cur_parent == PLL_MODE_NORM) {
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_SLOW);
+ pll->rate_change_remuxed = 1;
+ }
+ break;
+ case POST_RATE_CHANGE:
+ if (pll->rate_change_remuxed) {
+ pll_mux_ops->set_parent(&pll_mux->hw, PLL_MODE_NORM);
+ pll->rate_change_remuxed = 0;
+ }
+ break;
+ }
+
+ return NOTIFY_OK;
+}
+
+/**
+ * PLL used in RK3066, RK3188 and RK3288
+ */
+
+#define RK3066_PLL_RESET_DELAY(nr) ((nr * 500) / 24 + 1)
+
+#define RK3066_PLLCON(i) (i * 0x4)
+#define RK3066_PLLCON0_OD_MASK 0xf
+#define RK3066_PLLCON0_OD_SHIFT 0
+#define RK3066_PLLCON0_NR_MASK 0x3f
+#define RK3066_PLLCON0_NR_SHIFT 8
+#define RK3066_PLLCON1_NF_MASK 0x1fff
+#define RK3066_PLLCON1_NF_SHIFT 0
+#define RK3066_PLLCON2_BWADJ_MASK 0xfff
+#define RK3066_PLLCON2_BWADJ_SHIFT 0
+#define RK3066_PLLCON3_RESET (1 << 5)
+#define RK3066_PLLCON3_PWRDOWN (1 << 1)
+#define RK3066_PLLCON3_BYPASS (1 << 0)
+
+static unsigned long rockchip_rk3066_pll_recalc_rate(struct clk_hw *hw,
+ unsigned long prate)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ u64 nf, nr, no, rate64 = prate;
+ u32 pllcon;
+
+ pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(3));
+ if (pllcon & RK3066_PLLCON3_BYPASS) {
+ pr_debug("%s: pll %s is bypassed\n", __func__,
+ __clk_get_name(hw->clk));
+ return prate;
+ }
+
+ pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(1));
+ nf = (pllcon >> RK3066_PLLCON1_NF_SHIFT) & RK3066_PLLCON1_NF_MASK;
+
+ pllcon = readl_relaxed(pll->reg_base + RK3066_PLLCON(0));
+ nr = (pllcon >> RK3066_PLLCON0_NR_SHIFT) & RK3066_PLLCON0_NR_MASK;
+ no = (pllcon >> RK3066_PLLCON0_OD_SHIFT) & RK3066_PLLCON0_OD_MASK;
+
+ rate64 *= (nf + 1);
+ do_div(rate64, nr + 1);
+ do_div(rate64, no + 1);
+
+ return (unsigned long)rate64;
+}
+
+static int rockchip_rk3066_pll_set_rate(struct clk_hw *hw, unsigned long drate,
+ unsigned long prate)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ const struct rockchip_pll_rate_table *rate;
+ unsigned long old_rate = rockchip_rk3066_pll_recalc_rate(hw, prate);
+ struct regmap *grf = rockchip_clk_get_grf();
+ int ret;
+
+ if (IS_ERR(grf)) {
+ pr_debug("%s: grf regmap not available, aborting rate change\n",
+ __func__);
+ return PTR_ERR(grf);
+ }
+
+ pr_debug("%s: changing %s from %lu to %lu with a parent rate of %lu\n",
+ __func__, __clk_get_name(hw->clk), old_rate, drate, prate);
+
+ /* Get required rate settings from table */
+ rate = rockchip_get_pll_settings(pll, drate);
+ if (!rate) {
+ pr_err("%s: Invalid rate : %lu for pll clk %s\n", __func__,
+ drate, __clk_get_name(hw->clk));
+ return -EINVAL;
+ }
+
+ pr_debug("%s: rate settings for %lu (nr, no, nf): (%d, %d, %d)\n",
+ __func__, rate->rate, rate->nr, rate->no, rate->nf);
+
+ /* enter reset mode */
+ writel(HIWORD_UPDATE(RK3066_PLLCON3_RESET, RK3066_PLLCON3_RESET, 0),
+ pll->reg_base + RK3066_PLLCON(3));
+
+ /* update pll values */
+ writel(HIWORD_UPDATE(rate->nr - 1, RK3066_PLLCON0_NR_MASK,
+ RK3066_PLLCON0_NR_SHIFT) |
+ HIWORD_UPDATE(rate->no - 1, RK3066_PLLCON0_OD_MASK,
+ RK3066_PLLCON0_OD_SHIFT),
+ pll->reg_base + RK3066_PLLCON(0));
+
+ writel_relaxed(HIWORD_UPDATE(rate->nf - 1, RK3066_PLLCON1_NF_MASK,
+ RK3066_PLLCON1_NF_SHIFT),
+ pll->reg_base + RK3066_PLLCON(1));
+ writel_relaxed(HIWORD_UPDATE(rate->bwadj, RK3066_PLLCON2_BWADJ_MASK,
+ RK3066_PLLCON2_BWADJ_SHIFT),
+ pll->reg_base + RK3066_PLLCON(2));
+
+ /* leave reset and wait the reset_delay */
+ writel(HIWORD_UPDATE(0, RK3066_PLLCON3_RESET, 0),
+ pll->reg_base + RK3066_PLLCON(3));
+ udelay(RK3066_PLL_RESET_DELAY(rate->nr));
+
+ /* wait for the pll to lock */
+ ret = rockchip_pll_wait_lock(pll);
+ if (ret) {
+ pr_warn("%s: pll did not lock, trying to restore old rate %lu\n",
+ __func__, old_rate);
+ rockchip_rk3066_pll_set_rate(hw, old_rate, prate);
+ }
+
+ return ret;
+}
+
+static int rockchip_rk3066_pll_enable(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+ writel(HIWORD_UPDATE(0, RK3066_PLLCON3_PWRDOWN, 0),
+ pll->reg_base + RK3066_PLLCON(3));
+
+ return 0;
+}
+
+static void rockchip_rk3066_pll_disable(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+
+ writel(HIWORD_UPDATE(RK3066_PLLCON3_PWRDOWN,
+ RK3066_PLLCON3_PWRDOWN, 0),
+ pll->reg_base + RK3066_PLLCON(3));
+}
+
+static int rockchip_rk3066_pll_is_enabled(struct clk_hw *hw)
+{
+ struct rockchip_clk_pll *pll = to_rockchip_clk_pll(hw);
+ u32 pllcon = readl(pll->reg_base + RK3066_PLLCON(3));
+ return !(pllcon & RK3066_PLLCON3_PWRDOWN);
+}
+
+static const struct clk_ops rockchip_rk3066_pll_clk_norate_ops = {
+ .recalc_rate = rockchip_rk3066_pll_recalc_rate,
+ .enable = rockchip_rk3066_pll_enable,
+ .disable = rockchip_rk3066_pll_disable,
+ .is_enabled = rockchip_rk3066_pll_is_enabled,
+};
+
+static const struct clk_ops rockchip_rk3066_pll_clk_ops = {
+ .recalc_rate = rockchip_rk3066_pll_recalc_rate,
+ .round_rate = rockchip_pll_round_rate,
+ .set_rate = rockchip_rk3066_pll_set_rate,
+ .enable = rockchip_rk3066_pll_enable,
+ .disable = rockchip_rk3066_pll_disable,
+ .is_enabled = rockchip_rk3066_pll_is_enabled,
+};
+
+/*
+ * Common registering of pll clocks
+ */
+
+struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+ const char *name, const char **parent_names, u8 num_parents,
+ void __iomem *base, int con_offset, int grf_lock_offset,
+ int lock_shift, int mode_offset, int mode_shift,
+ struct rockchip_pll_rate_table *rate_table,
+ spinlock_t *lock)
+{
+ const char *pll_parents[3];
+ struct clk_init_data init;
+ struct rockchip_clk_pll *pll;
+ struct clk_mux *pll_mux;
+ struct clk *pll_clk, *mux_clk;
+ char pll_name[20];
+ int ret;
+
+ if (num_parents != 2) {
+ pr_err("%s: needs two parent clocks\n", __func__);
+ return ERR_PTR(-EINVAL);
+ }
+
+ /* name the actual pll */
+ snprintf(pll_name, sizeof(pll_name), "pll_%s", name);
+
+ pll = kzalloc(sizeof(*pll), GFP_KERNEL);
+ if (!pll)
+ return ERR_PTR(-ENOMEM);
+
+ init.name = pll_name;
+
+ /* keep all plls untouched for now */
+ init.flags = CLK_IGNORE_UNUSED;
+
+ init.parent_names = &parent_names[0];
+ init.num_parents = 1;
+
+ if (rate_table) {
+ int len;
+
+ /* find count of rates in rate_table */
+ for (len = 0; rate_table[len].rate != 0; )
+ len++;
+
+ pll->rate_count = len;
+ pll->rate_table = kmemdup(rate_table,
+ pll->rate_count *
+ sizeof(struct rockchip_pll_rate_table),
+ GFP_KERNEL);
+ WARN(!pll->rate_table,
+ "%s: could not allocate rate table for %s\n",
+ __func__, name);
+ }
+
+ switch (pll_type) {
+ case pll_rk3066:
+ if (!pll->rate_table)
+ init.ops = &rockchip_rk3066_pll_clk_norate_ops;
+ else
+ init.ops = &rockchip_rk3066_pll_clk_ops;
+ break;
+ default:
+ pr_warn("%s: Unknown pll type for pll clk %s\n",
+ __func__, name);
+ }
+
+ pll->hw.init = &init;
+ pll->type = pll_type;
+ pll->reg_base = base + con_offset;
+ pll->lock_offset = grf_lock_offset;
+ pll->lock_shift = lock_shift;
+ pll->lock = lock;
+ pll->clk_nb.notifier_call = rockchip_pll_notifier_cb;
+
+ pll_clk = clk_register(NULL, &pll->hw);
+ if (IS_ERR(pll_clk)) {
+ pr_err("%s: failed to register pll clock %s : %ld\n",
+ __func__, name, PTR_ERR(pll_clk));
+ mux_clk = pll_clk;
+ goto err_pll;
+ }
+
+ ret = clk_notifier_register(pll_clk, &pll->clk_nb);
+ if (ret) {
+ pr_err("%s: failed to register clock notifier for %s : %d\n",
+ __func__, name, ret);
+ mux_clk = ERR_PTR(ret);
+ goto err_pll_notifier;
+ }
+
+ /* create the mux on top of the real pll */
+ pll->pll_mux_ops = &clk_mux_ops;
+ pll_mux = &pll->pll_mux;
+
+ /* the actual muxing is xin24m, pll-output, xin32k */
+ pll_parents[0] = parent_names[0];
+ pll_parents[1] = pll_name;
+ pll_parents[2] = parent_names[1];
+
+ init.name = name;
+ init.flags = CLK_SET_RATE_PARENT;
+ init.ops = pll->pll_mux_ops;
+ init.parent_names = pll_parents;
+ init.num_parents = ARRAY_SIZE(pll_parents);
+
+ pll_mux->reg = base + mode_offset;
+ pll_mux->shift = mode_shift;
+ pll_mux->mask = PLL_MODE_MASK;
+ pll_mux->flags = 0;
+ pll_mux->lock = lock;
+ pll_mux->hw.init = &init;
+
+ if (pll_type == pll_rk3066)
+ pll_mux->flags |= CLK_MUX_HIWORD_MASK;
+
+ mux_clk = clk_register(NULL, &pll_mux->hw);
+ if (IS_ERR(mux_clk))
+ goto err_mux;
+
+ return mux_clk;
+
+err_mux:
+ ret = clk_notifier_unregister(pll_clk, &pll->clk_nb);
+ if (ret) {
+ pr_err("%s: could not unregister clock notifier in error path : %d\n",
+ __func__, ret);
+ return mux_clk;
+ }
+err_pll_notifier:
+ clk_unregister(pll_clk);
+err_pll:
+ kfree(pll);
+ return mux_clk;
+}
diff --git a/drivers/clk/rockchip/clk.c b/drivers/clk/rockchip/clk.c
index b01cac7..4bc8e92 100644
--- a/drivers/clk/rockchip/clk.c
+++ b/drivers/clk/rockchip/clk.c
@@ -23,6 +23,8 @@
#include <linux/slab.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
#include "clk.h"
/**
@@ -105,11 +107,15 @@ static DEFINE_SPINLOCK(clk_lock);
static struct clk **clk_table;
static void __iomem *reg_base;
static struct clk_onecell_data clk_data;
+static struct device_node *cru_node;
+static struct regmap *grf;
void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
unsigned long nr_clks)
{
reg_base = base;
+ cru_node = np;
+ grf = ERR_PTR(-EPROBE_DEFER);
clk_table = kzalloc(sizeof(struct clk *) * nr_clks, GFP_KERNEL);
if (!clk_table)
@@ -120,12 +126,41 @@ void __init rockchip_clk_init(struct device_node *np, void __iomem *base,
of_clk_add_provider(np, of_clk_src_onecell_get, &clk_data);
}
+struct regmap *rockchip_clk_get_grf(void)
+{
+ if (IS_ERR(grf))
+ grf = syscon_regmap_lookup_by_phandle(cru_node, "rockchip,grf");
+ return grf;
+}
+
void rockchip_clk_add_lookup(struct clk *clk, unsigned int id)
{
if (clk_table && id)
clk_table[id] = clk;
}
+void __init rockchip_clk_register_plls(struct rockchip_pll_clock *list,
+ unsigned int nr_pll, int grf_lock_offset)
+{
+ struct clk *clk;
+ int idx;
+
+ for (idx = 0; idx < nr_pll; idx++, list++) {
+ clk = rockchip_clk_register_pll(list->type, list->name,
+ list->parent_names, list->num_parents,
+ reg_base, list->con_offset, grf_lock_offset,
+ list->lock_shift, list->mode_offset,
+ list->mode_shift, list->rate_table, &clk_lock);
+ if (IS_ERR(clk)) {
+ pr_err("%s: failed to register clock %s\n", __func__,
+ list->name);
+ continue;
+ }
+
+ rockchip_clk_add_lookup(clk, list->id);
+ }
+}
+
void __init rockchip_clk_register_branches(
struct rockchip_clk_branch *list,
unsigned int nr_clk)
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index a77bd74..654dbe0 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -40,6 +40,77 @@
#define RK2928_SOFTRST_CON(x) (x * 0x4 + 0x110)
#define RK2928_MISC_CON 0x134
+enum rockchip_pll_type {
+ pll_rk3066,
+};
+
+#define RK3066_PLL_RATE(_rate, _nr, _nf, _no) \
+{ \
+ .rate = _rate##U, \
+ .nr = _nr, \
+ .nf = _nf, \
+ .no = _no, \
+ .bwadj = (_nf >> 1), \
+}
+
+struct rockchip_pll_rate_table {
+ unsigned long rate;
+ unsigned int nr;
+ unsigned int nf;
+ unsigned int no;
+ unsigned int bwadj;
+};
+
+/**
+ * struct rockchip_pll_clock: information about pll clock
+ * @id: platform specific id of the clock.
+ * @name: name of this pll clock.
+ * @parent_name: name of the parent clock.
+ * @flags: optional flags for basic clock.
+ * @con_offset: offset of the register for configuring the PLL.
+ * @mode_offset: offset of the register for configuring the PLL-mode.
+ * @mode_shift: offset inside the mode-register for the mode of this pll.
+ * @lock_shift: offset inside the lock register for the lock status.
+ * @type: Type of PLL to be registered.
+ * @rate_table: Table of usable pll rates
+ */
+struct rockchip_pll_clock {
+ unsigned int id;
+ const char *name;
+ const char **parent_names;
+ u8 num_parents;
+ unsigned long flags;
+ int con_offset;
+ int mode_offset;
+ int mode_shift;
+ int lock_shift;
+ enum rockchip_pll_type type;
+ struct rockchip_pll_rate_table *rate_table;
+};
+
+#define PLL(_type, _id, _name, _pnames, _flags, _con, _mode, _mshift, \
+ _lshift, _rtable) \
+ { \
+ .id = _id, \
+ .type = _type, \
+ .name = _name, \
+ .parent_names = _pnames, \
+ .num_parents = ARRAY_SIZE(_pnames), \
+ .flags = CLK_GET_RATE_NOCACHE | _flags, \
+ .con_offset = _con, \
+ .mode_offset = _mode, \
+ .mode_shift = _mshift, \
+ .lock_shift = _lshift, \
+ .rate_table = _rtable, \
+ }
+
+struct clk *rockchip_clk_register_pll(enum rockchip_pll_type pll_type,
+ const char *name, const char **parent_names, u8 num_parents,
+ void __iomem *base, int con_offset, int grf_lock_offset,
+ int lock_shift, int reg_mode, int mode_shift,
+ struct rockchip_pll_rate_table *rate_table,
+ spinlock_t *lock);
+
#define PNAME(x) static const char *x[] __initconst
enum rockchip_clk_branch_type {
@@ -244,8 +315,11 @@ struct rockchip_clk_branch {
void rockchip_clk_init(struct device_node *np, void __iomem *base,
unsigned long nr_clks);
+struct regmap *rockchip_clk_get_grf(void);
void rockchip_clk_add_lookup(struct clk *clk, unsigned int id);
void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
unsigned int nr_clk);
+void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
+ unsigned int nr_pll, int grf_lock_offset);
#endif
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 05/11] clk: rockchip: add reset controller
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (3 preceding siblings ...)
2014-05-23 19:35 ` [PATCH v3 04/11] clk: rockchip: add clock type for pll clocks and pll used on rk3066 Heiko Stübner
@ 2014-05-23 19:36 ` Heiko Stübner
2014-05-23 19:36 ` [PATCH v3 06/11] dt-bindings: add documentation for rk3188 clock and reset unit Heiko Stübner
` (5 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:36 UTC (permalink / raw)
To: linux-arm-kernel
All Rockchip SoCs at least down to the ARM9-based RK28xx include the reset-
controller for SoC peripherals in their clock controller.
While the older SoCs (ARM9 and Cortex-A8) use a regular scheme to change
register values, the Cortex-A9 SoCs use a hiword-mask making locking unecessary.
To be compatible with both schemes the reset controller takes a flag to
decide which scheme to use, similar to the other HIWORD_MASK flags used in the
clock framework.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/rockchip/Makefile | 1 +
drivers/clk/rockchip/clk.h | 14 +++++
drivers/clk/rockchip/softrst.c | 115 +++++++++++++++++++++++++++++++++++++++++
3 files changed, 130 insertions(+)
create mode 100644 drivers/clk/rockchip/softrst.c
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 2cb9164..85f8a55 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -5,3 +5,4 @@
obj-y += clk-rockchip.o
obj-y += clk.o
obj-y += clk-pll.o
+obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
diff --git a/drivers/clk/rockchip/clk.h b/drivers/clk/rockchip/clk.h
index 654dbe0..099cbed 100644
--- a/drivers/clk/rockchip/clk.h
+++ b/drivers/clk/rockchip/clk.h
@@ -322,4 +322,18 @@ void rockchip_clk_register_branches(struct rockchip_clk_branch *clk_list,
void rockchip_clk_register_plls(struct rockchip_pll_clock *pll_list,
unsigned int nr_pll, int grf_lock_offset);
+#define ROCKCHIP_SOFTRST_HIWORD_MASK BIT(0)
+
+#ifdef CONFIG_RESET_CONTROLLER
+void rockchip_register_softrst(struct device_node *np,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags);
+#else
+static inline void rockchip_register_softrst(struct device_node *np,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags)
+{
+}
+#endif
+
#endif
diff --git a/drivers/clk/rockchip/softrst.c b/drivers/clk/rockchip/softrst.c
new file mode 100644
index 0000000..e981f6c
--- /dev/null
+++ b/drivers/clk/rockchip/softrst.c
@@ -0,0 +1,115 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/reset-controller.h>
+#include <linux/spinlock.h>
+#include "clk.h"
+
+struct rockchip_softrst {
+ struct reset_controller_dev rcdev;
+ void __iomem *reg_base;
+ int num_regs;
+ int num_per_reg;
+ u8 flags;
+ spinlock_t lock;
+};
+
+static int rockchip_softrst_assert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rockchip_softrst *softrst = container_of(rcdev,
+ struct rockchip_softrst,
+ rcdev);
+ int bank = id / softrst->num_per_reg;
+ int offset = id % softrst->num_per_reg;
+
+ if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
+ writel(BIT(offset) | (BIT(offset) << 16),
+ softrst->reg_base + (bank * 4));
+ } else {
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&softrst->lock, flags);
+
+ reg = readl(softrst->reg_base + (bank * 4));
+ writel(reg | BIT(offset), softrst->reg_base + (bank * 4));
+
+ spin_unlock_irqrestore(&softrst->lock, flags);
+ }
+
+ return 0;
+}
+
+static int rockchip_softrst_deassert(struct reset_controller_dev *rcdev,
+ unsigned long id)
+{
+ struct rockchip_softrst *softrst = container_of(rcdev,
+ struct rockchip_softrst,
+ rcdev);
+ int bank = id / BITS_PER_LONG;
+ int offset = id % BITS_PER_LONG;
+
+ if (softrst->flags & ROCKCHIP_SOFTRST_HIWORD_MASK) {
+ writel((BIT(offset) << 16), softrst->reg_base + (bank * 4));
+ } else {
+ unsigned long flags;
+ u32 reg;
+
+ spin_lock_irqsave(&softrst->lock, flags);
+
+ reg = readl(softrst->reg_base + (bank * 4));
+ writel(reg & ~BIT(offset), softrst->reg_base + (bank * 4));
+
+ spin_unlock_irqrestore(&softrst->lock, flags);
+ }
+
+ return 0;
+}
+
+static struct reset_control_ops rockchip_softrst_ops = {
+ .assert = rockchip_softrst_assert,
+ .deassert = rockchip_softrst_deassert,
+};
+
+void __init rockchip_register_softrst(struct device_node *np,
+ unsigned int num_regs,
+ void __iomem *base, u8 flags)
+{
+ struct rockchip_softrst *softrst;
+ int ret;
+
+ softrst = kzalloc(sizeof(*softrst), GFP_KERNEL);
+ if (!softrst)
+ return;
+
+ softrst->reg_base = base;
+ softrst->num_regs = num_regs;
+ softrst->num_per_reg = (flags & ROCKCHIP_SOFTRST_HIWORD_MASK) ? 16
+ : 32;
+
+ softrst->rcdev.owner = THIS_MODULE;
+ softrst->rcdev.nr_resets = num_regs * softrst->num_per_reg;
+ softrst->rcdev.ops = &rockchip_softrst_ops;
+ softrst->rcdev.of_node = np;
+ ret = reset_controller_register(&softrst->rcdev);
+ if (ret) {
+ pr_err("%s: could not register reset controller, %d\n",
+ __func__, ret);
+ kfree(softrst);
+ }
+};
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 06/11] dt-bindings: add documentation for rk3188 clock and reset unit
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (4 preceding siblings ...)
2014-05-23 19:36 ` [PATCH v3 05/11] clk: rockchip: add reset controller Heiko Stübner
@ 2014-05-23 19:36 ` Heiko Stübner
2014-05-23 19:37 ` [PATCH v3 07/11] clk: rockchip: add clock driver for rk3188 clocks Heiko Stübner
` (4 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:36 UTC (permalink / raw)
To: linux-arm-kernel
This add bindings documentation for the clock and reset unit found on
rk3188 SoCs from Rockchip.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
.../bindings/clock/rockchip,rk3188-cru.txt | 59 ++++++++++++++++++++++
1 file changed, 59 insertions(+)
create mode 100644 Documentation/devicetree/bindings/clock/rockchip,rk3188-cru.txt
diff --git a/Documentation/devicetree/bindings/clock/rockchip,rk3188-cru.txt b/Documentation/devicetree/bindings/clock/rockchip,rk3188-cru.txt
new file mode 100644
index 0000000..56d1ef5
--- /dev/null
+++ b/Documentation/devicetree/bindings/clock/rockchip,rk3188-cru.txt
@@ -0,0 +1,59 @@
+* Rockchip RK3188 Clock and Reset Unit
+
+The RK3188 clock controller generates and supplies clock to various controllers
+within the SoC and also implements a reset controller for SoC peripherals.
+There exist two variants of the rk3188 SoC, the rk3188 and rk3188a, with
+slight differences in the pll handling.
+
+Required Properties:
+
+- compatible: should be "rockchip,rk3188-cru" or "rockchip,rk3188a-cru"
+- reg: physical base address of the controller and length of memory mapped
+ region.
+- #clock-cells: should be 1.
+- #reset-cells: should be 1.
+
+Optional Properties:
+
+- rockchip,grf: phandle to the syscon managing the "general register files"
+ If missing pll rates are not changable, due to the missing pll lock status.
+
+Each clock is assigned an identifier and client nodes can use this identifier
+to specify the clock which they consume. All available clocks are defined as
+preprocessor macros in dt-bindings/clock/rk3188-cru.h header and can be used
+in device tree sources.
+
+External clocks:
+
+There are several clocks that are generated outside the SoC. It is expected
+that they are defined using standard clock bindings with following
+clock-output-names:
+ - "xin24m" - crystal input - required,
+ - "xin32k" - rtc clock - optional,
+ - "ext_hsadc" - external HSADC clock - optional,
+ - "ext_cif" - external camera clock - optional,
+ - "ext_rmii" - external RMII clock - optional,
+ - "ext_jtag" - externalJTAG clock - optional
+
+Example: Clock controller node:
+
+ cru: cru at 20000000 {
+ compatible = "rockchip,rk3188-cru";
+ reg = <0x20000000 0x1000>;
+ rockchip,grf = <&grf>;
+
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
+Example: UART controller node that consumes the clock generated by the clock
+ controller:
+
+ uart0: serial at 10124000 {
+ compatible = "snps,dw-apb-uart";
+ reg = <0x10124000 0x400>;
+ interrupts = <GIC_SPI 34 IRQ_TYPE_LEVEL_HIGH>;
+ reg-shift = <2>;
+ reg-io-width = <1>;
+ clocks = <&cru SCLK_UART0>;
+ };
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 07/11] clk: rockchip: add clock driver for rk3188 clocks
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (5 preceding siblings ...)
2014-05-23 19:36 ` [PATCH v3 06/11] dt-bindings: add documentation for rk3188 clock and reset unit Heiko Stübner
@ 2014-05-23 19:37 ` Heiko Stübner
2014-05-23 19:37 ` [PATCH v3 08/11] ARM: rockchip: Select ARCH_HAS_RESET_CONTROLLER Heiko Stübner
` (3 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:37 UTC (permalink / raw)
To: linux-arm-kernel
This adds a clock driver that handles the specific muxes, dividers and gates
of rk3188 SoCs.
The structure of the clock list resembles the arrangement of their
counterparts in the clock architecture diagrams found in the SoC
documentation.
Clocks exported to the clock provider are currently limited to well known
or measured ones.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
drivers/clk/rockchip/Makefile | 2 +
drivers/clk/rockchip/clk-rk3188.c | 561 +++++++++++++++++++++++++++++++++
include/dt-bindings/clock/rk3188-cru.h | 78 +++++
3 files changed, 641 insertions(+)
create mode 100644 drivers/clk/rockchip/clk-rk3188.c
create mode 100644 include/dt-bindings/clock/rk3188-cru.h
diff --git a/drivers/clk/rockchip/Makefile b/drivers/clk/rockchip/Makefile
index 85f8a55..3eebf72 100644
--- a/drivers/clk/rockchip/Makefile
+++ b/drivers/clk/rockchip/Makefile
@@ -6,3 +6,5 @@ obj-y += clk-rockchip.o
obj-y += clk.o
obj-y += clk-pll.o
obj-$(CONFIG_RESET_CONTROLLER) += softrst.o
+
+obj-y += clk-rk3188.o
diff --git a/drivers/clk/rockchip/clk-rk3188.c b/drivers/clk/rockchip/clk-rk3188.c
new file mode 100644
index 0000000..0317b90
--- /dev/null
+++ b/drivers/clk/rockchip/clk-rk3188.c
@@ -0,0 +1,561 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk-provider.h>
+#include <linux/of.h>
+#include <linux/of_address.h>
+#include <dt-bindings/clock/rk3188-cru.h>
+#include "clk.h"
+
+#define RK3188_GRF_SOC_CON1 0xa4
+#define RK3188_GRF_SOC_STATUS 0xac
+
+enum rk3188_plls {
+ apll, cpll, dpll, gpll,
+};
+
+struct rockchip_pll_rate_table rk3188_apll_rates[] = {
+ RK3066_PLL_RATE(2208000000, 1, 92, 1),
+ RK3066_PLL_RATE(2184000000, 1, 91, 1),
+ RK3066_PLL_RATE(2160000000, 1, 90, 1),
+ RK3066_PLL_RATE(2136000000, 1, 89, 1),
+ RK3066_PLL_RATE(2112000000, 1, 88, 1),
+ RK3066_PLL_RATE(2088000000, 1, 87, 1),
+ RK3066_PLL_RATE(2064000000, 1, 86, 1),
+ RK3066_PLL_RATE(2040000000, 1, 85, 1),
+ RK3066_PLL_RATE(2016000000, 1, 84, 1),
+ RK3066_PLL_RATE(1992000000, 1, 83, 1),
+ RK3066_PLL_RATE(1968000000, 1, 82, 1),
+ RK3066_PLL_RATE(1944000000, 1, 81, 1),
+ RK3066_PLL_RATE(1920000000, 1, 80, 1),
+ RK3066_PLL_RATE(1896000000, 1, 79, 1),
+ RK3066_PLL_RATE(1872000000, 1, 78, 1),
+ RK3066_PLL_RATE(1848000000, 1, 77, 1),
+ RK3066_PLL_RATE(1824000000, 1, 76, 1),
+ RK3066_PLL_RATE(1800000000, 1, 75, 1),
+ RK3066_PLL_RATE(1776000000, 1, 74, 1),
+ RK3066_PLL_RATE(1752000000, 1, 73, 1),
+ RK3066_PLL_RATE(1728000000, 1, 72, 1),
+ RK3066_PLL_RATE(1704000000, 1, 71, 1),
+ RK3066_PLL_RATE(1680000000, 1, 70, 1),
+ RK3066_PLL_RATE(1656000000, 1, 69, 1),
+ RK3066_PLL_RATE(1632000000, 1, 68, 1),
+ RK3066_PLL_RATE(1608000000, 1, 67, 1),
+ RK3066_PLL_RATE(1560000000, 1, 65, 1),
+ RK3066_PLL_RATE(1512000000, 1, 63, 1),
+ RK3066_PLL_RATE(1488000000, 1, 62, 1),
+ RK3066_PLL_RATE(1464000000, 1, 61, 1),
+ RK3066_PLL_RATE(1440000000, 1, 60, 1),
+ RK3066_PLL_RATE(1416000000, 1, 59, 1),
+ RK3066_PLL_RATE(1392000000, 1, 58, 1),
+ RK3066_PLL_RATE(1368000000, 1, 57, 1),
+ RK3066_PLL_RATE(1344000000, 1, 56, 1),
+ RK3066_PLL_RATE(1320000000, 1, 55, 1),
+ RK3066_PLL_RATE(1296000000, 1, 54, 1),
+ RK3066_PLL_RATE(1272000000, 1, 53, 1),
+ RK3066_PLL_RATE(1248000000, 1, 52, 1),
+ RK3066_PLL_RATE(1224000000, 1, 51, 1),
+ RK3066_PLL_RATE(1200000000, 1, 50, 1),
+ RK3066_PLL_RATE(1176000000, 1, 49, 1),
+ RK3066_PLL_RATE(1128000000, 1, 47, 1),
+ RK3066_PLL_RATE(1104000000, 1, 46, 1),
+ RK3066_PLL_RATE(1008000000, 1, 84, 2),
+ RK3066_PLL_RATE( 912000000, 1, 76, 2),
+ RK3066_PLL_RATE( 888000000, 1, 74, 2),
+ RK3066_PLL_RATE( 816000000, 1, 68, 2),
+ RK3066_PLL_RATE( 792000000, 1, 66, 2),
+ RK3066_PLL_RATE( 696000000, 1, 58, 2),
+ RK3066_PLL_RATE( 600000000, 1, 50, 2),
+ RK3066_PLL_RATE( 504000000, 1, 84, 4),
+ RK3066_PLL_RATE( 408000000, 1, 68, 4),
+ RK3066_PLL_RATE( 312000000, 1, 52, 4),
+ RK3066_PLL_RATE( 252000000, 1, 84, 8),
+ RK3066_PLL_RATE( 216000000, 1, 72, 8),
+ RK3066_PLL_RATE( 126000000, 1, 84, 16),
+ RK3066_PLL_RATE( 48000000, 1, 64, 32),
+ { /* sentinel */ },
+};
+
+struct rockchip_pll_rate_table rk3188_cpll_rates[] = {
+ RK3066_PLL_RATE(1188000000, 2, 99, 1),
+ RK3066_PLL_RATE( 798000000, 2, 133, 2),
+ RK3066_PLL_RATE( 768000000, 1, 64, 2),
+ RK3066_PLL_RATE( 742500000, 8, 495, 2),
+ RK3066_PLL_RATE( 600000000, 1, 50, 2),
+ RK3066_PLL_RATE( 594000000, 2, 198, 4),
+ RK3066_PLL_RATE( 552000000, 1, 46, 2),
+ RK3066_PLL_RATE( 504000000, 1, 84, 4),
+ RK3066_PLL_RATE( 456000000, 1, 76, 4),
+ RK3066_PLL_RATE( 408000000, 1, 68, 4),
+ RK3066_PLL_RATE( 360000000, 1, 60, 4),
+ { /* sentinel */ },
+};
+
+struct rockchip_pll_rate_table rk3188_gpll_rates[] = {
+ RK3066_PLL_RATE(1200000000, 1, 50, 1),
+ RK3066_PLL_RATE(1188000000, 2, 99, 1),
+ RK3066_PLL_RATE( 891000000, 8, 594, 2),
+ RK3066_PLL_RATE( 768000000, 1, 64, 2),
+ RK3066_PLL_RATE( 594000000, 2, 198, 4),
+ RK3066_PLL_RATE( 384000000, 2, 128, 4),
+ RK3066_PLL_RATE( 300000000, 1, 50, 4),
+ RK3066_PLL_RATE( 297000000, 2, 198, 8),
+ RK3066_PLL_RATE( 148500000, 2, 99, 8),
+ { /* sentinel */ },
+};
+
+PNAME(mux_pll_p) = { "xin24m", "xin32k" };
+PNAME(mux_armclk_p) = { "apll", "gpll_armclk" };
+PNAME(mux_ddrphy_p) = { "dpll", "gpll_ddr" };
+PNAME(mux_pll_src_gpll_cpll_p) = { "gpll", "cpll" };
+PNAME(mux_pll_src_cpll_gpll_p) = { "cpll", "gpll" };
+PNAME(mux_aclk_cpu_p) = { "apll", "gpll" };
+PNAME(mux_sclk_cif_p) = { "cif_pre", "xin24m" };
+PNAME(mux_sclk_i2s_p) = { "i2s_pre", "i2s_frac", "xin12m" };
+PNAME(mux_sclk_spdif_p) = { "spdif_pre", "spdif_frac", "xin12m" };
+PNAME(mux_sclk_uart0_p) = { "uart0_pll", "uart0_frac", "xin24m" };
+PNAME(mux_sclk_uart1_p) = { "uart1_pll", "uart1_frac", "xin24m" };
+PNAME(mux_sclk_uart2_p) = { "uart2_pll", "uart2_frac", "xin24m" };
+PNAME(mux_sclk_uart3_p) = { "uart3_pll", "uart3_frac", "xin24m" };
+PNAME(mux_sclk_hsadc_p) = { "hsadc_pll", "hsadc_frac", "ext_hsadc" };
+PNAME(mux_mac_p) = { "gpll", "dpll" };
+PNAME(mux_sclk_macref_p) = { "mac_src", "ext_rmii" };
+PNAME(mux_hsicphy_p) = { "sclk_otgphy0", "sclk_otgphy1",
+ "gpll", "cpll" };
+PNAME(mux_testclk_p) = { "pclk_cpu", "hclk_peri", "dclk_lcdc0_src",
+ "sclk_uart0", "sclk_ddr", "hclk_vpu" };
+PNAME(mux_aclk_vpu_p) = { "aclk_vepu", "aclk_vdpu" };
+
+static struct rockchip_pll_clock rk3188_pll_clks[] __initdata = {
+ [apll] = PLL(pll_rk3066, 0, "apll", mux_pll_p, 0, RK2928_PLL_CON(0),
+ RK2928_MODE_CON, 0, 6, rk3188_apll_rates),
+ [dpll] = PLL(pll_rk3066, 0, "dpll", mux_pll_p, 0, RK2928_PLL_CON(4),
+ RK2928_MODE_CON, 4, 5, NULL),
+ [cpll] = PLL(pll_rk3066, 0, "cpll", mux_pll_p, 0, RK2928_PLL_CON(8),
+ RK2928_MODE_CON, 8, 7, rk3188_cpll_rates),
+ [gpll] = PLL(pll_rk3066, 0, "gpll", mux_pll_p, 0, RK2928_PLL_CON(12),
+ RK2928_MODE_CON, 12, 8, rk3188_gpll_rates),
+};
+
+#define MFLAGS CLK_MUX_HIWORD_MASK
+#define DFLAGS CLK_DIVIDER_HIWORD_MASK
+#define GFLAGS (CLK_GATE_HIWORD_MASK | CLK_GATE_SET_TO_DISABLE)
+
+/* 2 ^ (val + 1) */
+static struct clk_div_table div_core_peri_t[] = {
+ { .val = 0, .div = 2 },
+ { .val = 1, .div = 4 },
+ { .val = 2, .div = 8 },
+ { .val = 3, .div = 16 },
+ { /* sentinel */},
+};
+
+static struct clk_div_table div_aclk_core_t[] = {
+ { .val = 0, .div = 1 },
+ { .val = 1, .div = 2 },
+ { .val = 2, .div = 3 },
+ { .val = 3, .div = 4 },
+ { .val = 4, .div = 8 },
+ { /* sentinel */},
+};
+
+static struct rockchip_clk_branch rk3188_clk_branches[] __initdata = {
+ /*
+ * Clock-Architecture Diagram 2
+ */
+
+ GATE(0, "gpll_armclk", "gpll", 0, RK2928_CLKGATE_CON(0), 1, GFLAGS),
+ COMPOSITE_NOGATE(ARMCLK, "armclk", mux_armclk_p, 0,
+ RK2928_CLKSEL_CON(0), 8, 1, MFLAGS, 9, 5, DFLAGS),
+
+ /* these two are set by the cpuclk and should not be changed */
+ COMPOSITE_NOMUX_DIVTBL(CORE_PERI, "core_peri", "armclk", 0,
+ RK2928_CLKSEL_CON(0), 6, 2, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ div_core_peri_t, RK2928_CLKGATE_CON(0), 0, GFLAGS),
+ COMPOSITE_NOMUX_DIVTBL(0, "aclk_core", "armclk", 0,
+ RK2928_CLKSEL_CON(1), 3, 3, DFLAGS | CLK_DIVIDER_READ_ONLY,
+ div_aclk_core_t, RK2928_CLKGATE_CON(0), 7, GFLAGS),
+
+ COMPOSITE(0, "aclk_vepu", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(32), 7, 1, MFLAGS, 0, 5, DFLAGS,
+ RK2928_CLKGATE_CON(3), 9, GFLAGS),
+ GATE(0, "hclk_vepu", "aclk_vepu", 0,
+ RK2928_CLKGATE_CON(3), 10, GFLAGS),
+ COMPOSITE(0, "aclk_vdpu", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(32), 15, 1, MFLAGS, 8, 5, DFLAGS,
+ RK2928_CLKGATE_CON(3), 11, GFLAGS),
+ GATE(0, "hclk_vdpu", "aclk_vdpu", 0,
+ RK2928_CLKGATE_CON(3), 12, GFLAGS),
+ MUXGRF(0, "aclk_vpu", mux_aclk_vpu_p, 0,
+ RK3188_GRF_SOC_CON1, 10, 1, MFLAGS),
+
+ GATE(0, "gpll_ddr", "gpll", 0,
+ RK2928_CLKGATE_CON(1), 7, GFLAGS),
+ COMPOSITE(0, "ddrphy", mux_ddrphy_p, 0,
+ RK2928_CLKSEL_CON(26), 8, 1, MFLAGS, 0, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+ RK2928_CLKGATE_CON(0), 2, GFLAGS),
+
+ /* do not source aclk_cpu_pre from the apll, to keep complexity down */
+ COMPOSITE_NOGATE(0, "aclk_cpu_pre", mux_aclk_cpu_p, CLK_SET_RATE_NO_REPARENT,
+ RK2928_CLKSEL_CON(0), 5, 1, MFLAGS, 0, 5, DFLAGS),
+ GATE(0, "aclk_cpu", "aclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(0), 3, GFLAGS),
+
+ DIV(0, "pclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(1), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
+ GATE(0, "atclk_cpu", "pclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(0), 6, GFLAGS),
+ GATE(0, "pclk_cpu", "pclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(0), 5, GFLAGS),
+ DIV(0, "hclk_cpu_pre", "aclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(1), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO),
+ COMPOSITE_NOMUX(0, "hclk_ahb2apb", "hclk_cpu_pre", 0,
+ RK2928_CLKSEL_CON(1), 14, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+ RK2928_CLKGATE_CON(4), 9, GFLAGS),
+ GATE(0, "hclk_cpu", "hclk_cpu_pre", 0,
+ RK2928_CLKGATE_CON(0), 4, GFLAGS),
+
+ COMPOSITE(0, "aclk_gpu_src", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(34), 7, 1, MFLAGS, 0, 5, DFLAGS,
+ RK2928_CLKGATE_CON(3), 15, GFLAGS),
+ GATE(0, "aclk_gpu", "aclk_gpu_src", 0,
+ RK2928_CLKGATE_CON(9), 7, GFLAGS),
+
+ COMPOSITE(0, "aclk_lcdc0_pre", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(31), 7, 1, MFLAGS, 0, 5, DFLAGS,
+ RK2928_CLKGATE_CON(3), 0, GFLAGS),
+ COMPOSITE(0, "aclk_lcdc1_pre", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(31), 15, 1, MFLAGS, 8, 5, DFLAGS,
+ RK2928_CLKGATE_CON(1), 4, GFLAGS),
+
+ COMPOSITE(0, "aclk_peri_pre", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(10), 15, 1, MFLAGS, 0, 5, DFLAGS,
+ RK2928_CLKGATE_CON(2), 0, GFLAGS),
+ GATE(0, "aclk_peri", "aclk_peri_pre", 0,
+ RK2928_CLKGATE_CON(2), 1, GFLAGS),
+ COMPOSITE_NOMUX(0, "hclk_peri", "aclk_peri_pre", 0,
+ RK2928_CLKSEL_CON(10), 8, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+ RK2928_CLKGATE_CON(2), 2, GFLAGS),
+ COMPOSITE_NOMUX(0, "pclk_peri", "aclk_peri_pre", 0,
+ RK2928_CLKSEL_CON(10), 12, 2, DFLAGS | CLK_DIVIDER_POWER_OF_TWO,
+ RK2928_CLKGATE_CON(2), 3, GFLAGS),
+
+ /*
+ * Clock-Architecture Diagram 3
+ */
+
+ COMPOSITE(0, "dclk_lcdc0_src", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(27), 0, 1, MFLAGS, 8, 8, DFLAGS,
+ RK2928_CLKGATE_CON(3), 1, GFLAGS),
+ COMPOSITE(0, "dclk_lcdc1_src", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(28), 0, 1, MFLAGS, 8, 8, DFLAGS,
+ RK2928_CLKGATE_CON(3), 2, GFLAGS),
+
+ COMPOSITE(0, "cif_pre", mux_pll_src_cpll_gpll_p, 0,
+ RK2928_CLKSEL_CON(29), 0, 1, MFLAGS, 1, 5, DFLAGS,
+ RK2928_CLKGATE_CON(3), 7, GFLAGS),
+ MUX(0, "sclk_cif", mux_sclk_cif_p, 0,
+ RK2928_CLKSEL_CON(29), 7, 1, MFLAGS),
+
+ GATE(0, "pclk_cif_in", "ext_cif", 0,
+ RK2928_CLKGATE_CON(3), 3, GFLAGS),
+
+ MUX(0, "i2s_src", mux_pll_src_gpll_cpll_p, 0,
+ RK2928_CLKSEL_CON(2), 15, 1, MFLAGS),
+ COMPOSITE_NOMUX(0, "i2s_pre", "i2s_src", 0,
+ RK2928_CLKSEL_CON(3), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(0), 9, GFLAGS),
+ COMPOSITE_FRAC(0, "i2s_frac", "i2s_pll", 0, RK2928_CLKGATE_CON(7), 0,
+ RK2928_CLKGATE_CON(0), 10, GFLAGS),
+ MUX(0, "sclk_i2s", mux_sclk_i2s_p, 0,
+ RK2928_CLKSEL_CON(3), 8, 2, MFLAGS),
+ COMPOSITE_NOMUX(0, "spdif_pre", "i2s_src", 0,
+ RK2928_CLKSEL_CON(5), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(13), 13, GFLAGS),
+ COMPOSITE_FRAC(0, "spdif_frac", "spdif_pll", 0,
+ RK2928_CLKGATE_CON(9), 0,
+ RK2928_CLKGATE_CON(0), 14, GFLAGS),
+ MUX(0, "sclk_spdif", mux_sclk_spdif_p, 0,
+ RK2928_CLKSEL_CON(5), 8, 2, MFLAGS),
+
+ /*
+ * the 480m are generated inside the usb block from these clocks,
+ * but they are also a source for the hsicphy clock.
+ */
+ GATE(0, "sclk_otgphy0", "usb480m", 0,
+ RK2928_CLKGATE_CON(1), 5, GFLAGS),
+ GATE(0, "sclk_otgphy1", "usb480m", 0,
+ RK2928_CLKGATE_CON(1), 6, GFLAGS),
+
+ COMPOSITE_NODIV(0, "sclk_hsicphy_480m", mux_hsicphy_p, 0,
+ RK2928_CLKSEL_CON(30), 0, 2, DFLAGS,
+ RK2928_CLKGATE_CON(3), 6, GFLAGS),
+ DIV(0, "sclk_hsicphy_12m", "sclk_hsicphy_480m", 0,
+ RK2928_CLKGATE_CON(11), 8, 6, DFLAGS),
+
+ COMPOSITE(0, "mac_src", mux_mac_p, 0,
+ RK2928_CLKSEL_CON(21), 0, 1, MFLAGS, 8, 5, DFLAGS,
+ RK2928_CLKGATE_CON(2), 5, GFLAGS),
+ MUX(SCLK_MAC, "sclk_mac_ref", mux_sclk_macref_p, CLK_SET_RATE_PARENT,
+ RK2928_CLKSEL_CON(21), 4, 1, MFLAGS),
+
+ GATE(0, "sclk_mac_lbtest", "sclk_mac_ref",
+ RK2928_CLKGATE_CON(2), 12, 0, GFLAGS),
+
+ COMPOSITE(0, "hsadc_pll", mux_pll_src_gpll_cpll_p, 0,
+ RK2928_CLKSEL_CON(22), 0, 1, MFLAGS, 8, 8, DFLAGS,
+ RK2928_CLKGATE_CON(2), 6, GFLAGS),
+ COMPOSITE_FRAC(0, "hsadc_frac", "hsadc_pll",
+ RK2928_CLKGATE_CON(23), 0,
+ RK2928_CLKGATE_CON(2), 7, 0, GFLAGS),
+ MUX(0, "sclk_hsadc", mux_sclk_hsadc_p, 0,
+ RK2928_CLKSEL_CON(22), 4, 2, MFLAGS),
+
+ COMPOSITE_NOMUX(SCLK_SARADC, "sclk_saradc", "xin24m", 0,
+ RK2928_CLKSEL_CON(24), 8, 8, DFLAGS,
+ RK2928_CLKGATE_CON(2), 8, GFLAGS),
+
+ /*
+ * Clock-Architecture Diagram 4
+ */
+
+ GATE(0, "sclk_smc", "hclk_peri",
+ RK2928_CLKGATE_CON(2), 4, 0, GFLAGS),
+
+ COMPOSITE_NOMUX(SCLK_SPI0, "sclk_spi0", "pclk_peri", 0,
+ RK2928_CLKSEL_CON(25), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(2), 9, GFLAGS),
+ COMPOSITE_NOMUX(SCLK_SPI1, "sclk_spi1", "pclk_peri", 0,
+ RK2928_CLKSEL_CON(25), 8, 7, DFLAGS,
+ RK2928_CLKGATE_CON(2), 10, GFLAGS),
+
+ COMPOSITE_NOMUX(SCLK_MMC0, "sclk_mmc0", "hclk_peri", 0,
+ RK2928_CLKSEL_CON(11), 0, 6, DFLAGS,
+ RK2928_CLKGATE_CON(2), 11, GFLAGS),
+ COMPOSITE_NOMUX(SCLK_MMC1, "sclk_mmc1", "hclk_peri", 0,
+ RK2928_CLKSEL_CON(12), 0, 6, DFLAGS,
+ RK2928_CLKGATE_CON(2), 13, GFLAGS),
+ COMPOSITE_NOMUX(SCLK_MMC2, "sclk_mmc2", "hclk_peri", 0,
+ RK2928_CLKSEL_CON(12), 8, 6, DFLAGS,
+ RK2928_CLKGATE_CON(2), 14, GFLAGS),
+
+ MUX(0, "uart_pll", mux_pll_src_gpll_cpll_p, 0,
+ RK2928_CLKSEL_CON(12), 15, 1, MFLAGS),
+ COMPOSITE_NOMUX(0, "uart0_pll", "uart_pll", 0,
+ RK2928_CLKSEL_CON(13), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(1), 8, GFLAGS),
+ COMPOSITE_FRAC(0, "uart0_frac", "uart0_pll", 0,
+ RK2928_CLKSEL_CON(17), 0,
+ RK2928_CLKGATE_CON(1), 9, GFLAGS),
+ MUX(SCLK_UART0, "sclk_uart0", mux_sclk_uart0_p, 0,
+ RK2928_CLKSEL_CON(13), 8, 2, MFLAGS),
+ COMPOSITE_NOMUX(0, "uart1_pll", "uart_pll", 0,
+ RK2928_CLKSEL_CON(14), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(1), 10, GFLAGS),
+ COMPOSITE_FRAC(0, "uart1_frac", "uart1_pll", 0,
+ RK2928_CLKSEL_CON(18), 0,
+ RK2928_CLKGATE_CON(1), 11, GFLAGS),
+ MUX(SCLK_UART1, "sclk_uart1", mux_sclk_uart1_p, 0,
+ RK2928_CLKSEL_CON(14), 8, 2, MFLAGS),
+ COMPOSITE_NOMUX(0, "uart2_pll", "uart_pll", 0,
+ RK2928_CLKSEL_CON(15), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(1), 12, GFLAGS),
+ COMPOSITE_FRAC(0, "uart2_frac", "uart2_pll", 0,
+ RK2928_CLKSEL_CON(19), 0,
+ RK2928_CLKGATE_CON(1), 13, GFLAGS),
+ MUX(SCLK_UART2, "sclk_uart2", mux_sclk_uart2_p, 0,
+ RK2928_CLKSEL_CON(15), 8, 2, MFLAGS),
+ COMPOSITE_NOMUX(0, "uart3_pll", "uart_pll", 0,
+ RK2928_CLKSEL_CON(16), 0, 7, DFLAGS,
+ RK2928_CLKGATE_CON(1), 14, GFLAGS),
+ COMPOSITE_FRAC(0, "uart3_frac", "uart3_pll", 0,
+ RK2928_CLKSEL_CON(20), 0,
+ RK2928_CLKGATE_CON(1), 15, GFLAGS),
+ MUX(SCLK_UART3, "sclk_uart3", mux_sclk_uart3_p, 0,
+ RK2928_CLKSEL_CON(16), 8, 2, MFLAGS),
+
+ GATE(0, "timer0", "xin24m", 0, RK2928_CLKGATE_CON(1), 0, GFLAGS),
+ GATE(0, "timer1", "xin24m", 0, RK2928_CLKGATE_CON(1), 1, GFLAGS),
+ GATE(0, "timer2", "xin24m", 0, RK2928_CLKGATE_CON(3), 4, GFLAGS),
+ GATE(0, "timer3", "xin24m", 0, RK2928_CLKGATE_CON(1), 2, GFLAGS),
+ GATE(0, "timer4", "xin24m", 0, RK2928_CLKGATE_CON(3), 5, GFLAGS),
+ GATE(0, "timer5", "xin24m", 0, RK2928_CLKGATE_CON(3), 8, GFLAGS),
+ GATE(0, "timer6", "xin24m", 0, RK2928_CLKGATE_CON(3), 14, GFLAGS),
+
+ GATE(0, "jtag", "ext_jtag", 0, RK2928_CLKGATE_CON(1), 3, GFLAGS),
+
+ /*
+ * Clock-Architecture Diagram 5
+ */
+
+ /* clk_core_pre gates */
+ GATE(CORE_L2C, "core_l2c", "armclk", 0, RK2928_CLKGATE_CON(9), 4, GFLAGS),
+ GATE(0, "core_dbg", "armclk", 0, RK2928_CLKGATE_CON(9), 0, GFLAGS),
+
+ /* aclk_cpu gates */
+ GATE(ACLK_DMAC0, "aclk_dmac0", "aclk_cpu", 0, RK2928_CLKGATE_CON(5), 0, GFLAGS),
+ GATE(0, "aclk_intmem", "aclk_cpu", 0, RK2928_CLKGATE_CON(4), 12, GFLAGS),
+ GATE(0, "aclk_strc_sys", "aclk_cpu", 0, RK2928_CLKGATE_CON(4), 10, GFLAGS),
+
+ /* hclk_cpu gates */
+ GATE(0, "hclk_rom", "hclk_cpu", 0, RK2928_CLKGATE_CON(5), 6, GFLAGS),
+ GATE(HCLK_I2S, "hclk_i2s", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 2, GFLAGS),
+ GATE(HCLK_SPDIF, "hclk_spdif", "hclk_cpu", 0, RK2928_CLKGATE_CON(7), 1, GFLAGS),
+ GATE(0, "hclk_cpubus", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 8, GFLAGS),
+ /* hclk_ahb2apb is part of a clk branch */
+ GATE(0, "hclk_vio_bus", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 12, GFLAGS),
+ GATE(0, "hclk_lcdc0", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 1, GFLAGS),
+ GATE(0, "hclk_lcdc1", "aclk_cpu", 0, RK2928_CLKGATE_CON(6), 2, GFLAGS),
+ GATE(0, "hclk_cif", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 4, GFLAGS),
+ GATE(0, "hclk_ipp", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 9, GFLAGS),
+ GATE(0, "hclk_rga", "hclk_cpu", 0, RK2928_CLKGATE_CON(6), 10, GFLAGS),
+ GATE(0, "hclk_imem0", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 14, GFLAGS),
+ GATE(0, "hclk_imem1", "hclk_cpu", 0, RK2928_CLKGATE_CON(4), 15, GFLAGS),
+
+ /* hclk_peri gates */
+ GATE(0, "hclk_peri_axi_matrix", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 0, GFLAGS),
+ GATE(0, "hclk_peri_ahb_arbi", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 6, GFLAGS),
+ GATE(0, "hclk_emem_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 7, GFLAGS),
+ GATE(HCLK_EMAC, "hclk_emac", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 0, GFLAGS),
+ GATE(0, "hclk_nandc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 9, GFLAGS),
+ GATE(0, "hclk_usb_peri", "hclk_peri", 0, RK2928_CLKGATE_CON(4), 5, GFLAGS),
+ GATE(HCLK_OTG0, "hclk_usbotg0", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 13, GFLAGS),
+ GATE(HCLK_OTG1, "hclk_usbotg1", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 3, GFLAGS),
+ GATE(HCLK_HSIC, "hclk_hsic", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 4, GFLAGS),
+ GATE(HCLK_HSADC, "hclk_hsadc", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 5, GFLAGS),
+ GATE(HCLK_PIDF, "hclk_pidfilter", "hclk_peri", 0, RK2928_CLKGATE_CON(7), 6, GFLAGS),
+ GATE(HCLK_MMC0, "hclk_sdmmc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 10, GFLAGS),
+ GATE(HCLK_MMC1, "hclk_sdio", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 11, GFLAGS),
+ GATE(HCLK_MMC2, "hclk_emmc", "hclk_peri", 0, RK2928_CLKGATE_CON(5), 12, GFLAGS),
+
+ /* aclk_lcdc0_pre gates */
+ GATE(0, "aclk_vio0", "aclk_lcdc0_pre", 0, RK2928_CLKGATE_CON(6), 13, GFLAGS),
+ GATE(0, "aclk_lcdc0", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 0, GFLAGS),
+ GATE(0, "aclk_cif", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 5, GFLAGS),
+ GATE(0, "aclk_ipp", "aclk_vio0", 0, RK2928_CLKGATE_CON(6), 8, GFLAGS),
+
+ /* aclk_lcdc1_pre gates */
+ GATE(0, "aclk_vio1", "aclk_lcdc1_pre", 0, RK2928_CLKGATE_CON(9), 5, GFLAGS),
+ GATE(0, "aclk_lcdc1", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 3, GFLAGS),
+ GATE(0, "aclk_rga", "aclk_vio1", 0, RK2928_CLKGATE_CON(6), 11, GFLAGS),
+
+ /* atclk_cpu gates */
+ GATE(0, "atclk", "atclk_cpu", 0, RK2928_CLKGATE_CON(9), 3, GFLAGS),
+ GATE(0, "trace", "atclk_cpu", 0, RK2928_CLKGATE_CON(9), 2, GFLAGS),
+
+ /* hclk_cpu_h2p gates */
+ GATE(PCLK_UART0, "pclk_uart0", "hclk_ahb2apb", 0, RK2928_CLKGATE_CON(8), 0, GFLAGS),
+ GATE(PCLK_UART1, "pclk_uart1", "hclk_ahb2apb", 0, RK2928_CLKGATE_CON(8), 1, GFLAGS),
+
+ /* pclk_cpu gates */
+ GATE(PCLK_PWM01, "pclk_pwm01", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 10, GFLAGS),
+ GATE(PCLK_TIMER0, "pclk_timer0", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 7, GFLAGS),
+ GATE(PCLK_TIMER2, "pclk_timer3", "pclk_cpu", 0, RK2928_CLKGATE_CON(7), 9, GFLAGS),
+ GATE(PCLK_I2C0, "pclk_i2c0", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 4, GFLAGS),
+ GATE(PCLK_I2C1, "pclk_i2c1", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 5, GFLAGS),
+ GATE(PCLK_GPIO0, "pclk_gpio0", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 9, GFLAGS),
+ GATE(PCLK_GPIO1, "pclk_gpio1", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 10, GFLAGS),
+ GATE(PCLK_GPIO2, "pclk_gpio2", "pclk_cpu", 0, RK2928_CLKGATE_CON(8), 11, GFLAGS),
+ GATE(0, "pclk_efuse", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 2, GFLAGS),
+ GATE(0, "pclk_tzpc", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 3, GFLAGS),
+ GATE(0, "pclk_ddrupctl", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 7, GFLAGS),
+ GATE(0, "pclk_ddrpubl", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 6, GFLAGS),
+ GATE(0, "pclk_dbg", "pclk_cpu", 0, RK2928_CLKGATE_CON(9), 1, GFLAGS),
+ GATE(PCLK_GRF, "pclk_grf", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 4, GFLAGS),
+ GATE(PCLK_PMU, "pclk_pmu", "pclk_cpu", 0, RK2928_CLKGATE_CON(5), 5, GFLAGS),
+
+ /* aclk_peri */
+ GATE(ACLK_DMAC1, "aclk_dmac1", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 1, GFLAGS),
+ GATE(0, "aclk_smc", "aclk_peri", 0, RK2928_CLKGATE_CON(5), 8, GFLAGS),
+ GATE(ACLK_GPS, "aclk_gps", "aclk_peri", 0, RK2928_CLKGATE_CON(8), 13, GFLAGS),
+ GATE(0, "aclk_peri_niu", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 4, GFLAGS),
+ GATE(0, "aclk_cpu_peri", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 2, GFLAGS),
+ GATE(0, "aclk_peri_axi_matrix", "aclk_peri", 0, RK2928_CLKGATE_CON(4), 3, GFLAGS),
+
+ /* pclk_peri gates */
+ GATE(0, "pclk_peri_axi_matrix", "pclk_peri", 0, RK2928_CLKGATE_CON(4), 1, GFLAGS),
+ GATE(PCLK_PWM23, "pclk_pwm23", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 11, GFLAGS),
+ GATE(PCLK_WDT, "pclk_wdt", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 15, GFLAGS),
+ GATE(PCLK_SPI0, "pclk_spi0", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 12, GFLAGS),
+ GATE(PCLK_SPI1, "pclk_spi1", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 13, GFLAGS),
+ GATE(PCLK_UART2, "pclk_uart2", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 2, GFLAGS),
+ GATE(PCLK_UART3, "pclk_uart3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 3, GFLAGS),
+ GATE(PCLK_I2C2, "pclk_i2c2", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 6, GFLAGS),
+ GATE(PCLK_I2C3, "pclk_i2c3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 7, GFLAGS),
+ GATE(PCLK_I2C4, "pclk_i2c4", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 8, GFLAGS),
+ GATE(PCLK_GPIO3, "pclk_gpio3", "pclk_peri", 0, RK2928_CLKGATE_CON(8), 12, GFLAGS),
+ GATE(PCLK_SARADC, "pclk_saradc", "pclk_peri", 0, RK2928_CLKGATE_CON(7), 14, GFLAGS),
+
+ COMPOSITE_NODIV(0, "testclk", mux_testclk_p, 0,
+ RK2928_MISC_CON, 8, 3, MFLAGS,
+ RK2928_CLKGATE_CON(0), 15, GFLAGS),
+};
+
+static void __init rk3188a_clk_init(struct device_node *np)
+{
+ void __iomem *reg_base;
+ struct clk *clk;
+
+ reg_base = of_iomap(np, 0);
+ if (!reg_base) {
+ pr_err("%s: could not map cru region\n", __func__);
+ return;
+ }
+
+ rockchip_clk_init(np, reg_base, CLK_NR_CLKS);
+
+ /* xin12m is created by an cru-internal divider */
+ clk = clk_register_fixed_factor(NULL, "xin12m", "xin24m", 0, 1, 2);
+ if (IS_ERR(clk))
+ pr_warn("%s: could not register clock xin12m: %ld\n",
+ __func__, PTR_ERR(clk));
+
+ clk = clk_register_fixed_factor(NULL, "usb480m", "xin24m", 0, 20, 1);
+ if (IS_ERR(clk))
+ pr_warn("%s: could not register clock usb480m: %ld\n",
+ __func__, PTR_ERR(clk));
+
+ rockchip_clk_register_plls(rk3188_pll_clks,
+ ARRAY_SIZE(rk3188_pll_clks),
+ RK3188_GRF_SOC_STATUS);
+ rockchip_clk_register_branches(rk3188_clk_branches,
+ ARRAY_SIZE(rk3188_clk_branches));
+
+ rockchip_register_softrst(np, 9, reg_base + RK2928_SOFTRST_CON(0),
+ ROCKCHIP_SOFTRST_HIWORD_MASK);
+}
+CLK_OF_DECLARE(rk3188a_cru, "rockchip,rk3188a-cru", rk3188a_clk_init);
+
+static void __init rk3188_clk_init(struct device_node *np)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(rk3188_pll_clks); i++) {
+ struct rockchip_pll_clock *pll = &rk3188_pll_clks[i];
+ struct rockchip_pll_rate_table *rate;
+
+ if (!pll->rate_table)
+ continue;
+
+ rate = pll->rate_table;
+ while (rate->rate > 0) {
+ rate->bwadj = 0;
+ rate++;
+ }
+ }
+
+ rk3188a_clk_init(np);
+}
+CLK_OF_DECLARE(rk3188_cru, "rockchip,rk3188-cru", rk3188_clk_init);
diff --git a/include/dt-bindings/clock/rk3188-cru.h b/include/dt-bindings/clock/rk3188-cru.h
new file mode 100644
index 0000000..f045d13
--- /dev/null
+++ b/include/dt-bindings/clock/rk3188-cru.h
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2014 MundoReader S.L.
+ * Author: Heiko Stuebner <heiko@sntech.de>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ */
+
+/* core clocks from 1 */
+#define ARMCLK 1
+#define CORE_PERI 2
+#define CORE_L2C 3
+
+/* sclk gates (special clocks) from 64 */
+#define SCLK_UART0 64
+#define SCLK_UART1 65
+#define SCLK_UART2 66
+#define SCLK_UART3 67
+#define SCLK_MAC 68
+#define SCLK_SPI0 69
+#define SCLK_SPI1 70
+#define SCLK_SARADC 71
+#define SCLK_MMC0 72
+#define SCLK_MMC1 73
+#define SCLK_MMC2 74
+
+/* aclk gates from 192*/
+#define ACLK_DMAC0 192
+#define ACLK_DMAC1 193
+#define ACLK_GPS 194
+
+
+/* pclk gates from 320*/
+#define PCLK_GRF 320
+#define PCLK_PMU 321
+#define PCLK_TIMER0 322
+#define PCLK_TIMER2 323
+#define PCLK_PWM01 324
+#define PCLK_PWM23 325
+#define PCLK_SPI0 326
+#define PCLK_SPI1 327
+#define PCLK_SARADC 328
+#define PCLK_WDT 329
+#define PCLK_UART0 330
+#define PCLK_UART1 331
+#define PCLK_UART2 332
+#define PCLK_UART3 333
+#define PCLK_I2C0 334
+#define PCLK_I2C1 335
+#define PCLK_I2C2 336
+#define PCLK_I2C3 337
+#define PCLK_I2C4 338
+#define PCLK_GPIO0 339
+#define PCLK_GPIO1 340
+#define PCLK_GPIO2 341
+#define PCLK_GPIO3 342
+
+/* hclk gates from 448 */
+#define HCLK_MMC0 448
+#define HCLK_MMC1 449
+#define HCLK_MMC2 450
+#define HCLK_OTG0 451
+#define HCLK_EMAC 452
+#define HCLK_SPDIF 453
+#define HCLK_I2S 454
+#define HCLK_OTG1 455
+#define HCLK_HSIC 456
+#define HCLK_HSADC 457
+#define HCLK_PIDF 458
+
+#define CLK_NR_CLKS (HCLK_PIDF + 1)
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 08/11] ARM: rockchip: Select ARCH_HAS_RESET_CONTROLLER
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (6 preceding siblings ...)
2014-05-23 19:37 ` [PATCH v3 07/11] clk: rockchip: add clock driver for rk3188 clocks Heiko Stübner
@ 2014-05-23 19:37 ` Heiko Stübner
2014-05-23 19:38 ` [PATCH v3 09/11] ARM: dts: rk3188: add cru node and update device clocks to use it Heiko Stübner
` (2 subsequent siblings)
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:37 UTC (permalink / raw)
To: linux-arm-kernel
All known Rockchip SoCs have a reset controller in their CRUs, so it's
helpful to have the reset controller framework selected by default,
only be deselected by the user in special cases.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm/mach-rockchip/Kconfig | 1 +
1 file changed, 1 insertion(+)
diff --git a/arch/arm/mach-rockchip/Kconfig b/arch/arm/mach-rockchip/Kconfig
index 1caee6d..e4564c2 100644
--- a/arch/arm/mach-rockchip/Kconfig
+++ b/arch/arm/mach-rockchip/Kconfig
@@ -2,6 +2,7 @@ config ARCH_ROCKCHIP
bool "Rockchip RK2928 and RK3xxx SOCs" if ARCH_MULTI_V7
select PINCTRL
select PINCTRL_ROCKCHIP
+ select ARCH_HAS_RESET_CONTROLLER
select ARCH_REQUIRE_GPIOLIB
select ARM_GIC
select CACHE_L2X0
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 09/11] ARM: dts: rk3188: add cru node and update device clocks to use it
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (7 preceding siblings ...)
2014-05-23 19:37 ` [PATCH v3 08/11] ARM: rockchip: Select ARCH_HAS_RESET_CONTROLLER Heiko Stübner
@ 2014-05-23 19:38 ` Heiko Stübner
2014-05-23 19:38 ` [PATCH v3 10/11] ARM: dts: rockchip: move rk3188 core input clocks into main dtsi Heiko Stübner
2014-05-23 19:39 ` [PATCH v3 11/11] ARM: dts: rockchip: remove the now obsolete rk3188-clocks.dtsi Heiko Stübner
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:38 UTC (permalink / raw)
To: linux-arm-kernel
This adds a node for the clock and reset unit on rk3188 SoCs and updates
the device nodes retrieve their clocks from there, instead of the previous
gate clock nodes.
As the clocks diverge a bit until rk3066 can catch up, the shared nodes
between rk3066 and rk3188 get separated clocks-properties in the rk3188.dtsi.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm/boot/dts/rk3188.dtsi | 46 +++++++++++++++++++++++++++++++++++++++----
1 file changed, 42 insertions(+), 4 deletions(-)
diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
index a494fb0..3719436 100644
--- a/arch/arm/boot/dts/rk3188.dtsi
+++ b/arch/arm/boot/dts/rk3188.dtsi
@@ -15,6 +15,7 @@
#include <dt-bindings/gpio/gpio.h>
#include <dt-bindings/pinctrl/rockchip.h>
+#include <dt-bindings/clock/rk3188-cru.h>
#include "rk3xxx.dtsi"
#include "rk3188-clocks.dtsi"
@@ -54,10 +55,12 @@
soc {
global-timer at 1013c200 {
interrupts = <GIC_PPI 11 0xf04>;
+ clocks = <&cru CORE_PERI>;
};
local-timer at 1013c600 {
interrupts = <GIC_PPI 13 0xf04>;
+ clocks = <&cru CORE_PERI>;
};
sram: sram at 10080000 {
@@ -73,6 +76,41 @@
};
};
+ uart0: serial at 10124000 {
+ clocks = <&cru SCLK_UART0>;
+ };
+
+ uart1: serial at 10126000 {
+ clocks = <&cru SCLK_UART1>;
+ };
+
+ uart2: serial at 20064000 {
+ clocks = <&cru SCLK_UART2>;
+ };
+
+ uart3: serial at 20068000 {
+ clocks = <&cru SCLK_UART3>;
+ };
+
+ dwmmc at 10214000 {
+ clocks = <&cru HCLK_MMC0>, <&cru SCLK_MMC0>;
+ clock-names = "biu", "ciu";
+ };
+
+ dwmmc at 10218000 {
+ clocks = <&cru HCLK_MMC1>, <&cru SCLK_MMC1>;
+ clock-names = "biu", "ciu";
+ };
+
+ cru: cru at 20000000 {
+ compatible = "rockchip,rk3188-cru";
+ reg = <0x20000000 0x1000>;
+ rockchip,grf = <&grf>;
+
+ #clock-cells = <1>;
+ #reset-cells = <1>;
+ };
+
pinctrl at 20008000 {
compatible = "rockchip,rk3188-pinctrl";
rockchip,grf = <&grf>;
@@ -86,7 +124,7 @@
compatible = "rockchip,rk3188-gpio-bank0";
reg = <0x2000a000 0x100>;
interrupts = <GIC_SPI 54 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clk_gates8 9>;
+ clocks = <&cru PCLK_GPIO0>;
gpio-controller;
#gpio-cells = <2>;
@@ -99,7 +137,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x2003c000 0x100>;
interrupts = <GIC_SPI 55 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clk_gates8 10>;
+ clocks = <&cru PCLK_GPIO1>;
gpio-controller;
#gpio-cells = <2>;
@@ -112,7 +150,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x2003e000 0x100>;
interrupts = <GIC_SPI 56 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clk_gates8 11>;
+ clocks = <&cru PCLK_GPIO2>;
gpio-controller;
#gpio-cells = <2>;
@@ -125,7 +163,7 @@
compatible = "rockchip,gpio-bank";
reg = <0x20080000 0x100>;
interrupts = <GIC_SPI 57 IRQ_TYPE_LEVEL_HIGH>;
- clocks = <&clk_gates8 12>;
+ clocks = <&cru PCLK_GPIO3>;
gpio-controller;
#gpio-cells = <2>;
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 10/11] ARM: dts: rockchip: move rk3188 core input clocks into main dtsi
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (8 preceding siblings ...)
2014-05-23 19:38 ` [PATCH v3 09/11] ARM: dts: rk3188: add cru node and update device clocks to use it Heiko Stübner
@ 2014-05-23 19:38 ` Heiko Stübner
2014-05-23 19:39 ` [PATCH v3 11/11] ARM: dts: rockchip: remove the now obsolete rk3188-clocks.dtsi Heiko Stübner
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:38 UTC (permalink / raw)
To: linux-arm-kernel
The clock definitions get a lot shorter due to the soc clocks being handled by
rk3188-cru and only the input clocks remaining. These can not simply live
in the main rk3188.dtsi without affecting readability.
As we do now provide all necessary clocks, the dummy clock can go away too.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm/boot/dts/rk3188-clocks.dtsi | 18 ------------------
arch/arm/boot/dts/rk3188.dtsi | 12 ++++++++++++
2 files changed, 12 insertions(+), 18 deletions(-)
diff --git a/arch/arm/boot/dts/rk3188-clocks.dtsi b/arch/arm/boot/dts/rk3188-clocks.dtsi
index b1b92dc..8c1577a 100644
--- a/arch/arm/boot/dts/rk3188-clocks.dtsi
+++ b/arch/arm/boot/dts/rk3188-clocks.dtsi
@@ -19,24 +19,6 @@
#size-cells = <1>;
ranges;
- /*
- * This is a dummy clock, to be used as placeholder on
- * other mux clocks when a specific parent clock is not
- * yet implemented. It should be dropped when the driver
- * is complete.
- */
- dummy: dummy {
- compatible = "fixed-clock";
- clock-frequency = <0>;
- #clock-cells = <0>;
- };
-
- xin24m: xin24m {
- compatible = "fixed-clock";
- clock-frequency = <24000000>;
- #clock-cells = <0>;
- };
-
dummy48m: dummy48m {
compatible = "fixed-clock";
clock-frequency = <48000000>;
diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
index 3719436..3295c6b 100644
--- a/arch/arm/boot/dts/rk3188.dtsi
+++ b/arch/arm/boot/dts/rk3188.dtsi
@@ -52,6 +52,18 @@
};
};
+ clocks {
+ #address-cells = <1>;
+ #size-cells = <1>;
+ ranges;
+
+ xin24m: xin24m {
+ compatible = "fixed-clock";
+ clock-frequency = <24000000>;
+ #clock-cells = <0>;
+ };
+ };
+
soc {
global-timer at 1013c200 {
interrupts = <GIC_PPI 11 0xf04>;
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 11/11] ARM: dts: rockchip: remove the now obsolete rk3188-clocks.dtsi
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
` (9 preceding siblings ...)
2014-05-23 19:38 ` [PATCH v3 10/11] ARM: dts: rockchip: move rk3188 core input clocks into main dtsi Heiko Stübner
@ 2014-05-23 19:39 ` Heiko Stübner
10 siblings, 0 replies; 15+ messages in thread
From: Heiko Stübner @ 2014-05-23 19:39 UTC (permalink / raw)
To: linux-arm-kernel
The clocks.dtsi does not contain any meaningful data anymore and can thus
go away, as all clocks are now handled in the main dtsi.
Signed-off-by: Heiko Stuebner <heiko@sntech.de>
---
arch/arm/boot/dts/rk3188-clocks.dtsi | 271 -----------------------------------
arch/arm/boot/dts/rk3188.dtsi | 1 -
2 files changed, 272 deletions(-)
delete mode 100644 arch/arm/boot/dts/rk3188-clocks.dtsi
diff --git a/arch/arm/boot/dts/rk3188-clocks.dtsi b/arch/arm/boot/dts/rk3188-clocks.dtsi
deleted file mode 100644
index 8c1577a..0000000
--- a/arch/arm/boot/dts/rk3188-clocks.dtsi
+++ /dev/null
@@ -1,271 +0,0 @@
-/*
- * Copyright (c) 2013 MundoReader S.L.
- * Author: Heiko Stuebner <heiko@sntech.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- */
-
-/ {
- clocks {
- #address-cells = <1>;
- #size-cells = <1>;
- ranges;
-
- dummy48m: dummy48m {
- compatible = "fixed-clock";
- clock-frequency = <48000000>;
- #clock-cells = <0>;
- };
-
- dummy150m: dummy150m {
- compatible = "fixed-clock";
- clock-frequency = <150000000>;
- #clock-cells = <0>;
- };
-
- clk_gates0: gate-clk at 200000d0 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000d0 0x4>;
- clocks = <&dummy150m>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>;
-
- clock-output-names =
- "gate_core_periph", "gate_cpu_gpll",
- "gate_ddrphy", "gate_aclk_cpu",
- "gate_hclk_cpu", "gate_pclk_cpu",
- "gate_atclk_cpu", "gate_aclk_core",
- "reserved", "gate_i2s0",
- "gate_i2s0_frac", "reserved",
- "reserved", "gate_spdif",
- "gate_spdif_frac", "gate_testclk";
-
- #clock-cells = <1>;
- };
-
- clk_gates1: gate-clk at 200000d4 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000d4 0x4>;
- clocks = <&xin24m>, <&xin24m>,
- <&xin24m>, <&dummy>,
- <&dummy>, <&xin24m>,
- <&xin24m>, <&dummy>,
- <&xin24m>, <&dummy>,
- <&xin24m>, <&dummy>,
- <&xin24m>, <&dummy>,
- <&xin24m>, <&dummy>;
-
- clock-output-names =
- "gate_timer0", "gate_timer1",
- "gate_timer3", "gate_jtag",
- "gate_aclk_lcdc1_src", "gate_otgphy0",
- "gate_otgphy1", "gate_ddr_gpll",
- "gate_uart0", "gate_frac_uart0",
- "gate_uart1", "gate_frac_uart1",
- "gate_uart2", "gate_frac_uart2",
- "gate_uart3", "gate_frac_uart3";
-
- #clock-cells = <1>;
- };
-
- clk_gates2: gate-clk at 200000d8 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000d8 0x4>;
- clocks = <&clk_gates2 1>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&clk_gates2 3>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy48m>,
- <&dummy>, <&dummy48m>,
- <&dummy>, <&dummy>;
-
- clock-output-names =
- "gate_periph_src", "gate_aclk_periph",
- "gate_hclk_periph", "gate_pclk_periph",
- "gate_smc", "gate_mac",
- "gate_hsadc", "gate_hsadc_frac",
- "gate_saradc", "gate_spi0",
- "gate_spi1", "gate_mmc0",
- "gate_mac_lbtest", "gate_mmc1",
- "gate_emmc", "reserved";
-
- #clock-cells = <1>;
- };
-
- clk_gates3: gate-clk at 200000dc {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000dc 0x4>;
- clocks = <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&xin24m>, <&xin24m>,
- <&dummy>, <&dummy>,
- <&xin24m>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&xin24m>, <&dummy>;
-
- clock-output-names =
- "gate_aclk_lcdc0_src", "gate_dclk_lcdc0",
- "gate_dclk_lcdc1", "gate_pclkin_cif0",
- "gate_timer2", "gate_timer4",
- "gate_hsicphy", "gate_cif0_out",
- "gate_timer5", "gate_aclk_vepu",
- "gate_hclk_vepu", "gate_aclk_vdpu",
- "gate_hclk_vdpu", "reserved",
- "gate_timer6", "gate_aclk_gpu_src";
-
- #clock-cells = <1>;
- };
-
- clk_gates4: gate-clk at 200000e0 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000e0 0x4>;
- clocks = <&clk_gates2 2>, <&clk_gates2 3>,
- <&clk_gates2 1>, <&clk_gates2 1>,
- <&clk_gates2 1>, <&clk_gates2 2>,
- <&clk_gates2 2>, <&clk_gates2 2>,
- <&clk_gates0 4>, <&clk_gates0 4>,
- <&clk_gates0 3>, <&dummy>,
- <&clk_gates0 3>, <&dummy>,
- <&dummy>, <&dummy>;
-
- clock-output-names =
- "gate_hclk_peri_axi_matrix", "gate_pclk_peri_axi_matrix",
- "gate_aclk_cpu_peri", "gate_aclk_peri_axi_matrix",
- "gate_aclk_pei_niu", "gate_hclk_usb_peri",
- "gate_hclk_peri_ahb_arbi", "gate_hclk_emem_peri",
- "gate_hclk_cpubus", "gate_hclk_ahb2apb",
- "gate_aclk_strc_sys", "reserved",
- "gate_aclk_intmem", "reserved",
- "gate_hclk_imem1", "gate_hclk_imem0";
-
- #clock-cells = <1>;
- };
-
- clk_gates5: gate-clk at 200000e4 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000e4 0x4>;
- clocks = <&clk_gates0 3>, <&clk_gates2 1>,
- <&clk_gates0 5>, <&clk_gates0 5>,
- <&clk_gates0 5>, <&clk_gates0 5>,
- <&clk_gates0 4>, <&clk_gates0 5>,
- <&clk_gates2 1>, <&clk_gates2 2>,
- <&clk_gates2 2>, <&clk_gates2 2>,
- <&clk_gates2 2>, <&clk_gates4 5>;
-
- clock-output-names =
- "gate_aclk_dmac1", "gate_aclk_dmac2",
- "gate_pclk_efuse", "gate_pclk_tzpc",
- "gate_pclk_grf", "gate_pclk_pmu",
- "gate_hclk_rom", "gate_pclk_ddrupctl",
- "gate_aclk_smc", "gate_hclk_nandc",
- "gate_hclk_mmc0", "gate_hclk_mmc1",
- "gate_hclk_emmc", "gate_hclk_otg0";
-
- #clock-cells = <1>;
- };
-
- clk_gates6: gate-clk at 200000e8 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000e8 0x4>;
- clocks = <&clk_gates3 0>, <&clk_gates0 4>,
- <&clk_gates0 4>, <&clk_gates1 4>,
- <&clk_gates0 4>, <&clk_gates3 0>,
- <&dummy>, <&dummy>,
- <&clk_gates3 0>, <&clk_gates0 4>,
- <&clk_gates0 4>, <&clk_gates1 4>,
- <&clk_gates0 4>, <&clk_gates3 0>;
-
- clock-output-names =
- "gate_aclk_lcdc0", "gate_hclk_lcdc0",
- "gate_hclk_lcdc1", "gate_aclk_lcdc1",
- "gate_hclk_cif0", "gate_aclk_cif0",
- "reserved", "reserved",
- "gate_aclk_ipp", "gate_hclk_ipp",
- "gate_hclk_rga", "gate_aclk_rga",
- "gate_hclk_vio_bus", "gate_aclk_vio0";
-
- #clock-cells = <1>;
- };
-
- clk_gates7: gate-clk at 200000ec {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000ec 0x4>;
- clocks = <&clk_gates2 2>, <&clk_gates0 4>,
- <&clk_gates0 4>, <&dummy>,
- <&dummy>, <&clk_gates2 2>,
- <&clk_gates2 2>, <&clk_gates0 5>,
- <&dummy>, <&clk_gates0 5>,
- <&clk_gates0 5>, <&clk_gates2 3>,
- <&clk_gates2 3>, <&clk_gates2 3>,
- <&clk_gates2 3>, <&clk_gates2 3>;
-
- clock-output-names =
- "gate_hclk_emac", "gate_hclk_spdif",
- "gate_hclk_i2s0_2ch", "gate_hclk_otg1",
- "gate_hclk_hsic", "gate_hclk_hsadc",
- "gate_hclk_pidf", "gate_pclk_timer0",
- "reserved", "gate_pclk_timer2",
- "gate_pclk_pwm01", "gate_pclk_pwm23",
- "gate_pclk_spi0", "gate_pclk_spi1",
- "gate_pclk_saradc", "gate_pclk_wdt";
-
- #clock-cells = <1>;
- };
-
- clk_gates8: gate-clk at 200000f0 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000f0 0x4>;
- clocks = <&clk_gates0 5>, <&clk_gates0 5>,
- <&clk_gates2 3>, <&clk_gates2 3>,
- <&clk_gates0 5>, <&clk_gates0 5>,
- <&clk_gates2 3>, <&clk_gates2 3>,
- <&clk_gates2 3>, <&clk_gates0 5>,
- <&clk_gates0 5>, <&clk_gates0 5>,
- <&clk_gates2 3>, <&dummy>;
-
- clock-output-names =
- "gate_pclk_uart0", "gate_pclk_uart1",
- "gate_pclk_uart2", "gate_pclk_uart3",
- "gate_pclk_i2c0", "gate_pclk_i2c1",
- "gate_pclk_i2c2", "gate_pclk_i2c3",
- "gate_pclk_i2c4", "gate_pclk_gpio0",
- "gate_pclk_gpio1", "gate_pclk_gpio2",
- "gate_pclk_gpio3", "gate_aclk_gps";
-
- #clock-cells = <1>;
- };
-
- clk_gates9: gate-clk at 200000f4 {
- compatible = "rockchip,rk2928-gate-clk";
- reg = <0x200000f4 0x4>;
- clocks = <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>,
- <&dummy>, <&dummy>;
-
- clock-output-names =
- "gate_clk_core_dbg", "gate_pclk_dbg",
- "gate_clk_trace", "gate_atclk",
- "gate_clk_l2c", "gate_aclk_vio1",
- "gate_pclk_publ", "gate_aclk_gpu";
-
- #clock-cells = <1>;
- };
- };
-
-};
diff --git a/arch/arm/boot/dts/rk3188.dtsi b/arch/arm/boot/dts/rk3188.dtsi
index 3295c6b..835d600 100644
--- a/arch/arm/boot/dts/rk3188.dtsi
+++ b/arch/arm/boot/dts/rk3188.dtsi
@@ -17,7 +17,6 @@
#include <dt-bindings/pinctrl/rockchip.h>
#include <dt-bindings/clock/rk3188-cru.h>
#include "rk3xxx.dtsi"
-#include "rk3188-clocks.dtsi"
/ {
compatible = "rockchip,rk3188";
--
1.9.0
^ permalink raw reply related [flat|nested] 15+ messages in thread
* [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent
2014-05-23 19:33 ` [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent Heiko Stübner
@ 2014-05-26 9:24 ` Boris BREZILLON
2014-05-26 10:27 ` Heiko Stübner
0 siblings, 1 reply; 15+ messages in thread
From: Boris BREZILLON @ 2014-05-26 9:24 UTC (permalink / raw)
To: linux-arm-kernel
Hello Heiko,
On 23/05/2014 21:33, Heiko St?bner wrote:
> From: Boris BREZILLON <b.brezillon@overkiz.com>
>
> In case the rate_hw does not implement determine_rate, but only round_rate
> we fallback to best_parent selection if mux_hw is present and support
> reparenting.
>
> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
>
> This also fixes a rate calculation problem when using the standard div and
> mux ops, as in this case currently only the mux->determine_rate is used
> in the composite rate calculation.
> [fixed the output to actually return a rate instead of the diff]
Sorry for the delay and thanks for fixing this.
Anyway, when I first proposed this patch, Emilio (added in Cc) told me
this should not be automatically done by the composite clk driver, and
the driver should instead provide the determine_rate function.
Mike, any opinion on this patch ?
Best Regards,
Boris
> Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> ---
> drivers/clk/clk-composite.c | 51 ++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 50 insertions(+), 1 deletion(-)
>
> diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
> index 57a078e..0a4cd21 100644
> --- a/drivers/clk/clk-composite.c
> +++ b/drivers/clk/clk-composite.c
> @@ -64,11 +64,59 @@ static long clk_composite_determine_rate(struct clk_hw *hw, unsigned long rate,
> const struct clk_ops *mux_ops = composite->mux_ops;
> struct clk_hw *rate_hw = composite->rate_hw;
> struct clk_hw *mux_hw = composite->mux_hw;
> + struct clk *parent;
> + unsigned long parent_rate;
> + long tmp_rate, best_rate;
> + unsigned long rate_diff;
> + unsigned long best_rate_diff = ULONG_MAX;
> + int i;
>
> if (rate_hw && rate_ops && rate_ops->determine_rate) {
> rate_hw->clk = hw->clk;
> return rate_ops->determine_rate(rate_hw, rate, best_parent_rate,
> best_parent_p);
> + } else if (rate_hw && rate_ops && rate_ops->round_rate &&
> + mux_hw && mux_ops && mux_ops->set_parent) {
> + *best_parent_p = NULL;
> +
> + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_NO_REPARENT) {
> + *best_parent_p = clk_get_parent(mux_hw->clk);
> + *best_parent_rate = __clk_get_rate(*best_parent_p);
> +
> + return rate_ops->round_rate(rate_hw, rate,
> + best_parent_rate);
> + }
> +
> + for (i = 0; i < __clk_get_num_parents(mux_hw->clk); i++) {
> + parent = clk_get_parent_by_index(mux_hw->clk, i);
> + if (!parent)
> + continue;
> +
> + parent_rate = __clk_get_rate(parent);
> +
> + tmp_rate = rate_ops->round_rate(rate_hw, rate,
> + &parent_rate);
> + if (tmp_rate < 0)
> + continue;
> +
> + if (tmp_rate < rate)
> + rate_diff = rate - tmp_rate;
> + else
> + rate_diff = tmp_rate - rate;
> +
> + if (!rate_diff || !*best_parent_p
> + || best_rate_diff > rate_diff) {
> + *best_parent_p = parent;
> + *best_parent_rate = parent_rate;
> + best_rate_diff = rate_diff;
> + best_rate = tmp_rate;
> + }
> +
> + if (!rate_diff)
> + return rate;
> + }
> +
> + return best_rate;
> } else if (mux_hw && mux_ops && mux_ops->determine_rate) {
> mux_hw->clk = hw->clk;
> return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
> @@ -196,7 +244,8 @@ struct clk *clk_register_composite(struct device *dev, const char *name,
> composite->rate_hw = rate_hw;
> composite->rate_ops = rate_ops;
> clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
> - if (rate_ops->determine_rate)
> + if (rate_ops->determine_rate ||
> + (rate_ops->round_rate && clk_composite_ops->set_parent))
> clk_composite_ops->determine_rate = clk_composite_determine_rate;
> }
>
--
Boris Brezillon, Free Electrons
Embedded Linux and Kernel engineering
http://free-electrons.com
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent
2014-05-26 9:24 ` Boris BREZILLON
@ 2014-05-26 10:27 ` Heiko Stübner
2014-05-28 11:07 ` Gabriel Fernandez
0 siblings, 1 reply; 15+ messages in thread
From: Heiko Stübner @ 2014-05-26 10:27 UTC (permalink / raw)
To: linux-arm-kernel
Am Montag, 26. Mai 2014, 11:24:44 schrieb Boris BREZILLON:
> Hello Heiko,
>
> On 23/05/2014 21:33, Heiko St?bner wrote:
> > From: Boris BREZILLON <b.brezillon@overkiz.com>
> >
> > In case the rate_hw does not implement determine_rate, but only round_rate
> > we fallback to best_parent selection if mux_hw is present and support
> > reparenting.
> >
> > Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
> >
> > This also fixes a rate calculation problem when using the standard div and
> > mux ops, as in this case currently only the mux->determine_rate is used
> > in the composite rate calculation.
> > [fixed the output to actually return a rate instead of the diff]
>
> Sorry for the delay and thanks for fixing this.
>
> Anyway, when I first proposed this patch, Emilio (added in Cc) told me
> this should not be automatically done by the composite clk driver, and
> the driver should instead provide the determine_rate function.
Just to point out, as I'm not sure if it comes across correctly from my
description above, it looks like the composite behaviour is currently broken
when using the standard mux and div ops for it.
As only mux_ops provides a determine rate callback, it will always only run
mux_ops->determine_rate
thus returning the parent rate as the target rate.
So when the parents are 600 and 891 MHz and you want 75MHz [which the divider
can provide], the clock would always be set to 600MHz, which may be way out of
spec - as seen on my rk3188 clock tree.
When looking at the other composite users, only st/clkgen-mux.c uses the same
pattern with both the standard mux and div operations. I've added Gabriel,
maybe he can check what happens on his arch, when an affected clock is changed.
> Mike, any opinion on this patch ?
>
> Best Regards,
>
> Boris
>
> > Signed-off-by: Heiko Stuebner <heiko@sntech.de>
> > ---
> >
> > drivers/clk/clk-composite.c | 51
> > ++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 50
> > insertions(+), 1 deletion(-)
> >
> > diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c
> > index 57a078e..0a4cd21 100644
> > --- a/drivers/clk/clk-composite.c
> > +++ b/drivers/clk/clk-composite.c
> > @@ -64,11 +64,59 @@ static long clk_composite_determine_rate(struct clk_hw
> > *hw, unsigned long rate,>
> > const struct clk_ops *mux_ops = composite->mux_ops;
> > struct clk_hw *rate_hw = composite->rate_hw;
> > struct clk_hw *mux_hw = composite->mux_hw;
> >
> > + struct clk *parent;
> > + unsigned long parent_rate;
> > + long tmp_rate, best_rate;
> > + unsigned long rate_diff;
> > + unsigned long best_rate_diff = ULONG_MAX;
> > + int i;
> >
> > if (rate_hw && rate_ops && rate_ops->determine_rate) {
> >
> > rate_hw->clk = hw->clk;
> > return rate_ops->determine_rate(rate_hw, rate,
best_parent_rate,
> >
> > best_parent_p);
> >
> > + } else if (rate_hw && rate_ops && rate_ops->round_rate &&
> > + mux_hw && mux_ops && mux_ops->set_parent) {
> > + *best_parent_p = NULL;
> > +
> > + if (__clk_get_flags(hw->clk) & CLK_SET_RATE_NO_REPARENT) {
> > + *best_parent_p = clk_get_parent(mux_hw->clk);
> > + *best_parent_rate = __clk_get_rate(*best_parent_p);
> > +
> > + return rate_ops->round_rate(rate_hw, rate,
> > + best_parent_rate);
> > + }
> > +
> > + for (i = 0; i < __clk_get_num_parents(mux_hw->clk); i++) {
> > + parent = clk_get_parent_by_index(mux_hw->clk, i);
> > + if (!parent)
> > + continue;
> > +
> > + parent_rate = __clk_get_rate(parent);
> > +
> > + tmp_rate = rate_ops->round_rate(rate_hw, rate,
> > + &parent_rate);
> > + if (tmp_rate < 0)
> > + continue;
> > +
> > + if (tmp_rate < rate)
> > + rate_diff = rate - tmp_rate;
> > + else
> > + rate_diff = tmp_rate - rate;
> > +
> > + if (!rate_diff || !*best_parent_p
> > + || best_rate_diff > rate_diff) {
> > + *best_parent_p = parent;
> > + *best_parent_rate = parent_rate;
> > + best_rate_diff = rate_diff;
> > + best_rate = tmp_rate;
> > + }
> > +
> > + if (!rate_diff)
> > + return rate;
> > + }
> > +
> > + return best_rate;
> >
> > } else if (mux_hw && mux_ops && mux_ops->determine_rate) {
> >
> > mux_hw->clk = hw->clk;
> > return mux_ops->determine_rate(mux_hw, rate, best_parent_rate,
> >
> > @@ -196,7 +244,8 @@ struct clk *clk_register_composite(struct device *dev,
> > const char *name,>
> > composite->rate_hw = rate_hw;
> > composite->rate_ops = rate_ops;
> > clk_composite_ops->recalc_rate = clk_composite_recalc_rate;
> >
> > - if (rate_ops->determine_rate)
> > + if (rate_ops->determine_rate ||
> > + (rate_ops->round_rate && clk_composite_ops->set_parent))
> >
> > clk_composite_ops->determine_rate =
clk_composite_determine_rate;
> >
> > }
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent
2014-05-26 10:27 ` Heiko Stübner
@ 2014-05-28 11:07 ` Gabriel Fernandez
0 siblings, 0 replies; 15+ messages in thread
From: Gabriel Fernandez @ 2014-05-28 11:07 UTC (permalink / raw)
To: linux-arm-kernel
Hi Heiko,
On 05/26/2014 12:27 PM, Heiko St?bner wrote:
> Am Montag, 26. Mai 2014, 11:24:44 schrieb Boris BREZILLON:
>> Hello Heiko,
>>
>> On 23/05/2014 21:33, Heiko St?bner wrote:
>>> From: Boris BREZILLON <b.brezillon@overkiz.com>
>>>
>>> In case the rate_hw does not implement determine_rate, but only round_rate
>>> we fallback to best_parent selection if mux_hw is present and support
>>> reparenting.
>>>
>>> Signed-off-by: Boris BREZILLON <b.brezillon@overkiz.com>
>>>
>>> This also fixes a rate calculation problem when using the standard div and
>>> mux ops, as in this case currently only the mux->determine_rate is used
>>> in the composite rate calculation.
>>> [fixed the output to actually return a rate instead of the diff]
>> Sorry for the delay and thanks for fixing this.
>>
>> Anyway, when I first proposed this patch, Emilio (added in Cc) told me
>> this should not be automatically done by the composite clk driver, and
>> the driver should instead provide the determine_rate function.
> Just to point out, as I'm not sure if it comes across correctly from my
> description above, it looks like the composite behaviour is currently broken
> when using the standard mux and div ops for it.
>
> As only mux_ops provides a determine rate callback, it will always only run
> mux_ops->determine_rate
> thus returning the parent rate as the target rate.
YesI saw this regression for me later, i had planned to propose a correction
this patch is therefore highly welcome.
Thanks !
> So when the parents are 600 and 891 MHz and you want 75MHz [which the divider
> can provide], the clock would always be set to 600MHz, which may be way out of
> spec - as seen on my rk3188 clock tree.
>
>
> When looking at the other composite users, only st/clkgen-mux.c uses the same
> pattern with both the standard mux and div operations. I've added Gabriel,
> maybe he can check what happens on his arch, when an affected clock is changed.
I tested this use case with the patch, the good parent is selected (600
Mhz) and make the
division to have 75 Mhz.
But if CLK_SET_RATE_PARENT is set, it will take the 981 Mhz clock and
recalculate the parent rate
(because the diff rate is equal or a little better than the 600 Mhz clock)
Which is a shame, all child clocks are affected.
It was the best choice to choose other parent
and do just a division (not recalculate the parent rate)
Best Regards
Gabriel.
>
>> Mike, any opinion on this patch ?
>>
>> Best Regards,
>>
>> Boris
>>
^ permalink raw reply [flat|nested] 15+ messages in thread
end of thread, other threads:[~2014-05-28 11:07 UTC | newest]
Thread overview: 15+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2014-05-23 19:32 [PATCH v3 00/11] Add real clock support for Rockchip's RK3188 Heiko Stübner
2014-05-23 19:33 ` [PATCH v3 01/11] clk: composite: support determine_rate using rate_ops->round_rate + mux_ops->set_parent Heiko Stübner
2014-05-26 9:24 ` Boris BREZILLON
2014-05-26 10:27 ` Heiko Stübner
2014-05-28 11:07 ` Gabriel Fernandez
2014-05-23 19:34 ` [PATCH v3 02/11] clk: composite: allow read-only clocks Heiko Stübner
2014-05-23 19:34 ` [PATCH v3 03/11] clk: rockchip: add basic infrastructure for clock branches Heiko Stübner
2014-05-23 19:35 ` [PATCH v3 04/11] clk: rockchip: add clock type for pll clocks and pll used on rk3066 Heiko Stübner
2014-05-23 19:36 ` [PATCH v3 05/11] clk: rockchip: add reset controller Heiko Stübner
2014-05-23 19:36 ` [PATCH v3 06/11] dt-bindings: add documentation for rk3188 clock and reset unit Heiko Stübner
2014-05-23 19:37 ` [PATCH v3 07/11] clk: rockchip: add clock driver for rk3188 clocks Heiko Stübner
2014-05-23 19:37 ` [PATCH v3 08/11] ARM: rockchip: Select ARCH_HAS_RESET_CONTROLLER Heiko Stübner
2014-05-23 19:38 ` [PATCH v3 09/11] ARM: dts: rk3188: add cru node and update device clocks to use it Heiko Stübner
2014-05-23 19:38 ` [PATCH v3 10/11] ARM: dts: rockchip: move rk3188 core input clocks into main dtsi Heiko Stübner
2014-05-23 19:39 ` [PATCH v3 11/11] ARM: dts: rockchip: remove the now obsolete rk3188-clocks.dtsi Heiko Stübner
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).