/* * Copyright (c) 2017 BayLibre, SAS * * SPDX-License-Identifier: Apache-2.0 */ #define DT_DRV_COMPAT zephyr_i2c_target_eeprom #include #include #include #include #include #include #define LOG_LEVEL CONFIG_I2C_LOG_LEVEL #include LOG_MODULE_REGISTER(i2c_target); struct i2c_eeprom_target_data { struct i2c_target_config config; uint32_t buffer_size; uint8_t *buffer; uint32_t buffer_idx; bool first_write; }; struct i2c_eeprom_target_config { struct i2c_dt_spec bus; uint32_t buffer_size; uint8_t *buffer; }; int eeprom_target_program(const struct device *dev, const uint8_t *eeprom_data, unsigned int length) { struct i2c_eeprom_target_data *data = dev->data; if (length > data->buffer_size) { return -EINVAL; } memcpy(data->buffer, eeprom_data, length); return 0; } int eeprom_target_read(const struct device *dev, uint8_t *eeprom_data, unsigned int offset) { struct i2c_eeprom_target_data *data = dev->data; if (!data || offset >= data->buffer_size) { return -EINVAL; } *eeprom_data = data->buffer[offset]; return 0; } #ifdef CONFIG_I2C_EEPROM_TARGET_RUNTIME_ADDR int eeprom_target_set_addr(const struct device *dev, uint8_t addr) { const struct i2c_eeprom_target_config *cfg = dev->config; struct i2c_eeprom_target_data *data = dev->data; int ret; ret = i2c_target_unregister(cfg->bus.bus, &data->config); if (ret) { LOG_DBG("eeprom target failed to unregister"); return ret; } data->config.address = addr; return i2c_target_register(cfg->bus.bus, &data->config); } #endif /* CONFIG_I2C_EEPROM_TARGET_RUNTIME_ADDR */ static int eeprom_target_write_requested(struct i2c_target_config *config) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_data, config); LOG_DBG("eeprom: write req"); data->first_write = true; return 0; } static int eeprom_target_read_requested(struct i2c_target_config *config, uint8_t *val) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_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_target_write_received(struct i2c_target_config *config, uint8_t val) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_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_target_read_processed(struct i2c_target_config *config, uint8_t *val) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_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_target_stop(struct i2c_target_config *config) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_data, config); LOG_DBG("eeprom: stop"); data->first_write = true; return 0; } #ifdef CONFIG_I2C_TARGET_BUFFER_MODE static void eeprom_target_buf_write_received(struct i2c_target_config *config, uint8_t *ptr, uint32_t len) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_data, config); /* The first byte is offset */ data->buffer_idx = *ptr; memcpy(&data->buffer[data->buffer_idx], ptr + 1, len - 1); } static int eeprom_target_buf_read_requested(struct i2c_target_config *config, uint8_t **ptr, uint32_t *len) { struct i2c_eeprom_target_data *data = CONTAINER_OF(config, struct i2c_eeprom_target_data, config); *ptr = &data->buffer[data->buffer_idx]; *len = data->buffer_size; return 0; } #endif static int eeprom_target_register(const struct device *dev) { const struct i2c_eeprom_target_config *cfg = dev->config; struct i2c_eeprom_target_data *data = dev->data; return i2c_target_register(cfg->bus.bus, &data->config); } static int eeprom_target_unregister(const struct device *dev) { const struct i2c_eeprom_target_config *cfg = dev->config; struct i2c_eeprom_target_data *data = dev->data; return i2c_target_unregister(cfg->bus.bus, &data->config); } static const struct i2c_target_driver_api api_funcs = { .driver_register = eeprom_target_register, .driver_unregister = eeprom_target_unregister, }; static const struct i2c_target_callbacks eeprom_callbacks = { .write_requested = eeprom_target_write_requested, .read_requested = eeprom_target_read_requested, .write_received = eeprom_target_write_received, .read_processed = eeprom_target_read_processed, #ifdef CONFIG_I2C_TARGET_BUFFER_MODE .buf_write_received = eeprom_target_buf_write_received, .buf_read_requested = eeprom_target_buf_read_requested, #endif .stop = eeprom_target_stop, }; static int i2c_eeprom_target_init(const struct device *dev) { struct i2c_eeprom_target_data *data = dev->data; const struct i2c_eeprom_target_config *cfg = dev->config; if (!device_is_ready(cfg->bus.bus)) { LOG_ERR("I2C controller device not ready"); return -ENODEV; } data->buffer_size = cfg->buffer_size; data->buffer = cfg->buffer; data->config.address = cfg->bus.addr; data->config.callbacks = &eeprom_callbacks; return 0; } #define I2C_EEPROM_INIT(inst) \ static struct i2c_eeprom_target_data \ i2c_eeprom_target_##inst##_dev_data; \ \ static uint8_t \ i2c_eeprom_target_##inst##_buffer[(DT_INST_PROP(inst, size))]; \ \ static const struct i2c_eeprom_target_config \ i2c_eeprom_target_##inst##_cfg = { \ .bus = I2C_DT_SPEC_INST_GET(inst), \ .buffer_size = DT_INST_PROP(inst, size), \ .buffer = i2c_eeprom_target_##inst##_buffer \ }; \ \ DEVICE_DT_INST_DEFINE(inst, \ &i2c_eeprom_target_init, \ NULL, \ &i2c_eeprom_target_##inst##_dev_data, \ &i2c_eeprom_target_##inst##_cfg, \ POST_KERNEL, \ CONFIG_I2C_TARGET_INIT_PRIORITY, \ &api_funcs); DT_INST_FOREACH_STATUS_OKAY(I2C_EEPROM_INIT)