static u8 lp8788_select_buck_vout_addr(struct lp8788_buck *buck, enum lp8788_buck_id id) { enum lp8788_dvs_mode mode = lp8788_get_buck_dvs_ctrl_mode(buck, id); struct lp8788_buck1_dvs *b1_dvs; struct lp8788_buck2_dvs *b2_dvs; u8 val, idx, addr; int pin1, pin2; switch (id) { case BUCK1: if (mode == EXTPIN) { b1_dvs = (struct lp8788_buck1_dvs *)buck->dvs; if (!b1_dvs) goto err; idx = gpio_get_value(b1_dvs->gpio) ? 1 : 0; } else { lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); idx = (val & LP8788_BUCK1_DVS_M) >> LP8788_BUCK1_DVS_S; } addr = LP8788_BUCK1_VOUT0 + idx; break; case BUCK2: if (mode == EXTPIN) { b2_dvs = (struct lp8788_buck2_dvs *)buck->dvs; if (!b2_dvs) goto err; pin1 = gpio_get_value(b2_dvs->gpio[0]); pin2 = gpio_get_value(b2_dvs->gpio[1]); if (pin1 == PIN_LOW && pin2 == PIN_LOW) idx = 0; else if (pin1 == PIN_LOW && pin2 == PIN_HIGH) idx = 2; else if (pin1 == PIN_HIGH && pin2 == PIN_LOW) idx = 1; else idx = 3; } else { lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); idx = (val & LP8788_BUCK2_DVS_M) >> LP8788_BUCK2_DVS_S; } addr = LP8788_BUCK2_VOUT0 + idx; break; default: goto err; } return addr; err: return INVALID_ADDR; }
static int lp8788_get_battery_status(struct lp8788_charger *pchg, union power_supply_propval *val) { enum lp8788_charging_state state; u8 data; int ret; ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); if (ret) return ret; state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; switch (state) { case LP8788_OFF: val->intval = POWER_SUPPLY_STATUS_DISCHARGING; break; case LP8788_PRECHARGE: case LP8788_CC: case LP8788_CV: case LP8788_HIGH_CURRENT: val->intval = POWER_SUPPLY_STATUS_CHARGING; break; case LP8788_MAINTENANCE: val->intval = POWER_SUPPLY_STATUS_FULL; break; default: val->intval = POWER_SUPPLY_STATUS_NOT_CHARGING; break; } return 0; }
static bool lp8788_is_charger_detected(struct lp8788_charger *pchg) { u8 data; lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); data &= LP8788_CHG_INPUT_STATE_M; return data == LP8788_SYSTEM_SUPPLY || data == LP8788_FULL_FUNCTION; }
static int lp8788_get_charging_termination_voltage(struct lp8788_charger *pchg, union power_supply_propval *val) { u8 read; lp8788_read_byte(pchg->lp, LP8788_CHG_VTERM, &read); read &= LP8788_CHG_VTERM_M; val->intval = LP8788_VTERM_MIN + LP8788_VTERM_STEP * read; return 0; }
static int lp8788_get_battery_charging_current(struct lp8788_charger *pchg, union power_supply_propval *val) { u8 read; lp8788_read_byte(pchg->lp, LP8788_CHG_IBATT, &read); read &= LP8788_CHG_IBATT_M; val->intval = LP8788_ISEL_STEP * (min_t(int, read, LP8788_ISEL_MAX) + 1); return 0; }
static int lp8788_buck_enable_time(struct regulator_dev *rdev) { struct lp8788_buck *buck = rdev_get_drvdata(rdev); enum lp8788_buck_id id = rdev_get_id(rdev); u8 val, addr = LP8788_BUCK1_TIMESTEP + id; if (lp8788_read_byte(buck->lp, addr, &val)) return -EINVAL; val = (val & LP8788_STARTUP_TIME_M) >> LP8788_STARTUP_TIME_S; return ENABLE_TIME_USEC * val; }
static int lp8788_get_battery_present(struct lp8788_charger *pchg, union power_supply_propval *val) { u8 data; int ret; ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); if (ret) return ret; val->intval = !(data & LP8788_NO_BATT_M); return 0; }
static unsigned int lp8788_buck_get_mode(struct regulator_dev *rdev) { struct lp8788_buck *buck = rdev_get_drvdata(rdev); enum lp8788_buck_id id = rdev_get_id(rdev); u8 val; int ret; ret = lp8788_read_byte(buck->lp, LP8788_BUCK_PWM, &val); if (ret) return ret; return val & BUCK_FPWM_MASK(id) ? REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL; }
static ssize_t lp8788_show_eoc_time(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); char *stime[] = { "400ms", "5min", "10min", "15min", "20min", "25min", "30min" "No timeout" }; u8 val; lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); val = (val & LP8788_CHG_EOC_TIME_M) >> LP8788_CHG_EOC_TIME_S; return scnprintf(buf, PAGE_SIZE, "End Of Charge Time: %s\n", stime[val]); }
static int lp8788_buck12_get_voltage_sel(struct regulator_dev *rdev) { struct lp8788_buck *buck = rdev_get_drvdata(rdev); enum lp8788_buck_id id = rdev_get_id(rdev); int ret; u8 val, addr; addr = lp8788_select_buck_vout_addr(buck, id); if (!lp8788_is_valid_buck_addr(addr)) return -EINVAL; ret = lp8788_read_byte(buck->lp, addr, &val); if (ret) return ret; return val & LP8788_VOUT_M; }
static ssize_t lp8788_show_eoc_level(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); char *abs_level[] = { "25mA", "49mA", "75mA", "98mA" }; char *relative_level[] = { "5%", "10%", "15%", "20%" }; char *level; u8 val; u8 mode; lp8788_read_byte(pchg->lp, LP8788_CHG_EOC, &val); mode = val & LP8788_CHG_EOC_MODE_M; val = (val & LP8788_CHG_EOC_LEVEL_M) >> LP8788_CHG_EOC_LEVEL_S; level = mode ? abs_level[val] : relative_level[val]; return scnprintf(buf, PAGE_SIZE, "End Of Charge Level: %s\n", level); }
static int lp8788_get_battery_health(struct lp8788_charger *pchg, union power_supply_propval *val) { u8 data; int ret; ret = lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); if (ret) return ret; if (data & LP8788_NO_BATT_M) val->intval = POWER_SUPPLY_HEALTH_UNSPEC_FAILURE; else if (data & LP8788_BAD_BATT_M) val->intval = POWER_SUPPLY_HEALTH_DEAD; else val->intval = POWER_SUPPLY_HEALTH_GOOD; return 0; }
static enum lp8788_dvs_mode lp8788_get_buck_dvs_ctrl_mode(struct lp8788_buck *buck, enum lp8788_buck_id id) { u8 val, mask; switch (id) { case BUCK1: mask = LP8788_BUCK1_DVS_SEL_M; break; case BUCK2: mask = LP8788_BUCK2_DVS_SEL_M; break; default: return REGISTER; } lp8788_read_byte(buck->lp, LP8788_BUCK_DVS_SEL, &val); return val & mask ? REGISTER : EXTPIN; }
static int lp8788_charger_get_property(struct power_supply *psy, enum power_supply_property psp, union power_supply_propval *val) { struct lp8788_charger *pchg = dev_get_drvdata(psy->dev->parent); u8 read; switch (psp) { case POWER_SUPPLY_PROP_ONLINE: val->intval = lp8788_is_charger_detected(pchg); break; case POWER_SUPPLY_PROP_CURRENT_MAX: lp8788_read_byte(pchg->lp, LP8788_CHG_IDCIN, &read); val->intval = LP8788_ISEL_STEP * (min_t(int, read, LP8788_ISEL_MAX) + 1); break; default: return -EINVAL; } return 0; }
static int lp8788_get_battery_capacity(struct lp8788_charger *pchg, union power_supply_propval *val) { struct lp8788 *lp = pchg->lp; struct lp8788_charger_platform_data *pdata = pchg->pdata; unsigned int max_vbatt; int vbatt; enum lp8788_charging_state state; u8 data; int ret; if (!pdata) return -EINVAL; max_vbatt = pdata->max_vbatt_mv; if (max_vbatt == 0) return -EINVAL; ret = lp8788_read_byte(lp, LP8788_CHG_STATUS, &data); if (ret) return ret; state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; if (state == LP8788_MAINTENANCE) { val->intval = LP8788_MAX_BATT_CAPACITY; } else { ret = lp8788_get_vbatt_adc(pchg, &vbatt); if (ret) return ret; val->intval = (vbatt * LP8788_MAX_BATT_CAPACITY) / max_vbatt; val->intval = min(val->intval, LP8788_MAX_BATT_CAPACITY); } return 0; }
static ssize_t lp8788_show_charger_status(struct device *dev, struct device_attribute *attr, char *buf) { struct lp8788_charger *pchg = dev_get_drvdata(dev); enum lp8788_charging_state state; char *desc[LP8788_MAX_CHG_STATE] = { [LP8788_OFF] = "CHARGER OFF", [LP8788_WARM_UP] = "WARM UP", [LP8788_LOW_INPUT] = "LOW INPUT STATE", [LP8788_PRECHARGE] = "CHARGING - PRECHARGE", [LP8788_CC] = "CHARGING - CC", [LP8788_CV] = "CHARGING - CV", [LP8788_MAINTENANCE] = "NO CHARGING - MAINTENANCE", [LP8788_BATTERY_FAULT] = "BATTERY FAULT", [LP8788_SYSTEM_SUPPORT] = "SYSTEM SUPPORT", [LP8788_HIGH_CURRENT] = "HIGH CURRENT", }; u8 data; lp8788_read_byte(pchg->lp, LP8788_CHG_STATUS, &data); state = (data & LP8788_CHG_STATE_M) >> LP8788_CHG_STATE_S; return scnprintf(buf, PAGE_SIZE, "%s\n", desc[state]); }