1// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Watchdog driver for the sl28cpld
4 *
5 * Copyright (c) 2021 Michael Walle <michael@walle.cc>
6 */
7
8#include <common.h>
9#include <dm.h>
10#include <wdt.h>
11#include <sl28cpld.h>
12#include <div64.h>
13
14#define SL28CPLD_WDT_CTRL		0x00
15#define  WDT_CTRL_EN0			BIT(0)
16#define  WDT_CTRL_EN1			BIT(1)
17#define  WDT_CTRL_EN_MASK		GENMASK(1, 0)
18#define  WDT_CTRL_LOCK			BIT(2)
19#define  WDT_CTRL_ASSERT_SYS_RESET	BIT(6)
20#define  WDT_CTRL_ASSERT_WDT_TIMEOUT	BIT(7)
21#define SL28CPLD_WDT_TIMEOUT		0x01
22#define SL28CPLD_WDT_KICK		0x02
23#define  WDT_KICK_VALUE			0x6b
24
25static int sl28cpld_wdt_reset(struct udevice *dev)
26{
27	return sl28cpld_write(dev, SL28CPLD_WDT_KICK, WDT_KICK_VALUE);
28}
29
30static int sl28cpld_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
31{
32	int ret, val;
33
34	val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL);
35	if (val < 0)
36		return val;
37
38	/* (1) disable watchdog */
39	val &= ~WDT_CTRL_EN_MASK;
40	ret = sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val);
41	if (ret)
42		return ret;
43
44	/* (2) set timeout */
45	ret = sl28cpld_write(dev, SL28CPLD_WDT_TIMEOUT, lldiv(timeout, 1000));
46	if (ret)
47		return ret;
48
49	/* (3) kick it, will reset timer to the timeout value */
50	ret = sl28cpld_wdt_reset(dev);
51	if (ret)
52		return ret;
53
54	/* (4) enable either recovery or normal one */
55	if (flags & BIT(0))
56		val |= WDT_CTRL_EN1;
57	else
58		val |= WDT_CTRL_EN0;
59
60	if (flags & BIT(1))
61		val |= WDT_CTRL_LOCK;
62
63	if (flags & BIT(2))
64		val &= ~WDT_CTRL_ASSERT_SYS_RESET;
65	else
66		val |= WDT_CTRL_ASSERT_SYS_RESET;
67
68	if (flags & BIT(3))
69		val |= WDT_CTRL_ASSERT_WDT_TIMEOUT;
70	else
71		val &= ~WDT_CTRL_ASSERT_WDT_TIMEOUT;
72
73	return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val);
74}
75
76static int sl28cpld_wdt_stop(struct udevice *dev)
77{
78	int val;
79
80	val = sl28cpld_read(dev, SL28CPLD_WDT_CTRL);
81	if (val < 0)
82		return val;
83
84	return sl28cpld_write(dev, SL28CPLD_WDT_CTRL, val & ~WDT_CTRL_EN_MASK);
85}
86
87static int sl28cpld_wdt_expire_now(struct udevice *dev, ulong flags)
88{
89	return sl28cpld_wdt_start(dev, 0, flags);
90}
91
92static const struct wdt_ops sl28cpld_wdt_ops = {
93	.start = sl28cpld_wdt_start,
94	.reset = sl28cpld_wdt_reset,
95	.stop = sl28cpld_wdt_stop,
96	.expire_now = sl28cpld_wdt_expire_now,
97};
98
99static const struct udevice_id sl28cpld_wdt_ids[] = {
100	{ .compatible = "kontron,sl28cpld-wdt", },
101	{}
102};
103
104U_BOOT_DRIVER(sl28cpld_wdt) = {
105	.name = "sl28cpld-wdt",
106	.id = UCLASS_WDT,
107	.of_match = sl28cpld_wdt_ids,
108	.ops = &sl28cpld_wdt_ops,
109};
110