1 /*
2  * Copyright (C) 2014 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #define LOG_TAG "healthd-flounder"
18 #include <healthd.h>
19 #include <sys/stat.h>
20 #include <fcntl.h>
21 #include <cutils/klog.h>
22 #include <sys/types.h>
23 #include <sys/sysinfo.h>
24 
25 /* Nominal voltage for ENERGY_COUNTER computation */
26 #define VOLTAGE_NOMINAL 3.7
27 
28 #define POWER_SUPPLY_SUBSYSTEM "power_supply"
29 #define POWER_SUPPLY_SYSFS_PATH "/sys/class/" POWER_SUPPLY_SUBSYSTEM
30 
31 #define MAX17050_PATH POWER_SUPPLY_SYSFS_PATH "/battery"
32 #define CHARGE_COUNTER_EXT_PATH MAX17050_PATH "/charge_counter_ext"
33 
34 #define PALMAS_VOLTAGE_MONITOR_PATH POWER_SUPPLY_SYSFS_PATH "/palmas_voltage_monitor"
35 #define VOLTAGE_MONITOR_PATH PALMAS_VOLTAGE_MONITOR_PATH "/device/voltage_monitor"
36 
37 #define BATTERY_FULL 100
38 #define BATTERY_LOW 15
39 #define BATTERY_CRITICAL_LOW_MV (3000)
40 #define BATTERY_DEAD_MV (2800)
41 #define NORMAL_MAX_SOC_DEC (2)
42 #define CRITICAL_LOW_FORCE_SOC_DROP (6)
43 #define UPDATE_PERIOD_MINIMUM_S (55)
44 
45 using namespace android;
46 static bool first_update_done;
47 static int lasttime_soc;
48 static unsigned int flounder_monitor_voltage;
49 static long last_update_time;
50 static bool force_decrease;
51 
read_sysfs(const char * path,char * buf,size_t size)52 static int read_sysfs(const char *path, char *buf, size_t size) {
53     char *cp = NULL;
54 
55     int fd = open(path, O_RDONLY, 0);
56     if (fd == -1) {
57         KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path);
58         return -1;
59     }
60 
61     ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
62     if (count > 0)
63         cp = (char *)memrchr(buf, '\n', count);
64 
65     if (cp)
66         *cp = '\0';
67     else
68         buf[0] = '\0';
69 
70     close(fd);
71     return count;
72 }
73 
write_sysfs(const char * path,char * s)74 static void write_sysfs(const char *path, char *s)
75 {
76     char buf[80];
77     int len;
78     int fd = open(path, O_WRONLY);
79 
80     if (fd < 0) {
81         strerror_r(errno, buf, sizeof(buf));
82         KLOG_ERROR(LOG_TAG, "Error opening %s: %s\n", path, buf);
83         return;
84     }
85 
86     len = write(fd, s, strlen(s));
87     if (len < 0) {
88         strerror_r(errno, buf, sizeof(buf));
89         KLOG_ERROR(LOG_TAG, "Error writing to %s: %s\n", path, buf);
90     }
91 
92     close(fd);
93 }
94 
get_int64_field(const char * path)95 static int64_t get_int64_field(const char *path) {
96     const int SIZE = 21;
97     char buf[SIZE];
98 
99     int64_t value = 0;
100     if (read_sysfs(path, buf, SIZE) > 0) {
101         value = strtoll(buf, NULL, 0);
102     }
103     return value;
104 }
105 
flounder_status_check(struct BatteryProperties * props)106 static void flounder_status_check(struct BatteryProperties *props)
107 {
108     if (props->batteryStatus == BATTERY_STATUS_UNKNOWN)
109         props->batteryStatus = BATTERY_STATUS_DISCHARGING;
110     else if (props->batteryStatus == BATTERY_STATUS_FULL &&
111         props->batteryLevel < BATTERY_FULL)
112         props->batteryStatus = BATTERY_STATUS_CHARGING;
113 }
114 
flounder_health_check(struct BatteryProperties * props)115 static void flounder_health_check(struct BatteryProperties *props)
116 {
117     if (props->batteryLevel >= BATTERY_FULL)
118         props->batteryHealth = BATTERY_HEALTH_GOOD;
119     else if (props->batteryLevel < BATTERY_LOW)
120         props->batteryHealth = BATTERY_HEALTH_DEAD;
121     else
122         props->batteryHealth = BATTERY_HEALTH_GOOD;
123 }
124 
flounder_voltage_monitor_check(struct BatteryProperties * props)125 static void flounder_voltage_monitor_check(struct BatteryProperties *props)
126 {
127     unsigned int monitor_voltage = 0;
128     int vcell_mv;
129     char voltage[10];
130 
131     if (props->batteryStatus != BATTERY_STATUS_CHARGING &&
132         props->batteryStatus != BATTERY_STATUS_FULL && props->batteryLevel > 0) {
133         vcell_mv = props->batteryVoltage;
134         if (vcell_mv > BATTERY_CRITICAL_LOW_MV)
135             monitor_voltage = BATTERY_CRITICAL_LOW_MV;
136         else if (vcell_mv > BATTERY_DEAD_MV)
137             monitor_voltage = BATTERY_DEAD_MV;
138     }
139 
140     if (monitor_voltage != flounder_monitor_voltage) {
141         snprintf(voltage, sizeof(voltage), "%d", monitor_voltage);
142         write_sysfs(VOLTAGE_MONITOR_PATH, voltage);
143         flounder_monitor_voltage = monitor_voltage;
144     }
145 }
146 
flounder_soc_adjust(struct BatteryProperties * props)147 static void flounder_soc_adjust(struct BatteryProperties *props)
148 {
149     int soc_decrease;
150     int soc, vcell_mv;
151     struct sysinfo info;
152     long uptime = 0;
153     int ret;
154 
155     ret = sysinfo(&info);
156     if (ret) {
157        KLOG_ERROR(LOG_TAG, "Fail to get sysinfo!!\n");
158        uptime = last_update_time;
159     } else
160        uptime = info.uptime;
161 
162     if (!first_update_done) {
163         if (props->batteryLevel >= BATTERY_FULL) {
164             props->batteryLevel = BATTERY_FULL - 1;
165             lasttime_soc = BATTERY_FULL - 1;
166         } else {
167             lasttime_soc = props->batteryLevel;
168         }
169         last_update_time = uptime;
170         first_update_done = true;
171     }
172 
173     if (props->batteryStatus == BATTERY_STATUS_FULL)
174         soc = BATTERY_FULL;
175     else if (props->batteryLevel >= BATTERY_FULL &&
176              lasttime_soc < BATTERY_FULL)
177         soc = BATTERY_FULL - 1;
178     else
179         soc = props->batteryLevel;
180 
181     if (props->batteryLevel > BATTERY_FULL)
182         props->batteryLevel = BATTERY_FULL;
183     else if (props->batteryLevel < 0)
184         props->batteryLevel = 0;
185 
186     vcell_mv = props->batteryVoltage;
187     if (props->batteryStatus == BATTERY_STATUS_DISCHARGING ||
188         props->batteryStatus == BATTERY_STATUS_NOT_CHARGING ||
189         props->batteryStatus == BATTERY_STATUS_UNKNOWN) {
190         if (vcell_mv >= BATTERY_CRITICAL_LOW_MV) {
191             force_decrease = false;
192             soc_decrease = lasttime_soc - soc;
193             if (soc_decrease < 0) {
194                 soc = lasttime_soc;
195                 goto done;
196             }
197 
198             if (uptime > last_update_time &&
199                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
200                 soc = lasttime_soc;
201                 goto done;
202             }
203 
204             if (soc_decrease < 0)
205                 soc_decrease = 0;
206             else if (soc_decrease > NORMAL_MAX_SOC_DEC)
207                 soc_decrease = NORMAL_MAX_SOC_DEC;
208 
209             soc = lasttime_soc - soc_decrease;
210         } else if (vcell_mv < BATTERY_DEAD_MV) {
211             soc = 0;
212         } else {
213             if (force_decrease &&
214                 uptime > last_update_time &&
215                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
216                 soc = lasttime_soc;
217                 goto done;
218             }
219 
220             soc_decrease = CRITICAL_LOW_FORCE_SOC_DROP;
221             if (lasttime_soc <= soc_decrease)
222                soc = 0;
223             else
224                 soc = lasttime_soc - soc_decrease;
225             force_decrease = true;
226         }
227     } else {
228         force_decrease = false;
229         if (soc > lasttime_soc)
230             soc = lasttime_soc + 1;
231     }
232     last_update_time = uptime;
233 done:
234     props->batteryLevel = lasttime_soc = soc;
235 }
236 
flounder_bat_monitor(struct BatteryProperties * props)237 static void flounder_bat_monitor(struct BatteryProperties *props)
238 {
239     flounder_soc_adjust(props);
240     flounder_health_check(props);
241     flounder_status_check(props);
242     flounder_voltage_monitor_check(props);
243 }
244 
healthd_board_battery_update(struct BatteryProperties * props)245 int healthd_board_battery_update(struct BatteryProperties *props)
246 {
247 
248     flounder_bat_monitor(props);
249 
250     // return 0 to log periodic polled battery status to kernel log
251     return 0;
252 }
253 
flounder_energy_counter(int64_t * energy)254 static int flounder_energy_counter(int64_t *energy)
255 {
256     *energy = get_int64_field(CHARGE_COUNTER_EXT_PATH) * VOLTAGE_NOMINAL;
257     return 0;
258 }
259 
healthd_board_init(struct healthd_config * config)260 void healthd_board_init(struct healthd_config *config)
261 {
262     config->energyCounter = flounder_energy_counter;
263 }
264