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