| /* |
| * Copyright (c) 2017 BayLibre, SAS |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <sys/util.h> |
| #include <kernel.h> |
| #include <errno.h> |
| #include <drivers/i2c.h> |
| #include <string.h> |
| #include <drivers/i2c/slave/eeprom.h> |
| |
| #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL |
| #include <logging/log.h> |
| LOG_MODULE_REGISTER(i2c_slave); |
| |
| struct i2c_eeprom_slave_data { |
| struct device *i2c_controller; |
| struct i2c_slave_config config; |
| u32_t buffer_size; |
| u8_t *buffer; |
| u32_t buffer_idx; |
| bool first_write; |
| }; |
| |
| struct i2c_eeprom_slave_config { |
| char *controller_dev_name; |
| u8_t address; |
| u32_t buffer_size; |
| u8_t *buffer; |
| }; |
| |
| /* convenience defines */ |
| #define DEV_CFG(dev) \ |
| ((const struct i2c_eeprom_slave_config * const) \ |
| (dev)->config->config_info) |
| #define DEV_DATA(dev) \ |
| ((struct i2c_eeprom_slave_data * const)(dev)->driver_data) |
| |
| int eeprom_slave_program(struct device *dev, u8_t *eeprom_data, |
| unsigned int length) |
| { |
| struct i2c_eeprom_slave_data *data = dev->driver_data; |
| |
| if (length > data->buffer_size) { |
| return -EINVAL; |
| } |
| |
| memcpy(data->buffer, eeprom_data, length); |
| |
| return 0; |
| } |
| |
| int eeprom_slave_read(struct device *dev, u8_t *eeprom_data, |
| unsigned int offset) |
| { |
| struct i2c_eeprom_slave_data *data = dev->driver_data; |
| |
| if (!data || offset >= data->buffer_size) { |
| return -EINVAL; |
| } |
| |
| *eeprom_data = data->buffer[offset]; |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_write_requested(struct i2c_slave_config *config) |
| { |
| struct i2c_eeprom_slave_data *data = CONTAINER_OF(config, |
| struct i2c_eeprom_slave_data, |
| config); |
| |
| LOG_DBG("eeprom: write req"); |
| |
| data->first_write = true; |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_read_requested(struct i2c_slave_config *config, |
| u8_t *val) |
| { |
| struct i2c_eeprom_slave_data *data = CONTAINER_OF(config, |
| struct i2c_eeprom_slave_data, |
| config); |
| |
| *val = data->buffer[data->buffer_idx]; |
| |
| LOG_DBG("eeprom: read req, val=0x%x", *val); |
| |
| /* Increment will be done in the read_processed callback */ |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_write_received(struct i2c_slave_config *config, |
| u8_t val) |
| { |
| struct i2c_eeprom_slave_data *data = CONTAINER_OF(config, |
| struct i2c_eeprom_slave_data, |
| config); |
| |
| LOG_DBG("eeprom: write done, val=0x%x", val); |
| |
| /* In case EEPROM wants to be R/O, return !0 here could trigger |
| * a NACK to the I2C controller, support depends on the |
| * I2C controller support |
| */ |
| |
| if (data->first_write) { |
| data->buffer_idx = val; |
| data->first_write = false; |
| } else { |
| data->buffer[data->buffer_idx++] = val; |
| } |
| |
| data->buffer_idx = data->buffer_idx % data->buffer_size; |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_read_processed(struct i2c_slave_config *config, |
| u8_t *val) |
| { |
| struct i2c_eeprom_slave_data *data = CONTAINER_OF(config, |
| struct i2c_eeprom_slave_data, |
| config); |
| |
| /* Increment here */ |
| data->buffer_idx = (data->buffer_idx + 1) % data->buffer_size; |
| |
| *val = data->buffer[data->buffer_idx]; |
| |
| LOG_DBG("eeprom: read done, val=0x%x", *val); |
| |
| /* Increment will be done in the next read_processed callback |
| * In case of STOP, the byte won't be taken in account |
| */ |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_stop(struct i2c_slave_config *config) |
| { |
| struct i2c_eeprom_slave_data *data = CONTAINER_OF(config, |
| struct i2c_eeprom_slave_data, |
| config); |
| |
| LOG_DBG("eeprom: stop"); |
| |
| data->first_write = true; |
| |
| return 0; |
| } |
| |
| static int eeprom_slave_register(struct device *dev) |
| { |
| struct i2c_eeprom_slave_data *data = dev->driver_data; |
| |
| return i2c_slave_register(data->i2c_controller, &data->config); |
| } |
| |
| static int eeprom_slave_unregister(struct device *dev) |
| { |
| struct i2c_eeprom_slave_data *data = dev->driver_data; |
| |
| return i2c_slave_unregister(data->i2c_controller, &data->config); |
| } |
| |
| static const struct i2c_slave_driver_api api_funcs = { |
| .driver_register = eeprom_slave_register, |
| .driver_unregister = eeprom_slave_unregister, |
| }; |
| |
| static const struct i2c_slave_callbacks eeprom_callbacks = { |
| .write_requested = eeprom_slave_write_requested, |
| .read_requested = eeprom_slave_read_requested, |
| .write_received = eeprom_slave_write_received, |
| .read_processed = eeprom_slave_read_processed, |
| .stop = eeprom_slave_stop, |
| }; |
| |
| static int i2c_eeprom_slave_init(struct device *dev) |
| { |
| struct i2c_eeprom_slave_data *data = DEV_DATA(dev); |
| const struct i2c_eeprom_slave_config *cfg = DEV_CFG(dev); |
| |
| data->i2c_controller = |
| device_get_binding(cfg->controller_dev_name); |
| if (!data->i2c_controller) { |
| LOG_ERR("i2c controller not found: %s", |
| cfg->controller_dev_name); |
| return -EINVAL; |
| } |
| |
| data->buffer_size = cfg->buffer_size; |
| data->buffer = cfg->buffer; |
| data->config.address = cfg->address; |
| data->config.callbacks = &eeprom_callbacks; |
| |
| return 0; |
| } |
| |
| #ifdef DT_INST_0_ATMEL_AT24 |
| |
| static struct i2c_eeprom_slave_data i2c_eeprom_slave_0_dev_data; |
| |
| static u8_t i2c_eeprom_slave_0_buffer[(DT_INST_0_ATMEL_AT24_SIZE * 1024)]; |
| |
| static const struct i2c_eeprom_slave_config i2c_eeprom_slave_0_cfg = { |
| .controller_dev_name = DT_INST_0_ATMEL_AT24_BUS_NAME, |
| .address = DT_INST_0_ATMEL_AT24_BASE_ADDRESS, |
| .buffer_size = (DT_INST_0_ATMEL_AT24_SIZE * 1024), |
| .buffer = i2c_eeprom_slave_0_buffer |
| }; |
| |
| DEVICE_AND_API_INIT(i2c_eeprom_slave_0, DT_INST_0_ATMEL_AT24_LABEL, |
| &i2c_eeprom_slave_init, |
| &i2c_eeprom_slave_0_dev_data, &i2c_eeprom_slave_0_cfg, |
| POST_KERNEL, CONFIG_I2C_SLAVE_INIT_PRIORITY, |
| &api_funcs); |
| |
| #endif /* DT_INST_0_ATMEL_AT24 */ |
| |
| #ifdef DT_INST_1_ATMEL_AT24 |
| |
| static struct i2c_eeprom_slave_data i2c_eeprom_slave_1_dev_data; |
| |
| static u8_t i2c_eeprom_slave_1_buffer[(DT_INST_1_ATMEL_AT24_SIZE * 1024)]; |
| |
| static const struct i2c_eeprom_slave_config i2c_eeprom_slave_1_cfg = { |
| .controller_dev_name = DT_INST_1_ATMEL_AT24_BUS_NAME, |
| .address = DT_INST_1_ATMEL_AT24_BASE_ADDRESS, |
| .buffer_size = (DT_INST_1_ATMEL_AT24_SIZE * 1024), |
| .buffer = i2c_eeprom_slave_1_buffer |
| }; |
| |
| DEVICE_AND_API_INIT(i2c_eeprom_slave_1, DT_INST_1_ATMEL_AT24_LABEL, |
| &i2c_eeprom_slave_init, |
| &i2c_eeprom_slave_1_dev_data, &i2c_eeprom_slave_1_cfg, |
| POST_KERNEL, CONFIG_I2C_SLAVE_INIT_PRIORITY, |
| &api_funcs); |
| |
| #endif /* DT_INST_1_ATMEL_AT24 */ |