1 /*
2  * Copyright (c) 2022 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nordic_npm6001_wdt
7 
8 #include <errno.h>
9 
10 #include <zephyr/device.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/watchdog.h>
13 #include <zephyr/sys/util_macro.h>
14 #include <zephyr/toolchain.h>
15 
16 /* nPM6001 Watchdog related registers */
17 #define NPM6001_WDARMEDVALUE	0x54U
18 #define NPM6001_WDARMEDSTROBE	0x55U
19 #define NPM6001_WDTRIGGERVALUE0 0x56U
20 #define NPM6001_WDTRIGGERVALUE1 0x57U
21 #define NPM6001_WDTRIGGERVALUE2 0x58U
22 #define NPM6001_WDDATASTROBE	0x5DU
23 #define NPM6001_WDPWRUPVALUE	0x5EU
24 #define NPM6001_WDPWRUPSTROBE	0x5FU
25 #define NPM6001_WDKICK		0x60U
26 #define NPM6001_WDREQPOWERDOWN	0x62U
27 
28 /* nPM6001 WDTRIGGERVALUEx ms/LSB, min/max values */
29 #define NPM6001_WDTRIGGERVALUE_MS_LSB 4000U
30 #define NPM6001_WDTRIGGERVALUE_MIN    0x2U
31 #define NPM6001_WDTRIGGERVALUE_MAX    0xFFFFFFU
32 
33 /* nPM6001 WDPWRUPVALUE fields */
34 #define NPM6001_WDPWRUPVALUE_OSC_ENABLE	    BIT(0)
35 #define NPM6001_WDPWRUPVALUE_COUNTER_ENABLE BIT(1)
36 #define NPM6001_WDPWRUPVALUE_LS_ENABLE	    BIT(2)
37 
38 struct wdt_npm6001_config {
39 	struct i2c_dt_spec bus;
40 };
41 
wdt_npm6001_setup(const struct device * dev,uint8_t options)42 static int wdt_npm6001_setup(const struct device *dev, uint8_t options)
43 {
44 	ARG_UNUSED(dev);
45 	ARG_UNUSED(options);
46 
47 	return 0;
48 }
49 
wdt_npm6001_disable(const struct device * dev)50 static int wdt_npm6001_disable(const struct device *dev)
51 {
52 	const struct wdt_npm6001_config *config = dev->config;
53 	uint8_t buf[4] = {NPM6001_WDARMEDVALUE, 1U, NPM6001_WDARMEDSTROBE, 1U};
54 
55 	return i2c_write_dt(&config->bus, buf, sizeof(buf));
56 }
57 
wdt_npm6001_install_timeout(const struct device * dev,const struct wdt_timeout_cfg * timeout)58 static int wdt_npm6001_install_timeout(const struct device *dev,
59 				       const struct wdt_timeout_cfg *timeout)
60 {
61 	const struct wdt_npm6001_config *config = dev->config;
62 	uint32_t window;
63 	uint8_t buf[2];
64 	int ret;
65 
66 	if (timeout->window.min != 0U) {
67 		return -EINVAL;
68 	}
69 
70 	/* round-up timeout, e.g. 5s -> 8s */
71 	window = (((timeout->window.max + NPM6001_WDTRIGGERVALUE_MS_LSB - 1U) /
72 		   NPM6001_WDTRIGGERVALUE_MS_LSB) +
73 		  1U);
74 	if ((window < NPM6001_WDTRIGGERVALUE_MIN) ||
75 	    (window > NPM6001_WDTRIGGERVALUE_MAX)) {
76 		return -EINVAL;
77 	}
78 
79 	/* enable OSC/COUNTER/LS */
80 	buf[0] = NPM6001_WDPWRUPVALUE;
81 	buf[1] = NPM6001_WDPWRUPVALUE_OSC_ENABLE |
82 		 NPM6001_WDPWRUPVALUE_COUNTER_ENABLE |
83 		 NPM6001_WDPWRUPVALUE_LS_ENABLE;
84 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
85 	if (ret < 0) {
86 		return ret;
87 	}
88 
89 	buf[0] = NPM6001_WDPWRUPSTROBE;
90 	buf[1] = 1U;
91 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
92 	if (ret < 0) {
93 		return ret;
94 	}
95 
96 	/* write trigger value */
97 	buf[0] = NPM6001_WDTRIGGERVALUE0;
98 	buf[1] = (uint8_t)window;
99 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
100 	if (ret < 0) {
101 		return ret;
102 	}
103 
104 	buf[0] = NPM6001_WDTRIGGERVALUE1;
105 	buf[1] = (uint8_t)(window >> 8U);
106 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
107 	if (ret < 0) {
108 		return ret;
109 	}
110 
111 	buf[0] = NPM6001_WDTRIGGERVALUE2;
112 	buf[1] = (uint8_t)(window >> 16U);
113 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
114 	if (ret < 0) {
115 		return ret;
116 	}
117 
118 	buf[0] = NPM6001_WDDATASTROBE;
119 	buf[1] = 1U;
120 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
121 	if (ret < 0) {
122 		return ret;
123 	}
124 
125 	/* arm watchdog & kick */
126 	buf[0] = NPM6001_WDARMEDVALUE;
127 	buf[1] = 1U;
128 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
129 	if (ret < 0) {
130 		return ret;
131 	}
132 
133 	buf[0] = NPM6001_WDARMEDSTROBE;
134 	buf[1] = 1U;
135 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
136 	if (ret < 0) {
137 		return ret;
138 	}
139 
140 	buf[0] = NPM6001_WDKICK;
141 	buf[1] = 1U;
142 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf));
143 	if (ret < 0) {
144 		return ret;
145 	}
146 
147 	return 0;
148 }
149 
wdt_npm6001_feed(const struct device * dev,int channel_id)150 static int wdt_npm6001_feed(const struct device *dev, int channel_id)
151 {
152 	const struct wdt_npm6001_config *config = dev->config;
153 	uint8_t buf[2] = {NPM6001_WDKICK, 1U};
154 
155 	ARG_UNUSED(channel_id);
156 
157 	return i2c_write_dt(&config->bus, buf, sizeof(buf));
158 }
159 
160 static const struct wdt_driver_api wdt_npm6001_api = {
161 	.setup = wdt_npm6001_setup,
162 	.disable = wdt_npm6001_disable,
163 	.install_timeout = wdt_npm6001_install_timeout,
164 	.feed = wdt_npm6001_feed,
165 };
166 
wdt_npm6001_init(const struct device * dev)167 static int wdt_npm6001_init(const struct device *dev)
168 {
169 	const struct wdt_npm6001_config *config = dev->config;
170 
171 	if (!device_is_ready(config->bus.bus)) {
172 		return -ENODEV;
173 	}
174 
175 	return 0;
176 }
177 
178 #define WDT_NPM6001_DEFINE(n)                                                  \
179 	static const struct wdt_npm6001_config wdt_npm6001_config##n = {       \
180 		.bus = I2C_DT_SPEC_GET(DT_INST_PARENT(n)),                     \
181 	};                                                                     \
182                                                                                \
183 	DEVICE_DT_INST_DEFINE(n, &wdt_npm6001_init, NULL, NULL,                \
184 			      &wdt_npm6001_config##n, POST_KERNEL,             \
185 			      CONFIG_WDT_NPM6001_INIT_PRIORITY,                \
186 			      &wdt_npm6001_api);
187 
188 DT_INST_FOREACH_STATUS_OKAY(WDT_NPM6001_DEFINE)
189