diff --git a/fel.c b/fel.c index 3b16ad8..9cb8923 100644 --- a/fel.c +++ b/fel.c @@ -1055,6 +1055,20 @@ void aw_rmr_request(feldev_handle *dev, uint32_t entry_point, bool aarch64) pr_info(" done.\n"); } +/* Use the watchdog to simply reboot. Useful to get out of fel without + * power cycling or plugging. + */ +void aw_wd_reset(feldev_handle *dev) +{ + const watchdog_info *wd = dev->soc_info->watchdog; + if (!wd) { + pr_error("No watchdog information available (yet) for soc: %s\n", dev->soc_info->name); + return; + } + fel_writel(dev, wd->reg_mode, wd->reg_mode_value); + pr_info("Requested watchdog reset\n"); +} + /* check buffer for magic "#=uEnv", indicating uEnv.txt compatible format */ static bool is_uEnv(void *buffer, size_t size) { @@ -1164,6 +1178,7 @@ void usage(const char *cmd) { " dump address length Binary memory dump\n" " exe[cute] address Call function address\n" " reset64 address RMR request for AArch64 warm boot\n" + " wdreset Reboot via watchdog\n" " memmove dest source size Copy bytes within device memory\n" " readl address Read 32-bit value from device memory\n" " writel address value Write 32-bit value to device memory\n" @@ -1296,6 +1311,8 @@ int main(int argc, char **argv) /* Cancel U-Boot autostart, and stop processing args */ uboot_autostart = false; break; + } else if (strcmp(argv[1], "wdreset") == 0) { + aw_wd_reset(handle); } else if (strncmp(argv[1], "ver", 3) == 0) { aw_fel_print_version(handle); } else if (strcmp(argv[1], "sid") == 0) { diff --git a/soc_info.c b/soc_info.c index cac5d28..b1a19cc 100644 --- a/soc_info.c +++ b/soc_info.c @@ -110,6 +110,16 @@ sram_swap_buffers h6_sram_swap_buffers[] = { { .size = 0 } /* End of the table */ }; +const watchdog_info wd_a10_compat = { + .reg_mode = 0x01C20C94, + .reg_mode_value = 3, +}; + +const watchdog_info wd_h3_compat = { + .reg_mode = 0x01C20Cb8, + .reg_mode_value = 1, +}; + soc_info_t soc_info_table[] = { { .soc_id = 0x1623, /* Allwinner A10 */ @@ -119,6 +129,7 @@ soc_info_t soc_info_table[] = { .swap_buffers = a10_a13_a20_sram_swap_buffers, .needs_l2en = true, .sid_base = 0x01C23800, + .watchdog = &wd_a10_compat, },{ .soc_id = 0x1625, /* Allwinner A10s, A13, R8 */ .name = "A13", @@ -195,6 +206,7 @@ soc_info_t soc_info_table[] = { .sid_fix = true, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, + .watchdog = &wd_h3_compat, },{ .soc_id = 0x1681, /* Allwinner V3s */ .name = "V3s", @@ -215,6 +227,7 @@ soc_info_t soc_info_table[] = { .rvbar_reg = 0x017000A0, /* Check L.NOP in the OpenRISC reset vector */ .needs_smc_workaround_if_zero_word_at_addr = 0x40004, + .watchdog = &wd_h3_compat, },{ .soc_id = 0x1701, /* Allwinner R40 */ .name = "R40", diff --git a/soc_info.h b/soc_info.h index d424c66..ad2d535 100644 --- a/soc_info.h +++ b/soc_info.h @@ -51,6 +51,16 @@ typedef struct { uint32_t size; /* buffer size */ } sram_swap_buffers; +/* + * Contains information on the watchdog peripheral, to enable reset + */ +typedef struct { + /* Register that needs to be written to */ + uint32_t reg_mode; + /* Value to write to trigger a reset */ + uint32_t reg_mode_value; +} watchdog_info; + /* * Each SoC variant may have its own list of memory buffers to be exchanged * and the information about the placement of the thunk code, which handles @@ -101,6 +111,7 @@ typedef struct { uint32_t sid_base; /* base address for SID registers */ uint32_t sid_offset; /* offset for SID_KEY[0-3], "root key" */ uint32_t rvbar_reg; /* MMIO address of RVBARADDR0_L register */ + const watchdog_info *watchdog; /* Used for reset */ bool sid_fix; /* Use SID workaround (read via register) */ /* Use SMC workaround (enter secure mode) if can't read from this address */ uint32_t needs_smc_workaround_if_zero_word_at_addr;