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>
12 #include <linux/module.h>
13 #include <linux/device.h>
14 #include <linux/slab.h>
15 #include <linux/rational.h>
17 static inline u32
clk_fd_readl(struct clk_fractional_divider
*fd
)
19 if (fd
->flags
& CLK_FRAC_DIVIDER_BIG_ENDIAN
)
20 return ioread32be(fd
->reg
);
22 return readl(fd
->reg
);
25 static inline void clk_fd_writel(struct clk_fractional_divider
*fd
, u32 val
)
27 if (fd
->flags
& CLK_FRAC_DIVIDER_BIG_ENDIAN
)
28 iowrite32be(val
, fd
->reg
);
33 static unsigned long clk_fd_recalc_rate(struct clk_hw
*hw
,
34 unsigned long parent_rate
)
36 struct clk_fractional_divider
*fd
= to_clk_fd(hw
);
37 unsigned long flags
= 0;
43 spin_lock_irqsave(fd
->lock
, flags
);
47 val
= clk_fd_readl(fd
);
50 spin_unlock_irqrestore(fd
->lock
, flags
);
54 m
= (val
& fd
->mmask
) >> fd
->mshift
;
55 n
= (val
& fd
->nmask
) >> fd
->nshift
;
57 if (fd
->flags
& CLK_FRAC_DIVIDER_ZERO_BASED
) {
65 ret
= (u64
)parent_rate
* m
;
71 static void clk_fd_general_approximation(struct clk_hw
*hw
, unsigned long rate
,
72 unsigned long *parent_rate
,
73 unsigned long *m
, unsigned long *n
)
75 struct clk_fractional_divider
*fd
= to_clk_fd(hw
);
79 * Get rate closer to *parent_rate to guarantee there is no overflow
80 * for m and n. In the result it will be the nearest rate left shifted
81 * by (scale - fd->nwidth) bits.
83 scale
= fls_long(*parent_rate
/ rate
- 1);
84 if (scale
> fd
->nwidth
)
85 rate
<<= scale
- fd
->nwidth
;
87 rational_best_approximation(rate
, *parent_rate
,
88 GENMASK(fd
->mwidth
- 1, 0), GENMASK(fd
->nwidth
- 1, 0),
92 static long clk_fd_round_rate(struct clk_hw
*hw
, unsigned long rate
,
93 unsigned long *parent_rate
)
95 struct clk_fractional_divider
*fd
= to_clk_fd(hw
);
99 if (!rate
|| (!clk_hw_can_set_rate_parent(hw
) && rate
>= *parent_rate
))
102 if (fd
->approximation
)
103 fd
->approximation(hw
, rate
, parent_rate
, &m
, &n
);
105 clk_fd_general_approximation(hw
, rate
, parent_rate
, &m
, &n
);
107 ret
= (u64
)*parent_rate
* m
;
113 static int clk_fd_set_rate(struct clk_hw
*hw
, unsigned long rate
,
114 unsigned long parent_rate
)
116 struct clk_fractional_divider
*fd
= to_clk_fd(hw
);
117 unsigned long flags
= 0;
121 rational_best_approximation(rate
, parent_rate
,
122 GENMASK(fd
->mwidth
- 1, 0), GENMASK(fd
->nwidth
- 1, 0),
125 if (fd
->flags
& CLK_FRAC_DIVIDER_ZERO_BASED
) {
131 spin_lock_irqsave(fd
->lock
, flags
);
135 val
= clk_fd_readl(fd
);
136 val
&= ~(fd
->mmask
| fd
->nmask
);
137 val
|= (m
<< fd
->mshift
) | (n
<< fd
->nshift
);
138 clk_fd_writel(fd
, val
);
141 spin_unlock_irqrestore(fd
->lock
, flags
);
148 const struct clk_ops clk_fractional_divider_ops
= {
149 .recalc_rate
= clk_fd_recalc_rate
,
150 .round_rate
= clk_fd_round_rate
,
151 .set_rate
= clk_fd_set_rate
,
153 EXPORT_SYMBOL_GPL(clk_fractional_divider_ops
);
155 struct clk_hw
*clk_hw_register_fractional_divider(struct device
*dev
,
156 const char *name
, const char *parent_name
, unsigned long flags
,
157 void __iomem
*reg
, u8 mshift
, u8 mwidth
, u8 nshift
, u8 nwidth
,
158 u8 clk_divider_flags
, spinlock_t
*lock
)
160 struct clk_fractional_divider
*fd
;
161 struct clk_init_data init
;
165 fd
= kzalloc(sizeof(*fd
), GFP_KERNEL
);
167 return ERR_PTR(-ENOMEM
);
170 init
.ops
= &clk_fractional_divider_ops
;
172 init
.parent_names
= parent_name
? &parent_name
: NULL
;
173 init
.num_parents
= parent_name
? 1 : 0;
178 fd
->mmask
= GENMASK(mwidth
- 1, 0) << mshift
;
181 fd
->nmask
= GENMASK(nwidth
- 1, 0) << nshift
;
182 fd
->flags
= clk_divider_flags
;
187 ret
= clk_hw_register(dev
, hw
);
195 EXPORT_SYMBOL_GPL(clk_hw_register_fractional_divider
);
197 struct clk
*clk_register_fractional_divider(struct device
*dev
,
198 const char *name
, const char *parent_name
, unsigned long flags
,
199 void __iomem
*reg
, u8 mshift
, u8 mwidth
, u8 nshift
, u8 nwidth
,
200 u8 clk_divider_flags
, spinlock_t
*lock
)
204 hw
= clk_hw_register_fractional_divider(dev
, name
, parent_name
, flags
,
205 reg
, mshift
, mwidth
, nshift
, nwidth
, clk_divider_flags
,
211 EXPORT_SYMBOL_GPL(clk_register_fractional_divider
);
213 void clk_hw_unregister_fractional_divider(struct clk_hw
*hw
)
215 struct clk_fractional_divider
*fd
;
219 clk_hw_unregister(hw
);