1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nordic_npm2100_wdt
7 
8 #include <errno.h>
9 
10 #include <zephyr/kernel.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/watchdog.h>
13 #include <zephyr/drivers/mfd/npm2100.h>
14 
15 #define TIMER_TASKS_START 0xB0U
16 #define TIMER_TASKS_STOP  0xB1U
17 #define TIMER_TASKS_KICK  0xB2U
18 #define TIMER_CONFIG      0xB3U
19 #define TIMER_TARGET_HI   0xB4U
20 #define TIMER_TARGET_MID  0xB5U
21 #define TIMER_TARGET_LO   0xB6U
22 #define TIMER_STATUS      0xB7U
23 #define TIMER_BOOT_MON    0xB8U
24 
25 struct wdt_npm2100_config {
26 	const struct device *mfd;
27 	struct i2c_dt_spec i2c;
28 };
29 
30 struct wdt_npm2100_data {
31 	bool timeout_valid;
32 };
33 
wdt_npm2100_setup(const struct device * dev,uint8_t options)34 static int wdt_npm2100_setup(const struct device *dev, uint8_t options)
35 {
36 	const struct wdt_npm2100_config *config = dev->config;
37 	struct wdt_npm2100_data *data = dev->data;
38 
39 	if (!data->timeout_valid) {
40 		return -EINVAL;
41 	}
42 
43 	return mfd_npm2100_start_timer(config->mfd);
44 }
45 
wdt_npm2100_disable(const struct device * dev)46 static int wdt_npm2100_disable(const struct device *dev)
47 {
48 	const struct wdt_npm2100_config *config = dev->config;
49 	struct wdt_npm2100_data *data = dev->data;
50 	int ret;
51 
52 	ret = i2c_reg_write_byte_dt(&config->i2c, TIMER_TASKS_STOP, 1U);
53 	if (ret < 0) {
54 		return ret;
55 	}
56 
57 	data->timeout_valid = false;
58 
59 	return 0;
60 }
61 
wdt_npm2100_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * timeout)62 static int wdt_npm2100_install_timeout(const struct device *dev,
63 				       const struct wdt_timeout_cfg *timeout)
64 {
65 	const struct wdt_npm2100_config *config = dev->config;
66 	struct wdt_npm2100_data *data = dev->data;
67 	uint8_t mode;
68 	int ret;
69 
70 	if (data->timeout_valid) {
71 		return -ENOMEM;
72 	}
73 
74 	if (timeout->window.min != 0U) {
75 		return -EINVAL;
76 	}
77 
78 	switch (timeout->flags & WDT_FLAG_RESET_MASK) {
79 	case WDT_FLAG_RESET_NONE:
80 		/* Watchdog expiry causes event only, and does not reset */
81 		mode = NPM2100_TIMER_MODE_GENERAL_PURPOSE;
82 		break;
83 	case WDT_FLAG_RESET_CPU_CORE:
84 		/* Watchdog expiry asserts reset output */
85 		mode = NPM2100_TIMER_MODE_WDT_RESET;
86 		break;
87 	case WDT_FLAG_RESET_SOC:
88 		/* Watchdog expiry causes full power cycle */
89 		mode = NPM2100_TIMER_MODE_WDT_POWER_CYCLE;
90 		break;
91 	default:
92 		return -EINVAL;
93 	}
94 
95 	ret = mfd_npm2100_set_timer(config->mfd, timeout->window.max, mode);
96 	if (ret < 0) {
97 		return ret;
98 	}
99 
100 	data->timeout_valid = true;
101 
102 	return 0;
103 }
104 
wdt_npm2100_feed(const struct device * dev,int channel_id)105 static int wdt_npm2100_feed(const struct device *dev, int channel_id)
106 {
107 	const struct wdt_npm2100_config *config = dev->config;
108 
109 	if (channel_id != 0) {
110 		return -EINVAL;
111 	}
112 
113 	return i2c_reg_write_byte_dt(&config->i2c, TIMER_TASKS_KICK, 1U);
114 }
115 
116 static DEVICE_API(wdt, wdt_npm2100_api) = {
117 	.setup = wdt_npm2100_setup,
118 	.disable = wdt_npm2100_disable,
119 	.install_timeout = wdt_npm2100_install_timeout,
120 	.feed = wdt_npm2100_feed,
121 };
122 
wdt_npm2100_init(const struct device * dev)123 static int wdt_npm2100_init(const struct device *dev)
124 {
125 	const struct wdt_npm2100_config *config = dev->config;
126 
127 	if (!i2c_is_ready_dt(&config->i2c)) {
128 		return -ENODEV;
129 	}
130 
131 	/* Disable boot monitor */
132 	return wdt_npm2100_disable(dev);
133 }
134 
135 #define WDT_NPM2100_DEFINE(n)                                                                      \
136 	static struct wdt_npm2100_data data##n;                                                    \
137                                                                                                    \
138 	static const struct wdt_npm2100_config config##n = {                                       \
139 		.mfd = DEVICE_DT_GET(DT_INST_PARENT(n)),                                           \
140 		.i2c = I2C_DT_SPEC_GET(DT_INST_PARENT(n)),                                         \
141 	};                                                                                         \
142                                                                                                    \
143 	DEVICE_DT_INST_DEFINE(n, &wdt_npm2100_init, NULL, &data##n, &config##n, POST_KERNEL,       \
144 			      CONFIG_WDT_NPM2100_INIT_PRIORITY, &wdt_npm2100_api);
145 
146 DT_INST_FOREACH_STATUS_OKAY(WDT_NPM2100_DEFINE)
147