blob: 5eec261f3991154a0c9fe091c67eba52057e6f62 [file] [log] [blame]
b.liue9582032025-04-17 19:18:16 +08001/*
2 * common function for clock framework source file
3 *
4 * Copyright (C) 2012 Marvell
5 * Chao Xie <xiechao.mail@gmail.com>
6 * Zhoujie Wu <zjwu@marvell.com>
7 * Lu Cao <lucao@marvell.com>
8 *
9 * This file is licensed under the terms of the GNU General Public
10 * License version 2. This program is licensed "as is" without any
11 * warranty of any kind, whether express or implied.
12 */
13
14#include <linux/module.h>
15#include <linux/kernel.h>
16#include <linux/cpufreq.h>
17#include <linux/devfreq.h>
18#include <linux/clk/mmpcpdvc.h>
19
20#include "clk.h"
21
22enum comp {
23 _CORE,
24 _DDR,
25 _AXI,
26 BOOT_DVFS_MAX,
27};
28
29static unsigned long minfreq[BOOT_DVFS_MAX] = { 0, 0, 0 };
30static unsigned long bootfreq[BOOT_DVFS_MAX];
31static struct clk *clk[BOOT_DVFS_MAX];
32static char *clk_name[BOOT_DVFS_MAX] = {"cpu", "ddr", "axi"};
33
34static struct pm_qos_request sdh_core_qos_max, sdh_ddr_qos_max;
35static struct pm_qos_request sdh_core_qos_min;
36
37static int __init sdh_tuning_init(void)
38{
39#if defined(CONFIG_CPU_FREQ) && defined(CONFIG_PM_DEVFREQ)
40 struct cpufreq_frequency_table *cpufreq_table =
41 cpufreq_frequency_get_table(0);
42 struct devfreq_frequency_table *ddrfreq_table =
43 devfreq_frequency_get_table(DEVFREQ_DDR);
44
45 if (cpufreq_table)
46 minfreq[_CORE] = cpufreq_table[0].frequency;
47 else
48 pr_err("%s cpufreq_table get failed, use 0 to set!\n", __func__);
49
50 if (ddrfreq_table)
51 minfreq[_DDR] = ddrfreq_table[0].frequency;
52 else
53 pr_err("%s ddrfreq_table get failed, use 0 to set!\n", __func__);
54#else
55 pr_info("%s CONFIG_CPU_FREQ & CONFIG_PM_DEVFREQ not defined!\n", __func__);
56 minfreq[_CORE] = 0;
57 minfreq[_DDR] = 0;
58#endif
59
60 sdh_core_qos_max.name = "sdh_tuning_core_max";
61 pm_qos_add_request(&sdh_core_qos_max,
62 PM_QOS_CPUFREQ_MAX, PM_QOS_DEFAULT_VALUE);
63
64 sdh_core_qos_min.name = "sdh_tuning_core_min";
65 pm_qos_add_request(&sdh_core_qos_min,
66 PM_QOS_CPUFREQ_MIN, PM_QOS_DEFAULT_VALUE);
67
68 sdh_ddr_qos_max.name = "sdh_tuning_ddr_max";
69 pm_qos_add_request(&sdh_ddr_qos_max,
70 PM_QOS_DDR_DEVFREQ_MAX, PM_QOS_DEFAULT_VALUE);
71
72 return 0;
73}
74arch_initcall(sdh_tuning_init);
75
76int sdh_tunning_savefreq(void)
77{
78 int i;
79
80 for (i = 0; i < BOOT_DVFS_MAX; i++) {
81 if (unlikely(!clk[i])) {
82 clk[i] = clk_get(NULL, clk_name[i]);
83 if (!clk[i]) {
84 pr_err("failed to get clk %s\n", clk_name[i]);
85 return -1;
86 }
87 }
88
89 if (!bootfreq[i])
90 bootfreq[i] = clk_get_rate(clk[i]);
91
92 }
93#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
94 return 0;
95#else
96 clk_set_rate(clk[_AXI], minfreq[_AXI]*1000);
97 pm_qos_update_request(&sdh_core_qos_max, minfreq[_CORE]);
98 pm_qos_update_request(&sdh_ddr_qos_max, minfreq[_DDR]);
99#endif
100 return 0;
101}
102
103#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
104int sdh_tunning_updatefreq(unsigned int dvfs_level)
105{
106 long freq;
107
108 /* request PM_QOS_CPUFREQ_MAX */
109 freq = get_dvc_freq(0, dvfs_level);
110 if (freq >= 0) {
111 pm_qos_update_request(&sdh_core_qos_max, (s32)(freq/1000));
112 pm_qos_update_request(&sdh_core_qos_min, (s32)(freq/1000));
113 } else {
114 pm_qos_update_request(&sdh_core_qos_max, 0);
115 pr_err("fail to get CORE freq for dvfs level %d\n", dvfs_level);
116 }
117 /* request PM_QOS_DDR_DEVFREQ_MAX */
118 freq = get_dvc_freq(2, dvfs_level);
119 if (freq >= 0) {
120 pm_qos_update_request(&sdh_ddr_qos_max, (s32)(freq/1000));
121 } else {
122 pm_qos_update_request(&sdh_ddr_qos_max, 0);
123 pr_err("fail to get DDR freq for dvfs level %d\n", dvfs_level);
124 }
125
126 return 0;
127}
128#else
129int sdh_tunning_updatefreq(unsigned int dvfs_level)
130{
131 return 0;
132}
133#endif
134
135int sdh_tunning_restorefreq(void)
136{
137 int i;
138
139 pm_qos_update_request(&sdh_core_qos_max, PM_QOS_DEFAULT_VALUE);
140 pm_qos_update_request(&sdh_ddr_qos_max, PM_QOS_DEFAULT_VALUE);
141#if defined(CONFIG_ARM64) && defined(CONFIG_ASR_DVFS)
142 pm_qos_update_request(&sdh_core_qos_min, PM_QOS_DEFAULT_VALUE);
143#endif
144 for (i = 0; i < BOOT_DVFS_MAX; i++) {
145 if (!clk[i])
146 continue;
147 if (i != _CORE)
148 clk_set_rate(clk[i], bootfreq[i]);
149 }
150 return 0;
151}
152
153static unsigned int platvl_min, platvl_max;
154
155void plat_set_vl_min(u32 vl_num)
156{
157 platvl_min = vl_num;
158}
159
160unsigned int plat_get_vl_min(void)
161{
162 return platvl_min;
163}
164void plat_set_vl_max(u32 vl_num)
165{
166 platvl_max = vl_num;
167}
168
169unsigned int plat_get_vl_max(void)
170{
171 return platvl_max;
172}
173
174static struct cpmsa_dvc_info cpmsadvcinfo;
175
176/*
177 * This interface will be used by different platform to fill CP DVC info
178 */
179int fillcpdvcinfo(struct cpmsa_dvc_info *dvc_info)
180{
181 if (!dvc_info)
182 return -EINVAL;
183
184 memcpy(&cpmsadvcinfo, dvc_info, sizeof(struct cpmsa_dvc_info));
185 return 0;
186}
187
188/*
189 * This interface will be used by telephony to get CP DVC info, and
190 * they will use ACIPC to pass the info to CP
191 */
192int getcpdvcinfo(struct cpmsa_dvc_info *dvc_info)
193{
194 if (!dvc_info)
195 return -EINVAL;
196
197 memcpy(dvc_info, &cpmsadvcinfo, sizeof(struct cpmsa_dvc_info));
198 return 0;
199}
200EXPORT_SYMBOL(getcpdvcinfo);
201
202static struct ddr_dfc_info ddrdfc;
203
204/*
205 * This interface will be used by different platform to fill CP ddr dfc info
206 */
207int fillddrdfcinfo(struct ddr_dfc_info *dfc_info)
208{
209 if (!dfc_info)
210 return -EINVAL;
211
212 memcpy(&ddrdfc, dfc_info, sizeof(struct ddr_dfc_info));
213 return 0;
214}
215
216/*
217 * This interface will be used by telephony to get CP ddr dfc info, and
218 * they will use ACIPC to pass the info to CP
219 */
220int getddrdfcinfo(struct ddr_dfc_info *dfc_info)
221{
222 if (!dfc_info)
223 return -EINVAL;
224
225 memcpy(dfc_info, &ddrdfc, sizeof(struct ddr_dfc_info));
226 return 0;
227}
228EXPORT_SYMBOL(getddrdfcinfo);