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 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 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 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 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 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 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 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 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 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 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 269 void healthd_board_init(struct healthd_config *config) 270 { 271 config->energyCounter = flounder_energy_counter; 272 } 273