From a49e6c4b820488b79ada6da9ce609fa8611d3e00 Mon Sep 17 00:00:00 2001 From: Liu Ying Date: Thu, 4 Jul 2013 17:35:46 +0800 Subject: [PATCH] ARM: imx: add common clock support for fixup mux One register may have several fields to control some clocks. It is possible that the read/write values of some fields may map to different real functional values, so writing to the other fields in the same register may break a working clock tree. A real case is the aclk_podf field in the register 'CCM Serial Clock Multiplexer Register 1' of i.MX6Q/SDL SoC. This patch introduces a fixup hook for multiplexer clock which is called before writing a value to clock registers to support this kind of multiplexer clocks. Signed-off-by: Liu Ying Signed-off-by: Shawn Guo --- arch/arm/mach-imx/Makefile | 2 +- arch/arm/mach-imx/clk-fixup-mux.c | 107 ++++++++++++++++++++++++++++++ arch/arm/mach-imx/clk.h | 4 ++ 3 files changed, 112 insertions(+), 1 deletion(-) create mode 100644 arch/arm/mach-imx/clk-fixup-mux.c diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index 92b91a22eb4d..5383c589ad71 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -16,7 +16,7 @@ obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o mm-imx5.o clk-imx51-imx53.o ehci-imx5.o $(i obj-$(CONFIG_COMMON_CLK) += clk-pllv1.o clk-pllv2.o clk-pllv3.o clk-gate2.o \ clk-pfd.o clk-busy.o clk.o \ - clk-fixup-div.o + clk-fixup-div.o clk-fixup-mux.o obj-$(CONFIG_IMX_HAVE_IOMUX_V1) += iomux-v1.o obj-$(CONFIG_ARCH_MXC_IOMUX_V3) += iomux-v3.o diff --git a/arch/arm/mach-imx/clk-fixup-mux.c b/arch/arm/mach-imx/clk-fixup-mux.c new file mode 100644 index 000000000000..deb4b8093b30 --- /dev/null +++ b/arch/arm/mach-imx/clk-fixup-mux.c @@ -0,0 +1,107 @@ +/* + * Copyright (C) 2013 Freescale Semiconductor, Inc. + * + * The code contained herein is licensed under the GNU General Public + * License. You may obtain a copy of the GNU General Public License + * Version 2 or later at the following locations: + * + * http://www.opensource.org/licenses/gpl-license.html + * http://www.gnu.org/copyleft/gpl.html + */ + +#include +#include +#include +#include +#include "clk.h" + +#define to_clk_mux(_hw) container_of(_hw, struct clk_mux, hw) + +/** + * struct clk_fixup_mux - imx integer fixup multiplexer clock + * @mux: the parent class + * @ops: pointer to clk_ops of parent class + * @fixup: a hook to fixup the write value + * + * The imx fixup multiplexer clock is a subclass of basic clk_mux + * with an addtional fixup hook. + */ +struct clk_fixup_mux { + struct clk_mux mux; + const struct clk_ops *ops; + void (*fixup)(u32 *val); +}; + +static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw) +{ + struct clk_mux *mux = to_clk_mux(hw); + + return container_of(mux, struct clk_fixup_mux, mux); +} + +static u8 clk_fixup_mux_get_parent(struct clk_hw *hw) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + + return fixup_mux->ops->get_parent(&fixup_mux->mux.hw); +} + +static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw); + struct clk_mux *mux = to_clk_mux(hw); + unsigned long flags = 0; + u32 val; + + spin_lock_irqsave(mux->lock, flags); + + val = readl(mux->reg); + val &= ~(mux->mask << mux->shift); + val |= index << mux->shift; + fixup_mux->fixup(&val); + writel(val, mux->reg); + + spin_unlock_irqrestore(mux->lock, flags); + + return 0; +} + +static const struct clk_ops clk_fixup_mux_ops = { + .get_parent = clk_fixup_mux_get_parent, + .set_parent = clk_fixup_mux_set_parent, +}; + +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parents, + int num_parents, void (*fixup)(u32 *val)) +{ + struct clk_fixup_mux *fixup_mux; + struct clk *clk; + struct clk_init_data init; + + if (!fixup) + return ERR_PTR(-EINVAL); + + fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL); + if (!fixup_mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &clk_fixup_mux_ops; + init.parent_names = parents; + init.num_parents = num_parents; + + fixup_mux->mux.reg = reg; + fixup_mux->mux.shift = shift; + fixup_mux->mux.mask = BIT(width) - 1; + fixup_mux->mux.lock = &imx_ccm_lock; + fixup_mux->mux.hw.init = &init; + fixup_mux->ops = &clk_mux_ops; + fixup_mux->fixup = fixup; + + clk = clk_register(NULL, &fixup_mux->mux.hw); + if (IS_ERR(clk)) + kfree(fixup_mux); + + return clk; +} diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index 51eb38538cc4..2534359fdedc 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -53,6 +53,10 @@ struct clk *imx_clk_fixup_divider(const char *name, const char *parent, void __iomem *reg, u8 shift, u8 width, void (*fixup)(u32 *val)); +struct clk *imx_clk_fixup_mux(const char *name, void __iomem *reg, + u8 shift, u8 width, const char **parents, + int num_parents, void (*fixup)(u32 *val)); + static inline struct clk *imx_clk_fixed(const char *name, int rate) { return clk_register_fixed_rate(NULL, name, NULL, CLK_IS_ROOT, rate);