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