summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/ast_wdt.c
blob: f7b5a1adc1085459cc57ba9f440866c1d26be7b4 (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
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright 2017 Google, Inc
 */

#include <common.h>
#include <dm.h>
#include <errno.h>
#include <log.h>
#include <wdt.h>
#include <asm/io.h>
#include <asm/arch/wdt.h>
#include <linux/err.h>

#define WDT_AST2500	2500
#define WDT_AST2400	2400

struct ast_wdt_priv {
	struct ast_wdt *regs;
};

static int ast_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
{
	struct ast_wdt_priv *priv = dev_get_priv(dev);
	ulong driver_data = dev_get_driver_data(dev);
	u32 reset_mode = ast_reset_mode_from_flags(flags);

	/* 32 bits at 1MHz is 4294967ms */
	timeout = min_t(u64, timeout, 4294967);

	/* WDT counts in ticks of 1MHz clock. 1ms / 1e3 * 1e6 */
	timeout *= 1000;

	clrsetbits_le32(&priv->regs->ctrl,
			WDT_CTRL_RESET_MASK << WDT_CTRL_RESET_MODE_SHIFT,
			reset_mode << WDT_CTRL_RESET_MODE_SHIFT);

	if (driver_data >= WDT_AST2500 && reset_mode == WDT_CTRL_RESET_SOC)
		writel(ast_reset_mask_from_flags(flags),
		       &priv->regs->reset_mask);

	writel((u32) timeout, &priv->regs->counter_reload_val);
	writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);
	/*
	 * Setting CLK1MHZ bit is just for compatibility with ast2400 part.
	 * On ast2500 watchdog timer clock is fixed at 1MHz and the bit is
	 * read-only
	 */
	setbits_le32(&priv->regs->ctrl,
		     WDT_CTRL_EN | WDT_CTRL_RESET | WDT_CTRL_CLK1MHZ);

	return 0;
}

static int ast_wdt_stop(struct udevice *dev)
{
	struct ast_wdt_priv *priv = dev_get_priv(dev);

	clrbits_le32(&priv->regs->ctrl, WDT_CTRL_EN);

	writel(WDT_RESET_DEFAULT, &priv->regs->reset_mask);
	return 0;
}

static int ast_wdt_reset(struct udevice *dev)
{
	struct ast_wdt_priv *priv = dev_get_priv(dev);

	writel(WDT_COUNTER_RESTART_VAL, &priv->regs->counter_restart);

	return 0;
}

static int ast_wdt_expire_now(struct udevice *dev, ulong flags)
{
	struct ast_wdt_priv *priv = dev_get_priv(dev);
	int ret;

	ret = ast_wdt_start(dev, 1, flags);
	if (ret)
		return ret;

	while (readl(&priv->regs->ctrl) & WDT_CTRL_EN)
		;

	return ast_wdt_stop(dev);
}

static int ast_wdt_of_to_plat(struct udevice *dev)
{
	struct ast_wdt_priv *priv = dev_get_priv(dev);

	priv->regs = dev_read_addr_ptr(dev);
	if (!priv->regs)
		return -EINVAL;

	return 0;
}

static const struct wdt_ops ast_wdt_ops = {
	.start = ast_wdt_start,
	.reset = ast_wdt_reset,
	.stop = ast_wdt_stop,
	.expire_now = ast_wdt_expire_now,
};

static const struct udevice_id ast_wdt_ids[] = {
	{ .compatible = "aspeed,wdt", .data = WDT_AST2500 },
	{ .compatible = "aspeed,ast2500-wdt", .data = WDT_AST2500 },
	{ .compatible = "aspeed,ast2400-wdt", .data = WDT_AST2400 },
	{}
};

static int ast_wdt_probe(struct udevice *dev)
{
	debug("%s() wdt%u\n", __func__, dev_seq(dev));
	ast_wdt_stop(dev);

	return 0;
}

U_BOOT_DRIVER(ast_wdt) = {
	.name = "ast_wdt",
	.id = UCLASS_WDT,
	.of_match = ast_wdt_ids,
	.probe = ast_wdt_probe,
	.priv_auto	= sizeof(struct ast_wdt_priv),
	.of_to_plat = ast_wdt_of_to_plat,
	.ops = &ast_wdt_ops,
};