/* * Copyright 2024 Google LLC * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT microchip_mcp7940n #include #include #include #include #include #include LOG_MODULE_DECLARE(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 mcp7940n_emul_cfg { }; struct mcp7940n_emul_data { uint8_t rtcwkday; uint8_t data[MICROCHIP_MCP7940N_SRAM_SIZE]; }; static int mcp7940n_emul_init(const struct emul *target, const struct device *parent) { ARG_UNUSED(target); ARG_UNUSED(parent); return 0; } static int mcp7940n_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs, int addr) { struct mcp7940n_emul_data *data = target->data; i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false); if (num_msgs < 1) { LOG_ERR("Invalid number of messages: %d", num_msgs); return -EIO; } if (FIELD_GET(I2C_MSG_READ, msgs->flags)) { LOG_ERR("Unexpected read"); return -EIO; } if (msgs->len < 1) { LOG_ERR("Unexpected msg0 length %d", msgs->len); return -EIO; } uint8_t regn = msgs->buf[0]; bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1; bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1; if (!is_stop && !is_read) { /* First message was a write with the register number, check next message */ msgs++; is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1; is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1; } if (is_read) { /* Read data */ if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) { msgs->buf[0] = data->rtcwkday; return 0; } if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET && regn + msgs->len <= MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) { for (int i = 0; i < msgs->len; ++i) { msgs->buf[i] = data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET]; } return 0; } } else { /* Write data */ if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) { data->rtcwkday = msgs->buf[1]; return 0; } if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET && regn + msgs->len - 1 <= MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) { for (int i = 0; i < msgs->len; ++i) { data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET] = msgs->buf[1 + i]; } return 0; } } return -EIO; } static const struct i2c_emul_api mcp7940n_emul_api_i2c = { .transfer = mcp7940n_emul_transfer_i2c, }; static int mcp7940n_emul_backend_set_data(const struct emul *target, size_t offset, size_t count, const uint8_t *buffer) { struct mcp7940n_emul_data *data = target->data; if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) { return -ERANGE; } for (size_t i = 0; i < count; ++i) { data->data[offset + i] = buffer[i]; } return 0; } static int mcp7940n_emul_backend_get_data(const struct emul *target, size_t offset, size_t count, uint8_t *buffer) { struct mcp7940n_emul_data *data = target->data; if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) { return -ERANGE; } for (size_t i = 0; i < count; ++i) { buffer[i] = data->data[offset + i]; } return 0; } static const struct emul_bbram_driver_api mcp7940n_emul_backend_api = { .set_data = mcp7940n_emul_backend_set_data, .get_data = mcp7940n_emul_backend_get_data, }; #define MCP7940N_EMUL(inst) \ static const struct mcp7940n_emul_cfg mcp7940n_emul_cfg_##inst; \ static struct mcp7940n_emul_data mcp7940n_emul_data_##inst; \ EMUL_DT_INST_DEFINE(inst, mcp7940n_emul_init, &mcp7940n_emul_data_##inst, \ &mcp7940n_emul_cfg_##inst, &mcp7940n_emul_api_i2c, \ &mcp7940n_emul_backend_api) DT_INST_FOREACH_STATUS_OKAY(MCP7940N_EMUL)