1 /*
2  * Copyright (c) 2017 BayLibre, SAS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #include <zephyr/drivers/clock_control/stm32_clock_control.h>
8 #include <zephyr/drivers/clock_control.h>
9 #include <zephyr/sys/util.h>
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12 #include <zephyr/drivers/i2c.h>
13 
14 #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
15 #include <zephyr/logging/log.h>
16 LOG_MODULE_DECLARE(main);
17 
18 struct i2c_virtual_data {
19 	sys_slist_t targets;
20 };
21 
i2c_virtual_runtime_configure(const struct device * dev,uint32_t config)22 int i2c_virtual_runtime_configure(const struct device *dev, uint32_t config)
23 {
24 	return 0;
25 }
26 
find_address(struct i2c_virtual_data * data,uint16_t address,bool is_10bit)27 static struct i2c_target_config *find_address(struct i2c_virtual_data *data,
28 					     uint16_t address, bool is_10bit)
29 {
30 	struct i2c_target_config *cfg = NULL;
31 	sys_snode_t *node;
32 	bool search_10bit;
33 
34 	SYS_SLIST_FOR_EACH_NODE(&data->targets, node) {
35 		cfg = CONTAINER_OF(node, struct i2c_target_config, node);
36 
37 		search_10bit = (cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS);
38 
39 		if (cfg->address == address && search_10bit == is_10bit) {
40 			return cfg;
41 		}
42 	}
43 
44 	return NULL;
45 }
46 
47 /* Attach I2C targets */
i2c_virtual_target_register(const struct device * dev,struct i2c_target_config * config)48 int i2c_virtual_target_register(const struct device *dev,
49 			       struct i2c_target_config *config)
50 {
51 	struct i2c_virtual_data *data = dev->data;
52 
53 	if (!config) {
54 		return -EINVAL;
55 	}
56 
57 	/* Check the address is unique */
58 	if (find_address(data, config->address,
59 			 (config->flags & I2C_TARGET_FLAGS_ADDR_10_BITS))) {
60 		return -EINVAL;
61 	}
62 
63 	sys_slist_append(&data->targets, &config->node);
64 
65 	return 0;
66 }
67 
68 
i2c_virtual_target_unregister(const struct device * dev,struct i2c_target_config * config)69 int i2c_virtual_target_unregister(const struct device *dev,
70 				 struct i2c_target_config *config)
71 {
72 	struct i2c_virtual_data *data = dev->data;
73 
74 	if (!config) {
75 		return -EINVAL;
76 	}
77 
78 	if (!sys_slist_find_and_remove(&data->targets, &config->node)) {
79 		return -EINVAL;
80 	}
81 
82 	return 0;
83 }
84 
i2c_virtual_msg_write(const struct device * dev,struct i2c_msg * msg,struct i2c_target_config * config,bool prev_write)85 static int i2c_virtual_msg_write(const struct device *dev,
86 				 struct i2c_msg *msg,
87 				 struct i2c_target_config *config,
88 				 bool prev_write)
89 {
90 	unsigned int len = 0U;
91 	uint8_t *buf = msg->buf;
92 	int ret;
93 
94 	if (!prev_write) {
95 		config->callbacks->write_requested(config);
96 	}
97 
98 	len = msg->len;
99 	while (len) {
100 
101 		ret = config->callbacks->write_received(config, *buf);
102 		if (ret) {
103 			goto error;
104 		}
105 		buf++;
106 		len--;
107 	}
108 
109 	if (!(msg->flags & I2C_MSG_RESTART) && msg->flags & I2C_MSG_STOP) {
110 		config->callbacks->stop(config);
111 	}
112 
113 	return 0;
114 error:
115 	LOG_DBG("%s: NACK", __func__);
116 
117 	return -EIO;
118 }
119 
i2c_virtual_msg_read(const struct device * dev,struct i2c_msg * msg,struct i2c_target_config * config)120 static int i2c_virtual_msg_read(const struct device *dev, struct i2c_msg *msg,
121 				struct i2c_target_config *config)
122 {
123 	unsigned int len = msg->len;
124 	uint8_t *buf = msg->buf;
125 
126 	if (!msg->len) {
127 		return 0;
128 	}
129 
130 	config->callbacks->read_requested(config, buf);
131 	buf++;
132 	len--;
133 
134 	while (len) {
135 		config->callbacks->read_processed(config, buf);
136 		buf++;
137 		len--;
138 	}
139 
140 	if (!(msg->flags & I2C_MSG_RESTART) && msg->flags & I2C_MSG_STOP) {
141 		config->callbacks->stop(config);
142 	}
143 
144 	return 0;
145 }
146 
147 #define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK)
148 
i2c_virtual_transfer(const struct device * dev,struct i2c_msg * msg,uint8_t num_msgs,uint16_t target)149 static int i2c_virtual_transfer(const struct device *dev, struct i2c_msg *msg,
150 				uint8_t num_msgs, uint16_t target)
151 {
152 	struct i2c_virtual_data *data = dev->data;
153 	struct i2c_msg *current, *next;
154 	struct i2c_target_config *cfg;
155 	bool is_write = false;
156 	int ret = 0;
157 
158 	cfg = find_address(data, target, (msg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS));
159 	if (!cfg) {
160 		return -EIO;
161 	}
162 
163 	current = msg;
164 	current->flags |= I2C_MSG_RESTART;
165 	while (num_msgs > 0) {
166 		if (num_msgs > 1) {
167 			next = current + 1;
168 
169 			/*
170 			 * Stop or restart condition between messages
171 			 * of different directions is required
172 			 */
173 			if (OPERATION(current) != OPERATION(next)) {
174 				if (!(next->flags & I2C_MSG_RESTART)) {
175 					ret = -EINVAL;
176 					break;
177 				}
178 			}
179 		}
180 
181 		/* Stop condition is required for the last message */
182 		if ((num_msgs == 1U) && !(current->flags & I2C_MSG_STOP)) {
183 			ret = -EINVAL;
184 			break;
185 		}
186 
187 		if ((current->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
188 			ret = i2c_virtual_msg_write(dev, current,
189 						    cfg, is_write);
190 			is_write = true;
191 		} else {
192 			ret = i2c_virtual_msg_read(dev, current, cfg);
193 			is_write = false;
194 		}
195 
196 		if (ret < 0) {
197 			break;
198 		}
199 
200 		current++;
201 		num_msgs--;
202 	}
203 
204 	return ret;
205 }
206 
207 static DEVICE_API(i2c, api_funcs) = {
208 	.configure = i2c_virtual_runtime_configure,
209 	.transfer = i2c_virtual_transfer,
210 	.target_register = i2c_virtual_target_register,
211 	.target_unregister = i2c_virtual_target_unregister,
212 };
213 
i2c_virtual_init(const struct device * dev)214 static int i2c_virtual_init(const struct device *dev)
215 {
216 	struct i2c_virtual_data *data = dev->data;
217 
218 	sys_slist_init(&data->targets);
219 
220 	return 0;
221 }
222 
223 static struct i2c_virtual_data i2c_virtual_dev_data_0;
224 
225 DEVICE_DEFINE(i2c_virtual_0, CONFIG_I2C_VIRTUAL_NAME, &i2c_virtual_init,
226 		NULL, &i2c_virtual_dev_data_0, NULL,
227 		POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
228 		&api_funcs);
229