From 30ee400614385ac49f4c9b4bc03d77ff8f07a61e Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Mon, 11 Feb 2013 12:07:16 -0200 Subject: clk: mxs: Fix sparse warnings Fix the following sparse warnings: drivers/clk/mxs/clk.c:17:1: warning: symbol 'mxs_lock' was not declared. Should it be static? drivers/clk/mxs/clk.c:19:5: warning: symbol 'mxs_clk_wait' was not declared. Should it be static? Signed-off-by: Fabio Estevam Acked-by: Shawn Guo Signed-off-by: Mike Turquette --- drivers/clk/mxs/clk.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/mxs/clk.c b/drivers/clk/mxs/clk.c index b24d56067c8..5301bce8957 100644 --- a/drivers/clk/mxs/clk.c +++ b/drivers/clk/mxs/clk.c @@ -13,6 +13,7 @@ #include #include #include +#include "clk.h" DEFINE_SPINLOCK(mxs_lock); -- cgit v1.2.3 From 3d6ee287a3e341c88eafd0b4620b12d640b3736b Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:02 +0100 Subject: clk: Introduce optional is_prepared callback To reflect whether a clk_hw is prepared the clk_hw may implement the optional is_prepared callback. If not implemented we fall back to use the software prepare counter. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 21 +++++++++++++++++++++ include/linux/clk-provider.h | 6 ++++++ 2 files changed, 27 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index ed87b240580..7571b5054f3 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -451,6 +451,27 @@ unsigned long __clk_get_flags(struct clk *clk) return !clk ? 0 : clk->flags; } +bool __clk_is_prepared(struct clk *clk) +{ + int ret; + + if (!clk) + return false; + + /* + * .is_prepared is optional for clocks that can prepare + * fall back to software usage counter if it is missing + */ + if (!clk->ops->is_prepared) { + ret = clk->prepare_count ? 1 : 0; + goto out; + } + + ret = clk->ops->is_prepared(clk->hw); +out: + return !!ret; +} + bool __clk_is_enabled(struct clk *clk) { int ret; diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 7f197d7addb..ee946862e05 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -45,6 +45,10 @@ struct clk_hw; * undo any work done in the @prepare callback. Called with * prepare_lock held. * + * @is_prepared: Queries the hardware to determine if the clock is prepared. + * This function is allowed to sleep. Optional, if this op is not + * set then the prepare count will be used. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -108,6 +112,7 @@ struct clk_hw; struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); + int (*is_prepared)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); @@ -351,6 +356,7 @@ unsigned int __clk_get_enable_count(struct clk *clk); unsigned int __clk_get_prepare_count(struct clk *clk); unsigned long __clk_get_rate(struct clk *clk); unsigned long __clk_get_flags(struct clk *clk); +bool __clk_is_prepared(struct clk *clk); bool __clk_is_enabled(struct clk *clk); struct clk *__clk_lookup(const char *name); -- cgit v1.2.3 From 1c155b3dfe08351f5fc811062648969f1ba7af53 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:03 +0100 Subject: clk: Unprepare the unused prepared slow clocks at late init The unused ungated fast clocks are already being disabled from clk_disable_unused at late init. This patch extend this sequence to the slow unused prepared clocks to be unprepared. Unless the optional .is_prepared callback is implemented by a clk_hw the clk_disable_unused sequence will not unprepare any unused clocks, since it will fall back to use the software prepare counter. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette [mturquette@linaro.org: fixed hlist accessors per b67bfe0d] --- drivers/clk/clk.c | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 7571b5054f3..c0141f3e110 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -335,6 +335,28 @@ late_initcall(clk_debug_init); static inline int clk_debug_register(struct clk *clk) { return 0; } #endif +/* caller must hold prepare_lock */ +static void clk_unprepare_unused_subtree(struct clk *clk) +{ + struct clk *child; + + if (!clk) + return; + + hlist_for_each_entry(child, &clk->children, child_node) + clk_unprepare_unused_subtree(child); + + if (clk->prepare_count) + return; + + if (clk->flags & CLK_IGNORE_UNUSED) + return; + + if (__clk_is_prepared(clk)) + if (clk->ops->unprepare) + clk->ops->unprepare(clk->hw); +} + /* caller must hold prepare_lock */ static void clk_disable_unused_subtree(struct clk *clk) { @@ -386,6 +408,12 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_disable_unused_subtree(clk); + hlist_for_each_entry(clk, &clk_root_list, child_node) + clk_unprepare_unused_subtree(clk); + + hlist_for_each_entry(clk, &clk_orphan_list, child_node) + clk_unprepare_unused_subtree(clk); + mutex_unlock(&prepare_lock); return 0; -- cgit v1.2.3 From 3cc8247f1dce79511de8bf0f69ab02a46cc315b7 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:04 +0100 Subject: clk: Introduce optional unprepare_unused callback An unprepare_unused callback is introduced due to the same reasons to why the disable_unused callback was added. During the clk_disable_unused sequence, those clk_hw that needs specific treatment with regards to being unprepared, shall implement the unprepare_unused callback. Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 7 +++++-- include/linux/clk-provider.h | 5 +++++ 2 files changed, 10 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index c0141f3e110..253792a46c0 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -352,9 +352,12 @@ static void clk_unprepare_unused_subtree(struct clk *clk) if (clk->flags & CLK_IGNORE_UNUSED) return; - if (__clk_is_prepared(clk)) - if (clk->ops->unprepare) + if (__clk_is_prepared(clk)) { + if (clk->ops->unprepare_unused) + clk->ops->unprepare_unused(clk->hw); + else if (clk->ops->unprepare) clk->ops->unprepare(clk->hw); + } } /* caller must hold prepare_lock */ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index ee946862e05..56e6cc12c79 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -49,6 +49,10 @@ struct clk_hw; * This function is allowed to sleep. Optional, if this op is not * set then the prepare count will be used. * + * @unprepare_unused: Unprepare the clock atomically. Only called from + * clk_disable_unused for prepare clocks with special needs. + * Called with prepare mutex held. This function may sleep. + * * @enable: Enable the clock atomically. This must not return until the * clock is generating a valid clock signal, usable by consumer * devices. Called with enable_lock held. This function must not @@ -113,6 +117,7 @@ struct clk_ops { int (*prepare)(struct clk_hw *hw); void (*unprepare)(struct clk_hw *hw); int (*is_prepared)(struct clk_hw *hw); + void (*unprepare_unused)(struct clk_hw *hw); int (*enable)(struct clk_hw *hw); void (*disable)(struct clk_hw *hw); int (*is_enabled)(struct clk_hw *hw); -- cgit v1.2.3 From 2850985f7749abca7fc374a438bc0126ca28c9c4 Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Tue, 12 Mar 2013 20:26:05 +0100 Subject: clk: ux500: Support is_prepared callback for clk-prcmu To be able to gate unused prcmu clocks from the clk_disable_unused sequence, clk-prcmu now implements the is_prepared callback. Signed-off-by: Ulf Hansson Signed-off-by: Mike Turquette --- drivers/clk/ux500/clk-prcmu.c | 134 +++++++++++++++++++++++++----------------- 1 file changed, 80 insertions(+), 54 deletions(-) diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 74faa7e3cf5..9d832567c6b 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -20,15 +20,23 @@ struct clk_prcmu { struct clk_hw hw; u8 cg_sel; + int is_prepared; int is_enabled; + int opp_requested; }; /* PRCMU clock operations. */ static int clk_prcmu_prepare(struct clk_hw *hw) { + int ret; struct clk_prcmu *clk = to_clk_prcmu(hw); - return prcmu_request_clock(clk->cg_sel, true); + + ret = prcmu_request_clock(clk->cg_sel, true); + if (!ret) + clk->is_prepared = 1; + + return ret;; } static void clk_prcmu_unprepare(struct clk_hw *hw) @@ -37,6 +45,14 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, hw->init->name); + else + clk->is_prepared = 0; +} + +static int clk_prcmu_is_prepared(struct clk_hw *hw) +{ + struct clk_prcmu *clk = to_clk_prcmu(hw); + return clk->is_prepared; } static int clk_prcmu_enable(struct clk_hw *hw) @@ -79,58 +95,52 @@ static int clk_prcmu_set_rate(struct clk_hw *hw, unsigned long rate, return prcmu_set_clock_rate(clk->cg_sel, rate); } -static int request_ape_opp100(bool enable) -{ - static int reqs; - int err = 0; - - if (enable) { - if (!reqs) - err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, - "clock", 100); - if (!err) - reqs++; - } else { - reqs--; - if (!reqs) - prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, - "clock"); - } - return err; -} - static int clk_prcmu_opp_prepare(struct clk_hw *hw) { int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = request_ape_opp100(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP100 for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_qos_add_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk), + 100); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", + __func__, hw->init->name); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) - request_ape_opp100(false); + if (err) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (request_ape_opp100(false)) - goto out_error; - return; - -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); + return; + } + + if (clk->opp_requested) { + prcmu_qos_remove_requirement(PRCMU_QOS_APE_OPP, + (char *)__clk_get_name(hw->clk)); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) @@ -138,38 +148,49 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) int err; struct clk_prcmu *clk = to_clk_prcmu(hw); - err = prcmu_request_ape_opp_100_voltage(true); - if (err) { - pr_err("clk_prcmu: %s failed to request APE OPP VOLT for %s.\n", - __func__, hw->init->name); - return err; + if (!clk->opp_requested) { + err = prcmu_request_ape_opp_100_voltage(true); + if (err) { + pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", + __func__, hw->init->name); + return err; + } + clk->opp_requested = 1; } err = prcmu_request_clock(clk->cg_sel, true); - if (err) + if (err) { prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + return err; + } - return err; + clk->is_prepared = 1; + return 0; } static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) { struct clk_prcmu *clk = to_clk_prcmu(hw); - if (prcmu_request_clock(clk->cg_sel, false)) - goto out_error; - if (prcmu_request_ape_opp_100_voltage(false)) - goto out_error; - return; - -out_error: - pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + if (prcmu_request_clock(clk->cg_sel, false)) { + pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, + hw->init->name); + return; + } + + if (clk->opp_requested) { + prcmu_request_ape_opp_100_voltage(false); + clk->opp_requested = 0; + } + + clk->is_prepared = 0; } static struct clk_ops clk_prcmu_scalable_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -181,6 +202,7 @@ static struct clk_ops clk_prcmu_scalable_ops = { static struct clk_ops clk_prcmu_gate_ops = { .prepare = clk_prcmu_prepare, .unprepare = clk_prcmu_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -202,6 +224,7 @@ static struct clk_ops clk_prcmu_rate_ops = { static struct clk_ops clk_prcmu_opp_gate_ops = { .prepare = clk_prcmu_opp_prepare, .unprepare = clk_prcmu_opp_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -211,6 +234,7 @@ static struct clk_ops clk_prcmu_opp_gate_ops = { static struct clk_ops clk_prcmu_opp_volt_scalable_ops = { .prepare = clk_prcmu_opp_volt_prepare, .unprepare = clk_prcmu_opp_volt_unprepare, + .is_prepared = clk_prcmu_is_prepared, .enable = clk_prcmu_enable, .disable = clk_prcmu_disable, .is_enabled = clk_prcmu_is_enabled, @@ -242,7 +266,9 @@ static struct clk *clk_reg_prcmu(const char *name, } clk->cg_sel = cg_sel; + clk->is_prepared = 1; clk->is_enabled = 1; + clk->opp_requested = 0; /* "rate" can be used for changing the initial frequency */ if (rate) prcmu_set_clock_rate(cg_sel, rate); -- cgit v1.2.3 From 0e646c52cf0ee186ec50b41c4db8cf81500c8dd1 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Mon, 11 Mar 2013 16:22:29 +0100 Subject: clk: Add axi-clkgen driver This driver adds support for the AXI clkgen pcore to the common clock framework. The AXI clkgen pcore is a AXI front-end to the MMCM_ADV frequency synthesizer commonly found in Xilinx FPGAs. The AXI clkgen pcore is used in Analog Devices' reference designs targeting Xilinx FPGAs. Signed-off-by: Lars-Peter Clausen Signed-off-by: Mike Turquette --- .../devicetree/bindings/clock/axi-clkgen.txt | 22 ++ drivers/clk/Kconfig | 8 + drivers/clk/Makefile | 1 + drivers/clk/clk-axi-clkgen.c | 331 +++++++++++++++++++++ 4 files changed, 362 insertions(+) create mode 100644 Documentation/devicetree/bindings/clock/axi-clkgen.txt create mode 100644 drivers/clk/clk-axi-clkgen.c diff --git a/Documentation/devicetree/bindings/clock/axi-clkgen.txt b/Documentation/devicetree/bindings/clock/axi-clkgen.txt new file mode 100644 index 00000000000..028b493e97f --- /dev/null +++ b/Documentation/devicetree/bindings/clock/axi-clkgen.txt @@ -0,0 +1,22 @@ +Binding for the axi-clkgen clock generator + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be "adi,axi-clkgen". +- #clock-cells : from common clock binding; Should always be set to 0. +- reg : Address and length of the axi-clkgen register set. +- clocks : Phandle and clock specifier for the parent clock. + +Optional properties: +- clock-output-names : From common clock binding. + +Example: + clock@0xff000000 { + compatible = "adi,axi-clkgen"; + #clock-cells = <0>; + reg = <0xff000000 0x1000>; + clocks = <&osc 1>; + }; diff --git a/drivers/clk/Kconfig b/drivers/clk/Kconfig index a47e6ee98b8..a64caefdba1 100644 --- a/drivers/clk/Kconfig +++ b/drivers/clk/Kconfig @@ -63,6 +63,14 @@ config CLK_TWL6040 McPDM. McPDM module is using the external bit clock on the McPDM bus as functional clock. +config COMMON_CLK_AXI_CLKGEN + tristate "AXI clkgen driver" + depends on ARCH_ZYNQ || MICROBLAZE + help + ---help--- + Support for the Analog Devices axi-clkgen pcore clock generator for Xilinx + FPGAs. It is commonly used in Analog Devices' reference designs. + endmenu source "drivers/clk/mvebu/Kconfig" diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 300d4775d92..1c22f9dc721 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -31,6 +31,7 @@ obj-$(CONFIG_ARCH_TEGRA) += tegra/ obj-$(CONFIG_X86) += x86/ # Chip specific +obj-$(CONFIG_COMMON_CLK_AXI_CLKGEN) += clk-axi-clkgen.o obj-$(CONFIG_COMMON_CLK_WM831X) += clk-wm831x.o obj-$(CONFIG_COMMON_CLK_MAX77686) += clk-max77686.o obj-$(CONFIG_CLK_TWL6040) += clk-twl6040.o diff --git a/drivers/clk/clk-axi-clkgen.c b/drivers/clk/clk-axi-clkgen.c new file mode 100644 index 00000000000..8137327847c --- /dev/null +++ b/drivers/clk/clk-axi-clkgen.c @@ -0,0 +1,331 @@ +/* + * AXI clkgen driver + * + * Copyright 2012-2013 Analog Devices Inc. + * Author: Lars-Peter Clausen + * + * Licensed under the GPL-2. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXI_CLKGEN_REG_UPDATE_ENABLE 0x04 +#define AXI_CLKGEN_REG_CLK_OUT1 0x08 +#define AXI_CLKGEN_REG_CLK_OUT2 0x0c +#define AXI_CLKGEN_REG_CLK_DIV 0x10 +#define AXI_CLKGEN_REG_CLK_FB1 0x14 +#define AXI_CLKGEN_REG_CLK_FB2 0x18 +#define AXI_CLKGEN_REG_LOCK1 0x1c +#define AXI_CLKGEN_REG_LOCK2 0x20 +#define AXI_CLKGEN_REG_LOCK3 0x24 +#define AXI_CLKGEN_REG_FILTER1 0x28 +#define AXI_CLKGEN_REG_FILTER2 0x2c + +struct axi_clkgen { + void __iomem *base; + struct clk_hw clk_hw; +}; + +static uint32_t axi_clkgen_lookup_filter(unsigned int m) +{ + switch (m) { + case 0: + return 0x01001990; + case 1: + return 0x01001190; + case 2: + return 0x01009890; + case 3: + return 0x01001890; + case 4: + return 0x01008890; + case 5 ... 8: + return 0x01009090; + case 9 ... 11: + return 0x01000890; + case 12: + return 0x08009090; + case 13 ... 22: + return 0x01001090; + case 23 ... 36: + return 0x01008090; + case 37 ... 46: + return 0x08001090; + default: + return 0x08008090; + } +} + +static const uint32_t axi_clkgen_lock_table[] = { + 0x060603e8, 0x060603e8, 0x080803e8, 0x0b0b03e8, + 0x0e0e03e8, 0x111103e8, 0x131303e8, 0x161603e8, + 0x191903e8, 0x1c1c03e8, 0x1f1f0384, 0x1f1f0339, + 0x1f1f02ee, 0x1f1f02bc, 0x1f1f028a, 0x1f1f0271, + 0x1f1f023f, 0x1f1f0226, 0x1f1f020d, 0x1f1f01f4, + 0x1f1f01db, 0x1f1f01c2, 0x1f1f01a9, 0x1f1f0190, + 0x1f1f0190, 0x1f1f0177, 0x1f1f015e, 0x1f1f015e, + 0x1f1f0145, 0x1f1f0145, 0x1f1f012c, 0x1f1f012c, + 0x1f1f012c, 0x1f1f0113, 0x1f1f0113, 0x1f1f0113, +}; + +static uint32_t axi_clkgen_lookup_lock(unsigned int m) +{ + if (m < ARRAY_SIZE(axi_clkgen_lock_table)) + return axi_clkgen_lock_table[m]; + return 0x1f1f00fa; +} + +static const unsigned int fpfd_min = 10000; +static const unsigned int fpfd_max = 300000; +static const unsigned int fvco_min = 600000; +static const unsigned int fvco_max = 1200000; + +static void axi_clkgen_calc_params(unsigned long fin, unsigned long fout, + unsigned int *best_d, unsigned int *best_m, unsigned int *best_dout) +{ + unsigned long d, d_min, d_max, _d_min, _d_max; + unsigned long m, m_min, m_max; + unsigned long f, dout, best_f, fvco; + + fin /= 1000; + fout /= 1000; + + best_f = ULONG_MAX; + *best_d = 0; + *best_m = 0; + *best_dout = 0; + + d_min = max_t(unsigned long, DIV_ROUND_UP(fin, fpfd_max), 1); + d_max = min_t(unsigned long, fin / fpfd_min, 80); + + m_min = max_t(unsigned long, DIV_ROUND_UP(fvco_min, fin) * d_min, 1); + m_max = min_t(unsigned long, fvco_max * d_max / fin, 64); + + for (m = m_min; m <= m_max; m++) { + _d_min = max(d_min, DIV_ROUND_UP(fin * m, fvco_max)); + _d_max = min(d_max, fin * m / fvco_min); + + for (d = _d_min; d <= _d_max; d++) { + fvco = fin * m / d; + + dout = DIV_ROUND_CLOSEST(fvco, fout); + dout = clamp_t(unsigned long, dout, 1, 128); + f = fvco / dout; + if (abs(f - fout) < abs(best_f - fout)) { + best_f = f; + *best_d = d; + *best_m = m; + *best_dout = dout; + if (best_f == fout) + return; + } + } + } +} + +static void axi_clkgen_calc_clk_params(unsigned int divider, unsigned int *low, + unsigned int *high, unsigned int *edge, unsigned int *nocount) +{ + if (divider == 1) + *nocount = 1; + else + *nocount = 0; + + *high = divider / 2; + *edge = divider % 2; + *low = divider - *high; +} + +static void axi_clkgen_write(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int val) +{ + writel(val, axi_clkgen->base + reg); +} + +static void axi_clkgen_read(struct axi_clkgen *axi_clkgen, + unsigned int reg, unsigned int *val) +{ + *val = readl(axi_clkgen->base + reg); +} + +static struct axi_clkgen *clk_hw_to_axi_clkgen(struct clk_hw *clk_hw) +{ + return container_of(clk_hw, struct axi_clkgen, clk_hw); +} + +static int axi_clkgen_set_rate(struct clk_hw *clk_hw, + unsigned long rate, unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int nocount; + unsigned int high; + unsigned int edge; + unsigned int low; + uint32_t filter; + uint32_t lock; + + if (parent_rate == 0 || rate == 0) + return -EINVAL; + + axi_clkgen_calc_params(parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + filter = axi_clkgen_lookup_filter(m - 1); + lock = axi_clkgen_lookup_lock(m - 1); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 0); + + axi_clkgen_calc_clk_params(dout, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_calc_clk_params(d, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, + (edge << 13) | (nocount << 12) | (high << 6) | low); + + axi_clkgen_calc_clk_params(m, &low, &high, &edge, &nocount); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, + (high << 6) | low); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_CLK_FB2, + (edge << 7) | (nocount << 6)); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK1, lock & 0x3ff); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK2, + (((lock >> 16) & 0x1f) << 10) | 0x1); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_LOCK3, + (((lock >> 24) & 0x1f) << 10) | 0x3e9); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER1, filter >> 16); + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_FILTER2, filter); + + axi_clkgen_write(axi_clkgen, AXI_CLKGEN_REG_UPDATE_ENABLE, 1); + + return 0; +} + +static long axi_clkgen_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + unsigned int d, m, dout; + + axi_clkgen_calc_params(*parent_rate, rate, &d, &m, &dout); + + if (d == 0 || dout == 0 || m == 0) + return -EINVAL; + + return *parent_rate / d * m / dout; +} + +static unsigned long axi_clkgen_recalc_rate(struct clk_hw *clk_hw, + unsigned long parent_rate) +{ + struct axi_clkgen *axi_clkgen = clk_hw_to_axi_clkgen(clk_hw); + unsigned int d, m, dout; + unsigned int reg; + unsigned long long tmp; + + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_OUT1, ®); + dout = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_DIV, ®); + d = (reg & 0x3f) + ((reg >> 6) & 0x3f); + axi_clkgen_read(axi_clkgen, AXI_CLKGEN_REG_CLK_FB1, ®); + m = (reg & 0x3f) + ((reg >> 6) & 0x3f); + + if (d == 0 || dout == 0) + return 0; + + tmp = (unsigned long long)(parent_rate / d) * m; + do_div(tmp, dout); + + if (tmp > ULONG_MAX) + return ULONG_MAX; + + return tmp; +} + +static const struct clk_ops axi_clkgen_ops = { + .recalc_rate = axi_clkgen_recalc_rate, + .round_rate = axi_clkgen_round_rate, + .set_rate = axi_clkgen_set_rate, +}; + +static int axi_clkgen_probe(struct platform_device *pdev) +{ + struct axi_clkgen *axi_clkgen; + struct clk_init_data init; + const char *parent_name; + const char *clk_name; + struct resource *mem; + struct clk *clk; + + axi_clkgen = devm_kzalloc(&pdev->dev, sizeof(*axi_clkgen), GFP_KERNEL); + if (!axi_clkgen) + return -ENOMEM; + + mem = platform_get_resource(pdev, IORESOURCE_MEM, 0); + axi_clkgen->base = devm_ioremap_resource(&pdev->dev, mem); + if (IS_ERR(axi_clkgen->base)) + return PTR_ERR(axi_clkgen->base); + + parent_name = of_clk_get_parent_name(pdev->dev.of_node, 0); + if (!parent_name) + return -EINVAL; + + clk_name = pdev->dev.of_node->name; + of_property_read_string(pdev->dev.of_node, "clock-output-names", + &clk_name); + + init.name = clk_name; + init.ops = &axi_clkgen_ops; + init.flags = 0; + init.parent_names = &parent_name; + init.num_parents = 1; + + axi_clkgen->clk_hw.init = &init; + clk = devm_clk_register(&pdev->dev, &axi_clkgen->clk_hw); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + return of_clk_add_provider(pdev->dev.of_node, of_clk_src_simple_get, + clk); +} + +static int axi_clkgen_remove(struct platform_device *pdev) +{ + of_clk_del_provider(pdev->dev.of_node); + + return 0; +} + +static const struct of_device_id axi_clkgen_ids[] = { + { .compatible = "adi,axi-clkgen-1.00.a" }, + { }, +}; +MODULE_DEVICE_TABLE(of, axi_clkgen_ids); + +static struct platform_driver axi_clkgen_driver = { + .driver = { + .name = "adi-axi-clkgen", + .owner = THIS_MODULE, + .of_match_table = axi_clkgen_ids, + }, + .probe = axi_clkgen_probe, + .remove = axi_clkgen_remove, +}; +module_platform_driver(axi_clkgen_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Lars-Peter Clausen "); +MODULE_DESCRIPTION("Driver for the Analog Devices' AXI clkgen pcore clock generator"); -- cgit v1.2.3 From a368a6a33b107d680e955c799e6e1e3d6b4bbe8a Mon Sep 17 00:00:00 2001 From: Eduardo Valentin Date: Thu, 28 Feb 2013 09:59:07 -0400 Subject: documentation: clk: fix couple of misspelling Correcting misspelling inside the clk.txt. Signed-off-by: Eduardo Valentin Signed-off-by: Mike Turquette --- Documentation/clk.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Documentation/clk.txt b/Documentation/clk.txt index 1943fae014f..4274a546eb5 100644 --- a/Documentation/clk.txt +++ b/Documentation/clk.txt @@ -174,9 +174,9 @@ int clk_foo_enable(struct clk_hw *hw) }; Below is a matrix detailing which clk_ops are mandatory based upon the -hardware capbilities of that clock. A cell marked as "y" means +hardware capabilities of that clock. A cell marked as "y" means mandatory, a cell marked as "n" implies that either including that -callback is invalid or otherwise uneccesary. Empty cells are either +callback is invalid or otherwise unnecessary. Empty cells are either optional or must be evaluated on a case-by-case basis. clock hardware characteristics -- cgit v1.2.3 From 04981724172062029c4986e1d90732ff3c574944 Mon Sep 17 00:00:00 2001 From: Vipul Kumar Samar Date: Thu, 7 Mar 2013 12:35:24 +0530 Subject: clk:SPEAr1340: Correct parent clock configuration This patch corrects wrongly configured parent clock for following devices: * Video enc/decoder * Video ip * Pin control * ACP * camx Signed-off-by: Vipul Kumar Samar Reviewed-by: Shiraz Hashim Acked-by: Viresh Kumar Signed-off-by: Mike Turquette --- drivers/clk/spear/spear1340_clock.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/clk/spear/spear1340_clock.c b/drivers/clk/spear/spear1340_clock.c index 82abea366b7..35e7e2698e1 100644 --- a/drivers/clk/spear/spear1340_clock.c +++ b/drivers/clk/spear/spear1340_clock.c @@ -960,47 +960,47 @@ void __init spear1340_clk_init(void) SPEAR1340_SPDIF_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0100000.spdif-in"); - clk = clk_register_gate(NULL, "acp_clk", "acp_mclk", 0, + clk = clk_register_gate(NULL, "acp_clk", "ahb_clk", 0, SPEAR1340_PERIP2_CLK_ENB, SPEAR1340_ACP_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "acp_clk"); - clk = clk_register_gate(NULL, "plgpio_clk", "plgpio_mclk", 0, + clk = clk_register_gate(NULL, "plgpio_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_PLGPIO_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "e2800000.gpio"); - clk = clk_register_gate(NULL, "video_dec_clk", "video_dec_mclk", 0, + clk = clk_register_gate(NULL, "video_dec_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_DEC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_dec"); - clk = clk_register_gate(NULL, "video_enc_clk", "video_enc_mclk", 0, + clk = clk_register_gate(NULL, "video_enc_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_ENC_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "video_enc"); - clk = clk_register_gate(NULL, "video_in_clk", "video_in_mclk", 0, + clk = clk_register_gate(NULL, "video_in_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_VIDEO_IN_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "spear_vip"); - clk = clk_register_gate(NULL, "cam0_clk", "cam0_mclk", 0, + clk = clk_register_gate(NULL, "cam0_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM0_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0200000.cam0"); - clk = clk_register_gate(NULL, "cam1_clk", "cam1_mclk", 0, + clk = clk_register_gate(NULL, "cam1_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM1_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0300000.cam1"); - clk = clk_register_gate(NULL, "cam2_clk", "cam2_mclk", 0, + clk = clk_register_gate(NULL, "cam2_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM2_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0400000.cam2"); - clk = clk_register_gate(NULL, "cam3_clk", "cam3_mclk", 0, + clk = clk_register_gate(NULL, "cam3_clk", "ahb_clk", 0, SPEAR1340_PERIP3_CLK_ENB, SPEAR1340_CAM3_CLK_ENB, 0, &_lock); clk_register_clkdev(clk, NULL, "d0500000.cam3"); -- cgit v1.2.3 From f15ea6cbc81b1369c5eefcd8eada35057c46ab86 Mon Sep 17 00:00:00 2001 From: Wei Yongjun Date: Mon, 11 Mar 2013 23:55:18 +0800 Subject: clk: prima2: fix return value check in sirfsoc_of_clk_init() In case of error, the function clk_get() returns ERR_PTR() not NULL. The NULL test in the return value check should be replaced with IS_ERR(). Signed-off-by: Wei Yongjun Acked-by: Barry Song <21cnbao@gmail.com> Signed-off-by: Mike Turquette [mturquette@linaro.org: added missing parenthesis to fix compile break] --- drivers/clk/clk-prima2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk-prima2.c b/drivers/clk/clk-prima2.c index f8e9d0c27be..643ca653fef 100644 --- a/drivers/clk/clk-prima2.c +++ b/drivers/clk/clk-prima2.c @@ -1113,7 +1113,7 @@ void __init sirfsoc_of_clk_init(void) for (i = pll1; i < maxclk; i++) { prima2_clks[i] = clk_register(NULL, prima2_clk_hw_array[i]); - BUG_ON(!prima2_clks[i]); + BUG_ON(IS_ERR(prima2_clks[i])); } clk_register_clkdev(prima2_clks[cpu], NULL, "cpu"); clk_register_clkdev(prima2_clks[io], NULL, "io"); -- cgit v1.2.3 From 5fda6858a49c2d8706adcc05f083b64af172d3eb Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Wed, 13 Mar 2013 15:17:49 +0530 Subject: clk: Fix incorrect return type in clk.c Return type of function clk_propagate_rate_change is a pointer. But 0 was being returned. Change it to NULL. Silences the following warning: drivers/clk/clk.c:977:24: warning: Using plain integer as NULL pointer Signed-off-by: Sachin Kamat Reviewed-by: Pankaj Jangra Signed-off-by: Mike Turquette --- drivers/clk/clk.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 253792a46c0..5e8ffff9936 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -1026,7 +1026,7 @@ static struct clk *clk_propagate_rate_change(struct clk *clk, unsigned long even int ret = NOTIFY_DONE; if (clk->rate == clk->new_rate) - return 0; + return NULL; if (clk->notifier_count) { ret = __clk_notify(clk, event, clk->rate, clk->new_rate); -- cgit v1.2.3 From ce4f3313b05c836c21a91ac89f87dccf84ce9561 Mon Sep 17 00:00:00 2001 From: Peter De Schrijver Date: Fri, 22 Mar 2013 14:07:53 +0200 Subject: clk: add table lookup to mux Add a table lookup feature to the mux clock. Also allow arbitrary masks instead of the width. This will be used by some clocks on Tegra114. Also adapt the tegra periph clk because it uses struct clk_mux directly. Signed-off-by: Peter De Schrijver Tested-by: Stephen Warren Signed-off-by: Mike Turquette --- drivers/clk/clk-mux.c | 50 ++++++++++++++++++++++++++++++++++---------- drivers/clk/tegra/clk.h | 27 +++++++++++++++++------- include/linux/clk-private.h | 2 +- include/linux/clk-provider.h | 9 +++++++- 4 files changed, 67 insertions(+), 21 deletions(-) diff --git a/drivers/clk/clk-mux.c b/drivers/clk/clk-mux.c index 508c032edce..25b1734560d 100644 --- a/drivers/clk/clk-mux.c +++ b/drivers/clk/clk-mux.c @@ -32,6 +32,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) { struct clk_mux *mux = to_clk_mux(hw); + int num_parents = __clk_get_num_parents(hw->clk); u32 val; /* @@ -42,7 +43,16 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) * val = 0x4 really means "bit 2, index starts at bit 0" */ val = readl(mux->reg) >> mux->shift; - val &= (1 << mux->width) - 1; + val &= mux->mask; + + if (mux->table) { + int i; + + for (i = 0; i < num_parents; i++) + if (mux->table[i] == val) + return i; + return -EINVAL; + } if (val && (mux->flags & CLK_MUX_INDEX_BIT)) val = ffs(val) - 1; @@ -50,7 +60,7 @@ static u8 clk_mux_get_parent(struct clk_hw *hw) if (val && (mux->flags & CLK_MUX_INDEX_ONE)) val--; - if (val >= __clk_get_num_parents(hw->clk)) + if (val >= num_parents) return -EINVAL; return val; @@ -62,17 +72,22 @@ static int clk_mux_set_parent(struct clk_hw *hw, u8 index) u32 val; unsigned long flags = 0; - if (mux->flags & CLK_MUX_INDEX_BIT) - index = (1 << ffs(index)); + if (mux->table) + index = mux->table[index]; - if (mux->flags & CLK_MUX_INDEX_ONE) - index++; + else { + if (mux->flags & CLK_MUX_INDEX_BIT) + index = (1 << ffs(index)); + + if (mux->flags & CLK_MUX_INDEX_ONE) + index++; + } if (mux->lock) spin_lock_irqsave(mux->lock, flags); val = readl(mux->reg); - val &= ~(((1 << mux->width) - 1) << mux->shift); + val &= ~(mux->mask << mux->shift); val |= index << mux->shift; writel(val, mux->reg); @@ -88,10 +103,10 @@ const struct clk_ops clk_mux_ops = { }; EXPORT_SYMBOL_GPL(clk_mux_ops); -struct clk *clk_register_mux(struct device *dev, const char *name, +struct clk *clk_register_mux_table(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, - void __iomem *reg, u8 shift, u8 width, - u8 clk_mux_flags, spinlock_t *lock) + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock) { struct clk_mux *mux; struct clk *clk; @@ -113,9 +128,10 @@ struct clk *clk_register_mux(struct device *dev, const char *name, /* struct clk_mux assignments */ mux->reg = reg; mux->shift = shift; - mux->width = width; + mux->mask = mask; mux->flags = clk_mux_flags; mux->lock = lock; + mux->table = table; mux->hw.init = &init; clk = clk_register(dev, &mux->hw); @@ -125,3 +141,15 @@ struct clk *clk_register_mux(struct device *dev, const char *name, return clk; } + +struct clk *clk_register_mux(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u8 width, + u8 clk_mux_flags, spinlock_t *lock) +{ + u32 mask = BIT(width) - 1; + + return clk_register_mux_table(dev, name, parent_names, num_parents, + flags, reg, shift, mask, clk_mux_flags, + NULL, lock); +} diff --git a/drivers/clk/tegra/clk.h b/drivers/clk/tegra/clk.h index 0744731c622..a09d7dcaf18 100644 --- a/drivers/clk/tegra/clk.h +++ b/drivers/clk/tegra/clk.h @@ -355,15 +355,16 @@ struct clk *tegra_clk_register_periph_nodiv(const char *name, struct tegra_clk_periph *periph, void __iomem *clk_base, u32 offset); -#define TEGRA_CLK_PERIPH(_mux_shift, _mux_width, _mux_flags, \ +#define TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, _mux_flags, \ _div_shift, _div_width, _div_frac_width, \ _div_flags, _clk_num, _enb_refcnt, _regs, \ - _gate_flags) \ + _gate_flags, _table) \ { \ .mux = { \ .flags = _mux_flags, \ .shift = _mux_shift, \ - .width = _mux_width, \ + .mask = _mux_mask, \ + .table = _table, \ }, \ .divider = { \ .flags = _div_flags, \ @@ -393,26 +394,36 @@ struct tegra_periph_init_data { const char *dev_id; }; -#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset, \ - _mux_shift, _mux_width, _mux_flags, _div_shift, \ +#define TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_mask, _mux_flags, _div_shift, \ _div_width, _div_frac_width, _div_flags, _regs, \ - _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id, _table) \ { \ .name = _name, \ .clk_id = _clk_id, \ .parent_names = _parent_names, \ .num_parents = ARRAY_SIZE(_parent_names), \ - .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_width, \ + .periph = TEGRA_CLK_PERIPH(_mux_shift, _mux_mask, \ _mux_flags, _div_shift, \ _div_width, _div_frac_width, \ _div_flags, _clk_num, \ _enb_refcnt, _regs, \ - _gate_flags), \ + _gate_flags, _table), \ .offset = _offset, \ .con_id = _con_id, \ .dev_id = _dev_id, \ } +#define TEGRA_INIT_DATA(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, _mux_width, _mux_flags, _div_shift, \ + _div_width, _div_frac_width, _div_flags, _regs, \ + _clk_num, _enb_refcnt, _gate_flags, _clk_id) \ + TEGRA_INIT_DATA_TABLE(_name, _con_id, _dev_id, _parent_names, _offset,\ + _mux_shift, BIT(_mux_width) - 1, _mux_flags, \ + _div_shift, _div_width, _div_frac_width, _div_flags, \ + _regs, _clk_num, _enb_refcnt, _gate_flags, _clk_id,\ + NULL) + /** * struct clk_super_mux - super clock * diff --git a/include/linux/clk-private.h b/include/linux/clk-private.h index 9c7f5807824..dd7adff76e8 100644 --- a/include/linux/clk-private.h +++ b/include/linux/clk-private.h @@ -152,7 +152,7 @@ struct clk { }, \ .reg = _reg, \ .shift = _shift, \ - .width = _width, \ + .mask = BIT(_width) - 1, \ .flags = _mux_flags, \ .lock = _lock, \ }; \ diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 56e6cc12c79..63ba3b74079 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -297,8 +297,9 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, struct clk_mux { struct clk_hw hw; void __iomem *reg; + u32 *table; + u32 mask; u8 shift; - u8 width; u8 flags; spinlock_t *lock; }; @@ -307,11 +308,17 @@ struct clk_mux { #define CLK_MUX_INDEX_BIT BIT(1) extern const struct clk_ops clk_mux_ops; + struct clk *clk_register_mux(struct device *dev, const char *name, const char **parent_names, u8 num_parents, unsigned long flags, void __iomem *reg, u8 shift, u8 width, u8 clk_mux_flags, spinlock_t *lock); +struct clk *clk_register_mux_table(struct device *dev, const char *name, + const char **parent_names, u8 num_parents, unsigned long flags, + void __iomem *reg, u8 shift, u32 mask, + u8 clk_mux_flags, u32 *table, spinlock_t *lock); + /** * struct clk_fixed_factor - fixed multiplier and divider clock * -- cgit v1.2.3 From ece70094f6ab2107d4313fa1802b13dab0234ac5 Mon Sep 17 00:00:00 2001 From: Prashant Gaikwad Date: Wed, 20 Mar 2013 17:30:34 +0530 Subject: clk: Add composite clock type Not all clocks are required to be decomposed into basic clock types but at the same time want to use the functionality provided by these basic clock types instead of duplicating. For example, Tegra SoC has ~100 clocks which can be decomposed into Mux -> Div -> Gate clock types making the clock count to ~300. Also, parent change operation can not be performed on gate clock which forces to use mux clock in driver if want to change the parent. Instead aggregate the basic clock types functionality into one clock and just use this clock for all operations. This clock type re-uses the functionality of basic clock types and not limited to basic clock types but any hardware-specific implementation. Signed-off-by: Prashant Gaikwad Signed-off-by: Mike Turquette --- drivers/clk/Makefile | 1 + drivers/clk/clk-composite.c | 201 +++++++++++++++++++++++++++++++++++++++++++ include/linux/clk-provider.h | 31 +++++++ 3 files changed, 233 insertions(+) create mode 100644 drivers/clk/clk-composite.c diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 1c22f9dc721..41cb123a2d0 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -7,6 +7,7 @@ obj-$(CONFIG_COMMON_CLK) += clk-fixed-factor.o obj-$(CONFIG_COMMON_CLK) += clk-fixed-rate.o obj-$(CONFIG_COMMON_CLK) += clk-gate.o obj-$(CONFIG_COMMON_CLK) += clk-mux.o +obj-$(CONFIG_COMMON_CLK) += clk-composite.o # SoCs specific obj-$(CONFIG_ARCH_BCM2835) += clk-bcm2835.o diff --git a/drivers/clk/clk-composite.c b/drivers/clk/clk-composite.c new file mode 100644 index 00000000000..097dee4fd20 --- /dev/null +++ b/drivers/clk/clk-composite.c @@ -0,0 +1,201 @@ +/* + * Copyright (c) 2013 NVIDIA CORPORATION. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include +#include + +#define to_clk_composite(_hw) container_of(_hw, struct clk_composite, hw) + +static u8 clk_composite_get_parent(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->get_parent(mux_hw); +} + +static int clk_composite_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *mux_ops = composite->mux_ops; + struct clk_hw *mux_hw = composite->mux_hw; + + mux_hw->clk = hw->clk; + + return mux_ops->set_parent(mux_hw, index); +} + +static unsigned long clk_composite_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->recalc_rate(div_hw, parent_rate); +} + +static long clk_composite_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->round_rate(div_hw, rate, prate); +} + +static int clk_composite_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *div_ops = composite->div_ops; + struct clk_hw *div_hw = composite->div_hw; + + div_hw->clk = hw->clk; + + return div_ops->set_rate(div_hw, rate, parent_rate); +} + +static int clk_composite_is_enabled(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->is_enabled(gate_hw); +} + +static int clk_composite_enable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + return gate_ops->enable(gate_hw); +} + +static void clk_composite_disable(struct clk_hw *hw) +{ + struct clk_composite *composite = to_clk_composite(hw); + const struct clk_ops *gate_ops = composite->gate_ops; + struct clk_hw *gate_hw = composite->gate_hw; + + gate_hw->clk = hw->clk; + + gate_ops->disable(gate_hw); +} + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *div_hw, const struct clk_ops *div_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags) +{ + struct clk *clk; + struct clk_init_data init; + struct clk_composite *composite; + struct clk_ops *clk_composite_ops; + + composite = kzalloc(sizeof(*composite), GFP_KERNEL); + if (!composite) { + pr_err("%s: could not allocate composite clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = num_parents; + + clk_composite_ops = &composite->ops; + + if (mux_hw && mux_ops) { + if (!mux_ops->get_parent || !mux_ops->set_parent) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + 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 (div_hw && div_ops) { + if (!div_ops->recalc_rate || !div_ops->round_rate || + !div_ops->set_rate) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->div_hw = div_hw; + composite->div_ops = div_ops; + clk_composite_ops->recalc_rate = clk_composite_recalc_rate; + clk_composite_ops->round_rate = clk_composite_round_rate; + clk_composite_ops->set_rate = clk_composite_set_rate; + } + + if (gate_hw && gate_ops) { + if (!gate_ops->is_enabled || !gate_ops->enable || + !gate_ops->disable) { + clk = ERR_PTR(-EINVAL); + goto err; + } + + composite->gate_hw = gate_hw; + composite->gate_ops = gate_ops; + clk_composite_ops->is_enabled = clk_composite_is_enabled; + clk_composite_ops->enable = clk_composite_enable; + clk_composite_ops->disable = clk_composite_disable; + } + + init.ops = clk_composite_ops; + composite->hw.init = &init; + + clk = clk_register(dev, &composite->hw); + if (IS_ERR(clk)) + goto err; + + if (composite->mux_hw) + composite->mux_hw->clk = clk; + + if (composite->div_hw) + composite->div_hw->clk = clk; + + if (composite->gate_hw) + composite->gate_hw->clk = clk; + + return clk; + +err: + kfree(composite); + return clk; +} diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 63ba3b74079..1f035280279 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -342,6 +342,37 @@ struct clk *clk_register_fixed_factor(struct device *dev, const char *name, const char *parent_name, unsigned long flags, unsigned int mult, unsigned int div); +/*** + * struct clk_composite - aggregate clock of mux, divider and gate clocks + * + * @hw: handle between common and hardware-specific interfaces + * @mux_hw: handle between composite and hardware-specifix mux clock + * @div_hw: handle between composite and hardware-specifix divider clock + * @gate_hw: handle between composite and hardware-specifix gate clock + * @mux_ops: clock ops for mux + * @div_ops: clock ops for divider + * @gate_ops: clock ops for gate + */ +struct clk_composite { + struct clk_hw hw; + struct clk_ops ops; + + struct clk_hw *mux_hw; + struct clk_hw *div_hw; + struct clk_hw *gate_hw; + + const struct clk_ops *mux_ops; + const struct clk_ops *div_ops; + const struct clk_ops *gate_ops; +}; + +struct clk *clk_register_composite(struct device *dev, const char *name, + const char **parent_names, int num_parents, + struct clk_hw *mux_hw, const struct clk_ops *mux_ops, + struct clk_hw *div_hw, const struct clk_ops *div_ops, + struct clk_hw *gate_hw, const struct clk_ops *gate_ops, + unsigned long flags); + /** * clk_register - allocate a new clock, register it and return an opaque cookie * @dev: device that is registering this clock -- cgit v1.2.3 From a35e89ad91c5379bef867eac1aff46a6187f7d74 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Tue, 26 Mar 2013 10:42:10 -0300 Subject: ARM: imx: adapt clk_busy_mux to new clk_mux struct Commit ce4f3313b05 (clk: add table lookup to mux) caused the following build error on imx_v4_v5_defconfig/imx_v6_v7_defconfig: arch/arm/mach-imx/clk-busy.c:172:11: error: 'struct clk_mux' has no member named 'width' Fix it by passing the 'mask' field. Signed-off-by: Fabio Estevam Acked-by: Peter De Schrijver Signed-off-by: Mike Turquette [mturquette@linaro.org: shortened $SUBJECT line] --- arch/arm/mach-imx/clk-busy.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/arch/arm/mach-imx/clk-busy.c b/arch/arm/mach-imx/clk-busy.c index 1ab91b5209e..85b728cc27a 100644 --- a/arch/arm/mach-imx/clk-busy.c +++ b/arch/arm/mach-imx/clk-busy.c @@ -169,7 +169,7 @@ struct clk *imx_clk_busy_mux(const char *name, void __iomem *reg, u8 shift, busy->mux.reg = reg; busy->mux.shift = shift; - busy->mux.width = width; + busy->mux.mask = BIT(width) - 1; busy->mux.lock = &imx_ccm_lock; busy->mux_ops = &clk_mux_ops; -- cgit v1.2.3 From b54891685162e09c4292cd38f067ca118604353c Mon Sep 17 00:00:00 2001 From: Maxime Coquelin Date: Tue, 26 Mar 2013 15:27:15 +0100 Subject: clk: ux500: Fix prcmu clocks registration In clk_reg_prcmu(), clk->hw.init field is assigned with a reference local to clk_reg_prcmu() function. This patch replaces references to clk->hw.init with calls to __clk_get_name when called after clock registration. This patch applies on top of v3.9-rc4. Signed-off-by: Maxime Coquelin Acked-by: Ulf Hansson Signed-off-by: Mike Turquette [mturquette@linaro.org: resolved trivial merge issues] --- drivers/clk/ux500/clk-prcmu.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/drivers/clk/ux500/clk-prcmu.c b/drivers/clk/ux500/clk-prcmu.c index 9d832567c6b..293a2885441 100644 --- a/drivers/clk/ux500/clk-prcmu.c +++ b/drivers/clk/ux500/clk-prcmu.c @@ -44,7 +44,7 @@ static void clk_prcmu_unprepare(struct clk_hw *hw) struct clk_prcmu *clk = to_clk_prcmu(hw); if (prcmu_request_clock(clk->cg_sel, false)) pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); else clk->is_prepared = 0; } @@ -106,7 +106,7 @@ static int clk_prcmu_opp_prepare(struct clk_hw *hw) 100); if (err) { pr_err("clk_prcmu: %s fail req APE OPP for %s.\n", - __func__, hw->init->name); + __func__, __clk_get_name(hw->clk)); return err; } clk->opp_requested = 1; @@ -130,7 +130,7 @@ static void clk_prcmu_opp_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); return; } @@ -152,7 +152,7 @@ static int clk_prcmu_opp_volt_prepare(struct clk_hw *hw) err = prcmu_request_ape_opp_100_voltage(true); if (err) { pr_err("clk_prcmu: %s fail req APE OPP VOLT for %s.\n", - __func__, hw->init->name); + __func__, __clk_get_name(hw->clk)); return err; } clk->opp_requested = 1; @@ -175,7 +175,7 @@ static void clk_prcmu_opp_volt_unprepare(struct clk_hw *hw) if (prcmu_request_clock(clk->cg_sel, false)) { pr_err("clk_prcmu: %s failed to disable %s.\n", __func__, - hw->init->name); + __clk_get_name(hw->clk)); return; } -- cgit v1.2.3 From e874a6697710f52fa8ab29487a99034d5d96fdcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 25 Feb 2013 11:44:26 -0300 Subject: clk: arm: sunxi: Add a new clock driver for sunxi SOCs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit implements the base CPU clocks for sunxi devices. It has been tested using a slightly modified cpufreq driver from the linux-sunxi 3.0 tree. Additionally, document the new bindings introduced by this patch. Idling: / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 60000000 cpu 0 0 60000000 axi 0 0 60000000 ahb 0 0 60000000 apb0 0 0 30000000 dummy 0 0 0 After "yes >/dev/null &": / # cat /sys/kernel/debug/clk/clk_summary clock enable_cnt prepare_cnt rate --------------------------------------------------------------------- osc32k 0 0 32768 osc24M_fixed 0 0 24000000 osc24M 0 0 24000000 apb1_mux 0 0 24000000 apb1 0 0 24000000 pll1 0 0 1008000000 cpu 0 0 1008000000 axi 0 0 336000000 ahb 0 0 168000000 apb0 0 0 84000000 dummy 0 0 0 Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 44 +++ drivers/clk/Makefile | 1 + drivers/clk/sunxi/Makefile | 5 + drivers/clk/sunxi/clk-factors.c | 180 +++++++++++ drivers/clk/sunxi/clk-factors.h | 27 ++ drivers/clk/sunxi/clk-sunxi.c | 362 ++++++++++++++++++++++ drivers/clocksource/sunxi_timer.c | 4 +- include/linux/clk/sunxi.h | 22 ++ 8 files changed, 643 insertions(+), 2 deletions(-) create mode 100644 Documentation/devicetree/bindings/clock/sunxi.txt create mode 100644 drivers/clk/sunxi/Makefile create mode 100644 drivers/clk/sunxi/clk-factors.c create mode 100644 drivers/clk/sunxi/clk-factors.h create mode 100644 drivers/clk/sunxi/clk-sunxi.c create mode 100644 include/linux/clk/sunxi.h diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt new file mode 100644 index 00000000000..b23cfbdbcd6 --- /dev/null +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -0,0 +1,44 @@ +Device Tree Clock bindings for arch-sunxi + +This binding uses the common clock binding[1]. + +[1] Documentation/devicetree/bindings/clock/clock-bindings.txt + +Required properties: +- compatible : shall be one of the following: + "allwinner,sunxi-osc-clk" - for a gatable oscillator + "allwinner,sunxi-pll1-clk" - for the main PLL clock + "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock + "allwinner,sunxi-axi-clk" - for the sunxi AXI clock + "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock + "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock + "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock + "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + +Required properties for all clocks: +- reg : shall be the control register address for the clock. +- clocks : shall be the input parent clock(s) phandle for the clock +- #clock-cells : from common clock binding; shall be set to 0. + +For example: + +osc24M: osc24M@01c20050 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-osc-clk"; + reg = <0x01c20050 0x4>; + clocks = <&osc24M_fixed>; +}; + +pll1: pll1@01c20000 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-pll1-clk"; + reg = <0x01c20000 0x4>; + clocks = <&osc24M>; +}; + +cpu: cpu@01c20054 { + #clock-cells = <0>; + compatible = "allwinner,sunxi-cpu-clk"; + reg = <0x01c20054 0x4>; + clocks = <&osc32k>, <&osc24M>, <&pll1>; +}; diff --git a/drivers/clk/Makefile b/drivers/clk/Makefile index 41cb123a2d0..79e98e41672 100644 --- a/drivers/clk/Makefile +++ b/drivers/clk/Makefile @@ -24,6 +24,7 @@ ifeq ($(CONFIG_COMMON_CLK), y) obj-$(CONFIG_ARCH_MMP) += mmp/ endif obj-$(CONFIG_MACH_LOONGSON1) += clk-ls1x.o +obj-$(CONFIG_ARCH_SUNXI) += sunxi/ obj-$(CONFIG_ARCH_U8500) += ux500/ obj-$(CONFIG_ARCH_VT8500) += clk-vt8500.o obj-$(CONFIG_ARCH_ZYNQ) += clk-zynq.o diff --git a/drivers/clk/sunxi/Makefile b/drivers/clk/sunxi/Makefile new file mode 100644 index 00000000000..b5bac917612 --- /dev/null +++ b/drivers/clk/sunxi/Makefile @@ -0,0 +1,5 @@ +# +# Makefile for sunxi specific clk +# + +obj-y += clk-sunxi.o clk-factors.o diff --git a/drivers/clk/sunxi/clk-factors.c b/drivers/clk/sunxi/clk-factors.c new file mode 100644 index 00000000000..88523f91d9b --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.c @@ -0,0 +1,180 @@ +/* + * Copyright (C) 2013 Emilio López + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Adjustable factor-based clock implementation + */ + +#include +#include +#include +#include +#include +#include + +#include + +#include "clk-factors.h" + +/* + * DOC: basic adjustable factor-based clock that cannot gate + * + * Traits of this clock: + * prepare - clk_prepare only ensures that parents are prepared + * enable - clk_enable only ensures that parents are enabled + * rate - rate is adjustable. + * clk->rate = (parent->rate * N * (K + 1) >> P) / (M + 1) + * parent - fixed parent. No clk_set_parent support + */ + +struct clk_factors { + struct clk_hw hw; + void __iomem *reg; + struct clk_factors_config *config; + void (*get_factors) (u32 *rate, u32 parent, u8 *n, u8 *k, u8 *m, u8 *p); + spinlock_t *lock; +}; + +#define to_clk_factors(_hw) container_of(_hw, struct clk_factors, hw) + +#define SETMASK(len, pos) (((-1U) >> (31-len)) << (pos)) +#define CLRMASK(len, pos) (~(SETMASK(len, pos))) +#define FACTOR_GET(bit, len, reg) (((reg) & SETMASK(len, bit)) >> (bit)) + +#define FACTOR_SET(bit, len, reg, val) \ + (((reg) & CLRMASK(len, bit)) | (val << (bit))) + +static unsigned long clk_factors_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + u8 n = 1, k = 0, p = 0, m = 0; + u32 reg; + unsigned long rate; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Get each individual factor if applicable */ + if (config->nwidth != SUNXI_FACTORS_NOT_APPLICABLE) + n = FACTOR_GET(config->nshift, config->nwidth, reg); + if (config->kwidth != SUNXI_FACTORS_NOT_APPLICABLE) + k = FACTOR_GET(config->kshift, config->kwidth, reg); + if (config->mwidth != SUNXI_FACTORS_NOT_APPLICABLE) + m = FACTOR_GET(config->mshift, config->mwidth, reg); + if (config->pwidth != SUNXI_FACTORS_NOT_APPLICABLE) + p = FACTOR_GET(config->pshift, config->pwidth, reg); + + /* Calculate the rate */ + rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + + return rate; +} + +static long clk_factors_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *parent_rate) +{ + struct clk_factors *factors = to_clk_factors(hw); + factors->get_factors((u32 *)&rate, (u32)*parent_rate, + NULL, NULL, NULL, NULL); + + return rate; +} + +static int clk_factors_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + u8 n, k, m, p; + u32 reg; + struct clk_factors *factors = to_clk_factors(hw); + struct clk_factors_config *config = factors->config; + unsigned long flags = 0; + + factors->get_factors((u32 *)&rate, (u32)parent_rate, &n, &k, &m, &p); + + if (factors->lock) + spin_lock_irqsave(factors->lock, flags); + + /* Fetch the register value */ + reg = readl(factors->reg); + + /* Set up the new factors - macros do not do anything if width is 0 */ + reg = FACTOR_SET(config->nshift, config->nwidth, reg, n); + reg = FACTOR_SET(config->kshift, config->kwidth, reg, k); + reg = FACTOR_SET(config->mshift, config->mwidth, reg, m); + reg = FACTOR_SET(config->pshift, config->pwidth, reg, p); + + /* Apply them now */ + writel(reg, factors->reg); + + /* delay 500us so pll stabilizes */ + __delay((rate >> 20) * 500 / 2); + + if (factors->lock) + spin_unlock_irqrestore(factors->lock, flags); + + return 0; +} + +static const struct clk_ops clk_factors_ops = { + .recalc_rate = clk_factors_recalc_rate, + .round_rate = clk_factors_round_rate, + .set_rate = clk_factors_set_rate, +}; + +/** + * clk_register_factors - register a factors clock with + * the clock framework + * @dev: device registering this clock + * @name: name of this clock + * @parent_name: name of clock's parent + * @flags: framework-specific flags + * @reg: register address to adjust factors + * @config: shift and width of factors n, k, m and p + * @get_factors: function to calculate the factors for a given frequency + * @lock: shared register lock for this clock + */ +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors)(u32 *rate, u32 parent, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock) +{ + struct clk_factors *factors; + struct clk *clk; + struct clk_init_data init; + + /* allocate the factors */ + factors = kzalloc(sizeof(struct clk_factors), GFP_KERNEL); + if (!factors) { + pr_err("%s: could not allocate factors clk\n", __func__); + return ERR_PTR(-ENOMEM); + } + + init.name = name; + init.ops = &clk_factors_ops; + init.flags = flags; + init.parent_names = (parent_name ? &parent_name : NULL); + init.num_parents = (parent_name ? 1 : 0); + + /* struct clk_factors assignments */ + factors->reg = reg; + factors->config = config; + factors->lock = lock; + factors->hw.init = &init; + factors->get_factors = get_factors; + + /* register the clock */ + clk = clk_register(dev, &factors->hw); + + if (IS_ERR(clk)) + kfree(factors); + + return clk; +} diff --git a/drivers/clk/sunxi/clk-factors.h b/drivers/clk/sunxi/clk-factors.h new file mode 100644 index 00000000000..f49851cc438 --- /dev/null +++ b/drivers/clk/sunxi/clk-factors.h @@ -0,0 +1,27 @@ +#ifndef __MACH_SUNXI_CLK_FACTORS_H +#define __MACH_SUNXI_CLK_FACTORS_H + +#include +#include + +#define SUNXI_FACTORS_NOT_APPLICABLE (0) + +struct clk_factors_config { + u8 nshift; + u8 nwidth; + u8 kshift; + u8 kwidth; + u8 mshift; + u8 mwidth; + u8 pshift; + u8 pwidth; +}; + +struct clk *clk_register_factors(struct device *dev, const char *name, + const char *parent_name, + unsigned long flags, void __iomem *reg, + struct clk_factors_config *config, + void (*get_factors) (u32 *rate, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p), + spinlock_t *lock); +#endif diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c new file mode 100644 index 00000000000..d4ad1c22859 --- /dev/null +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -0,0 +1,362 @@ +/* + * Copyright 2013 Emilio López + * + * Emilio López + * + * 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 +#include +#include +#include +#include + +#include "clk-factors.h" + +static DEFINE_SPINLOCK(clk_lock); + +/** + * sunxi_osc_clk_setup() - Setup function for gatable oscillator + */ + +#define SUNXI_OSC24M_GATE 0 + +static void __init sunxi_osc_clk_setup(struct device_node *node) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, SUNXI_OSC24M_GATE, 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_get_pll1_factors() - calculates n, k, m, p factors for PLL1 + * PLL1 rate is calculated as follows + * rate = (parent_rate * n * (k + 1) >> p) / (m + 1); + * parent_rate is always 24Mhz + */ + +static void sunxi_get_pll1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 div; + + /* Normalize value to a 6M multiple */ + div = *freq / 6000000; + *freq = 6000000 * div; + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + /* m is always zero for pll1 */ + *m = 0; + + /* k is 1 only on these cases */ + if (*freq >= 768000000 || *freq == 42000000 || *freq == 54000000) + *k = 1; + else + *k = 0; + + /* p will be 3 for divs under 10 */ + if (div < 10) + *p = 3; + + /* p will be 2 for divs between 10 - 20 and odd divs under 32 */ + else if (div < 20 || (div < 32 && (div & 1))) + *p = 2; + + /* p will be 1 for even divs under 32, divs under 40 and odd pairs + * of divs between 40-62 */ + else if (div < 40 || (div < 64 && (div & 2))) + *p = 1; + + /* any other entries have p = 0 */ + else + *p = 0; + + /* calculate a suitable n based on k and p */ + div <<= *p; + div /= (*k + 1); + *n = div / 4; +} + + + +/** + * sunxi_get_apb1_factors() - calculates m, p factors for APB1 + * APB1 rate is calculated as follows + * rate = (parent_rate >> p) / (m + 1); + */ + +static void sunxi_get_apb1_factors(u32 *freq, u32 parent_rate, + u8 *n, u8 *k, u8 *m, u8 *p) +{ + u8 calcm, calcp; + + if (parent_rate < *freq) + *freq = parent_rate; + + parent_rate = (parent_rate + (*freq - 1)) / *freq; + + /* Invalid rate! */ + if (parent_rate > 32) + return; + + if (parent_rate <= 4) + calcp = 0; + else if (parent_rate <= 8) + calcp = 1; + else if (parent_rate <= 16) + calcp = 2; + else + calcp = 3; + + calcm = (parent_rate >> calcp) - 1; + + *freq = (parent_rate >> calcp) / (calcm + 1); + + /* we were called to round the frequency, we can now return */ + if (n == NULL) + return; + + *m = calcm; + *p = calcp; +} + + + +/** + * sunxi_factors_clk_setup() - Setup function for factor clocks + */ + +struct factors_data { + struct clk_factors_config *table; + void (*getter) (u32 *rate, u32 parent_rate, u8 *n, u8 *k, u8 *m, u8 *p); +}; + +static struct clk_factors_config pll1_config = { + .nshift = 8, + .nwidth = 5, + .kshift = 4, + .kwidth = 2, + .mshift = 0, + .mwidth = 2, + .pshift = 16, + .pwidth = 2, +}; + +static struct clk_factors_config apb1_config = { + .mshift = 0, + .mwidth = 5, + .pshift = 16, + .pwidth = 2, +}; + +static const __initconst struct factors_data pll1_data = { + .table = &pll1_config, + .getter = sunxi_get_pll1_factors, +}; + +static const __initconst struct factors_data apb1_data = { + .table = &apb1_config, + .getter = sunxi_get_apb1_factors, +}; + +static void __init sunxi_factors_clk_setup(struct device_node *node, + struct factors_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *parent; + void *reg; + + reg = of_iomap(node, 0); + + parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, + reg, data->table, data->getter, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_mux_clk_setup() - Setup function for muxes + */ + +#define SUNXI_MUX_GATE_WIDTH 2 + +struct mux_data { + u8 shift; +}; + +static const __initconst struct mux_data cpu_data = { + .shift = 16, +}; + +static const __initconst struct mux_data apb1_mux_data = { + .shift = 24, +}; + +static void __init sunxi_mux_clk_setup(struct device_node *node, + struct mux_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); + void *reg; + int i = 0; + + reg = of_iomap(node, 0); + + while (i < 5 && (parents[i] = of_clk_get_parent_name(node, i)) != NULL) + i++; + + clk = clk_register_mux(NULL, clk_name, parents, i, 0, reg, + data->shift, SUNXI_MUX_GATE_WIDTH, + 0, &clk_lock); + + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + + +/** + * sunxi_divider_clk_setup() - Setup function for simple divider clocks + */ + +#define SUNXI_DIVISOR_WIDTH 2 + +struct div_data { + u8 shift; + u8 pow; +}; + +static const __initconst struct div_data axi_data = { + .shift = 0, + .pow = 0, +}; + +static const __initconst struct div_data ahb_data = { + .shift = 4, + .pow = 1, +}; + +static const __initconst struct div_data apb0_data = { + .shift = 8, + .pow = 1, +}; + +static void __init sunxi_divider_clk_setup(struct device_node *node, + struct div_data *data) +{ + struct clk *clk; + const char *clk_name = node->name; + const char *clk_parent; + void *reg; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + clk = clk_register_divider(NULL, clk_name, clk_parent, 0, + reg, data->shift, SUNXI_DIVISOR_WIDTH, + data->pow ? CLK_DIVIDER_POWER_OF_TWO : 0, + &clk_lock); + if (clk) { + of_clk_add_provider(node, of_clk_src_simple_get, clk); + clk_register_clkdev(clk, clk_name, NULL); + } +} + + +/* Matches for of_clk_init */ +static const __initconst struct of_device_id clk_match[] = { + {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, + {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {} +}; + +/* Matches for factors clocks */ +static const __initconst struct of_device_id clk_factors_match[] = { + {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {} +}; + +/* Matches for divider clocks */ +static const __initconst struct of_device_id clk_div_match[] = { + {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {} +}; + +/* Matches for mux clocks */ +static const __initconst struct of_device_id clk_mux_match[] = { + {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {} +}; + +static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, + void *function) +{ + struct device_node *np; + const struct div_data *data; + const struct of_device_id *match; + void (*setup_function)(struct device_node *, const void *) = function; + + for_each_matching_node(np, clk_match) { + match = of_match_node(clk_match, np); + data = match->data; + setup_function(np, data); + } +} + +void __init sunxi_init_clocks(void) +{ + /* Register all the simple sunxi clocks on DT */ + of_clk_init(clk_match); + + /* Register factor clocks */ + of_sunxi_table_clock_setup(clk_factors_match, sunxi_factors_clk_setup); + + /* Register divider clocks */ + of_sunxi_table_clock_setup(clk_div_match, sunxi_divider_clk_setup); + + /* Register mux clocks */ + of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); +} diff --git a/drivers/clocksource/sunxi_timer.c b/drivers/clocksource/sunxi_timer.c index 4086b916715..0ce85e29769 100644 --- a/drivers/clocksource/sunxi_timer.c +++ b/drivers/clocksource/sunxi_timer.c @@ -23,7 +23,7 @@ #include #include #include -#include +#include #define TIMER_CTL_REG 0x00 #define TIMER_CTL_ENABLE (1 << 0) @@ -123,7 +123,7 @@ void __init sunxi_timer_init(void) if (irq <= 0) panic("Can't parse IRQ"); - of_clk_init(NULL); + sunxi_init_clocks(); clk = of_clk_get(node, 0); if (IS_ERR(clk)) diff --git a/include/linux/clk/sunxi.h b/include/linux/clk/sunxi.h new file mode 100644 index 00000000000..e074fdd5a23 --- /dev/null +++ b/include/linux/clk/sunxi.h @@ -0,0 +1,22 @@ +/* + * Copyright 2012 Maxime Ripard + * + * Maxime Ripard + * + * 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 __LINUX_CLK_SUNXI_H_ +#define __LINUX_CLK_SUNXI_H_ + +void __init sunxi_init_clocks(void); + +#endif -- cgit v1.2.3 From 8eb896ab7a716308698de77073a9265f584b12fc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Mon, 25 Feb 2013 11:44:28 -0300 Subject: arm: sunxi: Add useful information about sunxi clocks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patch contains useful bits of information about the sunxi clocks that may help and/or be interesting for current and future developers. Signed-off-by: Emilio López Acked-by: Maxime Ripard Signed-off-by: Mike Turquette --- Documentation/arm/sunxi/clocks.txt | 56 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100644 Documentation/arm/sunxi/clocks.txt diff --git a/Documentation/arm/sunxi/clocks.txt b/Documentation/arm/sunxi/clocks.txt new file mode 100644 index 00000000000..e09a88aa313 --- /dev/null +++ b/Documentation/arm/sunxi/clocks.txt @@ -0,0 +1,56 @@ +Frequently asked questions about the sunxi clock system +======================================================= + +This document contains useful bits of information that people tend to ask +about the sunxi clock system, as well as accompanying ASCII art when adequate. + +Q: Why is the main 24MHz oscillator gatable? Wouldn't that break the + system? + +A: The 24MHz oscillator allows gating to save power. Indeed, if gated + carelessly the system would stop functioning, but with the right + steps, one can gate it and keep the system running. Consider this + simplified suspend example: + + While the system is operational, you would see something like + + 24MHz 32kHz + | + PLL1 + \ + \_ CPU Mux + | + [CPU] + + When you are about to suspend, you switch the CPU Mux to the 32kHz + oscillator: + + 24Mhz 32kHz + | | + PLL1 | + / + CPU Mux _/ + | + [CPU] + + Finally you can gate the main oscillator + + 32kHz + | + | + / + CPU Mux _/ + | + [CPU] + +Q: Were can I learn more about the sunxi clocks? + +A: The linux-sunxi wiki contains a page documenting the clock registers, + you can find it at + + http://linux-sunxi.org/A10/CCM + + The authoritative source for information at this time is the ccmu driver + released by Allwinner, you can find it at + + https://github.com/linux-sunxi/linux-sunxi/tree/sunxi-3.0/arch/arm/mach-sun4i/clock/ccmu -- cgit v1.2.3 From e3276998da12c6ec093befd6e49be4848414d57e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Tue, 26 Mar 2013 23:39:17 -0300 Subject: clk: sunxi: rename compatible strings MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit During the introduction of the Allwinner SoC platforms, sunxi was initially meant as a generic name for all the variants of the Allwinner SoC. It was ok at the time of the support of only the A10 and A13 that look pretty much the same; but it's beginning to be troublesome with the future addition of the Allwinner A31 (sun6i) that is quite different, and would introduce some weird logic, where sunxi would actually mean in some case sun4i and sun5i but without sun6i... Moreover, it makes the compatible strings naming scheme not consistent with other architectures, where usually for this kind of compability, we just use the oldest SoC name that has this IP, so let's do just this. Signed-off-by: Emilio López Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 22 +++++++++++----------- drivers/clk/sunxi/clk-sunxi.c | 16 ++++++++-------- 2 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index b23cfbdbcd6..20b8479c276 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -6,14 +6,14 @@ This binding uses the common clock binding[1]. Required properties: - compatible : shall be one of the following: - "allwinner,sunxi-osc-clk" - for a gatable oscillator - "allwinner,sunxi-pll1-clk" - for the main PLL clock - "allwinner,sunxi-cpu-clk" - for the CPU multiplexer clock - "allwinner,sunxi-axi-clk" - for the sunxi AXI clock - "allwinner,sunxi-ahb-clk" - for the sunxi AHB clock - "allwinner,sunxi-apb0-clk" - for the sunxi APB0 clock - "allwinner,sunxi-apb1-clk" - for the sunxi APB1 clock - "allwinner,sunxi-apb1-mux-clk" - for the sunxi APB1 clock muxing + "allwinner,sun4i-osc-clk" - for a gatable oscillator + "allwinner,sun4i-pll1-clk" - for the main PLL clock + "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock + "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-ahb-clk" - for the AHB clock + "allwinner,sun4i-apb0-clk" - for the APB0 clock + "allwinner,sun4i-apb1-clk" - for the APB1 clock + "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing Required properties for all clocks: - reg : shall be the control register address for the clock. @@ -24,21 +24,21 @@ For example: osc24M: osc24M@01c20050 { #clock-cells = <0>; - compatible = "allwinner,sunxi-osc-clk"; + compatible = "allwinner,sun4i-osc-clk"; reg = <0x01c20050 0x4>; clocks = <&osc24M_fixed>; }; pll1: pll1@01c20000 { #clock-cells = <0>; - compatible = "allwinner,sunxi-pll1-clk"; + compatible = "allwinner,sun4i-pll1-clk"; reg = <0x01c20000 0x4>; clocks = <&osc24M>; }; cpu: cpu@01c20054 { #clock-cells = <0>; - compatible = "allwinner,sunxi-cpu-clk"; + compatible = "allwinner,sun4i-cpu-clk"; reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d4ad1c22859..d528a249669 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -305,29 +305,29 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, /* Matches for of_clk_init */ static const __initconst struct of_device_id clk_match[] = { {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, - {.compatible = "allwinner,sunxi-osc-clk", .data = sunxi_osc_clk_setup,}, + {.compatible = "allwinner,sun4i-osc-clk", .data = sunxi_osc_clk_setup,}, {} }; /* Matches for factors clocks */ static const __initconst struct of_device_id clk_factors_match[] = { - {.compatible = "allwinner,sunxi-pll1-clk", .data = &pll1_data,}, - {.compatible = "allwinner,sunxi-apb1-clk", .data = &apb1_data,}, + {.compatible = "allwinner,sun4i-pll1-clk", .data = &pll1_data,}, + {.compatible = "allwinner,sun4i-apb1-clk", .data = &apb1_data,}, {} }; /* Matches for divider clocks */ static const __initconst struct of_device_id clk_div_match[] = { - {.compatible = "allwinner,sunxi-axi-clk", .data = &axi_data,}, - {.compatible = "allwinner,sunxi-ahb-clk", .data = &ahb_data,}, - {.compatible = "allwinner,sunxi-apb0-clk", .data = &apb0_data,}, + {.compatible = "allwinner,sun4i-axi-clk", .data = &axi_data,}, + {.compatible = "allwinner,sun4i-ahb-clk", .data = &ahb_data,}, + {.compatible = "allwinner,sun4i-apb0-clk", .data = &apb0_data,}, {} }; /* Matches for mux clocks */ static const __initconst struct of_device_id clk_mux_match[] = { - {.compatible = "allwinner,sunxi-cpu-clk", .data = &cpu_data,}, - {.compatible = "allwinner,sunxi-apb1-mux-clk", .data = &apb1_mux_data,}, + {.compatible = "allwinner,sun4i-cpu-clk", .data = &cpu_data,}, + {.compatible = "allwinner,sun4i-apb1-mux-clk", .data = &apb1_mux_data,}, {} }; -- cgit v1.2.3 From 43c4120c0656692d08f5c005881cc0c4573ce3b5 Mon Sep 17 00:00:00 2001 From: Michal Simek Date: Wed, 27 Mar 2013 15:45:06 +0100 Subject: clk: zynq: Add missing zynq clk header Include zynq clk header where init function is declared. It removes this sparse warning: drivers/clk/clk-zynq.c:373:13: warning: symbol 'xilinx_zynq_clocks_init' was not declared. Should it be static? Signed-off-by: Michal Simek Signed-off-by: Mike Turquette --- drivers/clk/clk-zynq.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/clk/clk-zynq.c b/drivers/clk/clk-zynq.c index b14a25f3925..32062977f45 100644 --- a/drivers/clk/clk-zynq.c +++ b/drivers/clk/clk-zynq.c @@ -20,6 +20,7 @@ #include #include #include +#include static void __iomem *slcr_base; -- cgit v1.2.3 From eab89f690ee0805c02017d7959f4f930379a8c46 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Thu, 28 Mar 2013 13:59:01 -0700 Subject: clk: abstract locking out into helper functions Create locking helpers for the global mutex and global spinlock. The definitions of these helpers will be expanded upon in the next patch which introduces reentrancy into the locking scheme. Signed-off-by: Mike Turquette Cc: Rajagopal Venkat Cc: David Brown Tested-by: Laurent Pinchart Reviewed-by: Thomas Gleixner Reviewed-by: Ulf Hansson --- drivers/clk/clk.c | 99 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 61 insertions(+), 38 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 5e8ffff9936..0b5d61201ca 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -27,6 +27,29 @@ static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); +/*** locking ***/ +static void clk_prepare_lock(void) +{ + mutex_lock(&prepare_lock); +} + +static void clk_prepare_unlock(void) +{ + mutex_unlock(&prepare_lock); +} + +static unsigned long clk_enable_lock(void) +{ + unsigned long flags; + spin_lock_irqsave(&enable_lock, flags); + return flags; +} + +static void clk_enable_unlock(unsigned long flags) +{ + spin_unlock_irqrestore(&enable_lock, flags); +} + /*** debugfs support ***/ #ifdef CONFIG_COMMON_CLK_DEBUG @@ -69,7 +92,7 @@ static int clk_summary_show(struct seq_file *s, void *data) seq_printf(s, " clock enable_cnt prepare_cnt rate\n"); seq_printf(s, "---------------------------------------------------------------------\n"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) clk_summary_show_subtree(s, c, 0); @@ -77,7 +100,7 @@ static int clk_summary_show(struct seq_file *s, void *data) hlist_for_each_entry(c, &clk_orphan_list, child_node) clk_summary_show_subtree(s, c, 0); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -130,7 +153,7 @@ static int clk_dump(struct seq_file *s, void *data) seq_printf(s, "{"); - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(c, &clk_root_list, child_node) { if (!first_node) @@ -144,7 +167,7 @@ static int clk_dump(struct seq_file *s, void *data) clk_dump_subtree(s, c, 0); } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); seq_printf(s, "}"); return 0; @@ -316,7 +339,7 @@ static int __init clk_debug_init(void) if (!orphandir) return -ENOMEM; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_debug_create_subtree(clk, rootdir); @@ -326,7 +349,7 @@ static int __init clk_debug_init(void) inited = 1; - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -372,7 +395,7 @@ static void clk_disable_unused_subtree(struct clk *clk) hlist_for_each_entry(child, &clk->children, child_node) clk_disable_unused_subtree(child); - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) goto unlock_out; @@ -393,7 +416,7 @@ static void clk_disable_unused_subtree(struct clk *clk) } unlock_out: - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); out: return; @@ -403,7 +426,7 @@ static int clk_disable_unused(void) { struct clk *clk; - mutex_lock(&prepare_lock); + clk_prepare_lock(); hlist_for_each_entry(clk, &clk_root_list, child_node) clk_disable_unused_subtree(clk); @@ -417,7 +440,7 @@ static int clk_disable_unused(void) hlist_for_each_entry(clk, &clk_orphan_list, child_node) clk_unprepare_unused_subtree(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return 0; } @@ -600,9 +623,9 @@ void __clk_unprepare(struct clk *clk) */ void clk_unprepare(struct clk *clk) { - mutex_lock(&prepare_lock); + clk_prepare_lock(); __clk_unprepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); } EXPORT_SYMBOL_GPL(clk_unprepare); @@ -648,9 +671,9 @@ int clk_prepare(struct clk *clk) { int ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_prepare(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -692,9 +715,9 @@ void clk_disable(struct clk *clk) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); __clk_disable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); } EXPORT_SYMBOL_GPL(clk_disable); @@ -745,9 +768,9 @@ int clk_enable(struct clk *clk) unsigned long flags; int ret; - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); ret = __clk_enable(clk); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); return ret; } @@ -792,9 +815,9 @@ long clk_round_rate(struct clk *clk, unsigned long rate) { unsigned long ret; - mutex_lock(&prepare_lock); + clk_prepare_lock(); ret = __clk_round_rate(clk, rate); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -889,13 +912,13 @@ unsigned long clk_get_rate(struct clk *clk) { unsigned long rate; - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk && (clk->flags & CLK_GET_RATE_NOCACHE)) __clk_recalc_rates(clk, 0); rate = __clk_get_rate(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return rate; } @@ -1100,7 +1123,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) int ret = 0; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* bail early if nothing to do */ if (rate == clk->rate) @@ -1132,7 +1155,7 @@ int clk_set_rate(struct clk *clk, unsigned long rate) clk_change_rate(top); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1148,9 +1171,9 @@ struct clk *clk_get_parent(struct clk *clk) { struct clk *parent; - mutex_lock(&prepare_lock); + clk_prepare_lock(); parent = __clk_get_parent(clk); - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return parent; } @@ -1294,19 +1317,19 @@ static int __clk_set_parent(struct clk *clk, struct clk *parent) __clk_prepare(parent); /* FIXME replace with clk_is_enabled(clk) someday */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_enable(parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); /* change clock input source */ ret = clk->ops->set_parent(clk->hw, i); /* clean up old prepare and enable */ - spin_lock_irqsave(&enable_lock, flags); + flags = clk_enable_lock(); if (clk->enable_count) __clk_disable(old_parent); - spin_unlock_irqrestore(&enable_lock, flags); + clk_enable_unlock(flags); if (clk->prepare_count) __clk_unprepare(old_parent); @@ -1338,7 +1361,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) return -ENOSYS; /* prevent racing with updates to the clock topology */ - mutex_lock(&prepare_lock); + clk_prepare_lock(); if (clk->parent == parent) goto out; @@ -1367,7 +1390,7 @@ int clk_set_parent(struct clk *clk, struct clk *parent) __clk_reparent(clk, parent); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1390,7 +1413,7 @@ int __clk_init(struct device *dev, struct clk *clk) if (!clk) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* check to see if a clock with this name is already registered */ if (__clk_lookup(clk->name)) { @@ -1514,7 +1537,7 @@ int __clk_init(struct device *dev, struct clk *clk) clk_debug_register(clk); out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1748,7 +1771,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); /* search the list of notifiers for this clk */ list_for_each_entry(cn, &clk_notifier_list, node) @@ -1772,7 +1795,7 @@ int clk_notifier_register(struct clk *clk, struct notifier_block *nb) clk->notifier_count++; out: - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } @@ -1797,7 +1820,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) if (!clk || !nb) return -EINVAL; - mutex_lock(&prepare_lock); + clk_prepare_lock(); list_for_each_entry(cn, &clk_notifier_list, node) if (cn->clk == clk) @@ -1818,7 +1841,7 @@ int clk_notifier_unregister(struct clk *clk, struct notifier_block *nb) ret = -ENOENT; } - mutex_unlock(&prepare_lock); + clk_prepare_unlock(); return ret; } -- cgit v1.2.3 From 533ddeb1e86f506129ee388a6cc13796dcf31311 Mon Sep 17 00:00:00 2001 From: Mike Turquette Date: Thu, 28 Mar 2013 13:59:02 -0700 Subject: clk: allow reentrant calls into the clk framework Reentrancy into the clock framework is necessary for clock operations that result in nested calls to the clk api. A common example is a clock that is prepared via an i2c transaction, such as a clock inside of a discrete audio chip or a power management IC. The i2c subsystem itself will use the clk api resulting in a deadlock: clk_prepare(audio_clk) i2c_transfer(..) clk_prepare(i2c_controller_clk) The ability to reenter the clock framework prevents this deadlock. Other use cases exist such as allowing .set_rate callbacks to call clk_set_parent to achieve the best rate, or to save power in certain configurations. Yet another example is performing pinctrl operations from a clk_ops callback. Calls into the pinctrl subsystem may call clk_{un}prepare on an unrelated clock. Allowing for nested calls to reenter the clock framework enables both of these use cases. Reentrancy is implemented by two global pointers that track the owner currently holding a global lock. One pointer tracks the owner during sleepable, mutex-protected operations and the other one tracks the owner during non-interruptible, spinlock-protected operations. When the clk framework is entered we try to hold the global lock. If it is held we compare the current task against the current owner; a match implies a nested call and we reenter. If the values do not match then we block on the lock until it is released. Signed-off-by: Mike Turquette Cc: Rajagopal Venkat Cc: David Brown Tested-by: Laurent Pinchart Reviewed-by: Thomas Gleixner Reviewed-by: Ulf Hansson --- drivers/clk/clk.c | 44 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 42 insertions(+), 2 deletions(-) diff --git a/drivers/clk/clk.c b/drivers/clk/clk.c index 0b5d61201ca..0230c9d9597 100644 --- a/drivers/clk/clk.c +++ b/drivers/clk/clk.c @@ -19,10 +19,17 @@ #include #include #include +#include static DEFINE_SPINLOCK(enable_lock); static DEFINE_MUTEX(prepare_lock); +static struct task_struct *prepare_owner; +static struct task_struct *enable_owner; + +static int prepare_refcnt; +static int enable_refcnt; + static HLIST_HEAD(clk_root_list); static HLIST_HEAD(clk_orphan_list); static LIST_HEAD(clk_notifier_list); @@ -30,23 +37,56 @@ static LIST_HEAD(clk_notifier_list); /*** locking ***/ static void clk_prepare_lock(void) { - mutex_lock(&prepare_lock); + if (!mutex_trylock(&prepare_lock)) { + if (prepare_owner == current) { + prepare_refcnt++; + return; + } + mutex_lock(&prepare_lock); + } + WARN_ON_ONCE(prepare_owner != NULL); + WARN_ON_ONCE(prepare_refcnt != 0); + prepare_owner = current; + prepare_refcnt = 1; } static void clk_prepare_unlock(void) { + WARN_ON_ONCE(prepare_owner != current); + WARN_ON_ONCE(prepare_refcnt == 0); + + if (--prepare_refcnt) + return; + prepare_owner = NULL; mutex_unlock(&prepare_lock); } static unsigned long clk_enable_lock(void) { unsigned long flags; - spin_lock_irqsave(&enable_lock, flags); + + if (!spin_trylock_irqsave(&enable_lock, flags)) { + if (enable_owner == current) { + enable_refcnt++; + return flags; + } + spin_lock_irqsave(&enable_lock, flags); + } + WARN_ON_ONCE(enable_owner != NULL); + WARN_ON_ONCE(enable_refcnt != 0); + enable_owner = current; + enable_refcnt = 1; return flags; } static void clk_enable_unlock(unsigned long flags) { + WARN_ON_ONCE(enable_owner != current); + WARN_ON_ONCE(enable_refcnt == 0); + + if (--enable_refcnt) + return; + enable_owner = NULL; spin_unlock_irqrestore(&enable_lock, flags); } -- cgit v1.2.3 From 3566d40c1a4617461b38c82059bdc41d622faa8b Mon Sep 17 00:00:00 2001 From: James Hogan Date: Mon, 25 Mar 2013 14:35:07 +0000 Subject: clk: fix clk_mux::flags kerneldoc The kerneldoc comment for struct clk_mux documented the non-existent num_clks instead of flags. Correct this. Signed-off-by: James Hogan Signed-off-by: Mike Turquette --- include/linux/clk-provider.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index 1f035280279..b1675074fe7 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -284,7 +284,7 @@ struct clk *clk_register_divider_table(struct device *dev, const char *name, * @reg: register controlling multiplexer * @shift: shift to multiplexer bit field * @width: width of mutliplexer bit field - * @num_clks: number of parent clocks + * @flags: hardware-specific flags * @lock: register lock * * Clock with multiple selectable parents. Implements .get_parent, .set_parent -- cgit v1.2.3 From f640c0fad698c0e4b07e05373681d3681125d6af Mon Sep 17 00:00:00 2001 From: Jean-Francois Moine Date: Tue, 2 Apr 2013 13:02:36 +0200 Subject: clk: mvebu: Use common of_clk_init() function The use common of_clk_init() function simplifies the clock initialization and adds handling of the DT "fixed-clock". Signed-off-by: Jean-Francois Moine Signed-off-by: Mike Turquette [mturquette@linaro.org: fixed $SUBJECT to reflect correct file path] --- drivers/clk/mvebu/clk-cpu.c | 17 ++--------------- drivers/clk/mvebu/clk-cpu.h | 22 ---------------------- drivers/clk/mvebu/clk.c | 6 +----- 3 files changed, 3 insertions(+), 42 deletions(-) delete mode 100644 drivers/clk/mvebu/clk-cpu.h diff --git a/drivers/clk/mvebu/clk-cpu.c b/drivers/clk/mvebu/clk-cpu.c index 9dd2551a0a4..b0fbc071549 100644 --- a/drivers/clk/mvebu/clk-cpu.c +++ b/drivers/clk/mvebu/clk-cpu.c @@ -16,7 +16,6 @@ #include #include #include -#include "clk-cpu.h" #define SYS_CTRL_CLK_DIVIDER_CTRL_OFFSET 0x0 #define SYS_CTRL_CLK_DIVIDER_VALUE_OFFSET 0xC @@ -173,17 +172,5 @@ clks_out: kfree(cpuclk); } -static const __initconst struct of_device_id clk_cpu_match[] = { - { - .compatible = "marvell,armada-xp-cpu-clock", - .data = of_cpu_clk_setup, - }, - { - /* sentinel */ - }, -}; - -void __init mvebu_cpu_clk_init(void) -{ - of_clk_init(clk_cpu_match); -} +CLK_OF_DECLARE(armada_xp_cpu_clock, "marvell,armada-xp-cpu-clock", + of_cpu_clk_setup); diff --git a/drivers/clk/mvebu/clk-cpu.h b/drivers/clk/mvebu/clk-cpu.h deleted file mode 100644 index 08e2affba4e..00000000000 --- a/drivers/clk/mvebu/clk-cpu.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Marvell MVEBU CPU clock handling. - * - * Copyright (C) 2012 Marvell - * - * Gregory CLEMENT - * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - */ - -#ifndef __MVEBU_CLK_CPU_H -#define __MVEBU_CLK_CPU_H - -#ifdef CONFIG_MVEBU_CLK_CPU -void __init mvebu_cpu_clk_init(void); -#else -static inline void mvebu_cpu_clk_init(void) {} -#endif - -#endif diff --git a/drivers/clk/mvebu/clk.c b/drivers/clk/mvebu/clk.c index 855681b8a9d..29f10fb3006 100644 --- a/drivers/clk/mvebu/clk.c +++ b/drivers/clk/mvebu/clk.c @@ -10,18 +10,14 @@ * warranty of any kind, whether express or implied. */ #include -#include #include -#include -#include #include #include "clk-core.h" -#include "clk-cpu.h" #include "clk-gating-ctrl.h" void __init mvebu_clocks_init(void) { mvebu_core_clk_init(); mvebu_gating_clk_init(); - mvebu_cpu_clk_init(); + of_clk_init(NULL); } -- cgit v1.2.3 From 056b205316cc3dcf8a67cf813a26ff8a72bf3cb9 Mon Sep 17 00:00:00 2001 From: Soren Brinkmann Date: Tue, 2 Apr 2013 15:36:56 -0700 Subject: clk: divider: Introduce CLK_DIVIDER_ALLOW_ZERO flag Dividers which have CLK_DIVIDER_ONE_BASED set have a redundant state, being a divider value of zero. Some hardware implementations allow a zero divider which simply doesn't alter the frequency. I.e. it acts like a divide by one or bypassing the divider. This flag is used to handle such HW in the clk-divider model. Signed-off-by: Soren Brinkmann Signed-off-by: Mike Turquette --- drivers/clk/clk-divider.c | 5 +++-- include/linux/clk-provider.h | 8 +++++++- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/drivers/clk/clk-divider.c b/drivers/clk/clk-divider.c index 68b40210117..6d967416043 100644 --- a/drivers/clk/clk-divider.c +++ b/drivers/clk/clk-divider.c @@ -109,8 +109,9 @@ static unsigned long clk_divider_recalc_rate(struct clk_hw *hw, div = _get_div(divider, val); if (!div) { - WARN(1, "%s: Invalid divisor for clock %s\n", __func__, - __clk_get_name(hw->clk)); + WARN(!(divider->flags & CLK_DIVIDER_ALLOW_ZERO), + "%s: Zero divisor and CLK_DIVIDER_ALLOW_ZERO not set\n", + __clk_get_name(hw->clk)); return parent_rate; } diff --git a/include/linux/clk-provider.h b/include/linux/clk-provider.h index b1675074fe7..9fdfae74d66 100644 --- a/include/linux/clk-provider.h +++ b/include/linux/clk-provider.h @@ -249,9 +249,14 @@ struct clk_div_table { * CLK_DIVIDER_ONE_BASED - by default the divisor is the value read from the * register plus one. If CLK_DIVIDER_ONE_BASED is set then the divider is * the raw value read from the register, with the value of zero considered - * invalid + * invalid, unless CLK_DIVIDER_ALLOW_ZERO is set. * CLK_DIVIDER_POWER_OF_TWO - clock divisor is 2 raised to the value read from * the hardware register + * CLK_DIVIDER_ALLOW_ZERO - Allow zero divisors. For dividers which have + * CLK_DIVIDER_ONE_BASED set, it is possible to end up with a zero divisor. + * Some hardware implementations gracefully handle this case and allow a + * zero divisor by not modifying their input clock + * (divide by one / bypass). */ struct clk_divider { struct clk_hw hw; @@ -265,6 +270,7 @@ struct clk_divider { #define CLK_DIVIDER_ONE_BASED BIT(0) #define CLK_DIVIDER_POWER_OF_TWO BIT(1) +#define CLK_DIVIDER_ALLOW_ZERO BIT(2) extern const struct clk_ops clk_divider_ops; struct clk *clk_register_divider(struct device *dev, const char *name, -- cgit v1.2.3 From 13569a709ad12aef4d9c2b352c92e95ab7dd201f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 27 Mar 2013 18:20:37 -0300 Subject: clk: sunxi: Add support for AXI, AHB, APB0 and APB1 gates MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This patchset adds DT support for all the AXI, AHB, APB0 and APB1 gates present on sunxi SoCs. Signed-off-by: Emilio López Reviewed-by: Gregory CLEMENT Signed-off-by: Mike Turquette --- Documentation/devicetree/bindings/clock/sunxi.txt | 109 +++++++++++++++++++++- drivers/clk/sunxi/clk-sunxi.c | 88 +++++++++++++++++ 2 files changed, 196 insertions(+), 1 deletion(-) diff --git a/Documentation/devicetree/bindings/clock/sunxi.txt b/Documentation/devicetree/bindings/clock/sunxi.txt index 20b8479c276..729f52426fe 100644 --- a/Documentation/devicetree/bindings/clock/sunxi.txt +++ b/Documentation/devicetree/bindings/clock/sunxi.txt @@ -10,15 +10,23 @@ Required properties: "allwinner,sun4i-pll1-clk" - for the main PLL clock "allwinner,sun4i-cpu-clk" - for the CPU multiplexer clock "allwinner,sun4i-axi-clk" - for the AXI clock + "allwinner,sun4i-axi-gates-clk" - for the AXI gates "allwinner,sun4i-ahb-clk" - for the AHB clock + "allwinner,sun4i-ahb-gates-clk" - for the AHB gates "allwinner,sun4i-apb0-clk" - for the APB0 clock + "allwinner,sun4i-apb0-gates-clk" - for the APB0 gates "allwinner,sun4i-apb1-clk" - for the APB1 clock "allwinner,sun4i-apb1-mux-clk" - for the APB1 clock muxing + "allwinner,sun4i-apb1-gates-clk" - for the APB1 gates Required properties for all clocks: - reg : shall be the control register address for the clock. - clocks : shall be the input parent clock(s) phandle for the clock -- #clock-cells : from common clock binding; shall be set to 0. +- #clock-cells : from common clock binding; shall be set to 0 except for + "allwinner,sun4i-*-gates-clk" where it shall be set to 1 + +Additionally, "allwinner,sun4i-*-gates-clk" clocks require: +- clock-output-names : the corresponding gate names that the clock controls For example: @@ -42,3 +50,102 @@ cpu: cpu@01c20054 { reg = <0x01c20054 0x4>; clocks = <&osc32k>, <&osc24M>, <&pll1>; }; + + + +Gate clock outputs + +The "allwinner,sun4i-*-gates-clk" clocks provide several gatable outputs; +their corresponding offsets as present on sun4i are listed below. Note that +some of these gates are not present on sun5i. + + * AXI gates ("allwinner,sun4i-axi-gates-clk") + + DRAM 0 + + * AHB gates ("allwinner,sun4i-ahb-gates-clk") + + USB0 0 + EHCI0 1 + OHCI0 2* + EHCI1 3 + OHCI1 4* + SS 5 + DMA 6 + BIST 7 + MMC0 8 + MMC1 9 + MMC2 10 + MMC3 11 + MS 12** + NAND 13 + SDRAM 14 + + ACE 16 + EMAC 17 + TS 18 + + SPI0 20 + SPI1 21 + SPI2 22 + SPI3 23 + PATA 24 + SATA 25** + GPS 26* + + VE 32 + TVD 33 + TVE0 34 + TVE1 35 + LCD0 36 + LCD1 37 + + CSI0 40 + CSI1 41 + + HDMI 43 + DE_BE0 44 + DE_BE1 45 + DE_FE0 46 + DE_FE1 47 + + MP 50 + + MALI400 52 + + * APB0 gates ("allwinner,sun4i-apb0-gates-clk") + + CODEC 0 + SPDIF 1* + AC97 2 + IIS 3 + + PIO 5 + IR0 6 + IR1 7 + + KEYPAD 10 + + * APB1 gates ("allwinner,sun4i-apb1-gates-clk") + + I2C0 0 + I2C1 1 + I2C2 2 + + CAN 4 + SCR 5 + PS20 6 + PS21 7 + + UART0 16 + UART1 17 + UART2 18 + UART3 19 + UART4 20 + UART5 21 + UART6 22 + UART7 23 + +Notation: + [*]: The datasheet didn't mention these, but they are present on AW code + [**]: The datasheet had this marked as "NC" but they are used on AW code diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index d528a249669..244de90d536 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -302,6 +302,82 @@ static void __init sunxi_divider_clk_setup(struct device_node *node, } + +/** + * sunxi_gates_clk_setup() - Setup function for leaf gates on clocks + */ + +#define SUNXI_GATES_MAX_SIZE 64 + +struct gates_data { + DECLARE_BITMAP(mask, SUNXI_GATES_MAX_SIZE); +}; + +static const __initconst struct gates_data axi_gates_data = { + .mask = {1}, +}; + +static const __initconst struct gates_data ahb_gates_data = { + .mask = {0x7F77FFF, 0x14FB3F}, +}; + +static const __initconst struct gates_data apb0_gates_data = { + .mask = {0x4EF}, +}; + +static const __initconst struct gates_data apb1_gates_data = { + .mask = {0xFF00F7}, +}; + +static void __init sunxi_gates_clk_setup(struct device_node *node, + struct gates_data *data) +{ + struct clk_onecell_data *clk_data; + const char *clk_parent; + const char *clk_name; + void *reg; + int qty; + int i = 0; + int j = 0; + int ignore; + + reg = of_iomap(node, 0); + + clk_parent = of_clk_get_parent_name(node, 0); + + /* Worst-case size approximation and memory allocation */ + qty = find_last_bit(data->mask, SUNXI_GATES_MAX_SIZE); + clk_data = kmalloc(sizeof(struct clk_onecell_data), GFP_KERNEL); + if (!clk_data) + return; + clk_data->clks = kzalloc((qty+1) * sizeof(struct clk *), GFP_KERNEL); + if (!clk_data->clks) { + kfree(clk_data); + return; + } + + for_each_set_bit(i, data->mask, SUNXI_GATES_MAX_SIZE) { + of_property_read_string_index(node, "clock-output-names", + j, &clk_name); + + /* No driver claims this clock, but it should remain gated */ + ignore = !strcmp("ahb_sdram", clk_name) ? CLK_IGNORE_UNUSED : 0; + + clk_data->clks[i] = clk_register_gate(NULL, clk_name, + clk_parent, ignore, + reg + 4 * (i/32), i % 32, + 0, &clk_lock); + WARN_ON(IS_ERR(clk_data->clks[i])); + + j++; + } + + /* Adjust to the real max */ + clk_data->clk_num = i; + + of_clk_add_provider(node, of_clk_src_onecell_get, clk_data); +} + /* Matches for of_clk_init */ static const __initconst struct of_device_id clk_match[] = { {.compatible = "fixed-clock", .data = of_fixed_clk_setup,}, @@ -331,6 +407,15 @@ static const __initconst struct of_device_id clk_mux_match[] = { {} }; +/* Matches for gate clocks */ +static const __initconst struct of_device_id clk_gates_match[] = { + {.compatible = "allwinner,sun4i-axi-gates-clk", .data = &axi_gates_data,}, + {.compatible = "allwinner,sun4i-ahb-gates-clk", .data = &ahb_gates_data,}, + {.compatible = "allwinner,sun4i-apb0-gates-clk", .data = &apb0_gates_data,}, + {.compatible = "allwinner,sun4i-apb1-gates-clk", .data = &apb1_gates_data,}, + {} +}; + static void __init of_sunxi_table_clock_setup(const struct of_device_id *clk_match, void *function) { @@ -359,4 +444,7 @@ void __init sunxi_init_clocks(void) /* Register mux clocks */ of_sunxi_table_clock_setup(clk_mux_match, sunxi_mux_clk_setup); + + /* Register gate clocks */ + of_sunxi_table_clock_setup(clk_gates_match, sunxi_gates_clk_setup); } -- cgit v1.2.3 From 5a4fe9b55db4ec5b6627245a91445c6495a136df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 27 Mar 2013 18:20:42 -0300 Subject: clk: sunxi: drop CLK_IGNORE_UNUSED MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This flag was in place to prevent important clocks from getting gated while they had no users. Now that the UART driver supports clocks properly, we can drop this. Signed-off-by: Emilio López Reviewed-by: Gregory CLEMENT Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index 244de90d536..daa51ab9701 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -41,8 +41,8 @@ static void __init sunxi_osc_clk_setup(struct device_node *node) parent = of_clk_get_parent_name(node, 0); - clk = clk_register_gate(NULL, clk_name, parent, CLK_IGNORE_UNUSED, - reg, SUNXI_OSC24M_GATE, 0, &clk_lock); + clk = clk_register_gate(NULL, clk_name, parent, 0, reg, + SUNXI_OSC24M_GATE, 0, &clk_lock); if (clk) { of_clk_add_provider(node, of_clk_src_simple_get, clk); @@ -198,8 +198,8 @@ static void __init sunxi_factors_clk_setup(struct device_node *node, parent = of_clk_get_parent_name(node, 0); - clk = clk_register_factors(NULL, clk_name, parent, CLK_IGNORE_UNUSED, - reg, data->table, data->getter, &clk_lock); + clk = clk_register_factors(NULL, clk_name, parent, 0, reg, + data->table, data->getter, &clk_lock); if (clk) { of_clk_add_provider(node, of_clk_src_simple_get, clk); -- cgit v1.2.3 From 918d7f6f68620e0721bb31402ebf87e15f826831 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Emilio=20L=C3=B3pez?= Date: Wed, 27 Mar 2013 18:20:43 -0300 Subject: clk: sunxi: drop an unnecesary kmalloc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit clk_register will copy this information, so we can just use a normal array and do one less dynamic allocation. Signed-off-by: Emilio López Reviewed-by: Gregory CLEMENT Signed-off-by: Mike Turquette --- drivers/clk/sunxi/clk-sunxi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/clk/sunxi/clk-sunxi.c b/drivers/clk/sunxi/clk-sunxi.c index daa51ab9701..0bb0eb4ed21 100644 --- a/drivers/clk/sunxi/clk-sunxi.c +++ b/drivers/clk/sunxi/clk-sunxi.c @@ -232,7 +232,7 @@ static void __init sunxi_mux_clk_setup(struct device_node *node, { struct clk *clk; const char *clk_name = node->name; - const char **parents = kmalloc(sizeof(char *) * 5, GFP_KERNEL); + const char *parents[5]; void *reg; int i = 0; -- cgit v1.2.3