^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-or-later
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * OMAP2+ MPU WD_TIMER-specific code
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Copyright (C) 2012 Texas Instruments, Inc.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) #include <linux/io.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/err.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/platform_data/omap-wd-timer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include "omap_hwmod.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include "omap_device.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include "wd_timer.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17) #include "common.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #include "prm.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) #include "soc.h"
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) * In order to avoid any assumptions from bootloader regarding WDT
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) * settings, WDT module is reset during init. This enables the watchdog
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) * timer. Hence it is required to disable the watchdog after the WDT reset
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) * during init. Otherwise the system would reboot as per the default
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) * watchdog timer registers settings.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) #define OMAP_WDT_WPS 0x34
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) #define OMAP_WDT_SPR 0x48
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31) int omap2_wd_timer_disable(struct omap_hwmod *oh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) void __iomem *base;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35) if (!oh) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) base = omap_hwmod_get_mpu_rt_va(oh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) if (!base) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42) pr_err("%s: Could not get the base address for %s\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) oh->name, __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) return -EINVAL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) /* sequence required to disable watchdog */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48) writel_relaxed(0xAAAA, base + OMAP_WDT_SPR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) cpu_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) writel_relaxed(0x5555, base + OMAP_WDT_SPR);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53) while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) cpu_relax();
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) /**
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61) * @oh: struct omap_hwmod *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) * care to execute the special watchdog disable sequence. This is
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) * because the watchdog is re-armed upon OCP softreset. (On OMAP4,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) * this behavior was apparently changed and the watchdog is no longer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67) * re-armed after an OCP soft-reset.) Returns -ETIMEDOUT if the reset
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) * did not complete, or 0 upon success.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) * XXX Most of this code should be moved to the omap_hwmod.c layer
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) * during a normal merge window. omap_hwmod_softreset() should be
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72) * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) * should call the hwmod _ocp_softreset() code.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) int omap2_wd_timer_reset(struct omap_hwmod *oh)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) int c = 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) /* Write to the SOFTRESET bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80) omap_hwmod_softreset(oh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) /* Poll on RESETDONE bit */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) omap_test_timeout((omap_hwmod_read(oh,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) oh->class->sysc->syss_offs)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85) & SYSS_RESETDONE_MASK),
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) MAX_MODULE_SOFTRESET_WAIT, c);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88) if (oh->class->sysc->srst_udelay)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) udelay(oh->class->sysc->srst_udelay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) if (c == MAX_MODULE_SOFTRESET_WAIT)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) pr_warn("%s: %s: softreset failed (waited %d usec)\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) pr_debug("%s: %s: softreset in %d usec\n", __func__,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) oh->name, c);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) omap2_wd_timer_disable(oh);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) }