hc
2023-12-11 d2ccde1c8e90d38cee87a1b0309ad2827f3fd30d
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
// SPDX-License-Identifier: GPL-2.0-or-later
/*
 * Copyright (c) 2016 Rockchip Electronics Co. Ltd.
 * Author: Lin Huang <hl@rock-chips.com>
 */
 
#include <linux/arm-smccc.h>
#include <linux/clk.h>
#include <linux/clk-provider.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/rockchip/rockchip_sip.h>
#include <linux/slab.h>
#include <soc/rockchip/rockchip_sip.h>
#ifdef CONFIG_ARM
#include <asm/psci.h>
#endif
 
#include "clk.h"
 
struct rockchip_ddrclk {
   struct clk_hw    hw;
   void __iomem    *reg_base;
   int        mux_offset;
   int        mux_shift;
   int        mux_width;
   int        div_shift;
   int        div_width;
   int        ddr_flag;
};
 
#define to_rockchip_ddrclk_hw(hw) container_of(hw, struct rockchip_ddrclk, hw)
 
struct share_params_ddrclk {
   u32 hz;
   u32 lcdc_type;
};
 
struct rockchip_ddrclk_data {
   void __iomem *params;
   int (*dmcfreq_wait_complete)(void);
};
 
static struct rockchip_ddrclk_data ddr_data = {NULL, NULL};
 
void rockchip_set_ddrclk_params(void __iomem *params)
{
   ddr_data.params = params;
}
EXPORT_SYMBOL(rockchip_set_ddrclk_params);
 
void rockchip_set_ddrclk_dmcfreq_wait_complete(int (*func)(void))
{
   ddr_data.dmcfreq_wait_complete = func;
}
EXPORT_SYMBOL(rockchip_set_ddrclk_dmcfreq_wait_complete);
 
static int rockchip_ddrclk_sip_set_rate(struct clk_hw *hw, unsigned long drate,
                   unsigned long prate)
{
   struct arm_smccc_res res;
 
   arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, drate, 0,
             ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE,
             0, 0, 0, 0, &res);
 
   if (res.a0)
       return 0;
   else
       return -EPERM;
}
 
static unsigned long
rockchip_ddrclk_sip_recalc_rate(struct clk_hw *hw,
               unsigned long parent_rate)
{
   struct arm_smccc_res res;
 
   arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, 0, 0,
             ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE,
             0, 0, 0, 0, &res);
 
   return res.a0;
}
 
static long rockchip_ddrclk_sip_round_rate(struct clk_hw *hw,
                      unsigned long rate,
                      unsigned long *prate)
{
   struct arm_smccc_res res;
 
   arm_smccc_smc(ROCKCHIP_SIP_DRAM_FREQ, rate, 0,
             ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE,
             0, 0, 0, 0, &res);
 
   return res.a0;
}
 
static u8 rockchip_ddrclk_get_parent(struct clk_hw *hw)
{
   struct rockchip_ddrclk *ddrclk = to_rockchip_ddrclk_hw(hw);
   u32 val;
 
   val = readl(ddrclk->reg_base +
           ddrclk->mux_offset) >> ddrclk->mux_shift;
   val &= GENMASK(ddrclk->mux_width - 1, 0);
 
   return val;
}
 
static const struct clk_ops rockchip_ddrclk_sip_ops = {
   .recalc_rate = rockchip_ddrclk_sip_recalc_rate,
   .set_rate = rockchip_ddrclk_sip_set_rate,
   .round_rate = rockchip_ddrclk_sip_round_rate,
   .get_parent = rockchip_ddrclk_get_parent,
};
 
static int rockchip_ddrclk_sip_set_rate_v2(struct clk_hw *hw,
                      unsigned long drate,
                      unsigned long prate)
{
   struct share_params_ddrclk *p;
   struct arm_smccc_res res;
 
   p = (struct share_params_ddrclk *)ddr_data.params;
   if (p)
       p->hz = drate;
 
   res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
              ROCKCHIP_SIP_CONFIG_DRAM_SET_RATE);
 
   if ((int)res.a1 == SIP_RET_SET_RATE_TIMEOUT) {
       if (ddr_data.dmcfreq_wait_complete)
           ddr_data.dmcfreq_wait_complete();
   }
 
   return res.a0;
}
 
static unsigned long rockchip_ddrclk_sip_recalc_rate_v2
           (struct clk_hw *hw, unsigned long parent_rate)
{
   struct arm_smccc_res res;
 
   res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
              ROCKCHIP_SIP_CONFIG_DRAM_GET_RATE);
   if (!res.a0)
       return res.a1;
   else
       return 0;
}
 
static long rockchip_ddrclk_sip_round_rate_v2(struct clk_hw *hw,
                         unsigned long rate,
                         unsigned long *prate)
{
   struct share_params_ddrclk *p;
   struct arm_smccc_res res;
 
   p = (struct share_params_ddrclk *)ddr_data.params;
   if (p)
       p->hz = rate;
 
   res = sip_smc_dram(SHARE_PAGE_TYPE_DDR, 0,
              ROCKCHIP_SIP_CONFIG_DRAM_ROUND_RATE);
   if (!res.a0)
       return res.a1;
   else
       return 0;
}
 
static const struct clk_ops rockchip_ddrclk_sip_ops_v2 = {
   .recalc_rate = rockchip_ddrclk_sip_recalc_rate_v2,
   .set_rate = rockchip_ddrclk_sip_set_rate_v2,
   .round_rate = rockchip_ddrclk_sip_round_rate_v2,
   .get_parent = rockchip_ddrclk_get_parent,
};
 
struct clk *rockchip_clk_register_ddrclk(const char *name, int flags,
                    const char *const *parent_names,
                    u8 num_parents, int mux_offset,
                    int mux_shift, int mux_width,
                    int div_shift, int div_width,
                    int ddr_flag, void __iomem *reg_base)
{
   struct rockchip_ddrclk *ddrclk;
   struct clk_init_data init;
   struct clk *clk;
 
#ifdef CONFIG_ARM
   if (!psci_smp_available())
       return NULL;
#endif
 
   ddrclk = kzalloc(sizeof(*ddrclk), GFP_KERNEL);
   if (!ddrclk)
       return ERR_PTR(-ENOMEM);
 
   init.name = name;
   init.parent_names = parent_names;
   init.num_parents = num_parents;
 
   init.flags = flags;
   init.flags |= CLK_SET_RATE_NO_REPARENT;
 
   switch (ddr_flag) {
#ifdef CONFIG_ROCKCHIP_DDRCLK_SIP
   case ROCKCHIP_DDRCLK_SIP:
       init.ops = &rockchip_ddrclk_sip_ops;
       break;
#endif
#ifdef CONFIG_ROCKCHIP_DDRCLK_SIP_V2
   case ROCKCHIP_DDRCLK_SIP_V2:
       init.ops = &rockchip_ddrclk_sip_ops_v2;
       break;
#endif
   default:
       pr_err("%s: unsupported ddrclk type %d\n", __func__, ddr_flag);
       kfree(ddrclk);
       return ERR_PTR(-EINVAL);
   }
 
   ddrclk->reg_base = reg_base;
   ddrclk->hw.init = &init;
   ddrclk->mux_offset = mux_offset;
   ddrclk->mux_shift = mux_shift;
   ddrclk->mux_width = mux_width;
   ddrclk->div_shift = div_shift;
   ddrclk->div_width = div_width;
   ddrclk->ddr_flag = ddr_flag;
 
   clk = clk_register(NULL, &ddrclk->hw);
   if (IS_ERR(clk))
       kfree(ddrclk);
 
   return clk;
}
EXPORT_SYMBOL_GPL(rockchip_clk_register_ddrclk);