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 <logging/log.h>
16 LOG_MODULE_REGISTER(i2c_emul_ctlr);
17
18 #include <device.h>
19 #include <drivers/emul.h>
20 #include <drivers/i2c.h>
21 #include <drivers/i2c_emul.h>
22
23 /** Working data for the device */
24 struct i2c_emul_data {
25 /* List of struct i2c_emul associated with the device */
26 sys_slist_t emuls;
27 /* I2C host configuration */
28 uint32_t config;
29 };
30
i2c_emul_get_config(const struct device * dev)31 uint32_t i2c_emul_get_config(const struct device *dev)
32 {
33 struct i2c_emul_data *data = dev->data;
34
35 return data->config;
36 }
37
38 /**
39 * Find an emulator by its I2C address
40 *
41 * @param dev I2C emulation controller device
42 * @param addr I2C address of that device
43 * @return emulator ro use
44 * @return NULL if not found
45 */
i2c_emul_find(const struct device * dev,int addr)46 static struct i2c_emul *i2c_emul_find(const struct device *dev, int addr)
47 {
48 struct i2c_emul_data *data = dev->data;
49 sys_snode_t *node;
50
51 SYS_SLIST_FOR_EACH_NODE(&data->emuls, node) {
52 struct i2c_emul *emul = NULL;
53
54 emul = CONTAINER_OF(node, struct i2c_emul, node);
55 if (emul->addr == addr) {
56 return emul;
57 }
58 }
59
60 return NULL;
61 }
62
i2c_emul_configure(const struct device * dev,uint32_t dev_config)63 static int i2c_emul_configure(const struct device *dev, uint32_t dev_config)
64 {
65 struct i2c_emul_data *data = dev->data;
66
67 data->config = dev_config;
68
69 return 0;
70 }
71
i2c_emul_transfer(const struct device * dev,struct i2c_msg * msgs,uint8_t num_msgs,uint16_t addr)72 static int i2c_emul_transfer(const struct device *dev, struct i2c_msg *msgs,
73 uint8_t num_msgs, uint16_t addr)
74 {
75 struct i2c_emul *emul;
76 const struct i2c_emul_api *api;
77 int ret;
78
79 emul = i2c_emul_find(dev, addr);
80 if (!emul) {
81 return -EIO;
82 }
83
84 api = emul->api;
85 __ASSERT_NO_MSG(emul->api);
86 __ASSERT_NO_MSG(emul->api->transfer);
87
88 ret = api->transfer(emul, msgs, num_msgs, addr);
89 if (ret) {
90 return ret;
91 }
92
93 return 0;
94 }
95
96 /**
97 * Set up a new emulator and add it to the list
98 *
99 * @param dev I2C emulation controller device
100 */
i2c_emul_init(const struct device * dev)101 static int i2c_emul_init(const struct device *dev)
102 {
103 struct i2c_emul_data *data = dev->data;
104 const struct emul_list_for_bus *list = dev->config;
105 int rc;
106
107 sys_slist_init(&data->emuls);
108
109 rc = emul_init_for_bus_from_list(dev, list);
110
111 return rc;
112 }
113
i2c_emul_register(const struct device * dev,const char * name,struct i2c_emul * emul)114 int i2c_emul_register(const struct device *dev, const char *name,
115 struct i2c_emul *emul)
116 {
117 struct i2c_emul_data *data = dev->data;
118
119 sys_slist_append(&data->emuls, &emul->node);
120
121 LOG_INF("Register emulator '%s' at I2C addr %02x\n", name, emul->addr);
122
123 return 0;
124 }
125
126 /* Device instantiation */
127
128 static struct i2c_driver_api i2c_emul_api = {
129 .configure = i2c_emul_configure,
130 .transfer = i2c_emul_transfer,
131 };
132
133 #define EMUL_LINK_AND_COMMA(node_id) { \
134 .label = DT_LABEL(node_id), \
135 },
136
137 #define I2C_EMUL_INIT(n) \
138 static const struct emul_link_for_bus emuls_##n[] = { \
139 DT_FOREACH_CHILD(DT_DRV_INST(n), EMUL_LINK_AND_COMMA) \
140 }; \
141 static struct emul_list_for_bus i2c_emul_cfg_##n = { \
142 .children = emuls_##n, \
143 .num_children = ARRAY_SIZE(emuls_##n), \
144 }; \
145 static struct i2c_emul_data i2c_emul_data_##n; \
146 DEVICE_DT_INST_DEFINE(n, \
147 i2c_emul_init, \
148 NULL, \
149 &i2c_emul_data_##n, \
150 &i2c_emul_cfg_##n, \
151 POST_KERNEL, \
152 CONFIG_I2C_INIT_PRIORITY, \
153 &i2c_emul_api);
154
155 DT_INST_FOREACH_STATUS_OKAY(I2C_EMUL_INIT)
156