18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-or-later
28c2ecf20Sopenharmony_ci/*
38c2ecf20Sopenharmony_ci * OMAP2+ MPU WD_TIMER-specific code
48c2ecf20Sopenharmony_ci *
58c2ecf20Sopenharmony_ci * Copyright (C) 2012 Texas Instruments, Inc.
68c2ecf20Sopenharmony_ci */
78c2ecf20Sopenharmony_ci
88c2ecf20Sopenharmony_ci#include <linux/kernel.h>
98c2ecf20Sopenharmony_ci#include <linux/io.h>
108c2ecf20Sopenharmony_ci#include <linux/err.h>
118c2ecf20Sopenharmony_ci
128c2ecf20Sopenharmony_ci#include <linux/platform_data/omap-wd-timer.h>
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "omap_hwmod.h"
158c2ecf20Sopenharmony_ci#include "omap_device.h"
168c2ecf20Sopenharmony_ci#include "wd_timer.h"
178c2ecf20Sopenharmony_ci#include "common.h"
188c2ecf20Sopenharmony_ci#include "prm.h"
198c2ecf20Sopenharmony_ci#include "soc.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_ci/*
228c2ecf20Sopenharmony_ci * In order to avoid any assumptions from bootloader regarding WDT
238c2ecf20Sopenharmony_ci * settings, WDT module is reset during init. This enables the watchdog
248c2ecf20Sopenharmony_ci * timer. Hence it is required to disable the watchdog after the WDT reset
258c2ecf20Sopenharmony_ci * during init. Otherwise the system would reboot as per the default
268c2ecf20Sopenharmony_ci * watchdog timer registers settings.
278c2ecf20Sopenharmony_ci */
288c2ecf20Sopenharmony_ci#define OMAP_WDT_WPS		0x34
298c2ecf20Sopenharmony_ci#define OMAP_WDT_SPR		0x48
308c2ecf20Sopenharmony_ci
318c2ecf20Sopenharmony_ciint omap2_wd_timer_disable(struct omap_hwmod *oh)
328c2ecf20Sopenharmony_ci{
338c2ecf20Sopenharmony_ci	void __iomem *base;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	if (!oh) {
368c2ecf20Sopenharmony_ci		pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
378c2ecf20Sopenharmony_ci		return -EINVAL;
388c2ecf20Sopenharmony_ci	}
398c2ecf20Sopenharmony_ci
408c2ecf20Sopenharmony_ci	base = omap_hwmod_get_mpu_rt_va(oh);
418c2ecf20Sopenharmony_ci	if (!base) {
428c2ecf20Sopenharmony_ci		pr_err("%s: Could not get the base address for %s\n",
438c2ecf20Sopenharmony_ci				oh->name, __func__);
448c2ecf20Sopenharmony_ci		return -EINVAL;
458c2ecf20Sopenharmony_ci	}
468c2ecf20Sopenharmony_ci
478c2ecf20Sopenharmony_ci	/* sequence required to disable watchdog */
488c2ecf20Sopenharmony_ci	writel_relaxed(0xAAAA, base + OMAP_WDT_SPR);
498c2ecf20Sopenharmony_ci	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
508c2ecf20Sopenharmony_ci		cpu_relax();
518c2ecf20Sopenharmony_ci
528c2ecf20Sopenharmony_ci	writel_relaxed(0x5555, base + OMAP_WDT_SPR);
538c2ecf20Sopenharmony_ci	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
548c2ecf20Sopenharmony_ci		cpu_relax();
558c2ecf20Sopenharmony_ci
568c2ecf20Sopenharmony_ci	return 0;
578c2ecf20Sopenharmony_ci}
588c2ecf20Sopenharmony_ci
598c2ecf20Sopenharmony_ci/**
608c2ecf20Sopenharmony_ci * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
618c2ecf20Sopenharmony_ci * @oh: struct omap_hwmod *
628c2ecf20Sopenharmony_ci *
638c2ecf20Sopenharmony_ci * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
648c2ecf20Sopenharmony_ci * care to execute the special watchdog disable sequence.  This is
658c2ecf20Sopenharmony_ci * because the watchdog is re-armed upon OCP softreset.  (On OMAP4,
668c2ecf20Sopenharmony_ci * this behavior was apparently changed and the watchdog is no longer
678c2ecf20Sopenharmony_ci * re-armed after an OCP soft-reset.)  Returns -ETIMEDOUT if the reset
688c2ecf20Sopenharmony_ci * did not complete, or 0 upon success.
698c2ecf20Sopenharmony_ci *
708c2ecf20Sopenharmony_ci * XXX Most of this code should be moved to the omap_hwmod.c layer
718c2ecf20Sopenharmony_ci * during a normal merge window.  omap_hwmod_softreset() should be
728c2ecf20Sopenharmony_ci * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
738c2ecf20Sopenharmony_ci * should call the hwmod _ocp_softreset() code.
748c2ecf20Sopenharmony_ci */
758c2ecf20Sopenharmony_ciint omap2_wd_timer_reset(struct omap_hwmod *oh)
768c2ecf20Sopenharmony_ci{
778c2ecf20Sopenharmony_ci	int c = 0;
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	/* Write to the SOFTRESET bit */
808c2ecf20Sopenharmony_ci	omap_hwmod_softreset(oh);
818c2ecf20Sopenharmony_ci
828c2ecf20Sopenharmony_ci	/* Poll on RESETDONE bit */
838c2ecf20Sopenharmony_ci	omap_test_timeout((omap_hwmod_read(oh,
848c2ecf20Sopenharmony_ci					   oh->class->sysc->syss_offs)
858c2ecf20Sopenharmony_ci			   & SYSS_RESETDONE_MASK),
868c2ecf20Sopenharmony_ci			  MAX_MODULE_SOFTRESET_WAIT, c);
878c2ecf20Sopenharmony_ci
888c2ecf20Sopenharmony_ci	if (oh->class->sysc->srst_udelay)
898c2ecf20Sopenharmony_ci		udelay(oh->class->sysc->srst_udelay);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	if (c == MAX_MODULE_SOFTRESET_WAIT)
928c2ecf20Sopenharmony_ci		pr_warn("%s: %s: softreset failed (waited %d usec)\n",
938c2ecf20Sopenharmony_ci			__func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
948c2ecf20Sopenharmony_ci	else
958c2ecf20Sopenharmony_ci		pr_debug("%s: %s: softreset in %d usec\n", __func__,
968c2ecf20Sopenharmony_ci			 oh->name, c);
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
998c2ecf20Sopenharmony_ci		omap2_wd_timer_disable(oh);
1008c2ecf20Sopenharmony_ci}
101