1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 ST Microelectronics |
4 | * Viresh Kumar <vireshk@kernel.org> |
5 | * |
6 | * Fractional Synthesizer clock implementation |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "clk-frac-synth: " fmt |
10 | |
11 | #include <linux/clk-provider.h> |
12 | #include <linux/slab.h> |
13 | #include <linux/io.h> |
14 | #include <linux/err.h> |
15 | #include "clk.h" |
16 | |
17 | #define DIV_FACTOR_MASK 0x1FFFF |
18 | |
19 | /* |
20 | * DOC: Fractional Synthesizer clock |
21 | * |
22 | * Fout from synthesizer can be given from below equation: |
23 | * |
24 | * Fout= Fin/2*div (division factor) |
25 | * div is 17 bits:- |
26 | * 0-13 (fractional part) |
27 | * 14-16 (integer part) |
28 | * div is (16-14 bits).(13-0 bits) (in binary) |
29 | * |
30 | * Fout = Fin/(2 * div) |
31 | * Fout = ((Fin / 10000)/(2 * div)) * 10000 |
32 | * Fout = (2^14 * (Fin / 10000)/(2^14 * (2 * div))) * 10000 |
33 | * Fout = (((Fin / 10000) << 14)/(2 * (div << 14))) * 10000 |
34 | * |
35 | * div << 14 simply 17 bit value written at register. |
36 | * Max error due to scaling down by 10000 is 10 KHz |
37 | */ |
38 | |
39 | #define to_clk_frac(_hw) container_of(_hw, struct clk_frac, hw) |
40 | |
41 | static unsigned long frac_calc_rate(struct clk_hw *hw, unsigned long prate, |
42 | int index) |
43 | { |
44 | struct clk_frac *frac = to_clk_frac(hw); |
45 | struct frac_rate_tbl *rtbl = frac->rtbl; |
46 | |
47 | prate /= 10000; |
48 | prate <<= 14; |
49 | prate /= (2 * rtbl[index].div); |
50 | prate *= 10000; |
51 | |
52 | return prate; |
53 | } |
54 | |
55 | static long clk_frac_round_rate(struct clk_hw *hw, unsigned long drate, |
56 | unsigned long *prate) |
57 | { |
58 | struct clk_frac *frac = to_clk_frac(hw); |
59 | int unused; |
60 | |
61 | return clk_round_rate_index(hw, drate, parent_rate: *prate, calc_rate: frac_calc_rate, |
62 | rtbl_cnt: frac->rtbl_cnt, index: &unused); |
63 | } |
64 | |
65 | static unsigned long clk_frac_recalc_rate(struct clk_hw *hw, |
66 | unsigned long parent_rate) |
67 | { |
68 | struct clk_frac *frac = to_clk_frac(hw); |
69 | unsigned long flags = 0; |
70 | unsigned int div = 1, val; |
71 | |
72 | if (frac->lock) |
73 | spin_lock_irqsave(frac->lock, flags); |
74 | |
75 | val = readl_relaxed(frac->reg); |
76 | |
77 | if (frac->lock) |
78 | spin_unlock_irqrestore(lock: frac->lock, flags); |
79 | |
80 | div = val & DIV_FACTOR_MASK; |
81 | |
82 | if (!div) |
83 | return 0; |
84 | |
85 | parent_rate = parent_rate / 10000; |
86 | |
87 | parent_rate = (parent_rate << 14) / (2 * div); |
88 | return parent_rate * 10000; |
89 | } |
90 | |
91 | /* Configures new clock rate of frac */ |
92 | static int clk_frac_set_rate(struct clk_hw *hw, unsigned long drate, |
93 | unsigned long prate) |
94 | { |
95 | struct clk_frac *frac = to_clk_frac(hw); |
96 | struct frac_rate_tbl *rtbl = frac->rtbl; |
97 | unsigned long flags = 0, val; |
98 | int i; |
99 | |
100 | clk_round_rate_index(hw, drate, parent_rate: prate, calc_rate: frac_calc_rate, rtbl_cnt: frac->rtbl_cnt, |
101 | index: &i); |
102 | |
103 | if (frac->lock) |
104 | spin_lock_irqsave(frac->lock, flags); |
105 | |
106 | val = readl_relaxed(frac->reg) & ~DIV_FACTOR_MASK; |
107 | val |= rtbl[i].div & DIV_FACTOR_MASK; |
108 | writel_relaxed(val, frac->reg); |
109 | |
110 | if (frac->lock) |
111 | spin_unlock_irqrestore(lock: frac->lock, flags); |
112 | |
113 | return 0; |
114 | } |
115 | |
116 | static const struct clk_ops clk_frac_ops = { |
117 | .recalc_rate = clk_frac_recalc_rate, |
118 | .round_rate = clk_frac_round_rate, |
119 | .set_rate = clk_frac_set_rate, |
120 | }; |
121 | |
122 | struct clk *clk_register_frac(const char *name, const char *parent_name, |
123 | unsigned long flags, void __iomem *reg, |
124 | struct frac_rate_tbl *rtbl, u8 rtbl_cnt, spinlock_t *lock) |
125 | { |
126 | struct clk_init_data init; |
127 | struct clk_frac *frac; |
128 | struct clk *clk; |
129 | |
130 | if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { |
131 | pr_err("Invalid arguments passed\n" ); |
132 | return ERR_PTR(error: -EINVAL); |
133 | } |
134 | |
135 | frac = kzalloc(size: sizeof(*frac), GFP_KERNEL); |
136 | if (!frac) |
137 | return ERR_PTR(error: -ENOMEM); |
138 | |
139 | /* struct clk_frac assignments */ |
140 | frac->reg = reg; |
141 | frac->rtbl = rtbl; |
142 | frac->rtbl_cnt = rtbl_cnt; |
143 | frac->lock = lock; |
144 | frac->hw.init = &init; |
145 | |
146 | init.name = name; |
147 | init.ops = &clk_frac_ops; |
148 | init.flags = flags; |
149 | init.parent_names = &parent_name; |
150 | init.num_parents = 1; |
151 | |
152 | clk = clk_register(NULL, hw: &frac->hw); |
153 | if (!IS_ERR_OR_NULL(ptr: clk)) |
154 | return clk; |
155 | |
156 | pr_err("clk register failed\n" ); |
157 | kfree(objp: frac); |
158 | |
159 | return NULL; |
160 | } |
161 | |