162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later 262306a36Sopenharmony_ci/* 362306a36Sopenharmony_ci * OMAP2+ MPU WD_TIMER-specific code 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * Copyright (C) 2012 Texas Instruments, Inc. 662306a36Sopenharmony_ci */ 762306a36Sopenharmony_ci 862306a36Sopenharmony_ci#include <linux/kernel.h> 962306a36Sopenharmony_ci#include <linux/io.h> 1062306a36Sopenharmony_ci#include <linux/err.h> 1162306a36Sopenharmony_ci 1262306a36Sopenharmony_ci#include <linux/platform_data/omap-wd-timer.h> 1362306a36Sopenharmony_ci 1462306a36Sopenharmony_ci#include "omap_hwmod.h" 1562306a36Sopenharmony_ci#include "omap_device.h" 1662306a36Sopenharmony_ci#include "wd_timer.h" 1762306a36Sopenharmony_ci#include "common.h" 1862306a36Sopenharmony_ci#include "prm.h" 1962306a36Sopenharmony_ci#include "soc.h" 2062306a36Sopenharmony_ci 2162306a36Sopenharmony_ci/* 2262306a36Sopenharmony_ci * In order to avoid any assumptions from bootloader regarding WDT 2362306a36Sopenharmony_ci * settings, WDT module is reset during init. This enables the watchdog 2462306a36Sopenharmony_ci * timer. Hence it is required to disable the watchdog after the WDT reset 2562306a36Sopenharmony_ci * during init. Otherwise the system would reboot as per the default 2662306a36Sopenharmony_ci * watchdog timer registers settings. 2762306a36Sopenharmony_ci */ 2862306a36Sopenharmony_ci#define OMAP_WDT_WPS 0x34 2962306a36Sopenharmony_ci#define OMAP_WDT_SPR 0x48 3062306a36Sopenharmony_ci 3162306a36Sopenharmony_ciint omap2_wd_timer_disable(struct omap_hwmod *oh) 3262306a36Sopenharmony_ci{ 3362306a36Sopenharmony_ci void __iomem *base; 3462306a36Sopenharmony_ci 3562306a36Sopenharmony_ci if (!oh) { 3662306a36Sopenharmony_ci pr_err("%s: Could not look up wdtimer_hwmod\n", __func__); 3762306a36Sopenharmony_ci return -EINVAL; 3862306a36Sopenharmony_ci } 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_ci base = omap_hwmod_get_mpu_rt_va(oh); 4162306a36Sopenharmony_ci if (!base) { 4262306a36Sopenharmony_ci pr_err("%s: Could not get the base address for %s\n", 4362306a36Sopenharmony_ci oh->name, __func__); 4462306a36Sopenharmony_ci return -EINVAL; 4562306a36Sopenharmony_ci } 4662306a36Sopenharmony_ci 4762306a36Sopenharmony_ci /* sequence required to disable watchdog */ 4862306a36Sopenharmony_ci writel_relaxed(0xAAAA, base + OMAP_WDT_SPR); 4962306a36Sopenharmony_ci while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10) 5062306a36Sopenharmony_ci cpu_relax(); 5162306a36Sopenharmony_ci 5262306a36Sopenharmony_ci writel_relaxed(0x5555, base + OMAP_WDT_SPR); 5362306a36Sopenharmony_ci while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10) 5462306a36Sopenharmony_ci cpu_relax(); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci return 0; 5762306a36Sopenharmony_ci} 5862306a36Sopenharmony_ci 5962306a36Sopenharmony_ci/** 6062306a36Sopenharmony_ci * omap2_wdtimer_reset - reset and disable the WDTIMER IP block 6162306a36Sopenharmony_ci * @oh: struct omap_hwmod * 6262306a36Sopenharmony_ci * 6362306a36Sopenharmony_ci * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take 6462306a36Sopenharmony_ci * care to execute the special watchdog disable sequence. This is 6562306a36Sopenharmony_ci * because the watchdog is re-armed upon OCP softreset. (On OMAP4, 6662306a36Sopenharmony_ci * this behavior was apparently changed and the watchdog is no longer 6762306a36Sopenharmony_ci * re-armed after an OCP soft-reset.) Returns -ETIMEDOUT if the reset 6862306a36Sopenharmony_ci * did not complete, or 0 upon success. 6962306a36Sopenharmony_ci * 7062306a36Sopenharmony_ci * XXX Most of this code should be moved to the omap_hwmod.c layer 7162306a36Sopenharmony_ci * during a normal merge window. omap_hwmod_softreset() should be 7262306a36Sopenharmony_ci * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset() 7362306a36Sopenharmony_ci * should call the hwmod _ocp_softreset() code. 7462306a36Sopenharmony_ci */ 7562306a36Sopenharmony_ciint omap2_wd_timer_reset(struct omap_hwmod *oh) 7662306a36Sopenharmony_ci{ 7762306a36Sopenharmony_ci int c = 0; 7862306a36Sopenharmony_ci 7962306a36Sopenharmony_ci /* Write to the SOFTRESET bit */ 8062306a36Sopenharmony_ci omap_hwmod_softreset(oh); 8162306a36Sopenharmony_ci 8262306a36Sopenharmony_ci /* Poll on RESETDONE bit */ 8362306a36Sopenharmony_ci omap_test_timeout((omap_hwmod_read(oh, 8462306a36Sopenharmony_ci oh->class->sysc->syss_offs) 8562306a36Sopenharmony_ci & SYSS_RESETDONE_MASK), 8662306a36Sopenharmony_ci MAX_MODULE_SOFTRESET_WAIT, c); 8762306a36Sopenharmony_ci 8862306a36Sopenharmony_ci if (oh->class->sysc->srst_udelay) 8962306a36Sopenharmony_ci udelay(oh->class->sysc->srst_udelay); 9062306a36Sopenharmony_ci 9162306a36Sopenharmony_ci if (c == MAX_MODULE_SOFTRESET_WAIT) 9262306a36Sopenharmony_ci pr_warn("%s: %s: softreset failed (waited %d usec)\n", 9362306a36Sopenharmony_ci __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT); 9462306a36Sopenharmony_ci else 9562306a36Sopenharmony_ci pr_debug("%s: %s: softreset in %d usec\n", __func__, 9662306a36Sopenharmony_ci oh->name, c); 9762306a36Sopenharmony_ci 9862306a36Sopenharmony_ci return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT : 9962306a36Sopenharmony_ci omap2_wd_timer_disable(oh); 10062306a36Sopenharmony_ci} 101