1fcaf2036SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
211f68120SShawn Guo /*
311f68120SShawn Guo * Copyright (C) 2013 Freescale Semiconductor, Inc.
411f68120SShawn Guo */
511f68120SShawn Guo
67d6b5e4fSAnson Huang #include <linux/bits.h>
711f68120SShawn Guo #include <linux/clk-provider.h>
811f68120SShawn Guo #include <linux/err.h>
911f68120SShawn Guo #include <linux/io.h>
1011f68120SShawn Guo #include <linux/slab.h>
1111f68120SShawn Guo #include "clk.h"
1211f68120SShawn Guo
1311f68120SShawn Guo /**
1411f68120SShawn Guo * struct clk_fixup_mux - imx integer fixup multiplexer clock
1511f68120SShawn Guo * @mux: the parent class
1611f68120SShawn Guo * @ops: pointer to clk_ops of parent class
1711f68120SShawn Guo * @fixup: a hook to fixup the write value
1811f68120SShawn Guo *
1911f68120SShawn Guo * The imx fixup multiplexer clock is a subclass of basic clk_mux
2011f68120SShawn Guo * with an addtional fixup hook.
2111f68120SShawn Guo */
2211f68120SShawn Guo struct clk_fixup_mux {
2311f68120SShawn Guo struct clk_mux mux;
2411f68120SShawn Guo const struct clk_ops *ops;
2511f68120SShawn Guo void (*fixup)(u32 *val);
2611f68120SShawn Guo };
2711f68120SShawn Guo
to_clk_fixup_mux(struct clk_hw * hw)2811f68120SShawn Guo static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw)
2911f68120SShawn Guo {
3011f68120SShawn Guo struct clk_mux *mux = to_clk_mux(hw);
3111f68120SShawn Guo
3211f68120SShawn Guo return container_of(mux, struct clk_fixup_mux, mux);
3311f68120SShawn Guo }
3411f68120SShawn Guo
clk_fixup_mux_get_parent(struct clk_hw * hw)3511f68120SShawn Guo static u8 clk_fixup_mux_get_parent(struct clk_hw *hw)
3611f68120SShawn Guo {
3711f68120SShawn Guo struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw);
3811f68120SShawn Guo
3911f68120SShawn Guo return fixup_mux->ops->get_parent(&fixup_mux->mux.hw);
4011f68120SShawn Guo }
4111f68120SShawn Guo
clk_fixup_mux_set_parent(struct clk_hw * hw,u8 index)4211f68120SShawn Guo static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index)
4311f68120SShawn Guo {
4411f68120SShawn Guo struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw);
4511f68120SShawn Guo struct clk_mux *mux = to_clk_mux(hw);
4679ccef69SAnson Huang unsigned long flags;
4711f68120SShawn Guo u32 val;
4811f68120SShawn Guo
4911f68120SShawn Guo spin_lock_irqsave(mux->lock, flags);
5011f68120SShawn Guo
5111f68120SShawn Guo val = readl(mux->reg);
5211f68120SShawn Guo val &= ~(mux->mask << mux->shift);
5311f68120SShawn Guo val |= index << mux->shift;
5411f68120SShawn Guo fixup_mux->fixup(&val);
5511f68120SShawn Guo writel(val, mux->reg);
5611f68120SShawn Guo
5711f68120SShawn Guo spin_unlock_irqrestore(mux->lock, flags);
5811f68120SShawn Guo
5911f68120SShawn Guo return 0;
6011f68120SShawn Guo }
6111f68120SShawn Guo
6211f68120SShawn Guo static const struct clk_ops clk_fixup_mux_ops = {
63*b2252ca6SMaxime Ripard .determine_rate = clk_hw_determine_rate_no_reparent,
6411f68120SShawn Guo .get_parent = clk_fixup_mux_get_parent,
6511f68120SShawn Guo .set_parent = clk_fixup_mux_set_parent,
6611f68120SShawn Guo };
6711f68120SShawn Guo
imx_clk_hw_fixup_mux(const char * name,void __iomem * reg,u8 shift,u8 width,const char * const * parents,int num_parents,void (* fixup)(u32 * val))683ead0f1eSAbel Vesa struct clk_hw *imx_clk_hw_fixup_mux(const char *name, void __iomem *reg,
699e5ef7a5SA.s. Dong u8 shift, u8 width, const char * const *parents,
7011f68120SShawn Guo int num_parents, void (*fixup)(u32 *val))
7111f68120SShawn Guo {
7211f68120SShawn Guo struct clk_fixup_mux *fixup_mux;
733ead0f1eSAbel Vesa struct clk_hw *hw;
7411f68120SShawn Guo struct clk_init_data init;
753ead0f1eSAbel Vesa int ret;
7611f68120SShawn Guo
7711f68120SShawn Guo if (!fixup)
7811f68120SShawn Guo return ERR_PTR(-EINVAL);
7911f68120SShawn Guo
8011f68120SShawn Guo fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL);
8111f68120SShawn Guo if (!fixup_mux)
8211f68120SShawn Guo return ERR_PTR(-ENOMEM);
8311f68120SShawn Guo
8411f68120SShawn Guo init.name = name;
8511f68120SShawn Guo init.ops = &clk_fixup_mux_ops;
8611f68120SShawn Guo init.parent_names = parents;
8711f68120SShawn Guo init.num_parents = num_parents;
8811f68120SShawn Guo init.flags = 0;
8911f68120SShawn Guo
9011f68120SShawn Guo fixup_mux->mux.reg = reg;
9111f68120SShawn Guo fixup_mux->mux.shift = shift;
9211f68120SShawn Guo fixup_mux->mux.mask = BIT(width) - 1;
9311f68120SShawn Guo fixup_mux->mux.lock = &imx_ccm_lock;
9411f68120SShawn Guo fixup_mux->mux.hw.init = &init;
9511f68120SShawn Guo fixup_mux->ops = &clk_mux_ops;
9611f68120SShawn Guo fixup_mux->fixup = fixup;
9711f68120SShawn Guo
983ead0f1eSAbel Vesa hw = &fixup_mux->mux.hw;
9911f68120SShawn Guo
1003ead0f1eSAbel Vesa ret = clk_hw_register(NULL, hw);
1013ead0f1eSAbel Vesa if (ret) {
1023ead0f1eSAbel Vesa kfree(fixup_mux);
1033ead0f1eSAbel Vesa return ERR_PTR(ret);
1043ead0f1eSAbel Vesa }
1053ead0f1eSAbel Vesa
1063ead0f1eSAbel Vesa return hw;
10711f68120SShawn Guo }
108