blob: 19cbed57f4e4a7705a9e6644816e323669e02edd [file] [log] [blame]
rjw1f884582022-01-06 17:20:42 +08001/*
2 * Copyright (C) 2018 MediaTek Inc.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License version 2 as
6 * published by the Free Software Foundation.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 */
13
14#include <linux/sched.h>
15#include <linux/kernel.h>
16#include <linux/module.h>
17#include <linux/random.h>
18#include <linux/fs.h>
19
20#include "met_drv.h"
21#include "trace.h"
22#include "core_plf_init.h"
23#include "core_plf_trace.h"
24
25
26static unsigned int MT_GPU_DVFS_IDX = NR_MT_CPU_DVFS;
27static unsigned int g_u4GPUVolt;
28static unsigned int g_u4Volt[NR_MT_CPU_DVFS + 1];
29
30/* g_ap_ptpod: cpu volt from ap or sspm setting
31 * if 1: cpu volt from ap
32 * if 0: cpu volt from sspm */
33static unsigned int g_ap_ptpod = 1;
34
35/* gpu_volt_enable:
36 * if 1: enable gpu volt to output
37 * if 0: disable gpu volt to output */
38static unsigned int gpu_volt_enable = 1;
39
40/* get_volt_by_wq:
41 * if 1: get cpu/gpu volt by workqueue
42 * if 0: get cpu/gpu volt in irq */
43static unsigned int get_volt_by_wq;
44
45static int ptpod_started;
46static struct kobject *kobj_ptpod;
47static struct delayed_work get_volt_dwork;
48
49noinline void ms_ptpod(void)
50{
51 char *SOB, *EOB;
52
53 if (g_ap_ptpod) {
54 MET_TRACE_GETBUF(&SOB, &EOB);
55
56 if (gpu_volt_enable) {
57 g_u4Volt[MT_GPU_DVFS_IDX] = g_u4GPUVolt;
58 EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt), g_u4Volt);
59 } else
60 EOB = ms_formatD_EOL(EOB, ARRAY_SIZE(g_u4Volt) - 1, g_u4Volt);
61
62 MET_TRACE_PUTBUF(SOB, EOB);
63 } else {
64 if (gpu_volt_enable) {
65 MET_TRACE_GETBUF(&SOB, &EOB);
66 EOB = ms_formatD_EOL(EOB, 1, &g_u4GPUVolt);
67 MET_TRACE_PUTBUF(SOB, EOB);
68 }
69 }
70}
71
72#if 0
73static void ptpod_cpu_voltSampler(enum mt_cpu_dvfs_id id, unsigned int volt)
74{
75 switch (id) {
76 case MT_CPU_DVFS_LL:
77 g_u4CPUVolt_LL = volt;
78 break;
79 case MT_CPU_DVFS_L:
80 g_u4CPUVolt_L = volt;
81 break;
82 case MT_CPU_DVFS_CCI:
83 g_u4CPUVolt_CCI = volt;
84 break;
85 default:
86 return;
87 }
88 ptpod();
89}
90#endif
91
92#if 0
93static void ptpod_gpu_voltSampler(unsigned int a_u4Volt)
94{
95 g_u4GPUVolt = (a_u4Volt+50)/100;
96
97 if (ptpod_started)
98 ptpod();
99}
100#endif
101
102#define PTPOD_CONF_SHOW_IMPLEMENT(var) \
103 do { \
104 int i; \
105 i = snprintf(buf, PAGE_SIZE, "%d\n", var); \
106 return i; \
107 } while (0)
108
109#define PTPOD_CONF_STORE_IMPLEMENT(var) \
110 do { \
111 int value; \
112 if ((n == 0) || (buf == NULL)) \
113 return -EINVAL; \
114 if (kstrtoint(buf, 0, &value) != 0) \
115 return -EINVAL; \
116 if (value == 1) \
117 var = 1; \
118 else \
119 var = 0; \
120 return n; \
121 } while (0)
122
123
124static ssize_t ap_ptpod_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
125{
126 PTPOD_CONF_SHOW_IMPLEMENT(g_ap_ptpod);
127}
128
129static ssize_t ap_ptpod_enable_store(struct kobject *kobj,
130 struct kobj_attribute *attr,
131 const char *buf,
132 size_t n)
133{
134 PTPOD_CONF_STORE_IMPLEMENT(g_ap_ptpod);
135}
136
137static ssize_t gpu_volt_enable_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
138{
139 PTPOD_CONF_SHOW_IMPLEMENT(gpu_volt_enable);
140}
141
142static ssize_t gpu_volt_enable_store(struct kobject *kobj,
143 struct kobj_attribute *attr,
144 const char *buf,
145 size_t n)
146{
147 PTPOD_CONF_STORE_IMPLEMENT(gpu_volt_enable);
148}
149
150static ssize_t get_volt_by_wq_show(struct kobject *kobj, struct kobj_attribute *attr, char *buf)
151{
152 PTPOD_CONF_SHOW_IMPLEMENT(get_volt_by_wq);
153}
154
155static ssize_t get_volt_by_wq_store(struct kobject *kobj,
156 struct kobj_attribute *attr,
157 const char *buf,
158 size_t n)
159{
160 PTPOD_CONF_STORE_IMPLEMENT(get_volt_by_wq);
161}
162
163static struct kobj_attribute ap_ptpod_enable_attr = __ATTR(ap_ptpod_enable, 0664, ap_ptpod_enable_show, ap_ptpod_enable_store);
164static struct kobj_attribute gpu_volt_enable_attr = __ATTR(gpu_volt_enable, 0664, gpu_volt_enable_show, gpu_volt_enable_store);
165static struct kobj_attribute get_volt_by_wq_attr = __ATTR(get_volt_by_wq, 0664, get_volt_by_wq_show, get_volt_by_wq_store);
166
167/* create ptpod related kobj node */
168#define KOBJ_ATTR_LIST \
169 do { \
170 KOBJ_ATTR_ITEM(ap_ptpod_enable); \
171 KOBJ_ATTR_ITEM(gpu_volt_enable); \
172 KOBJ_ATTR_ITEM(get_volt_by_wq); \
173 } while (0)
174
175static int ptpod_create(struct kobject *parent)
176{
177 int ret = 0;
178
179 kobj_ptpod = parent;
180
181#define KOBJ_ATTR_ITEM(attr_name) \
182 do { \
183 ret = sysfs_create_file(kobj_ptpod, &attr_name ## _attr.attr); \
184 if (ret != 0) { \
185 pr_notice("Failed to create " #attr_name " in sysfs\n"); \
186 return ret; \
187 } \
188 } while (0)
189 KOBJ_ATTR_LIST;
190#undef KOBJ_ATTR_ITEM
191
192 return 0;
193}
194
195static void ptpod_delete(void)
196{
197#define KOBJ_ATTR_ITEM(attr_name) \
198 sysfs_remove_file(kobj_ptpod, &attr_name ## _attr.attr)
199
200 if (kobj_ptpod != NULL) {
201 KOBJ_ATTR_LIST;
202 kobj_ptpod = NULL;
203 }
204#undef KOBJ_ATTR_ITEM
205}
206
207static void update_volt_value(void)
208{
209 int i;
210
211 if (g_ap_ptpod && mt_cpufreq_get_cur_volt_symbol) {
212 /*
213 g_u4CPUVolt_LL = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_LL)/100;
214 g_u4CPUVolt_L = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_L)/100;
215 g_u4CPUVolt_CCI = mt_cpufreq_get_cur_volt_symbol(MT_CPU_DVFS_CCI)/100;
216 */
217 for (i = 0; i < NR_MT_CPU_DVFS; i++)
218 g_u4Volt[i] = mt_cpufreq_get_cur_volt_symbol(i) / 100;
219 }
220
221 if (gpu_volt_enable) {
222 if (mt_gpufreq_get_cur_volt_symbol)
223 g_u4GPUVolt = ((mt_gpufreq_get_cur_volt_symbol() + 50) / 100);
224 }
225}
226
227static void get_volt_notify(unsigned long long stamp, int cpu)
228{
229 schedule_delayed_work(&get_volt_dwork, 0);
230}
231
232static void met_ptpod_polling_by_wq(struct work_struct *work)
233{
234 update_volt_value();
235
236 ms_ptpod();
237}
238
239static void met_ptpod_polling(unsigned long long stamp, int cpu)
240{
241 update_volt_value();
242
243 ms_ptpod();
244}
245
246/*
247 * Called from "met-cmd --start"
248 */
249static void ptpod_start(void)
250{
251#if 0
252 met_gpufreq_setvolt_registerCB(ptpod_gpu_voltSampler, kFOR_MET_PTPOD_USE);
253#endif
254
255 if (get_volt_by_wq) {
256 met_ptpod.timed_polling = get_volt_notify;
257
258 INIT_DELAYED_WORK(&get_volt_dwork, met_ptpod_polling_by_wq);
259 } else
260 met_ptpod.timed_polling = met_ptpod_polling;
261
262 update_volt_value();
263
264 ms_ptpod();
265
266#if 0
267 /* register callback */
268 if (mt_cpufreq_setvolt_registerCB_symbol)
269 mt_cpufreq_setvolt_registerCB_symbol(ptpod_cpu_voltSampler);
270#endif
271
272 ptpod_started = 1;
273}
274
275/*
276 * Called from "met-cmd --stop"
277 */
278static void ptpod_stop(void)
279{
280 ptpod_started = 0;
281
282#if 0
283 /* unregister callback */
284 if (mt_cpufreq_setvolt_registerCB_symbol)
285 mt_cpufreq_setvolt_registerCB_symbol(NULL);
286#endif
287
288 if (get_volt_by_wq)
289 cancel_delayed_work_sync(&get_volt_dwork);
290
291 update_volt_value();
292
293 ms_ptpod();
294
295#if 0
296 met_gpufreq_setvolt_registerCB(NULL, kFOR_MET_PTPOD_USE);
297#endif
298}
299
300static char help[] =
301 " --ptpod Measure CPU/GPU voltage\n";
302static int ptpod_print_help(char *buf, int len)
303{
304 return snprintf(buf, PAGE_SIZE, help);
305}
306
307/*
308 * It will be called back when run "met-cmd --extract" and mode is 1
309 */
310static int ptpod_print_header(char *buf, int len)
311{
312 int str_len = 0;
313
314 if (g_ap_ptpod) {
315 int i;
316
317 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
318 "met-info [000] 0.0: met_ptpod_header: ");
319
320 for (i = 0; i < NR_MT_CPU_DVFS; i++)
321 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "CPUVolt_%d,", i);
322
323 if (gpu_volt_enable)
324 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
325
326 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
327 "met-info [000] 0.0: met_ptpod_version: ap\n");
328 } else {
329 if (gpu_volt_enable) {
330 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
331 "met-info [000] 0.0: met_ptpod_header: ");
332
333 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len, "GPUVolt\n");
334 }
335
336 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
337 "met-info [000] 0.0: met_ptpod_version: sspm\n");
338 }
339
340 if (gpu_volt_enable)
341 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
342 "met-info [000] 0.0: met_ptpod_gpu_volt_enable: YES\n");
343 else
344 str_len += snprintf(buf + str_len, PAGE_SIZE - str_len,
345 "met-info [000] 0.0: met_ptpod_gpu_volt_enable: NO\n");
346
347 return str_len;
348}
349
350struct metdevice met_ptpod = {
351 .name = "ptpod",
352 .owner = THIS_MODULE,
353 .type = MET_TYPE_PMU,
354 .cpu_related = 0,
355 .create_subfs = ptpod_create,
356 .delete_subfs = ptpod_delete,
357 .start = ptpod_start,
358 .stop = ptpod_stop,
359 .timed_polling = met_ptpod_polling,
360 .print_help = ptpod_print_help,
361 .print_header = ptpod_print_header,
362};
363EXPORT_SYMBOL(met_ptpod);