diff options
author | Shawn Guo <shawn.guo@freescale.com> | 2014-08-26 15:06:33 +0800 |
---|---|---|
committer | Shawn Guo <shawn.guo@freescale.com> | 2014-09-16 10:06:48 +0800 |
commit | 19d863446a6b75b2f97b3012acf67c40b9f2ea1f (patch) | |
tree | 2e5c01c62795c0e0a2d33ba5f2f2bb0f00a2b43d | |
parent | bd404b1d337b960c44b75fcb01e3170f1d41ae80 (diff) | |
download | blackbird-op-linux-19d863446a6b75b2f97b3012acf67c40b9f2ea1f.tar.gz blackbird-op-linux-19d863446a6b75b2f97b3012acf67c40b9f2ea1f.zip |
ARM: imx: add an exclusive gate clock type
There are a couple of gate clocks are mutually exclusive on i.MX6, i.e.
LVDSCLK1_IBEN and LVDSCLK1_OBEN. They cannot be enabled simultaneously.
This patches adds an exclusive gate clock type specifically for such
case. The clock driver will need to call imx_clk_gate_exclusive() to
register a gate clock with parameter exclusive_mask indicating the mask
of gate bits which are mutually exclusive to this gate clock.
Right now, it only handles the exclusive gate clocks which are defined
in a single hardware register, which is the case we're running into
today. But it can be extended to handle exclusive gate clocks defined
in different registers later if needed.
Signed-off-by: Shawn Guo <shawn.guo@freescale.com>
-rw-r--r-- | arch/arm/mach-imx/Makefile | 3 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk-gate-exclusive.c | 94 | ||||
-rw-r--r-- | arch/arm/mach-imx/clk.h | 3 |
3 files changed, 99 insertions, 1 deletions
diff --git a/arch/arm/mach-imx/Makefile b/arch/arm/mach-imx/Makefile index ef215d108e4d..6e4fcd8339cd 100644 --- a/arch/arm/mach-imx/Makefile +++ b/arch/arm/mach-imx/Makefile @@ -16,7 +16,8 @@ obj-$(CONFIG_SOC_IMX5) += cpu-imx5.o clk-imx51-imx53.o $(imx5-pm-y) 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-mux.o + clk-fixup-div.o clk-fixup-mux.o \ + clk-gate-exclusive.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-gate-exclusive.c b/arch/arm/mach-imx/clk-gate-exclusive.c new file mode 100644 index 000000000000..c12f5f2e04dc --- /dev/null +++ b/arch/arm/mach-imx/clk-gate-exclusive.c @@ -0,0 +1,94 @@ +/* + * Copyright 2014 Freescale Semiconductor, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/slab.h> +#include "clk.h" + +/** + * struct clk_gate_exclusive - i.MX specific gate clock which is mutually + * exclusive with other gate clocks + * + * @gate: the parent class + * @exclusive_mask: mask of gate bits which are mutually exclusive to this + * gate clock + * + * The imx exclusive gate clock is a subclass of basic clk_gate + * with an addtional mask to indicate which other gate bits in the same + * register is mutually exclusive to this gate clock. + */ +struct clk_gate_exclusive { + struct clk_gate gate; + u32 exclusive_mask; +}; + +static int clk_gate_exclusive_enable(struct clk_hw *hw) +{ + struct clk_gate *gate = container_of(hw, struct clk_gate, hw); + struct clk_gate_exclusive *exgate = container_of(gate, + struct clk_gate_exclusive, gate); + u32 val = readl(gate->reg); + + if (val & exgate->exclusive_mask) + return -EBUSY; + + return clk_gate_ops.enable(hw); +} + +static void clk_gate_exclusive_disable(struct clk_hw *hw) +{ + clk_gate_ops.disable(hw); +} + +static int clk_gate_exclusive_is_enabled(struct clk_hw *hw) +{ + return clk_gate_ops.is_enabled(hw); +} + +static const struct clk_ops clk_gate_exclusive_ops = { + .enable = clk_gate_exclusive_enable, + .disable = clk_gate_exclusive_disable, + .is_enabled = clk_gate_exclusive_is_enabled, +}; + +struct clk *imx_clk_gate_exclusive(const char *name, const char *parent, + void __iomem *reg, u8 shift, u32 exclusive_mask) +{ + struct clk_gate_exclusive *exgate; + struct clk_gate *gate; + struct clk *clk; + struct clk_init_data init; + + if (exclusive_mask == 0) + return ERR_PTR(-EINVAL); + + exgate = kzalloc(sizeof(*exgate), GFP_KERNEL); + if (!exgate) + return ERR_PTR(-ENOMEM); + gate = &exgate->gate; + + init.name = name; + init.ops = &clk_gate_exclusive_ops; + init.flags = CLK_SET_RATE_PARENT; + init.parent_names = parent ? &parent : NULL; + init.num_parents = parent ? 1 : 0; + + gate->reg = reg; + gate->bit_idx = shift; + gate->lock = &imx_ccm_lock; + gate->hw.init = &init; + exgate->exclusive_mask = exclusive_mask; + + clk = clk_register(NULL, &gate->hw); + if (IS_ERR(clk)) + kfree(exgate); + + return clk; +} diff --git a/arch/arm/mach-imx/clk.h b/arch/arm/mach-imx/clk.h index d5ba76fee115..4cdf8b6a74e8 100644 --- a/arch/arm/mach-imx/clk.h +++ b/arch/arm/mach-imx/clk.h @@ -36,6 +36,9 @@ struct clk *clk_register_gate2(struct device *dev, const char *name, struct clk * imx_obtain_fixed_clock( const char *name, unsigned long rate); +struct clk *imx_clk_gate_exclusive(const char *name, const char *parent, + void __iomem *reg, u8 shift, u32 exclusive_mask); + static inline struct clk *imx_clk_gate2(const char *name, const char *parent, void __iomem *reg, u8 shift) { |