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