| /* | 
 |  * Copyright (c) 2022 Nordic Semiconductor ASA | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT nordic_npm6001_wdt | 
 |  | 
 | #include <errno.h> | 
 |  | 
 | #include <zephyr/device.h> | 
 | #include <zephyr/drivers/i2c.h> | 
 | #include <zephyr/drivers/watchdog.h> | 
 | #include <zephyr/sys/util_macro.h> | 
 | #include <zephyr/toolchain.h> | 
 |  | 
 | /* nPM6001 Watchdog related registers */ | 
 | #define NPM6001_WDARMEDVALUE	0x54U | 
 | #define NPM6001_WDARMEDSTROBE	0x55U | 
 | #define NPM6001_WDTRIGGERVALUE0 0x56U | 
 | #define NPM6001_WDTRIGGERVALUE1 0x57U | 
 | #define NPM6001_WDTRIGGERVALUE2 0x58U | 
 | #define NPM6001_WDDATASTROBE	0x5DU | 
 | #define NPM6001_WDPWRUPVALUE	0x5EU | 
 | #define NPM6001_WDPWRUPSTROBE	0x5FU | 
 | #define NPM6001_WDKICK		0x60U | 
 | #define NPM6001_WDREQPOWERDOWN	0x62U | 
 |  | 
 | /* nPM6001 WDTRIGGERVALUEx ms/LSB, min/max values */ | 
 | #define NPM6001_WDTRIGGERVALUE_MS_LSB 4000U | 
 | #define NPM6001_WDTRIGGERVALUE_MIN    0x2U | 
 | #define NPM6001_WDTRIGGERVALUE_MAX    0xFFFFFFU | 
 |  | 
 | /* nPM6001 WDPWRUPVALUE fields */ | 
 | #define NPM6001_WDPWRUPVALUE_OSC_ENABLE	    BIT(0) | 
 | #define NPM6001_WDPWRUPVALUE_COUNTER_ENABLE BIT(1) | 
 | #define NPM6001_WDPWRUPVALUE_LS_ENABLE	    BIT(2) | 
 |  | 
 | struct wdt_npm6001_config { | 
 | 	struct i2c_dt_spec bus; | 
 | }; | 
 |  | 
 | static int wdt_npm6001_setup(const struct device *dev, uint8_t options) | 
 | { | 
 | 	ARG_UNUSED(dev); | 
 | 	ARG_UNUSED(options); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int wdt_npm6001_disable(const struct device *dev) | 
 | { | 
 | 	const struct wdt_npm6001_config *config = dev->config; | 
 | 	uint8_t buf[4] = {NPM6001_WDARMEDVALUE, 1U, NPM6001_WDARMEDSTROBE, 1U}; | 
 |  | 
 | 	return i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | } | 
 |  | 
 | static int wdt_npm6001_install_timeout(const struct device *dev, | 
 | 				       const struct wdt_timeout_cfg *timeout) | 
 | { | 
 | 	const struct wdt_npm6001_config *config = dev->config; | 
 | 	uint32_t window; | 
 | 	uint8_t buf[2]; | 
 | 	int ret; | 
 |  | 
 | 	if (timeout->window.min != 0U) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* round-up timeout, e.g. 5s -> 8s */ | 
 | 	window = (((timeout->window.max + NPM6001_WDTRIGGERVALUE_MS_LSB - 1U) / | 
 | 		   NPM6001_WDTRIGGERVALUE_MS_LSB) + | 
 | 		  1U); | 
 | 	if ((window < NPM6001_WDTRIGGERVALUE_MIN) || | 
 | 	    (window > NPM6001_WDTRIGGERVALUE_MAX)) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	/* enable OSC/COUNTER/LS */ | 
 | 	buf[0] = NPM6001_WDPWRUPVALUE; | 
 | 	buf[1] = NPM6001_WDPWRUPVALUE_OSC_ENABLE | | 
 | 		 NPM6001_WDPWRUPVALUE_COUNTER_ENABLE | | 
 | 		 NPM6001_WDPWRUPVALUE_LS_ENABLE; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDPWRUPSTROBE; | 
 | 	buf[1] = 1U; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* write trigger value */ | 
 | 	buf[0] = NPM6001_WDTRIGGERVALUE0; | 
 | 	buf[1] = (uint8_t)window; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDTRIGGERVALUE1; | 
 | 	buf[1] = (uint8_t)(window >> 8U); | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDTRIGGERVALUE2; | 
 | 	buf[1] = (uint8_t)(window >> 16U); | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDDATASTROBE; | 
 | 	buf[1] = 1U; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	/* arm watchdog & kick */ | 
 | 	buf[0] = NPM6001_WDARMEDVALUE; | 
 | 	buf[1] = 1U; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDARMEDSTROBE; | 
 | 	buf[1] = 1U; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	buf[0] = NPM6001_WDKICK; | 
 | 	buf[1] = 1U; | 
 | 	ret = i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | 	if (ret < 0) { | 
 | 		return ret; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int wdt_npm6001_feed(const struct device *dev, int channel_id) | 
 | { | 
 | 	const struct wdt_npm6001_config *config = dev->config; | 
 | 	uint8_t buf[2] = {NPM6001_WDKICK, 1U}; | 
 |  | 
 | 	ARG_UNUSED(channel_id); | 
 |  | 
 | 	return i2c_write_dt(&config->bus, buf, sizeof(buf)); | 
 | } | 
 |  | 
 | static const struct wdt_driver_api wdt_npm6001_api = { | 
 | 	.setup = wdt_npm6001_setup, | 
 | 	.disable = wdt_npm6001_disable, | 
 | 	.install_timeout = wdt_npm6001_install_timeout, | 
 | 	.feed = wdt_npm6001_feed, | 
 | }; | 
 |  | 
 | static int wdt_npm6001_init(const struct device *dev) | 
 | { | 
 | 	const struct wdt_npm6001_config *config = dev->config; | 
 |  | 
 | 	if (!device_is_ready(config->bus.bus)) { | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | #define WDT_NPM6001_DEFINE(n)                                                  \ | 
 | 	static const struct wdt_npm6001_config wdt_npm6001_config##n = {       \ | 
 | 		.bus = I2C_DT_SPEC_GET(DT_INST_PARENT(n)),                     \ | 
 | 	};                                                                     \ | 
 |                                                                                \ | 
 | 	DEVICE_DT_INST_DEFINE(n, &wdt_npm6001_init, NULL, NULL,                \ | 
 | 			      &wdt_npm6001_config##n, POST_KERNEL,             \ | 
 | 			      CONFIG_WDT_NPM6001_INIT_PRIORITY,                \ | 
 | 			      &wdt_npm6001_api); | 
 |  | 
 | DT_INST_FOREACH_STATUS_OKAY(WDT_NPM6001_DEFINE) |