diff options
Diffstat (limited to 'drivers/clk/imx/clk-gpr-mux.c')
-rw-r--r-- | drivers/clk/imx/clk-gpr-mux.c | 114 |
1 files changed, 114 insertions, 0 deletions
diff --git a/drivers/clk/imx/clk-gpr-mux.c b/drivers/clk/imx/clk-gpr-mux.c new file mode 100644 index 0000000000..0e14b61cba --- /dev/null +++ b/drivers/clk/imx/clk-gpr-mux.c @@ -0,0 +1,114 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + */ + +#define pr_fmt(fmt) "imx:clk-gpr-mux: " fmt + +#include <linux/module.h> + +#include <linux/clk-provider.h> +#include <linux/errno.h> +#include <linux/export.h> +#include <linux/io.h> +#include <linux/slab.h> +#include <linux/regmap.h> +#include <linux/mfd/syscon.h> + +#include "clk.h" + +struct imx_clk_gpr { + struct clk_hw hw; + struct regmap *regmap; + u32 mask; + u32 reg; + const u32 *mux_table; +}; + +static struct imx_clk_gpr *to_imx_clk_gpr(struct clk_hw *hw) +{ + return container_of(hw, struct imx_clk_gpr, hw); +} + +static u8 imx_clk_gpr_mux_get_parent(struct clk_hw *hw) +{ + struct imx_clk_gpr *priv = to_imx_clk_gpr(hw); + unsigned int val; + int ret; + + ret = regmap_read(priv->regmap, priv->reg, &val); + if (ret) + goto get_parent_err; + + val &= priv->mask; + + ret = clk_mux_val_to_index(hw, priv->mux_table, 0, val); + if (ret < 0) + goto get_parent_err; + + return ret; + +get_parent_err: + pr_err("%s: failed to get parent (%pe)\n", + clk_hw_get_name(hw), ERR_PTR(ret)); + + /* return some realistic non negative value. Potentially we could + * give index to some dummy error parent. + */ + return 0; +} + +static int imx_clk_gpr_mux_set_parent(struct clk_hw *hw, u8 index) +{ + struct imx_clk_gpr *priv = to_imx_clk_gpr(hw); + unsigned int val = clk_mux_index_to_val(priv->mux_table, 0, index); + + return regmap_update_bits(priv->regmap, priv->reg, priv->mask, val); +} + +static const struct clk_ops imx_clk_gpr_mux_ops = { + .get_parent = imx_clk_gpr_mux_get_parent, + .set_parent = imx_clk_gpr_mux_set_parent, + .determine_rate = __clk_mux_determine_rate, +}; + +struct clk_hw *imx_clk_gpr_mux(const char *name, const char *compatible, + u32 reg, const char **parent_names, + u8 num_parents, const u32 *mux_table, u32 mask) +{ + struct clk_init_data init = { }; + struct imx_clk_gpr *priv; + struct regmap *regmap; + struct clk_hw *hw; + int ret; + + regmap = syscon_regmap_lookup_by_compatible(compatible); + if (IS_ERR(regmap)) { + pr_err("failed to find %s regmap\n", compatible); + return ERR_CAST(regmap); + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (!priv) + return ERR_PTR(-ENOMEM); + + init.name = name; + init.ops = &imx_clk_gpr_mux_ops; + init.parent_names = parent_names; + init.num_parents = num_parents; + init.flags = CLK_SET_RATE_GATE | CLK_SET_PARENT_GATE; + + priv->hw.init = &init; + priv->regmap = regmap; + priv->mux_table = mux_table; + priv->reg = reg; + priv->mask = mask; + + hw = &priv->hw; + ret = clk_hw_register(NULL, &priv->hw); + if (ret) { + kfree(priv); + hw = ERR_PTR(ret); + } + + return hw; +} |