summaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/wd_timer.c
blob: 70240a54995cc2248f657e7a3bcec709133e7c88 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
/*
 * OMAP2+ MPU WD_TIMER-specific code
 *
 * Copyright (C) 2012 Texas Instruments, Inc.
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 */

#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/err.h>

#include <linux/platform_data/omap-wd-timer.h>

#include "omap_hwmod.h"
#include "omap_device.h"
#include "wd_timer.h"
#include "common.h"
#include "prm.h"
#include "soc.h"

/*
 * In order to avoid any assumptions from bootloader regarding WDT
 * settings, WDT module is reset during init. This enables the watchdog
 * timer. Hence it is required to disable the watchdog after the WDT reset
 * during init. Otherwise the system would reboot as per the default
 * watchdog timer registers settings.
 */
#define OMAP_WDT_WPS		0x34
#define OMAP_WDT_SPR		0x48

int omap2_wd_timer_disable(struct omap_hwmod *oh)
{
	void __iomem *base;

	if (!oh) {
		pr_err("%s: Could not look up wdtimer_hwmod\n", __func__);
		return -EINVAL;
	}

	base = omap_hwmod_get_mpu_rt_va(oh);
	if (!base) {
		pr_err("%s: Could not get the base address for %s\n",
				oh->name, __func__);
		return -EINVAL;
	}

	/* sequence required to disable watchdog */
	__raw_writel(0xAAAA, base + OMAP_WDT_SPR);
	while (__raw_readl(base + OMAP_WDT_WPS) & 0x10)
		cpu_relax();

	__raw_writel(0x5555, base + OMAP_WDT_SPR);
	while (__raw_readl(base + OMAP_WDT_WPS) & 0x10)
		cpu_relax();

	return 0;
}

/**
 * omap2_wdtimer_reset - reset and disable the WDTIMER IP block
 * @oh: struct omap_hwmod *
 *
 * After the WDTIMER IP blocks are reset on OMAP2/3, we must also take
 * care to execute the special watchdog disable sequence.  This is
 * because the watchdog is re-armed upon OCP softreset.  (On OMAP4,
 * this behavior was apparently changed and the watchdog is no longer
 * re-armed after an OCP soft-reset.)  Returns -ETIMEDOUT if the reset
 * did not complete, or 0 upon success.
 *
 * XXX Most of this code should be moved to the omap_hwmod.c layer
 * during a normal merge window.  omap_hwmod_softreset() should be
 * renamed to omap_hwmod_set_ocp_softreset(), and omap_hwmod_softreset()
 * should call the hwmod _ocp_softreset() code.
 */
int omap2_wd_timer_reset(struct omap_hwmod *oh)
{
	int c = 0;

	/* Write to the SOFTRESET bit */
	omap_hwmod_softreset(oh);

	/* Poll on RESETDONE bit */
	omap_test_timeout((omap_hwmod_read(oh,
					   oh->class->sysc->syss_offs)
			   & SYSS_RESETDONE_MASK),
			  MAX_MODULE_SOFTRESET_WAIT, c);

	if (oh->class->sysc->srst_udelay)
		udelay(oh->class->sysc->srst_udelay);

	if (c == MAX_MODULE_SOFTRESET_WAIT)
		pr_warning("%s: %s: softreset failed (waited %d usec)\n",
			   __func__, oh->name, MAX_MODULE_SOFTRESET_WAIT);
	else
		pr_debug("%s: %s: softreset in %d usec\n", __func__,
			 oh->name, c);

	return (c == MAX_MODULE_SOFTRESET_WAIT) ? -ETIMEDOUT :
		omap2_wd_timer_disable(oh);
}

static int __init omap_init_wdt(void)
{
	int id = -1;
	struct platform_device *pdev;
	struct omap_hwmod *oh;
	char *oh_name = "wd_timer2";
	char *dev_name = "omap_wdt";
	struct omap_wd_timer_platform_data pdata;

	if (!cpu_class_is_omap2() || of_have_populated_dt())
		return 0;

	oh = omap_hwmod_lookup(oh_name);
	if (!oh) {
		pr_err("Could not look up wd_timer%d hwmod\n", id);
		return -EINVAL;
	}

	pdata.read_reset_sources = prm_read_reset_sources;

	pdev = omap_device_build(dev_name, id, oh, &pdata,
				 sizeof(struct omap_wd_timer_platform_data),
				 NULL, 0, 0);
	WARN(IS_ERR(pdev), "Can't build omap_device for %s:%s.\n",
	     dev_name, oh->name);
	return 0;
}
omap_subsys_initcall(omap_init_wdt);