xref: /openbmc/linux/drivers/clk/imx/clk-fixup-mux.c (revision 7d6b5e4f2445728b9eca71f3fc21fd453d6b36b7)
1fcaf2036SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-or-later
211f68120SShawn Guo /*
311f68120SShawn Guo  * Copyright (C) 2013 Freescale Semiconductor, Inc.
411f68120SShawn Guo  */
511f68120SShawn Guo 
6*7d6b5e4fSAnson 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 
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 
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 
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 = {
6311f68120SShawn Guo 	.get_parent = clk_fixup_mux_get_parent,
6411f68120SShawn Guo 	.set_parent = clk_fixup_mux_set_parent,
6511f68120SShawn Guo };
6611f68120SShawn Guo 
673ead0f1eSAbel Vesa struct clk_hw *imx_clk_hw_fixup_mux(const char *name, void __iomem *reg,
689e5ef7a5SA.s. Dong 			      u8 shift, u8 width, const char * const *parents,
6911f68120SShawn Guo 			      int num_parents, void (*fixup)(u32 *val))
7011f68120SShawn Guo {
7111f68120SShawn Guo 	struct clk_fixup_mux *fixup_mux;
723ead0f1eSAbel Vesa 	struct clk_hw *hw;
7311f68120SShawn Guo 	struct clk_init_data init;
743ead0f1eSAbel Vesa 	int ret;
7511f68120SShawn Guo 
7611f68120SShawn Guo 	if (!fixup)
7711f68120SShawn Guo 		return ERR_PTR(-EINVAL);
7811f68120SShawn Guo 
7911f68120SShawn Guo 	fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL);
8011f68120SShawn Guo 	if (!fixup_mux)
8111f68120SShawn Guo 		return ERR_PTR(-ENOMEM);
8211f68120SShawn Guo 
8311f68120SShawn Guo 	init.name = name;
8411f68120SShawn Guo 	init.ops = &clk_fixup_mux_ops;
8511f68120SShawn Guo 	init.parent_names = parents;
8611f68120SShawn Guo 	init.num_parents = num_parents;
8711f68120SShawn Guo 	init.flags = 0;
8811f68120SShawn Guo 
8911f68120SShawn Guo 	fixup_mux->mux.reg = reg;
9011f68120SShawn Guo 	fixup_mux->mux.shift = shift;
9111f68120SShawn Guo 	fixup_mux->mux.mask = BIT(width) - 1;
9211f68120SShawn Guo 	fixup_mux->mux.lock = &imx_ccm_lock;
9311f68120SShawn Guo 	fixup_mux->mux.hw.init = &init;
9411f68120SShawn Guo 	fixup_mux->ops = &clk_mux_ops;
9511f68120SShawn Guo 	fixup_mux->fixup = fixup;
9611f68120SShawn Guo 
973ead0f1eSAbel Vesa 	hw = &fixup_mux->mux.hw;
9811f68120SShawn Guo 
993ead0f1eSAbel Vesa 	ret = clk_hw_register(NULL, hw);
1003ead0f1eSAbel Vesa 	if (ret) {
1013ead0f1eSAbel Vesa 		kfree(fixup_mux);
1023ead0f1eSAbel Vesa 		return ERR_PTR(ret);
1033ead0f1eSAbel Vesa 	}
1043ead0f1eSAbel Vesa 
1053ead0f1eSAbel Vesa 	return hw;
10611f68120SShawn Guo }
107