blob: a2fca660a8e36f942568536690e91955acd41d9d [file] [log] [blame]
Hai Pham1b4ca862023-01-26 21:06:07 +01001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Renesas RCar Gen3 CPG MSSR driver
4 *
5 * Copyright (C) 2017 Marek Vasut <marek.vasut@gmail.com>
6 *
7 * Based on the following driver from Linux kernel:
8 * r8a7796 Clock Pulse Generator / Module Standby and Software Reset
9 *
10 * Copyright (C) 2016 Glider bvba
11 */
12
13#include <common.h>
14#include <clk-uclass.h>
15#include <dm.h>
16#include <errno.h>
17#include <log.h>
18#include <wait_bit.h>
19#include <asm/global_data.h>
20#include <asm/io.h>
21#include <linux/bitfield.h>
22#include <linux/bitops.h>
23#include <linux/clk-provider.h>
24
25#include <dt-bindings/clock/renesas-cpg-mssr.h>
26
27#include "renesas-cpg-mssr.h"
28#include "rcar-gen3-cpg.h"
29#include "rcar-cpg-lib.h"
30
31#define SDnSRCFC_SHIFT 2
32#define STPnHCK_TABLE (CPG_SDCKCR_STPnHCK >> SDnSRCFC_SHIFT)
33
34/* Non-constant mask variant of FIELD_GET/FIELD_PREP */
35#define field_get(_mask, _reg) (((_reg) & (_mask)) >> (ffs(_mask) - 1))
36#define field_prep(_mask, _val) (((_val) << (ffs(_mask) - 1)) & (_mask))
37
38static const struct clk_div_table cpg_sdh_div_table[] = {
39 { 0, 1 }, { 1, 2 }, { STPnHCK_TABLE | 2, 4 }, { STPnHCK_TABLE | 3, 8 },
40 { STPnHCK_TABLE | 4, 16 }, { 0, 0 },
41};
42
43static const struct clk_div_table cpg_sd_div_table[] = {
44 { 0, 2 }, { 1, 4 }, { 0, 0 },
45};
46
47static const struct clk_div_table cpg_rpc_div_table[] = {
48 { 1, 2 }, { 3, 4 }, { 5, 6 }, { 7, 8 }, { 0, 0 },
49};
50
51static unsigned int rcar_clk_get_table_div(const struct clk_div_table *table,
52 const u32 value)
53{
54 const struct clk_div_table *clkt;
55
56 for (clkt = table; clkt->div; clkt++)
57 if (clkt->val == value)
58 return clkt->div;
59 return 0;
60}
61
62static int rcar_clk_get_table_val(const struct clk_div_table *table,
63 unsigned int div)
64{
65 const struct clk_div_table *clkt;
66
67 for (clkt = table; clkt->div; clkt++)
68 if (clkt->div == div)
69 return clkt->val;
70 return -EINVAL;
71}
72
73s64 rcar_clk_get_rate64_div_table(unsigned int parent, u64 parent_rate,
74 void __iomem *reg, const u32 mask,
75 const struct clk_div_table *table, char *name)
76{
77 u32 value, div;
78 u64 rate;
79
80 value = field_get(mask, readl(reg));
81 div = rcar_clk_get_table_div(table, value);
82 if (!div)
83 return -EINVAL;
84
85 rate = parent_rate / div;
86 debug("%s[%i] %s clk: parent=%i div=%u => rate=%llu\n",
87 __func__, __LINE__, name, parent, div, rate);
88
89 return rate;
90}
91
92int rcar_clk_set_rate64_div_table(unsigned int parent, u64 parent_rate, ulong rate,
93 void __iomem *reg, const u32 mask,
94 const struct clk_div_table *table, char *name)
95{
96 u32 value = 0, div = 0;
97
98 div = DIV_ROUND_CLOSEST(parent_rate, rate);
99 value = rcar_clk_get_table_val(table, div);
100 if (value < 0)
101 return value;
102
103 clrsetbits_le32(reg, mask, field_prep(mask, value));
104
105 debug("%s[%i] %s clk: parent=%i div=%u rate=%lu => val=%u\n",
106 __func__, __LINE__, name, parent, div, rate, value);
107
108 return 0;
109}
110
111s64 rcar_clk_get_rate64_rpc(unsigned int parent, u64 parent_rate, void __iomem *reg)
112{
113 return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
114 CPG_RPCCKCR_DIV_PRE_MASK,
115 cpg_rpc_div_table, "RPC");
116}
117
118u64 rcar_clk_get_rate64_rpcd2(unsigned int parent, u64 parent_rate)
119{
120 u64 rate = 0;
121
122 rate = parent_rate / 2;
123 debug("%s[%i] RPCD2 clk: parent=%i => rate=%llu\n",
124 __func__, __LINE__, parent, rate);
125
126 return rate;
127}
128
129s64 rcar_clk_get_rate64_sdh(unsigned int parent, u64 parent_rate, void __iomem *reg)
130{
131 /*
132 * This takes STPnHCK and STPnCK bits into consideration
133 * in the table look up too, hence the inobvious GENMASK
134 * below. Bits [7:5] always read zero, so this is OKish.
135 */
136 return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
137 CPG_SDCKCR_SRCFC_MASK |
138 GENMASK(9, 5),
139 cpg_sdh_div_table, "SDH");
140}
141
142s64 rcar_clk_get_rate64_sd(unsigned int parent, u64 parent_rate, void __iomem *reg)
143{
144 return rcar_clk_get_rate64_div_table(parent, parent_rate, reg,
145 CPG_SDCKCR_FC_MASK,
146 cpg_sd_div_table, "SD");
147}
148
149int rcar_clk_set_rate64_sdh(unsigned int parent, u64 parent_rate, ulong rate,
150 void __iomem *reg)
151{
152 /*
153 * This takes STPnHCK and STPnCK bits into consideration
154 * in the table look up too, hence the inobvious GENMASK
155 * below. Bits [7:5] always read zero, so this is OKish.
156 */
157 return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg,
158 CPG_SDCKCR_SRCFC_MASK |
159 GENMASK(9, 5),
160 cpg_sdh_div_table, "SDH");
161}
162
163int rcar_clk_set_rate64_sd(unsigned int parent, u64 parent_rate, ulong rate,
164 void __iomem *reg)
165{
166 return rcar_clk_set_rate64_div_table(parent, parent_rate, rate, reg,
167 CPG_SDCKCR_FC_MASK,
168 cpg_sd_div_table, "SD");
169}