GNU Linux-libre 6.8.9-gnu
[releases.git] / drivers / clk / spear / clk-gpt-synth.c
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, *prate, gpt_calc_rate,
49                         gpt->rtbl_cnt, &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(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, prate, gpt_calc_rate, gpt->rtbl_cnt,
86                         &i);
87
88         if (gpt->lock)
89                 spin_lock_irqsave(gpt->lock, flags);
90
91         val = readl(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(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(-EINVAL);
122         }
123
124         gpt = kzalloc(sizeof(*gpt), GFP_KERNEL);
125         if (!gpt)
126                 return ERR_PTR(-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, &gpt->hw);
142         if (!IS_ERR_OR_NULL(clk))
143                 return clk;
144
145         pr_err("clk register failed\n");
146         kfree(gpt);
147
148         return NULL;
149 }