Skip to content

Commit

Permalink
wdt: Wait for timer expiration period before un-gating ARM clock
Browse files Browse the repository at this point in the history
Allowing the ARM core to resume execution before the reset has
actually fired can lead to undesirable situations such as old
in-memory firmware corrupting a new firmware image just written by a
'write firmware' command.  Add a usleep() call to wdt_perform_reset()
so that it waits the necessary amount of time itself rather than
returning a duration for the caller to wait.

Because the debug UART interface seems to lock up if left active
across a reset, we also bracket the sleep (during which the reset is
expected to occur) with a release/reinit of the bridge interface.

Signed-off-by: Zev Weiss <[email protected]>
  • Loading branch information
zevweiss committed Nov 16, 2023
1 parent 4a53415 commit 7e35dfb
Show file tree
Hide file tree
Showing 4 changed files with 24 additions and 17 deletions.
8 changes: 2 additions & 6 deletions src/cmd/reset.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ int cmd_reset(const char *name __unused, int argc, char *argv[])
{
struct host _host, *host = &_host;
struct soc _soc, *soc = &_soc;
int64_t wait = 0;
struct ahb *ahb;
struct clk *clk;
struct wdt *wdt;
Expand Down Expand Up @@ -83,11 +82,8 @@ int cmd_reset(const char *name __unused, int argc, char *argv[])

/* wdt_perform_reset ungates the ARM if required */
logi("Performing SoC reset\n");
if ((wait = wdt_perform_reset(wdt)) > 0)
usleep(wait);

/* Cleanup */
if (wait < 0) {
rc = wdt_perform_reset(wdt);
if (rc < 0) {
clk_enable_arm:
if ((cleanup = clk_enable(clk, clk_arm)) < 0) {
errno = -cleanup;
Expand Down
10 changes: 2 additions & 8 deletions src/cmd/write.c
Original file line number Diff line number Diff line change
Expand Up @@ -163,22 +163,16 @@ int cmd_write(const char *name __unused, int argc, char *argv[])
if (rc == 0) {
if (!ahb->drv->local) {
struct wdt *wdt;
int64_t wait;

logi("Performing SoC reset\n");
if (!(wdt = wdt_get_by_name(soc, "wdt2"))) {
loge("Failed to acquire wdt2 controller, exiting\n");
goto cleanup_soc;
}

wait = wdt_perform_reset(wdt);

if (wait < 0) {
rc = wait;
rc = wdt_perform_reset(wdt);
if (rc < 0)
goto cleanup_soc;
}

usleep(wait);
}
} else {
logi("Deconfiguring VUART host Tx discard\n");
Expand Down
21 changes: 19 additions & 2 deletions src/soc/wdt.c
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <inttypes.h>
#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

/* Registers */
#define WDT_RELOAD 0x04
Expand Down Expand Up @@ -119,7 +120,7 @@ static int64_t wdt_usecs_to_ticks(struct wdt *ctx, uint32_t usecs)
return usecs;
}

int64_t wdt_perform_reset(struct wdt *ctx)
int wdt_perform_reset(struct wdt *ctx)
{
uint32_t mode;
int64_t wait;
Expand Down Expand Up @@ -159,6 +160,22 @@ int64_t wdt_perform_reset(struct wdt *ctx)
if ((rc = wdt_writel(ctx, WDT_CTRL, mode)) < 0)
return rc;

if ((rc = ahb_release_bridge(ctx->soc->ahb)) < 0)
return rc;

/*
* Allow a little extra time for reset to occur (we're timing this
* asynchronously after all) before we try to reinitialize the bridge
*/
wait += 1000000;
logd("Waiting %"PRId64" microseconds for watchdog timer to expire\n", wait);
usleep(wait);

if ((rc = ahb_reinit_bridge(ctx->soc->ahb)) < 0) {
loge("Failed to reinitialize bridge after reset: %d\n", rc);
return rc;
}

/* The ARM clock gate is sticky on reset?! Ensure it's clear */
if ((rc = clk_enable(ctx->clk, clk_arm)) < 0)
return rc;
Expand All @@ -167,7 +184,7 @@ int64_t wdt_perform_reset(struct wdt *ctx)
if (rc < 0)
return rc;

return wait;
return 0;
}

static const struct soc_device_id wdt_match[] = {
Expand Down
2 changes: 1 addition & 1 deletion src/soc/wdt.h
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ int wdt_prevent_reset(struct soc *soc);
struct wdt;

int wdt_init(struct wdt *ctx, struct soc *soc, const char *name);
int64_t wdt_perform_reset(struct wdt *ctx);
int wdt_perform_reset(struct wdt *ctx);
void wdt_destroy(struct wdt *ctx);

struct wdt *wdt_get_by_name(struct soc *soc, const char *name);
Expand Down

0 comments on commit 7e35dfb

Please sign in to comment.