blob: f65b18c7f6f8f938d1407d8323ee4b83a3a68ed5 [file] [log] [blame]
Marek Vasutf63b2952018-01-08 16:38:51 +01001/*
2 * Renesas RCar Gen2 CPG MSSR driver
3 *
4 * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
5 *
6 * Based on the following driver from Linux kernel:
7 * r8a7796 Clock Pulse Generator / Module Standby and Software Reset
8 *
9 * Copyright (C) 2016 Glider bvba
10 *
11 * SPDX-License-Identifier: GPL-2.0+
12 */
13
14#include <common.h>
15#include <clk-uclass.h>
16#include <dm.h>
17#include <errno.h>
18#include <asm/io.h>
19
20#include <dt-bindings/clock/renesas-cpg-mssr.h>
21
22#include "renesas-cpg-mssr.h"
23#include "rcar-gen2-cpg.h"
24
25#define CPG_RST_MODEMR 0x0060
26
27#define CPG_PLL0CR 0x00d8
28#define CPG_SDCKCR 0x0074
29
30struct clk_div_table {
31 u8 val;
32 u8 div;
33};
34
35/* SDHI divisors */
36static const struct clk_div_table cpg_sdh_div_table[] = {
37 { 0, 2 }, { 1, 3 }, { 2, 4 }, { 3, 6 },
38 { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
39 { 8, 24 }, { 10, 36 }, { 11, 48 }, { 0, 0 },
40};
41
42static const struct clk_div_table cpg_sd01_div_table[] = {
43 { 4, 8 }, { 5, 12 }, { 6, 16 }, { 7, 18 },
44 { 8, 24 }, { 10, 36 }, { 11, 48 }, { 12, 10 },
45 { 0, 0 },
46};
47
48static u8 gen2_clk_get_sdh_div(const struct clk_div_table *table, u8 div)
49{
50 while ((*table++).val) {
51 if ((*table).div == div)
52 return div;
53 }
54 return 0xff;
55}
56
57static int gen2_clk_enable(struct clk *clk)
58{
59 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
60
61 return renesas_clk_endisable(clk, priv->base, true);
62}
63
64static int gen2_clk_disable(struct clk *clk)
65{
66 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
67
68 return renesas_clk_endisable(clk, priv->base, false);
69}
70
71static ulong gen2_clk_get_rate(struct clk *clk)
72{
73 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
74 struct cpg_mssr_info *info = priv->info;
75 struct clk parent;
76 const struct cpg_core_clk *core;
77 const struct rcar_gen2_cpg_pll_config *pll_config =
78 priv->cpg_pll_config;
79 u32 value, mult, div, rate = 0;
80 int ret;
81
82 debug("%s[%i] Clock: id=%lu\n", __func__, __LINE__, clk->id);
83
84 ret = renesas_clk_get_parent(clk, info, &parent);
85 if (ret) {
86 printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret);
87 return ret;
88 }
89
90 if (renesas_clk_is_mod(clk)) {
91 rate = gen2_clk_get_rate(&parent);
92 debug("%s[%i] MOD clk: parent=%lu => rate=%u\n",
93 __func__, __LINE__, parent.id, rate);
94 return rate;
95 }
96
97 ret = renesas_clk_get_core(clk, info, &core);
98 if (ret)
99 return ret;
100
101 switch (core->type) {
102 case CLK_TYPE_IN:
103 if (core->id == info->clk_extal_id) {
104 rate = clk_get_rate(&priv->clk_extal);
105 debug("%s[%i] EXTAL clk: rate=%u\n",
106 __func__, __LINE__, rate);
107 return rate;
108 }
109
110 if (core->id == info->clk_extal_usb_id) {
111 rate = clk_get_rate(&priv->clk_extal_usb);
112 debug("%s[%i] EXTALR clk: rate=%u\n",
113 __func__, __LINE__, rate);
114 return rate;
115 }
116
117 return -EINVAL;
118
119 case CLK_TYPE_FF:
120 rate = (gen2_clk_get_rate(&parent) * core->mult) / core->div;
121 debug("%s[%i] FIXED clk: parent=%i div=%i mul=%i => rate=%u\n",
122 __func__, __LINE__,
123 core->parent, core->mult, core->div, rate);
124 return rate;
125
126 case CLK_TYPE_DIV6P1: /* DIV6 Clock with 1 parent clock */
127 value = (readl(priv->base + core->offset) & 0x3f) + 1;
128 rate = gen2_clk_get_rate(&parent) / value;
129 debug("%s[%i] DIV6P1 clk: parent=%i div=%i => rate=%u\n",
130 __func__, __LINE__,
131 core->parent, value, rate);
132 return rate;
133
134 case CLK_TYPE_GEN2_MAIN:
135 rate = gen2_clk_get_rate(&parent) / pll_config->extal_div;
136 debug("%s[%i] MAIN clk: parent=%i extal_div=%i => rate=%u\n",
137 __func__, __LINE__,
138 core->parent, pll_config->extal_div, rate);
139 return rate;
140
141 case CLK_TYPE_GEN2_PLL0:
142 /*
143 * PLL0 is a configurable multiplier clock except on R-Car
144 * V2H/E2. Register the PLL0 clock as a fixed factor clock for
145 * now as there's no generic multiplier clock implementation and
146 * we currently have no need to change the multiplier value.
147 */
148 mult = pll_config->pll0_mult;
149 if (!mult) {
150 value = readl(priv->base + CPG_PLL0CR);
151 mult = (((value >> 24) & 0x7f) + 1) * 2;
152 }
153
154 rate = (gen2_clk_get_rate(&parent) * mult) / info->pll0_div;
155 debug("%s[%i] PLL0 clk: parent=%i mult=%u => rate=%u\n",
156 __func__, __LINE__, core->parent, mult, rate);
157 return rate;
158
159 case CLK_TYPE_GEN2_PLL1:
160 rate = (gen2_clk_get_rate(&parent) * pll_config->pll1_mult) / 2;
161 debug("%s[%i] PLL1 clk: parent=%i mul=%i => rate=%u\n",
162 __func__, __LINE__,
163 core->parent, pll_config->pll1_mult, rate);
164 return rate;
165
166 case CLK_TYPE_GEN2_PLL3:
167 rate = gen2_clk_get_rate(&parent) * pll_config->pll3_mult;
168 debug("%s[%i] PLL3 clk: parent=%i mul=%i => rate=%u\n",
169 __func__, __LINE__,
170 core->parent, pll_config->pll3_mult, rate);
171 return rate;
172
173 case CLK_TYPE_GEN2_SDH:
174 value = (readl(priv->base + CPG_SDCKCR) >> 8) & 0xf;
175 div = gen2_clk_get_sdh_div(cpg_sdh_div_table, value);
176 rate = gen2_clk_get_rate(&parent) / div;
177 debug("%s[%i] SDH clk: parent=%i div=%i => rate=%u\n",
178 __func__, __LINE__,
179 core->parent, div, rate);
180 return rate;
181
182 case CLK_TYPE_GEN2_SD0:
183 value = (readl(priv->base + CPG_SDCKCR) >> 4) & 0xf;
184 div = gen2_clk_get_sdh_div(cpg_sd01_div_table, value);
185 rate = gen2_clk_get_rate(&parent) / div;
186 debug("%s[%i] SD0 clk: parent=%i div=%i => rate=%u\n",
187 __func__, __LINE__,
188 core->parent, div, rate);
189 return rate;
190
191 case CLK_TYPE_GEN2_SD1:
192 value = (readl(priv->base + CPG_SDCKCR) >> 0) & 0xf;
193 div = gen2_clk_get_sdh_div(cpg_sd01_div_table, value);
194 rate = gen2_clk_get_rate(&parent) / div;
195 debug("%s[%i] SD1 clk: parent=%i div=%i => rate=%u\n",
196 __func__, __LINE__,
197 core->parent, div, rate);
198 return rate;
199 }
200
201 printf("%s[%i] unknown fail\n", __func__, __LINE__);
202
203 return -ENOENT;
204}
205
206static ulong gen2_clk_set_rate(struct clk *clk, ulong rate)
207{
208 return gen2_clk_get_rate(clk);
209}
210
211static int gen2_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
212{
213 if (args->args_count != 2) {
214 debug("Invaild args_count: %d\n", args->args_count);
215 return -EINVAL;
216 }
217
218 clk->id = (args->args[0] << 16) | args->args[1];
219
220 return 0;
221}
222
223const struct clk_ops gen2_clk_ops = {
224 .enable = gen2_clk_enable,
225 .disable = gen2_clk_disable,
226 .get_rate = gen2_clk_get_rate,
227 .set_rate = gen2_clk_set_rate,
228 .of_xlate = gen2_clk_of_xlate,
229};
230
231int gen2_clk_probe(struct udevice *dev)
232{
233 struct gen2_clk_priv *priv = dev_get_priv(dev);
234 struct cpg_mssr_info *info =
235 (struct cpg_mssr_info *)dev_get_driver_data(dev);
236 fdt_addr_t rst_base;
237 u32 cpg_mode;
238 int ret;
239
240 priv->base = (struct gen2_base *)devfdt_get_addr(dev);
241 if (!priv->base)
242 return -EINVAL;
243
244 priv->info = info;
245 ret = fdt_node_offset_by_compatible(gd->fdt_blob, -1, info->reset_node);
246 if (ret < 0)
247 return ret;
248
249 rst_base = fdtdec_get_addr(gd->fdt_blob, ret, "reg");
250 if (rst_base == FDT_ADDR_T_NONE)
251 return -EINVAL;
252
253 cpg_mode = readl(rst_base + CPG_RST_MODEMR);
254
255 priv->cpg_pll_config =
256 (struct rcar_gen2_cpg_pll_config *)info->get_pll_config(cpg_mode);
257 if (!priv->cpg_pll_config->extal_div)
258 return -EINVAL;
259
260 ret = clk_get_by_name(dev, "extal", &priv->clk_extal);
261 if (ret < 0)
262 return ret;
263
264 if (info->extal_usb_node) {
265 ret = clk_get_by_name(dev, info->extal_usb_node,
266 &priv->clk_extal_usb);
267 if (ret < 0)
268 return ret;
269 }
270
271 return 0;
272}
273
274int gen2_clk_remove(struct udevice *dev)
275{
276 struct gen2_clk_priv *priv = dev_get_priv(dev);
277
278 return renesas_clk_remove(priv->base, priv->info);
279}