// SPDX-License-Identifier: Apache-2.0 // Copyright (C) 2018,2019 IBM Corp. #include "ast.h" #include "wdt.h" #include #include #include enum wdt { wdt1, wdt2, wdt3 }; #define AST_G5_WDTS (wdt3 + 1) static inline uint32_t wdt_addr(enum wdt wdt, uint32_t reg) { assert(reg < WDT_SIZE); return AST_G5_WDT | (wdt * WDT_SIZE) | reg; } static int wdt_stop(struct ahb *ahb, enum wdt wdt) { uint32_t ctrl; uint32_t val; int rc; ctrl = wdt_addr(wdt, WDT_CTRL); rc = ahb_readl(ahb, ctrl, &val); if (rc < 0) return rc; val &= ~WDT_CTRL_ENABLE; return ahb_writel(ahb, ctrl, val); } int wdt_prevent_reset(struct ahb *ahb) { for (int i = 0; i < AST_G5_WDTS; i++) { int rc; rc = wdt_stop(ahb, i); if (rc < 0) return rc; } return 0; } static uint32_t wdt_usecs_to_ticks(struct ahb *ahb, enum wdt wdt, uint32_t usecs) { uint32_t val; int rc; rc = ahb_readl(ahb, wdt_addr(wdt, WDT_CTRL), &val); if (rc < 0) return rc; /* Don't support PCLK as a source yet, involves scraping around in SCU */ assert(val & WDT_CTRL_CLK_1MHZ); return usecs; } int64_t wdt_perform_reset(struct ahb *ahb) { enum wdt victim = wdt2; uint32_t mode; int64_t wait; int rc; rc = wdt_stop(ahb, victim); if (rc < 0) return rc; /* Reset everything except SPI, X-DMA, MCTP and SDRAM */ /* Explicitly, reset the AHB bridges */ rc = ahb_writel(ahb, wdt_addr(victim, WDT_RESET_MASK), 0x23ffffb); if (rc < 0) return rc; /* Wait enough time to cover using the debug UART for a reset */ wait = wdt_usecs_to_ticks(ahb, victim, 5000000); if (wait < 0) return wait; rc = ahb_writel(ahb, wdt_addr(victim, WDT_RELOAD), wait); if (rc < 0) return rc; mode = WDT_CTRL_RESET_SOC | WDT_CTRL_CLK_1MHZ | WDT_CTRL_SYS_RESET | WDT_CTRL_ENABLE; rc = ahb_writel(ahb, wdt_addr(victim, WDT_CTRL), mode); if (rc < 0) return rc; /* The ARM clock gate is sticky on reset?! Ensure it's clear */ rc = ahb_writel(ahb, AST_G5_SCU | SCU_SILICON_REVISION, SCU_HW_STRAP_ARM_CLK); if (rc < 0); rc = ahb_writel(ahb, wdt_addr(victim, WDT_RELOAD), 0); if (rc < 0) return rc; return wait; }