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