| /* | 
 |  * Copyright (c) 2023, Nordic Semiconductor ASA | 
 |  * | 
 |  * SPDX-License-Identifier: Apache-2.0 | 
 |  */ | 
 |  | 
 | #define DT_DRV_COMPAT microchip_mcp7940n | 
 |  | 
 | #include <string.h> | 
 | #include <zephyr/device.h> | 
 | #include <zephyr/devicetree.h> | 
 | #include <zephyr/drivers/i2c.h> | 
 | #include <zephyr/drivers/bbram.h> | 
 | #include <zephyr/kernel.h> | 
 | #include <zephyr/sys/util.h> | 
 | #include <zephyr/logging/log.h> | 
 |  | 
 | LOG_MODULE_REGISTER(bbram_microchip_mcp7940n, CONFIG_BBRAM_LOG_LEVEL); | 
 |  | 
 | #define MICROCHIP_MCP7940N_SRAM_OFFSET 0x20 | 
 | #define MICROCHIP_MCP7940N_SRAM_SIZE 64 | 
 | #define MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS 0x03 | 
 | #define MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT BIT(3) | 
 | #define MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT BIT(4) | 
 |  | 
 | struct microchip_mcp7940n_bbram_data { | 
 | 	struct k_mutex lock; | 
 | }; | 
 |  | 
 | struct microchip_mcp7940n_bbram_config { | 
 | 	struct i2c_dt_spec i2c; | 
 | }; | 
 |  | 
 | static int microchip_mcp7940n_bbram_init(const struct device *dev) | 
 | { | 
 | 	const struct microchip_mcp7940n_bbram_config *config = dev->config; | 
 | 	struct microchip_mcp7940n_bbram_data *data = dev->data; | 
 | 	int32_t rc = 0; | 
 | 	uint8_t buffer; | 
 |  | 
 | 	if (!device_is_ready(config->i2c.bus)) { | 
 | 		LOG_ERR("I2C device %s is not ready", config->i2c.bus->name); | 
 | 		return -ENODEV; | 
 | 	} | 
 |  | 
 | 	k_mutex_init(&data->lock); | 
 |  | 
 | 	rc = i2c_reg_read_byte_dt(&config->i2c, | 
 | 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS, | 
 | 				  &buffer); | 
 |  | 
 | 	if (rc != 0) { | 
 | 		LOG_ERR("Failed to read RTCWKDAY register: %d", rc); | 
 | 	} | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int microchip_mcp7940n_bbram_size(const struct device *dev, size_t *size) | 
 | { | 
 | 	*size = MICROCHIP_MCP7940N_SRAM_SIZE; | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static int microchip_mcp7940n_bbram_is_invalid(const struct device *dev) | 
 | { | 
 | 	const struct microchip_mcp7940n_bbram_config *config = dev->config; | 
 | 	struct microchip_mcp7940n_bbram_data *data = dev->data; | 
 | 	int32_t rc = 0; | 
 | 	uint8_t buffer; | 
 | 	bool data_valid = true; | 
 |  | 
 | 	k_mutex_lock(&data->lock, K_FOREVER); | 
 |  | 
 | 	rc = i2c_reg_read_byte_dt(&config->i2c, | 
 | 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS, | 
 | 				  &buffer); | 
 |  | 
 |  | 
 | 	if ((buffer & MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT)) { | 
 | 		data_valid = false; | 
 |  | 
 | 		buffer &= (buffer ^ MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT); | 
 |  | 
 | 		rc = i2c_reg_write_byte_dt(&config->i2c, | 
 | 					   MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS, | 
 | 					   buffer); | 
 |  | 
 | 		if (rc != 0) { | 
 | 			LOG_ERR("Failed to write RTCWKDAY register: %d", rc); | 
 | 			goto finish; | 
 | 		} | 
 |  | 
 | 	} | 
 |  | 
 | finish: | 
 | 	k_mutex_unlock(&data->lock); | 
 |  | 
 | 	if (rc == 0 && data_valid == true) { | 
 | 		rc = 1; | 
 | 	} | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int microchip_mcp7940n_bbram_check_standby_power(const struct device *dev) | 
 | { | 
 | 	const struct microchip_mcp7940n_bbram_config *config = dev->config; | 
 | 	struct microchip_mcp7940n_bbram_data *data = dev->data; | 
 | 	int32_t rc = 0; | 
 | 	uint8_t buffer; | 
 | 	bool power_enabled = true; | 
 |  | 
 | 	k_mutex_lock(&data->lock, K_FOREVER); | 
 |  | 
 | 	rc = i2c_reg_read_byte_dt(&config->i2c, | 
 | 				  MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS, | 
 | 				  &buffer); | 
 |  | 
 |  | 
 | 	if (!(buffer & MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT)) { | 
 | 		power_enabled = false; | 
 |  | 
 | 		buffer |= MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT; | 
 |  | 
 | 		rc = i2c_reg_write_byte_dt(&config->i2c, | 
 | 					   MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS, | 
 | 					   buffer); | 
 |  | 
 | 		if (rc != 0) { | 
 | 			LOG_ERR("Failed to write RTCWKDAY register: %d", rc); | 
 | 			goto finish; | 
 | 		} | 
 |  | 
 | 	} | 
 |  | 
 | finish: | 
 | 	k_mutex_unlock(&data->lock); | 
 |  | 
 | 	if (rc == 0 && power_enabled == true) { | 
 | 		rc = 1; | 
 | 	} | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int microchip_mcp7940n_bbram_read(const struct device *dev, size_t offset, size_t size, | 
 | 					 uint8_t *buffer) | 
 | { | 
 | 	const struct microchip_mcp7940n_bbram_config *config = dev->config; | 
 | 	struct microchip_mcp7940n_bbram_data *data = dev->data; | 
 | 	size_t i = 0; | 
 | 	int32_t rc = 0; | 
 |  | 
 | 	if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	k_mutex_lock(&data->lock, K_FOREVER); | 
 |  | 
 | 	while (i < size) { | 
 | 		LOG_DBG("Read from 0x%x", (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i)); | 
 | 		rc = i2c_reg_read_byte_dt(&config->i2c, | 
 | 					  (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i), | 
 | 					  &buffer[i]); | 
 |  | 
 | 		if (rc != 0) { | 
 | 			goto finish; | 
 | 		} | 
 |  | 
 | 		++i; | 
 | 	} | 
 |  | 
 | finish: | 
 | 	k_mutex_unlock(&data->lock); | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static int microchip_mcp7940n_bbram_write(const struct device *dev, size_t offset, size_t size, | 
 | 					  const uint8_t *buffer) | 
 | { | 
 | 	const struct microchip_mcp7940n_bbram_config *config = dev->config; | 
 | 	struct microchip_mcp7940n_bbram_data *data = dev->data; | 
 | 	size_t i = 0; | 
 | 	int32_t rc = 0; | 
 |  | 
 | 	if (size == 0 || (offset + size) > MICROCHIP_MCP7940N_SRAM_SIZE) { | 
 | 		return -EINVAL; | 
 | 	} | 
 |  | 
 | 	k_mutex_lock(&data->lock, K_FOREVER); | 
 |  | 
 | 	while (i < size) { | 
 | 		LOG_DBG("Write 0x%x to 0x%x", buffer[i], | 
 | 			(MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i)); | 
 | 		rc = i2c_reg_write_byte_dt(&config->i2c, | 
 | 					   (MICROCHIP_MCP7940N_SRAM_OFFSET + offset + i), | 
 | 					   buffer[i]); | 
 |  | 
 | 		if (rc != 0) { | 
 | 			goto finish; | 
 | 		} | 
 |  | 
 | 		++i; | 
 | 	} | 
 |  | 
 | finish: | 
 | 	k_mutex_unlock(&data->lock); | 
 |  | 
 | 	return rc; | 
 | } | 
 |  | 
 | static const struct bbram_driver_api microchip_mcp7940n_bbram_api = { | 
 | 	.get_size = microchip_mcp7940n_bbram_size, | 
 | 	.check_invalid = microchip_mcp7940n_bbram_is_invalid, | 
 | 	.check_standby_power = microchip_mcp7940n_bbram_check_standby_power, | 
 | 	.read = microchip_mcp7940n_bbram_read, | 
 | 	.write = microchip_mcp7940n_bbram_write, | 
 | }; | 
 |  | 
 | #define MICROCHIP_MCP7940N_BBRAM_DEVICE(inst)							\ | 
 | 	static struct microchip_mcp7940n_bbram_data microchip_mcp7940n_bbram_data_##inst;	\ | 
 | 	static const struct microchip_mcp7940n_bbram_config					\ | 
 | 		microchip_mcp7940n_bbram_config_##inst = {					\ | 
 | 		.i2c = I2C_DT_SPEC_INST_GET(inst),						\ | 
 | 	};											\ | 
 | 	DEVICE_DT_INST_DEFINE(inst,								\ | 
 | 			      µchip_mcp7940n_bbram_init,					\ | 
 | 			      NULL,								\ | 
 | 			      µchip_mcp7940n_bbram_data_##inst,				\ | 
 | 			      µchip_mcp7940n_bbram_config_##inst,				\ | 
 | 			      POST_KERNEL,							\ | 
 | 			      CONFIG_BBRAM_INIT_PRIORITY,					\ | 
 | 			      µchip_mcp7940n_bbram_api); | 
 |  | 
 | DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_MCP7940N_BBRAM_DEVICE) |