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