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