Linux kernel mirror (for testing)
git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel
os
linux
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
31static 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
42static int clk_gpt_determine_rate(struct clk_hw *hw,
43 struct clk_rate_request *req)
44{
45 struct clk_gpt *gpt = to_clk_gpt(hw);
46 int unused;
47
48 req->rate = clk_round_rate_index(hw, req->rate, req->best_parent_rate,
49 gpt_calc_rate, gpt->rtbl_cnt, &unused);
50
51 return 0;
52}
53
54static unsigned long clk_gpt_recalc_rate(struct clk_hw *hw,
55 unsigned long parent_rate)
56{
57 struct clk_gpt *gpt = to_clk_gpt(hw);
58 unsigned long flags = 0;
59 unsigned int div = 1, val;
60
61 if (gpt->lock)
62 spin_lock_irqsave(gpt->lock, flags);
63
64 val = readl_relaxed(gpt->reg);
65
66 if (gpt->lock)
67 spin_unlock_irqrestore(gpt->lock, flags);
68
69 div += val & GPT_MSCALE_MASK;
70 div *= 1 << (((val >> GPT_NSCALE_SHIFT) & GPT_NSCALE_MASK) + 1);
71
72 if (!div)
73 return 0;
74
75 return parent_rate / div;
76}
77
78/* Configures new clock rate of gpt */
79static int clk_gpt_set_rate(struct clk_hw *hw, unsigned long drate,
80 unsigned long prate)
81{
82 struct clk_gpt *gpt = to_clk_gpt(hw);
83 struct gpt_rate_tbl *rtbl = gpt->rtbl;
84 unsigned long flags = 0, val;
85 int i;
86
87 clk_round_rate_index(hw, drate, prate, gpt_calc_rate, gpt->rtbl_cnt,
88 &i);
89
90 if (gpt->lock)
91 spin_lock_irqsave(gpt->lock, flags);
92
93 val = readl(gpt->reg) & ~GPT_MSCALE_MASK;
94 val &= ~(GPT_NSCALE_MASK << GPT_NSCALE_SHIFT);
95
96 val |= rtbl[i].mscale & GPT_MSCALE_MASK;
97 val |= (rtbl[i].nscale & GPT_NSCALE_MASK) << GPT_NSCALE_SHIFT;
98
99 writel_relaxed(val, gpt->reg);
100
101 if (gpt->lock)
102 spin_unlock_irqrestore(gpt->lock, flags);
103
104 return 0;
105}
106
107static const struct clk_ops clk_gpt_ops = {
108 .recalc_rate = clk_gpt_recalc_rate,
109 .determine_rate = clk_gpt_determine_rate,
110 .set_rate = clk_gpt_set_rate,
111};
112
113struct clk *clk_register_gpt(const char *name, const char *parent_name, unsigned
114 long flags, void __iomem *reg, struct gpt_rate_tbl *rtbl, u8
115 rtbl_cnt, spinlock_t *lock)
116{
117 struct clk_init_data init;
118 struct clk_gpt *gpt;
119 struct clk *clk;
120
121 if (!name || !parent_name || !reg || !rtbl || !rtbl_cnt) {
122 pr_err("Invalid arguments passed\n");
123 return ERR_PTR(-EINVAL);
124 }
125
126 gpt = kzalloc_obj(*gpt);
127 if (!gpt)
128 return ERR_PTR(-ENOMEM);
129
130 /* struct clk_gpt assignments */
131 gpt->reg = reg;
132 gpt->rtbl = rtbl;
133 gpt->rtbl_cnt = rtbl_cnt;
134 gpt->lock = lock;
135 gpt->hw.init = &init;
136
137 init.name = name;
138 init.ops = &clk_gpt_ops;
139 init.flags = flags;
140 init.parent_names = &parent_name;
141 init.num_parents = 1;
142
143 clk = clk_register(NULL, &gpt->hw);
144 if (!IS_ERR_OR_NULL(clk))
145 return clk;
146
147 pr_err("clk register failed\n");
148 kfree(gpt);
149
150 return NULL;
151}