summaryrefslogtreecommitdiff
path: root/drivers/watchdog/mcf_wdt.c
blob: 5092a256af0ca00fed17e48f482b6c05b1b1034e (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
134
135
// SPDX-License-Identifier: GPL-2.0
/*
 * mcf_wdt.c - driver for ColdFire on-chip watchdog
 *
 * Author: Angelo Dureghello <angelo@kernel-space.org>
 *
 */

#include <config.h>
#include <dm.h>
#include <hang.h>
#include <asm/io.h>
#include <wdt.h>
#include <linux/bitops.h>

#define DIVIDER_5XXX	4096
#define DIVIDER_5282	8192

#define WCR_EN          BIT(0)
#define WCR_HALTED      BIT(1)
#define WCR_DOZE        BIT(2)
#define WCR_WAIT        BIT(3)

struct watchdog_regs {
	u16 wcr;        /* Control */
	u16 wmr;        /* Service */
	u16 wcntr;      /* Counter */
	u16 wsr;        /* Reset Status */
};

static void mcf_watchdog_reset(struct watchdog_regs *wdog)
{
	if (!IS_ENABLED(CONFIG_WATCHDOG_RESET_DISABLE)) {
		writew(0x5555, &wdog->wsr);
		writew(0xaaaa, &wdog->wsr);
	}
}

static void mcf_watchdog_init(struct watchdog_regs *wdog, u32 fixed_divider,
			      u64 timeout_msecs)
{
	u32 wdog_module, cycles_per_sec;

	cycles_per_sec = CFG_SYS_CLK / fixed_divider;

	wdog_module = cycles_per_sec * ((u32)timeout_msecs / 1000);
	wdog_module += (cycles_per_sec / 1000) * ((u32)timeout_msecs % 1000);

	/* Limit check, max 16 bits */
	if (wdog_module > 0xffff)
		wdog_module = 0xffff;

	/* Set timeout and enable watchdog */
	writew((u16)wdog_module, &wdog->wmr);
	writew(WCR_EN, &wdog->wcr);

	mcf_watchdog_reset(wdog);
}

struct mcf_wdt_priv {
	void __iomem *base;
	u32 fixed_divider;
};

static int mcf_wdt_expire_now(struct udevice *dev, ulong flags)
{
	hang();

	return 0;
}

static int mcf_wdt_reset(struct udevice *dev)
{
	struct mcf_wdt_priv *priv = dev_get_priv(dev);

	mcf_watchdog_reset(priv->base);

	return 0;
}

static int mcf_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
	struct mcf_wdt_priv *priv = dev_get_priv(dev);

	/* Timeout from fdt (timeout) comes in milliseconds */
	mcf_watchdog_init(priv->base, priv->fixed_divider, timeout);

	return 0;
}

static int mcf_wdt_stop(struct udevice *dev)
{
	struct mcf_wdt_priv *priv = dev_get_priv(dev);
	struct watchdog_regs *wdog = (struct watchdog_regs *)priv->base;

	setbits_be16(&wdog->wcr, WCR_HALTED);

	return 0;
}

static int mcf_wdt_probe(struct udevice *dev)
{
	struct mcf_wdt_priv *priv = dev_get_priv(dev);

	priv->base = dev_read_addr_ptr(dev);
	if (!priv->base)
		return -ENOENT;

	priv->fixed_divider = (u32)dev_get_driver_data(dev);

	return 0;
}

static const struct wdt_ops mcf_wdt_ops = {
	.start		= mcf_wdt_start,
	.stop		= mcf_wdt_stop,
	.reset		= mcf_wdt_reset,
	.expire_now	= mcf_wdt_expire_now,
};

static const struct udevice_id mcf_wdt_ids[] = {
	{ .compatible = "fsl,mcf5208-wdt", .data = DIVIDER_5XXX },
	{ .compatible = "fsl,mcf5282-wdt", .data = DIVIDER_5282 },
	{}
};

U_BOOT_DRIVER(mcf_wdt) = {
	.name		= "mcf_wdt",
	.id		= UCLASS_WDT,
	.of_match	= mcf_wdt_ids,
	.probe		= mcf_wdt_probe,
	.ops		= &mcf_wdt_ops,
	.priv_auto	= sizeof(struct mcf_wdt_priv),
	.flags		= DM_FLAG_PRE_RELOC,
};