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