Updates the API and types to match updated I2C terminology. Replaces master with controller and slave with target. Updates all drivers to match the changed macros, types, and API signatures. Signed-off-by: Tom Burdick <thomas.burdick@intel.com>
229 lines
4.6 KiB
C
229 lines
4.6 KiB
C
/*
|
|
* Copyright (c) 2017 BayLibre, SAS
|
|
*
|
|
* SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
#include <zephyr/drivers/clock_control/stm32_clock_control.h>
|
|
#include <zephyr/drivers/clock_control.h>
|
|
#include <zephyr/sys/util.h>
|
|
#include <zephyr/kernel.h>
|
|
#include <errno.h>
|
|
#include <zephyr/drivers/i2c.h>
|
|
|
|
#define LOG_LEVEL CONFIG_I2C_LOG_LEVEL
|
|
#include <zephyr/logging/log.h>
|
|
LOG_MODULE_DECLARE(main);
|
|
|
|
struct i2c_virtual_data {
|
|
sys_slist_t targets;
|
|
};
|
|
|
|
int i2c_virtual_runtime_configure(const struct device *dev, uint32_t config)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static struct i2c_target_config *find_address(struct i2c_virtual_data *data,
|
|
uint16_t address, bool is_10bit)
|
|
{
|
|
struct i2c_target_config *cfg = NULL;
|
|
sys_snode_t *node;
|
|
bool search_10bit;
|
|
|
|
SYS_SLIST_FOR_EACH_NODE(&data->targets, node) {
|
|
cfg = CONTAINER_OF(node, struct i2c_target_config, node);
|
|
|
|
search_10bit = (cfg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS);
|
|
|
|
if (cfg->address == address && search_10bit == is_10bit) {
|
|
return cfg;
|
|
}
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/* Attach I2C targets */
|
|
int i2c_virtual_target_register(const struct device *dev,
|
|
struct i2c_target_config *config)
|
|
{
|
|
struct i2c_virtual_data *data = dev->data;
|
|
|
|
if (!config) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
/* Check the address is unique */
|
|
if (find_address(data, config->address,
|
|
(config->flags & I2C_TARGET_FLAGS_ADDR_10_BITS))) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
sys_slist_append(&data->targets, &config->node);
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
int i2c_virtual_target_unregister(const struct device *dev,
|
|
struct i2c_target_config *config)
|
|
{
|
|
struct i2c_virtual_data *data = dev->data;
|
|
|
|
if (!config) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
if (!sys_slist_find_and_remove(&data->targets, &config->node)) {
|
|
return -EINVAL;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int i2c_virtual_msg_write(const struct device *dev,
|
|
struct i2c_msg *msg,
|
|
struct i2c_target_config *config,
|
|
bool prev_write)
|
|
{
|
|
unsigned int len = 0U;
|
|
uint8_t *buf = msg->buf;
|
|
int ret;
|
|
|
|
if (!prev_write) {
|
|
config->callbacks->write_requested(config);
|
|
}
|
|
|
|
len = msg->len;
|
|
while (len) {
|
|
|
|
ret = config->callbacks->write_received(config, *buf);
|
|
if (ret) {
|
|
goto error;
|
|
}
|
|
buf++;
|
|
len--;
|
|
}
|
|
|
|
if (!(msg->flags & I2C_MSG_RESTART) && msg->flags & I2C_MSG_STOP) {
|
|
config->callbacks->stop(config);
|
|
}
|
|
|
|
return 0;
|
|
error:
|
|
LOG_DBG("%s: NACK", __func__);
|
|
|
|
return -EIO;
|
|
}
|
|
|
|
static int i2c_virtual_msg_read(const struct device *dev, struct i2c_msg *msg,
|
|
struct i2c_target_config *config)
|
|
{
|
|
unsigned int len = msg->len;
|
|
uint8_t *buf = msg->buf;
|
|
|
|
if (!msg->len) {
|
|
return 0;
|
|
}
|
|
|
|
config->callbacks->read_requested(config, buf);
|
|
buf++;
|
|
len--;
|
|
|
|
while (len) {
|
|
config->callbacks->read_processed(config, buf);
|
|
buf++;
|
|
len--;
|
|
}
|
|
|
|
if (!(msg->flags & I2C_MSG_RESTART) && msg->flags & I2C_MSG_STOP) {
|
|
config->callbacks->stop(config);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
#define OPERATION(msg) (((struct i2c_msg *) msg)->flags & I2C_MSG_RW_MASK)
|
|
|
|
static int i2c_virtual_transfer(const struct device *dev, struct i2c_msg *msg,
|
|
uint8_t num_msgs, uint16_t target)
|
|
{
|
|
struct i2c_virtual_data *data = dev->data;
|
|
struct i2c_msg *current, *next;
|
|
struct i2c_target_config *cfg;
|
|
bool is_write = false;
|
|
int ret = 0;
|
|
|
|
cfg = find_address(data, target, (msg->flags & I2C_TARGET_FLAGS_ADDR_10_BITS));
|
|
if (!cfg) {
|
|
return -EIO;
|
|
}
|
|
|
|
current = msg;
|
|
current->flags |= I2C_MSG_RESTART;
|
|
while (num_msgs > 0) {
|
|
if (num_msgs > 1) {
|
|
next = current + 1;
|
|
|
|
/*
|
|
* Stop or restart condition between messages
|
|
* of different directions is required
|
|
*/
|
|
if (OPERATION(current) != OPERATION(next)) {
|
|
if (!(next->flags & I2C_MSG_RESTART)) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Stop condition is required for the last message */
|
|
if ((num_msgs == 1U) && !(current->flags & I2C_MSG_STOP)) {
|
|
ret = -EINVAL;
|
|
break;
|
|
}
|
|
|
|
if ((current->flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) {
|
|
ret = i2c_virtual_msg_write(dev, current,
|
|
cfg, is_write);
|
|
is_write = true;
|
|
} else {
|
|
ret = i2c_virtual_msg_read(dev, current, cfg);
|
|
is_write = false;
|
|
}
|
|
|
|
if (ret < 0) {
|
|
break;
|
|
}
|
|
|
|
current++;
|
|
num_msgs--;
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
static const struct i2c_driver_api api_funcs = {
|
|
.configure = i2c_virtual_runtime_configure,
|
|
.transfer = i2c_virtual_transfer,
|
|
.target_register = i2c_virtual_target_register,
|
|
.target_unregister = i2c_virtual_target_unregister,
|
|
};
|
|
|
|
static int i2c_virtual_init(const struct device *dev)
|
|
{
|
|
struct i2c_virtual_data *data = dev->data;
|
|
|
|
sys_slist_init(&data->targets);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static struct i2c_virtual_data i2c_virtual_dev_data_0;
|
|
|
|
DEVICE_DEFINE(i2c_virtual_0, CONFIG_I2C_VIRTUAL_NAME, &i2c_virtual_init,
|
|
NULL, &i2c_virtual_dev_data_0, NULL,
|
|
POST_KERNEL, CONFIG_KERNEL_INIT_PRIORITY_DEVICE,
|
|
&api_funcs);
|