1*8c0984e5SSebastian Reichel /* 2*8c0984e5SSebastian Reichel * Copyright (C) 2011 Samsung Electronics Co., Ltd. 3*8c0984e5SSebastian Reichel * MyungJoo Ham <myungjoo.ham@samsung.com> 4*8c0984e5SSebastian Reichel * 5*8c0984e5SSebastian Reichel * This driver enables to monitor battery health and control charger 6*8c0984e5SSebastian Reichel * during suspend-to-mem. 7*8c0984e5SSebastian Reichel * Charger manager depends on other devices. register this later than 8*8c0984e5SSebastian Reichel * the depending devices. 9*8c0984e5SSebastian Reichel * 10*8c0984e5SSebastian Reichel * This program is free software; you can redistribute it and/or modify 11*8c0984e5SSebastian Reichel * it under the terms of the GNU General Public License version 2 as 12*8c0984e5SSebastian Reichel * published by the Free Software Foundation. 13*8c0984e5SSebastian Reichel **/ 14*8c0984e5SSebastian Reichel 15*8c0984e5SSebastian Reichel #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt 16*8c0984e5SSebastian Reichel 17*8c0984e5SSebastian Reichel #include <linux/io.h> 18*8c0984e5SSebastian Reichel #include <linux/module.h> 19*8c0984e5SSebastian Reichel #include <linux/irq.h> 20*8c0984e5SSebastian Reichel #include <linux/interrupt.h> 21*8c0984e5SSebastian Reichel #include <linux/rtc.h> 22*8c0984e5SSebastian Reichel #include <linux/slab.h> 23*8c0984e5SSebastian Reichel #include <linux/workqueue.h> 24*8c0984e5SSebastian Reichel #include <linux/platform_device.h> 25*8c0984e5SSebastian Reichel #include <linux/power/charger-manager.h> 26*8c0984e5SSebastian Reichel #include <linux/regulator/consumer.h> 27*8c0984e5SSebastian Reichel #include <linux/sysfs.h> 28*8c0984e5SSebastian Reichel #include <linux/of.h> 29*8c0984e5SSebastian Reichel #include <linux/thermal.h> 30*8c0984e5SSebastian Reichel 31*8c0984e5SSebastian Reichel /* 32*8c0984e5SSebastian Reichel * Default termperature threshold for charging. 33*8c0984e5SSebastian Reichel * Every temperature units are in tenth of centigrade. 34*8c0984e5SSebastian Reichel */ 35*8c0984e5SSebastian Reichel #define CM_DEFAULT_RECHARGE_TEMP_DIFF 50 36*8c0984e5SSebastian Reichel #define CM_DEFAULT_CHARGE_TEMP_MAX 500 37*8c0984e5SSebastian Reichel 38*8c0984e5SSebastian Reichel static const char * const default_event_names[] = { 39*8c0984e5SSebastian Reichel [CM_EVENT_UNKNOWN] = "Unknown", 40*8c0984e5SSebastian Reichel [CM_EVENT_BATT_FULL] = "Battery Full", 41*8c0984e5SSebastian Reichel [CM_EVENT_BATT_IN] = "Battery Inserted", 42*8c0984e5SSebastian Reichel [CM_EVENT_BATT_OUT] = "Battery Pulled Out", 43*8c0984e5SSebastian Reichel [CM_EVENT_BATT_OVERHEAT] = "Battery Overheat", 44*8c0984e5SSebastian Reichel [CM_EVENT_BATT_COLD] = "Battery Cold", 45*8c0984e5SSebastian Reichel [CM_EVENT_EXT_PWR_IN_OUT] = "External Power Attach/Detach", 46*8c0984e5SSebastian Reichel [CM_EVENT_CHG_START_STOP] = "Charging Start/Stop", 47*8c0984e5SSebastian Reichel [CM_EVENT_OTHERS] = "Other battery events" 48*8c0984e5SSebastian Reichel }; 49*8c0984e5SSebastian Reichel 50*8c0984e5SSebastian Reichel /* 51*8c0984e5SSebastian Reichel * Regard CM_JIFFIES_SMALL jiffies is small enough to ignore for 52*8c0984e5SSebastian Reichel * delayed works so that we can run delayed works with CM_JIFFIES_SMALL 53*8c0984e5SSebastian Reichel * without any delays. 54*8c0984e5SSebastian Reichel */ 55*8c0984e5SSebastian Reichel #define CM_JIFFIES_SMALL (2) 56*8c0984e5SSebastian Reichel 57*8c0984e5SSebastian Reichel /* If y is valid (> 0) and smaller than x, do x = y */ 58*8c0984e5SSebastian Reichel #define CM_MIN_VALID(x, y) x = (((y > 0) && ((x) > (y))) ? (y) : (x)) 59*8c0984e5SSebastian Reichel 60*8c0984e5SSebastian Reichel /* 61*8c0984e5SSebastian Reichel * Regard CM_RTC_SMALL (sec) is small enough to ignore error in invoking 62*8c0984e5SSebastian Reichel * rtc alarm. It should be 2 or larger 63*8c0984e5SSebastian Reichel */ 64*8c0984e5SSebastian Reichel #define CM_RTC_SMALL (2) 65*8c0984e5SSebastian Reichel 66*8c0984e5SSebastian Reichel #define UEVENT_BUF_SIZE 32 67*8c0984e5SSebastian Reichel 68*8c0984e5SSebastian Reichel static LIST_HEAD(cm_list); 69*8c0984e5SSebastian Reichel static DEFINE_MUTEX(cm_list_mtx); 70*8c0984e5SSebastian Reichel 71*8c0984e5SSebastian Reichel /* About in-suspend (suspend-again) monitoring */ 72*8c0984e5SSebastian Reichel static struct alarm *cm_timer; 73*8c0984e5SSebastian Reichel 74*8c0984e5SSebastian Reichel static bool cm_suspended; 75*8c0984e5SSebastian Reichel static bool cm_timer_set; 76*8c0984e5SSebastian Reichel static unsigned long cm_suspend_duration_ms; 77*8c0984e5SSebastian Reichel 78*8c0984e5SSebastian Reichel /* About normal (not suspended) monitoring */ 79*8c0984e5SSebastian Reichel static unsigned long polling_jiffy = ULONG_MAX; /* ULONG_MAX: no polling */ 80*8c0984e5SSebastian Reichel static unsigned long next_polling; /* Next appointed polling time */ 81*8c0984e5SSebastian Reichel static struct workqueue_struct *cm_wq; /* init at driver add */ 82*8c0984e5SSebastian Reichel static struct delayed_work cm_monitor_work; /* init at driver add */ 83*8c0984e5SSebastian Reichel 84*8c0984e5SSebastian Reichel /** 85*8c0984e5SSebastian Reichel * is_batt_present - See if the battery presents in place. 86*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 87*8c0984e5SSebastian Reichel */ 88*8c0984e5SSebastian Reichel static bool is_batt_present(struct charger_manager *cm) 89*8c0984e5SSebastian Reichel { 90*8c0984e5SSebastian Reichel union power_supply_propval val; 91*8c0984e5SSebastian Reichel struct power_supply *psy; 92*8c0984e5SSebastian Reichel bool present = false; 93*8c0984e5SSebastian Reichel int i, ret; 94*8c0984e5SSebastian Reichel 95*8c0984e5SSebastian Reichel switch (cm->desc->battery_present) { 96*8c0984e5SSebastian Reichel case CM_BATTERY_PRESENT: 97*8c0984e5SSebastian Reichel present = true; 98*8c0984e5SSebastian Reichel break; 99*8c0984e5SSebastian Reichel case CM_NO_BATTERY: 100*8c0984e5SSebastian Reichel break; 101*8c0984e5SSebastian Reichel case CM_FUEL_GAUGE: 102*8c0984e5SSebastian Reichel psy = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 103*8c0984e5SSebastian Reichel if (!psy) 104*8c0984e5SSebastian Reichel break; 105*8c0984e5SSebastian Reichel 106*8c0984e5SSebastian Reichel ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, 107*8c0984e5SSebastian Reichel &val); 108*8c0984e5SSebastian Reichel if (ret == 0 && val.intval) 109*8c0984e5SSebastian Reichel present = true; 110*8c0984e5SSebastian Reichel power_supply_put(psy); 111*8c0984e5SSebastian Reichel break; 112*8c0984e5SSebastian Reichel case CM_CHARGER_STAT: 113*8c0984e5SSebastian Reichel for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 114*8c0984e5SSebastian Reichel psy = power_supply_get_by_name( 115*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 116*8c0984e5SSebastian Reichel if (!psy) { 117*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 118*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 119*8c0984e5SSebastian Reichel continue; 120*8c0984e5SSebastian Reichel } 121*8c0984e5SSebastian Reichel 122*8c0984e5SSebastian Reichel ret = power_supply_get_property(psy, 123*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, &val); 124*8c0984e5SSebastian Reichel power_supply_put(psy); 125*8c0984e5SSebastian Reichel if (ret == 0 && val.intval) { 126*8c0984e5SSebastian Reichel present = true; 127*8c0984e5SSebastian Reichel break; 128*8c0984e5SSebastian Reichel } 129*8c0984e5SSebastian Reichel } 130*8c0984e5SSebastian Reichel break; 131*8c0984e5SSebastian Reichel } 132*8c0984e5SSebastian Reichel 133*8c0984e5SSebastian Reichel return present; 134*8c0984e5SSebastian Reichel } 135*8c0984e5SSebastian Reichel 136*8c0984e5SSebastian Reichel /** 137*8c0984e5SSebastian Reichel * is_ext_pwr_online - See if an external power source is attached to charge 138*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 139*8c0984e5SSebastian Reichel * 140*8c0984e5SSebastian Reichel * Returns true if at least one of the chargers of the battery has an external 141*8c0984e5SSebastian Reichel * power source attached to charge the battery regardless of whether it is 142*8c0984e5SSebastian Reichel * actually charging or not. 143*8c0984e5SSebastian Reichel */ 144*8c0984e5SSebastian Reichel static bool is_ext_pwr_online(struct charger_manager *cm) 145*8c0984e5SSebastian Reichel { 146*8c0984e5SSebastian Reichel union power_supply_propval val; 147*8c0984e5SSebastian Reichel struct power_supply *psy; 148*8c0984e5SSebastian Reichel bool online = false; 149*8c0984e5SSebastian Reichel int i, ret; 150*8c0984e5SSebastian Reichel 151*8c0984e5SSebastian Reichel /* If at least one of them has one, it's yes. */ 152*8c0984e5SSebastian Reichel for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 153*8c0984e5SSebastian Reichel psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 154*8c0984e5SSebastian Reichel if (!psy) { 155*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 156*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 157*8c0984e5SSebastian Reichel continue; 158*8c0984e5SSebastian Reichel } 159*8c0984e5SSebastian Reichel 160*8c0984e5SSebastian Reichel ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 161*8c0984e5SSebastian Reichel &val); 162*8c0984e5SSebastian Reichel power_supply_put(psy); 163*8c0984e5SSebastian Reichel if (ret == 0 && val.intval) { 164*8c0984e5SSebastian Reichel online = true; 165*8c0984e5SSebastian Reichel break; 166*8c0984e5SSebastian Reichel } 167*8c0984e5SSebastian Reichel } 168*8c0984e5SSebastian Reichel 169*8c0984e5SSebastian Reichel return online; 170*8c0984e5SSebastian Reichel } 171*8c0984e5SSebastian Reichel 172*8c0984e5SSebastian Reichel /** 173*8c0984e5SSebastian Reichel * get_batt_uV - Get the voltage level of the battery 174*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 175*8c0984e5SSebastian Reichel * @uV: the voltage level returned. 176*8c0984e5SSebastian Reichel * 177*8c0984e5SSebastian Reichel * Returns 0 if there is no error. 178*8c0984e5SSebastian Reichel * Returns a negative value on error. 179*8c0984e5SSebastian Reichel */ 180*8c0984e5SSebastian Reichel static int get_batt_uV(struct charger_manager *cm, int *uV) 181*8c0984e5SSebastian Reichel { 182*8c0984e5SSebastian Reichel union power_supply_propval val; 183*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge; 184*8c0984e5SSebastian Reichel int ret; 185*8c0984e5SSebastian Reichel 186*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 187*8c0984e5SSebastian Reichel if (!fuel_gauge) 188*8c0984e5SSebastian Reichel return -ENODEV; 189*8c0984e5SSebastian Reichel 190*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 191*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); 192*8c0984e5SSebastian Reichel power_supply_put(fuel_gauge); 193*8c0984e5SSebastian Reichel if (ret) 194*8c0984e5SSebastian Reichel return ret; 195*8c0984e5SSebastian Reichel 196*8c0984e5SSebastian Reichel *uV = val.intval; 197*8c0984e5SSebastian Reichel return 0; 198*8c0984e5SSebastian Reichel } 199*8c0984e5SSebastian Reichel 200*8c0984e5SSebastian Reichel /** 201*8c0984e5SSebastian Reichel * is_charging - Returns true if the battery is being charged. 202*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 203*8c0984e5SSebastian Reichel */ 204*8c0984e5SSebastian Reichel static bool is_charging(struct charger_manager *cm) 205*8c0984e5SSebastian Reichel { 206*8c0984e5SSebastian Reichel int i, ret; 207*8c0984e5SSebastian Reichel bool charging = false; 208*8c0984e5SSebastian Reichel struct power_supply *psy; 209*8c0984e5SSebastian Reichel union power_supply_propval val; 210*8c0984e5SSebastian Reichel 211*8c0984e5SSebastian Reichel /* If there is no battery, it cannot be charged */ 212*8c0984e5SSebastian Reichel if (!is_batt_present(cm)) 213*8c0984e5SSebastian Reichel return false; 214*8c0984e5SSebastian Reichel 215*8c0984e5SSebastian Reichel /* If at least one of the charger is charging, return yes */ 216*8c0984e5SSebastian Reichel for (i = 0; cm->desc->psy_charger_stat[i]; i++) { 217*8c0984e5SSebastian Reichel /* 1. The charger sholuld not be DISABLED */ 218*8c0984e5SSebastian Reichel if (cm->emergency_stop) 219*8c0984e5SSebastian Reichel continue; 220*8c0984e5SSebastian Reichel if (!cm->charger_enabled) 221*8c0984e5SSebastian Reichel continue; 222*8c0984e5SSebastian Reichel 223*8c0984e5SSebastian Reichel psy = power_supply_get_by_name(cm->desc->psy_charger_stat[i]); 224*8c0984e5SSebastian Reichel if (!psy) { 225*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot find power supply \"%s\"\n", 226*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 227*8c0984e5SSebastian Reichel continue; 228*8c0984e5SSebastian Reichel } 229*8c0984e5SSebastian Reichel 230*8c0984e5SSebastian Reichel /* 2. The charger should be online (ext-power) */ 231*8c0984e5SSebastian Reichel ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_ONLINE, 232*8c0984e5SSebastian Reichel &val); 233*8c0984e5SSebastian Reichel if (ret) { 234*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Cannot read ONLINE value from %s\n", 235*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 236*8c0984e5SSebastian Reichel power_supply_put(psy); 237*8c0984e5SSebastian Reichel continue; 238*8c0984e5SSebastian Reichel } 239*8c0984e5SSebastian Reichel if (val.intval == 0) { 240*8c0984e5SSebastian Reichel power_supply_put(psy); 241*8c0984e5SSebastian Reichel continue; 242*8c0984e5SSebastian Reichel } 243*8c0984e5SSebastian Reichel 244*8c0984e5SSebastian Reichel /* 245*8c0984e5SSebastian Reichel * 3. The charger should not be FULL, DISCHARGING, 246*8c0984e5SSebastian Reichel * or NOT_CHARGING. 247*8c0984e5SSebastian Reichel */ 248*8c0984e5SSebastian Reichel ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_STATUS, 249*8c0984e5SSebastian Reichel &val); 250*8c0984e5SSebastian Reichel power_supply_put(psy); 251*8c0984e5SSebastian Reichel if (ret) { 252*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Cannot read STATUS value from %s\n", 253*8c0984e5SSebastian Reichel cm->desc->psy_charger_stat[i]); 254*8c0984e5SSebastian Reichel continue; 255*8c0984e5SSebastian Reichel } 256*8c0984e5SSebastian Reichel if (val.intval == POWER_SUPPLY_STATUS_FULL || 257*8c0984e5SSebastian Reichel val.intval == POWER_SUPPLY_STATUS_DISCHARGING || 258*8c0984e5SSebastian Reichel val.intval == POWER_SUPPLY_STATUS_NOT_CHARGING) 259*8c0984e5SSebastian Reichel continue; 260*8c0984e5SSebastian Reichel 261*8c0984e5SSebastian Reichel /* Then, this is charging. */ 262*8c0984e5SSebastian Reichel charging = true; 263*8c0984e5SSebastian Reichel break; 264*8c0984e5SSebastian Reichel } 265*8c0984e5SSebastian Reichel 266*8c0984e5SSebastian Reichel return charging; 267*8c0984e5SSebastian Reichel } 268*8c0984e5SSebastian Reichel 269*8c0984e5SSebastian Reichel /** 270*8c0984e5SSebastian Reichel * is_full_charged - Returns true if the battery is fully charged. 271*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 272*8c0984e5SSebastian Reichel */ 273*8c0984e5SSebastian Reichel static bool is_full_charged(struct charger_manager *cm) 274*8c0984e5SSebastian Reichel { 275*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 276*8c0984e5SSebastian Reichel union power_supply_propval val; 277*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge; 278*8c0984e5SSebastian Reichel bool is_full = false; 279*8c0984e5SSebastian Reichel int ret = 0; 280*8c0984e5SSebastian Reichel int uV; 281*8c0984e5SSebastian Reichel 282*8c0984e5SSebastian Reichel /* If there is no battery, it cannot be charged */ 283*8c0984e5SSebastian Reichel if (!is_batt_present(cm)) 284*8c0984e5SSebastian Reichel return false; 285*8c0984e5SSebastian Reichel 286*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 287*8c0984e5SSebastian Reichel if (!fuel_gauge) 288*8c0984e5SSebastian Reichel return false; 289*8c0984e5SSebastian Reichel 290*8c0984e5SSebastian Reichel if (desc->fullbatt_full_capacity > 0) { 291*8c0984e5SSebastian Reichel val.intval = 0; 292*8c0984e5SSebastian Reichel 293*8c0984e5SSebastian Reichel /* Not full if capacity of fuel gauge isn't full */ 294*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 295*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, &val); 296*8c0984e5SSebastian Reichel if (!ret && val.intval > desc->fullbatt_full_capacity) { 297*8c0984e5SSebastian Reichel is_full = true; 298*8c0984e5SSebastian Reichel goto out; 299*8c0984e5SSebastian Reichel } 300*8c0984e5SSebastian Reichel } 301*8c0984e5SSebastian Reichel 302*8c0984e5SSebastian Reichel /* Full, if it's over the fullbatt voltage */ 303*8c0984e5SSebastian Reichel if (desc->fullbatt_uV > 0) { 304*8c0984e5SSebastian Reichel ret = get_batt_uV(cm, &uV); 305*8c0984e5SSebastian Reichel if (!ret && uV >= desc->fullbatt_uV) { 306*8c0984e5SSebastian Reichel is_full = true; 307*8c0984e5SSebastian Reichel goto out; 308*8c0984e5SSebastian Reichel } 309*8c0984e5SSebastian Reichel } 310*8c0984e5SSebastian Reichel 311*8c0984e5SSebastian Reichel /* Full, if the capacity is more than fullbatt_soc */ 312*8c0984e5SSebastian Reichel if (desc->fullbatt_soc > 0) { 313*8c0984e5SSebastian Reichel val.intval = 0; 314*8c0984e5SSebastian Reichel 315*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 316*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, &val); 317*8c0984e5SSebastian Reichel if (!ret && val.intval >= desc->fullbatt_soc) { 318*8c0984e5SSebastian Reichel is_full = true; 319*8c0984e5SSebastian Reichel goto out; 320*8c0984e5SSebastian Reichel } 321*8c0984e5SSebastian Reichel } 322*8c0984e5SSebastian Reichel 323*8c0984e5SSebastian Reichel out: 324*8c0984e5SSebastian Reichel power_supply_put(fuel_gauge); 325*8c0984e5SSebastian Reichel return is_full; 326*8c0984e5SSebastian Reichel } 327*8c0984e5SSebastian Reichel 328*8c0984e5SSebastian Reichel /** 329*8c0984e5SSebastian Reichel * is_polling_required - Return true if need to continue polling for this CM. 330*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 331*8c0984e5SSebastian Reichel */ 332*8c0984e5SSebastian Reichel static bool is_polling_required(struct charger_manager *cm) 333*8c0984e5SSebastian Reichel { 334*8c0984e5SSebastian Reichel switch (cm->desc->polling_mode) { 335*8c0984e5SSebastian Reichel case CM_POLL_DISABLE: 336*8c0984e5SSebastian Reichel return false; 337*8c0984e5SSebastian Reichel case CM_POLL_ALWAYS: 338*8c0984e5SSebastian Reichel return true; 339*8c0984e5SSebastian Reichel case CM_POLL_EXTERNAL_POWER_ONLY: 340*8c0984e5SSebastian Reichel return is_ext_pwr_online(cm); 341*8c0984e5SSebastian Reichel case CM_POLL_CHARGING_ONLY: 342*8c0984e5SSebastian Reichel return is_charging(cm); 343*8c0984e5SSebastian Reichel default: 344*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Incorrect polling_mode (%d)\n", 345*8c0984e5SSebastian Reichel cm->desc->polling_mode); 346*8c0984e5SSebastian Reichel } 347*8c0984e5SSebastian Reichel 348*8c0984e5SSebastian Reichel return false; 349*8c0984e5SSebastian Reichel } 350*8c0984e5SSebastian Reichel 351*8c0984e5SSebastian Reichel /** 352*8c0984e5SSebastian Reichel * try_charger_enable - Enable/Disable chargers altogether 353*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 354*8c0984e5SSebastian Reichel * @enable: true: enable / false: disable 355*8c0984e5SSebastian Reichel * 356*8c0984e5SSebastian Reichel * Note that Charger Manager keeps the charger enabled regardless whether 357*8c0984e5SSebastian Reichel * the charger is charging or not (because battery is full or no external 358*8c0984e5SSebastian Reichel * power source exists) except when CM needs to disable chargers forcibly 359*8c0984e5SSebastian Reichel * bacause of emergency causes; when the battery is overheated or too cold. 360*8c0984e5SSebastian Reichel */ 361*8c0984e5SSebastian Reichel static int try_charger_enable(struct charger_manager *cm, bool enable) 362*8c0984e5SSebastian Reichel { 363*8c0984e5SSebastian Reichel int err = 0, i; 364*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 365*8c0984e5SSebastian Reichel 366*8c0984e5SSebastian Reichel /* Ignore if it's redundent command */ 367*8c0984e5SSebastian Reichel if (enable == cm->charger_enabled) 368*8c0984e5SSebastian Reichel return 0; 369*8c0984e5SSebastian Reichel 370*8c0984e5SSebastian Reichel if (enable) { 371*8c0984e5SSebastian Reichel if (cm->emergency_stop) 372*8c0984e5SSebastian Reichel return -EAGAIN; 373*8c0984e5SSebastian Reichel 374*8c0984e5SSebastian Reichel /* 375*8c0984e5SSebastian Reichel * Save start time of charging to limit 376*8c0984e5SSebastian Reichel * maximum possible charging time. 377*8c0984e5SSebastian Reichel */ 378*8c0984e5SSebastian Reichel cm->charging_start_time = ktime_to_ms(ktime_get()); 379*8c0984e5SSebastian Reichel cm->charging_end_time = 0; 380*8c0984e5SSebastian Reichel 381*8c0984e5SSebastian Reichel for (i = 0 ; i < desc->num_charger_regulators ; i++) { 382*8c0984e5SSebastian Reichel if (desc->charger_regulators[i].externally_control) 383*8c0984e5SSebastian Reichel continue; 384*8c0984e5SSebastian Reichel 385*8c0984e5SSebastian Reichel err = regulator_enable(desc->charger_regulators[i].consumer); 386*8c0984e5SSebastian Reichel if (err < 0) { 387*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Cannot enable %s regulator\n", 388*8c0984e5SSebastian Reichel desc->charger_regulators[i].regulator_name); 389*8c0984e5SSebastian Reichel } 390*8c0984e5SSebastian Reichel } 391*8c0984e5SSebastian Reichel } else { 392*8c0984e5SSebastian Reichel /* 393*8c0984e5SSebastian Reichel * Save end time of charging to maintain fully charged state 394*8c0984e5SSebastian Reichel * of battery after full-batt. 395*8c0984e5SSebastian Reichel */ 396*8c0984e5SSebastian Reichel cm->charging_start_time = 0; 397*8c0984e5SSebastian Reichel cm->charging_end_time = ktime_to_ms(ktime_get()); 398*8c0984e5SSebastian Reichel 399*8c0984e5SSebastian Reichel for (i = 0 ; i < desc->num_charger_regulators ; i++) { 400*8c0984e5SSebastian Reichel if (desc->charger_regulators[i].externally_control) 401*8c0984e5SSebastian Reichel continue; 402*8c0984e5SSebastian Reichel 403*8c0984e5SSebastian Reichel err = regulator_disable(desc->charger_regulators[i].consumer); 404*8c0984e5SSebastian Reichel if (err < 0) { 405*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Cannot disable %s regulator\n", 406*8c0984e5SSebastian Reichel desc->charger_regulators[i].regulator_name); 407*8c0984e5SSebastian Reichel } 408*8c0984e5SSebastian Reichel } 409*8c0984e5SSebastian Reichel 410*8c0984e5SSebastian Reichel /* 411*8c0984e5SSebastian Reichel * Abnormal battery state - Stop charging forcibly, 412*8c0984e5SSebastian Reichel * even if charger was enabled at the other places 413*8c0984e5SSebastian Reichel */ 414*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 415*8c0984e5SSebastian Reichel if (regulator_is_enabled( 416*8c0984e5SSebastian Reichel desc->charger_regulators[i].consumer)) { 417*8c0984e5SSebastian Reichel regulator_force_disable( 418*8c0984e5SSebastian Reichel desc->charger_regulators[i].consumer); 419*8c0984e5SSebastian Reichel dev_warn(cm->dev, "Disable regulator(%s) forcibly\n", 420*8c0984e5SSebastian Reichel desc->charger_regulators[i].regulator_name); 421*8c0984e5SSebastian Reichel } 422*8c0984e5SSebastian Reichel } 423*8c0984e5SSebastian Reichel } 424*8c0984e5SSebastian Reichel 425*8c0984e5SSebastian Reichel if (!err) 426*8c0984e5SSebastian Reichel cm->charger_enabled = enable; 427*8c0984e5SSebastian Reichel 428*8c0984e5SSebastian Reichel return err; 429*8c0984e5SSebastian Reichel } 430*8c0984e5SSebastian Reichel 431*8c0984e5SSebastian Reichel /** 432*8c0984e5SSebastian Reichel * try_charger_restart - Restart charging. 433*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 434*8c0984e5SSebastian Reichel * 435*8c0984e5SSebastian Reichel * Restart charging by turning off and on the charger. 436*8c0984e5SSebastian Reichel */ 437*8c0984e5SSebastian Reichel static int try_charger_restart(struct charger_manager *cm) 438*8c0984e5SSebastian Reichel { 439*8c0984e5SSebastian Reichel int err; 440*8c0984e5SSebastian Reichel 441*8c0984e5SSebastian Reichel if (cm->emergency_stop) 442*8c0984e5SSebastian Reichel return -EAGAIN; 443*8c0984e5SSebastian Reichel 444*8c0984e5SSebastian Reichel err = try_charger_enable(cm, false); 445*8c0984e5SSebastian Reichel if (err) 446*8c0984e5SSebastian Reichel return err; 447*8c0984e5SSebastian Reichel 448*8c0984e5SSebastian Reichel return try_charger_enable(cm, true); 449*8c0984e5SSebastian Reichel } 450*8c0984e5SSebastian Reichel 451*8c0984e5SSebastian Reichel /** 452*8c0984e5SSebastian Reichel * uevent_notify - Let users know something has changed. 453*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 454*8c0984e5SSebastian Reichel * @event: the event string. 455*8c0984e5SSebastian Reichel * 456*8c0984e5SSebastian Reichel * If @event is null, it implies that uevent_notify is called 457*8c0984e5SSebastian Reichel * by resume function. When called in the resume function, cm_suspended 458*8c0984e5SSebastian Reichel * should be already reset to false in order to let uevent_notify 459*8c0984e5SSebastian Reichel * notify the recent event during the suspend to users. While 460*8c0984e5SSebastian Reichel * suspended, uevent_notify does not notify users, but tracks 461*8c0984e5SSebastian Reichel * events so that uevent_notify can notify users later after resumed. 462*8c0984e5SSebastian Reichel */ 463*8c0984e5SSebastian Reichel static void uevent_notify(struct charger_manager *cm, const char *event) 464*8c0984e5SSebastian Reichel { 465*8c0984e5SSebastian Reichel static char env_str[UEVENT_BUF_SIZE + 1] = ""; 466*8c0984e5SSebastian Reichel static char env_str_save[UEVENT_BUF_SIZE + 1] = ""; 467*8c0984e5SSebastian Reichel 468*8c0984e5SSebastian Reichel if (cm_suspended) { 469*8c0984e5SSebastian Reichel /* Nothing in suspended-event buffer */ 470*8c0984e5SSebastian Reichel if (env_str_save[0] == 0) { 471*8c0984e5SSebastian Reichel if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) 472*8c0984e5SSebastian Reichel return; /* status not changed */ 473*8c0984e5SSebastian Reichel strncpy(env_str_save, event, UEVENT_BUF_SIZE); 474*8c0984e5SSebastian Reichel return; 475*8c0984e5SSebastian Reichel } 476*8c0984e5SSebastian Reichel 477*8c0984e5SSebastian Reichel if (!strncmp(env_str_save, event, UEVENT_BUF_SIZE)) 478*8c0984e5SSebastian Reichel return; /* Duplicated. */ 479*8c0984e5SSebastian Reichel strncpy(env_str_save, event, UEVENT_BUF_SIZE); 480*8c0984e5SSebastian Reichel return; 481*8c0984e5SSebastian Reichel } 482*8c0984e5SSebastian Reichel 483*8c0984e5SSebastian Reichel if (event == NULL) { 484*8c0984e5SSebastian Reichel /* No messages pending */ 485*8c0984e5SSebastian Reichel if (!env_str_save[0]) 486*8c0984e5SSebastian Reichel return; 487*8c0984e5SSebastian Reichel 488*8c0984e5SSebastian Reichel strncpy(env_str, env_str_save, UEVENT_BUF_SIZE); 489*8c0984e5SSebastian Reichel kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); 490*8c0984e5SSebastian Reichel env_str_save[0] = 0; 491*8c0984e5SSebastian Reichel 492*8c0984e5SSebastian Reichel return; 493*8c0984e5SSebastian Reichel } 494*8c0984e5SSebastian Reichel 495*8c0984e5SSebastian Reichel /* status not changed */ 496*8c0984e5SSebastian Reichel if (!strncmp(env_str, event, UEVENT_BUF_SIZE)) 497*8c0984e5SSebastian Reichel return; 498*8c0984e5SSebastian Reichel 499*8c0984e5SSebastian Reichel /* save the status and notify the update */ 500*8c0984e5SSebastian Reichel strncpy(env_str, event, UEVENT_BUF_SIZE); 501*8c0984e5SSebastian Reichel kobject_uevent(&cm->dev->kobj, KOBJ_CHANGE); 502*8c0984e5SSebastian Reichel 503*8c0984e5SSebastian Reichel dev_info(cm->dev, "%s\n", event); 504*8c0984e5SSebastian Reichel } 505*8c0984e5SSebastian Reichel 506*8c0984e5SSebastian Reichel /** 507*8c0984e5SSebastian Reichel * fullbatt_vchk - Check voltage drop some times after "FULL" event. 508*8c0984e5SSebastian Reichel * @work: the work_struct appointing the function 509*8c0984e5SSebastian Reichel * 510*8c0984e5SSebastian Reichel * If a user has designated "fullbatt_vchkdrop_ms/uV" values with 511*8c0984e5SSebastian Reichel * charger_desc, Charger Manager checks voltage drop after the battery 512*8c0984e5SSebastian Reichel * "FULL" event. It checks whether the voltage has dropped more than 513*8c0984e5SSebastian Reichel * fullbatt_vchkdrop_uV by calling this function after fullbatt_vchkrop_ms. 514*8c0984e5SSebastian Reichel */ 515*8c0984e5SSebastian Reichel static void fullbatt_vchk(struct work_struct *work) 516*8c0984e5SSebastian Reichel { 517*8c0984e5SSebastian Reichel struct delayed_work *dwork = to_delayed_work(work); 518*8c0984e5SSebastian Reichel struct charger_manager *cm = container_of(dwork, 519*8c0984e5SSebastian Reichel struct charger_manager, fullbatt_vchk_work); 520*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 521*8c0984e5SSebastian Reichel int batt_uV, err, diff; 522*8c0984e5SSebastian Reichel 523*8c0984e5SSebastian Reichel /* remove the appointment for fullbatt_vchk */ 524*8c0984e5SSebastian Reichel cm->fullbatt_vchk_jiffies_at = 0; 525*8c0984e5SSebastian Reichel 526*8c0984e5SSebastian Reichel if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) 527*8c0984e5SSebastian Reichel return; 528*8c0984e5SSebastian Reichel 529*8c0984e5SSebastian Reichel err = get_batt_uV(cm, &batt_uV); 530*8c0984e5SSebastian Reichel if (err) { 531*8c0984e5SSebastian Reichel dev_err(cm->dev, "%s: get_batt_uV error(%d)\n", __func__, err); 532*8c0984e5SSebastian Reichel return; 533*8c0984e5SSebastian Reichel } 534*8c0984e5SSebastian Reichel 535*8c0984e5SSebastian Reichel diff = desc->fullbatt_uV - batt_uV; 536*8c0984e5SSebastian Reichel if (diff < 0) 537*8c0984e5SSebastian Reichel return; 538*8c0984e5SSebastian Reichel 539*8c0984e5SSebastian Reichel dev_info(cm->dev, "VBATT dropped %duV after full-batt\n", diff); 540*8c0984e5SSebastian Reichel 541*8c0984e5SSebastian Reichel if (diff > desc->fullbatt_vchkdrop_uV) { 542*8c0984e5SSebastian Reichel try_charger_restart(cm); 543*8c0984e5SSebastian Reichel uevent_notify(cm, "Recharging"); 544*8c0984e5SSebastian Reichel } 545*8c0984e5SSebastian Reichel } 546*8c0984e5SSebastian Reichel 547*8c0984e5SSebastian Reichel /** 548*8c0984e5SSebastian Reichel * check_charging_duration - Monitor charging/discharging duration 549*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 550*8c0984e5SSebastian Reichel * 551*8c0984e5SSebastian Reichel * If whole charging duration exceed 'charging_max_duration_ms', 552*8c0984e5SSebastian Reichel * cm stop charging to prevent overcharge/overheat. If discharging 553*8c0984e5SSebastian Reichel * duration exceed 'discharging _max_duration_ms', charger cable is 554*8c0984e5SSebastian Reichel * attached, after full-batt, cm start charging to maintain fully 555*8c0984e5SSebastian Reichel * charged state for battery. 556*8c0984e5SSebastian Reichel */ 557*8c0984e5SSebastian Reichel static int check_charging_duration(struct charger_manager *cm) 558*8c0984e5SSebastian Reichel { 559*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 560*8c0984e5SSebastian Reichel u64 curr = ktime_to_ms(ktime_get()); 561*8c0984e5SSebastian Reichel u64 duration; 562*8c0984e5SSebastian Reichel int ret = false; 563*8c0984e5SSebastian Reichel 564*8c0984e5SSebastian Reichel if (!desc->charging_max_duration_ms && 565*8c0984e5SSebastian Reichel !desc->discharging_max_duration_ms) 566*8c0984e5SSebastian Reichel return ret; 567*8c0984e5SSebastian Reichel 568*8c0984e5SSebastian Reichel if (cm->charger_enabled) { 569*8c0984e5SSebastian Reichel duration = curr - cm->charging_start_time; 570*8c0984e5SSebastian Reichel 571*8c0984e5SSebastian Reichel if (duration > desc->charging_max_duration_ms) { 572*8c0984e5SSebastian Reichel dev_info(cm->dev, "Charging duration exceed %ums\n", 573*8c0984e5SSebastian Reichel desc->charging_max_duration_ms); 574*8c0984e5SSebastian Reichel uevent_notify(cm, "Discharging"); 575*8c0984e5SSebastian Reichel try_charger_enable(cm, false); 576*8c0984e5SSebastian Reichel ret = true; 577*8c0984e5SSebastian Reichel } 578*8c0984e5SSebastian Reichel } else if (is_ext_pwr_online(cm) && !cm->charger_enabled) { 579*8c0984e5SSebastian Reichel duration = curr - cm->charging_end_time; 580*8c0984e5SSebastian Reichel 581*8c0984e5SSebastian Reichel if (duration > desc->charging_max_duration_ms && 582*8c0984e5SSebastian Reichel is_ext_pwr_online(cm)) { 583*8c0984e5SSebastian Reichel dev_info(cm->dev, "Discharging duration exceed %ums\n", 584*8c0984e5SSebastian Reichel desc->discharging_max_duration_ms); 585*8c0984e5SSebastian Reichel uevent_notify(cm, "Recharging"); 586*8c0984e5SSebastian Reichel try_charger_enable(cm, true); 587*8c0984e5SSebastian Reichel ret = true; 588*8c0984e5SSebastian Reichel } 589*8c0984e5SSebastian Reichel } 590*8c0984e5SSebastian Reichel 591*8c0984e5SSebastian Reichel return ret; 592*8c0984e5SSebastian Reichel } 593*8c0984e5SSebastian Reichel 594*8c0984e5SSebastian Reichel static int cm_get_battery_temperature_by_psy(struct charger_manager *cm, 595*8c0984e5SSebastian Reichel int *temp) 596*8c0984e5SSebastian Reichel { 597*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge; 598*8c0984e5SSebastian Reichel int ret; 599*8c0984e5SSebastian Reichel 600*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 601*8c0984e5SSebastian Reichel if (!fuel_gauge) 602*8c0984e5SSebastian Reichel return -ENODEV; 603*8c0984e5SSebastian Reichel 604*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 605*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, 606*8c0984e5SSebastian Reichel (union power_supply_propval *)temp); 607*8c0984e5SSebastian Reichel power_supply_put(fuel_gauge); 608*8c0984e5SSebastian Reichel 609*8c0984e5SSebastian Reichel return ret; 610*8c0984e5SSebastian Reichel } 611*8c0984e5SSebastian Reichel 612*8c0984e5SSebastian Reichel static int cm_get_battery_temperature(struct charger_manager *cm, 613*8c0984e5SSebastian Reichel int *temp) 614*8c0984e5SSebastian Reichel { 615*8c0984e5SSebastian Reichel int ret; 616*8c0984e5SSebastian Reichel 617*8c0984e5SSebastian Reichel if (!cm->desc->measure_battery_temp) 618*8c0984e5SSebastian Reichel return -ENODEV; 619*8c0984e5SSebastian Reichel 620*8c0984e5SSebastian Reichel #ifdef CONFIG_THERMAL 621*8c0984e5SSebastian Reichel if (cm->tzd_batt) { 622*8c0984e5SSebastian Reichel ret = thermal_zone_get_temp(cm->tzd_batt, temp); 623*8c0984e5SSebastian Reichel if (!ret) 624*8c0984e5SSebastian Reichel /* Calibrate temperature unit */ 625*8c0984e5SSebastian Reichel *temp /= 100; 626*8c0984e5SSebastian Reichel } else 627*8c0984e5SSebastian Reichel #endif 628*8c0984e5SSebastian Reichel { 629*8c0984e5SSebastian Reichel /* if-else continued from CONFIG_THERMAL */ 630*8c0984e5SSebastian Reichel ret = cm_get_battery_temperature_by_psy(cm, temp); 631*8c0984e5SSebastian Reichel } 632*8c0984e5SSebastian Reichel 633*8c0984e5SSebastian Reichel return ret; 634*8c0984e5SSebastian Reichel } 635*8c0984e5SSebastian Reichel 636*8c0984e5SSebastian Reichel static int cm_check_thermal_status(struct charger_manager *cm) 637*8c0984e5SSebastian Reichel { 638*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 639*8c0984e5SSebastian Reichel int temp, upper_limit, lower_limit; 640*8c0984e5SSebastian Reichel int ret = 0; 641*8c0984e5SSebastian Reichel 642*8c0984e5SSebastian Reichel ret = cm_get_battery_temperature(cm, &temp); 643*8c0984e5SSebastian Reichel if (ret) { 644*8c0984e5SSebastian Reichel /* FIXME: 645*8c0984e5SSebastian Reichel * No information of battery temperature might 646*8c0984e5SSebastian Reichel * occur hazadous result. We have to handle it 647*8c0984e5SSebastian Reichel * depending on battery type. 648*8c0984e5SSebastian Reichel */ 649*8c0984e5SSebastian Reichel dev_err(cm->dev, "Failed to get battery temperature\n"); 650*8c0984e5SSebastian Reichel return 0; 651*8c0984e5SSebastian Reichel } 652*8c0984e5SSebastian Reichel 653*8c0984e5SSebastian Reichel upper_limit = desc->temp_max; 654*8c0984e5SSebastian Reichel lower_limit = desc->temp_min; 655*8c0984e5SSebastian Reichel 656*8c0984e5SSebastian Reichel if (cm->emergency_stop) { 657*8c0984e5SSebastian Reichel upper_limit -= desc->temp_diff; 658*8c0984e5SSebastian Reichel lower_limit += desc->temp_diff; 659*8c0984e5SSebastian Reichel } 660*8c0984e5SSebastian Reichel 661*8c0984e5SSebastian Reichel if (temp > upper_limit) 662*8c0984e5SSebastian Reichel ret = CM_EVENT_BATT_OVERHEAT; 663*8c0984e5SSebastian Reichel else if (temp < lower_limit) 664*8c0984e5SSebastian Reichel ret = CM_EVENT_BATT_COLD; 665*8c0984e5SSebastian Reichel 666*8c0984e5SSebastian Reichel return ret; 667*8c0984e5SSebastian Reichel } 668*8c0984e5SSebastian Reichel 669*8c0984e5SSebastian Reichel /** 670*8c0984e5SSebastian Reichel * _cm_monitor - Monitor the temperature and return true for exceptions. 671*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 672*8c0984e5SSebastian Reichel * 673*8c0984e5SSebastian Reichel * Returns true if there is an event to notify for the battery. 674*8c0984e5SSebastian Reichel * (True if the status of "emergency_stop" changes) 675*8c0984e5SSebastian Reichel */ 676*8c0984e5SSebastian Reichel static bool _cm_monitor(struct charger_manager *cm) 677*8c0984e5SSebastian Reichel { 678*8c0984e5SSebastian Reichel int temp_alrt; 679*8c0984e5SSebastian Reichel 680*8c0984e5SSebastian Reichel temp_alrt = cm_check_thermal_status(cm); 681*8c0984e5SSebastian Reichel 682*8c0984e5SSebastian Reichel /* It has been stopped already */ 683*8c0984e5SSebastian Reichel if (temp_alrt && cm->emergency_stop) 684*8c0984e5SSebastian Reichel return false; 685*8c0984e5SSebastian Reichel 686*8c0984e5SSebastian Reichel /* 687*8c0984e5SSebastian Reichel * Check temperature whether overheat or cold. 688*8c0984e5SSebastian Reichel * If temperature is out of range normal state, stop charging. 689*8c0984e5SSebastian Reichel */ 690*8c0984e5SSebastian Reichel if (temp_alrt) { 691*8c0984e5SSebastian Reichel cm->emergency_stop = temp_alrt; 692*8c0984e5SSebastian Reichel if (!try_charger_enable(cm, false)) 693*8c0984e5SSebastian Reichel uevent_notify(cm, default_event_names[temp_alrt]); 694*8c0984e5SSebastian Reichel 695*8c0984e5SSebastian Reichel /* 696*8c0984e5SSebastian Reichel * Check whole charging duration and discharing duration 697*8c0984e5SSebastian Reichel * after full-batt. 698*8c0984e5SSebastian Reichel */ 699*8c0984e5SSebastian Reichel } else if (!cm->emergency_stop && check_charging_duration(cm)) { 700*8c0984e5SSebastian Reichel dev_dbg(cm->dev, 701*8c0984e5SSebastian Reichel "Charging/Discharging duration is out of range\n"); 702*8c0984e5SSebastian Reichel /* 703*8c0984e5SSebastian Reichel * Check dropped voltage of battery. If battery voltage is more 704*8c0984e5SSebastian Reichel * dropped than fullbatt_vchkdrop_uV after fully charged state, 705*8c0984e5SSebastian Reichel * charger-manager have to recharge battery. 706*8c0984e5SSebastian Reichel */ 707*8c0984e5SSebastian Reichel } else if (!cm->emergency_stop && is_ext_pwr_online(cm) && 708*8c0984e5SSebastian Reichel !cm->charger_enabled) { 709*8c0984e5SSebastian Reichel fullbatt_vchk(&cm->fullbatt_vchk_work.work); 710*8c0984e5SSebastian Reichel 711*8c0984e5SSebastian Reichel /* 712*8c0984e5SSebastian Reichel * Check whether fully charged state to protect overcharge 713*8c0984e5SSebastian Reichel * if charger-manager is charging for battery. 714*8c0984e5SSebastian Reichel */ 715*8c0984e5SSebastian Reichel } else if (!cm->emergency_stop && is_full_charged(cm) && 716*8c0984e5SSebastian Reichel cm->charger_enabled) { 717*8c0984e5SSebastian Reichel dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); 718*8c0984e5SSebastian Reichel uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); 719*8c0984e5SSebastian Reichel 720*8c0984e5SSebastian Reichel try_charger_enable(cm, false); 721*8c0984e5SSebastian Reichel 722*8c0984e5SSebastian Reichel fullbatt_vchk(&cm->fullbatt_vchk_work.work); 723*8c0984e5SSebastian Reichel } else { 724*8c0984e5SSebastian Reichel cm->emergency_stop = 0; 725*8c0984e5SSebastian Reichel if (is_ext_pwr_online(cm)) { 726*8c0984e5SSebastian Reichel if (!try_charger_enable(cm, true)) 727*8c0984e5SSebastian Reichel uevent_notify(cm, "CHARGING"); 728*8c0984e5SSebastian Reichel } 729*8c0984e5SSebastian Reichel } 730*8c0984e5SSebastian Reichel 731*8c0984e5SSebastian Reichel return true; 732*8c0984e5SSebastian Reichel } 733*8c0984e5SSebastian Reichel 734*8c0984e5SSebastian Reichel /** 735*8c0984e5SSebastian Reichel * cm_monitor - Monitor every battery. 736*8c0984e5SSebastian Reichel * 737*8c0984e5SSebastian Reichel * Returns true if there is an event to notify from any of the batteries. 738*8c0984e5SSebastian Reichel * (True if the status of "emergency_stop" changes) 739*8c0984e5SSebastian Reichel */ 740*8c0984e5SSebastian Reichel static bool cm_monitor(void) 741*8c0984e5SSebastian Reichel { 742*8c0984e5SSebastian Reichel bool stop = false; 743*8c0984e5SSebastian Reichel struct charger_manager *cm; 744*8c0984e5SSebastian Reichel 745*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 746*8c0984e5SSebastian Reichel 747*8c0984e5SSebastian Reichel list_for_each_entry(cm, &cm_list, entry) { 748*8c0984e5SSebastian Reichel if (_cm_monitor(cm)) 749*8c0984e5SSebastian Reichel stop = true; 750*8c0984e5SSebastian Reichel } 751*8c0984e5SSebastian Reichel 752*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 753*8c0984e5SSebastian Reichel 754*8c0984e5SSebastian Reichel return stop; 755*8c0984e5SSebastian Reichel } 756*8c0984e5SSebastian Reichel 757*8c0984e5SSebastian Reichel /** 758*8c0984e5SSebastian Reichel * _setup_polling - Setup the next instance of polling. 759*8c0984e5SSebastian Reichel * @work: work_struct of the function _setup_polling. 760*8c0984e5SSebastian Reichel */ 761*8c0984e5SSebastian Reichel static void _setup_polling(struct work_struct *work) 762*8c0984e5SSebastian Reichel { 763*8c0984e5SSebastian Reichel unsigned long min = ULONG_MAX; 764*8c0984e5SSebastian Reichel struct charger_manager *cm; 765*8c0984e5SSebastian Reichel bool keep_polling = false; 766*8c0984e5SSebastian Reichel unsigned long _next_polling; 767*8c0984e5SSebastian Reichel 768*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 769*8c0984e5SSebastian Reichel 770*8c0984e5SSebastian Reichel list_for_each_entry(cm, &cm_list, entry) { 771*8c0984e5SSebastian Reichel if (is_polling_required(cm) && cm->desc->polling_interval_ms) { 772*8c0984e5SSebastian Reichel keep_polling = true; 773*8c0984e5SSebastian Reichel 774*8c0984e5SSebastian Reichel if (min > cm->desc->polling_interval_ms) 775*8c0984e5SSebastian Reichel min = cm->desc->polling_interval_ms; 776*8c0984e5SSebastian Reichel } 777*8c0984e5SSebastian Reichel } 778*8c0984e5SSebastian Reichel 779*8c0984e5SSebastian Reichel polling_jiffy = msecs_to_jiffies(min); 780*8c0984e5SSebastian Reichel if (polling_jiffy <= CM_JIFFIES_SMALL) 781*8c0984e5SSebastian Reichel polling_jiffy = CM_JIFFIES_SMALL + 1; 782*8c0984e5SSebastian Reichel 783*8c0984e5SSebastian Reichel if (!keep_polling) 784*8c0984e5SSebastian Reichel polling_jiffy = ULONG_MAX; 785*8c0984e5SSebastian Reichel if (polling_jiffy == ULONG_MAX) 786*8c0984e5SSebastian Reichel goto out; 787*8c0984e5SSebastian Reichel 788*8c0984e5SSebastian Reichel WARN(cm_wq == NULL, "charger-manager: workqueue not initialized" 789*8c0984e5SSebastian Reichel ". try it later. %s\n", __func__); 790*8c0984e5SSebastian Reichel 791*8c0984e5SSebastian Reichel /* 792*8c0984e5SSebastian Reichel * Use mod_delayed_work() iff the next polling interval should 793*8c0984e5SSebastian Reichel * occur before the currently scheduled one. If @cm_monitor_work 794*8c0984e5SSebastian Reichel * isn't active, the end result is the same, so no need to worry 795*8c0984e5SSebastian Reichel * about stale @next_polling. 796*8c0984e5SSebastian Reichel */ 797*8c0984e5SSebastian Reichel _next_polling = jiffies + polling_jiffy; 798*8c0984e5SSebastian Reichel 799*8c0984e5SSebastian Reichel if (time_before(_next_polling, next_polling)) { 800*8c0984e5SSebastian Reichel mod_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy); 801*8c0984e5SSebastian Reichel next_polling = _next_polling; 802*8c0984e5SSebastian Reichel } else { 803*8c0984e5SSebastian Reichel if (queue_delayed_work(cm_wq, &cm_monitor_work, polling_jiffy)) 804*8c0984e5SSebastian Reichel next_polling = _next_polling; 805*8c0984e5SSebastian Reichel } 806*8c0984e5SSebastian Reichel out: 807*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 808*8c0984e5SSebastian Reichel } 809*8c0984e5SSebastian Reichel static DECLARE_WORK(setup_polling, _setup_polling); 810*8c0984e5SSebastian Reichel 811*8c0984e5SSebastian Reichel /** 812*8c0984e5SSebastian Reichel * cm_monitor_poller - The Monitor / Poller. 813*8c0984e5SSebastian Reichel * @work: work_struct of the function cm_monitor_poller 814*8c0984e5SSebastian Reichel * 815*8c0984e5SSebastian Reichel * During non-suspended state, cm_monitor_poller is used to poll and monitor 816*8c0984e5SSebastian Reichel * the batteries. 817*8c0984e5SSebastian Reichel */ 818*8c0984e5SSebastian Reichel static void cm_monitor_poller(struct work_struct *work) 819*8c0984e5SSebastian Reichel { 820*8c0984e5SSebastian Reichel cm_monitor(); 821*8c0984e5SSebastian Reichel schedule_work(&setup_polling); 822*8c0984e5SSebastian Reichel } 823*8c0984e5SSebastian Reichel 824*8c0984e5SSebastian Reichel /** 825*8c0984e5SSebastian Reichel * fullbatt_handler - Event handler for CM_EVENT_BATT_FULL 826*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 827*8c0984e5SSebastian Reichel */ 828*8c0984e5SSebastian Reichel static void fullbatt_handler(struct charger_manager *cm) 829*8c0984e5SSebastian Reichel { 830*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 831*8c0984e5SSebastian Reichel 832*8c0984e5SSebastian Reichel if (!desc->fullbatt_vchkdrop_uV || !desc->fullbatt_vchkdrop_ms) 833*8c0984e5SSebastian Reichel goto out; 834*8c0984e5SSebastian Reichel 835*8c0984e5SSebastian Reichel if (cm_suspended) 836*8c0984e5SSebastian Reichel device_set_wakeup_capable(cm->dev, true); 837*8c0984e5SSebastian Reichel 838*8c0984e5SSebastian Reichel mod_delayed_work(cm_wq, &cm->fullbatt_vchk_work, 839*8c0984e5SSebastian Reichel msecs_to_jiffies(desc->fullbatt_vchkdrop_ms)); 840*8c0984e5SSebastian Reichel cm->fullbatt_vchk_jiffies_at = jiffies + msecs_to_jiffies( 841*8c0984e5SSebastian Reichel desc->fullbatt_vchkdrop_ms); 842*8c0984e5SSebastian Reichel 843*8c0984e5SSebastian Reichel if (cm->fullbatt_vchk_jiffies_at == 0) 844*8c0984e5SSebastian Reichel cm->fullbatt_vchk_jiffies_at = 1; 845*8c0984e5SSebastian Reichel 846*8c0984e5SSebastian Reichel out: 847*8c0984e5SSebastian Reichel dev_info(cm->dev, "EVENT_HANDLE: Battery Fully Charged\n"); 848*8c0984e5SSebastian Reichel uevent_notify(cm, default_event_names[CM_EVENT_BATT_FULL]); 849*8c0984e5SSebastian Reichel } 850*8c0984e5SSebastian Reichel 851*8c0984e5SSebastian Reichel /** 852*8c0984e5SSebastian Reichel * battout_handler - Event handler for CM_EVENT_BATT_OUT 853*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 854*8c0984e5SSebastian Reichel */ 855*8c0984e5SSebastian Reichel static void battout_handler(struct charger_manager *cm) 856*8c0984e5SSebastian Reichel { 857*8c0984e5SSebastian Reichel if (cm_suspended) 858*8c0984e5SSebastian Reichel device_set_wakeup_capable(cm->dev, true); 859*8c0984e5SSebastian Reichel 860*8c0984e5SSebastian Reichel if (!is_batt_present(cm)) { 861*8c0984e5SSebastian Reichel dev_emerg(cm->dev, "Battery Pulled Out!\n"); 862*8c0984e5SSebastian Reichel uevent_notify(cm, default_event_names[CM_EVENT_BATT_OUT]); 863*8c0984e5SSebastian Reichel } else { 864*8c0984e5SSebastian Reichel uevent_notify(cm, "Battery Reinserted?"); 865*8c0984e5SSebastian Reichel } 866*8c0984e5SSebastian Reichel } 867*8c0984e5SSebastian Reichel 868*8c0984e5SSebastian Reichel /** 869*8c0984e5SSebastian Reichel * misc_event_handler - Handler for other evnets 870*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 871*8c0984e5SSebastian Reichel * @type: the Charger Manager representing the battery. 872*8c0984e5SSebastian Reichel */ 873*8c0984e5SSebastian Reichel static void misc_event_handler(struct charger_manager *cm, 874*8c0984e5SSebastian Reichel enum cm_event_types type) 875*8c0984e5SSebastian Reichel { 876*8c0984e5SSebastian Reichel if (cm_suspended) 877*8c0984e5SSebastian Reichel device_set_wakeup_capable(cm->dev, true); 878*8c0984e5SSebastian Reichel 879*8c0984e5SSebastian Reichel if (is_polling_required(cm) && cm->desc->polling_interval_ms) 880*8c0984e5SSebastian Reichel schedule_work(&setup_polling); 881*8c0984e5SSebastian Reichel uevent_notify(cm, default_event_names[type]); 882*8c0984e5SSebastian Reichel } 883*8c0984e5SSebastian Reichel 884*8c0984e5SSebastian Reichel static int charger_get_property(struct power_supply *psy, 885*8c0984e5SSebastian Reichel enum power_supply_property psp, 886*8c0984e5SSebastian Reichel union power_supply_propval *val) 887*8c0984e5SSebastian Reichel { 888*8c0984e5SSebastian Reichel struct charger_manager *cm = power_supply_get_drvdata(psy); 889*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 890*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge = NULL; 891*8c0984e5SSebastian Reichel int ret = 0; 892*8c0984e5SSebastian Reichel int uV; 893*8c0984e5SSebastian Reichel 894*8c0984e5SSebastian Reichel switch (psp) { 895*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_STATUS: 896*8c0984e5SSebastian Reichel if (is_charging(cm)) 897*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_CHARGING; 898*8c0984e5SSebastian Reichel else if (is_ext_pwr_online(cm)) 899*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; 900*8c0984e5SSebastian Reichel else 901*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_STATUS_DISCHARGING; 902*8c0984e5SSebastian Reichel break; 903*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_HEALTH: 904*8c0984e5SSebastian Reichel if (cm->emergency_stop > 0) 905*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_OVERHEAT; 906*8c0984e5SSebastian Reichel else if (cm->emergency_stop < 0) 907*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_COLD; 908*8c0984e5SSebastian Reichel else 909*8c0984e5SSebastian Reichel val->intval = POWER_SUPPLY_HEALTH_GOOD; 910*8c0984e5SSebastian Reichel break; 911*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_PRESENT: 912*8c0984e5SSebastian Reichel if (is_batt_present(cm)) 913*8c0984e5SSebastian Reichel val->intval = 1; 914*8c0984e5SSebastian Reichel else 915*8c0984e5SSebastian Reichel val->intval = 0; 916*8c0984e5SSebastian Reichel break; 917*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_VOLTAGE_NOW: 918*8c0984e5SSebastian Reichel ret = get_batt_uV(cm, &val->intval); 919*8c0984e5SSebastian Reichel break; 920*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CURRENT_NOW: 921*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 922*8c0984e5SSebastian Reichel if (!fuel_gauge) { 923*8c0984e5SSebastian Reichel ret = -ENODEV; 924*8c0984e5SSebastian Reichel break; 925*8c0984e5SSebastian Reichel } 926*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 927*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, val); 928*8c0984e5SSebastian Reichel break; 929*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP: 930*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_TEMP_AMBIENT: 931*8c0984e5SSebastian Reichel return cm_get_battery_temperature(cm, &val->intval); 932*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CAPACITY: 933*8c0984e5SSebastian Reichel if (!is_batt_present(cm)) { 934*8c0984e5SSebastian Reichel /* There is no battery. Assume 100% */ 935*8c0984e5SSebastian Reichel val->intval = 100; 936*8c0984e5SSebastian Reichel break; 937*8c0984e5SSebastian Reichel } 938*8c0984e5SSebastian Reichel 939*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(cm->desc->psy_fuel_gauge); 940*8c0984e5SSebastian Reichel if (!fuel_gauge) { 941*8c0984e5SSebastian Reichel ret = -ENODEV; 942*8c0984e5SSebastian Reichel break; 943*8c0984e5SSebastian Reichel } 944*8c0984e5SSebastian Reichel 945*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 946*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, val); 947*8c0984e5SSebastian Reichel if (ret) 948*8c0984e5SSebastian Reichel break; 949*8c0984e5SSebastian Reichel 950*8c0984e5SSebastian Reichel if (val->intval > 100) { 951*8c0984e5SSebastian Reichel val->intval = 100; 952*8c0984e5SSebastian Reichel break; 953*8c0984e5SSebastian Reichel } 954*8c0984e5SSebastian Reichel if (val->intval < 0) 955*8c0984e5SSebastian Reichel val->intval = 0; 956*8c0984e5SSebastian Reichel 957*8c0984e5SSebastian Reichel /* Do not adjust SOC when charging: voltage is overrated */ 958*8c0984e5SSebastian Reichel if (is_charging(cm)) 959*8c0984e5SSebastian Reichel break; 960*8c0984e5SSebastian Reichel 961*8c0984e5SSebastian Reichel /* 962*8c0984e5SSebastian Reichel * If the capacity value is inconsistent, calibrate it base on 963*8c0984e5SSebastian Reichel * the battery voltage values and the thresholds given as desc 964*8c0984e5SSebastian Reichel */ 965*8c0984e5SSebastian Reichel ret = get_batt_uV(cm, &uV); 966*8c0984e5SSebastian Reichel if (ret) { 967*8c0984e5SSebastian Reichel /* Voltage information not available. No calibration */ 968*8c0984e5SSebastian Reichel ret = 0; 969*8c0984e5SSebastian Reichel break; 970*8c0984e5SSebastian Reichel } 971*8c0984e5SSebastian Reichel 972*8c0984e5SSebastian Reichel if (desc->fullbatt_uV > 0 && uV >= desc->fullbatt_uV && 973*8c0984e5SSebastian Reichel !is_charging(cm)) { 974*8c0984e5SSebastian Reichel val->intval = 100; 975*8c0984e5SSebastian Reichel break; 976*8c0984e5SSebastian Reichel } 977*8c0984e5SSebastian Reichel 978*8c0984e5SSebastian Reichel break; 979*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_ONLINE: 980*8c0984e5SSebastian Reichel if (is_ext_pwr_online(cm)) 981*8c0984e5SSebastian Reichel val->intval = 1; 982*8c0984e5SSebastian Reichel else 983*8c0984e5SSebastian Reichel val->intval = 0; 984*8c0984e5SSebastian Reichel break; 985*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_FULL: 986*8c0984e5SSebastian Reichel if (is_full_charged(cm)) 987*8c0984e5SSebastian Reichel val->intval = 1; 988*8c0984e5SSebastian Reichel else 989*8c0984e5SSebastian Reichel val->intval = 0; 990*8c0984e5SSebastian Reichel ret = 0; 991*8c0984e5SSebastian Reichel break; 992*8c0984e5SSebastian Reichel case POWER_SUPPLY_PROP_CHARGE_NOW: 993*8c0984e5SSebastian Reichel if (is_charging(cm)) { 994*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name( 995*8c0984e5SSebastian Reichel cm->desc->psy_fuel_gauge); 996*8c0984e5SSebastian Reichel if (!fuel_gauge) { 997*8c0984e5SSebastian Reichel ret = -ENODEV; 998*8c0984e5SSebastian Reichel break; 999*8c0984e5SSebastian Reichel } 1000*8c0984e5SSebastian Reichel 1001*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 1002*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, 1003*8c0984e5SSebastian Reichel val); 1004*8c0984e5SSebastian Reichel if (ret) { 1005*8c0984e5SSebastian Reichel val->intval = 1; 1006*8c0984e5SSebastian Reichel ret = 0; 1007*8c0984e5SSebastian Reichel } else { 1008*8c0984e5SSebastian Reichel /* If CHARGE_NOW is supplied, use it */ 1009*8c0984e5SSebastian Reichel val->intval = (val->intval > 0) ? 1010*8c0984e5SSebastian Reichel val->intval : 1; 1011*8c0984e5SSebastian Reichel } 1012*8c0984e5SSebastian Reichel } else { 1013*8c0984e5SSebastian Reichel val->intval = 0; 1014*8c0984e5SSebastian Reichel } 1015*8c0984e5SSebastian Reichel break; 1016*8c0984e5SSebastian Reichel default: 1017*8c0984e5SSebastian Reichel return -EINVAL; 1018*8c0984e5SSebastian Reichel } 1019*8c0984e5SSebastian Reichel if (fuel_gauge) 1020*8c0984e5SSebastian Reichel power_supply_put(fuel_gauge); 1021*8c0984e5SSebastian Reichel return ret; 1022*8c0984e5SSebastian Reichel } 1023*8c0984e5SSebastian Reichel 1024*8c0984e5SSebastian Reichel #define NUM_CHARGER_PSY_OPTIONAL (4) 1025*8c0984e5SSebastian Reichel static enum power_supply_property default_charger_props[] = { 1026*8c0984e5SSebastian Reichel /* Guaranteed to provide */ 1027*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_STATUS, 1028*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_HEALTH, 1029*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_PRESENT, 1030*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_VOLTAGE_NOW, 1031*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CAPACITY, 1032*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_ONLINE, 1033*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_FULL, 1034*8c0984e5SSebastian Reichel /* 1035*8c0984e5SSebastian Reichel * Optional properties are: 1036*8c0984e5SSebastian Reichel * POWER_SUPPLY_PROP_CHARGE_NOW, 1037*8c0984e5SSebastian Reichel * POWER_SUPPLY_PROP_CURRENT_NOW, 1038*8c0984e5SSebastian Reichel * POWER_SUPPLY_PROP_TEMP, and 1039*8c0984e5SSebastian Reichel * POWER_SUPPLY_PROP_TEMP_AMBIENT, 1040*8c0984e5SSebastian Reichel */ 1041*8c0984e5SSebastian Reichel }; 1042*8c0984e5SSebastian Reichel 1043*8c0984e5SSebastian Reichel static const struct power_supply_desc psy_default = { 1044*8c0984e5SSebastian Reichel .name = "battery", 1045*8c0984e5SSebastian Reichel .type = POWER_SUPPLY_TYPE_BATTERY, 1046*8c0984e5SSebastian Reichel .properties = default_charger_props, 1047*8c0984e5SSebastian Reichel .num_properties = ARRAY_SIZE(default_charger_props), 1048*8c0984e5SSebastian Reichel .get_property = charger_get_property, 1049*8c0984e5SSebastian Reichel .no_thermal = true, 1050*8c0984e5SSebastian Reichel }; 1051*8c0984e5SSebastian Reichel 1052*8c0984e5SSebastian Reichel /** 1053*8c0984e5SSebastian Reichel * cm_setup_timer - For in-suspend monitoring setup wakeup alarm 1054*8c0984e5SSebastian Reichel * for suspend_again. 1055*8c0984e5SSebastian Reichel * 1056*8c0984e5SSebastian Reichel * Returns true if the alarm is set for Charger Manager to use. 1057*8c0984e5SSebastian Reichel * Returns false if 1058*8c0984e5SSebastian Reichel * cm_setup_timer fails to set an alarm, 1059*8c0984e5SSebastian Reichel * cm_setup_timer does not need to set an alarm for Charger Manager, 1060*8c0984e5SSebastian Reichel * or an alarm previously configured is to be used. 1061*8c0984e5SSebastian Reichel */ 1062*8c0984e5SSebastian Reichel static bool cm_setup_timer(void) 1063*8c0984e5SSebastian Reichel { 1064*8c0984e5SSebastian Reichel struct charger_manager *cm; 1065*8c0984e5SSebastian Reichel unsigned int wakeup_ms = UINT_MAX; 1066*8c0984e5SSebastian Reichel int timer_req = 0; 1067*8c0984e5SSebastian Reichel 1068*8c0984e5SSebastian Reichel if (time_after(next_polling, jiffies)) 1069*8c0984e5SSebastian Reichel CM_MIN_VALID(wakeup_ms, 1070*8c0984e5SSebastian Reichel jiffies_to_msecs(next_polling - jiffies)); 1071*8c0984e5SSebastian Reichel 1072*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 1073*8c0984e5SSebastian Reichel list_for_each_entry(cm, &cm_list, entry) { 1074*8c0984e5SSebastian Reichel unsigned int fbchk_ms = 0; 1075*8c0984e5SSebastian Reichel 1076*8c0984e5SSebastian Reichel /* fullbatt_vchk is required. setup timer for that */ 1077*8c0984e5SSebastian Reichel if (cm->fullbatt_vchk_jiffies_at) { 1078*8c0984e5SSebastian Reichel fbchk_ms = jiffies_to_msecs(cm->fullbatt_vchk_jiffies_at 1079*8c0984e5SSebastian Reichel - jiffies); 1080*8c0984e5SSebastian Reichel if (time_is_before_eq_jiffies( 1081*8c0984e5SSebastian Reichel cm->fullbatt_vchk_jiffies_at) || 1082*8c0984e5SSebastian Reichel msecs_to_jiffies(fbchk_ms) < CM_JIFFIES_SMALL) { 1083*8c0984e5SSebastian Reichel fullbatt_vchk(&cm->fullbatt_vchk_work.work); 1084*8c0984e5SSebastian Reichel fbchk_ms = 0; 1085*8c0984e5SSebastian Reichel } 1086*8c0984e5SSebastian Reichel } 1087*8c0984e5SSebastian Reichel CM_MIN_VALID(wakeup_ms, fbchk_ms); 1088*8c0984e5SSebastian Reichel 1089*8c0984e5SSebastian Reichel /* Skip if polling is not required for this CM */ 1090*8c0984e5SSebastian Reichel if (!is_polling_required(cm) && !cm->emergency_stop) 1091*8c0984e5SSebastian Reichel continue; 1092*8c0984e5SSebastian Reichel timer_req++; 1093*8c0984e5SSebastian Reichel if (cm->desc->polling_interval_ms == 0) 1094*8c0984e5SSebastian Reichel continue; 1095*8c0984e5SSebastian Reichel CM_MIN_VALID(wakeup_ms, cm->desc->polling_interval_ms); 1096*8c0984e5SSebastian Reichel } 1097*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 1098*8c0984e5SSebastian Reichel 1099*8c0984e5SSebastian Reichel if (timer_req && cm_timer) { 1100*8c0984e5SSebastian Reichel ktime_t now, add; 1101*8c0984e5SSebastian Reichel 1102*8c0984e5SSebastian Reichel /* 1103*8c0984e5SSebastian Reichel * Set alarm with the polling interval (wakeup_ms) 1104*8c0984e5SSebastian Reichel * The alarm time should be NOW + CM_RTC_SMALL or later. 1105*8c0984e5SSebastian Reichel */ 1106*8c0984e5SSebastian Reichel if (wakeup_ms == UINT_MAX || 1107*8c0984e5SSebastian Reichel wakeup_ms < CM_RTC_SMALL * MSEC_PER_SEC) 1108*8c0984e5SSebastian Reichel wakeup_ms = 2 * CM_RTC_SMALL * MSEC_PER_SEC; 1109*8c0984e5SSebastian Reichel 1110*8c0984e5SSebastian Reichel pr_info("Charger Manager wakeup timer: %u ms\n", wakeup_ms); 1111*8c0984e5SSebastian Reichel 1112*8c0984e5SSebastian Reichel now = ktime_get_boottime(); 1113*8c0984e5SSebastian Reichel add = ktime_set(wakeup_ms / MSEC_PER_SEC, 1114*8c0984e5SSebastian Reichel (wakeup_ms % MSEC_PER_SEC) * NSEC_PER_MSEC); 1115*8c0984e5SSebastian Reichel alarm_start(cm_timer, ktime_add(now, add)); 1116*8c0984e5SSebastian Reichel 1117*8c0984e5SSebastian Reichel cm_suspend_duration_ms = wakeup_ms; 1118*8c0984e5SSebastian Reichel 1119*8c0984e5SSebastian Reichel return true; 1120*8c0984e5SSebastian Reichel } 1121*8c0984e5SSebastian Reichel return false; 1122*8c0984e5SSebastian Reichel } 1123*8c0984e5SSebastian Reichel 1124*8c0984e5SSebastian Reichel /** 1125*8c0984e5SSebastian Reichel * charger_extcon_work - enable/diable charger according to the state 1126*8c0984e5SSebastian Reichel * of charger cable 1127*8c0984e5SSebastian Reichel * 1128*8c0984e5SSebastian Reichel * @work: work_struct of the function charger_extcon_work. 1129*8c0984e5SSebastian Reichel */ 1130*8c0984e5SSebastian Reichel static void charger_extcon_work(struct work_struct *work) 1131*8c0984e5SSebastian Reichel { 1132*8c0984e5SSebastian Reichel struct charger_cable *cable = 1133*8c0984e5SSebastian Reichel container_of(work, struct charger_cable, wq); 1134*8c0984e5SSebastian Reichel int ret; 1135*8c0984e5SSebastian Reichel 1136*8c0984e5SSebastian Reichel if (cable->attached && cable->min_uA != 0 && cable->max_uA != 0) { 1137*8c0984e5SSebastian Reichel ret = regulator_set_current_limit(cable->charger->consumer, 1138*8c0984e5SSebastian Reichel cable->min_uA, cable->max_uA); 1139*8c0984e5SSebastian Reichel if (ret < 0) { 1140*8c0984e5SSebastian Reichel pr_err("Cannot set current limit of %s (%s)\n", 1141*8c0984e5SSebastian Reichel cable->charger->regulator_name, cable->name); 1142*8c0984e5SSebastian Reichel return; 1143*8c0984e5SSebastian Reichel } 1144*8c0984e5SSebastian Reichel 1145*8c0984e5SSebastian Reichel pr_info("Set current limit of %s : %duA ~ %duA\n", 1146*8c0984e5SSebastian Reichel cable->charger->regulator_name, 1147*8c0984e5SSebastian Reichel cable->min_uA, cable->max_uA); 1148*8c0984e5SSebastian Reichel } 1149*8c0984e5SSebastian Reichel 1150*8c0984e5SSebastian Reichel try_charger_enable(cable->cm, cable->attached); 1151*8c0984e5SSebastian Reichel } 1152*8c0984e5SSebastian Reichel 1153*8c0984e5SSebastian Reichel /** 1154*8c0984e5SSebastian Reichel * charger_extcon_notifier - receive the state of charger cable 1155*8c0984e5SSebastian Reichel * when registered cable is attached or detached. 1156*8c0984e5SSebastian Reichel * 1157*8c0984e5SSebastian Reichel * @self: the notifier block of the charger_extcon_notifier. 1158*8c0984e5SSebastian Reichel * @event: the cable state. 1159*8c0984e5SSebastian Reichel * @ptr: the data pointer of notifier block. 1160*8c0984e5SSebastian Reichel */ 1161*8c0984e5SSebastian Reichel static int charger_extcon_notifier(struct notifier_block *self, 1162*8c0984e5SSebastian Reichel unsigned long event, void *ptr) 1163*8c0984e5SSebastian Reichel { 1164*8c0984e5SSebastian Reichel struct charger_cable *cable = 1165*8c0984e5SSebastian Reichel container_of(self, struct charger_cable, nb); 1166*8c0984e5SSebastian Reichel 1167*8c0984e5SSebastian Reichel /* 1168*8c0984e5SSebastian Reichel * The newly state of charger cable. 1169*8c0984e5SSebastian Reichel * If cable is attached, cable->attached is true. 1170*8c0984e5SSebastian Reichel */ 1171*8c0984e5SSebastian Reichel cable->attached = event; 1172*8c0984e5SSebastian Reichel 1173*8c0984e5SSebastian Reichel /* 1174*8c0984e5SSebastian Reichel * Setup monitoring to check battery state 1175*8c0984e5SSebastian Reichel * when charger cable is attached. 1176*8c0984e5SSebastian Reichel */ 1177*8c0984e5SSebastian Reichel if (cable->attached && is_polling_required(cable->cm)) { 1178*8c0984e5SSebastian Reichel cancel_work_sync(&setup_polling); 1179*8c0984e5SSebastian Reichel schedule_work(&setup_polling); 1180*8c0984e5SSebastian Reichel } 1181*8c0984e5SSebastian Reichel 1182*8c0984e5SSebastian Reichel /* 1183*8c0984e5SSebastian Reichel * Setup work for controlling charger(regulator) 1184*8c0984e5SSebastian Reichel * according to charger cable. 1185*8c0984e5SSebastian Reichel */ 1186*8c0984e5SSebastian Reichel schedule_work(&cable->wq); 1187*8c0984e5SSebastian Reichel 1188*8c0984e5SSebastian Reichel return NOTIFY_DONE; 1189*8c0984e5SSebastian Reichel } 1190*8c0984e5SSebastian Reichel 1191*8c0984e5SSebastian Reichel /** 1192*8c0984e5SSebastian Reichel * charger_extcon_init - register external connector to use it 1193*8c0984e5SSebastian Reichel * as the charger cable 1194*8c0984e5SSebastian Reichel * 1195*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 1196*8c0984e5SSebastian Reichel * @cable: the Charger cable representing the external connector. 1197*8c0984e5SSebastian Reichel */ 1198*8c0984e5SSebastian Reichel static int charger_extcon_init(struct charger_manager *cm, 1199*8c0984e5SSebastian Reichel struct charger_cable *cable) 1200*8c0984e5SSebastian Reichel { 1201*8c0984e5SSebastian Reichel int ret = 0; 1202*8c0984e5SSebastian Reichel 1203*8c0984e5SSebastian Reichel /* 1204*8c0984e5SSebastian Reichel * Charger manager use Extcon framework to identify 1205*8c0984e5SSebastian Reichel * the charger cable among various external connector 1206*8c0984e5SSebastian Reichel * cable (e.g., TA, USB, MHL, Dock). 1207*8c0984e5SSebastian Reichel */ 1208*8c0984e5SSebastian Reichel INIT_WORK(&cable->wq, charger_extcon_work); 1209*8c0984e5SSebastian Reichel cable->nb.notifier_call = charger_extcon_notifier; 1210*8c0984e5SSebastian Reichel ret = extcon_register_interest(&cable->extcon_dev, 1211*8c0984e5SSebastian Reichel cable->extcon_name, cable->name, &cable->nb); 1212*8c0984e5SSebastian Reichel if (ret < 0) { 1213*8c0984e5SSebastian Reichel pr_info("Cannot register extcon_dev for %s(cable: %s)\n", 1214*8c0984e5SSebastian Reichel cable->extcon_name, cable->name); 1215*8c0984e5SSebastian Reichel ret = -EINVAL; 1216*8c0984e5SSebastian Reichel } 1217*8c0984e5SSebastian Reichel 1218*8c0984e5SSebastian Reichel return ret; 1219*8c0984e5SSebastian Reichel } 1220*8c0984e5SSebastian Reichel 1221*8c0984e5SSebastian Reichel /** 1222*8c0984e5SSebastian Reichel * charger_manager_register_extcon - Register extcon device to recevie state 1223*8c0984e5SSebastian Reichel * of charger cable. 1224*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 1225*8c0984e5SSebastian Reichel * 1226*8c0984e5SSebastian Reichel * This function support EXTCON(External Connector) subsystem to detect the 1227*8c0984e5SSebastian Reichel * state of charger cables for enabling or disabling charger(regulator) and 1228*8c0984e5SSebastian Reichel * select the charger cable for charging among a number of external cable 1229*8c0984e5SSebastian Reichel * according to policy of H/W board. 1230*8c0984e5SSebastian Reichel */ 1231*8c0984e5SSebastian Reichel static int charger_manager_register_extcon(struct charger_manager *cm) 1232*8c0984e5SSebastian Reichel { 1233*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 1234*8c0984e5SSebastian Reichel struct charger_regulator *charger; 1235*8c0984e5SSebastian Reichel int ret = 0; 1236*8c0984e5SSebastian Reichel int i; 1237*8c0984e5SSebastian Reichel int j; 1238*8c0984e5SSebastian Reichel 1239*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 1240*8c0984e5SSebastian Reichel charger = &desc->charger_regulators[i]; 1241*8c0984e5SSebastian Reichel 1242*8c0984e5SSebastian Reichel charger->consumer = regulator_get(cm->dev, 1243*8c0984e5SSebastian Reichel charger->regulator_name); 1244*8c0984e5SSebastian Reichel if (IS_ERR(charger->consumer)) { 1245*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot find charger(%s)\n", 1246*8c0984e5SSebastian Reichel charger->regulator_name); 1247*8c0984e5SSebastian Reichel return PTR_ERR(charger->consumer); 1248*8c0984e5SSebastian Reichel } 1249*8c0984e5SSebastian Reichel charger->cm = cm; 1250*8c0984e5SSebastian Reichel 1251*8c0984e5SSebastian Reichel for (j = 0; j < charger->num_cables; j++) { 1252*8c0984e5SSebastian Reichel struct charger_cable *cable = &charger->cables[j]; 1253*8c0984e5SSebastian Reichel 1254*8c0984e5SSebastian Reichel ret = charger_extcon_init(cm, cable); 1255*8c0984e5SSebastian Reichel if (ret < 0) { 1256*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot initialize charger(%s)\n", 1257*8c0984e5SSebastian Reichel charger->regulator_name); 1258*8c0984e5SSebastian Reichel goto err; 1259*8c0984e5SSebastian Reichel } 1260*8c0984e5SSebastian Reichel cable->charger = charger; 1261*8c0984e5SSebastian Reichel cable->cm = cm; 1262*8c0984e5SSebastian Reichel } 1263*8c0984e5SSebastian Reichel } 1264*8c0984e5SSebastian Reichel 1265*8c0984e5SSebastian Reichel err: 1266*8c0984e5SSebastian Reichel return ret; 1267*8c0984e5SSebastian Reichel } 1268*8c0984e5SSebastian Reichel 1269*8c0984e5SSebastian Reichel /* help function of sysfs node to control charger(regulator) */ 1270*8c0984e5SSebastian Reichel static ssize_t charger_name_show(struct device *dev, 1271*8c0984e5SSebastian Reichel struct device_attribute *attr, char *buf) 1272*8c0984e5SSebastian Reichel { 1273*8c0984e5SSebastian Reichel struct charger_regulator *charger 1274*8c0984e5SSebastian Reichel = container_of(attr, struct charger_regulator, attr_name); 1275*8c0984e5SSebastian Reichel 1276*8c0984e5SSebastian Reichel return sprintf(buf, "%s\n", charger->regulator_name); 1277*8c0984e5SSebastian Reichel } 1278*8c0984e5SSebastian Reichel 1279*8c0984e5SSebastian Reichel static ssize_t charger_state_show(struct device *dev, 1280*8c0984e5SSebastian Reichel struct device_attribute *attr, char *buf) 1281*8c0984e5SSebastian Reichel { 1282*8c0984e5SSebastian Reichel struct charger_regulator *charger 1283*8c0984e5SSebastian Reichel = container_of(attr, struct charger_regulator, attr_state); 1284*8c0984e5SSebastian Reichel int state = 0; 1285*8c0984e5SSebastian Reichel 1286*8c0984e5SSebastian Reichel if (!charger->externally_control) 1287*8c0984e5SSebastian Reichel state = regulator_is_enabled(charger->consumer); 1288*8c0984e5SSebastian Reichel 1289*8c0984e5SSebastian Reichel return sprintf(buf, "%s\n", state ? "enabled" : "disabled"); 1290*8c0984e5SSebastian Reichel } 1291*8c0984e5SSebastian Reichel 1292*8c0984e5SSebastian Reichel static ssize_t charger_externally_control_show(struct device *dev, 1293*8c0984e5SSebastian Reichel struct device_attribute *attr, char *buf) 1294*8c0984e5SSebastian Reichel { 1295*8c0984e5SSebastian Reichel struct charger_regulator *charger = container_of(attr, 1296*8c0984e5SSebastian Reichel struct charger_regulator, attr_externally_control); 1297*8c0984e5SSebastian Reichel 1298*8c0984e5SSebastian Reichel return sprintf(buf, "%d\n", charger->externally_control); 1299*8c0984e5SSebastian Reichel } 1300*8c0984e5SSebastian Reichel 1301*8c0984e5SSebastian Reichel static ssize_t charger_externally_control_store(struct device *dev, 1302*8c0984e5SSebastian Reichel struct device_attribute *attr, const char *buf, 1303*8c0984e5SSebastian Reichel size_t count) 1304*8c0984e5SSebastian Reichel { 1305*8c0984e5SSebastian Reichel struct charger_regulator *charger 1306*8c0984e5SSebastian Reichel = container_of(attr, struct charger_regulator, 1307*8c0984e5SSebastian Reichel attr_externally_control); 1308*8c0984e5SSebastian Reichel struct charger_manager *cm = charger->cm; 1309*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 1310*8c0984e5SSebastian Reichel int i; 1311*8c0984e5SSebastian Reichel int ret; 1312*8c0984e5SSebastian Reichel int externally_control; 1313*8c0984e5SSebastian Reichel int chargers_externally_control = 1; 1314*8c0984e5SSebastian Reichel 1315*8c0984e5SSebastian Reichel ret = sscanf(buf, "%d", &externally_control); 1316*8c0984e5SSebastian Reichel if (ret == 0) { 1317*8c0984e5SSebastian Reichel ret = -EINVAL; 1318*8c0984e5SSebastian Reichel return ret; 1319*8c0984e5SSebastian Reichel } 1320*8c0984e5SSebastian Reichel 1321*8c0984e5SSebastian Reichel if (!externally_control) { 1322*8c0984e5SSebastian Reichel charger->externally_control = 0; 1323*8c0984e5SSebastian Reichel return count; 1324*8c0984e5SSebastian Reichel } 1325*8c0984e5SSebastian Reichel 1326*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 1327*8c0984e5SSebastian Reichel if (&desc->charger_regulators[i] != charger && 1328*8c0984e5SSebastian Reichel !desc->charger_regulators[i].externally_control) { 1329*8c0984e5SSebastian Reichel /* 1330*8c0984e5SSebastian Reichel * At least, one charger is controlled by 1331*8c0984e5SSebastian Reichel * charger-manager 1332*8c0984e5SSebastian Reichel */ 1333*8c0984e5SSebastian Reichel chargers_externally_control = 0; 1334*8c0984e5SSebastian Reichel break; 1335*8c0984e5SSebastian Reichel } 1336*8c0984e5SSebastian Reichel } 1337*8c0984e5SSebastian Reichel 1338*8c0984e5SSebastian Reichel if (!chargers_externally_control) { 1339*8c0984e5SSebastian Reichel if (cm->charger_enabled) { 1340*8c0984e5SSebastian Reichel try_charger_enable(charger->cm, false); 1341*8c0984e5SSebastian Reichel charger->externally_control = externally_control; 1342*8c0984e5SSebastian Reichel try_charger_enable(charger->cm, true); 1343*8c0984e5SSebastian Reichel } else { 1344*8c0984e5SSebastian Reichel charger->externally_control = externally_control; 1345*8c0984e5SSebastian Reichel } 1346*8c0984e5SSebastian Reichel } else { 1347*8c0984e5SSebastian Reichel dev_warn(cm->dev, 1348*8c0984e5SSebastian Reichel "'%s' regulator should be controlled in charger-manager because charger-manager must need at least one charger for charging\n", 1349*8c0984e5SSebastian Reichel charger->regulator_name); 1350*8c0984e5SSebastian Reichel } 1351*8c0984e5SSebastian Reichel 1352*8c0984e5SSebastian Reichel return count; 1353*8c0984e5SSebastian Reichel } 1354*8c0984e5SSebastian Reichel 1355*8c0984e5SSebastian Reichel /** 1356*8c0984e5SSebastian Reichel * charger_manager_register_sysfs - Register sysfs entry for each charger 1357*8c0984e5SSebastian Reichel * @cm: the Charger Manager representing the battery. 1358*8c0984e5SSebastian Reichel * 1359*8c0984e5SSebastian Reichel * This function add sysfs entry for charger(regulator) to control charger from 1360*8c0984e5SSebastian Reichel * user-space. If some development board use one more chargers for charging 1361*8c0984e5SSebastian Reichel * but only need one charger on specific case which is dependent on user 1362*8c0984e5SSebastian Reichel * scenario or hardware restrictions, the user enter 1 or 0(zero) to '/sys/ 1363*8c0984e5SSebastian Reichel * class/power_supply/battery/charger.[index]/externally_control'. For example, 1364*8c0984e5SSebastian Reichel * if user enter 1 to 'sys/class/power_supply/battery/charger.[index]/ 1365*8c0984e5SSebastian Reichel * externally_control, this charger isn't controlled from charger-manager and 1366*8c0984e5SSebastian Reichel * always stay off state of regulator. 1367*8c0984e5SSebastian Reichel */ 1368*8c0984e5SSebastian Reichel static int charger_manager_register_sysfs(struct charger_manager *cm) 1369*8c0984e5SSebastian Reichel { 1370*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 1371*8c0984e5SSebastian Reichel struct charger_regulator *charger; 1372*8c0984e5SSebastian Reichel int chargers_externally_control = 1; 1373*8c0984e5SSebastian Reichel char buf[11]; 1374*8c0984e5SSebastian Reichel char *str; 1375*8c0984e5SSebastian Reichel int ret = 0; 1376*8c0984e5SSebastian Reichel int i; 1377*8c0984e5SSebastian Reichel 1378*8c0984e5SSebastian Reichel /* Create sysfs entry to control charger(regulator) */ 1379*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 1380*8c0984e5SSebastian Reichel charger = &desc->charger_regulators[i]; 1381*8c0984e5SSebastian Reichel 1382*8c0984e5SSebastian Reichel snprintf(buf, 10, "charger.%d", i); 1383*8c0984e5SSebastian Reichel str = devm_kzalloc(cm->dev, 1384*8c0984e5SSebastian Reichel sizeof(char) * (strlen(buf) + 1), GFP_KERNEL); 1385*8c0984e5SSebastian Reichel if (!str) { 1386*8c0984e5SSebastian Reichel ret = -ENOMEM; 1387*8c0984e5SSebastian Reichel goto err; 1388*8c0984e5SSebastian Reichel } 1389*8c0984e5SSebastian Reichel strcpy(str, buf); 1390*8c0984e5SSebastian Reichel 1391*8c0984e5SSebastian Reichel charger->attrs[0] = &charger->attr_name.attr; 1392*8c0984e5SSebastian Reichel charger->attrs[1] = &charger->attr_state.attr; 1393*8c0984e5SSebastian Reichel charger->attrs[2] = &charger->attr_externally_control.attr; 1394*8c0984e5SSebastian Reichel charger->attrs[3] = NULL; 1395*8c0984e5SSebastian Reichel charger->attr_g.name = str; 1396*8c0984e5SSebastian Reichel charger->attr_g.attrs = charger->attrs; 1397*8c0984e5SSebastian Reichel 1398*8c0984e5SSebastian Reichel sysfs_attr_init(&charger->attr_name.attr); 1399*8c0984e5SSebastian Reichel charger->attr_name.attr.name = "name"; 1400*8c0984e5SSebastian Reichel charger->attr_name.attr.mode = 0444; 1401*8c0984e5SSebastian Reichel charger->attr_name.show = charger_name_show; 1402*8c0984e5SSebastian Reichel 1403*8c0984e5SSebastian Reichel sysfs_attr_init(&charger->attr_state.attr); 1404*8c0984e5SSebastian Reichel charger->attr_state.attr.name = "state"; 1405*8c0984e5SSebastian Reichel charger->attr_state.attr.mode = 0444; 1406*8c0984e5SSebastian Reichel charger->attr_state.show = charger_state_show; 1407*8c0984e5SSebastian Reichel 1408*8c0984e5SSebastian Reichel sysfs_attr_init(&charger->attr_externally_control.attr); 1409*8c0984e5SSebastian Reichel charger->attr_externally_control.attr.name 1410*8c0984e5SSebastian Reichel = "externally_control"; 1411*8c0984e5SSebastian Reichel charger->attr_externally_control.attr.mode = 0644; 1412*8c0984e5SSebastian Reichel charger->attr_externally_control.show 1413*8c0984e5SSebastian Reichel = charger_externally_control_show; 1414*8c0984e5SSebastian Reichel charger->attr_externally_control.store 1415*8c0984e5SSebastian Reichel = charger_externally_control_store; 1416*8c0984e5SSebastian Reichel 1417*8c0984e5SSebastian Reichel if (!desc->charger_regulators[i].externally_control || 1418*8c0984e5SSebastian Reichel !chargers_externally_control) 1419*8c0984e5SSebastian Reichel chargers_externally_control = 0; 1420*8c0984e5SSebastian Reichel 1421*8c0984e5SSebastian Reichel dev_info(cm->dev, "'%s' regulator's externally_control is %d\n", 1422*8c0984e5SSebastian Reichel charger->regulator_name, charger->externally_control); 1423*8c0984e5SSebastian Reichel 1424*8c0984e5SSebastian Reichel ret = sysfs_create_group(&cm->charger_psy->dev.kobj, 1425*8c0984e5SSebastian Reichel &charger->attr_g); 1426*8c0984e5SSebastian Reichel if (ret < 0) { 1427*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot create sysfs entry of %s regulator\n", 1428*8c0984e5SSebastian Reichel charger->regulator_name); 1429*8c0984e5SSebastian Reichel ret = -EINVAL; 1430*8c0984e5SSebastian Reichel goto err; 1431*8c0984e5SSebastian Reichel } 1432*8c0984e5SSebastian Reichel } 1433*8c0984e5SSebastian Reichel 1434*8c0984e5SSebastian Reichel if (chargers_externally_control) { 1435*8c0984e5SSebastian Reichel dev_err(cm->dev, "Cannot register regulator because charger-manager must need at least one charger for charging battery\n"); 1436*8c0984e5SSebastian Reichel ret = -EINVAL; 1437*8c0984e5SSebastian Reichel goto err; 1438*8c0984e5SSebastian Reichel } 1439*8c0984e5SSebastian Reichel 1440*8c0984e5SSebastian Reichel err: 1441*8c0984e5SSebastian Reichel return ret; 1442*8c0984e5SSebastian Reichel } 1443*8c0984e5SSebastian Reichel 1444*8c0984e5SSebastian Reichel static int cm_init_thermal_data(struct charger_manager *cm, 1445*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge) 1446*8c0984e5SSebastian Reichel { 1447*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 1448*8c0984e5SSebastian Reichel union power_supply_propval val; 1449*8c0984e5SSebastian Reichel int ret; 1450*8c0984e5SSebastian Reichel 1451*8c0984e5SSebastian Reichel /* Verify whether fuel gauge provides battery temperature */ 1452*8c0984e5SSebastian Reichel ret = power_supply_get_property(fuel_gauge, 1453*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP, &val); 1454*8c0984e5SSebastian Reichel 1455*8c0984e5SSebastian Reichel if (!ret) { 1456*8c0984e5SSebastian Reichel cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = 1457*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP; 1458*8c0984e5SSebastian Reichel cm->charger_psy_desc.num_properties++; 1459*8c0984e5SSebastian Reichel cm->desc->measure_battery_temp = true; 1460*8c0984e5SSebastian Reichel } 1461*8c0984e5SSebastian Reichel #ifdef CONFIG_THERMAL 1462*8c0984e5SSebastian Reichel if (ret && desc->thermal_zone) { 1463*8c0984e5SSebastian Reichel cm->tzd_batt = 1464*8c0984e5SSebastian Reichel thermal_zone_get_zone_by_name(desc->thermal_zone); 1465*8c0984e5SSebastian Reichel if (IS_ERR(cm->tzd_batt)) 1466*8c0984e5SSebastian Reichel return PTR_ERR(cm->tzd_batt); 1467*8c0984e5SSebastian Reichel 1468*8c0984e5SSebastian Reichel /* Use external thermometer */ 1469*8c0984e5SSebastian Reichel cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = 1470*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_TEMP_AMBIENT; 1471*8c0984e5SSebastian Reichel cm->charger_psy_desc.num_properties++; 1472*8c0984e5SSebastian Reichel cm->desc->measure_battery_temp = true; 1473*8c0984e5SSebastian Reichel ret = 0; 1474*8c0984e5SSebastian Reichel } 1475*8c0984e5SSebastian Reichel #endif 1476*8c0984e5SSebastian Reichel if (cm->desc->measure_battery_temp) { 1477*8c0984e5SSebastian Reichel /* NOTICE : Default allowable minimum charge temperature is 0 */ 1478*8c0984e5SSebastian Reichel if (!desc->temp_max) 1479*8c0984e5SSebastian Reichel desc->temp_max = CM_DEFAULT_CHARGE_TEMP_MAX; 1480*8c0984e5SSebastian Reichel if (!desc->temp_diff) 1481*8c0984e5SSebastian Reichel desc->temp_diff = CM_DEFAULT_RECHARGE_TEMP_DIFF; 1482*8c0984e5SSebastian Reichel } 1483*8c0984e5SSebastian Reichel 1484*8c0984e5SSebastian Reichel return ret; 1485*8c0984e5SSebastian Reichel } 1486*8c0984e5SSebastian Reichel 1487*8c0984e5SSebastian Reichel static const struct of_device_id charger_manager_match[] = { 1488*8c0984e5SSebastian Reichel { 1489*8c0984e5SSebastian Reichel .compatible = "charger-manager", 1490*8c0984e5SSebastian Reichel }, 1491*8c0984e5SSebastian Reichel {}, 1492*8c0984e5SSebastian Reichel }; 1493*8c0984e5SSebastian Reichel 1494*8c0984e5SSebastian Reichel static struct charger_desc *of_cm_parse_desc(struct device *dev) 1495*8c0984e5SSebastian Reichel { 1496*8c0984e5SSebastian Reichel struct charger_desc *desc; 1497*8c0984e5SSebastian Reichel struct device_node *np = dev->of_node; 1498*8c0984e5SSebastian Reichel u32 poll_mode = CM_POLL_DISABLE; 1499*8c0984e5SSebastian Reichel u32 battery_stat = CM_NO_BATTERY; 1500*8c0984e5SSebastian Reichel int num_chgs = 0; 1501*8c0984e5SSebastian Reichel 1502*8c0984e5SSebastian Reichel desc = devm_kzalloc(dev, sizeof(*desc), GFP_KERNEL); 1503*8c0984e5SSebastian Reichel if (!desc) 1504*8c0984e5SSebastian Reichel return ERR_PTR(-ENOMEM); 1505*8c0984e5SSebastian Reichel 1506*8c0984e5SSebastian Reichel of_property_read_string(np, "cm-name", &desc->psy_name); 1507*8c0984e5SSebastian Reichel 1508*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-poll-mode", &poll_mode); 1509*8c0984e5SSebastian Reichel desc->polling_mode = poll_mode; 1510*8c0984e5SSebastian Reichel 1511*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-poll-interval", 1512*8c0984e5SSebastian Reichel &desc->polling_interval_ms); 1513*8c0984e5SSebastian Reichel 1514*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-fullbatt-vchkdrop-ms", 1515*8c0984e5SSebastian Reichel &desc->fullbatt_vchkdrop_ms); 1516*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-fullbatt-vchkdrop-volt", 1517*8c0984e5SSebastian Reichel &desc->fullbatt_vchkdrop_uV); 1518*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-fullbatt-voltage", &desc->fullbatt_uV); 1519*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-fullbatt-soc", &desc->fullbatt_soc); 1520*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-fullbatt-capacity", 1521*8c0984e5SSebastian Reichel &desc->fullbatt_full_capacity); 1522*8c0984e5SSebastian Reichel 1523*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-battery-stat", &battery_stat); 1524*8c0984e5SSebastian Reichel desc->battery_present = battery_stat; 1525*8c0984e5SSebastian Reichel 1526*8c0984e5SSebastian Reichel /* chargers */ 1527*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-num-chargers", &num_chgs); 1528*8c0984e5SSebastian Reichel if (num_chgs) { 1529*8c0984e5SSebastian Reichel /* Allocate empty bin at the tail of array */ 1530*8c0984e5SSebastian Reichel desc->psy_charger_stat = devm_kzalloc(dev, sizeof(char *) 1531*8c0984e5SSebastian Reichel * (num_chgs + 1), GFP_KERNEL); 1532*8c0984e5SSebastian Reichel if (desc->psy_charger_stat) { 1533*8c0984e5SSebastian Reichel int i; 1534*8c0984e5SSebastian Reichel for (i = 0; i < num_chgs; i++) 1535*8c0984e5SSebastian Reichel of_property_read_string_index(np, "cm-chargers", 1536*8c0984e5SSebastian Reichel i, &desc->psy_charger_stat[i]); 1537*8c0984e5SSebastian Reichel } else { 1538*8c0984e5SSebastian Reichel return ERR_PTR(-ENOMEM); 1539*8c0984e5SSebastian Reichel } 1540*8c0984e5SSebastian Reichel } 1541*8c0984e5SSebastian Reichel 1542*8c0984e5SSebastian Reichel of_property_read_string(np, "cm-fuel-gauge", &desc->psy_fuel_gauge); 1543*8c0984e5SSebastian Reichel 1544*8c0984e5SSebastian Reichel of_property_read_string(np, "cm-thermal-zone", &desc->thermal_zone); 1545*8c0984e5SSebastian Reichel 1546*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-battery-cold", &desc->temp_min); 1547*8c0984e5SSebastian Reichel if (of_get_property(np, "cm-battery-cold-in-minus", NULL)) 1548*8c0984e5SSebastian Reichel desc->temp_min *= -1; 1549*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-battery-hot", &desc->temp_max); 1550*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-battery-temp-diff", &desc->temp_diff); 1551*8c0984e5SSebastian Reichel 1552*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-charging-max", 1553*8c0984e5SSebastian Reichel &desc->charging_max_duration_ms); 1554*8c0984e5SSebastian Reichel of_property_read_u32(np, "cm-discharging-max", 1555*8c0984e5SSebastian Reichel &desc->discharging_max_duration_ms); 1556*8c0984e5SSebastian Reichel 1557*8c0984e5SSebastian Reichel /* battery charger regualtors */ 1558*8c0984e5SSebastian Reichel desc->num_charger_regulators = of_get_child_count(np); 1559*8c0984e5SSebastian Reichel if (desc->num_charger_regulators) { 1560*8c0984e5SSebastian Reichel struct charger_regulator *chg_regs; 1561*8c0984e5SSebastian Reichel struct device_node *child; 1562*8c0984e5SSebastian Reichel 1563*8c0984e5SSebastian Reichel chg_regs = devm_kzalloc(dev, sizeof(*chg_regs) 1564*8c0984e5SSebastian Reichel * desc->num_charger_regulators, 1565*8c0984e5SSebastian Reichel GFP_KERNEL); 1566*8c0984e5SSebastian Reichel if (!chg_regs) 1567*8c0984e5SSebastian Reichel return ERR_PTR(-ENOMEM); 1568*8c0984e5SSebastian Reichel 1569*8c0984e5SSebastian Reichel desc->charger_regulators = chg_regs; 1570*8c0984e5SSebastian Reichel 1571*8c0984e5SSebastian Reichel for_each_child_of_node(np, child) { 1572*8c0984e5SSebastian Reichel struct charger_cable *cables; 1573*8c0984e5SSebastian Reichel struct device_node *_child; 1574*8c0984e5SSebastian Reichel 1575*8c0984e5SSebastian Reichel of_property_read_string(child, "cm-regulator-name", 1576*8c0984e5SSebastian Reichel &chg_regs->regulator_name); 1577*8c0984e5SSebastian Reichel 1578*8c0984e5SSebastian Reichel /* charger cables */ 1579*8c0984e5SSebastian Reichel chg_regs->num_cables = of_get_child_count(child); 1580*8c0984e5SSebastian Reichel if (chg_regs->num_cables) { 1581*8c0984e5SSebastian Reichel cables = devm_kzalloc(dev, sizeof(*cables) 1582*8c0984e5SSebastian Reichel * chg_regs->num_cables, 1583*8c0984e5SSebastian Reichel GFP_KERNEL); 1584*8c0984e5SSebastian Reichel if (!cables) { 1585*8c0984e5SSebastian Reichel of_node_put(child); 1586*8c0984e5SSebastian Reichel return ERR_PTR(-ENOMEM); 1587*8c0984e5SSebastian Reichel } 1588*8c0984e5SSebastian Reichel 1589*8c0984e5SSebastian Reichel chg_regs->cables = cables; 1590*8c0984e5SSebastian Reichel 1591*8c0984e5SSebastian Reichel for_each_child_of_node(child, _child) { 1592*8c0984e5SSebastian Reichel of_property_read_string(_child, 1593*8c0984e5SSebastian Reichel "cm-cable-name", &cables->name); 1594*8c0984e5SSebastian Reichel of_property_read_string(_child, 1595*8c0984e5SSebastian Reichel "cm-cable-extcon", 1596*8c0984e5SSebastian Reichel &cables->extcon_name); 1597*8c0984e5SSebastian Reichel of_property_read_u32(_child, 1598*8c0984e5SSebastian Reichel "cm-cable-min", 1599*8c0984e5SSebastian Reichel &cables->min_uA); 1600*8c0984e5SSebastian Reichel of_property_read_u32(_child, 1601*8c0984e5SSebastian Reichel "cm-cable-max", 1602*8c0984e5SSebastian Reichel &cables->max_uA); 1603*8c0984e5SSebastian Reichel cables++; 1604*8c0984e5SSebastian Reichel } 1605*8c0984e5SSebastian Reichel } 1606*8c0984e5SSebastian Reichel chg_regs++; 1607*8c0984e5SSebastian Reichel } 1608*8c0984e5SSebastian Reichel } 1609*8c0984e5SSebastian Reichel return desc; 1610*8c0984e5SSebastian Reichel } 1611*8c0984e5SSebastian Reichel 1612*8c0984e5SSebastian Reichel static inline struct charger_desc *cm_get_drv_data(struct platform_device *pdev) 1613*8c0984e5SSebastian Reichel { 1614*8c0984e5SSebastian Reichel if (pdev->dev.of_node) 1615*8c0984e5SSebastian Reichel return of_cm_parse_desc(&pdev->dev); 1616*8c0984e5SSebastian Reichel return dev_get_platdata(&pdev->dev); 1617*8c0984e5SSebastian Reichel } 1618*8c0984e5SSebastian Reichel 1619*8c0984e5SSebastian Reichel static enum alarmtimer_restart cm_timer_func(struct alarm *alarm, ktime_t now) 1620*8c0984e5SSebastian Reichel { 1621*8c0984e5SSebastian Reichel cm_timer_set = false; 1622*8c0984e5SSebastian Reichel return ALARMTIMER_NORESTART; 1623*8c0984e5SSebastian Reichel } 1624*8c0984e5SSebastian Reichel 1625*8c0984e5SSebastian Reichel static int charger_manager_probe(struct platform_device *pdev) 1626*8c0984e5SSebastian Reichel { 1627*8c0984e5SSebastian Reichel struct charger_desc *desc = cm_get_drv_data(pdev); 1628*8c0984e5SSebastian Reichel struct charger_manager *cm; 1629*8c0984e5SSebastian Reichel int ret = 0, i = 0; 1630*8c0984e5SSebastian Reichel int j = 0; 1631*8c0984e5SSebastian Reichel union power_supply_propval val; 1632*8c0984e5SSebastian Reichel struct power_supply *fuel_gauge; 1633*8c0984e5SSebastian Reichel struct power_supply_config psy_cfg = {}; 1634*8c0984e5SSebastian Reichel 1635*8c0984e5SSebastian Reichel if (IS_ERR(desc)) { 1636*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "No platform data (desc) found\n"); 1637*8c0984e5SSebastian Reichel return -ENODEV; 1638*8c0984e5SSebastian Reichel } 1639*8c0984e5SSebastian Reichel 1640*8c0984e5SSebastian Reichel cm = devm_kzalloc(&pdev->dev, 1641*8c0984e5SSebastian Reichel sizeof(struct charger_manager), GFP_KERNEL); 1642*8c0984e5SSebastian Reichel if (!cm) 1643*8c0984e5SSebastian Reichel return -ENOMEM; 1644*8c0984e5SSebastian Reichel 1645*8c0984e5SSebastian Reichel /* Basic Values. Unspecified are Null or 0 */ 1646*8c0984e5SSebastian Reichel cm->dev = &pdev->dev; 1647*8c0984e5SSebastian Reichel cm->desc = desc; 1648*8c0984e5SSebastian Reichel psy_cfg.drv_data = cm; 1649*8c0984e5SSebastian Reichel 1650*8c0984e5SSebastian Reichel /* Initialize alarm timer */ 1651*8c0984e5SSebastian Reichel if (alarmtimer_get_rtcdev()) { 1652*8c0984e5SSebastian Reichel cm_timer = devm_kzalloc(cm->dev, sizeof(*cm_timer), GFP_KERNEL); 1653*8c0984e5SSebastian Reichel alarm_init(cm_timer, ALARM_BOOTTIME, cm_timer_func); 1654*8c0984e5SSebastian Reichel } 1655*8c0984e5SSebastian Reichel 1656*8c0984e5SSebastian Reichel /* 1657*8c0984e5SSebastian Reichel * The following two do not need to be errors. 1658*8c0984e5SSebastian Reichel * Users may intentionally ignore those two features. 1659*8c0984e5SSebastian Reichel */ 1660*8c0984e5SSebastian Reichel if (desc->fullbatt_uV == 0) { 1661*8c0984e5SSebastian Reichel dev_info(&pdev->dev, "Ignoring full-battery voltage threshold as it is not supplied\n"); 1662*8c0984e5SSebastian Reichel } 1663*8c0984e5SSebastian Reichel if (!desc->fullbatt_vchkdrop_ms || !desc->fullbatt_vchkdrop_uV) { 1664*8c0984e5SSebastian Reichel dev_info(&pdev->dev, "Disabling full-battery voltage drop checking mechanism as it is not supplied\n"); 1665*8c0984e5SSebastian Reichel desc->fullbatt_vchkdrop_ms = 0; 1666*8c0984e5SSebastian Reichel desc->fullbatt_vchkdrop_uV = 0; 1667*8c0984e5SSebastian Reichel } 1668*8c0984e5SSebastian Reichel if (desc->fullbatt_soc == 0) { 1669*8c0984e5SSebastian Reichel dev_info(&pdev->dev, "Ignoring full-battery soc(state of charge) threshold as it is not supplied\n"); 1670*8c0984e5SSebastian Reichel } 1671*8c0984e5SSebastian Reichel if (desc->fullbatt_full_capacity == 0) { 1672*8c0984e5SSebastian Reichel dev_info(&pdev->dev, "Ignoring full-battery full capacity threshold as it is not supplied\n"); 1673*8c0984e5SSebastian Reichel } 1674*8c0984e5SSebastian Reichel 1675*8c0984e5SSebastian Reichel if (!desc->charger_regulators || desc->num_charger_regulators < 1) { 1676*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "charger_regulators undefined\n"); 1677*8c0984e5SSebastian Reichel return -EINVAL; 1678*8c0984e5SSebastian Reichel } 1679*8c0984e5SSebastian Reichel 1680*8c0984e5SSebastian Reichel if (!desc->psy_charger_stat || !desc->psy_charger_stat[0]) { 1681*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "No power supply defined\n"); 1682*8c0984e5SSebastian Reichel return -EINVAL; 1683*8c0984e5SSebastian Reichel } 1684*8c0984e5SSebastian Reichel 1685*8c0984e5SSebastian Reichel if (!desc->psy_fuel_gauge) { 1686*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "No fuel gauge power supply defined\n"); 1687*8c0984e5SSebastian Reichel return -EINVAL; 1688*8c0984e5SSebastian Reichel } 1689*8c0984e5SSebastian Reichel 1690*8c0984e5SSebastian Reichel /* Counting index only */ 1691*8c0984e5SSebastian Reichel while (desc->psy_charger_stat[i]) 1692*8c0984e5SSebastian Reichel i++; 1693*8c0984e5SSebastian Reichel 1694*8c0984e5SSebastian Reichel /* Check if charger's supplies are present at probe */ 1695*8c0984e5SSebastian Reichel for (i = 0; desc->psy_charger_stat[i]; i++) { 1696*8c0984e5SSebastian Reichel struct power_supply *psy; 1697*8c0984e5SSebastian Reichel 1698*8c0984e5SSebastian Reichel psy = power_supply_get_by_name(desc->psy_charger_stat[i]); 1699*8c0984e5SSebastian Reichel if (!psy) { 1700*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 1701*8c0984e5SSebastian Reichel desc->psy_charger_stat[i]); 1702*8c0984e5SSebastian Reichel return -ENODEV; 1703*8c0984e5SSebastian Reichel } 1704*8c0984e5SSebastian Reichel power_supply_put(psy); 1705*8c0984e5SSebastian Reichel } 1706*8c0984e5SSebastian Reichel 1707*8c0984e5SSebastian Reichel if (desc->polling_interval_ms == 0 || 1708*8c0984e5SSebastian Reichel msecs_to_jiffies(desc->polling_interval_ms) <= CM_JIFFIES_SMALL) { 1709*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "polling_interval_ms is too small\n"); 1710*8c0984e5SSebastian Reichel return -EINVAL; 1711*8c0984e5SSebastian Reichel } 1712*8c0984e5SSebastian Reichel 1713*8c0984e5SSebastian Reichel if (!desc->charging_max_duration_ms || 1714*8c0984e5SSebastian Reichel !desc->discharging_max_duration_ms) { 1715*8c0984e5SSebastian Reichel dev_info(&pdev->dev, "Cannot limit charging duration checking mechanism to prevent overcharge/overheat and control discharging duration\n"); 1716*8c0984e5SSebastian Reichel desc->charging_max_duration_ms = 0; 1717*8c0984e5SSebastian Reichel desc->discharging_max_duration_ms = 0; 1718*8c0984e5SSebastian Reichel } 1719*8c0984e5SSebastian Reichel 1720*8c0984e5SSebastian Reichel platform_set_drvdata(pdev, cm); 1721*8c0984e5SSebastian Reichel 1722*8c0984e5SSebastian Reichel memcpy(&cm->charger_psy_desc, &psy_default, sizeof(psy_default)); 1723*8c0984e5SSebastian Reichel 1724*8c0984e5SSebastian Reichel if (!desc->psy_name) 1725*8c0984e5SSebastian Reichel strncpy(cm->psy_name_buf, psy_default.name, PSY_NAME_MAX); 1726*8c0984e5SSebastian Reichel else 1727*8c0984e5SSebastian Reichel strncpy(cm->psy_name_buf, desc->psy_name, PSY_NAME_MAX); 1728*8c0984e5SSebastian Reichel cm->charger_psy_desc.name = cm->psy_name_buf; 1729*8c0984e5SSebastian Reichel 1730*8c0984e5SSebastian Reichel /* Allocate for psy properties because they may vary */ 1731*8c0984e5SSebastian Reichel cm->charger_psy_desc.properties = devm_kzalloc(&pdev->dev, 1732*8c0984e5SSebastian Reichel sizeof(enum power_supply_property) 1733*8c0984e5SSebastian Reichel * (ARRAY_SIZE(default_charger_props) + 1734*8c0984e5SSebastian Reichel NUM_CHARGER_PSY_OPTIONAL), GFP_KERNEL); 1735*8c0984e5SSebastian Reichel if (!cm->charger_psy_desc.properties) 1736*8c0984e5SSebastian Reichel return -ENOMEM; 1737*8c0984e5SSebastian Reichel 1738*8c0984e5SSebastian Reichel memcpy(cm->charger_psy_desc.properties, default_charger_props, 1739*8c0984e5SSebastian Reichel sizeof(enum power_supply_property) * 1740*8c0984e5SSebastian Reichel ARRAY_SIZE(default_charger_props)); 1741*8c0984e5SSebastian Reichel cm->charger_psy_desc.num_properties = psy_default.num_properties; 1742*8c0984e5SSebastian Reichel 1743*8c0984e5SSebastian Reichel /* Find which optional psy-properties are available */ 1744*8c0984e5SSebastian Reichel fuel_gauge = power_supply_get_by_name(desc->psy_fuel_gauge); 1745*8c0984e5SSebastian Reichel if (!fuel_gauge) { 1746*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "Cannot find power supply \"%s\"\n", 1747*8c0984e5SSebastian Reichel desc->psy_fuel_gauge); 1748*8c0984e5SSebastian Reichel return -ENODEV; 1749*8c0984e5SSebastian Reichel } 1750*8c0984e5SSebastian Reichel if (!power_supply_get_property(fuel_gauge, 1751*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW, &val)) { 1752*8c0984e5SSebastian Reichel cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = 1753*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CHARGE_NOW; 1754*8c0984e5SSebastian Reichel cm->charger_psy_desc.num_properties++; 1755*8c0984e5SSebastian Reichel } 1756*8c0984e5SSebastian Reichel if (!power_supply_get_property(fuel_gauge, 1757*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW, 1758*8c0984e5SSebastian Reichel &val)) { 1759*8c0984e5SSebastian Reichel cm->charger_psy_desc.properties[cm->charger_psy_desc.num_properties] = 1760*8c0984e5SSebastian Reichel POWER_SUPPLY_PROP_CURRENT_NOW; 1761*8c0984e5SSebastian Reichel cm->charger_psy_desc.num_properties++; 1762*8c0984e5SSebastian Reichel } 1763*8c0984e5SSebastian Reichel 1764*8c0984e5SSebastian Reichel ret = cm_init_thermal_data(cm, fuel_gauge); 1765*8c0984e5SSebastian Reichel if (ret) { 1766*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "Failed to initialize thermal data\n"); 1767*8c0984e5SSebastian Reichel cm->desc->measure_battery_temp = false; 1768*8c0984e5SSebastian Reichel } 1769*8c0984e5SSebastian Reichel power_supply_put(fuel_gauge); 1770*8c0984e5SSebastian Reichel 1771*8c0984e5SSebastian Reichel INIT_DELAYED_WORK(&cm->fullbatt_vchk_work, fullbatt_vchk); 1772*8c0984e5SSebastian Reichel 1773*8c0984e5SSebastian Reichel cm->charger_psy = power_supply_register(&pdev->dev, 1774*8c0984e5SSebastian Reichel &cm->charger_psy_desc, 1775*8c0984e5SSebastian Reichel &psy_cfg); 1776*8c0984e5SSebastian Reichel if (IS_ERR(cm->charger_psy)) { 1777*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "Cannot register charger-manager with name \"%s\"\n", 1778*8c0984e5SSebastian Reichel cm->charger_psy_desc.name); 1779*8c0984e5SSebastian Reichel return PTR_ERR(cm->charger_psy); 1780*8c0984e5SSebastian Reichel } 1781*8c0984e5SSebastian Reichel 1782*8c0984e5SSebastian Reichel /* Register extcon device for charger cable */ 1783*8c0984e5SSebastian Reichel ret = charger_manager_register_extcon(cm); 1784*8c0984e5SSebastian Reichel if (ret < 0) { 1785*8c0984e5SSebastian Reichel dev_err(&pdev->dev, "Cannot initialize extcon device\n"); 1786*8c0984e5SSebastian Reichel goto err_reg_extcon; 1787*8c0984e5SSebastian Reichel } 1788*8c0984e5SSebastian Reichel 1789*8c0984e5SSebastian Reichel /* Register sysfs entry for charger(regulator) */ 1790*8c0984e5SSebastian Reichel ret = charger_manager_register_sysfs(cm); 1791*8c0984e5SSebastian Reichel if (ret < 0) { 1792*8c0984e5SSebastian Reichel dev_err(&pdev->dev, 1793*8c0984e5SSebastian Reichel "Cannot initialize sysfs entry of regulator\n"); 1794*8c0984e5SSebastian Reichel goto err_reg_sysfs; 1795*8c0984e5SSebastian Reichel } 1796*8c0984e5SSebastian Reichel 1797*8c0984e5SSebastian Reichel /* Add to the list */ 1798*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 1799*8c0984e5SSebastian Reichel list_add(&cm->entry, &cm_list); 1800*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 1801*8c0984e5SSebastian Reichel 1802*8c0984e5SSebastian Reichel /* 1803*8c0984e5SSebastian Reichel * Charger-manager is capable of waking up the systme from sleep 1804*8c0984e5SSebastian Reichel * when event is happend through cm_notify_event() 1805*8c0984e5SSebastian Reichel */ 1806*8c0984e5SSebastian Reichel device_init_wakeup(&pdev->dev, true); 1807*8c0984e5SSebastian Reichel device_set_wakeup_capable(&pdev->dev, false); 1808*8c0984e5SSebastian Reichel 1809*8c0984e5SSebastian Reichel /* 1810*8c0984e5SSebastian Reichel * Charger-manager have to check the charging state right after 1811*8c0984e5SSebastian Reichel * tialization of charger-manager and then update current charging 1812*8c0984e5SSebastian Reichel * state. 1813*8c0984e5SSebastian Reichel */ 1814*8c0984e5SSebastian Reichel cm_monitor(); 1815*8c0984e5SSebastian Reichel 1816*8c0984e5SSebastian Reichel schedule_work(&setup_polling); 1817*8c0984e5SSebastian Reichel 1818*8c0984e5SSebastian Reichel return 0; 1819*8c0984e5SSebastian Reichel 1820*8c0984e5SSebastian Reichel err_reg_sysfs: 1821*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 1822*8c0984e5SSebastian Reichel struct charger_regulator *charger; 1823*8c0984e5SSebastian Reichel 1824*8c0984e5SSebastian Reichel charger = &desc->charger_regulators[i]; 1825*8c0984e5SSebastian Reichel sysfs_remove_group(&cm->charger_psy->dev.kobj, 1826*8c0984e5SSebastian Reichel &charger->attr_g); 1827*8c0984e5SSebastian Reichel } 1828*8c0984e5SSebastian Reichel err_reg_extcon: 1829*8c0984e5SSebastian Reichel for (i = 0; i < desc->num_charger_regulators; i++) { 1830*8c0984e5SSebastian Reichel struct charger_regulator *charger; 1831*8c0984e5SSebastian Reichel 1832*8c0984e5SSebastian Reichel charger = &desc->charger_regulators[i]; 1833*8c0984e5SSebastian Reichel for (j = 0; j < charger->num_cables; j++) { 1834*8c0984e5SSebastian Reichel struct charger_cable *cable = &charger->cables[j]; 1835*8c0984e5SSebastian Reichel /* Remove notifier block if only edev exists */ 1836*8c0984e5SSebastian Reichel if (cable->extcon_dev.edev) 1837*8c0984e5SSebastian Reichel extcon_unregister_interest(&cable->extcon_dev); 1838*8c0984e5SSebastian Reichel } 1839*8c0984e5SSebastian Reichel 1840*8c0984e5SSebastian Reichel regulator_put(desc->charger_regulators[i].consumer); 1841*8c0984e5SSebastian Reichel } 1842*8c0984e5SSebastian Reichel 1843*8c0984e5SSebastian Reichel power_supply_unregister(cm->charger_psy); 1844*8c0984e5SSebastian Reichel 1845*8c0984e5SSebastian Reichel return ret; 1846*8c0984e5SSebastian Reichel } 1847*8c0984e5SSebastian Reichel 1848*8c0984e5SSebastian Reichel static int charger_manager_remove(struct platform_device *pdev) 1849*8c0984e5SSebastian Reichel { 1850*8c0984e5SSebastian Reichel struct charger_manager *cm = platform_get_drvdata(pdev); 1851*8c0984e5SSebastian Reichel struct charger_desc *desc = cm->desc; 1852*8c0984e5SSebastian Reichel int i = 0; 1853*8c0984e5SSebastian Reichel int j = 0; 1854*8c0984e5SSebastian Reichel 1855*8c0984e5SSebastian Reichel /* Remove from the list */ 1856*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 1857*8c0984e5SSebastian Reichel list_del(&cm->entry); 1858*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 1859*8c0984e5SSebastian Reichel 1860*8c0984e5SSebastian Reichel cancel_work_sync(&setup_polling); 1861*8c0984e5SSebastian Reichel cancel_delayed_work_sync(&cm_monitor_work); 1862*8c0984e5SSebastian Reichel 1863*8c0984e5SSebastian Reichel for (i = 0 ; i < desc->num_charger_regulators ; i++) { 1864*8c0984e5SSebastian Reichel struct charger_regulator *charger 1865*8c0984e5SSebastian Reichel = &desc->charger_regulators[i]; 1866*8c0984e5SSebastian Reichel for (j = 0 ; j < charger->num_cables ; j++) { 1867*8c0984e5SSebastian Reichel struct charger_cable *cable = &charger->cables[j]; 1868*8c0984e5SSebastian Reichel extcon_unregister_interest(&cable->extcon_dev); 1869*8c0984e5SSebastian Reichel } 1870*8c0984e5SSebastian Reichel } 1871*8c0984e5SSebastian Reichel 1872*8c0984e5SSebastian Reichel for (i = 0 ; i < desc->num_charger_regulators ; i++) 1873*8c0984e5SSebastian Reichel regulator_put(desc->charger_regulators[i].consumer); 1874*8c0984e5SSebastian Reichel 1875*8c0984e5SSebastian Reichel power_supply_unregister(cm->charger_psy); 1876*8c0984e5SSebastian Reichel 1877*8c0984e5SSebastian Reichel try_charger_enable(cm, false); 1878*8c0984e5SSebastian Reichel 1879*8c0984e5SSebastian Reichel return 0; 1880*8c0984e5SSebastian Reichel } 1881*8c0984e5SSebastian Reichel 1882*8c0984e5SSebastian Reichel static const struct platform_device_id charger_manager_id[] = { 1883*8c0984e5SSebastian Reichel { "charger-manager", 0 }, 1884*8c0984e5SSebastian Reichel { }, 1885*8c0984e5SSebastian Reichel }; 1886*8c0984e5SSebastian Reichel MODULE_DEVICE_TABLE(platform, charger_manager_id); 1887*8c0984e5SSebastian Reichel 1888*8c0984e5SSebastian Reichel static int cm_suspend_noirq(struct device *dev) 1889*8c0984e5SSebastian Reichel { 1890*8c0984e5SSebastian Reichel int ret = 0; 1891*8c0984e5SSebastian Reichel 1892*8c0984e5SSebastian Reichel if (device_may_wakeup(dev)) { 1893*8c0984e5SSebastian Reichel device_set_wakeup_capable(dev, false); 1894*8c0984e5SSebastian Reichel ret = -EAGAIN; 1895*8c0984e5SSebastian Reichel } 1896*8c0984e5SSebastian Reichel 1897*8c0984e5SSebastian Reichel return ret; 1898*8c0984e5SSebastian Reichel } 1899*8c0984e5SSebastian Reichel 1900*8c0984e5SSebastian Reichel static bool cm_need_to_awake(void) 1901*8c0984e5SSebastian Reichel { 1902*8c0984e5SSebastian Reichel struct charger_manager *cm; 1903*8c0984e5SSebastian Reichel 1904*8c0984e5SSebastian Reichel if (cm_timer) 1905*8c0984e5SSebastian Reichel return false; 1906*8c0984e5SSebastian Reichel 1907*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 1908*8c0984e5SSebastian Reichel list_for_each_entry(cm, &cm_list, entry) { 1909*8c0984e5SSebastian Reichel if (is_charging(cm)) { 1910*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 1911*8c0984e5SSebastian Reichel return true; 1912*8c0984e5SSebastian Reichel } 1913*8c0984e5SSebastian Reichel } 1914*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 1915*8c0984e5SSebastian Reichel 1916*8c0984e5SSebastian Reichel return false; 1917*8c0984e5SSebastian Reichel } 1918*8c0984e5SSebastian Reichel 1919*8c0984e5SSebastian Reichel static int cm_suspend_prepare(struct device *dev) 1920*8c0984e5SSebastian Reichel { 1921*8c0984e5SSebastian Reichel struct charger_manager *cm = dev_get_drvdata(dev); 1922*8c0984e5SSebastian Reichel 1923*8c0984e5SSebastian Reichel if (cm_need_to_awake()) 1924*8c0984e5SSebastian Reichel return -EBUSY; 1925*8c0984e5SSebastian Reichel 1926*8c0984e5SSebastian Reichel if (!cm_suspended) 1927*8c0984e5SSebastian Reichel cm_suspended = true; 1928*8c0984e5SSebastian Reichel 1929*8c0984e5SSebastian Reichel cm_timer_set = cm_setup_timer(); 1930*8c0984e5SSebastian Reichel 1931*8c0984e5SSebastian Reichel if (cm_timer_set) { 1932*8c0984e5SSebastian Reichel cancel_work_sync(&setup_polling); 1933*8c0984e5SSebastian Reichel cancel_delayed_work_sync(&cm_monitor_work); 1934*8c0984e5SSebastian Reichel cancel_delayed_work(&cm->fullbatt_vchk_work); 1935*8c0984e5SSebastian Reichel } 1936*8c0984e5SSebastian Reichel 1937*8c0984e5SSebastian Reichel return 0; 1938*8c0984e5SSebastian Reichel } 1939*8c0984e5SSebastian Reichel 1940*8c0984e5SSebastian Reichel static void cm_suspend_complete(struct device *dev) 1941*8c0984e5SSebastian Reichel { 1942*8c0984e5SSebastian Reichel struct charger_manager *cm = dev_get_drvdata(dev); 1943*8c0984e5SSebastian Reichel 1944*8c0984e5SSebastian Reichel if (cm_suspended) 1945*8c0984e5SSebastian Reichel cm_suspended = false; 1946*8c0984e5SSebastian Reichel 1947*8c0984e5SSebastian Reichel if (cm_timer_set) { 1948*8c0984e5SSebastian Reichel ktime_t remain; 1949*8c0984e5SSebastian Reichel 1950*8c0984e5SSebastian Reichel alarm_cancel(cm_timer); 1951*8c0984e5SSebastian Reichel cm_timer_set = false; 1952*8c0984e5SSebastian Reichel remain = alarm_expires_remaining(cm_timer); 1953*8c0984e5SSebastian Reichel cm_suspend_duration_ms -= ktime_to_ms(remain); 1954*8c0984e5SSebastian Reichel schedule_work(&setup_polling); 1955*8c0984e5SSebastian Reichel } 1956*8c0984e5SSebastian Reichel 1957*8c0984e5SSebastian Reichel _cm_monitor(cm); 1958*8c0984e5SSebastian Reichel 1959*8c0984e5SSebastian Reichel /* Re-enqueue delayed work (fullbatt_vchk_work) */ 1960*8c0984e5SSebastian Reichel if (cm->fullbatt_vchk_jiffies_at) { 1961*8c0984e5SSebastian Reichel unsigned long delay = 0; 1962*8c0984e5SSebastian Reichel unsigned long now = jiffies + CM_JIFFIES_SMALL; 1963*8c0984e5SSebastian Reichel 1964*8c0984e5SSebastian Reichel if (time_after_eq(now, cm->fullbatt_vchk_jiffies_at)) { 1965*8c0984e5SSebastian Reichel delay = (unsigned long)((long)now 1966*8c0984e5SSebastian Reichel - (long)(cm->fullbatt_vchk_jiffies_at)); 1967*8c0984e5SSebastian Reichel delay = jiffies_to_msecs(delay); 1968*8c0984e5SSebastian Reichel } else { 1969*8c0984e5SSebastian Reichel delay = 0; 1970*8c0984e5SSebastian Reichel } 1971*8c0984e5SSebastian Reichel 1972*8c0984e5SSebastian Reichel /* 1973*8c0984e5SSebastian Reichel * Account for cm_suspend_duration_ms with assuming that 1974*8c0984e5SSebastian Reichel * timer stops in suspend. 1975*8c0984e5SSebastian Reichel */ 1976*8c0984e5SSebastian Reichel if (delay > cm_suspend_duration_ms) 1977*8c0984e5SSebastian Reichel delay -= cm_suspend_duration_ms; 1978*8c0984e5SSebastian Reichel else 1979*8c0984e5SSebastian Reichel delay = 0; 1980*8c0984e5SSebastian Reichel 1981*8c0984e5SSebastian Reichel queue_delayed_work(cm_wq, &cm->fullbatt_vchk_work, 1982*8c0984e5SSebastian Reichel msecs_to_jiffies(delay)); 1983*8c0984e5SSebastian Reichel } 1984*8c0984e5SSebastian Reichel device_set_wakeup_capable(cm->dev, false); 1985*8c0984e5SSebastian Reichel } 1986*8c0984e5SSebastian Reichel 1987*8c0984e5SSebastian Reichel static const struct dev_pm_ops charger_manager_pm = { 1988*8c0984e5SSebastian Reichel .prepare = cm_suspend_prepare, 1989*8c0984e5SSebastian Reichel .suspend_noirq = cm_suspend_noirq, 1990*8c0984e5SSebastian Reichel .complete = cm_suspend_complete, 1991*8c0984e5SSebastian Reichel }; 1992*8c0984e5SSebastian Reichel 1993*8c0984e5SSebastian Reichel static struct platform_driver charger_manager_driver = { 1994*8c0984e5SSebastian Reichel .driver = { 1995*8c0984e5SSebastian Reichel .name = "charger-manager", 1996*8c0984e5SSebastian Reichel .pm = &charger_manager_pm, 1997*8c0984e5SSebastian Reichel .of_match_table = charger_manager_match, 1998*8c0984e5SSebastian Reichel }, 1999*8c0984e5SSebastian Reichel .probe = charger_manager_probe, 2000*8c0984e5SSebastian Reichel .remove = charger_manager_remove, 2001*8c0984e5SSebastian Reichel .id_table = charger_manager_id, 2002*8c0984e5SSebastian Reichel }; 2003*8c0984e5SSebastian Reichel 2004*8c0984e5SSebastian Reichel static int __init charger_manager_init(void) 2005*8c0984e5SSebastian Reichel { 2006*8c0984e5SSebastian Reichel cm_wq = create_freezable_workqueue("charger_manager"); 2007*8c0984e5SSebastian Reichel INIT_DELAYED_WORK(&cm_monitor_work, cm_monitor_poller); 2008*8c0984e5SSebastian Reichel 2009*8c0984e5SSebastian Reichel return platform_driver_register(&charger_manager_driver); 2010*8c0984e5SSebastian Reichel } 2011*8c0984e5SSebastian Reichel late_initcall(charger_manager_init); 2012*8c0984e5SSebastian Reichel 2013*8c0984e5SSebastian Reichel static void __exit charger_manager_cleanup(void) 2014*8c0984e5SSebastian Reichel { 2015*8c0984e5SSebastian Reichel destroy_workqueue(cm_wq); 2016*8c0984e5SSebastian Reichel cm_wq = NULL; 2017*8c0984e5SSebastian Reichel 2018*8c0984e5SSebastian Reichel platform_driver_unregister(&charger_manager_driver); 2019*8c0984e5SSebastian Reichel } 2020*8c0984e5SSebastian Reichel module_exit(charger_manager_cleanup); 2021*8c0984e5SSebastian Reichel 2022*8c0984e5SSebastian Reichel /** 2023*8c0984e5SSebastian Reichel * cm_notify_event - charger driver notify Charger Manager of charger event 2024*8c0984e5SSebastian Reichel * @psy: pointer to instance of charger's power_supply 2025*8c0984e5SSebastian Reichel * @type: type of charger event 2026*8c0984e5SSebastian Reichel * @msg: optional message passed to uevent_notify fuction 2027*8c0984e5SSebastian Reichel */ 2028*8c0984e5SSebastian Reichel void cm_notify_event(struct power_supply *psy, enum cm_event_types type, 2029*8c0984e5SSebastian Reichel char *msg) 2030*8c0984e5SSebastian Reichel { 2031*8c0984e5SSebastian Reichel struct charger_manager *cm; 2032*8c0984e5SSebastian Reichel bool found_power_supply = false; 2033*8c0984e5SSebastian Reichel 2034*8c0984e5SSebastian Reichel if (psy == NULL) 2035*8c0984e5SSebastian Reichel return; 2036*8c0984e5SSebastian Reichel 2037*8c0984e5SSebastian Reichel mutex_lock(&cm_list_mtx); 2038*8c0984e5SSebastian Reichel list_for_each_entry(cm, &cm_list, entry) { 2039*8c0984e5SSebastian Reichel if (match_string(cm->desc->psy_charger_stat, -1, 2040*8c0984e5SSebastian Reichel psy->desc->name) >= 0) { 2041*8c0984e5SSebastian Reichel found_power_supply = true; 2042*8c0984e5SSebastian Reichel break; 2043*8c0984e5SSebastian Reichel } 2044*8c0984e5SSebastian Reichel } 2045*8c0984e5SSebastian Reichel mutex_unlock(&cm_list_mtx); 2046*8c0984e5SSebastian Reichel 2047*8c0984e5SSebastian Reichel if (!found_power_supply) 2048*8c0984e5SSebastian Reichel return; 2049*8c0984e5SSebastian Reichel 2050*8c0984e5SSebastian Reichel switch (type) { 2051*8c0984e5SSebastian Reichel case CM_EVENT_BATT_FULL: 2052*8c0984e5SSebastian Reichel fullbatt_handler(cm); 2053*8c0984e5SSebastian Reichel break; 2054*8c0984e5SSebastian Reichel case CM_EVENT_BATT_OUT: 2055*8c0984e5SSebastian Reichel battout_handler(cm); 2056*8c0984e5SSebastian Reichel break; 2057*8c0984e5SSebastian Reichel case CM_EVENT_BATT_IN: 2058*8c0984e5SSebastian Reichel case CM_EVENT_EXT_PWR_IN_OUT ... CM_EVENT_CHG_START_STOP: 2059*8c0984e5SSebastian Reichel misc_event_handler(cm, type); 2060*8c0984e5SSebastian Reichel break; 2061*8c0984e5SSebastian Reichel case CM_EVENT_UNKNOWN: 2062*8c0984e5SSebastian Reichel case CM_EVENT_OTHERS: 2063*8c0984e5SSebastian Reichel uevent_notify(cm, msg ? msg : default_event_names[type]); 2064*8c0984e5SSebastian Reichel break; 2065*8c0984e5SSebastian Reichel default: 2066*8c0984e5SSebastian Reichel dev_err(cm->dev, "%s: type not specified\n", __func__); 2067*8c0984e5SSebastian Reichel break; 2068*8c0984e5SSebastian Reichel } 2069*8c0984e5SSebastian Reichel } 2070*8c0984e5SSebastian Reichel EXPORT_SYMBOL_GPL(cm_notify_event); 2071*8c0984e5SSebastian Reichel 2072*8c0984e5SSebastian Reichel MODULE_AUTHOR("MyungJoo Ham <myungjoo.ham@samsung.com>"); 2073*8c0984e5SSebastian Reichel MODULE_DESCRIPTION("Charger Manager"); 2074*8c0984e5SSebastian Reichel MODULE_LICENSE("GPL"); 2075