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