Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-or-later
0002 /*
0003  * Copyright (C) 2013 Freescale Semiconductor, Inc.
0004  */
0005 
0006 #include <linux/bits.h>
0007 #include <linux/clk-provider.h>
0008 #include <linux/err.h>
0009 #include <linux/io.h>
0010 #include <linux/slab.h>
0011 #include "clk.h"
0012 
0013 /**
0014  * struct clk_fixup_mux - imx integer fixup multiplexer clock
0015  * @mux: the parent class
0016  * @ops: pointer to clk_ops of parent class
0017  * @fixup: a hook to fixup the write value
0018  *
0019  * The imx fixup multiplexer clock is a subclass of basic clk_mux
0020  * with an addtional fixup hook.
0021  */
0022 struct clk_fixup_mux {
0023     struct clk_mux mux;
0024     const struct clk_ops *ops;
0025     void (*fixup)(u32 *val);
0026 };
0027 
0028 static inline struct clk_fixup_mux *to_clk_fixup_mux(struct clk_hw *hw)
0029 {
0030     struct clk_mux *mux = to_clk_mux(hw);
0031 
0032     return container_of(mux, struct clk_fixup_mux, mux);
0033 }
0034 
0035 static u8 clk_fixup_mux_get_parent(struct clk_hw *hw)
0036 {
0037     struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw);
0038 
0039     return fixup_mux->ops->get_parent(&fixup_mux->mux.hw);
0040 }
0041 
0042 static int clk_fixup_mux_set_parent(struct clk_hw *hw, u8 index)
0043 {
0044     struct clk_fixup_mux *fixup_mux = to_clk_fixup_mux(hw);
0045     struct clk_mux *mux = to_clk_mux(hw);
0046     unsigned long flags;
0047     u32 val;
0048 
0049     spin_lock_irqsave(mux->lock, flags);
0050 
0051     val = readl(mux->reg);
0052     val &= ~(mux->mask << mux->shift);
0053     val |= index << mux->shift;
0054     fixup_mux->fixup(&val);
0055     writel(val, mux->reg);
0056 
0057     spin_unlock_irqrestore(mux->lock, flags);
0058 
0059     return 0;
0060 }
0061 
0062 static const struct clk_ops clk_fixup_mux_ops = {
0063     .get_parent = clk_fixup_mux_get_parent,
0064     .set_parent = clk_fixup_mux_set_parent,
0065 };
0066 
0067 struct clk_hw *imx_clk_hw_fixup_mux(const char *name, void __iomem *reg,
0068                   u8 shift, u8 width, const char * const *parents,
0069                   int num_parents, void (*fixup)(u32 *val))
0070 {
0071     struct clk_fixup_mux *fixup_mux;
0072     struct clk_hw *hw;
0073     struct clk_init_data init;
0074     int ret;
0075 
0076     if (!fixup)
0077         return ERR_PTR(-EINVAL);
0078 
0079     fixup_mux = kzalloc(sizeof(*fixup_mux), GFP_KERNEL);
0080     if (!fixup_mux)
0081         return ERR_PTR(-ENOMEM);
0082 
0083     init.name = name;
0084     init.ops = &clk_fixup_mux_ops;
0085     init.parent_names = parents;
0086     init.num_parents = num_parents;
0087     init.flags = 0;
0088 
0089     fixup_mux->mux.reg = reg;
0090     fixup_mux->mux.shift = shift;
0091     fixup_mux->mux.mask = BIT(width) - 1;
0092     fixup_mux->mux.lock = &imx_ccm_lock;
0093     fixup_mux->mux.hw.init = &init;
0094     fixup_mux->ops = &clk_mux_ops;
0095     fixup_mux->fixup = fixup;
0096 
0097     hw = &fixup_mux->mux.hw;
0098 
0099     ret = clk_hw_register(NULL, hw);
0100     if (ret) {
0101         kfree(fixup_mux);
0102         return ERR_PTR(ret);
0103     }
0104 
0105     return hw;
0106 }