• 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 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 
power_init(struct power_module * module)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 
process_video_decode_hint(void * metadata)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 
process_video_encode_hint(void * metadata)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 
power_hint_override(struct power_module * module,power_hint_t hint,void * data)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 
calc_timespan_us(struct timespec start,struct timespec end)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 
power_hint(struct power_module * module,power_hint_t hint,void * data)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 
set_interactive_override(struct power_module * module,int on)390 int __attribute__ ((weak)) set_interactive_override(struct power_module *module, int on)
391 {
392     return HINT_NONE;
393 }
394 
set_interactive(struct power_module * module,int on)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 
get_number_of_platform_modes(struct power_module * module)600 static ssize_t get_number_of_platform_modes(struct power_module *module) {
601    return PLATFORM_SLEEP_MODES;
602 }
603 
get_voter_list(struct power_module * module,size_t * voter)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 
extract_stats(uint64_t * list,char * file,unsigned int num_parameters,unsigned int index)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 
get_platform_low_power_stats(struct power_module * module,power_state_platform_sleep_state_t * list)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 
power_open(const hw_module_t * module,const char * name,hw_device_t ** device)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