1 /*
2 * Copyright (c) 2017 BayLibre, SAS
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #define DT_DRV_COMPAT atmel_at24
8
9 #include <sys/util.h>
10 #include <kernel.h>
11 #include <errno.h>
12 #include <drivers/i2c.h>
13 #include <string.h>
14 #include <drivers/i2c/slave/eeprom.h>
15
16 #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
17 #include <logging/log.h>
18 LOG_MODULE_REGISTER(i2c_slave);
19
20 struct i2c_eeprom_slave_data {
21 const struct device *i2c_controller;
22 struct i2c_slave_config config;
23 uint32_t buffer_size;
24 uint8_t *buffer;
25 uint32_t buffer_idx;
26 bool first_write;
27 };
28
29 struct i2c_eeprom_slave_config {
30 char *controller_dev_name;
31 uint8_t address;
32 uint32_t buffer_size;
33 uint8_t *buffer;
34 };
35
36 /* convenience defines */
37 #define DEV_CFG(dev) \
38 ((const struct i2c_eeprom_slave_config * const) \
39 (dev)->config)
40 #define DEV_DATA(dev) \
41 ((struct i2c_eeprom_slave_data * const)(dev)->data)
42
eeprom_slave_program(const struct device * dev,const uint8_t * eeprom_data,unsigned int length)43 int eeprom_slave_program(const struct device *dev, const uint8_t *eeprom_data,
44 unsigned int length)
45 {
46 struct i2c_eeprom_slave_data *data = dev->data;
47
48 if (length > data->buffer_size) {
49 return -EINVAL;
50 }
51
52 memcpy(data->buffer, eeprom_data, length);
53
54 return 0;
55 }
56
eeprom_slave_read(const struct device * dev,uint8_t * eeprom_data,unsigned int offset)57 int eeprom_slave_read(const struct device *dev, uint8_t *eeprom_data,
58 unsigned int offset)
59 {
60 struct i2c_eeprom_slave_data *data = dev->data;
61
62 if (!data || offset >= data->buffer_size) {
63 return -EINVAL;
64 }
65
66 *eeprom_data = data->buffer[offset];
67
68 return 0;
69 }
70
eeprom_slave_write_requested(struct i2c_slave_config * config)71 static int eeprom_slave_write_requested(struct i2c_slave_config *config)
72 {
73 struct i2c_eeprom_slave_data *data = CONTAINER_OF(config,
74 struct i2c_eeprom_slave_data,
75 config);
76
77 LOG_DBG("eeprom: write req");
78
79 data->first_write = true;
80
81 return 0;
82 }
83
eeprom_slave_read_requested(struct i2c_slave_config * config,uint8_t * val)84 static int eeprom_slave_read_requested(struct i2c_slave_config *config,
85 uint8_t *val)
86 {
87 struct i2c_eeprom_slave_data *data = CONTAINER_OF(config,
88 struct i2c_eeprom_slave_data,
89 config);
90
91 *val = data->buffer[data->buffer_idx];
92
93 LOG_DBG("eeprom: read req, val=0x%x", *val);
94
95 /* Increment will be done in the read_processed callback */
96
97 return 0;
98 }
99
eeprom_slave_write_received(struct i2c_slave_config * config,uint8_t val)100 static int eeprom_slave_write_received(struct i2c_slave_config *config,
101 uint8_t val)
102 {
103 struct i2c_eeprom_slave_data *data = CONTAINER_OF(config,
104 struct i2c_eeprom_slave_data,
105 config);
106
107 LOG_DBG("eeprom: write done, val=0x%x", val);
108
109 /* In case EEPROM wants to be R/O, return !0 here could trigger
110 * a NACK to the I2C controller, support depends on the
111 * I2C controller support
112 */
113
114 if (data->first_write) {
115 data->buffer_idx = val;
116 data->first_write = false;
117 } else {
118 data->buffer[data->buffer_idx++] = val;
119 }
120
121 data->buffer_idx = data->buffer_idx % data->buffer_size;
122
123 return 0;
124 }
125
eeprom_slave_read_processed(struct i2c_slave_config * config,uint8_t * val)126 static int eeprom_slave_read_processed(struct i2c_slave_config *config,
127 uint8_t *val)
128 {
129 struct i2c_eeprom_slave_data *data = CONTAINER_OF(config,
130 struct i2c_eeprom_slave_data,
131 config);
132
133 /* Increment here */
134 data->buffer_idx = (data->buffer_idx + 1) % data->buffer_size;
135
136 *val = data->buffer[data->buffer_idx];
137
138 LOG_DBG("eeprom: read done, val=0x%x", *val);
139
140 /* Increment will be done in the next read_processed callback
141 * In case of STOP, the byte won't be taken in account
142 */
143
144 return 0;
145 }
146
eeprom_slave_stop(struct i2c_slave_config * config)147 static int eeprom_slave_stop(struct i2c_slave_config *config)
148 {
149 struct i2c_eeprom_slave_data *data = CONTAINER_OF(config,
150 struct i2c_eeprom_slave_data,
151 config);
152
153 LOG_DBG("eeprom: stop");
154
155 data->first_write = true;
156
157 return 0;
158 }
159
eeprom_slave_register(const struct device * dev)160 static int eeprom_slave_register(const struct device *dev)
161 {
162 struct i2c_eeprom_slave_data *data = dev->data;
163
164 return i2c_slave_register(data->i2c_controller, &data->config);
165 }
166
eeprom_slave_unregister(const struct device * dev)167 static int eeprom_slave_unregister(const struct device *dev)
168 {
169 struct i2c_eeprom_slave_data *data = dev->data;
170
171 return i2c_slave_unregister(data->i2c_controller, &data->config);
172 }
173
174 static const struct i2c_slave_driver_api api_funcs = {
175 .driver_register = eeprom_slave_register,
176 .driver_unregister = eeprom_slave_unregister,
177 };
178
179 static const struct i2c_slave_callbacks eeprom_callbacks = {
180 .write_requested = eeprom_slave_write_requested,
181 .read_requested = eeprom_slave_read_requested,
182 .write_received = eeprom_slave_write_received,
183 .read_processed = eeprom_slave_read_processed,
184 .stop = eeprom_slave_stop,
185 };
186
i2c_eeprom_slave_init(const struct device * dev)187 static int i2c_eeprom_slave_init(const struct device *dev)
188 {
189 struct i2c_eeprom_slave_data *data = DEV_DATA(dev);
190 const struct i2c_eeprom_slave_config *cfg = DEV_CFG(dev);
191
192 data->i2c_controller =
193 device_get_binding(cfg->controller_dev_name);
194 if (!data->i2c_controller) {
195 LOG_ERR("i2c controller not found: %s",
196 cfg->controller_dev_name);
197 return -EINVAL;
198 }
199
200 data->buffer_size = cfg->buffer_size;
201 data->buffer = cfg->buffer;
202 data->config.address = cfg->address;
203 data->config.callbacks = &eeprom_callbacks;
204
205 return 0;
206 }
207
208 #define I2C_EEPROM_INIT(inst) \
209 static struct i2c_eeprom_slave_data \
210 i2c_eeprom_slave_##inst##_dev_data; \
211 \
212 static uint8_t \
213 i2c_eeprom_slave_##inst##_buffer[(DT_INST_PROP(inst, size))]; \
214 \
215 static const struct i2c_eeprom_slave_config \
216 i2c_eeprom_slave_##inst##_cfg = { \
217 .controller_dev_name = DT_INST_BUS_LABEL(inst), \
218 .address = DT_INST_REG_ADDR(inst), \
219 .buffer_size = DT_INST_PROP(inst, size), \
220 .buffer = i2c_eeprom_slave_##inst##_buffer \
221 }; \
222 \
223 DEVICE_DT_INST_DEFINE(inst, \
224 &i2c_eeprom_slave_init, \
225 NULL, \
226 &i2c_eeprom_slave_##inst##_dev_data, \
227 &i2c_eeprom_slave_##inst##_cfg, \
228 POST_KERNEL, \
229 CONFIG_I2C_SLAVE_INIT_PRIORITY, \
230 &api_funcs);
231
232 DT_INST_FOREACH_STATUS_OKAY(I2C_EEPROM_INIT)
233