You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
167 lines
4.7 KiB
167 lines
4.7 KiB
/* |
|
* Copyright 2020 Google LLC |
|
* Copyright (c) 2020 Nordic Semiconductor ASA |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
#define DT_DRV_COMPAT atmel_at24 |
|
|
|
#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL |
|
#include <zephyr/logging/log.h> |
|
LOG_MODULE_REGISTER(atmel_at24); |
|
|
|
#include <zephyr/device.h> |
|
#include <zephyr/drivers/emul.h> |
|
#include <zephyr/drivers/i2c.h> |
|
#include <zephyr/drivers/i2c_emul.h> |
|
|
|
/** Run-time data used by the emulator */ |
|
struct at24_emul_data { |
|
/** I2C emulator detail */ |
|
struct i2c_emul emul; |
|
/** AT24 device being emulated */ |
|
const struct device *i2c; |
|
/** Current register to read (address) */ |
|
uint32_t cur_reg; |
|
}; |
|
|
|
/** Static configuration for the emulator */ |
|
struct at24_emul_cfg { |
|
/** EEPROM data contents */ |
|
uint8_t *buf; |
|
/** Size of EEPROM in bytes */ |
|
uint32_t size; |
|
/** Address of EEPROM on i2c bus */ |
|
uint16_t addr; |
|
/** Address width for EEPROM in bits (only 8 is supported at present) */ |
|
uint8_t addr_width; |
|
}; |
|
|
|
/** |
|
* Emulator an I2C transfer to an AT24 chip |
|
* |
|
* This handles simple reads and writes |
|
* |
|
* @param emul I2C emulation information |
|
* @param msgs List of messages to process. For 'read' messages, this function |
|
* updates the 'buf' member with the data that was read |
|
* @param num_msgs Number of messages to process |
|
* @param addr Address of the I2C target device. This is assumed to be correct, |
|
* due to the |
|
* @retval 0 If successful |
|
* @retval -EIO General input / output error |
|
*/ |
|
static int at24_emul_transfer(const struct emul *target, struct i2c_msg *msgs, |
|
int num_msgs, int addr) |
|
{ |
|
struct at24_emul_data *data; |
|
const struct at24_emul_cfg *cfg; |
|
unsigned int len; |
|
bool too_fast; |
|
uint32_t i2c_cfg; |
|
|
|
data = target->data; |
|
cfg = target->cfg; |
|
|
|
if (cfg->addr != addr) { |
|
LOG_ERR("Address mismatch, expected %02x, got %02x", cfg->addr, |
|
addr); |
|
return -EIO; |
|
} |
|
|
|
if (i2c_get_config(data->i2c, &i2c_cfg)) { |
|
LOG_ERR("i2c_get_config failed"); |
|
return -EIO; |
|
} |
|
/* For testing purposes, fail if the bus speed is above standard */ |
|
too_fast = (I2C_SPEED_GET(i2c_cfg) > I2C_SPEED_STANDARD); |
|
if (too_fast) { |
|
LOG_ERR("Speed too high"); |
|
return -EIO; |
|
} |
|
|
|
i2c_dump_msgs_rw(target->dev, msgs, num_msgs, addr, false); |
|
switch (num_msgs) { |
|
case 1: |
|
if (msgs->flags & I2C_MSG_READ) { |
|
/* handle read */ |
|
break; |
|
} |
|
data->cur_reg = msgs->buf[0]; |
|
len = MIN(msgs->len - 1, cfg->size - data->cur_reg); |
|
memcpy(&cfg->buf[data->cur_reg], &msgs->buf[1], len); |
|
return 0; |
|
case 2: |
|
if (msgs->flags & I2C_MSG_READ) { |
|
LOG_ERR("Unexpected read"); |
|
return -EIO; |
|
} |
|
data->cur_reg = msgs->buf[0]; |
|
|
|
/* Now process the 'read' part of the message */ |
|
msgs++; |
|
if (!(msgs->flags & I2C_MSG_READ)) { |
|
LOG_ERR("Unexpected write"); |
|
return -EIO; |
|
} |
|
break; |
|
default: |
|
LOG_ERR("Invalid number of messages"); |
|
return -EIO; |
|
} |
|
|
|
/* Read data from the EEPROM into the buffer */ |
|
len = MIN(msgs->len, cfg->size - data->cur_reg); |
|
memcpy(msgs->buf, &cfg->buf[data->cur_reg], len); |
|
data->cur_reg += len; |
|
|
|
return 0; |
|
} |
|
|
|
/* Device instantiation */ |
|
|
|
static struct i2c_emul_api bus_api = { |
|
.transfer = at24_emul_transfer, |
|
}; |
|
|
|
/** |
|
* Set up a new AT24 emulator |
|
* |
|
* This should be called for each AT24 device that needs to be emulated. It |
|
* registers it with the I2C emulation controller. |
|
* |
|
* @param target Emulation information |
|
* @param parent Device to emulate (must use AT24 driver) |
|
* @return 0 indicating success (always) |
|
*/ |
|
static int emul_atmel_at24_init(const struct emul *target, const struct device *parent) |
|
{ |
|
const struct at24_emul_cfg *cfg = target->cfg; |
|
struct at24_emul_data *data = target->data; |
|
|
|
data->emul.api = &bus_api; |
|
data->emul.addr = cfg->addr; |
|
data->emul.target = target; |
|
data->i2c = parent; |
|
data->cur_reg = 0; |
|
|
|
/* Start with an erased EEPROM, assuming all 0xff */ |
|
memset(cfg->buf, 0xff, cfg->size); |
|
|
|
return 0; |
|
} |
|
|
|
#define EEPROM_AT24_EMUL(n) \ |
|
static uint8_t at24_emul_buf_##n[DT_INST_PROP(n, size)]; \ |
|
static struct at24_emul_data at24_emul_data_##n; \ |
|
static const struct at24_emul_cfg at24_emul_cfg_##n = { \ |
|
.buf = at24_emul_buf_##n, \ |
|
.size = DT_INST_PROP(n, size), \ |
|
.addr = DT_INST_REG_ADDR(n), \ |
|
.addr_width = 8, \ |
|
}; \ |
|
EMUL_DT_INST_DEFINE(n, emul_atmel_at24_init, &at24_emul_data_##n, &at24_emul_cfg_##n, \ |
|
&bus_api, NULL) |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(EEPROM_AT24_EMUL)
|
|
|