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