diff options
-rw-r--r-- | common/peripheral_charger.c | 167 | ||||
-rw-r--r-- | driver/nfc/ctn730.c | 78 | ||||
-rw-r--r-- | include/peripheral_charger.h | 11 |
3 files changed, 143 insertions, 113 deletions
diff --git a/common/peripheral_charger.c b/common/peripheral_charger.c index 3c11c68be1..d176b75603 100644 --- a/common/peripheral_charger.c +++ b/common/peripheral_charger.c @@ -58,6 +58,7 @@ static const char *_text_event(enum pchg_event event) [PCHG_EVENT_CHARGE_UPDATE] = "CHARGE_UPDATE", [PCHG_EVENT_CHARGE_ENDED] = "CHARGE_ENDED", [PCHG_EVENT_CHARGE_STOPPED] = "CHARGE_STOPPED", + [PCHG_EVENT_IN_NORMAL] = "IN_NORMAL", [PCHG_EVENT_CHARGE_ERROR] = "CHARGE_ERROR", [PCHG_EVENT_INITIALIZE] = "INITIALIZE", [PCHG_EVENT_ENABLE] = "ENABLE", @@ -71,62 +72,57 @@ static const char *_text_event(enum pchg_event event) return event_names[event]; } -static enum pchg_state pchg_reset(struct pchg *ctx) +static void _clear_port(struct pchg *ctx) { mutex_lock(&ctx->mtx); queue_init(&ctx->events); mutex_unlock(&ctx->mtx); atomic_clear(&ctx->irq); - - /* When fw update is implemented, this will be the branch point. */ - pchg_queue_event(ctx, PCHG_EVENT_INITIALIZE); - return PCHG_STATE_RESET; + ctx->battery_percent = 0; + ctx->error = 0; } -static enum pchg_state pchg_initialize(struct pchg *ctx, enum pchg_state state) +static enum pchg_state pchg_reset(struct pchg *ctx) { - int rv = ctx->cfg->drv->init(ctx); + enum pchg_state state = PCHG_STATE_RESET; + int rv; - if (rv == EC_SUCCESS) { - pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - state = PCHG_STATE_INITIALIZED; - } else if (rv == EC_SUCCESS_IN_PROGRESS) { - state = PCHG_STATE_RESET; - } else { - CPRINTS("ERR: Failed to initialize"); - } + /* + * In case we get asynchronous reset, clear port though it's redundant + * for a synchronous reset. + */ + _clear_port(ctx); - ctx->battery_percent = 0; - ctx->error = 0; + if (ctx->mode == PCHG_MODE_NORMAL) { + rv = ctx->cfg->drv->init(ctx); + if (rv == EC_SUCCESS) { + state = PCHG_STATE_INITIALIZED; + pchg_queue_event(ctx, PCHG_EVENT_ENABLE); + } else if (rv != EC_SUCCESS_IN_PROGRESS) { + CPRINTS("ERR: Failed to reset to normal mode"); + } + } return state; } -static enum pchg_state pchg_state_reset(struct pchg *ctx) +static void pchg_state_reset(struct pchg *ctx) { - enum pchg_state state = PCHG_STATE_RESET; - switch (ctx->event) { case PCHG_EVENT_RESET: - state = pchg_reset(ctx); - break; - case PCHG_EVENT_INITIALIZE: - state = pchg_initialize(ctx, state); + ctx->state = pchg_reset(ctx); break; - case PCHG_EVENT_INITIALIZED: + case PCHG_EVENT_IN_NORMAL: + ctx->state = PCHG_STATE_INITIALIZED; pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - state = PCHG_STATE_INITIALIZED; break; default: break; } - - return state; } -static enum pchg_state pchg_state_initialized(struct pchg *ctx) +static void pchg_state_initialized(struct pchg *ctx) { - enum pchg_state state = PCHG_STATE_INITIALIZED; int rv; if (ctx->event == PCHG_EVENT_ENABLE) @@ -134,54 +130,45 @@ static enum pchg_state pchg_state_initialized(struct pchg *ctx) /* Spin in INITIALIZED until error condition is cleared. */ if (ctx->error) - return state; + return; switch (ctx->event) { case PCHG_EVENT_RESET: - state = pchg_reset(ctx); - break; - case PCHG_EVENT_INITIALIZE: - state = pchg_initialize(ctx, state); + ctx->state = pchg_reset(ctx); break; case PCHG_EVENT_ENABLE: rv = ctx->cfg->drv->enable(ctx, true); if (rv == EC_SUCCESS) - state = PCHG_STATE_ENABLED; + ctx->state = PCHG_STATE_ENABLED; else if (rv != EC_SUCCESS_IN_PROGRESS) CPRINTS("ERR: Failed to enable"); break; case PCHG_EVENT_ENABLED: - state = PCHG_STATE_ENABLED; + ctx->state = PCHG_STATE_ENABLED; break; default: break; } - - return state; } -static enum pchg_state pchg_state_enabled(struct pchg *ctx) +static void pchg_state_enabled(struct pchg *ctx) { - enum pchg_state state = PCHG_STATE_ENABLED; int rv; switch (ctx->event) { case PCHG_EVENT_RESET: - state = pchg_reset(ctx); - break; - case PCHG_EVENT_INITIALIZE: - state = pchg_initialize(ctx, state); + ctx->state = pchg_reset(ctx); break; case PCHG_EVENT_DISABLE: ctx->error |= PCHG_ERROR_HOST; rv = ctx->cfg->drv->enable(ctx, false); if (rv == EC_SUCCESS) - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; else if (rv != EC_SUCCESS_IN_PROGRESS) CPRINTS("ERR: Failed to disable"); break; case PCHG_EVENT_DISABLED: - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; break; case PCHG_EVENT_DEVICE_DETECTED: /* @@ -189,100 +176,86 @@ static enum pchg_state pchg_state_enabled(struct pchg *ctx) * because device is already charged. */ ctx->cfg->drv->get_soc(ctx); - state = PCHG_STATE_DETECTED; + ctx->state = PCHG_STATE_DETECTED; break; case PCHG_EVENT_CHARGE_STARTED: - state = PCHG_STATE_CHARGING; + ctx->state = PCHG_STATE_CHARGING; break; default: break; } - - return state; } -static enum pchg_state pchg_state_detected(struct pchg *ctx) +static void pchg_state_detected(struct pchg *ctx) { - enum pchg_state state = PCHG_STATE_DETECTED; int rv; switch (ctx->event) { case PCHG_EVENT_RESET: - state = pchg_reset(ctx); - break; - case PCHG_EVENT_INITIALIZE: - state = pchg_initialize(ctx, state); + ctx->state = pchg_reset(ctx); break; case PCHG_EVENT_DISABLE: ctx->error |= PCHG_ERROR_HOST; rv = ctx->cfg->drv->enable(ctx, false); if (rv == EC_SUCCESS) - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; else if (rv != EC_SUCCESS_IN_PROGRESS) CPRINTS("ERR: Failed to disable"); break; case PCHG_EVENT_DISABLED: - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; break; case PCHG_EVENT_CHARGE_STARTED: - state = PCHG_STATE_CHARGING; + ctx->state = PCHG_STATE_CHARGING; break; case PCHG_EVENT_DEVICE_LOST: ctx->battery_percent = 0; - state = PCHG_STATE_ENABLED; + ctx->state = PCHG_STATE_ENABLED; break; case PCHG_EVENT_CHARGE_ERROR: - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; break; default: break; } - - return state; } -static enum pchg_state pchg_state_charging(struct pchg *ctx) +static void pchg_state_charging(struct pchg *ctx) { - enum pchg_state state = PCHG_STATE_CHARGING; int rv; switch (ctx->event) { case PCHG_EVENT_RESET: - pchg_reset(ctx); - break; - case PCHG_EVENT_INITIALIZE: - state = pchg_initialize(ctx, state); + ctx->state = pchg_reset(ctx); break; case PCHG_EVENT_DISABLE: ctx->error |= PCHG_ERROR_HOST; rv = ctx->cfg->drv->enable(ctx, false); if (rv == EC_SUCCESS) - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; else if (rv != EC_SUCCESS_IN_PROGRESS) CPRINTS("ERR: Failed to disable"); break; case PCHG_EVENT_DISABLED: - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; break; case PCHG_EVENT_CHARGE_UPDATE: CPRINTS("Battery %d%%", ctx->battery_percent); break; case PCHG_EVENT_DEVICE_LOST: ctx->battery_percent = 0; - state = PCHG_STATE_ENABLED; + ctx->state = PCHG_STATE_ENABLED; break; case PCHG_EVENT_CHARGE_ERROR: - state = PCHG_STATE_INITIALIZED; + ctx->state = PCHG_STATE_INITIALIZED; break; case PCHG_EVENT_CHARGE_ENDED: case PCHG_EVENT_CHARGE_STOPPED: - state = PCHG_STATE_DETECTED; + ctx->state = PCHG_STATE_DETECTED; break; default: break; } - - return state; } static int pchg_run(struct pchg *ctx) @@ -311,21 +284,24 @@ static int pchg_run(struct pchg *ctx) CPRINTS("IRQ:EVENT_%s", _text_event(ctx->event)); } + if (ctx->event == PCHG_EVENT_NONE) + return 0; + switch (ctx->state) { case PCHG_STATE_RESET: - ctx->state = pchg_state_reset(ctx); + pchg_state_reset(ctx); break; case PCHG_STATE_INITIALIZED: - ctx->state = pchg_state_initialized(ctx); + pchg_state_initialized(ctx); break; case PCHG_STATE_ENABLED: - ctx->state = pchg_state_enabled(ctx); + pchg_state_enabled(ctx); break; case PCHG_STATE_DETECTED: - ctx->state = pchg_state_detected(ctx); + pchg_state_detected(ctx); break; case PCHG_STATE_CHARGING: - ctx->state = pchg_state_charging(ctx); + pchg_state_charging(ctx); break; default: CPRINTS("ERR: Unknown state (%d)", ctx->state); @@ -374,6 +350,8 @@ static void pchg_startup(void) for (p = 0; p < pchg_count; p++) { ctx = &pchgs[p]; + _clear_port(ctx); + ctx->mode = PCHG_MODE_NORMAL; ctx->cfg->drv->reset(ctx); gpio_enable_interrupt(ctx->cfg->irq_pin); } @@ -392,9 +370,6 @@ static void pchg_shutdown(void) for (p = 0; p < pchg_count; p++) { ctx = &pchgs[0]; gpio_disable_interrupt(ctx->cfg->irq_pin); - mutex_lock(&ctx->mtx); - queue_init(&ctx->events); - mutex_unlock(&ctx->mtx); } } DECLARE_HOOK(HOOK_CHIPSET_SHUTDOWN, pchg_shutdown, HOOK_PRIO_DEFAULT); @@ -490,26 +465,30 @@ static int cc_pchg(int argc, char **argv) return EC_SUCCESS; } - if (!strcasecmp(argv[2], "reset")) - pchg_queue_event(ctx, PCHG_EVENT_RESET); - else if (!strcasecmp(argv[2], "init")) - pchg_queue_event(ctx, PCHG_EVENT_INITIALIZE); - else if (!strcasecmp(argv[2], "enable")) + if (!strcasecmp(argv[2], "reset")) { + if (argc == 3) + ctx->mode = PCHG_MODE_NORMAL; + else + return EC_ERROR_PARAM3; + gpio_disable_interrupt(ctx->cfg->irq_pin); + _clear_port(ctx); + ctx->cfg->drv->reset(ctx); + gpio_enable_interrupt(ctx->cfg->irq_pin); + } else if (!strcasecmp(argv[2], "enable")) { pchg_queue_event(ctx, PCHG_EVENT_ENABLE); - else if (!strcasecmp(argv[2], "disable")) + } else if (!strcasecmp(argv[2], "disable")) { pchg_queue_event(ctx, PCHG_EVENT_DISABLE); - else + } else { return EC_ERROR_PARAM2; + } task_wake(TASK_ID_PCHG); return EC_SUCCESS; } DECLARE_CONSOLE_COMMAND(pchg, cc_pchg, - "<port> [init/enable/disable]" "\n\t<port>" "\n\t<port> reset" - "\n\t<port> init" "\n\t<port> enable" "\n\t<port> disable", "Control peripheral chargers"); diff --git a/driver/nfc/ctn730.c b/driver/nfc/ctn730.c index 75487fa701..6efb75efb4 100644 --- a/driver/nfc/ctn730.c +++ b/driver/nfc/ctn730.c @@ -56,12 +56,17 @@ static const int _detection_interval_ms = 100; #define WLC_CHG_CTRL_CHARGING_INFO 0b010101 /* WLC_HOST_CTRL_RESET constants */ -#define WLC_HOST_CTRL_RESET_CMD_SIZE 1 -#define WLC_HOST_CTRL_RESET_RSP_SIZE 1 -#define WLC_HOST_CTRL_RESET_EVT_NORMAL_MODE 0x00 -#define WLC_HOST_CTRL_RESET_EVT_DOWNLOAD_MODE 0x01 -#define WLC_HOST_CTRL_RESET_CMD_MODE_NORMAL 0x00 -#define WLC_HOST_CTRL_RESET_CMD_MODE_DOWNLOAD 0x01 +#define WLC_HOST_CTRL_RESET_CMD_SIZE 1 +#define WLC_HOST_CTRL_RESET_RSP_SIZE 1 +#define WLC_HOST_CTRL_RESET_EVT_NORMAL_MODE 0x00 +#define WLC_HOST_CTRL_RESET_EVT_NORMAL_MODE_SIZE 3 +#define WLC_HOST_CTRL_RESET_EVT_DOWNLOAD_MODE 0x01 +#define WLC_HOST_CTRL_RESET_EVT_DOWNLOAD_MODE_SIZE 2 +#define WLC_HOST_CTRL_RESET_REASON_INTENDED 0x00 +#define WLC_HOST_CTRL_RESET_REASON_CORRUPTED 0x01 +#define WLC_HOST_CTRL_RESET_REASON_UNRECOVERABLE 0x02 +#define WLC_HOST_CTRL_RESET_CMD_MODE_NORMAL 0x00 +#define WLC_HOST_CTRL_RESET_CMD_MODE_DOWNLOAD 0x01 /* WLC_CHG_CTRL_ENABLE constants */ #define WLC_CHG_CTRL_ENABLE_CMD_SIZE 2 @@ -207,6 +212,20 @@ static const char *_text_status_code(uint8_t code) } } +static const char *_text_reset_reason(uint8_t code) +{ + switch (code) { + case WLC_HOST_CTRL_RESET_REASON_INTENDED: + return "intended"; + case WLC_HOST_CTRL_RESET_REASON_CORRUPTED: + return "corrupted"; + case WLC_HOST_CTRL_RESET_REASON_UNRECOVERABLE: + return "unrecoverable"; + default: + return "unknown"; + } +} + static int _i2c_read(int i2c_port, uint8_t *in, int in_len) { int rv; @@ -269,7 +288,9 @@ static int ctn730_init(struct pchg *ctx) cmd->message_type = CTN730_MESSAGE_TYPE_COMMAND; cmd->instruction = WLC_HOST_CTRL_RESET; cmd->length = WLC_HOST_CTRL_RESET_CMD_SIZE; - cmd->payload[0] = WLC_HOST_CTRL_RESET_CMD_MODE_NORMAL; + cmd->payload[0] = ctx->mode == PCHG_MODE_NORMAL + ? WLC_HOST_CTRL_RESET_CMD_MODE_NORMAL + : WLC_HOST_CTRL_RESET_CMD_MODE_DOWNLOAD; /* TODO: Run 1 sec timeout timer. */ rv = _send_command(ctx, cmd); @@ -327,26 +348,36 @@ static int _process_payload_response(struct pchg *ctx, struct ctn730_msg *res) switch (res->instruction) { case WLC_HOST_CTRL_RESET: - if (len != WLC_HOST_CTRL_RESET_RSP_SIZE - || buf[0] != WLC_HOST_STATUS_OK) + if (len != WLC_HOST_CTRL_RESET_RSP_SIZE) return EC_ERROR_UNKNOWN; + if (buf[0] != WLC_HOST_STATUS_OK) + ctx->event = PCHG_EVENT_OTHER_ERROR; break; case WLC_CHG_CTRL_ENABLE: - if (len != WLC_CHG_CTRL_ENABLE_RSP_SIZE - || buf[0] != WLC_HOST_STATUS_OK) + if (len != WLC_CHG_CTRL_ENABLE_RSP_SIZE) return EC_ERROR_UNKNOWN; - ctx->event = PCHG_EVENT_ENABLED; + if (buf[0] != WLC_HOST_STATUS_OK) + ctx->event = PCHG_EVENT_OTHER_ERROR; + else + ctx->event = PCHG_EVENT_ENABLED; break; case WLC_CHG_CTRL_DISABLE: - if (len != WLC_CHG_CTRL_DISABLE_RSP_SIZE - || buf[0] != WLC_HOST_STATUS_OK) + if (len != WLC_CHG_CTRL_DISABLE_RSP_SIZE) return EC_ERROR_UNKNOWN; + if (buf[0] != WLC_HOST_STATUS_OK) + ctx->event = PCHG_EVENT_OTHER_ERROR; + else + ctx->event = PCHG_EVENT_DISABLED; break; case WLC_CHG_CTRL_CHARGING_INFO: - if (len != WLC_CHG_CTRL_CHARGING_INFO_RSP_SIZE - || buf[0] != WLC_HOST_STATUS_OK) + if (len != WLC_CHG_CTRL_CHARGING_INFO_RSP_SIZE) return EC_ERROR_UNKNOWN; - ctx->battery_percent = buf[1]; + if (buf[0] != WLC_HOST_STATUS_OK) { + ctx->event = PCHG_EVENT_OTHER_ERROR; + } else { + ctx->battery_percent = buf[1]; + ctx->event = PCHG_EVENT_CHARGE_UPDATE; + } break; default: CPRINTS("Received unknown response (%d)", res->instruction); @@ -374,16 +405,25 @@ static int _process_payload_event(struct pchg *ctx, struct ctn730_msg *res) if (IS_ENABLED(CTN730_DEBUG)) CPRINTS("Payload: %ph", HEX_BUF(buf, len)); + ctx->event = PCHG_EVENT_NONE; + switch (res->instruction) { case WLC_HOST_CTRL_RESET: if (buf[0] == WLC_HOST_CTRL_RESET_EVT_NORMAL_MODE) { - ctx->event = PCHG_EVENT_INITIALIZED; + if (len != WLC_HOST_CTRL_RESET_EVT_NORMAL_MODE_SIZE) + return EC_ERROR_INVAL; + ctx->event = PCHG_EVENT_IN_NORMAL; + CPRINTS("Normal Mode (FW=0x%02x.%02x)", buf[1], buf[2]); /* * ctn730 isn't immediately ready for i2c write after * normal mode initialization (b:178096436). */ msleep(5); } else if (buf[0] == WLC_HOST_CTRL_RESET_EVT_DOWNLOAD_MODE) { + if (len != WLC_HOST_CTRL_RESET_EVT_DOWNLOAD_MODE_SIZE) + return EC_ERROR_INVAL; + CPRINTS("Download Mode (%s)", + _text_reset_reason(buf[1])); ctx->event = PCHG_EVENT_RESET; } else { return EC_ERROR_INVAL; @@ -481,7 +521,7 @@ static int ctn730_get_soc(struct pchg *ctx) if (rv) return rv; - return EC_SUCCESS; + return EC_SUCCESS_IN_PROGRESS; } /** diff --git a/include/peripheral_charger.h b/include/peripheral_charger.h index a53fbb9104..2e535d4f3e 100644 --- a/include/peripheral_charger.h +++ b/include/peripheral_charger.h @@ -83,7 +83,11 @@ enum pchg_event { PCHG_EVENT_CHARGE_UPDATE, PCHG_EVENT_CHARGE_ENDED, PCHG_EVENT_CHARGE_STOPPED, + PCHG_EVENT_IN_NORMAL, + + /* Errors */ PCHG_EVENT_CHARGE_ERROR, + PCHG_EVENT_OTHER_ERROR, /* Internal (a.k.a. Host) Events */ PCHG_EVENT_INITIALIZE, @@ -103,6 +107,11 @@ enum pchg_error { PCHG_ERROR_FOREIGN_OBJECT = BIT(3), }; +enum pchg_mode { + PCHG_MODE_NORMAL = 0, + PCHG_MODE_DOWNLOAD, +}; + /** * Data struct describing the configuration of a peripheral charging port. */ @@ -140,6 +149,8 @@ struct pchg { uint8_t battery_percent; /* Number of dropped events (due to queue overflow) */ uint32_t dropped_event_count; + /* enum pchg_mode */ + uint8_t mode; }; /** |