Add a clock driver for banks of clock muxes in the TZ1090 SoC. A single 32 bit register controls up to 32 separate clock muxes. The generic clock mux clock operations are wrapped in order to acquire and release the Meta global exclusive lock (__global_lock2) to ensure atomicity with other non-Linux cores and threads which may need to control some of the clocks. The tz1090_clk_register_mux_bank() helper function can be used to register a mux bank from static initialisation data. Macros are provided in tz1090/clk.h to aid the creation of this data, for example: MUX_BANK(tz1090_top_clkswitch, CLK_TOP_CLKSWITCH_BASE, TOP_CLKSWITCH, /* bit in[0] in[1] out */ MUX( 0, "@xtal1" "@xtal2", "sys_sw") MUX( 1, "@xtal1" "sys_div", "sys_x2_undeleted") MUX( 2, "@xtal1" "@afe_progdiv1", "out0_sw0") ... ); ... tz1090_clk_register_mux_bank(p, &tz1090_top_clkswitch); Signed-off-by: James Hogan <james.hogan@xxxxxxxxxx> Cc: Mike Turquette <mturquette@xxxxxxxxxx> Cc: linux-metag@xxxxxxxxxxxxxxx --- Changes since v1 (patch 8): - Renamed function prefixes from clk_tz1090_ to tz1090_clk_ for consistency with the rest. - Drop DT binding as it will be instantiated directly from a provider. - Add tz1090_clk_register_mux_bank() to conveniently register a bank of gates in a clock provider from static initilisation data. - Extend tz1090/clk.h interface for easy static initialisation with macros. --- drivers/clk/tz1090/Makefile | 1 + drivers/clk/tz1090/clk-tz1090-mux-bank.c | 139 +++++++++++++++++++++++++++++++ drivers/clk/tz1090/clk.h | 52 ++++++++++++ 3 files changed, 192 insertions(+) create mode 100644 drivers/clk/tz1090/clk-tz1090-mux-bank.c diff --git a/drivers/clk/tz1090/Makefile b/drivers/clk/tz1090/Makefile index ab88f53..d44acac 100644 --- a/drivers/clk/tz1090/Makefile +++ b/drivers/clk/tz1090/Makefile @@ -2,3 +2,4 @@ obj-y += clk.o obj-y += clk-tz1090-gate-bank.o +obj-y += clk-tz1090-mux-bank.o diff --git a/drivers/clk/tz1090/clk-tz1090-mux-bank.c b/drivers/clk/tz1090/clk-tz1090-mux-bank.c new file mode 100644 index 0000000..0c7732d --- /dev/null +++ b/drivers/clk/tz1090/clk-tz1090-mux-bank.c @@ -0,0 +1,139 @@ +/* + * Copyright (C) 2013-2014 Imagination Technologies Ltd. + * + * 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. + * + * TZ1090 Clock mux bank + */ + +#include <linux/clk-provider.h> +#include <linux/err.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/slab.h> +#include <asm/global_lock.h> + +#include "clk.h" + +/** + * struct tz1090_clk_mux_priv - tz1090 multiplexer clock + * + * @mux: the parent class + * @ops: pointer to clk_ops of parent class + * + * Clock with multiple selectable parents. Extends basic mux by using a global + * exclusive lock when read-modify-writing the mux field so that multiple + * threads/cores can use different fields in the same register. + */ +struct tz1090_clk_mux_priv { + struct clk_mux mux; + const struct clk_ops *ops; +}; + +static inline struct tz1090_clk_mux_priv *to_tz1090_clk_mux(struct clk_hw *hw) +{ + struct clk_mux *mux = container_of(hw, struct clk_mux, hw); + + return container_of(mux, struct tz1090_clk_mux_priv, mux); +} + +static u8 tz1090_clk_mux_get_parent(struct clk_hw *hw) +{ + struct tz1090_clk_mux_priv *mux = to_tz1090_clk_mux(hw); + + return mux->ops->get_parent(&mux->mux.hw); +} + +/* Acquire exclusive lock since other cores may access the same register */ +static int tz1090_clk_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct tz1090_clk_mux_priv *mux = to_tz1090_clk_mux(hw); + int ret; + unsigned long flags; + + __global_lock2(flags); + ret = mux->ops->set_parent(&mux->mux.hw, index); + __global_unlock2(flags); + + return ret; +} + +static long tz1090_clk_mux_determine_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate, + struct clk **best_parent) +{ + struct tz1090_clk_mux_priv *mux = to_tz1090_clk_mux(hw); + + return mux->ops->determine_rate(&mux->mux.hw, rate, prate, best_parent); +} + +static const struct clk_ops tz1090_clk_mux_ops = { + .get_parent = tz1090_clk_mux_get_parent, + .set_parent = tz1090_clk_mux_set_parent, + .determine_rate = tz1090_clk_mux_determine_rate, +}; + +static struct clk *__init __register_mux(const char *name, + const char **parent_names, + unsigned long flags, void __iomem *reg, + u8 shift, u8 clk_mux_flags) +{ + struct tz1090_clk_mux_priv *mux; + struct clk *clk; + struct clk_init_data init; + + /* allocate the mux */ + mux = kzalloc(sizeof(struct tz1090_clk_mux_priv), GFP_KERNEL); + if (!mux) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &tz1090_clk_mux_ops; + init.flags = flags | CLK_IS_BASIC; + init.parent_names = parent_names; + init.num_parents = 2; + + /* struct clk_mux assignments */ + mux->mux.reg = reg; + mux->mux.shift = shift; + mux->mux.mask = 0x1; + mux->mux.flags = clk_mux_flags; + mux->mux.hw.init = &init; + + /* struct tz1090_clk_mux_priv assignments */ + mux->ops = &clk_mux_ops; + + clk = clk_register(NULL, &mux->mux.hw); + + if (IS_ERR(clk)) + kfree(mux); + + return clk; +} + +/** + * tz1090_clk_register_mux_bank() - Register bank of muxes with a provider. + * @p: TZ1090 clock provider. + * @bank: Data describing mux bank. + */ +void __init tz1090_clk_register_mux_bank(struct tz1090_clk_provider *p, + const struct tz1090_clk_mux_bank *bank) +{ + const struct tz1090_clk_mux *mux; + struct clk *clk; + unsigned int id = bank->id_base; + const char *parents[2]; + + for (mux = bank->muxes; mux->name; ++mux, ++id) { + parents[0] = tz1090_clk_xlate(p, mux->parents[0]); + parents[1] = tz1090_clk_xlate(p, mux->parents[1]); + clk = __register_mux(tz1090_clk_xlate(p, mux->name), parents, + CLK_SET_RATE_PARENT, + p->base + bank->reg_base, mux->shift, 0); + p->clk_data.clks[id] = clk; + } +} diff --git a/drivers/clk/tz1090/clk.h b/drivers/clk/tz1090/clk.h index 06274b7..c9fe6ca 100644 --- a/drivers/clk/tz1090/clk.h +++ b/drivers/clk/tz1090/clk.h @@ -83,4 +83,56 @@ struct tz1090_clk_gate_bank { void tz1090_clk_register_gate_bank(struct tz1090_clk_provider *p, const struct tz1090_clk_gate_bank *bank); + +/* Clock mux banks */ + +/** + * struct tz1090_clk_mux - Describes an individual mux in a bank. + * @shift: Shift of bit controlling mux within the bank control register. + * @name: Name of muxed clock to provide. + * @parents: Name of two parent/source clocks for when the bit is 0 and 1. + */ +struct tz1090_clk_mux { + unsigned int shift; + const char *name; + const char *parents[2]; +}; + +/** + * struct tz1090_clk_mux_bank - Describes a mux bank. + * @id_base: Base id of bank in provider. + * Individual muxes get the id id_base + index in muxes array. + * @reg_base: Offset of mux bank register in the MMIO region. + * @muxes: Pointer to array of muxes in the bank, terminated by one with a + * NULL name field. + */ +struct tz1090_clk_mux_bank { + unsigned int id_base; + unsigned long reg_base; + const struct tz1090_clk_mux *muxes; +}; + +#define MUX(_shift, _parent0, _parent1, _name) \ + { \ + .shift = (_shift), \ + .name = (_name), \ + .parents = { \ + _parent0, \ + _parent1 \ + }, \ + }, + +#define MUX_BANK(_name, _id, _reg, _muxes) \ + static const struct tz1090_clk_mux_bank _name __initconst = { \ + .id_base = (_id), \ + .reg_base = (_reg), \ + .muxes = (const struct tz1090_clk_mux[]) { \ + _muxes \ + { .name = NULL } \ + }, \ + } + +void tz1090_clk_register_mux_bank(struct tz1090_clk_provider *p, + const struct tz1090_clk_mux_bank *bank); + #endif -- 2.0.4 -- To unsubscribe from this list: send the line "unsubscribe linux-metag" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html