1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Copyright (C) 2012 ST Microelectronics |
4 | * Viresh Kumar <vireshk@kernel.org> |
5 | * |
6 | * General Purpose Timer Synthesizer clock implementation |
7 | */ |
8 | |
9 | #define pr_fmt(fmt) "clk-gpt-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 GPT_MSCALE_MASK 0xFFF |
18 | #define GPT_NSCALE_SHIFT 12 |
19 | #define GPT_NSCALE_MASK 0xF |
20 | |
21 | /* |
22 | * DOC: General Purpose Timer Synthesizer clock |
23 | * |
24 | * Calculates gpt synth clk rate for different values of mscale and nscale |
25 | * |
26 | * Fout= Fin/((2 ^ (N+1)) * (M+1)) |
27 | */ |
28 | |
29 | #define to_clk_gpt(_hw) container_of(_hw, struct clk_gpt, hw) |
30 | |
31 | static unsigned long gpt_calc_rate(struct clk_hw *hw, unsigned long prate, |
32 | int index) |
33 | { |
34 | struct clk_gpt *gpt = to_clk_gpt(hw); |
35 | struct gpt_rate_tbl *rtbl = gpt->rtbl; |
36 | |
37 | prate /= ((1 << (rtbl[index].nscale + 1)) * (rtbl[index].mscale + 1)); |
38 | |
39 | return prate; |
40 | } |
41 | |
42 | static long clk_gpt_round_rate(struct clk_hw *hw, unsigned long drate, |
43 | unsigned long *prate) |
44 | { |
45 | struct clk_gpt *gpt = to_clk_gpt(hw); |
46 | int unused; |
47 | |
48 | return clk_round_rate_index(hw, drate, parent_rate: *prate, calc_rate: gpt_calc_rate, |
49 | rtbl_cnt: gpt->rtbl_cnt, index: &unused); |
50 | } |
51 | |
52 | static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw, |
53 | unsigned long parent_rate) |
54 | { |
55 | struct clk_gpt *gpt = to_clk_gpt(hw); |
56 | unsigned long flags = 0; |
57 | unsigned int div = 1, val; |
58 | |
59 | if (gpt->lock) |
60 | spin_lock_irqsave(gpt->lock, flags); |
61 | |
62 | val = readl_relaxed(gpt->reg); |
63 | |
64 | if (gpt->lock) |
65 | spin_unlock_irqrestore(lock: gpt->lock, flags); |
66 | |
67 | div += val & GPT_MSCALE_MASK; |
68 | div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1); |
69 | |
70 | if (!div) |
71 | return 0; |
72 | |
73 | return parent_rate / div; |
74 | } |
75 | |
76 | /* Configures new clock rate of gpt */ |
77 | static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate, |
78 | unsigned long prate) |
79 | { |
80 | struct clk_gpt *gpt = to_clk_gpt(hw); |
81 | struct gpt_rate_tbl *rtbl = gpt->rtbl; |
82 | unsigned long flags = 0, val; |
83 | int i; |
84 | |
85 | clk_round_rate_index(hw, drate, parent_rate: prate, calc_rate: gpt_calc_rate, rtbl_cnt: gpt->rtbl_cnt, |
86 | index: &i); |
87 | |
88 | if (gpt->lock) |
89 | spin_lock_irqsave(gpt->lock, flags); |
90 | |
91 | val = readl(addr: gpt->reg) & ~GPT_MSCALE_MASK; |
92 | val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT); |
93 | |
94 | val |= rtbl[i].mscale & GPT_MSCALE_MASK; |
95 | val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT; |
96 | |
97 | writel_relaxed(val, gpt->reg); |
98 | |
99 | if (gpt->lock) |
100 | spin_unlock_irqrestore(lock: gpt->lock, flags); |
101 | |
102 | return 0; |
103 | } |
104 | |
105 | static const struct clk_ops clk_gpt_ops = { |
106 | .recalc_rate = clk_gpt_recalc_rate, |
107 | .round_rate = clk_gpt_round_rate, |
108 | .set_rate = clk_gpt_set_rate, |
109 | }; |
110 | |
111 | struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned |
112 | long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8 |
113 | rtbl_cnt, spinlock_t *lock) |
114 | { |
115 | struct clk_init_data init; |
116 | struct clk_gpt *gpt; |
117 | struct clk *clk; |
118 | |
119 | if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) { |
120 | pr_err("Invalid arguments passed\n" ); |
121 | return ERR_PTR(error: -EINVAL); |
122 | } |
123 | |
124 | gpt = kzalloc(size: sizeof(*gpt), GFP_KERNEL); |
125 | if (!gpt) |
126 | return ERR_PTR(error: -ENOMEM); |
127 | |
128 | /* struct clk_gpt assignments */ |
129 | gpt->reg = reg; |
130 | gpt->rtbl = rtbl; |
131 | gpt->rtbl_cnt = rtbl_cnt; |
132 | gpt->lock = lock; |
133 | gpt->hw.init = &init; |
134 | |
135 | init.name = name; |
136 | init.ops = &clk_gpt_ops; |
137 | init.flags = flags; |
138 | init.parent_names = &parent_name; |
139 | init.num_parents = 1; |
140 | |
141 | clk = clk_register(NULL, hw: &gpt->hw); |
142 | if (!IS_ERR_OR_NULL(ptr: clk)) |
143 | return clk; |
144 | |
145 | pr_err("clk register failed\n" ); |
146 | kfree(objp: gpt); |
147 | |
148 | return NULL; |
149 | } |
150 | |