xref: /linux/drivers/clk/spear/clk-gpt-synth.c (revision 522ba450b56fff29f868b1552bdc2965f55de7ed)
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 
gpt_calc_rate(struct clk_hw * hw,unsigned long prate,int index)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 
clk_gpt_determine_rate(struct clk_hw * hw,struct clk_rate_request * req)42 static 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 
clk_gpt_recalc_rate(struct clk_hw * hw,unsigned long parent_rate)54 static 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 */
clk_gpt_set_rate(struct clk_hw * hw,unsigned long drate,unsigned long prate)79 static 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 
107 static 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 
clk_register_gpt(const char * name,const char * parent_name,unsigned long flags,void __iomem * reg,struct gpt_rate_tbl * rtbl,u8 rtbl_cnt,spinlock_t * lock)113 struct 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(sizeof(*gpt), GFP_KERNEL);
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 }
152