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 void power_init(struct power_module *module) 98 { 99 ALOGI("QCOM power HAL initing."); 100 101 int fd; 102 char buf[10] = {0}; 103 104 fd = open("/sys/devices/soc0/soc_id", O_RDONLY); 105 if (fd >= 0) { 106 if (read(fd, buf, sizeof(buf) - 1) == -1) { 107 ALOGW("Unable to read soc_id"); 108 } else { 109 int soc_id = atoi(buf); 110 if (soc_id == 194 || (soc_id >= 208 && soc_id <= 218) || soc_id == 178) { 111 display_boost = 1; 112 } 113 } 114 close(fd); 115 } 116 } 117 118 static void process_video_decode_hint(void *metadata) 119 { 120 char governor[80]; 121 struct video_decode_metadata_t video_decode_metadata; 122 123 if (get_scaling_governor(governor, sizeof(governor)) == -1) { 124 ALOGE("Can't obtain scaling governor."); 125 126 return; 127 } 128 129 if (metadata) { 130 ALOGI("Processing video decode hint. Metadata: %s", (char *)metadata); 131 } 132 133 /* Initialize encode metadata struct fields. */ 134 memset(&video_decode_metadata, 0, sizeof(struct video_decode_metadata_t)); 135 video_decode_metadata.state = -1; 136 video_decode_metadata.hint_id = DEFAULT_VIDEO_DECODE_HINT_ID; 137 138 if (metadata) { 139 if (parse_video_decode_metadata((char *)metadata, &video_decode_metadata) == 140 -1) { 141 ALOGE("Error occurred while parsing metadata."); 142 return; 143 } 144 } else { 145 return; 146 } 147 148 if (video_decode_metadata.state == 1) { 149 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 150 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 151 int resource_values[] = {THREAD_MIGRATION_SYNC_OFF}; 152 153 perform_hint_action(video_decode_metadata.hint_id, 154 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 155 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 156 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 157 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF}; 158 159 perform_hint_action(video_decode_metadata.hint_id, 160 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 161 } 162 } else if (video_decode_metadata.state == 0) { 163 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 164 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 165 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 166 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 167 undo_hint_action(video_decode_metadata.hint_id); 168 } 169 } 170 } 171 172 static void process_video_encode_hint(void *metadata) 173 { 174 char governor[80]; 175 struct video_encode_metadata_t video_encode_metadata; 176 177 if (get_scaling_governor(governor, sizeof(governor)) == -1) { 178 ALOGE("Can't obtain scaling governor."); 179 180 return; 181 } 182 183 /* Initialize encode metadata struct fields. */ 184 memset(&video_encode_metadata, 0, sizeof(struct video_encode_metadata_t)); 185 video_encode_metadata.state = -1; 186 video_encode_metadata.hint_id = DEFAULT_VIDEO_ENCODE_HINT_ID; 187 188 if (metadata) { 189 if (parse_video_encode_metadata((char *)metadata, &video_encode_metadata) == 190 -1) { 191 ALOGE("Error occurred while parsing metadata."); 192 return; 193 } 194 } else { 195 return; 196 } 197 198 if (video_encode_metadata.state == 1) { 199 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 200 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 201 int resource_values[] = {IO_BUSY_OFF, SAMPLING_DOWN_FACTOR_1, THREAD_MIGRATION_SYNC_OFF}; 202 203 perform_hint_action(video_encode_metadata.hint_id, 204 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 205 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 206 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 207 int resource_values[] = {TR_MS_30, HISPEED_LOAD_90, HS_FREQ_1026, THREAD_MIGRATION_SYNC_OFF, 208 INTERACTIVE_IO_BUSY_OFF}; 209 210 perform_hint_action(video_encode_metadata.hint_id, 211 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 212 } 213 } else if (video_encode_metadata.state == 0) { 214 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 215 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 216 undo_hint_action(video_encode_metadata.hint_id); 217 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 218 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 219 undo_hint_action(video_encode_metadata.hint_id); 220 } 221 } 222 } 223 224 int __attribute__ ((weak)) power_hint_override(struct power_module *module, power_hint_t hint, 225 void *data) 226 { 227 return HINT_NONE; 228 } 229 230 /* Declare function before use */ 231 int interaction(int duration, int num_args, int opt_list[]); 232 int interaction_with_handle(int lock_handle, int duration, int num_args, int opt_list[]); 233 234 static long long calc_timespan_us(struct timespec start, struct timespec end) { 235 long long diff_in_us = 0; 236 diff_in_us += (end.tv_sec - start.tv_sec) * USINSEC; 237 diff_in_us += (end.tv_nsec - start.tv_nsec) / NSINUS; 238 return diff_in_us; 239 } 240 241 static void power_hint(struct power_module *module, power_hint_t hint, 242 void *data) 243 { 244 /* Check if this hint has been overridden. */ 245 if (power_hint_override(module, hint, data) == HINT_HANDLED) { 246 /* The power_hint has been handled. We can skip the rest. */ 247 return; 248 } 249 250 switch(hint) { 251 case POWER_HINT_VSYNC: 252 break; 253 case POWER_HINT_INTERACTION: 254 { 255 int duration_hint = 0; 256 257 // little core freq bump for 1.5s 258 int resources[] = {0x20C}; 259 int duration = 1500; 260 static int handle_little = 0; 261 262 // big core freq bump for 500ms 263 int resources_big[] = {0x2312, 0x1F08}; 264 int duration_big = 500; 265 static int handle_big = 0; 266 267 // sched_downmigrate lowered to 10 for 1s at most 268 // should be half of upmigrate 269 int resources_downmigrate[] = {0x4F00}; 270 int duration_downmigrate = 1000; 271 static int handle_downmigrate = 0; 272 273 // sched_upmigrate lowered to at most 20 for 500ms 274 // set threshold based on elapsed time since last boost 275 int resources_upmigrate[] = {0x4E00}; 276 int duration_upmigrate = 500; 277 static int handle_upmigrate = 0; 278 279 // set duration hint 280 if (data) { 281 duration_hint = *((int*)data); 282 } 283 284 struct timespec cur_boost_timespec; 285 clock_gettime(CLOCK_MONOTONIC, &cur_boost_timespec); 286 287 pthread_mutex_lock(&s_interaction_lock); 288 long long elapsed_time = calc_timespan_us(s_previous_boost_timespec, cur_boost_timespec); 289 290 if (elapsed_time > 750000) 291 elapsed_time = 750000; 292 // don't hint if it's been less than 250ms since last boost 293 // also detect if we're doing anything resembling a fling 294 // support additional boosting in case of flings 295 else if (elapsed_time < 250000 && duration_hint <= 750) { 296 pthread_mutex_unlock(&s_interaction_lock); 297 return; 298 } 299 300 s_previous_boost_timespec = cur_boost_timespec; 301 pthread_mutex_unlock(&s_interaction_lock); 302 303 // 95: default upmigrate for phone 304 // 20: upmigrate for sporadic touch 305 // 750ms: a completely arbitrary threshold for last touch 306 int upmigrate_value = 95 - (int)(75. * ((elapsed_time*elapsed_time) / (750000.*750000.))); 307 308 // keep sched_upmigrate high when flinging 309 if (duration_hint >= 750) 310 upmigrate_value = 20; 311 312 resources_upmigrate[0] = resources_upmigrate[0] | upmigrate_value; 313 resources_downmigrate[0] = resources_downmigrate[0] | (upmigrate_value / 2); 314 315 // modify downmigrate duration based on interaction data hint 316 // 1000 <= duration_downmigrate <= 5000 317 // extend little core freq bump past downmigrate to soften downmigrates 318 if (duration_hint > 1000) { 319 if (duration_hint < 5000) { 320 duration_downmigrate = duration_hint; 321 duration = duration_hint + 750; 322 } else { 323 duration_downmigrate = 5000; 324 duration = 5750; 325 } 326 } 327 328 handle_little = interaction_with_handle(handle_little,duration, sizeof(resources)/sizeof(resources[0]), resources); 329 handle_big = interaction_with_handle(handle_big, duration_big, sizeof(resources_big)/sizeof(resources_big[0]), resources_big); 330 handle_downmigrate = interaction_with_handle(handle_downmigrate, duration_downmigrate, sizeof(resources_downmigrate)/sizeof(resources_downmigrate[0]), resources_downmigrate); 331 handle_upmigrate = interaction_with_handle(handle_upmigrate, duration_upmigrate, sizeof(resources_upmigrate)/sizeof(resources_upmigrate[0]), resources_upmigrate); 332 } 333 break; 334 case POWER_HINT_VIDEO_ENCODE: 335 process_video_encode_hint(data); 336 break; 337 case POWER_HINT_VIDEO_DECODE: 338 process_video_decode_hint(data); 339 break; 340 } 341 } 342 343 int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on) 344 { 345 return HINT_NONE; 346 } 347 348 void set_interactive(struct power_module *module, int on) 349 { 350 char governor[80]; 351 char tmp_str[NODE_MAX]; 352 struct video_encode_metadata_t video_encode_metadata; 353 int rc; 354 355 if (set_interactive_override(module, on) == HINT_HANDLED) { 356 return; 357 } 358 359 ALOGI("Got set_interactive hint"); 360 361 if (get_scaling_governor(governor, sizeof(governor)) == -1) { 362 ALOGE("Can't obtain scaling governor."); 363 364 return; 365 } 366 367 if (!on) { 368 /* Display off. */ 369 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 370 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 371 int resource_values[] = {DISPLAY_OFF, MS_500, THREAD_MIGRATION_SYNC_OFF}; 372 373 if (!display_hint_sent) { 374 perform_hint_action(DISPLAY_STATE_HINT_ID, 375 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 376 display_hint_sent = 1; 377 } 378 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 379 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 380 int resource_values[] = {TR_MS_50, THREAD_MIGRATION_SYNC_OFF}; 381 382 if (!display_hint_sent) { 383 perform_hint_action(DISPLAY_STATE_HINT_ID, 384 resource_values, sizeof(resource_values)/sizeof(resource_values[0])); 385 display_hint_sent = 1; 386 } 387 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && 388 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { 389 if (saved_interactive_mode == 1){ 390 /* Display turned off. */ 391 if (sysfs_read(DCVS_CPU0_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { 392 if (!slack_node_rw_failed) { 393 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MAX_NODE); 394 } 395 396 rc = 1; 397 } else { 398 saved_dcvs_cpu0_slack_max = atoi(tmp_str); 399 } 400 401 if (sysfs_read(DCVS_CPU0_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { 402 if (!slack_node_rw_failed) { 403 ALOGE("Failed to read from %s", DCVS_CPU0_SLACK_MIN_NODE); 404 } 405 406 rc = 1; 407 } else { 408 saved_dcvs_cpu0_slack_min = atoi(tmp_str); 409 } 410 411 if (sysfs_read(MPDECISION_SLACK_MAX_NODE, tmp_str, NODE_MAX - 1)) { 412 if (!slack_node_rw_failed) { 413 ALOGE("Failed to read from %s", MPDECISION_SLACK_MAX_NODE); 414 } 415 416 rc = 1; 417 } else { 418 saved_mpdecision_slack_max = atoi(tmp_str); 419 } 420 421 if (sysfs_read(MPDECISION_SLACK_MIN_NODE, tmp_str, NODE_MAX - 1)) { 422 if(!slack_node_rw_failed) { 423 ALOGE("Failed to read from %s", MPDECISION_SLACK_MIN_NODE); 424 } 425 426 rc = 1; 427 } else { 428 saved_mpdecision_slack_min = atoi(tmp_str); 429 } 430 431 /* Write new values. */ 432 if (saved_dcvs_cpu0_slack_max != -1) { 433 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_max); 434 435 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { 436 if (!slack_node_rw_failed) { 437 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); 438 } 439 440 rc = 1; 441 } 442 } 443 444 if (saved_dcvs_cpu0_slack_min != -1) { 445 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_dcvs_cpu0_slack_min); 446 447 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { 448 if(!slack_node_rw_failed) { 449 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); 450 } 451 452 rc = 1; 453 } 454 } 455 456 if (saved_mpdecision_slack_max != -1) { 457 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_max); 458 459 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { 460 if(!slack_node_rw_failed) { 461 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); 462 } 463 464 rc = 1; 465 } 466 } 467 468 if (saved_mpdecision_slack_min != -1) { 469 snprintf(tmp_str, NODE_MAX, "%d", 10 * saved_mpdecision_slack_min); 470 471 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { 472 if(!slack_node_rw_failed) { 473 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); 474 } 475 476 rc = 1; 477 } 478 } 479 } 480 481 slack_node_rw_failed = rc; 482 } 483 } else { 484 /* Display on. */ 485 if ((strncmp(governor, ONDEMAND_GOVERNOR, strlen(ONDEMAND_GOVERNOR)) == 0) && 486 (strlen(governor) == strlen(ONDEMAND_GOVERNOR))) { 487 undo_hint_action(DISPLAY_STATE_HINT_ID); 488 display_hint_sent = 0; 489 } else if ((strncmp(governor, INTERACTIVE_GOVERNOR, strlen(INTERACTIVE_GOVERNOR)) == 0) && 490 (strlen(governor) == strlen(INTERACTIVE_GOVERNOR))) { 491 undo_hint_action(DISPLAY_STATE_HINT_ID); 492 display_hint_sent = 0; 493 } else if ((strncmp(governor, MSMDCVS_GOVERNOR, strlen(MSMDCVS_GOVERNOR)) == 0) && 494 (strlen(governor) == strlen(MSMDCVS_GOVERNOR))) { 495 if (saved_interactive_mode == -1 || saved_interactive_mode == 0) { 496 /* Display turned on. Restore if possible. */ 497 if (saved_dcvs_cpu0_slack_max != -1) { 498 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_max); 499 500 if (sysfs_write(DCVS_CPU0_SLACK_MAX_NODE, tmp_str) != 0) { 501 if (!slack_node_rw_failed) { 502 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MAX_NODE); 503 } 504 505 rc = 1; 506 } 507 } 508 509 if (saved_dcvs_cpu0_slack_min != -1) { 510 snprintf(tmp_str, NODE_MAX, "%d", saved_dcvs_cpu0_slack_min); 511 512 if (sysfs_write(DCVS_CPU0_SLACK_MIN_NODE, tmp_str) != 0) { 513 if (!slack_node_rw_failed) { 514 ALOGE("Failed to write to %s", DCVS_CPU0_SLACK_MIN_NODE); 515 } 516 517 rc = 1; 518 } 519 } 520 521 if (saved_mpdecision_slack_max != -1) { 522 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_max); 523 524 if (sysfs_write(MPDECISION_SLACK_MAX_NODE, tmp_str) != 0) { 525 if (!slack_node_rw_failed) { 526 ALOGE("Failed to write to %s", MPDECISION_SLACK_MAX_NODE); 527 } 528 529 rc = 1; 530 } 531 } 532 533 if (saved_mpdecision_slack_min != -1) { 534 snprintf(tmp_str, NODE_MAX, "%d", saved_mpdecision_slack_min); 535 536 if (sysfs_write(MPDECISION_SLACK_MIN_NODE, tmp_str) != 0) { 537 if (!slack_node_rw_failed) { 538 ALOGE("Failed to write to %s", MPDECISION_SLACK_MIN_NODE); 539 } 540 541 rc = 1; 542 } 543 } 544 } 545 546 slack_node_rw_failed = rc; 547 } 548 } 549 550 saved_interactive_mode = !!on; 551 } 552 553 static ssize_t get_number_of_platform_modes(struct power_module *module) { 554 return PLATFORM_SLEEP_MODES; 555 } 556 557 static int get_voter_list(struct power_module *module, size_t *voter) { 558 voter[0] = XO_VOTERS; 559 voter[1] = VMIN_VOTERS; 560 561 return 0; 562 } 563 564 static int extract_stats(uint64_t *list, char *file, 565 unsigned int num_parameters, unsigned int index) { 566 FILE *fp; 567 ssize_t read; 568 size_t len; 569 char *line; 570 int ret; 571 572 fp = fopen(file, "r"); 573 if (fp == NULL) { 574 ret = -errno; 575 ALOGE("%s: failed to open: %s", __func__, strerror(errno)); 576 return ret; 577 } 578 579 for (line = NULL, len = 0; 580 ((read = getline(&line, &len, fp) != -1) && (index < num_parameters)); 581 free(line), line = NULL, len = 0) { 582 uint64_t value; 583 char* offset; 584 585 size_t begin = strspn(line, " \t"); 586 if (strncmp(line + begin, parameter_names[index], strlen(parameter_names[index]))) { 587 continue; 588 } 589 590 offset = memchr(line, ':', len); 591 if (!offset) { 592 continue; 593 } 594 595 if (!strcmp(file, RPM_MASTER_STAT)) { 596 /* RPM_MASTER_STAT is reported in hex */ 597 sscanf(offset, ":%" SCNx64, &value); 598 /* Duration is reported in rpm SLEEP TICKS */ 599 if (!strcmp(parameter_names[index], "xo_accumulated_duration")) { 600 value /= RPM_CLK; 601 } 602 } else { 603 /* RPM_STAT is reported in decimal */ 604 sscanf(offset, ":%" SCNu64, &value); 605 } 606 list[index] = value; 607 index++; 608 } 609 free(line); 610 611 fclose(fp); 612 return 0; 613 } 614 615 static int get_platform_low_power_stats(struct power_module *module, 616 power_state_platform_sleep_state_t *list) { 617 uint64_t stats[sizeof(parameter_names)] = {0}; 618 int ret; 619 620 if (!list) { 621 return -EINVAL; 622 } 623 624 ret = extract_stats(stats, RPM_STAT, RPM_PARAMETERS, 0); 625 626 if (ret) { 627 return ret; 628 } 629 630 ret = extract_stats(stats, RPM_MASTER_STAT, NUM_PARAMETERS, 4); 631 632 if (ret) { 633 return ret; 634 } 635 636 /* Update statistics for XO_shutdown */ 637 strcpy(list[0].name, "XO_shutdown"); 638 list[0].total_transitions = stats[0]; 639 list[0].residency_in_msec_since_boot = stats[1]; 640 list[0].supported_only_in_suspend = false; 641 list[0].number_of_voters = XO_VOTERS; 642 643 /* Update statistics for APSS voter */ 644 strcpy(list[0].voters[0].name, "APSS"); 645 list[0].voters[0].total_time_in_msec_voted_for_since_boot = stats[4]; 646 list[0].voters[0].total_number_of_times_voted_since_boot = stats[5]; 647 648 /* Update statistics for MPSS voter */ 649 strcpy(list[0].voters[1].name, "MPSS"); 650 list[0].voters[1].total_time_in_msec_voted_for_since_boot = stats[6]; 651 list[0].voters[1].total_number_of_times_voted_since_boot = stats[7]; 652 653 /* Update statistics for LPASS voter */ 654 strcpy(list[0].voters[2].name, "LPASS"); 655 list[0].voters[2].total_time_in_msec_voted_for_since_boot = stats[8]; 656 list[0].voters[2].total_number_of_times_voted_since_boot = stats[9]; 657 658 /* Update statistics for VMIN state */ 659 strcpy(list[1].name, "VMIN"); 660 list[1].total_transitions = stats[2]; 661 list[1].residency_in_msec_since_boot = stats[3]; 662 list[1].supported_only_in_suspend = false; 663 list[1].number_of_voters = VMIN_VOTERS; 664 665 return 0; 666 } 667 668 static int power_open(const hw_module_t* module, const char* name, 669 hw_device_t** device) 670 { 671 ALOGD("%s: enter; name=%s", __FUNCTION__, name); 672 int retval = 0; /* 0 is ok; -1 is error */ 673 674 if (strcmp(name, POWER_HARDWARE_MODULE_ID) == 0) { 675 power_module_t *dev = (power_module_t *)calloc(1, 676 sizeof(power_module_t)); 677 678 if (dev) { 679 /* Common hw_device_t fields */ 680 dev->common.tag = HARDWARE_MODULE_TAG; 681 dev->common.module_api_version = POWER_MODULE_API_VERSION_0_5; 682 dev->common.hal_api_version = HARDWARE_HAL_API_VERSION; 683 684 dev->init = power_init; 685 dev->powerHint = power_hint; 686 dev->setInteractive = set_interactive; 687 dev->get_number_of_platform_modes = get_number_of_platform_modes; 688 dev->get_platform_low_power_stats = get_platform_low_power_stats; 689 dev->get_voter_list = get_voter_list; 690 691 *device = (hw_device_t*)dev; 692 } else 693 retval = -ENOMEM; 694 } else { 695 retval = -EINVAL; 696 } 697 698 ALOGD("%s: exit %d", __FUNCTION__, retval); 699 return retval; 700 } 701 702 static struct hw_module_methods_t power_module_methods = { 703 .open = power_open, 704 }; 705 706 struct power_module HAL_MODULE_INFO_SYM = { 707 .common = { 708 .tag = HARDWARE_MODULE_TAG, 709 .module_api_version = POWER_MODULE_API_VERSION_0_5, 710 .hal_api_version = HARDWARE_HAL_API_VERSION, 711 .id = POWER_HARDWARE_MODULE_ID, 712 .name = "QCOM Power HAL", 713 .author = "Qualcomm", 714 .methods = &power_module_methods, 715 }, 716 717 .init = power_init, 718 .powerHint = power_hint, 719 .setInteractive = set_interactive, 720 .get_number_of_platform_modes = get_number_of_platform_modes, 721 .get_platform_low_power_stats = get_platform_low_power_stats, 722 .get_voter_list = get_voter_list 723 }; 724