1 /*
2  * Copyright (c) 2024 Nordic Semiconductor ASA
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT nordic_npm2100
7 
8 #include <errno.h>
9 
10 #include <zephyr/drivers/i2c.h>
11 #include <zephyr/sys/util.h>
12 #include <zephyr/sys/byteorder.h>
13 #include <zephyr/drivers/gpio.h>
14 #include <zephyr/drivers/gpio/gpio_utils.h>
15 #include <zephyr/drivers/mfd/npm2100.h>
16 
17 #define EVENTS_SET            0x00U
18 #define EVENTS_CLR            0x05U
19 #define INTEN_SET             0x0AU
20 #define GPIO_CONFIG           0x80U
21 #define GPIO_USAGE            0x83U
22 #define TIMER_TASKS_START     0xB0U
23 #define TIMER_CONFIG          0xB3U
24 #define TIMER_TARGET          0xB4U
25 #define TIMER_STATUS          0xB7U
26 #define SHPHLD_WAKEUP         0xC1U
27 #define SHPHLD_SHPHLD         0xC2U
28 #define HIBERNATE_TASKS_HIBER 0xC8U
29 #define RESET_TASKS_RESET     0xD0U
30 #define RESET_BUTTON          0xD2U
31 #define RESET_PIN             0xD3U
32 #define RESET_WRITESTICKY     0xDBU
33 #define RESET_STROBESTICKY    0xDCU
34 
35 #define SHPHLD_RESISTOR_MASK     0x03U
36 #define SHPHLD_RESISTOR_PULLUP   0x00U
37 #define SHPHLD_RESISTOR_NONE     0x01U
38 #define SHPHLD_RESISTOR_PULLDOWN 0x02U
39 #define SHPHLD_CURR_MASK         0x0CU
40 #define SHPHLD_PULL_ENABLE       0x10U
41 
42 #define WAKEUP_EDGE_FALLING    0x00U
43 #define WAKEUP_EDGE_RISING     0x01U
44 #define WAKEUP_HIBERNATE_PIN   0x00U
45 #define WAKEUP_HIBERNATE_NOPIN 0x02U
46 
47 #define TIMER_CONFIG_WKUP 3U
48 
49 #define TIMER_STATUS_IDLE 0U
50 
51 #define TIMER_PRESCALER_MUL 64ULL
52 #define TIMER_PRESCALER_DIV 1000ULL
53 #define TIMER_MAX           0xFFFFFFU
54 
55 #define EVENTS_SIZE 5U
56 
57 #define GPIO_USAGE_INTLO   0x01U
58 #define GPIO_USAGE_INTHI   0x02U
59 #define GPIO_CONFIG_OUTPUT 0x02U
60 
61 #define RESET_STICKY_PWRBUT 0x04U
62 
63 #define SHPHLD_LONGPRESS_SHIP    0
64 #define SHPHLD_LONGPRESS_DISABLE 1
65 #define SHPHLD_LONGPRESS_RESET   2
66 
67 struct mfd_npm2100_config {
68 	struct i2c_dt_spec i2c;
69 	struct gpio_dt_spec host_int_gpios;
70 	gpio_flags_t host_int_flags;
71 	gpio_pin_t pmic_int_pin;
72 	gpio_flags_t pmic_int_flags;
73 	gpio_flags_t shiphold_flags;
74 	uint8_t shiphold_longpress;
75 	uint8_t shiphold_current;
76 	uint8_t shiphold_hibernate_wakeup;
77 };
78 
79 struct mfd_npm2100_data {
80 	const struct device *dev;
81 	struct gpio_callback gpio_cb;
82 	struct k_work work;
83 	sys_slist_t callbacks;
84 };
85 
86 struct event_reg_t {
87 	uint8_t offset;
88 	uint8_t mask;
89 };
90 
91 static const struct event_reg_t event_reg[NPM2100_EVENT_MAX] = {
92 	[NPM2100_EVENT_SYS_DIETEMP_WARN] = {0x00U, 0x01U},
93 	[NPM2100_EVENT_SYS_SHIPHOLD_FALL] = {0x00U, 0x02U},
94 	[NPM2100_EVENT_SYS_SHIPHOLD_RISE] = {0x00U, 0x04U},
95 	[NPM2100_EVENT_SYS_PGRESET_FALL] = {0x00U, 0x08U},
96 	[NPM2100_EVENT_SYS_PGRESET_RISE] = {0x00U, 0x10U},
97 	[NPM2100_EVENT_SYS_TIMER_EXPIRY] = {0x00U, 0x20U},
98 	[NPM2100_EVENT_ADC_VBAT_READY] = {0x01U, 0x01U},
99 	[NPM2100_EVENT_ADC_DIETEMP_READY] = {0x01U, 0x02U},
100 	[NPM2100_EVENT_ADC_DROOP_DETECT] = {0x01U, 0x04U},
101 	[NPM2100_EVENT_ADC_VOUT_READY] = {0x01U, 0x08U},
102 	[NPM2100_EVENT_GPIO0_FALL] = {0x02U, 0x01U},
103 	[NPM2100_EVENT_GPIO0_RISE] = {0x02U, 0x02U},
104 	[NPM2100_EVENT_GPIO1_FALL] = {0x02U, 0x04U},
105 	[NPM2100_EVENT_GPIO1_RISE] = {0x02U, 0x08U},
106 	[NPM2100_EVENT_BOOST_VBAT_WARN] = {0x03U, 0x01U},
107 	[NPM2100_EVENT_BOOST_VOUT_MIN] = {0x03U, 0x02U},
108 	[NPM2100_EVENT_BOOST_VOUT_WARN] = {0x03U, 0x04U},
109 	[NPM2100_EVENT_BOOST_VOUT_DPS] = {0x03U, 0x08U},
110 	[NPM2100_EVENT_BOOST_VOUT_OK] = {0x03U, 0x10U},
111 	[NPM2100_EVENT_LDOSW_OCP] = {0x04U, 0x01U},
112 	[NPM2100_EVENT_LDOSW_VINTFAIL] = {0x04U, 0x02U},
113 };
114 
gpio_callback(const struct device * dev,struct gpio_callback * cb,uint32_t pins)115 static void gpio_callback(const struct device *dev, struct gpio_callback *cb, uint32_t pins)
116 {
117 	struct mfd_npm2100_data *data = CONTAINER_OF(cb, struct mfd_npm2100_data, gpio_cb);
118 	const struct mfd_npm2100_config *config = data->dev->config;
119 
120 	if (config->host_int_flags & GPIO_INT_LEVEL_ACTIVE) {
121 		/* When using level irq, disable until it can be cleared in the work callback */
122 		gpio_pin_interrupt_configure_dt(&config->host_int_gpios, GPIO_INT_DISABLE);
123 	}
124 
125 	k_work_submit(&data->work);
126 }
127 
work_callback(struct k_work * work)128 static void work_callback(struct k_work *work)
129 {
130 	struct mfd_npm2100_data *data = CONTAINER_OF(work, struct mfd_npm2100_data, work);
131 	const struct mfd_npm2100_config *config = data->dev->config;
132 	uint8_t buf[EVENTS_SIZE + 1U] = {EVENTS_SET};
133 	int ret;
134 
135 	/* Read MAIN SET registers into buffer, leaving space for register address */
136 	ret = i2c_write_read_dt(&config->i2c, &buf[0], 1U, &buf[1], EVENTS_SIZE);
137 	if (ret < 0) {
138 		k_work_submit(&data->work);
139 		goto enable_irq;
140 	}
141 
142 	for (int i = 0; i < NPM2100_EVENT_MAX; i++) {
143 		if ((buf[event_reg[i].offset + 1U] & event_reg[i].mask) != 0U) {
144 			gpio_fire_callbacks(&data->callbacks, data->dev, BIT(i));
145 		}
146 	}
147 
148 	/* Write read buffer back to clear registers to clear all processed events */
149 	buf[0] = EVENTS_CLR;
150 	ret = i2c_write_dt(&config->i2c, buf, EVENTS_SIZE + 1U);
151 	if (ret < 0) {
152 		k_work_submit(&data->work);
153 		goto enable_irq;
154 	}
155 
156 	/* Resubmit handler to queue if interrupt is still active */
157 	if (gpio_pin_get_dt(&config->host_int_gpios) != 0) {
158 		k_work_submit(&data->work);
159 	}
160 
161 enable_irq:
162 
163 	if (config->host_int_flags & GPIO_INT_LEVEL_ACTIVE) {
164 		/* Re-enable irq */
165 		gpio_pin_interrupt_configure_dt(&config->host_int_gpios, config->host_int_flags);
166 	}
167 }
168 
config_pmic_int(const struct device * dev)169 static int config_pmic_int(const struct device *dev)
170 {
171 	const struct mfd_npm2100_config *config = dev->config;
172 	uint8_t usage = GPIO_USAGE_INTHI;
173 
174 	if (config->pmic_int_flags & GPIO_ACTIVE_LOW) {
175 		usage = GPIO_USAGE_INTLO;
176 	}
177 
178 	/* Set specified PMIC pin to be interrupt output */
179 	int ret = i2c_reg_write_byte_dt(&config->i2c, GPIO_USAGE + config->pmic_int_pin, usage);
180 
181 	if (ret < 0) {
182 		return ret;
183 	}
184 
185 	/* Configure PMIC output pin */
186 	return i2c_reg_write_byte_dt(&config->i2c, GPIO_CONFIG + config->pmic_int_pin,
187 				     GPIO_CONFIG_OUTPUT);
188 }
189 
config_shphold(const struct device * dev)190 static int config_shphold(const struct device *dev)
191 {
192 	const struct mfd_npm2100_config *config = dev->config;
193 	uint8_t reg;
194 	int ret;
195 
196 	if (config->shiphold_longpress != SHPHLD_LONGPRESS_SHIP) {
197 		ret = i2c_reg_write_byte_dt(&config->i2c, RESET_WRITESTICKY, RESET_STICKY_PWRBUT);
198 		if (ret < 0) {
199 			return ret;
200 		}
201 
202 		ret = i2c_reg_write_byte_dt(&config->i2c, RESET_STROBESTICKY, 1U);
203 		if (ret < 0) {
204 			return ret;
205 		}
206 
207 		if (config->shiphold_longpress == SHPHLD_LONGPRESS_RESET) {
208 			ret = i2c_reg_write_byte_dt(&config->i2c, RESET_BUTTON, 0U);
209 			if (ret < 0) {
210 				return ret;
211 			}
212 
213 			ret = i2c_reg_write_byte_dt(&config->i2c, RESET_PIN, 1U);
214 			if (ret < 0) {
215 				return ret;
216 			}
217 		}
218 	}
219 
220 	reg = config->shiphold_hibernate_wakeup ? WAKEUP_HIBERNATE_PIN : WAKEUP_HIBERNATE_NOPIN;
221 	if ((config->shiphold_flags & GPIO_ACTIVE_HIGH) != 0U) {
222 		reg |= WAKEUP_EDGE_RISING;
223 	}
224 
225 	ret = i2c_reg_write_byte_dt(&config->i2c, SHPHLD_WAKEUP, reg);
226 	if (ret < 0) {
227 		return ret;
228 	}
229 
230 	if ((config->shiphold_flags & GPIO_PULL_UP) != 0U) {
231 		reg = SHPHLD_RESISTOR_PULLUP;
232 	} else if ((config->shiphold_flags & GPIO_PULL_DOWN) != 0U) {
233 		reg = SHPHLD_RESISTOR_PULLDOWN;
234 	} else {
235 		reg = SHPHLD_RESISTOR_NONE;
236 	}
237 	if (config->shiphold_current != 0U) {
238 		reg |= FIELD_PREP(SHPHLD_CURR_MASK, (config->shiphold_current - 1U));
239 		reg |= SHPHLD_PULL_ENABLE;
240 	}
241 
242 	return i2c_reg_write_byte_dt(&config->i2c, SHPHLD_SHPHLD, reg);
243 }
244 
mfd_npm2100_init(const struct device * dev)245 static int mfd_npm2100_init(const struct device *dev)
246 {
247 	const struct mfd_npm2100_config *config = dev->config;
248 	struct mfd_npm2100_data *mfd_data = dev->data;
249 	int ret;
250 
251 	if (!i2c_is_ready_dt(&config->i2c)) {
252 		return -ENODEV;
253 	}
254 
255 	mfd_data->dev = dev;
256 
257 	ret = config_shphold(dev);
258 	if (ret < 0) {
259 		return ret;
260 	}
261 
262 	if (config->host_int_gpios.port == NULL) {
263 		return 0;
264 	}
265 
266 	ret = config_pmic_int(dev);
267 	if (ret < 0) {
268 		return ret;
269 	}
270 
271 	/* Configure host interrupt GPIO */
272 	if (!gpio_is_ready_dt(&config->host_int_gpios)) {
273 		return -ENODEV;
274 	}
275 
276 	ret = gpio_pin_configure_dt(&config->host_int_gpios, GPIO_INPUT);
277 	if (ret < 0) {
278 		return ret;
279 	}
280 
281 	gpio_init_callback(&mfd_data->gpio_cb, gpio_callback, BIT(config->host_int_gpios.pin));
282 
283 	ret = gpio_add_callback_dt(&config->host_int_gpios, &mfd_data->gpio_cb);
284 	if (ret < 0) {
285 		return ret;
286 	}
287 
288 	mfd_data->work.handler = work_callback;
289 
290 	return gpio_pin_interrupt_configure_dt(&config->host_int_gpios, config->host_int_flags);
291 }
292 
mfd_npm2100_set_timer(const struct device * dev,uint32_t time_ms,enum mfd_npm2100_timer_mode mode)293 int mfd_npm2100_set_timer(const struct device *dev, uint32_t time_ms,
294 			  enum mfd_npm2100_timer_mode mode)
295 {
296 	const struct mfd_npm2100_config *config = dev->config;
297 	uint8_t buff[4] = {TIMER_TARGET};
298 	uint32_t ticks = (uint32_t)DIV_ROUND_CLOSEST(((uint64_t)time_ms * TIMER_PRESCALER_MUL),
299 						     TIMER_PRESCALER_DIV);
300 	uint8_t timer_status;
301 	int ret;
302 
303 	if (ticks > TIMER_MAX) {
304 		return -EINVAL;
305 	}
306 
307 	ret = i2c_reg_read_byte_dt(&config->i2c, TIMER_STATUS, &timer_status);
308 	if (ret < 0) {
309 		return ret;
310 	}
311 
312 	if (timer_status != TIMER_STATUS_IDLE) {
313 		return -EBUSY;
314 	}
315 
316 	sys_put_be24(ticks, &buff[1]);
317 
318 	ret = i2c_write_dt(&config->i2c, buff, sizeof(buff));
319 	if (ret < 0) {
320 		return ret;
321 	}
322 
323 	return i2c_reg_write_byte_dt(&config->i2c, TIMER_CONFIG, mode);
324 }
325 
mfd_npm2100_start_timer(const struct device * dev)326 int mfd_npm2100_start_timer(const struct device *dev)
327 {
328 	const struct mfd_npm2100_config *config = dev->config;
329 
330 	return i2c_reg_write_byte_dt(&config->i2c, TIMER_TASKS_START, 1U);
331 }
332 
mfd_npm2100_reset(const struct device * dev)333 int mfd_npm2100_reset(const struct device *dev)
334 {
335 	const struct mfd_npm2100_config *config = dev->config;
336 
337 	return i2c_reg_write_byte_dt(&config->i2c, RESET_TASKS_RESET, 1U);
338 }
339 
mfd_npm2100_hibernate(const struct device * dev,uint32_t time_ms)340 int mfd_npm2100_hibernate(const struct device *dev, uint32_t time_ms)
341 {
342 	const struct mfd_npm2100_config *config = dev->config;
343 	int ret;
344 
345 	if (time_ms > 0) {
346 		ret = mfd_npm2100_set_timer(dev, time_ms, NPM2100_TIMER_MODE_WAKEUP);
347 		if (ret < 0) {
348 			return ret;
349 		}
350 
351 		ret = mfd_npm2100_start_timer(dev);
352 		if (ret < 0) {
353 			return ret;
354 		}
355 	}
356 
357 	/* Ensure shiphold button is enabled so that wakeup will work */
358 	ret = i2c_reg_write_byte_dt(&config->i2c, RESET_WRITESTICKY, 0);
359 	if (ret < 0) {
360 		return ret;
361 	}
362 
363 	ret = i2c_reg_write_byte_dt(&config->i2c, RESET_STROBESTICKY, 1U);
364 	if (ret < 0) {
365 		return ret;
366 	}
367 
368 	return i2c_reg_write_byte_dt(&config->i2c, HIBERNATE_TASKS_HIBER, 1U);
369 }
370 
mfd_npm2100_add_callback(const struct device * dev,struct gpio_callback * callback)371 int mfd_npm2100_add_callback(const struct device *dev, struct gpio_callback *callback)
372 {
373 	const struct mfd_npm2100_config *config = dev->config;
374 	struct mfd_npm2100_data *data = dev->data;
375 
376 	/* Enable interrupts for specified events */
377 	for (int i = 0; i < NPM2100_EVENT_MAX; i++) {
378 		if ((callback->pin_mask & BIT(i)) != 0U) {
379 			/* Clear pending interrupt */
380 			int ret = i2c_reg_write_byte_dt(
381 				&config->i2c, event_reg[i].offset + EVENTS_CLR, event_reg[i].mask);
382 
383 			if (ret < 0) {
384 				return ret;
385 			}
386 
387 			ret = i2c_reg_write_byte_dt(&config->i2c, event_reg[i].offset + INTEN_SET,
388 						    event_reg[i].mask);
389 			if (ret < 0) {
390 				return ret;
391 			}
392 		}
393 	}
394 
395 	return gpio_manage_callback(&data->callbacks, callback, true);
396 }
397 
mfd_npm2100_remove_callback(const struct device * dev,struct gpio_callback * callback)398 int mfd_npm2100_remove_callback(const struct device *dev, struct gpio_callback *callback)
399 {
400 	struct mfd_npm2100_data *data = dev->data;
401 
402 	return gpio_manage_callback(&data->callbacks, callback, false);
403 }
404 
405 #define MFD_NPM2100_DEFINE(inst)                                                                   \
406 	static struct mfd_npm2100_data data##inst;                                                 \
407                                                                                                    \
408 	static const struct mfd_npm2100_config config##inst = {                                    \
409 		.i2c = I2C_DT_SPEC_INST_GET(inst),                                                 \
410 		.host_int_gpios = GPIO_DT_SPEC_INST_GET_OR(inst, host_int_gpios, {0}),             \
411 		.host_int_flags = DT_INST_ENUM_IDX_OR(inst, host_int_type, 0) == 0                 \
412 					  ? GPIO_INT_EDGE_TO_ACTIVE                                \
413 					  : GPIO_INT_LEVEL_ACTIVE,                                 \
414 		.pmic_int_pin = DT_INST_PROP_OR(inst, pmic_int_pin, 0),                            \
415 		.pmic_int_flags = DT_INST_PROP_OR(inst, pmic_int_flags, 0),                        \
416 		.shiphold_flags =                                                                  \
417 			DT_INST_PROP_OR(inst, shiphold_flags, (GPIO_ACTIVE_LOW | GPIO_PULL_UP)),   \
418 		.shiphold_longpress = DT_INST_ENUM_IDX_OR(inst, shiphold_longpress, 0),            \
419 		.shiphold_current = DT_INST_ENUM_IDX_OR(inst, shiphold_current, 0),                \
420 		.shiphold_hibernate_wakeup = DT_INST_PROP_OR(inst, shiphold_hibernate_wakeup, 0),  \
421 	};                                                                                         \
422                                                                                                    \
423 	DEVICE_DT_INST_DEFINE(inst, mfd_npm2100_init, NULL, &data##inst, &config##inst,            \
424 			      POST_KERNEL, CONFIG_MFD_INIT_PRIORITY, NULL);
425 
426 DT_INST_FOREACH_STATUS_OKAY(MFD_NPM2100_DEFINE)
427