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