1 /*
2 * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions are
6 * met:
7 * * * Redistributions of source code must retain the above copyright
8 * notice, this list of conditions and the following disclaimer.
9 * * Redistributions in binary form must reproduce the above
10 * copyright notice, this list of conditions and the following
11 * disclaimer in the documentation and/or other materials provided
12 * with the distribution.
13 * * Neither the name of The Linux Foundation nor the names of its
14 * contributors may be used to endorse or promote products derived
15 * from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
18 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
19 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
20 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
21 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 */
29
30 #define LOG_NIDEBUG 0
31
32 #include <errno.h>
33 #include <inttypes.h>
34 #include <string.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <dlfcn.h>
39 #include <stdlib.h>
40
41 #define LOG_TAG "QCOMPowerHAL"
42 #include <utils/Log.h>
43 #include <hardware/hardware.h>
44 #include <hardware/power.h>
45
46 #include "utils.h"
47 #include "metadata-defs.h"
48 #include "hint-data.h"
49 #include "performance.h"
50 #include "power-common.h"
51
52 #define PLATFORM_SLEEP_MODES 2
53 #define XO_VOTERS 3
54 #define VMIN_VOTERS 0
55
56 #define RPM_PARAMETERS 4
57 #define NUM_PARAMETERS 10
58
59 #ifndef RPM_STAT
60 #define RPM_STAT "/d/rpm_stats"
61 #endif
62
63 #ifndef RPM_MASTER_STAT
64 #define RPM_MASTER_STAT "/d/rpm_master_stats"
65 #endif
66
67 /* RPM runs at 19.2Mhz. Divide by 19200 for msec */
68 #define RPM_CLK 19200
69 #define USINSEC 1000000L
70 #define NSINUS 1000L
71
72 //interaction boost global variables
73 static pthread_mutex_t s_interaction_lock = PTHREAD_MUTEX_INITIALIZER;
74 static struct timespec s_previous_boost_timespec;
75
76 const char *parameter_names[] = {
77 "vlow_count",
78 "accumulated_vlow_time",
79 "vmin_count",
80 "accumulated_vmin_time",
81 "xo_accumulated_duration",
82 "xo_count",
83 "xo_accumulated_duration",
84 "xo_count",
85 "xo_accumulated_duration",
86 "xo_count"};
87
88 static int saved_dcvs_cpu0_slack_max = -1;
89 static int saved_dcvs_cpu0_slack_min = -1;
90 static int saved_mpdecision_slack_max = -1;
91 static int saved_mpdecision_slack_min = -1;
92 static int saved_interactive_mode = -1;
93 static int slack_node_rw_failed = 0;
94 static int display_hint_sent;
95 int display_boost;
96
97 static struct hw_module_methods_t power_module_methods = {
98 .open = NULL,
99 };
100
power_init(struct power_module * module)101 static void power_init(struct power_module *module)
102 {
103 ALOGI("QCOM power HAL initing.");
104
105 int fd;
106 char buf[10] = {0};
107
108 fd = open("/sys/devices/soc0/soc_id", O_RDONLY);
109 if (fd >= 0) {
110 if (read(fd, buf, sizeof(buf) - 1) == -1) {
111 ALOGW("Unable to read soc_id");
112 } else {
113 int soc_id = atoi(buf);
114 if (soc_id == 194 || (soc_id >= 208 && soc_id <= 218) || soc_id == 178) {
115 display_boost = 1;
116 }
117 }
118 close(fd);
119 }
120 }
121
process_video_decode_hint(void * metadata)122 static void process_video_decode_hint(void *metadata)
123 {
124 char governor[80];
125 struct video_decode_metadata_t video_decode_metadata;
126
127 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
128 ALOGE("Can't obtain scaling governor.");
129
130 return;
131 }
132
133 if (metadata) {
134 ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata);
135 }
136
137 /* Initialize encode metadata struct fields. */
138 memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t));
139 video_decode_metadata.state = -1;
140 video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID;
141
142 if (metadata) {
143 if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) ==
144 -1) {
145 ALOGE("Error occurred while parsing metadata.");
146 return;
147 }
148 } else {
149 return;
150 }
151
152 if (video_decode_metadata.state == 1) {
153 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
154 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
155 int resource_values[] = {THREAD_MIGRATION_SYNC_OFF};
156
157 perform_hint_action(video_decode_metadata.hint_id,
158 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
159 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
160 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
161 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF};
162
163 perform_hint_action(video_decode_metadata.hint_id,
164 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
165 }
166 } else if (video_decode_metadata.state == 0) {
167 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
168 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
169 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
170 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
171 undo_hint_action(video_decode_metadata.hint_id);
172 }
173 }
174 }
175
process_video_encode_hint(void * metadata)176 static void process_video_encode_hint(void *metadata)
177 {
178 char governor[80];
179 struct video_encode_metadata_t video_encode_metadata;
180
181 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
182 ALOGE("Can't obtain scaling governor.");
183
184 return;
185 }
186
187 /* Initialize encode metadata struct fields. */
188 memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t));
189 video_encode_metadata.state = -1;
190 video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID;
191
192 if (metadata) {
193 if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) ==
194 -1) {
195 ALOGE("Error occurred while parsing metadata.");
196 return;
197 }
198 } else {
199 return;
200 }
201
202 if (video_encode_metadata.state == 1) {
203 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
204 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
205 int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF};
206
207 perform_hint_action(video_encode_metadata.hint_id,
208 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
209 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
210 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
211 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF,
212 INTERACTIVE_IO_BUSY_OFF};
213
214 perform_hint_action(video_encode_metadata.hint_id,
215 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
216 }
217 } else if (video_encode_metadata.state == 0) {
218 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
219 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
220 undo_hint_action(video_encode_metadata.hint_id);
221 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
222 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
223 undo_hint_action(video_encode_metadata.hint_id);
224 }
225 }
226 }
227
power_hint_override(struct power_module * module,power_hint_t hint,void * data)228 int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint,
229 void *data)
230 {
231 return HINT_NONE;
232 }
233
234 /* Declare function before use */
235 int interaction(int duration, int num_args, int opt_list[]);
236 int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]);
237
calc_timespan_us(struct timespec start,struct timespec end)238 static long long calc_timespan_us(struct timespec start, struct timespec end) {
239 long long diff_in_us = 0;
240 diff_in_us += (end.tv_sec - start.tv_sec) * USINSEC;
241 diff_in_us += (end.tv_nsec - start.tv_nsec) / NSINUS;
242 return diff_in_us;
243 }
244
power_hint(struct power_module * module,power_hint_t hint,void * data)245 static void power_hint(struct power_module *module, power_hint_t hint,
246 void *data)
247 {
248 /* Check if this hint has been overridden. */
249 if (power_hint_override(module, hint, data) == HINT_HANDLED) {
250 /* The power_hint has been handled. We can skip the rest. */
251 return;
252 }
253
254 switch(hint) {
255 case POWER_HINT_VSYNC:
256 break;
257 case POWER_HINT_INTERACTION:
258 {
259 int duration_hint = 0;
260
261 // little core freq bump for 1.5s
262 int resources[] = {0x20C};
263 int duration = 1500;
264 static int handle_little = 0;
265
266 // big core freq bump for 500ms
267 int resources_big[] = {0x2312, 0x1F08};
268 int duration_big = 500;
269 static int handle_big = 0;
270
271 // sched_downmigrate lowered to 10 for 1s at most
272 // should be half of upmigrate
273 int resources_downmigrate[] = {0x4F00};
274 int duration_downmigrate = 1000;
275 static int handle_downmigrate = 0;
276
277 // sched_upmigrate lowered to at most 20 for 500ms
278 // set threshold based on elapsed time since last boost
279 int resources_upmigrate[] = {0x4E00};
280 int duration_upmigrate = 500;
281 static int handle_upmigrate = 0;
282
283 // set duration hint
284 if (data) {
285 duration_hint = *((int*)data);
286 }
287
288 struct timespec cur_boost_timespec;
289 clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec);
290
291 pthread_mutex_lock(&s_interaction_lock);
292 long long elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec);
293
294 if (elapsed_time > 750000)
295 elapsed_time = 750000;
296 // don't hint if it's been less than 250ms since last boost
297 // also detect if we're doing anything resembling a fling
298 // support additional boosting in case of flings
299 else if (elapsed_time < 250000 && duration_hint <= 750) {
300 pthread_mutex_unlock(&s_interaction_lock);
301 return;
302 }
303
304 s_previous_boost_timespec = cur_boost_timespec;
305 pthread_mutex_unlock(&s_interaction_lock);
306
307 // 95: default upmigrate for phone
308 // 20: upmigrate for sporadic touch
309 // 750ms: a completely arbitrary threshold for last touch
310 int upmigrate_value = 95 - (int)(75. * ((elapsed_time*elapsed_time) / (750000.*750000.)));
311
312 // keep sched_upmigrate high when flinging
313 if (duration_hint >= 750)
314 upmigrate_value = 20;
315
316 resources_upmigrate[0] = resources_upmigrate[0] | upmigrate_value;
317 resources_downmigrate[0] = resources_downmigrate[0] | (upmigrate_value / 2);
318
319 // modify downmigrate duration based on interaction data hint
320 // 1000 <= duration_downmigrate <= 5000
321 // extend little core freq bump past downmigrate to soften downmigrates
322 if (duration_hint > 1000) {
323 if (duration_hint < 5000) {
324 duration_downmigrate = duration_hint;
325 duration = duration_hint + 750;
326 } else {
327 duration_downmigrate = 5000;
328 duration = 5750;
329 }
330 }
331
332 handle_little = interaction_with_handle(handle_little,duration, sizeof(resources)/sizeof(resources[0]), resources);
333 handle_big = interaction_with_handle(handle_big, duration_big, sizeof(resources_big)/sizeof(resources_big[0]), resources_big);
334 handle_downmigrate = interaction_with_handle(handle_downmigrate, duration_downmigrate, sizeof(resources_downmigrate)/sizeof(resources_downmigrate[0]), resources_downmigrate);
335 handle_upmigrate = interaction_with_handle(handle_upmigrate, duration_upmigrate, sizeof(resources_upmigrate)/sizeof(resources_upmigrate[0]), resources_upmigrate);
336 }
337 break;
338 case POWER_HINT_VIDEO_ENCODE:
339 process_video_encode_hint(data);
340 break;
341 case POWER_HINT_VIDEO_DECODE:
342 process_video_decode_hint(data);
343 break;
344 }
345 }
346
set_interactive_override(struct power_module * module,int on)347 int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
348 {
349 return HINT_NONE;
350 }
351
set_interactive(struct power_module * module,int on)352 void set_interactive(struct power_module *module, int on)
353 {
354 char governor[80];
355 char tmp_str[NODE_MAX];
356 struct video_encode_metadata_t video_encode_metadata;
357 int rc;
358
359 if (set_interactive_override(module, on) == HINT_HANDLED) {
360 return;
361 }
362
363 ALOGI("Got set_interactive hint");
364
365 if (get_scaling_governor(governor, sizeof(governor)) == -1) {
366 ALOGE("Can't obtain scaling governor.");
367
368 return;
369 }
370
371 if (!on) {
372 /* Display off. */
373 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
374 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
375 int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF};
376
377 if (!display_hint_sent) {
378 perform_hint_action(DISPLAY_STATE_HINT_ID,
379 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
380 display_hint_sent = 1;
381 }
382 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
383 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
384 int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF};
385
386 if (!display_hint_sent) {
387 perform_hint_action(DISPLAY_STATE_HINT_ID,
388 resource_values, sizeof(resource_values)/sizeof(resource_values[0]));
389 display_hint_sent = 1;
390 }
391 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
392 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
393 if (saved_interactive_mode == 1){
394 /* Display turned off. */
395 if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
396 if (!slack_node_rw_failed) {
397 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE);
398 }
399
400 rc = 1;
401 } else {
402 saved_dcvs_cpu0_slack_max = atoi(tmp_str);
403 }
404
405 if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
406 if (!slack_node_rw_failed) {
407 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE);
408 }
409
410 rc = 1;
411 } else {
412 saved_dcvs_cpu0_slack_min = atoi(tmp_str);
413 }
414
415 if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) {
416 if (!slack_node_rw_failed) {
417 ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE);
418 }
419
420 rc = 1;
421 } else {
422 saved_mpdecision_slack_max = atoi(tmp_str);
423 }
424
425 if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) {
426 if(!slack_node_rw_failed) {
427 ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE);
428 }
429
430 rc = 1;
431 } else {
432 saved_mpdecision_slack_min = atoi(tmp_str);
433 }
434
435 /* Write new values. */
436 if (saved_dcvs_cpu0_slack_max != -1) {
437 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max);
438
439 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
440 if (!slack_node_rw_failed) {
441 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
442 }
443
444 rc = 1;
445 }
446 }
447
448 if (saved_dcvs_cpu0_slack_min != -1) {
449 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min);
450
451 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
452 if(!slack_node_rw_failed) {
453 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
454 }
455
456 rc = 1;
457 }
458 }
459
460 if (saved_mpdecision_slack_max != -1) {
461 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max);
462
463 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
464 if(!slack_node_rw_failed) {
465 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
466 }
467
468 rc = 1;
469 }
470 }
471
472 if (saved_mpdecision_slack_min != -1) {
473 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min);
474
475 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
476 if(!slack_node_rw_failed) {
477 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
478 }
479
480 rc = 1;
481 }
482 }
483 }
484
485 slack_node_rw_failed = rc;
486 }
487 } else {
488 /* Display on. */
489 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) &&
490 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) {
491 undo_hint_action(DISPLAY_STATE_HINT_ID);
492 display_hint_sent = 0;
493 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) &&
494 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) {
495 undo_hint_action(DISPLAY_STATE_HINT_ID);
496 display_hint_sent = 0;
497 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) &&
498 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) {
499 if (saved_interactive_mode == -1 || saved_interactive_mode == 0) {
500 /* Display turned on. Restore if possible. */
501 if (saved_dcvs_cpu0_slack_max != -1) {
502 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max);
503
504 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) {
505 if (!slack_node_rw_failed) {
506 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE);
507 }
508
509 rc = 1;
510 }
511 }
512
513 if (saved_dcvs_cpu0_slack_min != -1) {
514 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min);
515
516 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) {
517 if (!slack_node_rw_failed) {
518 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE);
519 }
520
521 rc = 1;
522 }
523 }
524
525 if (saved_mpdecision_slack_max != -1) {
526 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max);
527
528 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) {
529 if (!slack_node_rw_failed) {
530 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE);
531 }
532
533 rc = 1;
534 }
535 }
536
537 if (saved_mpdecision_slack_min != -1) {
538 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min);
539
540 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) {
541 if (!slack_node_rw_failed) {
542 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE);
543 }
544
545 rc = 1;
546 }
547 }
548 }
549
550 slack_node_rw_failed = rc;
551 }
552 }
553
554 saved_interactive_mode = !!on;
555 }
556
get_number_of_platform_modes(struct power_module * module)557 static ssize_t get_number_of_platform_modes(struct power_module *module) {
558 return PLATFORM_SLEEP_MODES;
559 }
560
get_voter_list(struct power_module * module,size_t * voter)561 static int get_voter_list(struct power_module *module, size_t *voter) {
562 voter[0] = XO_VOTERS;
563 voter[1] = VMIN_VOTERS;
564
565 return 0;
566 }
567
extract_stats(uint64_t * list,char * file,unsigned int num_parameters,unsigned int index)568 static int extract_stats(uint64_t *list, char *file,
569 unsigned int num_parameters, unsigned int index) {
570 FILE *fp;
571 ssize_t read;
572 size_t len;
573 char *line;
574 int ret;
575
576 fp = fopen(file, "r");
577 if (fp == NULL) {
578 ret = -errno;
579 ALOGE("%s: failed to open: %s", __func__, strerror(errno));
580 return ret;
581 }
582
583 for (line = NULL, len = 0;
584 ((read = getline(&line, &len, fp) != -1) && (index < num_parameters));
585 free(line), line = NULL, len = 0) {
586 uint64_t value;
587 char* offset;
588
589 size_t begin = strspn(line, " \t");
590 if (strncmp(line + begin, parameter_names[index], strlen(parameter_names[index]))) {
591 continue;
592 }
593
594 offset = memchr(line, ':', len);
595 if (!offset) {
596 continue;
597 }
598
599 if (!strcmp(file, RPM_MASTER_STAT)) {
600 /* RPM_MASTER_STAT is reported in hex */
601 sscanf(offset, ":%" SCNx64, &value);
602 /* Duration is reported in rpm SLEEP TICKS */
603 if (!strcmp(parameter_names[index], "xo_accumulated_duration")) {
604 value /= RPM_CLK;
605 }
606 } else {
607 /* RPM_STAT is reported in decimal */
608 sscanf(offset, ":%" SCNu64, &value);
609 }
610 list[index] = value;
611 index++;
612 }
613 free(line);
614
615 fclose(fp);
616 return 0;
617 }
618
get_platform_low_power_stats(struct power_module * module,power_state_platform_sleep_state_t * list)619 static int get_platform_low_power_stats(struct power_module *module,
620 power_state_platform_sleep_state_t *list) {
621 uint64_t stats[sizeof(parameter_names)] = {0};
622 int ret;
623
624 if (!list) {
625 return -EINVAL;
626 }
627
628 ret = extract_stats(stats, RPM_STAT, RPM_PARAMETERS, 0);
629
630 if (ret) {
631 return ret;
632 }
633
634 ret = extract_stats(stats, RPM_MASTER_STAT, NUM_PARAMETERS, 4);
635
636 if (ret) {
637 return ret;
638 }
639
640 /* Update statistics for XO_shutdown */
641 strcpy(list[0].name, "XO_shutdown");
642 list[0].total_transitions = stats[0];
643 list[0].residency_in_msec_since_boot = stats[1];
644 list[0].supported_only_in_suspend = false;
645 list[0].number_of_voters = XO_VOTERS;
646
647 /* Update statistics for APSS voter */
648 strcpy(list[0].voters[0].name, "APSS");
649 list[0].voters[0].total_time_in_msec_voted_for_since_boot = stats[4];
650 list[0].voters[0].total_number_of_times_voted_since_boot = stats[5];
651
652 /* Update statistics for MPSS voter */
653 strcpy(list[0].voters[1].name, "MPSS");
654 list[0].voters[1].total_time_in_msec_voted_for_since_boot = stats[6];
655 list[0].voters[1].total_number_of_times_voted_since_boot = stats[7];
656
657 /* Update statistics for LPASS voter */
658 strcpy(list[0].voters[2].name, "LPASS");
659 list[0].voters[2].total_time_in_msec_voted_for_since_boot = stats[8];
660 list[0].voters[2].total_number_of_times_voted_since_boot = stats[9];
661
662 /* Update statistics for VMIN state */
663 strcpy(list[1].name, "VMIN");
664 list[1].total_transitions = stats[2];
665 list[1].residency_in_msec_since_boot = stats[3];
666 list[1].supported_only_in_suspend = false;
667 list[1].number_of_voters = VMIN_VOTERS;
668
669 return 0;
670 }
671
672 struct power_module HAL_MODULE_INFO_SYM = {
673 .common = {
674 .tag = HARDWARE_MODULE_TAG,
675 .module_api_version = POWER_MODULE_API_VERSION_0_5,
676 .hal_api_version = HARDWARE_HAL_API_VERSION,
677 .id = POWER_HARDWARE_MODULE_ID,
678 .name = "QCOM Power HAL",
679 .author = "Qualcomm",
680 .methods = &power_module_methods,
681 },
682
683 .init = power_init,
684 .powerHint = power_hint,
685 .setInteractive = set_interactive,
686 .get_number_of_platform_modes = get_number_of_platform_modes,
687 .get_platform_low_power_stats = get_platform_low_power_stats,
688 .get_voter_list = get_voter_list
689 };
690