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 void ac_status_changed(void) { struct power_supply *psy = power_supply_get_by_name("olpc-ac"); if (psy) { power_supply_changed(psy); power_supply_put(psy); } }
static void raumfeld_power_signal_charged(void) { struct power_supply *psy = power_supply_get_by_name(raumfeld_power_supplicants[0]); if (psy) { power_supply_set_battery_charged(psy); power_supply_put(psy); } }
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 void olpc_xo175_ec_complete(void *arg) { struct olpc_xo175_ec *priv = arg; struct device *dev = &priv->spi->dev; struct power_supply *psy; unsigned long flags; u8 channel; u8 byte; int ret; ret = priv->msg.status; if (ret) { dev_err(dev, "SPI transfer failed: %d\n", ret); spin_lock_irqsave(&priv->cmd_state_lock, flags); if (priv->cmd_running) { priv->resp_len = 0; priv->cmd_state = CMD_STATE_ERROR_RECEIVED; complete(&priv->cmd_done); } spin_unlock_irqrestore(&priv->cmd_state_lock, flags); if (ret != -EINTR) olpc_xo175_ec_read_packet(priv); return; } channel = priv->rx_buf.resp.channel; byte = priv->rx_buf.resp.byte; switch (channel) { case CHAN_NONE: spin_lock_irqsave(&priv->cmd_state_lock, flags); if (!priv->cmd_running) { /* We can safely ignore these */ dev_err(dev, "spurious FIFO read packet\n"); spin_unlock_irqrestore(&priv->cmd_state_lock, flags); return; } priv->cmd_state = CMD_STATE_CMD_SENT; if (!priv->expected_resp_len) complete(&priv->cmd_done); olpc_xo175_ec_read_packet(priv); spin_unlock_irqrestore(&priv->cmd_state_lock, flags); return; case CHAN_SWITCH: spin_lock_irqsave(&priv->cmd_state_lock, flags); if (!priv->cmd_running) { /* Just go with the flow */ dev_err(dev, "spurious SWITCH packet\n"); memset(&priv->cmd, 0, sizeof(priv->cmd)); priv->cmd.command = CMD_ECHO; } priv->cmd_state = CMD_STATE_CMD_IN_TX_FIFO; /* Throw command into TxFIFO */ gpiod_set_value_cansleep(priv->gpio_cmd, 0); olpc_xo175_ec_send_command(priv, &priv->cmd, sizeof(priv->cmd)); spin_unlock_irqrestore(&priv->cmd_state_lock, flags); return; case CHAN_CMD_RESP: spin_lock_irqsave(&priv->cmd_state_lock, flags); if (!priv->cmd_running) { dev_err(dev, "spurious response packet\n"); } else if (priv->resp_len >= priv->expected_resp_len) { dev_err(dev, "too many response packets\n"); } else { priv->resp_data[priv->resp_len++] = byte; if (priv->resp_len == priv->expected_resp_len) { priv->cmd_state = CMD_STATE_RESP_RECEIVED; complete(&priv->cmd_done); } } spin_unlock_irqrestore(&priv->cmd_state_lock, flags); break; case CHAN_CMD_ERROR: spin_lock_irqsave(&priv->cmd_state_lock, flags); if (!priv->cmd_running) { dev_err(dev, "spurious cmd error packet\n"); } else { priv->resp_data[0] = byte; priv->resp_len = 1; priv->cmd_state = CMD_STATE_ERROR_RECEIVED; complete(&priv->cmd_done); } spin_unlock_irqrestore(&priv->cmd_state_lock, flags); break; case CHAN_KEYBOARD: dev_warn(dev, "keyboard is not supported\n"); break; case CHAN_TOUCHPAD: dev_warn(dev, "touchpad is not supported\n"); break; case CHAN_EVENT: dev_dbg(dev, "got event %.2x\n", byte); switch (byte) { case EVENT_AC_CHANGE: psy = power_supply_get_by_name("olpc-ac"); if (psy) { power_supply_changed(psy); power_supply_put(psy); } break; case EVENT_BATTERY_STATUS: case EVENT_BATTERY_CRITICAL: case EVENT_BATTERY_SOC_CHANGE: case EVENT_BATTERY_ERROR: psy = power_supply_get_by_name("olpc-battery"); if (psy) { power_supply_changed(psy); power_supply_put(psy); } break; case EVENT_POWER_PRESSED: input_report_key(priv->pwrbtn, KEY_POWER, 1); input_sync(priv->pwrbtn); input_report_key(priv->pwrbtn, KEY_POWER, 0); input_sync(priv->pwrbtn); /* fall through */ case EVENT_POWER_PRESS_WAKE: case EVENT_TIMED_HOST_WAKE: pm_wakeup_event(priv->pwrbtn->dev.parent, PM_WAKEUP_TIME); break; default: dev_dbg(dev, "ignored unknown event %.2x\n", byte); break; } break; case CHAN_DEBUG: if (byte == '\n') { olpc_xo175_ec_flush_logbuf(priv); } else if (isprint(byte)) { priv->logbuf[priv->logbuf_len++] = byte; if (priv->logbuf_len == LOG_BUF_SIZE) olpc_xo175_ec_flush_logbuf(priv); } break; default: dev_warn(dev, "unknown channel: %d, %.2x\n", channel, byte); break; } /* Most non-command packets get the TxFIFO refilled and an ACK. */ olpc_xo175_ec_read_packet(priv); }
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; }