static irqreturn_t pm860x_temp_handler(int irq, void *data) { struct power_supply *psy; struct pm860x_charger_info *info = data; union power_supply_propval temp; int value; int ret; psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) return IRQ_HANDLED; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_TEMP, &temp); if (ret) goto out; value = temp.intval / 10; mutex_lock(&info->lock); /* Temperature < -10 C or >40 C, Will not allow charge */ if (value < -10 || value > 40) info->allowed = 0; else info->allowed = 1; dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); mutex_unlock(&info->lock); set_charging_fsm(info); out: power_supply_put(psy); return IRQ_HANDLED; }
static irqreturn_t pm860x_done_handler(int irq, void *data) { struct pm860x_charger_info *info = data; struct power_supply *psy; union power_supply_propval val; int ret; int vbatt; mutex_lock(&info->lock); /* pre-charge done, will transimit to fast-charge stage */ if (info->state == FSM_PRECHARGE) { info->allowed = 1; goto out; } /* * Fast charge done, delay to read * the correct status of CHG_DET. */ mdelay(5); info->allowed = 0; psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) goto out; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &val); if (ret) goto out_psy_put; vbatt = val.intval / 1000; /* * CHG_DONE interrupt is faster than CHG_DET interrupt when * plug in/out usb, So we can not rely on info->online, we * need check pm8607 status register to check usb is online * or not, then we can decide it is real charge done * automatically or it is triggered by usb plug out; */ ret = pm860x_reg_read(info->i2c, PM8607_STATUS_2); if (ret < 0) goto out_psy_put; if (vbatt > CHARGE_THRESHOLD && ret & STATUS2_CHG) power_supply_set_property(psy, POWER_SUPPLY_PROP_CHARGE_FULL, &val); out_psy_put: power_supply_put(psy); out: mutex_unlock(&info->lock); dev_dbg(info->dev, "%s, Allowed: %d\n", __func__, info->allowed); set_charging_fsm(info); return IRQ_HANDLED; }
static ssize_t power_supply_show_property(struct device *dev, struct device_attribute *attr, char *buf) { static char *type_text[] = { "Unknown", "Battery", "UPS", "Mains", "USB", "USB_DCP", "USB_CDP", "USB_ACA" }; static char *status_text[] = { "Unknown", "Charging", "Discharging", "Not charging", "Full" }; static char *charge_type[] = { "Unknown", "N/A", "Trickle", "Fast" }; static char *health_text[] = { "Unknown", "Good", "Overheat", "Dead", "Over voltage", "Unspecified failure", "Cold", "Watchdog timer expire", "Safety timer expire" }; static char *technology_text[] = { "Unknown", "NiMH", "Li-ion", "Li-poly", "LiFe", "NiCd", "LiMn" }; static char *capacity_level_text[] = { "Unknown", "Critical", "Low", "Normal", "High", "Full" }; static char *scope_text[] = { "Unknown", "System", "Device" }; ssize_t ret = 0; struct power_supply *psy = dev_get_drvdata(dev); const ptrdiff_t off = attr - power_supply_attrs; union power_supply_propval value; if (off == POWER_SUPPLY_PROP_TYPE) { value.intval = psy->desc->type; } else { ret = power_supply_get_property(psy, off, &value); if (ret < 0) { if (ret == -ENODATA) dev_dbg(dev, "driver has no data for `%s' property\n", attr->attr.name); else if (ret != -ENODEV) dev_err(dev, "driver failed to report `%s' property: %zd\n", attr->attr.name, ret); return ret; } } if (off == POWER_SUPPLY_PROP_STATUS) return sprintf(buf, "%s\n", status_text[value.intval]); else if (off == POWER_SUPPLY_PROP_CHARGE_TYPE) return sprintf(buf, "%s\n", charge_type[value.intval]); else if (off == POWER_SUPPLY_PROP_HEALTH) return sprintf(buf, "%s\n", health_text[value.intval]); else if (off == POWER_SUPPLY_PROP_TECHNOLOGY) return sprintf(buf, "%s\n", technology_text[value.intval]); else if (off == POWER_SUPPLY_PROP_CAPACITY_LEVEL) return sprintf(buf, "%s\n", capacity_level_text[value.intval]); else if (off == POWER_SUPPLY_PROP_TYPE) return sprintf(buf, "%s\n", type_text[value.intval]); else if (off == POWER_SUPPLY_PROP_SCOPE) return sprintf(buf, "%s\n", scope_text[value.intval]); else if (off >= POWER_SUPPLY_PROP_MODEL_NAME) return sprintf(buf, "%s\n", value.strval); return sprintf(buf, "%d\n", value.intval); }
static int set_charging_fsm(struct pm860x_charger_info *info) { struct power_supply *psy; union power_supply_propval data; unsigned char fsm_state[][16] = { "init", "discharge", "precharge", "fastcharge", }; int ret; int vbatt; psy = power_supply_get_by_name(pm860x_supplied_to[0]); if (!psy) return -EINVAL; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_VOLTAGE_NOW, &data); if (ret) { power_supply_put(psy); return ret; } vbatt = data.intval / 1000; ret = power_supply_get_property(psy, POWER_SUPPLY_PROP_PRESENT, &data); if (ret) { power_supply_put(psy); return ret; } power_supply_put(psy); mutex_lock(&info->lock); info->present = data.intval; dev_dbg(info->dev, "Entering FSM:%s, Charger:%s, Battery:%s, " "Allowed:%d\n", &fsm_state[info->state][0], (info->online) ? "online" : "N/A", (info->present) ? "present" : "N/A", info->allowed); dev_dbg(info->dev, "set_charging_fsm:vbatt:%d(mV)\n", vbatt); switch (info->state) { case FSM_INIT: if (info->online && info->present && info->allowed) { if (vbatt < PRECHARGE_THRESHOLD) { info->state = FSM_PRECHARGE; start_precharge(info); } else if (vbatt > DISCHARGE_THRESHOLD) { info->state = FSM_DISCHARGE; stop_charge(info, vbatt); } else if (vbatt < DISCHARGE_THRESHOLD) { info->state = FSM_FASTCHARGE; start_fastcharge(info); } } else { if (vbatt < POWEROFF_THRESHOLD) { power_off_notification(info); } else { info->state = FSM_DISCHARGE; stop_charge(info, vbatt); } } break; case FSM_PRECHARGE: if (info->online && info->present && info->allowed) { if (vbatt > PRECHARGE_THRESHOLD) { info->state = FSM_FASTCHARGE; start_fastcharge(info); } } else { info->state = FSM_DISCHARGE; stop_charge(info, vbatt); } break; case FSM_FASTCHARGE: if (info->online && info->present && info->allowed) { if (vbatt < PRECHARGE_THRESHOLD) { info->state = FSM_PRECHARGE; start_precharge(info); } } else { info->state = FSM_DISCHARGE; stop_charge(info, vbatt); } break; case FSM_DISCHARGE: if (info->online && info->present && info->allowed) { if (vbatt < PRECHARGE_THRESHOLD) { info->state = FSM_PRECHARGE; start_precharge(info); } else if (vbatt < DISCHARGE_THRESHOLD) { info->state = FSM_FASTCHARGE; start_fastcharge(info); } } else { if (vbatt < POWEROFF_THRESHOLD) power_off_notification(info); else if (vbatt > CHARGE_THRESHOLD && info->online) set_vbatt_threshold(info, CHARGE_THRESHOLD, 0); } break; default: dev_warn(info->dev, "FSM meets wrong state:%d\n", info->state); break; } dev_dbg(info->dev, "Out FSM:%s, Charger:%s, Battery:%s, Allowed:%d\n", &fsm_state[info->state][0], (info->online) ? "online" : "N/A", (info->present) ? "present" : "N/A", info->allowed); mutex_unlock(&info->lock); return 0; }