1 /*
2  * Copyright (c) 2017 BayLibre, SAS
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #define DT_DRV_COMPAT zephyr_i2c_target_eeprom
8 
9 #include <zephyr/sys/util.h>
10 #include <zephyr/kernel.h>
11 #include <errno.h>
12 #include <zephyr/drivers/i2c.h>
13 #include <string.h>
14 #include <zephyr/drivers/i2c/target/eeprom.h>
15 
16 #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
17 #include <zephyr/logging/log.h>
18 LOG_MODULE_REGISTER(i2c_target);
19 
20 struct i2c_eeprom_target_data {
21 	struct i2c_target_config config;
22 	uint32_t buffer_size;
23 	uint8_t *buffer;
24 	uint32_t buffer_idx;
25 	uint32_t idx_write_cnt;
26 	uint8_t address_width;
27 };
28 
29 struct i2c_eeprom_target_config {
30 	struct i2c_dt_spec bus;
31 	uint32_t buffer_size;
32 	uint8_t *buffer;
33 };
34 
eeprom_target_program(const struct device * dev,const uint8_t * eeprom_data,unsigned int length)35 int eeprom_target_program(const struct device *dev, const uint8_t *eeprom_data,
36 			 unsigned int length)
37 {
38 	struct i2c_eeprom_target_data *data = dev->data;
39 
40 	if (length > data->buffer_size) {
41 		return -EINVAL;
42 	}
43 
44 	memcpy(data->buffer, eeprom_data, length);
45 
46 	return 0;
47 }
48 
eeprom_target_read(const struct device * dev,uint8_t * eeprom_data,unsigned int offset)49 int eeprom_target_read(const struct device *dev, uint8_t *eeprom_data,
50 		      unsigned int offset)
51 {
52 	struct i2c_eeprom_target_data *data = dev->data;
53 
54 	if (!data || offset >= data->buffer_size) {
55 		return -EINVAL;
56 	}
57 
58 	*eeprom_data = data->buffer[offset];
59 
60 	return 0;
61 }
62 
63 #ifdef CONFIG_I2C_EEPROM_TARGET_RUNTIME_ADDR
eeprom_target_set_addr(const struct device * dev,uint8_t addr)64 int eeprom_target_set_addr(const struct device *dev, uint8_t addr)
65 {
66 	const struct i2c_eeprom_target_config *cfg = dev->config;
67 	struct i2c_eeprom_target_data *data = dev->data;
68 	int ret;
69 
70 	ret = i2c_target_unregister(cfg->bus.bus, &data->config);
71 	if (ret) {
72 		LOG_DBG("eeprom target failed to unregister");
73 		return ret;
74 	}
75 
76 	data->config.address = addr;
77 
78 	return i2c_target_register(cfg->bus.bus, &data->config);
79 }
80 #endif /* CONFIG_I2C_EEPROM_TARGET_RUNTIME_ADDR */
81 
eeprom_target_write_requested(struct i2c_target_config * config)82 static int eeprom_target_write_requested(struct i2c_target_config *config)
83 {
84 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
85 						struct i2c_eeprom_target_data,
86 						config);
87 
88 	LOG_DBG("eeprom: write req");
89 
90 	data->idx_write_cnt = 0;
91 
92 	return 0;
93 }
94 
eeprom_target_read_requested(struct i2c_target_config * config,uint8_t * val)95 static int eeprom_target_read_requested(struct i2c_target_config *config,
96 				       uint8_t *val)
97 {
98 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
99 						struct i2c_eeprom_target_data,
100 						config);
101 
102 	*val = data->buffer[data->buffer_idx];
103 
104 	LOG_DBG("eeprom: read req, val=0x%x", *val);
105 
106 	/* Increment will be done in the read_processed callback */
107 
108 	return 0;
109 }
110 
eeprom_target_write_received(struct i2c_target_config * config,uint8_t val)111 static int eeprom_target_write_received(struct i2c_target_config *config,
112 				       uint8_t val)
113 {
114 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
115 						struct i2c_eeprom_target_data,
116 						config);
117 
118 	LOG_DBG("eeprom: write done, val=0x%x", val);
119 
120 	/* In case EEPROM wants to be R/O, return !0 here could trigger
121 	 * a NACK to the I2C controller, support depends on the
122 	 * I2C controller support
123 	 */
124 
125 	if (data->idx_write_cnt < (data->address_width >> 3)) {
126 		if (data->idx_write_cnt == 0) {
127 			data->buffer_idx = 0;
128 		}
129 
130 		data->buffer_idx = val | (data->buffer_idx << 8);
131 		data->idx_write_cnt++;
132 	} else {
133 		data->buffer[data->buffer_idx++] = val;
134 	}
135 
136 	data->buffer_idx = data->buffer_idx % data->buffer_size;
137 
138 	return 0;
139 }
140 
eeprom_target_read_processed(struct i2c_target_config * config,uint8_t * val)141 static int eeprom_target_read_processed(struct i2c_target_config *config,
142 				       uint8_t *val)
143 {
144 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
145 						struct i2c_eeprom_target_data,
146 						config);
147 
148 	/* Increment here */
149 	data->buffer_idx = (data->buffer_idx + 1) % data->buffer_size;
150 
151 	*val = data->buffer[data->buffer_idx];
152 
153 	LOG_DBG("eeprom: read done, val=0x%x", *val);
154 
155 	/* Increment will be done in the next read_processed callback
156 	 * In case of STOP, the byte won't be taken in account
157 	 */
158 
159 	return 0;
160 }
161 
eeprom_target_stop(struct i2c_target_config * config)162 static int eeprom_target_stop(struct i2c_target_config *config)
163 {
164 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
165 						struct i2c_eeprom_target_data,
166 						config);
167 
168 	LOG_DBG("eeprom: stop");
169 
170 	data->idx_write_cnt = 0;
171 
172 	return 0;
173 }
174 
175 #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
eeprom_target_buf_write_received(struct i2c_target_config * config,uint8_t * ptr,uint32_t len)176 static void eeprom_target_buf_write_received(struct i2c_target_config *config,
177 					     uint8_t *ptr, uint32_t len)
178 {
179 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
180 						struct i2c_eeprom_target_data,
181 						config);
182 	/* The first byte(s) is offset */
183 	uint32_t idx_write_cnt = 0;
184 
185 	data->buffer_idx = 0;
186 	while (idx_write_cnt < (data->address_width >> 3)) {
187 		data->buffer_idx = (data->buffer_idx << 8) | *ptr++;
188 		len--;
189 		idx_write_cnt++;
190 	}
191 
192 	if (len > 0) {
193 		memcpy(&data->buffer[data->buffer_idx], ptr, len);
194 	}
195 }
196 
eeprom_target_buf_read_requested(struct i2c_target_config * config,uint8_t ** ptr,uint32_t * len)197 static int eeprom_target_buf_read_requested(struct i2c_target_config *config,
198 					    uint8_t **ptr, uint32_t *len)
199 {
200 	struct i2c_eeprom_target_data *data = CONTAINER_OF(config,
201 						struct i2c_eeprom_target_data,
202 						config);
203 
204 	*ptr = &data->buffer[data->buffer_idx];
205 	*len = data->buffer_size;
206 
207 	return 0;
208 }
209 #endif
210 
eeprom_target_register(const struct device * dev)211 static int eeprom_target_register(const struct device *dev)
212 {
213 	const struct i2c_eeprom_target_config *cfg = dev->config;
214 	struct i2c_eeprom_target_data *data = dev->data;
215 
216 	return i2c_target_register(cfg->bus.bus, &data->config);
217 }
218 
eeprom_target_unregister(const struct device * dev)219 static int eeprom_target_unregister(const struct device *dev)
220 {
221 	const struct i2c_eeprom_target_config *cfg = dev->config;
222 	struct i2c_eeprom_target_data *data = dev->data;
223 
224 	return i2c_target_unregister(cfg->bus.bus, &data->config);
225 }
226 
227 static const struct i2c_target_driver_api api_funcs = {
228 	.driver_register = eeprom_target_register,
229 	.driver_unregister = eeprom_target_unregister,
230 };
231 
232 static const struct i2c_target_callbacks eeprom_callbacks = {
233 	.write_requested = eeprom_target_write_requested,
234 	.read_requested = eeprom_target_read_requested,
235 	.write_received = eeprom_target_write_received,
236 	.read_processed = eeprom_target_read_processed,
237 #ifdef CONFIG_I2C_TARGET_BUFFER_MODE
238 	.buf_write_received = eeprom_target_buf_write_received,
239 	.buf_read_requested = eeprom_target_buf_read_requested,
240 #endif
241 	.stop = eeprom_target_stop,
242 };
243 
i2c_eeprom_target_init(const struct device * dev)244 static int i2c_eeprom_target_init(const struct device *dev)
245 {
246 	struct i2c_eeprom_target_data *data = dev->data;
247 	const struct i2c_eeprom_target_config *cfg = dev->config;
248 
249 	if (!device_is_ready(cfg->bus.bus)) {
250 		LOG_ERR("I2C controller device not ready");
251 		return -ENODEV;
252 	}
253 
254 	data->buffer_size = cfg->buffer_size;
255 	data->buffer = cfg->buffer;
256 	data->config.address = cfg->bus.addr;
257 	data->config.callbacks = &eeprom_callbacks;
258 
259 	return 0;
260 }
261 
262 #define I2C_EEPROM_INIT(inst)						\
263 	static struct i2c_eeprom_target_data				\
264 		i2c_eeprom_target_##inst##_dev_data = {			\
265 			.address_width = DT_INST_PROP_OR(inst,		\
266 					address_width, 8),		\
267 		};							\
268 									\
269 	static uint8_t							\
270 	i2c_eeprom_target_##inst##_buffer[(DT_INST_PROP(inst, size))];	\
271 									\
272 	BUILD_ASSERT(DT_INST_PROP(inst, size) <=			\
273 			(1 << DT_INST_PROP_OR(inst, address_width, 8)), \
274 			"size must be <= than 2^address_width");	\
275 									\
276 	static const struct i2c_eeprom_target_config			\
277 		i2c_eeprom_target_##inst##_cfg = {			\
278 		.bus = I2C_DT_SPEC_INST_GET(inst),			\
279 		.buffer_size = DT_INST_PROP(inst, size),		\
280 		.buffer = i2c_eeprom_target_##inst##_buffer		\
281 	};								\
282 									\
283 	DEVICE_DT_INST_DEFINE(inst,					\
284 			    &i2c_eeprom_target_init,			\
285 			    NULL,			\
286 			    &i2c_eeprom_target_##inst##_dev_data,	\
287 			    &i2c_eeprom_target_##inst##_cfg,		\
288 			    POST_KERNEL,				\
289 			    CONFIG_I2C_TARGET_INIT_PRIORITY,		\
290 			    &api_funcs);
291 
292 DT_INST_FOREACH_STATUS_OKAY(I2C_EEPROM_INIT)
293