14074Swollman// SPDX-License-Identifier: GPL-2.0-or-later
24074Swollman/*
34074Swollman * OMAP2+ MPU WD_TIMER-specific code
44074Swollman *
54074Swollman * Copyright (C) 2012 Texas Instruments, Inc.
64074Swollman */
75792Swollman
84074Swollman#include <linux/kernel.h>
94074Swollman#include <linux/io.h>
104074Swollman#include <linux/err.h>
114074Swollman
124074Swollman#include <linux/platform_data/omap-wd-timer.h>
134074Swollman
144074Swollman#include "omap_hwmod.h"
154074Swollman#include "omap_device.h"
164074Swollman#include "wd_timer.h"
174074Swollman#include "common.h"
184074Swollman#include "prm.h"
194074Swollman#include "soc.h"
204074Swollman
214074Swollman/*
224074Swollman * In order to avoid any assumptions from bootloader regarding WDT
234074Swollman * settings, WDT module is reset during init. This enables the watchdog
244074Swollman * timer. Hence it is required to disable the watchdog after the WDT reset
254074Swollman * during init. Otherwise the system would reboot as per the default
264074Swollman * watchdog timer registers settings.
274074Swollman */
284074Swollman#define OMAP_WDT_WPS		0x34
294074Swollman#define OMAP_WDT_SPR		0x48
304893Swollman
314074Swollmanint omap2_wd_timer_disable(struct omap_hwmod *oh)
324074Swollman{
334074Swollman	void __iomem *base;
344074Swollman
354074Swollman	if (!oh) {
364074Swollman		pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
374074Swollman		return -EINVAL;
385101Swollman	}
394074Swollman
404074Swollman	base = omap_hwmod_get_mpu_rt_va(oh);
414074Swollman	if (!base) {
424074Swollman		pr_err("%s: Could not get the base address for %s\n",
434074Swollman				oh->name, __func__);
444074Swollman		return -EINVAL;
454074Swollman	}
464074Swollman
474074Swollman	/* sequence required to disable watchdog */
484074Swollman	writel_relaxed(0xAAAA, base + OMAP_WDT_SPR);
494074Swollman	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
505792Swollman		cpu_relax();
514074Swollman
525792Swollman	writel_relaxed(0x5555, base + OMAP_WDT_SPR);
535792Swollman	while (readl_relaxed(base + OMAP_WDT_WPS) & 0x10)
545792Swollman		cpu_relax();
555792Swollman
565792Swollman	return 0;
575792Swollman}
584074Swollman
594105Swollman/**
604074Swollman * omap2_wd_timer_reset - reset and disable the WDTIMER IP block
614074Swollman * @oh: struct omap_hwmod *
624074Swollman *
634105Swollman * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
644105Swollman * care to execute the special watchdog disable sequence.  This is
654105Swollman * because the watchdog is re-armed upon OCP softreset.  (On OMAP4,
664074Swollman * this behavior was apparently changed and the watchdog is no longer
674074Swollman * re-armed after an OCP soft-reset.)  Returns -ETIMEDOUT if the reset
684074Swollman * did not complete, or 0 upon success.
694074Swollman *
704074Swollman * XXX Most of this code should be moved to the omap_hwmod.c layer
714074Swollman * during a normal merge window.  omap_hwmod_softreset() should be
724074Swollman * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
734074Swollman * should call the hwmod _ocp_softreset() code.
745101Swollman *
755101Swollman * Returns: %0 on success or -errno value on error.
764105Swollman */
774074Swollmanint omap2_wd_timer_reset(struct omap_hwmod *oh)
784074Swollman{
794074Swollman	int c = 0;
804074Swollman
814074Swollman	/* Write to the SOFTRESET bit */
825792Swollman	omap_hwmod_softreset(oh);
834893Swollman
844105Swollman	/* Poll on RESETDONE bit */
854074Swollman	omap_test_timeout((omap_hwmod_read(oh,
865792Swollman					   oh->class->sysc->syss_offs)
874074Swollman			   & SYSS_RESETDONE_MASK),
884074Swollman			  MAX_MODULE_SOFTRESET_WAIT, c);
894074Swollman
904074Swollman	if (oh->class->sysc->srst_udelay)
914074Swollman		udelay(oh->class->sysc->srst_udelay);
924074Swollman
934074Swollman	if (c == MAX_MODULE_SOFTRESET_WAIT)
945792Swollman		pr_warn("%s: %s: softreset failed (waited %d usec)\n",
955792Swollman			__func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
965792Swollman	else
974105Swollman		pr_debug("%s: %s: softreset in %d usec\n", __func__,
984074Swollman			 oh->name, c);
994074Swollman
1005101Swollman	return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
1015101Swollman		omap2_wd_timer_disable(oh);
1024105Swollman}
1034074Swollman