2 * Renesas RCar Gen2 CPG MSSR driver
4 * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
6 * Based on the following driver from Linux kernel:
7 * r8a7796 Clock Pulse Generator / Module Standby and Software Reset
9 * Copyright (C) 2016 Glider bvba
11 * SPDX-License-Identifier: GPL-2.0+
15 #include <clk-uclass.h>
20 #include <dt-bindings/clock/renesas-cpg-mssr.h>
22 #include "renesas-cpg-mssr.h"
23 #include "rcar-gen2-cpg.h"
25 #define CPG_RST_MODEMR 0x0060
27 #define CPG_PLL0CR 0x00d8
28 #define CPG_SDCKCR 0x0074
30 struct clk_div_table {
36 static 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 },
42 static 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 },
48 static u8 gen2_clk_get_sdh_div(const struct clk_div_table *table, u8 div)
50 while ((*table++).val) {
51 if ((*table).div == div)
57 static int gen2_clk_enable(struct clk *clk)
59 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
61 return renesas_clk_endisable(clk, priv->base, true);
64 static int gen2_clk_disable(struct clk *clk)
66 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
68 return renesas_clk_endisable(clk, priv->base, false);
71 static ulong gen2_clk_get_rate(struct clk *clk)
73 struct gen2_clk_priv *priv = dev_get_priv(clk->dev);
74 struct cpg_mssr_info *info = priv->info;
76 const struct cpg_core_clk *core;
77 const struct rcar_gen2_cpg_pll_config *pll_config =
79 u32 value, mult, div, rate = 0;
82 debug("%s[%i] Clock: id=%lu\n", __func__, __LINE__, clk->id);
84 ret = renesas_clk_get_parent(clk, info, &parent);
86 printf("%s[%i] parent fail, ret=%i\n", __func__, __LINE__, ret);
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);
97 ret = renesas_clk_get_core(clk, info, &core);
101 switch (core->type) {
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);
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);
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",
123 core->parent, core->mult, core->div, rate);
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",
131 core->parent, value, rate);
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",
138 core->parent, pll_config->extal_div, rate);
141 case CLK_TYPE_GEN2_PLL0:
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.
148 mult = pll_config->pll0_mult;
150 value = readl(priv->base + CPG_PLL0CR);
151 mult = (((value >> 24) & 0x7f) + 1) * 2;
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);
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",
163 core->parent, pll_config->pll1_mult, rate);
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",
170 core->parent, pll_config->pll3_mult, rate);
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",
179 core->parent, div, rate);
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",
188 core->parent, div, rate);
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",
197 core->parent, div, rate);
201 printf("%s[%i] unknown fail\n", __func__, __LINE__);
206 static ulong gen2_clk_set_rate(struct clk *clk, ulong rate)
208 return gen2_clk_get_rate(clk);
211 static int gen2_clk_of_xlate(struct clk *clk, struct ofnode_phandle_args *args)
213 if (args->args_count != 2) {
214 debug("Invaild args_count: %d\n", args->args_count);
218 clk->id = (args->args[0] << 16) | args->args[1];
223 const 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,
231 int gen2_clk_probe(struct udevice *dev)
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);
240 priv->base = (struct gen2_base *)devfdt_get_addr(dev);
245 ret = fdt_node_offset_by_compatible(gd->fdt_blob, -1, info->reset_node);
249 rst_base = fdtdec_get_addr(gd->fdt_blob, ret, "reg");
250 if (rst_base == FDT_ADDR_T_NONE)
253 cpg_mode = readl(rst_base + CPG_RST_MODEMR);
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)
260 ret = clk_get_by_name(dev, "extal", &priv->clk_extal);
264 if (info->extal_usb_node) {
265 ret = clk_get_by_name(dev, info->extal_usb_node,
266 &priv->clk_extal_usb);
274 int gen2_clk_remove(struct udevice *dev)
276 struct gen2_clk_priv *priv = dev_get_priv(dev);
278 return renesas_clk_remove(priv->base, priv->info);