blob: b7216b578630b82b8f517d20521c785bb0df3bc0 [file] [log] [blame]
Fabio Estevam74bee302024-09-17 10:55:50 -03001// SPDX-License-Identifier: GPL-2.0+
2/*
3 * Based on the Linux drivers/watchdog/da9063_wdt.c file.
4 *
5 * Watchdog driver for DA9063 PMICs.
6 *
7 * Copyright(c) 2012 Dialog Semiconductor Ltd.
8 *
9 * Author: Mariusz Wojtasik <mariusz.wojtasik@diasemi.com>
10 *
11 * Ported to U-Boot by Fabio Estevam <festevam@denx.de>
12 *
13 */
14
15#include <dm.h>
16#include <dm/device-internal.h>
17#include <dm/device_compat.h>
18#include <dm/lists.h>
19#include <i2c.h>
20#include <linux/delay.h>
21#include <wdt.h>
22
23#define DA9063_REG_CONTROL_D 0x11
24/* DA9063_REG_CONTROL_D (addr=0x11) */
25#define DA9063_TWDSCALE_MASK 0x0
26#define DA9063_TWDSCALE_DISABLE 0
27#define DA9063_REG_CONTROL_F 0x13
28/* DA9063_REG_CONTROL_F (addr=0x13) */
29#define DA9063_WATCHDOG 0x01
30#define DA9063_SHUTDOWN 0x02
31
32/*
33 * Watchdog selector to timeout in seconds.
34 * 0: WDT disabled;
35 * others: timeout = 2048 ms * 2^(TWDSCALE-1).
36 */
37static const unsigned int wdt_timeout[] = { 0, 2, 4, 8, 16, 32, 65, 131 };
38
39#define DA9063_TWDSCALE_DISABLE 0
40#define DA9063_TWDSCALE_MIN 1
41#define DA9063_TWDSCALE_MAX (ARRAY_SIZE(wdt_timeout) - 1)
42
43static unsigned int da9063_wdt_timeout_to_sel(unsigned int secs)
44{
45 unsigned int i;
46
47 for (i = DA9063_TWDSCALE_MIN; i <= DA9063_TWDSCALE_MAX; i++) {
48 if (wdt_timeout[i] >= secs)
49 return i;
50 }
51
52 return DA9063_TWDSCALE_MAX;
53}
54
55static int da9063_read(struct udevice *dev, uint reg, uint8_t *buff, int len)
56{
57 return dm_i2c_read(dev->parent, reg, buff, len);
58}
59
60static int da9063_write(struct udevice *dev, uint reg, const u8 *buff, int len)
61{
62 return dm_i2c_write(dev->parent, reg, buff, len);
63}
64
65static int da9063_wdt_disable_timer(struct udevice *dev)
66{
67 u8 val;
68
69 da9063_read(dev, DA9063_REG_CONTROL_D, &val, 1);
70 val &= ~DA9063_TWDSCALE_MASK;
71 val |= DA9063_TWDSCALE_DISABLE;
72 da9063_write(dev, DA9063_REG_CONTROL_D, &val, 1);
73
74 return 0;
75}
76
77static int da9063_wdt_update_timeout(struct udevice *dev, unsigned int timeout)
78{
79 unsigned int regval;
80 int ret;
81 u8 val;
82
83 /*
84 * The watchdog triggers a reboot if a timeout value is already
85 * programmed because the timeout value combines two functions
86 * in one: indicating the counter limit and starting the watchdog.
87 * The watchdog must be disabled to be able to change the timeout
88 * value if the watchdog is already running. Then we can set the
89 * new timeout value which enables the watchdog again.
90 */
91 ret = da9063_wdt_disable_timer(dev);
92 if (ret)
93 return ret;
94
95 udelay(300);
96
97 regval = da9063_wdt_timeout_to_sel(timeout);
98
99 da9063_read(dev, DA9063_REG_CONTROL_D, &val, 1);
100 val &= ~DA9063_TWDSCALE_MASK;
101 val |= regval;
102 da9063_write(dev, DA9063_REG_CONTROL_D, &val, 1);
103
104 return 0;
105}
106
107static int da9063_wdt_start(struct udevice *dev, u64 timeout, ulong flags)
108{
109 return da9063_wdt_update_timeout(dev, timeout);
110}
111
112static int da9063_wdt_stop(struct udevice *dev)
113{
114 return da9063_wdt_disable_timer(dev);
115}
116
117static int da9063_wdt_reset(struct udevice *dev)
118{
119 u8 val = DA9063_WATCHDOG;
120
121 return da9063_write(dev, DA9063_REG_CONTROL_F, &val, 1);
122}
123
124static int da9063_wdt_expire_now(struct udevice *dev, ulong flags)
125{
126 u8 val = DA9063_SHUTDOWN;
127
128 return da9063_write(dev, DA9063_REG_CONTROL_F, &val, 1);
129}
130
131static const struct wdt_ops da9063_wdt_ops = {
132 .start = da9063_wdt_start,
133 .stop = da9063_wdt_stop,
134 .reset = da9063_wdt_reset,
135 .expire_now = da9063_wdt_expire_now,
136};
137
138static const struct udevice_id da9063_wdt_ids[] = {
139 { .compatible = "dlg,da9063-watchdog", },
140 {}
141};
142
143U_BOOT_DRIVER(da9063_wdt) = {
144 .name = "da9063-wdt",
145 .id = UCLASS_WDT,
146 .of_match = da9063_wdt_ids,
147 .ops = &da9063_wdt_ops,
148 .flags = DM_FLAG_PROBE_AFTER_BIND,
149};