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