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/clk-provider.h>
0007 #include <linux/err.h>
0008 #include <linux/io.h>
0009 #include <linux/slab.h>
0010 #include "clk.h"
0011 
0012 #define div_mask(d) ((1 << (d->width)) - 1)
0013 
0014 /**
0015  * struct clk_fixup_div - imx integer fixup divider clock
0016  * @divider: the parent class
0017  * @ops: pointer to clk_ops of parent class
0018  * @fixup: a hook to fixup the write value
0019  *
0020  * The imx fixup divider clock is a subclass of basic clk_divider
0021  * with an addtional fixup hook.
0022  */
0023 struct clk_fixup_div {
0024     struct clk_divider divider;
0025     const struct clk_ops *ops;
0026     void (*fixup)(u32 *val);
0027 };
0028 
0029 static inline struct clk_fixup_div *to_clk_fixup_div(struct clk_hw *hw)
0030 {
0031     struct clk_divider *divider = to_clk_divider(hw);
0032 
0033     return container_of(divider, struct clk_fixup_div, divider);
0034 }
0035 
0036 static unsigned long clk_fixup_div_recalc_rate(struct clk_hw *hw,
0037                      unsigned long parent_rate)
0038 {
0039     struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
0040 
0041     return fixup_div->ops->recalc_rate(&fixup_div->divider.hw, parent_rate);
0042 }
0043 
0044 static long clk_fixup_div_round_rate(struct clk_hw *hw, unsigned long rate,
0045                    unsigned long *prate)
0046 {
0047     struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
0048 
0049     return fixup_div->ops->round_rate(&fixup_div->divider.hw, rate, prate);
0050 }
0051 
0052 static int clk_fixup_div_set_rate(struct clk_hw *hw, unsigned long rate,
0053                 unsigned long parent_rate)
0054 {
0055     struct clk_fixup_div *fixup_div = to_clk_fixup_div(hw);
0056     struct clk_divider *div = to_clk_divider(hw);
0057     unsigned int divider, value;
0058     unsigned long flags;
0059     u32 val;
0060 
0061     divider = parent_rate / rate;
0062 
0063     /* Zero based divider */
0064     value = divider - 1;
0065 
0066     if (value > div_mask(div))
0067         value = div_mask(div);
0068 
0069     spin_lock_irqsave(div->lock, flags);
0070 
0071     val = readl(div->reg);
0072     val &= ~(div_mask(div) << div->shift);
0073     val |= value << div->shift;
0074     fixup_div->fixup(&val);
0075     writel(val, div->reg);
0076 
0077     spin_unlock_irqrestore(div->lock, flags);
0078 
0079     return 0;
0080 }
0081 
0082 static const struct clk_ops clk_fixup_div_ops = {
0083     .recalc_rate = clk_fixup_div_recalc_rate,
0084     .round_rate = clk_fixup_div_round_rate,
0085     .set_rate = clk_fixup_div_set_rate,
0086 };
0087 
0088 struct clk_hw *imx_clk_hw_fixup_divider(const char *name, const char *parent,
0089                   void __iomem *reg, u8 shift, u8 width,
0090                   void (*fixup)(u32 *val))
0091 {
0092     struct clk_fixup_div *fixup_div;
0093     struct clk_hw *hw;
0094     struct clk_init_data init;
0095     int ret;
0096 
0097     if (!fixup)
0098         return ERR_PTR(-EINVAL);
0099 
0100     fixup_div = kzalloc(sizeof(*fixup_div), GFP_KERNEL);
0101     if (!fixup_div)
0102         return ERR_PTR(-ENOMEM);
0103 
0104     init.name = name;
0105     init.ops = &clk_fixup_div_ops;
0106     init.flags = CLK_SET_RATE_PARENT;
0107     init.parent_names = parent ? &parent : NULL;
0108     init.num_parents = parent ? 1 : 0;
0109 
0110     fixup_div->divider.reg = reg;
0111     fixup_div->divider.shift = shift;
0112     fixup_div->divider.width = width;
0113     fixup_div->divider.lock = &imx_ccm_lock;
0114     fixup_div->divider.hw.init = &init;
0115     fixup_div->ops = &clk_divider_ops;
0116     fixup_div->fixup = fixup;
0117 
0118     hw = &fixup_div->divider.hw;
0119 
0120     ret = clk_hw_register(NULL, hw);
0121     if (ret) {
0122         kfree(fixup_div);
0123         return ERR_PTR(ret);
0124     }
0125 
0126     return hw;
0127 }