summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaisuke Nojiri <dnojiri@chromium.org>2021-03-11 13:17:35 -0800
committerCommit Bot <commit-bot@chromium.org>2021-03-25 21:27:54 +0000
commit7f7d570862ffe9570266954f5551a6bf8aa1c992 (patch)
tree44163c4f618d2a27d8491b86e9cbeaa0484aae0b
parentb0833e615e36a8f25647ddb85a4c0cf2ab92bcc0 (diff)
downloadchrome-ec-7f7d570862ffe9570266954f5551a6bf8aa1c992.tar.gz
PCHG: Support reset to firmware update mode
This patch allows a charger chip to reset to firmware update mode (a.k.a. download mode). Actual firmware update will be added in a separate patch. Currently, a reset event causes the state machine to unconditionally transition to normal mode. This patch makes the state machine check 'mode' field in the context to decide whether it transitions to normal mode or not. This patch also makes ctn730 driver explicitly return PCHG_EVENT_*_ERROR while PCHG_EVENT_NONE was returned previously. BUG=b:173235954,b:183151376 BRANCH=trogdor TEST=Stylus connect, disconnect, charge events are triggered as expected. Signed-off-by: Daisuke Nojiri <dnojiri@chromium.org> Change-Id: I10dbd25d0b3d5284952e57d4ade2949e9594c8d1 Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2770503 (cherry picked from commit 1c5ff6f4d1ddfd3862f9bcfc85a4564eac63d16f) Reviewed-on: https://chromium-review.googlesource.com/c/chromiumos/platform/ec/+/2787477
-rw-r--r--common/peripheral_charger.c167
-rw-r--r--driver/nfc/ctn730.c78
-rw-r--r--include/peripheral_charger.h11
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;
};
/**