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