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