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/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 #define BATTERY_OVERTEMP_THRESHOLD (550)
45 #define BATTERY_UNDERTEMP_THRESHOLD (0)
46 
47 using namespace android;
48 static bool first_update_done;
49 static int lasttime_soc;
50 static unsigned int flounder_monitor_voltage;
51 static long last_update_time;
52 static bool force_decrease;
53 
read_sysfs(const char * path,char * buf,size_t size)54 static int read_sysfs(const char *path, char *buf, size_t size) {
55     char *cp = NULL;
56 
57     int fd = open(path, O_RDONLY, 0);
58     if (fd == -1) {
59         KLOG_ERROR(LOG_TAG, "Could not open '%s'\n", path);
60         return -1;
61     }
62 
63     ssize_t count = TEMP_FAILURE_RETRY(read(fd, buf, size));
64     if (count > 0)
65         cp = (char *)memrchr(buf, '\n', count);
66 
67     if (cp)
68         *cp = '\0';
69     else
70         buf[0] = '\0';
71 
72     close(fd);
73     return count;
74 }
75 
write_sysfs(const char * path,char * s)76 static void write_sysfs(const char *path, char *s)
77 {
78     char buf[80];
79     int len;
80     int fd = open(path, O_WRONLY);
81 
82     if (fd < 0) {
83         strerror_r(errno, buf, sizeof(buf));
84         KLOG_ERROR(LOG_TAG, "Error opening %s: %s\n", path, buf);
85         return;
86     }
87 
88     len = write(fd, s, strlen(s));
89     if (len < 0) {
90         strerror_r(errno, buf, sizeof(buf));
91         KLOG_ERROR(LOG_TAG, "Error writing to %s: %s\n", path, buf);
92     }
93 
94     close(fd);
95 }
96 
get_int64_field(const char * path)97 static int64_t get_int64_field(const char *path) {
98     const int SIZE = 21;
99     char buf[SIZE];
100 
101     int64_t value = 0;
102     if (read_sysfs(path, buf, SIZE) > 0) {
103         value = strtoll(buf, NULL, 0);
104     }
105     return value;
106 }
107 
flounder_status_check(struct BatteryProperties * props)108 static void flounder_status_check(struct BatteryProperties *props)
109 {
110     if (props->batteryStatus == BATTERY_STATUS_UNKNOWN)
111         props->batteryStatus = BATTERY_STATUS_DISCHARGING;
112     else if (props->batteryStatus == BATTERY_STATUS_FULL &&
113         props->batteryLevel < BATTERY_FULL)
114         props->batteryStatus = BATTERY_STATUS_CHARGING;
115 }
116 
flounder_health_check(struct BatteryProperties * props)117 static void flounder_health_check(struct BatteryProperties *props)
118 {
119     if (props->batteryLevel >= BATTERY_FULL)
120         props->batteryHealth = BATTERY_HEALTH_GOOD;
121     else if (props->batteryLevel < BATTERY_LOW)
122         props->batteryHealth = BATTERY_HEALTH_DEAD;
123     else
124         props->batteryHealth = BATTERY_HEALTH_GOOD;
125 
126     if (props->batteryHealth == BATTERY_HEALTH_GOOD){
127         if (props->batteryTemperature > BATTERY_OVERTEMP_THRESHOLD)
128             props->batteryHealth = BATTERY_HEALTH_OVERHEAT;
129         else if(props->batteryTemperature < BATTERY_UNDERTEMP_THRESHOLD)
130             props->batteryHealth = BATTERY_HEALTH_COLD;
131     }
132 }
133 
flounder_voltage_monitor_check(struct BatteryProperties * props)134 static void flounder_voltage_monitor_check(struct BatteryProperties *props)
135 {
136     unsigned int monitor_voltage = 0;
137     int vcell_mv;
138     char voltage[10];
139 
140     if (props->batteryStatus != BATTERY_STATUS_CHARGING &&
141         props->batteryStatus != BATTERY_STATUS_FULL && props->batteryLevel > 0) {
142         vcell_mv = props->batteryVoltage;
143         if (vcell_mv > BATTERY_CRITICAL_LOW_MV)
144             monitor_voltage = BATTERY_CRITICAL_LOW_MV;
145         else if (vcell_mv > BATTERY_DEAD_MV)
146             monitor_voltage = BATTERY_DEAD_MV;
147     }
148 
149     if (monitor_voltage != flounder_monitor_voltage) {
150         snprintf(voltage, sizeof(voltage), "%d", monitor_voltage);
151         write_sysfs(VOLTAGE_MONITOR_PATH, voltage);
152         flounder_monitor_voltage = monitor_voltage;
153     }
154 }
155 
flounder_soc_adjust(struct BatteryProperties * props)156 static void flounder_soc_adjust(struct BatteryProperties *props)
157 {
158     int soc_decrease;
159     int soc, vcell_mv;
160     struct sysinfo info;
161     long uptime = 0;
162     int ret;
163 
164     ret = sysinfo(&info);
165     if (ret) {
166        KLOG_ERROR(LOG_TAG, "Fail to get sysinfo!!\n");
167        uptime = last_update_time;
168     } else
169        uptime = info.uptime;
170 
171     if (!first_update_done) {
172         if (props->batteryLevel >= BATTERY_FULL) {
173             props->batteryLevel = BATTERY_FULL - 1;
174             lasttime_soc = BATTERY_FULL - 1;
175         } else {
176             lasttime_soc = props->batteryLevel;
177         }
178         last_update_time = uptime;
179         first_update_done = true;
180     }
181 
182     if (props->batteryStatus == BATTERY_STATUS_FULL)
183         soc = BATTERY_FULL;
184     else if (props->batteryLevel >= BATTERY_FULL &&
185              lasttime_soc < BATTERY_FULL)
186         soc = BATTERY_FULL - 1;
187     else
188         soc = props->batteryLevel;
189 
190     if (props->batteryLevel > BATTERY_FULL)
191         props->batteryLevel = BATTERY_FULL;
192     else if (props->batteryLevel < 0)
193         props->batteryLevel = 0;
194 
195     vcell_mv = props->batteryVoltage;
196     if (props->batteryStatus == BATTERY_STATUS_DISCHARGING ||
197         props->batteryStatus == BATTERY_STATUS_NOT_CHARGING ||
198         props->batteryStatus == BATTERY_STATUS_UNKNOWN) {
199         if (vcell_mv >= BATTERY_CRITICAL_LOW_MV) {
200             force_decrease = false;
201             soc_decrease = lasttime_soc - soc;
202             if (soc_decrease < 0) {
203                 soc = lasttime_soc;
204                 goto done;
205             }
206 
207             if (uptime > last_update_time &&
208                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
209                 soc = lasttime_soc;
210                 goto done;
211             }
212 
213             if (soc_decrease < 0)
214                 soc_decrease = 0;
215             else if (soc_decrease > NORMAL_MAX_SOC_DEC)
216                 soc_decrease = NORMAL_MAX_SOC_DEC;
217 
218             soc = lasttime_soc - soc_decrease;
219         } else if (vcell_mv < BATTERY_DEAD_MV) {
220             soc = 0;
221         } else {
222             if (force_decrease &&
223                 uptime > last_update_time &&
224                 uptime - last_update_time <= UPDATE_PERIOD_MINIMUM_S) {
225                 soc = lasttime_soc;
226                 goto done;
227             }
228 
229             soc_decrease = CRITICAL_LOW_FORCE_SOC_DROP;
230             if (lasttime_soc <= soc_decrease)
231                soc = 0;
232             else
233                 soc = lasttime_soc - soc_decrease;
234             force_decrease = true;
235         }
236     } else {
237         force_decrease = false;
238         if (soc > lasttime_soc)
239             soc = lasttime_soc + 1;
240     }
241     last_update_time = uptime;
242 done:
243     props->batteryLevel = lasttime_soc = soc;
244 }
245 
flounder_bat_monitor(struct BatteryProperties * props)246 static void flounder_bat_monitor(struct BatteryProperties *props)
247 {
248     flounder_soc_adjust(props);
249     flounder_health_check(props);
250     flounder_status_check(props);
251     flounder_voltage_monitor_check(props);
252 }
253 
healthd_board_battery_update(struct BatteryProperties * props)254 int healthd_board_battery_update(struct BatteryProperties *props)
255 {
256 
257     flounder_bat_monitor(props);
258 
259     // return 0 to log periodic polled battery status to kernel log
260     return 0;
261 }
262 
flounder_energy_counter(int64_t * energy)263 static int flounder_energy_counter(int64_t *energy)
264 {
265     *energy = get_int64_field(CHARGE_COUNTER_EXT_PATH) * VOLTAGE_NOMINAL;
266     return 0;
267 }
268 
healthd_board_init(struct healthd_config * config)269 void healthd_board_init(struct healthd_config *config)
270 {
271     config->energyCounter = flounder_energy_counter;
272 }
273