1 /*
2 * This driver creates fake I2C buses which can contain emulated devices,
3 * implemented by a separate emulation driver. The API between this driver and
4 * its emulators is defined by struct i2c_emul_driver_api.
5 *
6 * Copyright 2020 Google LLC
7 * Copyright (c) 2020 Nordic Semiconductor ASA
8 *
9 * SPDX-License-Identifier: Apache-2.0
10 */
11
12 #define DT_DRV_COMPAT zephyr_i2c_emul_controller
13
14 #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_REGISTER(i2c_emul_ctlr);
17
18 #include <zephyr/device.h>
19 #include <zephyr/drivers/emul.h>
20 #include <zephyr/drivers/i2c.h>
21 #include <zephyr/drivers/i2c_emul.h>
22
23 #include "i2c-priv.h"
24
25 /** Working data for the device */
26 struct i2c_emul_data {
27 /* List of struct i2c_emul associated with the device */
28 sys_slist_t emuls;
29 /* I2C host configuration */
30 uint32_t config;
31 uint32_t bitrate;
32 #ifdef CONFIG_I2C_TARGET
33 struct i2c_target_config *target_cfg;
34 #endif
35 };
36
37 struct i2c_emul_config {
38 struct emul_list_for_bus emul_list;
39 bool target_buffered_mode;
40 const struct i2c_dt_spec *forward_list;
41 uint16_t forward_list_size;
42 };
43
44 /**
45 * Find an emulator by its I2C address
46 *
47 * @param dev I2C emulation controller device
48 * @param addr I2C address of that device
49 * @return emulator ro use
50 * @return NULL if not found
51 */
i2c_emul_find(const struct device * dev,int addr)52 static struct i2c_emul *i2c_emul_find(const struct device *dev, int addr)
53 {
54 struct i2c_emul_data *data = dev->data;
55 sys_snode_t *node;
56
57 SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
58 struct i2c_emul *emul = NULL;
59
60 emul = CONTAINER_OF(node, struct i2c_emul, node);
61 if (emul->addr == addr) {
62 return emul;
63 }
64 }
65
66 return NULL;
67 }
68
i2c_emul_configure(const struct device * dev,uint32_t dev_config)69 static int i2c_emul_configure(const struct device *dev, uint32_t dev_config)
70 {
71 struct i2c_emul_data *data = dev->data;
72
73 data->config = dev_config;
74
75 return 0;
76 }
77
i2c_emul_get_config(const struct device * dev,uint32_t * dev_config)78 static int i2c_emul_get_config(const struct device *dev, uint32_t *dev_config)
79 {
80 struct i2c_emul_data *data = dev->data;
81
82 *dev_config = data->config;
83
84 return 0;
85 }
86
87 #ifdef CONFIG_I2C_TARGET
i2c_emul_send_to_target(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs)88 static int i2c_emul_send_to_target(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs)
89 {
90 struct i2c_emul_data *data = dev->data;
91 const struct i2c_target_callbacks *callbacks = data->target_cfg->callbacks;
92
93 #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
94 const struct i2c_emul_config *config = dev->config;
95
96 if (config->target_buffered_mode) {
97 for (uint8_t i = 0; i < num_msgs; ++i) {
98 if (i2c_is_read_op(&msgs[i])) {
99 uint8_t *ptr = NULL;
100 uint32_t len;
101 int rc =
102 callbacks->buf_read_requested(data->target_cfg, &ptr, &len);
103
104 if (rc != 0) {
105 return rc;
106 }
107 if (len > msgs[i].len) {
108 LOG_ERR("buf_read_requested returned too many bytes");
109 return -ENOMEM;
110 }
111 memcpy(msgs[i].buf, ptr, len);
112 } else {
113 callbacks->buf_write_received(data->target_cfg, msgs[i].buf,
114 msgs[i].len);
115 }
116 if (i2c_is_stop_op(&msgs[i])) {
117 int rc = callbacks->stop(data->target_cfg);
118
119 if (rc != 0) {
120 return rc;
121 }
122 }
123 }
124 return 0;
125 }
126 #endif /* CONFIG_I2C_TARGET_BUFFER_MODE */
127
128 for (uint8_t i = 0; i < num_msgs; ++i) {
129 LOG_DBG(" msgs[%u].flags? 0x%02x", i, msgs[i].flags);
130 if (i2c_is_read_op(&msgs[i])) {
131 for (uint32_t j = 0; j < msgs[i].len; ++j) {
132 int rc = 0;
133
134 if (j == 0) {
135 LOG_DBG(" Calling read_requested with data %p",
136 (void *)&msgs[i].buf[j]);
137 rc = callbacks->read_requested(data->target_cfg,
138 &msgs[i].buf[j]);
139 } else {
140 LOG_DBG(" Calling read_processed with data %p",
141 (void *)&msgs[i].buf[j]);
142 rc = callbacks->read_processed(data->target_cfg,
143 &msgs[i].buf[j]);
144 }
145 if (rc != 0) {
146 return rc;
147 }
148 }
149 } else {
150 for (uint32_t j = 0; j < msgs[i].len; ++j) {
151 int rc = 0;
152
153 if (j == 0) {
154 LOG_DBG(" Calling write_requested");
155 rc = callbacks->write_requested(data->target_cfg);
156 }
157 if (rc != 0) {
158 return rc;
159 }
160 LOG_DBG(" Calling write_received with data 0x%02x",
161 msgs[i].buf[j]);
162 rc = callbacks->write_received(data->target_cfg, msgs[i].buf[j]);
163 if (rc != 0) {
164 return rc;
165 }
166 }
167 }
168 if (i2c_is_stop_op(&msgs[i])) {
169 int rc = callbacks->stop(data->target_cfg);
170
171 if (rc != 0) {
172 return rc;
173 }
174 }
175 }
176 return 0;
177 }
178 #endif /* CONFIG_I2C_TARGET*/
179
i2c_emul_transfer(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs,uint16_t addr)180 static int i2c_emul_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
181 uint16_t addr)
182 {
183 const struct i2c_emul_config *conf = dev->config;
184 struct i2c_emul *emul;
185 const struct i2c_emul_api *api;
186 int ret;
187
188 LOG_DBG("%s(dev=%p, addr=0x%02x)", __func__, (void *)dev, addr);
189 #ifdef CONFIG_I2C_TARGET
190 struct i2c_emul_data *data = dev->data;
191
192 /*
193 * First check if the bus is configured as a target, targets either listen to the address or
194 * ignore the messages. So if the address doesn't match, we're just going to bail.
195 */
196 LOG_DBG(" has_target_cfg? %d", data->target_cfg != NULL);
197 if (data->target_cfg != NULL) {
198 LOG_DBG(" target_cfg->address? 0x%02x", data->target_cfg->address);
199 if (data->target_cfg->address != addr) {
200 return -EINVAL;
201 }
202 LOG_DBG(" forwarding to target");
203 return i2c_emul_send_to_target(dev, msgs, num_msgs);
204 }
205 #endif /* CONFIG_I2C_TARGET */
206
207 /*
208 * We're not a target, but lets check if we need to forward this request before we start
209 * looking for a peripheral.
210 */
211 for (uint16_t i = 0; i < conf->forward_list_size; ++i) {
212 LOG_DBG(" Checking forward list [%u].addr? 0x%02x", i,
213 conf->forward_list[i].addr);
214 if (conf->forward_list[i].addr == addr) {
215 /* We need to forward this request */
216 return i2c_transfer(conf->forward_list[i].bus, msgs, num_msgs, addr);
217 }
218 }
219
220 emul = i2c_emul_find(dev, addr);
221 if (!emul) {
222 return -EIO;
223 }
224
225 api = emul->api;
226 __ASSERT_NO_MSG(emul->api);
227 __ASSERT_NO_MSG(emul->api->transfer);
228
229 if (emul->mock_api != NULL && emul->mock_api->transfer != NULL) {
230 ret = emul->mock_api->transfer(emul->target, msgs, num_msgs, addr);
231 if (ret != -ENOSYS) {
232 return ret;
233 }
234 }
235
236 return api->transfer(emul->target, msgs, num_msgs, addr);
237 }
238
239 /**
240 * Set up a new emulator and add it to the list
241 *
242 * @param dev I2C emulation controller device
243 */
i2c_emul_init(const struct device * dev)244 static int i2c_emul_init(const struct device *dev)
245 {
246 struct i2c_emul_data *data = dev->data;
247 int rc;
248
249 sys_slist_init(&data->emuls);
250
251 rc = emul_init_for_bus(dev);
252
253 /* Set config to an uninitialized state */
254 data->config = (I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(data->bitrate));
255
256 return rc;
257 }
258
i2c_emul_register(const struct device * dev,struct i2c_emul * emul)259 int i2c_emul_register(const struct device *dev, struct i2c_emul *emul)
260 {
261 struct i2c_emul_data *data = dev->data;
262 const char *name = emul->target->dev->name;
263
264 sys_slist_append(&data->emuls, &emul->node);
265
266 LOG_INF("Register emulator '%s' at I2C addr %02x", name, emul->addr);
267
268 return 0;
269 }
270
271 #ifdef CONFIG_I2C_TARGET
i2c_emul_target_register(const struct device * dev,struct i2c_target_config * cfg)272 static int i2c_emul_target_register(const struct device *dev, struct i2c_target_config *cfg)
273 {
274 struct i2c_emul_data *data = dev->data;
275
276 data->target_cfg = cfg;
277 return 0;
278 }
279
i2c_emul_target_unregister(const struct device * dev,struct i2c_target_config * cfg)280 static int i2c_emul_target_unregister(const struct device *dev, struct i2c_target_config *cfg)
281 {
282 struct i2c_emul_data *data = dev->data;
283
284 if (data->target_cfg != cfg) {
285 return -EINVAL;
286 }
287
288 data->target_cfg = NULL;
289 return 0;
290 }
291 #endif /* CONFIG_I2C_TARGET */
292
293 /* Device instantiation */
294
295 static DEVICE_API(i2c, i2c_emul_api) = {
296 .configure = i2c_emul_configure,
297 .get_config = i2c_emul_get_config,
298 .transfer = i2c_emul_transfer,
299 #ifdef CONFIG_I2C_TARGET
300 .target_register = i2c_emul_target_register,
301 .target_unregister = i2c_emul_target_unregister,
302 #endif
303 #ifdef CONFIG_I2C_RTIO
304 .iodev_submit = i2c_iodev_submit_fallback,
305 #endif
306 };
307
308 #define EMUL_LINK_AND_COMMA(node_id) \
309 { \
310 .dev = DEVICE_DT_GET(node_id), \
311 },
312
313 #define EMUL_FORWARD_ITEM(node_id, prop, idx) \
314 { \
315 .bus = DEVICE_DT_GET(DT_PHANDLE_BY_IDX(node_id, prop, idx)), \
316 .addr = DT_PHA_BY_IDX(node_id, prop, idx, addr), \
317 },
318
319 #define I2C_EMUL_INIT(n) \
320 static const struct emul_link_for_bus emuls_##n[] = { \
321 DT_FOREACH_CHILD_STATUS_OKAY(DT_DRV_INST(n), EMUL_LINK_AND_COMMA)}; \
322 static const struct i2c_dt_spec emul_forward_list_##n[] = { \
323 COND_CODE_1(DT_INST_NODE_HAS_PROP(n, forwards), \
324 (DT_INST_FOREACH_PROP_ELEM(n, forwards, EMUL_FORWARD_ITEM)), ())}; \
325 static struct i2c_emul_config i2c_emul_cfg_##n = { \
326 .emul_list = \
327 { \
328 .children = emuls_##n, \
329 .num_children = ARRAY_SIZE(emuls_##n), \
330 }, \
331 .target_buffered_mode = DT_INST_PROP(n, target_buffered_mode), \
332 .forward_list = emul_forward_list_##n, \
333 .forward_list_size = ARRAY_SIZE(emul_forward_list_##n), \
334 }; \
335 static struct i2c_emul_data i2c_emul_data_##n = { \
336 .bitrate = DT_INST_PROP(n, clock_frequency), \
337 }; \
338 I2C_DEVICE_DT_INST_DEFINE(n, i2c_emul_init, NULL, &i2c_emul_data_##n, &i2c_emul_cfg_##n, \
339 POST_KERNEL, CONFIG_I2C_INIT_PRIORITY, &i2c_emul_api);
340
341 DT_INST_FOREACH_STATUS_OKAY(I2C_EMUL_INIT)
342