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
power_init(struct power_module * module)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
process_video_decode_hint(void * metadata)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
process_video_encode_hint(void * metadata)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
power_hint_override(struct power_module * module,power_hint_t hint,void * data)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
calc_timespan_us(struct timespec start,struct timespec end)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
power_hint(struct power_module * module,power_hint_t hint,void * data)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
set_interactive_override(struct power_module * module,int on)343 int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
344 {
345 return HINT_NONE;
346 }
347
set_interactive(struct power_module * module,int on)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
get_number_of_platform_modes(struct power_module * module)553 static ssize_t get_number_of_platform_modes(struct power_module *module) {
554 return PLATFORM_SLEEP_MODES;
555 }
556
get_voter_list(struct power_module * module,size_t * voter)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
extract_stats(uint64_t * list,char * file,unsigned int num_parameters,unsigned int index)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
get_platform_low_power_stats(struct power_module * module,power_state_platform_sleep_state_t * list)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
power_open(const hw_module_t * module,const char * name,hw_device_t ** device)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