| /* |
| * 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) |