drivers: bbram: Add Microchip MCP7940N driver

Adds Microchip MCP7940N battery-backed RAM support.

Signed-off-by: Jamie McCrae <jamie.mccrae@nordicsemi.no>
This commit is contained in:
Jamie McCrae 2023-02-24 12:12:43 +00:00 committed by Carles Cufí
commit f9fd899da0
5 changed files with 256 additions and 2 deletions

View file

@ -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)

View file

@ -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"

View file

@ -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.

View file

@ -0,0 +1,238 @@
/*
* 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 ((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, \
&microchip_mcp7940n_bbram_init, \
NULL, \
&microchip_mcp7940n_bbram_data_##inst, \
&microchip_mcp7940n_bbram_config_##inst, \
POST_KERNEL, \
CONFIG_BBRAM_INIT_PRIORITY, \
&microchip_mcp7940n_bbram_api);
DT_INST_FOREACH_STATUS_OKAY(MICROCHIP_MCP7940N_BBRAM_DEVICE)

View file

@ -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.