summaryrefslogtreecommitdiffstats
path: root/meta-rcar-gen3-adas/recipes-kernel/linux/linux-renesas/0110-Renesas-clk-Add-RPC-clock-source.patch
blob: 37d08e9625f22ffb3191c51596df6b4663515675 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
From b4acae6eda19307e0051763a532f522006a82d5c Mon Sep 17 00:00:00 2001
From: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
Date: Mon, 19 Mar 2018 10:37:04 +0300
Subject: [PATCH 01/12] Renesas: clk: Add RPC clock source

Add RPC clock source functionality

Signed-off-by: Dmitry Shifrin <dmitry.shifrin@cogentembedded.com>
---
 drivers/clk/renesas/rcar-gen3-cpg.c | 200 ++++++++++++++++++++++++++++++++++++
 drivers/clk/renesas/rcar-gen3-cpg.h |   4 +
 2 files changed, 204 insertions(+)

diff --git a/drivers/clk/renesas/rcar-gen3-cpg.c b/drivers/clk/renesas/rcar-gen3-cpg.c
index 99acba2..969e1b2 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.c
+++ b/drivers/clk/renesas/rcar-gen3-cpg.c
@@ -599,6 +599,7 @@ static const struct clk_div_table cpg_sd01_div_table[] = {
 	{ 10, 36 }, { 11, 48 }, { 12, 10 }, {  0,  0 },
 };
 
+
 #define CPG_SD_STP_HCK		BIT(9)
 #define CPG_SD_STP_CK		BIT(8)
 
@@ -811,6 +812,202 @@ static struct clk * __init cpg_sd_clk_register(const struct cpg_core_clk *core,
 	return clk;
 }
 
+/**************** RPCSCK ***********************************/
+
+
+
+struct rpc_clock {
+	struct clk_hw hw;
+	void __iomem *reg;
+	const struct clk_div_table *div_table;
+	unsigned int div_num;
+	unsigned int div_min;
+	unsigned int div_max;
+};
+
+static const struct clk_div_table cpg_rpc_div_table[] = {
+	{  0x11, 20 }, {  0x13, 40 }, {  0x15, 60 }, {  0x17, 80 },
+	{  0x19, 24 }, {  0x1B, 48 }, {  0x1D, 72 }, {  0x1F, 96 },
+};
+
+#define to_rpc_clock(_hw) container_of(_hw, struct rpc_clock, hw)
+
+#define CPG_RPC_ENA_MASK	(BIT(9) | BIT(8))
+#define CPG_RPC_SCALE_MASK	(0x1F)
+
+
+static int cpg_rpc_clock_endisable(void __iomem *reg, bool enable)
+{
+	u32 val;
+
+	val = readl(reg);
+
+	if (enable)
+		val &= ~CPG_RPC_ENA_MASK;
+	else
+		val |= CPG_RPC_ENA_MASK;
+
+	writel(val, reg);
+	return 0;
+};
+
+static int cpg_rpc_clock_enable(struct clk_hw *hw)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+
+	cpg_rpc_clock_endisable(clock->reg, true);
+
+	return 0;
+}
+
+static void cpg_rpc_clock_disable(struct clk_hw *hw)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+
+	cpg_rpc_clock_endisable(clock->reg, false);
+}
+
+static int cpg_rpc_clock_is_enabled(struct clk_hw *hw)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+
+	return !(readl(clock->reg) & CPG_RPC_ENA_MASK);
+}
+
+static unsigned long cpg_rpc_clock_recalc_rate(struct clk_hw *hw,
+						unsigned long parent_rate)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+	unsigned long rate = parent_rate;
+	u32 val, scale;
+	unsigned int i;
+
+	val = readl(clock->reg);
+
+	scale = val & CPG_RPC_SCALE_MASK;
+	for (i = 0; i < clock->div_num; i++)
+		if (scale == clock->div_table[i].val)
+			break;
+
+	if (i >= clock->div_num)
+		return 0;
+
+	return DIV_ROUND_CLOSEST(rate, clock->div_table[i].div);
+}
+
+static unsigned int cpg_rpc_clock_calc_div(struct rpc_clock *clock,
+					  unsigned long rate,
+					  unsigned long parent_rate)
+{
+	unsigned int div;
+
+	if (!rate)
+		rate = 1;
+
+	div = DIV_ROUND_CLOSEST(parent_rate, rate);
+
+	return clamp_t(unsigned int, div, clock->div_min, clock->div_max);
+}
+
+static long cpg_rpc_clock_round_rate(struct clk_hw *hw, unsigned long rate,
+				      unsigned long *parent_rate)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+	unsigned int div = cpg_rpc_clock_calc_div(clock, rate, *parent_rate);
+
+	return DIV_ROUND_CLOSEST(*parent_rate, div);
+}
+
+static int cpg_rpc_clock_set_rate(struct clk_hw *hw, unsigned long rate,
+				   unsigned long parent_rate)
+{
+	struct rpc_clock *clock = to_rpc_clock(hw);
+	unsigned int div = cpg_rpc_clock_calc_div(clock, rate, parent_rate);
+	u32 val;
+	unsigned int i, nearest_i, nearest_div;
+
+
+	for (i = 0; i < clock->div_num; i++)
+		if (div == clock->div_table[i].div)
+			break;
+
+	if (i >= clock->div_num) {
+		/* find nearest */
+		nearest_div = -1;
+		nearest_i = clock->div_num;
+		for (i = 0; i < clock->div_num; i++) {
+			if (clock->div_table[i].div >= div &&
+			    clock->div_table[i].div <= nearest_div) {
+				nearest_i = i;
+				nearest_div = clock->div_table[i].div;
+			}
+		}
+
+		i = nearest_i;
+	}
+
+	if (i >= clock->div_num)
+		return -EINVAL;
+
+	val = readl(clock->reg);
+	val &= ~CPG_RPC_SCALE_MASK;
+	val |= clock->div_table[i].val;
+	writel(val, clock->reg);
+
+	return 0;
+}
+
+static const struct clk_ops cpg_rpc_clock_ops = {
+	.enable = cpg_rpc_clock_enable,
+	.disable = cpg_rpc_clock_disable,
+	.is_enabled = cpg_rpc_clock_is_enabled,
+	.recalc_rate = cpg_rpc_clock_recalc_rate,
+	.round_rate = cpg_rpc_clock_round_rate,
+	.set_rate = cpg_rpc_clock_set_rate,
+};
+
+static struct clk * __init cpg_rpc_clk_register(const struct cpg_core_clk *core,
+					       void __iomem *base,
+					       const char *parent_name)
+{
+	struct clk_init_data init;
+	struct rpc_clock *clock;
+	struct clk *clk;
+	unsigned int i;
+
+	clock = kzalloc(sizeof(*clock), GFP_KERNEL);
+	if (!clock)
+		return ERR_PTR(-ENOMEM);
+
+	init.name = core->name;
+	init.ops = &cpg_rpc_clock_ops;
+	init.flags = CLK_IS_BASIC | CLK_SET_RATE_PARENT;
+	init.parent_names = &parent_name;
+	init.num_parents = 1;
+
+	clock->reg = base + core->offset;
+	clock->hw.init = &init;
+	clock->div_table = cpg_rpc_div_table;
+	clock->div_num = ARRAY_SIZE(cpg_rpc_div_table);
+
+	clock->div_max = clock->div_table[0].div;
+	clock->div_min = clock->div_max;
+	for (i = 1; i < clock->div_num; i++) {
+		clock->div_max = max(clock->div_max, clock->div_table[i].div);
+		clock->div_min = min(clock->div_min, clock->div_table[i].div);
+	}
+
+	clk = clk_register(NULL, &clock->hw);
+	if (IS_ERR(clk))
+		kfree(clock);
+
+	return clk;
+}
+
+/***********************************************************/
+
+
+
 
 static const struct rcar_gen3_cpg_pll_config *cpg_pll_config __initdata;
 static unsigned int cpg_clk_extalr __initdata;
@@ -943,6 +1140,9 @@ struct clk * __init rcar_gen3_cpg_clk_register(struct device *dev,
 		return cpg_zg_clk_register(core->name, __clk_get_name(parent),
 					   base);
 
+	case CLK_TYPE_GEN3_RPCSRC:
+		return cpg_rpc_clk_register(core, base, __clk_get_name(parent));
+
 	default:
 		return ERR_PTR(-EINVAL);
 	}
diff --git a/drivers/clk/renesas/rcar-gen3-cpg.h b/drivers/clk/renesas/rcar-gen3-cpg.h
index 694bedc..636ef54 100644
--- a/drivers/clk/renesas/rcar-gen3-cpg.h
+++ b/drivers/clk/renesas/rcar-gen3-cpg.h
@@ -27,6 +27,7 @@ enum rcar_gen3_clk_types {
 	CLK_TYPE_GEN3_ZG,
 	CLK_TYPE_GEN3_RINT,
 	CLK_TYPE_GEN3_OSC,
+	CLK_TYPE_GEN3_RPCSRC,
 };
 
 #define DEF_GEN3_SD(_name, _id, _parent, _offset)	\
@@ -36,6 +37,9 @@ enum rcar_gen3_clk_types {
 #define DEF_GEN3_SD0H(_name, _id, _parent, _offset)	\
 	DEF_BASE(_name, _id, CLK_TYPE_GEN3_SD0H, _parent, .offset = _offset)
 
+#define DEF_GEN3_RPCSRC(_name, _id, _parent, _offset)	\
+	DEF_BASE(_name, _id, CLK_TYPE_GEN3_RPCSRC, _parent, .offset = _offset)
+
 struct rcar_gen3_cpg_pll_config {
 	unsigned int extal_div;
 	unsigned int pll1_mult;
-- 
2.7.4