• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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