From f9fd899da0c9185f601d10f8cc814417bddedf3b Mon Sep 17 00:00:00 2001 From: Jamie McCrae Date: Fri, 24 Feb 2023 12:12:43 +0000 Subject: [PATCH] drivers: bbram: Add Microchip MCP7940N driver Adds Microchip MCP7940N battery-backed RAM support. Signed-off-by: Jamie McCrae --- drivers/bbram/CMakeLists.txt | 1 + drivers/bbram/Kconfig | 5 + drivers/bbram/Kconfig.microchip | 10 + drivers/bbram/bbram_microchip_mcp7940n.c | 238 +++++++++++++++++++++++ dts/bindings/rtc/microchip,mcp7940n.yaml | 4 +- 5 files changed, 256 insertions(+), 2 deletions(-) create mode 100644 drivers/bbram/Kconfig.microchip create mode 100644 drivers/bbram/bbram_microchip_mcp7940n.c diff --git a/drivers/bbram/CMakeLists.txt b/drivers/bbram/CMakeLists.txt index 425d0482bc0..98eecf40371 100644 --- a/drivers/bbram/CMakeLists.txt +++ b/drivers/bbram/CMakeLists.txt @@ -8,5 +8,6 @@ zephyr_library_sources_ifdef(CONFIG_BBRAM_SHELL bbram_shell.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_NPCX bbram_npcx.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_IT8XXX2 bbram_it8xxx2.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_EMUL bbram_emul.c) +zephyr_library_sources_ifdef(CONFIG_BBRAM_MICROCHIP_MCP7940N bbram_microchip_mcp7940n.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_XEC bbram_xec.c) zephyr_library_sources_ifdef(CONFIG_BBRAM_STM32 bbram_stm32.c) diff --git a/drivers/bbram/Kconfig b/drivers/bbram/Kconfig index a6815905f85..84cebf27ec4 100644 --- a/drivers/bbram/Kconfig +++ b/drivers/bbram/Kconfig @@ -23,6 +23,9 @@ config BBRAM_INIT_PRIORITY # In STM32, BBRAM is a part of RTC. In this case init priority must be # lower than COUNTER_INIT_PRIORITY. default 65 if BBRAM_STM32 + # MCP7940N is an I2C device, therefore the init priority must be + # greater than I2C_INIT_PRIORITY. + default 55 if BBRAM_MICROCHIP_MCP7940N default 10 help BBRAM driver initialization priority @@ -33,6 +36,8 @@ source "drivers/bbram/Kconfig.it8xxx2" source "drivers/bbram/Kconfig.bbram_emul" +source "drivers/bbram/Kconfig.microchip" + source "drivers/bbram/Kconfig.xec" source "drivers/bbram/Kconfig.stm32" diff --git a/drivers/bbram/Kconfig.microchip b/drivers/bbram/Kconfig.microchip new file mode 100644 index 00000000000..015b2bcb124 --- /dev/null +++ b/drivers/bbram/Kconfig.microchip @@ -0,0 +1,10 @@ +# Copyright (c) 2023 Nordic Semiconductor ASA +# SPDX-License-Identifier: Apache-2.0 + +config BBRAM_MICROCHIP_MCP7940N + bool "Microchip MCP7940N SRAM BBRAM driver" + default y + depends on DT_HAS_MICROCHIP_MCP7940N_ENABLED + select I2C + help + Enable driver for Microchip MCP7940N SRAM based battery-backed RAM. diff --git a/drivers/bbram/bbram_microchip_mcp7940n.c b/drivers/bbram/bbram_microchip_mcp7940n.c new file mode 100644 index 00000000000..c3b538c842f --- /dev/null +++ b/drivers/bbram/bbram_microchip_mcp7940n.c @@ -0,0 +1,238 @@ +/* + * Copyright (c) 2023, Nordic Semiconductor ASA + * + * SPDX-License-Identifier: Apache-2.0 + */ + +#define DT_DRV_COMPAT microchip_mcp7940n + +#include +#include +#include +#include +#include +#include +#include +#include + +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 ((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 ((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) diff --git a/dts/bindings/rtc/microchip,mcp7940n.yaml b/dts/bindings/rtc/microchip,mcp7940n.yaml index 5b436755aa9..cf3ae7921e8 100644 --- a/dts/bindings/rtc/microchip,mcp7940n.yaml +++ b/dts/bindings/rtc/microchip,mcp7940n.yaml @@ -1,10 +1,11 @@ # # Copyright (c) 2021 Laird Connectivity +# Copyright (c) 2023 Nordic Semiconductor ASA # # SPDX-License-Identifier: Apache-2.0 # -description: Microchip MCP7940N I2C RTC +description: Microchip MCP7940N I2C RTC with battery-backed SRAM compatible: "microchip,mcp7940n" @@ -17,7 +18,6 @@ properties: int-gpios: type: phandle-array description: | - Host input connected to the MCP7940N MFP open drain output pin Notifies when an alarm has triggered by asserting this line.