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
|
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (C) 2017 Pengutronix, Lucas Stach <l.stach@pengutronix.de>
*
* Based on code from Carlo Caione <carlo@carlocaione.org>
*/
#include <common.h>
#include <init.h>
#include <io.h>
#include <restart.h>
#include <watchdog.h>
#define PM_RSTC 0x1c
#define PM_RSTS 0x20
#define PM_WDOG 0x24
#define PM_WDOG_RESET 0000000000
#define PM_PASSWORD 0x5a000000
#define PM_WDOG_TIME_SET 0x000fffff
#define PM_RSTC_WRCFG_CLR 0xffffffcf
#define PM_RSTC_WRCFG_SET 0x00000030
#define PM_RSTC_WRCFG_FULL_RESET 0x00000020
#define PM_RSTC_RESET 0x00000102
#define PM_RSTS_HADPOR_SET 0x00001000
#define PM_RSTS_HADSRH_SET 0x00000400
#define PM_RSTS_HADSRF_SET 0x00000200
#define PM_RSTS_HADSRQ_SET 0x00000100
#define PM_RSTS_HADWRH_SET 0x00000040
#define PM_RSTS_HADWRF_SET 0x00000020
#define PM_RSTS_HADWRQ_SET 0x00000010
#define PM_RSTS_HADDRH_SET 0x00000004
#define PM_RSTS_HADDRF_SET 0x00000002
#define PM_RSTS_HADDRQ_SET 0x00000001
#define SECS_TO_WDOG_TICKS(x) ((x) << 16)
/* Largest value where SECS_TO_WDOG_TICKS doesn't overflow 20 bits
* (PM_WDOG_TIME_SET) */
#define WDOG_SECS_MAX 15
struct bcm2835_wd {
struct watchdog wd;
void __iomem *base;
struct device_d *dev;
struct restart_handler restart;
};
#define RESET_TIMEOUT 10
static void __noreturn bcm2835_restart_soc(struct restart_handler *rst)
{
struct bcm2835_wd *priv = container_of(rst, struct bcm2835_wd, restart);
uint32_t rstc;
rstc = readl(priv->base + PM_RSTC);
rstc &= ~PM_RSTC_WRCFG_SET;
rstc |= PM_RSTC_WRCFG_FULL_RESET;
writel(PM_PASSWORD | RESET_TIMEOUT, priv->base + PM_WDOG);
writel(PM_PASSWORD | rstc, priv->base + PM_RSTC);
hang();
}
static int bcm2835_wd_set_timeout(struct watchdog *wd, unsigned timeout)
{
struct bcm2835_wd *priv = container_of(wd, struct bcm2835_wd, wd);
u32 val;
if (!timeout) {
writel(PM_PASSWORD | PM_RSTC_RESET, priv->base + PM_RSTC);
return 0;
}
writel(PM_PASSWORD | (SECS_TO_WDOG_TICKS(timeout) & PM_WDOG_TIME_SET),
priv->base + PM_WDOG);
val = readl(priv->base + PM_RSTC);
writel(PM_PASSWORD | (val & PM_RSTC_WRCFG_CLR) |
PM_RSTC_WRCFG_FULL_RESET, priv->base + PM_RSTC);
return 0;
}
static int bcm2835_wd_probe(struct device_d *dev)
{
struct resource *iores;
struct bcm2835_wd *priv;
int ret;
priv = xzalloc(sizeof(*priv));
iores = dev_request_mem_resource(dev, 0);
if (IS_ERR(iores)) {
dev_err(dev, "could not get memory region\n");
return PTR_ERR(iores);
}
priv->base = IOMEM(iores->start);
priv->wd.set_timeout = bcm2835_wd_set_timeout;
priv->wd.timeout_max = WDOG_SECS_MAX;
priv->wd.hwdev = dev;
priv->dev = dev;
if (IS_ENABLED(CONFIG_WATCHDOG_BCM2835)) {
ret = watchdog_register(&priv->wd);
if (ret) {
free(priv);
return ret;
}
}
priv->restart.name = "bcm2835_wd";
priv->restart.restart = bcm2835_restart_soc;
restart_handler_register(&priv->restart);
return 0;
}
static __maybe_unused struct of_device_id bcm2835_wd_dt_ids[] = {
{
.compatible = "brcm,bcm2835-pm-wdt",
}, {
/* sentinel */
},
};
static struct driver_d bcm2835_wd_driver = {
.name = "bcm2835_wd",
.of_compatible = DRV_OF_COMPAT(bcm2835_wd_dt_ids),
.probe = bcm2835_wd_probe,
};
device_platform_driver(bcm2835_wd_driver);
|