1 /*
2  * Copyright 2024 Google LLC
3  * SPDX-License-Identifier: Apache-2.0
4  */
5 
6 #define DT_DRV_COMPAT microchip_mcp7940n
7 
8 #include <zephyr/device.h>
9 #include <zephyr/drivers/emul.h>
10 #include <zephyr/drivers/emul_bbram.h>
11 #include <zephyr/drivers/i2c.h>
12 #include <zephyr/drivers/i2c_emul.h>
13 #include <zephyr/logging/log.h>
14 
15 LOG_MODULE_DECLARE(bbram_microchip_mcp7940n, CONFIG_BBRAM_LOG_LEVEL);
16 
17 #define MICROCHIP_MCP7940N_SRAM_OFFSET               0x20
18 #define MICROCHIP_MCP7940N_SRAM_SIZE                 64
19 #define MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS 0x03
20 #define MICROCHIP_MCP7940N_RTCWKDAY_VBATEN_BIT       BIT(3)
21 #define MICROCHIP_MCP7940N_RTCWKDAY_PWRFAIL_BIT      BIT(4)
22 
23 struct mcp7940n_emul_cfg {
24 };
25 
26 struct mcp7940n_emul_data {
27 	uint8_t rtcwkday;
28 	uint8_t data[MICROCHIP_MCP7940N_SRAM_SIZE];
29 };
30 
mcp7940n_emul_init(const struct emul * target,const struct device * parent)31 static int mcp7940n_emul_init(const struct emul *target, const struct device *parent)
32 {
33 	ARG_UNUSED(target);
34 	ARG_UNUSED(parent);
35 	return 0;
36 }
37 
mcp7940n_emul_transfer_i2c(const struct emul * target,struct i2c_msg * msgs,int num_msgs,int addr)38 static int mcp7940n_emul_transfer_i2c(const struct emul *target, struct i2c_msg *msgs, int num_msgs,
39 				      int addr)
40 {
41 	struct mcp7940n_emul_data *data = target->data;
42 
43 	i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false);
44 
45 	if (num_msgs < 1) {
46 		LOG_ERR("Invalid number of messages: %d", num_msgs);
47 		return -EIO;
48 	}
49 	if (FIELD_GET(I2C_MSG_READ, msgs->flags)) {
50 		LOG_ERR("Unexpected read");
51 		return -EIO;
52 	}
53 	if (msgs->len < 1) {
54 		LOG_ERR("Unexpected msg0 length %d", msgs->len);
55 		return -EIO;
56 	}
57 
58 	uint8_t regn = msgs->buf[0];
59 	bool is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
60 	bool is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
61 
62 	if (!is_stop && !is_read) {
63 		/* First message was a write with the register number, check next message */
64 		msgs++;
65 		is_read = FIELD_GET(I2C_MSG_READ, msgs->flags) == 1;
66 		is_stop = FIELD_GET(I2C_MSG_STOP, msgs->flags) == 1;
67 	}
68 
69 	if (is_read) {
70 		/* Read data */
71 		if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) {
72 			msgs->buf[0] = data->rtcwkday;
73 			return 0;
74 		}
75 		if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET &&
76 		    regn + msgs->len <=
77 			    MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) {
78 			for (int i = 0; i < msgs->len; ++i) {
79 				msgs->buf[i] =
80 					data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET];
81 			}
82 			return 0;
83 		}
84 	} else {
85 		/* Write data */
86 		if (regn == MICROCHIP_MCP7940N_RTCWKDAY_REGISTER_ADDRESS) {
87 			data->rtcwkday = msgs->buf[1];
88 			return 0;
89 		}
90 		if (regn >= MICROCHIP_MCP7940N_SRAM_OFFSET &&
91 		    regn + msgs->len - 1 <=
92 			    MICROCHIP_MCP7940N_SRAM_OFFSET + MICROCHIP_MCP7940N_SRAM_SIZE) {
93 			for (int i = 0; i < msgs->len; ++i) {
94 				data->data[regn + i - MICROCHIP_MCP7940N_SRAM_OFFSET] =
95 					msgs->buf[1 + i];
96 			}
97 			return 0;
98 		}
99 	}
100 
101 	return -EIO;
102 }
103 
104 static const struct i2c_emul_api mcp7940n_emul_api_i2c = {
105 	.transfer = mcp7940n_emul_transfer_i2c,
106 };
107 
mcp7940n_emul_backend_set_data(const struct emul * target,size_t offset,size_t count,const uint8_t * buffer)108 static int mcp7940n_emul_backend_set_data(const struct emul *target, size_t offset, size_t count,
109 					  const uint8_t *buffer)
110 {
111 	struct mcp7940n_emul_data *data = target->data;
112 
113 	if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) {
114 		return -ERANGE;
115 	}
116 
117 	for (size_t i = 0; i < count; ++i) {
118 		data->data[offset + i] = buffer[i];
119 	}
120 	return 0;
121 }
122 
mcp7940n_emul_backend_get_data(const struct emul * target,size_t offset,size_t count,uint8_t * buffer)123 static int mcp7940n_emul_backend_get_data(const struct emul *target, size_t offset, size_t count,
124 					  uint8_t *buffer)
125 {
126 	struct mcp7940n_emul_data *data = target->data;
127 
128 	if (offset + count > MICROCHIP_MCP7940N_SRAM_SIZE) {
129 		return -ERANGE;
130 	}
131 
132 	for (size_t i = 0; i < count; ++i) {
133 		buffer[i] = data->data[offset + i];
134 	}
135 	return 0;
136 }
137 
138 static const struct emul_bbram_driver_api mcp7940n_emul_backend_api = {
139 	.set_data = mcp7940n_emul_backend_set_data,
140 	.get_data = mcp7940n_emul_backend_get_data,
141 };
142 
143 #define MCP7940N_EMUL(inst)                                                                        \
144 	static const struct mcp7940n_emul_cfg mcp7940n_emul_cfg_##inst;                            \
145 	static struct mcp7940n_emul_data mcp7940n_emul_data_##inst;                                \
146 	EMUL_DT_INST_DEFINE(inst, mcp7940n_emul_init, &mcp7940n_emul_data_##inst,                  \
147 			    &mcp7940n_emul_cfg_##inst, &mcp7940n_emul_api_i2c,                     \
148 			    &mcp7940n_emul_backend_api)
149 
150 DT_INST_FOREACH_STATUS_OKAY(MCP7940N_EMUL)
151