summaryrefslogtreecommitdiffstats
path: root/drivers/watchdog/imxulp-wdt.c
blob: 5a89876175242af12c9e2be9e3128ca757673e21 (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
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
// SPDX-License-Identifier: GPL-2.0+
/*
 * Copyright (C) 2016 Freescale Semiconductor, Inc.
 */

#include <common.h>
#include <init.h>
#include <io.h>
#include <of.h>
#include <errno.h>
#include <malloc.h>
#include <restart.h>
#include <watchdog.h>
#include <reset_source.h>
#include <linux/clk.h>
#include <asm/system.h>

struct imxulp_socdata {
	bool prescaler_enable;
	unsigned int rate;
};

struct imxulp_wd {
	struct watchdog wd;
	void __iomem *base;
	bool prescaler_enable;
	struct device *dev;
	const struct imxulp_socdata *socdata;
};

#define REFRESH_WORD0 0xA602 /* 1st refresh word */
#define REFRESH_WORD1 0xB480 /* 2nd refresh word */

#define UNLOCK_WORD0 0xC520 /* 1st unlock word */
#define UNLOCK_WORD1 0xD928 /* 2nd unlock word */

#define UNLOCK_WORD 0xD928C520 /* unlock word */
#define REFRESH_WORD 0xB480A602 /* refresh word */

#define WDOG_CS                 0x0
#define WDOG_CS_UPDATE		BIT(5)
#define WDOG_CS_EN		BIT(7)
#define WDOG_CS_RCS		BIT(10)
#define WDOG_CS_ULK		BIT(11)
#define WDOG_CS_PRES		BIT(12)
#define WDOG_CS_CMD32EN		BIT(13)
#define WDOG_CS_FLG		BIT(14)
#define WDOG_CS_INT		BIT(6)
#define WDOG_CS_LPO_CLK		(0x1 << 8)

#define WDOG_CNT		0x4
#define WDOG_TOVAL		0x8

#define CLK_RATE_1KHZ		1000
#define CLK_RATE_32KHZ		128

static int imxulp_watchdog_set_timeout(struct watchdog *wd, unsigned int timeout)
{
	struct imxulp_wd *imxwd = container_of(wd, struct imxulp_wd, wd);
	u32 cmd32 = 0;

	if (timeout == 0)
		return -ENOSYS;

	if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
		writel(UNLOCK_WORD, imxwd->base + WDOG_CNT);
		cmd32 = WDOG_CS_CMD32EN;
	} else {
		writel(UNLOCK_WORD0, imxwd->base + WDOG_CNT);
		writel(UNLOCK_WORD1, imxwd->base + WDOG_CNT);
	}

	/* Wait WDOG Unlock */
	while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_ULK))
		;

	writel(timeout * imxwd->socdata->rate, imxwd->base + WDOG_TOVAL);

	if (imxwd->socdata->prescaler_enable)
		cmd32 |= WDOG_CS_PRES;

	writel(cmd32 | WDOG_CS_EN | WDOG_CS_UPDATE | WDOG_CS_LPO_CLK |
	       WDOG_CS_FLG | WDOG_CS_INT, imxwd->base + WDOG_CS);

	/* Wait WDOG reconfiguration */
	while (!(readl(imxwd->base + WDOG_CS) & WDOG_CS_RCS))
		;

	if (readl(imxwd->base + WDOG_CS) & WDOG_CS_CMD32EN) {
		writel(REFRESH_WORD, imxwd->base + WDOG_CNT);
	} else {
		writel(REFRESH_WORD0, imxwd->base + WDOG_CNT);
		writel(REFRESH_WORD1, imxwd->base + WDOG_CNT);
	}

	return 0;
}

static enum wdog_hw_runnning imxulp_wd_running(struct imxulp_wd *imxwd)
{
	if (readl(imxwd->base + WDOG_CS) & WDOG_CS_EN)
		return WDOG_HW_RUNNING;
	else
		return WDOG_HW_NOT_RUNNING;
}

static int imxulp_wd_probe(struct device *dev)
{
	struct imxulp_wd *imxwd;
	struct resource *iores;
	struct imxulp_socdata *socdata;
	int ret;

	ret = dev_get_drvdata(dev, (const void **)&socdata);
	if (ret)
		return ret;

	imxwd = xzalloc(sizeof(*imxwd));
	iores = dev_request_mem_resource(dev, 0);
	if (IS_ERR(iores))
		return dev_err_probe(dev, PTR_ERR(iores), "could not get memory region\n");

	imxwd->socdata = socdata;
	imxwd->base = IOMEM(iores->start);
	imxwd->wd.set_timeout = imxulp_watchdog_set_timeout;
	imxwd->wd.timeout_max = 0xffff / imxwd->socdata->rate;
	imxwd->wd.hwdev = dev;
	imxwd->wd.running = imxulp_wd_running(imxwd);

	ret = watchdog_register(&imxwd->wd);
	if (ret)
		return dev_err_probe(dev, ret, "Failed to register watchdog device\n");

	return 0;
}

static struct imxulp_socdata imx7ulp_wd_socdata = {
	.prescaler_enable = false,
	.rate = CLK_RATE_1KHZ,
};

static struct imxulp_socdata imx93_wd_socdata = {
	.prescaler_enable = true,
	.rate = CLK_RATE_32KHZ,
};

static __maybe_unused struct of_device_id imxulp_wdt_dt_ids[] = {
	{
		.compatible = "fsl,imx7ulp-wdt",
		.data = &imx7ulp_wd_socdata,
	}, {
		.compatible = "fsl,imx8ulp-wdt",
		.data = &imx7ulp_wd_socdata,
	}, {
		.compatible = "fsl,imx93-wdt",
		.data = &imx93_wd_socdata,
	}, {
		/* sentinel */
	}
};
MODULE_DEVICE_TABLE(of, imx_wdt_dt_ids);

static struct driver imxulp_wd_driver = {
	.name = "imxulp-watchdog",
	.probe = imxulp_wd_probe,
	.of_compatible = DRV_OF_COMPAT(imxulp_wdt_dt_ids),
};
device_platform_driver(imxulp_wd_driver);