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