1 // SPDX-License-Identifier: GPL-2.0
3 * Copyright (C) 2014 Intel Corporation
5 * Adjustable fractional divider clock implementation.
6 * Output rate = (m / n) * parent_rate.
7 * Uses rational best approximation algorithm.
10 #include <linux/clk-provider.h>
11 #include <linux/module.h>
12 #include <linux/device.h>
13 #include <linux/slab.h>
14 #include <linux/rational.h>
16 static inline u32 clk_fd_readl(struct clk_fractional_divider *fd)
18 if (fd->flags & CLK_FRAC_DIVIDER_BIG_ENDIAN)
19 return ioread32be(fd->reg);
21 return readl(fd->reg);
24 static inline void clk_fd_writel(struct clk_fractional_divider *fd, u32 val)
26 if (fd->flags & CLK_FRAC_DIVIDER_BIG_ENDIAN)
27 iowrite32be(val, fd->reg);
32 static unsigned long clk_fd_recalc_rate(struct clk_hw *hw,
33 unsigned long parent_rate)
35 struct clk_fractional_divider *fd = to_clk_fd(hw);
36 unsigned long flags = 0;
42 spin_lock_irqsave(fd->lock, flags);
46 val = clk_fd_readl(fd);
49 spin_unlock_irqrestore(fd->lock, flags);
53 m = (val & fd->mmask) >> fd->mshift;
54 n = (val & fd->nmask) >> fd->nshift;
56 if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
64 ret = (u64)parent_rate * m;
70 static void clk_fd_general_approximation(struct clk_hw *hw, unsigned long rate,
71 unsigned long *parent_rate,
72 unsigned long *m, unsigned long *n)
74 struct clk_fractional_divider *fd = to_clk_fd(hw);
78 * Get rate closer to *parent_rate to guarantee there is no overflow
79 * for m and n. In the result it will be the nearest rate left shifted
80 * by (scale - fd->nwidth) bits.
82 scale = fls_long(*parent_rate / rate - 1);
83 if (scale > fd->nwidth)
84 rate <<= scale - fd->nwidth;
86 rational_best_approximation(rate, *parent_rate,
87 GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
91 static long clk_fd_round_rate(struct clk_hw *hw, unsigned long rate,
92 unsigned long *parent_rate)
94 struct clk_fractional_divider *fd = to_clk_fd(hw);
98 if (!rate || (!clk_hw_can_set_rate_parent(hw) && rate >= *parent_rate))
101 if (fd->approximation)
102 fd->approximation(hw, rate, parent_rate, &m, &n);
104 clk_fd_general_approximation(hw, rate, parent_rate, &m, &n);
106 ret = (u64)*parent_rate * m;
112 static int clk_fd_set_rate(struct clk_hw *hw, unsigned long rate,
113 unsigned long parent_rate)
115 struct clk_fractional_divider *fd = to_clk_fd(hw);
116 unsigned long flags = 0;
120 rational_best_approximation(rate, parent_rate,
121 GENMASK(fd->mwidth - 1, 0), GENMASK(fd->nwidth - 1, 0),
124 if (fd->flags & CLK_FRAC_DIVIDER_ZERO_BASED) {
130 spin_lock_irqsave(fd->lock, flags);
134 val = clk_fd_readl(fd);
135 val &= ~(fd->mmask | fd->nmask);
136 val |= (m << fd->mshift) | (n << fd->nshift);
137 clk_fd_writel(fd, val);
140 spin_unlock_irqrestore(fd->lock, flags);
147 const struct clk_ops clk_fractional_divider_ops = {
148 .recalc_rate = clk_fd_recalc_rate,
149 .round_rate = clk_fd_round_rate,
150 .set_rate = clk_fd_set_rate,
152 EXPORT_SYMBOL_GPL(clk_fractional_divider_ops);
154 struct clk_hw *clk_hw_register_fractional_divider(struct device *dev,
155 const char *name, const char *parent_name, unsigned long flags,
156 void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
157 u8 clk_divider_flags, spinlock_t *lock)
159 struct clk_fractional_divider *fd;
160 struct clk_init_data init;
164 fd = kzalloc(sizeof(*fd), GFP_KERNEL);
166 return ERR_PTR(-ENOMEM);
169 init.ops = &clk_fractional_divider_ops;
171 init.parent_names = parent_name ? &parent_name : NULL;
172 init.num_parents = parent_name ? 1 : 0;
177 fd->mmask = GENMASK(mwidth - 1, 0) << mshift;
180 fd->nmask = GENMASK(nwidth - 1, 0) << nshift;
181 fd->flags = clk_divider_flags;
186 ret = clk_hw_register(dev, hw);
194 EXPORT_SYMBOL_GPL(clk_hw_register_fractional_divider);
196 struct clk *clk_register_fractional_divider(struct device *dev,
197 const char *name, const char *parent_name, unsigned long flags,
198 void __iomem *reg, u8 mshift, u8 mwidth, u8 nshift, u8 nwidth,
199 u8 clk_divider_flags, spinlock_t *lock)
203 hw = clk_hw_register_fractional_divider(dev, name, parent_name, flags,
204 reg, mshift, mwidth, nshift, nwidth, clk_divider_flags,
210 EXPORT_SYMBOL_GPL(clk_register_fractional_divider);
212 void clk_hw_unregister_fractional_divider(struct clk_hw *hw)
214 struct clk_fractional_divider *fd;
218 clk_hw_unregister(hw);