blob: f32aee5ca12a09e27a73b7d829bce06108ee4ccb [file] [log] [blame]
/*
* Copyright (c) 2024 Vogl Electronic GmbH
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT litex_litei2c
#include <zephyr/device.h>
#include <zephyr/drivers/i2c.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i2c_litex_litei2c, CONFIG_I2C_LOG_LEVEL);
#include "i2c-priv.h"
#include <soc.h>
#define I2C_LITEX_ANY_HAS_IRQ DT_ANY_INST_HAS_PROP_STATUS_OKAY(interrupts)
#define I2C_LITEX_ALL_HAS_IRQ DT_ALL_INST_HAS_PROP_STATUS_OKAY(interrupts)
#define I2C_LITEX_HAS_IRQ UTIL_OR(I2C_LITEX_ALL_HAS_IRQ, config->has_irq)
#define MASTER_STATUS_TX_READY_OFFSET 0x0
#define MASTER_STATUS_RX_READY_OFFSET 0x1
#define MASTER_STATUS_NACK_OFFSET 0x8
struct i2c_litex_litei2c_config {
uint32_t phy_speed_mode_addr;
uint32_t master_active_addr;
uint32_t master_settings_addr;
uint32_t master_addr_addr;
uint32_t master_rxtx_addr;
uint32_t master_status_addr;
uint32_t bitrate;
#if I2C_LITEX_ANY_HAS_IRQ
uint32_t master_ev_pending_addr;
uint32_t master_ev_enable_addr;
void (*irq_config_func)(const struct device *dev);
#if !I2C_LITEX_ALL_HAS_IRQ
bool has_irq;
#endif /* !I2C_LITEX_ALL_HAS_IRQ */
#endif /* I2C_LITEX_ANY_HAS_IRQ */
};
struct i2c_litex_litei2c_data {
struct k_mutex mutex;
#if I2C_LITEX_ANY_HAS_IRQ
struct k_sem sem_rx_ready;
#endif /* I2C_LITEX_ANY_HAS_IRQ */
};
static int i2c_litex_configure(const struct device *dev, uint32_t dev_config)
{
const struct i2c_litex_litei2c_config *config = dev->config;
struct i2c_litex_litei2c_data *data = dev->data;
if (I2C_ADDR_10_BITS & dev_config) {
return -ENOTSUP;
}
if (!(I2C_MODE_CONTROLLER & dev_config)) {
return -ENOTSUP;
}
k_mutex_lock(&data->mutex, K_FOREVER);
/* Setup speed to use */
switch (I2C_SPEED_GET(dev_config)) {
case I2C_SPEED_STANDARD:
litex_write8(0, config->phy_speed_mode_addr);
break;
case I2C_SPEED_FAST:
litex_write8(1, config->phy_speed_mode_addr);
break;
case I2C_SPEED_FAST_PLUS:
litex_write8(2, config->phy_speed_mode_addr);
break;
default:
k_mutex_unlock(&data->mutex);
return -ENOTSUP;
}
k_mutex_unlock(&data->mutex);
return 0;
}
static int i2c_litex_get_config(const struct device *dev, uint32_t *config)
{
const struct i2c_litex_litei2c_config *dev_config = dev->config;
*config = I2C_MODE_CONTROLLER;
switch (litex_read8(dev_config->phy_speed_mode_addr)) {
case 0:
*config |= I2C_SPEED_SET(I2C_SPEED_STANDARD);
break;
case 1:
*config |= I2C_SPEED_SET(I2C_SPEED_FAST);
break;
case 2:
*config |= I2C_SPEED_SET(I2C_SPEED_FAST_PLUS);
break;
default:
break;
}
return 0;
}
static int i2c_litex_write_settings(const struct device *dev, uint8_t len_tx, uint8_t len_rx,
bool recover)
{
const struct i2c_litex_litei2c_config *config = dev->config;
uint32_t settings = len_tx | (len_rx << 8) | (recover << 16);
litex_write32(settings, config->master_settings_addr);
return 0;
}
static void i2c_litex_wait_for_rx_ready(const struct device *dev)
{
const struct i2c_litex_litei2c_config *config = dev->config;
#if I2C_LITEX_ANY_HAS_IRQ
struct i2c_litex_litei2c_data *data = dev->data;
if (I2C_LITEX_HAS_IRQ) {
/* Wait for the RX ready event */
k_sem_take(&data->sem_rx_ready, K_FOREVER);
return;
}
#endif /* I2C_LITEX_ANY_HAS_IRQ */
while (!(litex_read8(config->master_status_addr) &
BIT(MASTER_STATUS_RX_READY_OFFSET))) {
/* Wait until RX is ready */
}
}
static int i2c_litex_transfer(const struct device *dev, struct i2c_msg *msgs, uint8_t num_msgs,
uint16_t addr)
{
const struct i2c_litex_litei2c_config *config = dev->config;
struct i2c_litex_litei2c_data *data = dev->data;
uint32_t len_tx_buf = 0;
uint32_t len_rx_buf = 0;
uint8_t len_tx = 0;
uint8_t len_rx = 0;
uint8_t *tx_buf_ptr;
uint8_t *rx_buf_ptr;
uint32_t tx_buf;
uint32_t rx_buf;
uint32_t tx_j = 0;
uint32_t rx_j = 0;
int ret = 0;
k_mutex_lock(&data->mutex, K_FOREVER);
litex_write8(1, config->master_active_addr);
/* Flush RX buffer */
while ((litex_read8(config->master_status_addr) &
BIT(MASTER_STATUS_RX_READY_OFFSET))) {
rx_buf = litex_read32(config->master_rxtx_addr);
LOG_DBG("flushed rxd: 0x%x", rx_buf);
}
while (!(litex_read8(config->master_status_addr) &
BIT(MASTER_STATUS_TX_READY_OFFSET))) {
(void)litex_read32(config->master_rxtx_addr);
}
#if I2C_LITEX_ANY_HAS_IRQ
if (I2C_LITEX_HAS_IRQ) {
litex_write8(BIT(0), config->master_ev_enable_addr);
litex_write8(BIT(0), config->master_ev_pending_addr);
k_sem_reset(&data->sem_rx_ready);
}
#endif /* I2C_LITEX_ANY_HAS_IRQ */
LOG_DBG("addr: 0x%x", addr);
litex_write8((uint8_t)addr, config->master_addr_addr);
for (uint8_t i = 0; i < num_msgs; i++) {
if (msgs[i].flags & I2C_MSG_READ) {
len_tx_buf = 0;
len_rx_buf = msgs[i].len;
rx_buf_ptr = msgs[i].buf;
tx_buf_ptr = NULL;
} else {
len_tx_buf = msgs[i].len;
tx_buf_ptr = msgs[i].buf;
if (!(msgs[i].flags & I2C_MSG_STOP) && (i + 1 < num_msgs) &&
(msgs[i + 1].flags & I2C_MSG_READ) &&
(msgs[i + 1].flags & I2C_MSG_RESTART)) {
i++;
len_rx_buf = msgs[i].len;
rx_buf_ptr = msgs[i].buf;
} else {
len_rx_buf = 0;
rx_buf_ptr = NULL;
}
}
LOG_HEXDUMP_DBG(tx_buf_ptr, len_tx_buf, "tx_buf");
tx_j = 0;
rx_j = 0;
do {
if (len_tx_buf > (tx_j + 4)) {
len_tx = 5;
len_rx = 0;
} else {
len_tx = len_tx_buf - tx_j;
if (len_rx_buf > (rx_j + 4)) {
len_rx = 5;
} else {
len_rx = len_rx_buf - rx_j;
}
}
tx_buf = 0;
switch (len_tx) {
case 5:
case 4:
tx_buf |= tx_buf_ptr[0 + tx_j] << 24;
tx_buf |= tx_buf_ptr[1 + tx_j] << 16;
tx_buf |= tx_buf_ptr[2 + tx_j] << 8;
tx_buf |= tx_buf_ptr[3 + tx_j];
tx_j += 4;
break;
case 3:
tx_buf |= tx_buf_ptr[0 + tx_j] << 16;
tx_buf |= tx_buf_ptr[1 + tx_j] << 8;
tx_buf |= tx_buf_ptr[2 + tx_j];
tx_j += 3;
break;
case 2:
tx_buf |= tx_buf_ptr[0 + tx_j] << 8;
tx_buf |= tx_buf_ptr[1 + tx_j];
tx_j += 2;
break;
case 1:
tx_buf |= tx_buf_ptr[0 + tx_j];
tx_j += 1;
break;
default:
break;
}
LOG_DBG("len_tx: %d, len_rx: %d", len_tx, len_rx);
i2c_litex_write_settings(dev, len_tx, len_rx, false);
LOG_DBG("tx_buf: 0x%x", tx_buf);
litex_write32(tx_buf, config->master_rxtx_addr);
i2c_litex_wait_for_rx_ready(dev);
if (litex_read16(config->master_status_addr) &
BIT(MASTER_STATUS_NACK_OFFSET)) {
LOG_DBG("NACK received (addr: 0x%x)", addr);
ret = -EIO;
}
rx_buf = litex_read32(config->master_rxtx_addr);
LOG_DBG("rx_buf: 0x%x", rx_buf);
switch (len_rx) {
case 5:
case 4:
rx_buf_ptr[0 + rx_j] = rx_buf >> 24;
rx_buf_ptr[1 + rx_j] = rx_buf >> 16;
rx_buf_ptr[2 + rx_j] = rx_buf >> 8;
rx_buf_ptr[3 + rx_j] = rx_buf;
rx_j += 4;
break;
case 3:
rx_buf_ptr[0 + rx_j] = rx_buf >> 16;
rx_buf_ptr[1 + rx_j] = rx_buf >> 8;
rx_buf_ptr[2 + rx_j] = rx_buf;
rx_j += 3;
break;
case 2:
rx_buf_ptr[0 + rx_j] = rx_buf >> 8;
rx_buf_ptr[1 + rx_j] = rx_buf;
rx_j += 2;
break;
case 1:
rx_buf_ptr[0 + rx_j] = rx_buf;
rx_j += 1;
break;
default:
break;
}
if (ret < 0) {
goto transfer_end;
}
} while ((tx_j < len_tx_buf) || (rx_j < len_rx_buf));
LOG_HEXDUMP_DBG(rx_buf_ptr, len_rx_buf, "rx_buf");
}
transfer_end:
litex_write8(0, config->master_active_addr);
#if I2C_LITEX_ANY_HAS_IRQ
if (I2C_LITEX_HAS_IRQ) {
litex_write8(0, config->master_ev_enable_addr);
}
#endif /* I2C_LITEX_ANY_HAS_IRQ */
k_mutex_unlock(&data->mutex);
return ret;
}
static int i2c_litex_recover_bus(const struct device *dev)
{
const struct i2c_litex_litei2c_config *config = dev->config;
struct i2c_litex_litei2c_data *data = dev->data;
k_mutex_lock(&data->mutex, K_FOREVER);
litex_write8(1, config->master_active_addr);
i2c_litex_write_settings(dev, 0, 0, true);
while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_TX_READY_OFFSET))) {
/* Wait for TX ready */
}
litex_write32(0, config->master_rxtx_addr);
while (!(litex_read8(config->master_status_addr) & BIT(MASTER_STATUS_RX_READY_OFFSET))) {
/* Wait for RX data */
}
(void)litex_read32(config->master_rxtx_addr);
litex_write8(0, config->master_active_addr);
k_mutex_unlock(&data->mutex);
return 0;
}
#if I2C_LITEX_ANY_HAS_IRQ
static void i2c_litex_irq_handler(const struct device *dev)
{
const struct i2c_litex_litei2c_config *config = dev->config;
struct i2c_litex_litei2c_data *data = dev->data;
if (litex_read8(config->master_ev_pending_addr) & BIT(0)) {
k_sem_give(&data->sem_rx_ready);
/* ack reader irq */
litex_write8(BIT(0), config->master_ev_pending_addr);
}
}
#endif /* I2C_LITEX_ANY_HAS_IRQ */
static int i2c_litex_init(const struct device *dev)
{
const struct i2c_litex_litei2c_config *config = dev->config;
int ret;
ret = i2c_litex_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
if (ret != 0) {
LOG_ERR("failed to configure I2C: %d", ret);
}
#if I2C_LITEX_ANY_HAS_IRQ
if (I2C_LITEX_HAS_IRQ) {
/* Disable interrupts initially */
litex_write8(0, config->master_ev_enable_addr);
config->irq_config_func(dev);
}
#endif /* I2C_LITEX_ANY_HAS_IRQ */
return ret;
}
static DEVICE_API(i2c, i2c_litex_litei2c_driver_api) = {
.configure = i2c_litex_configure,
.get_config = i2c_litex_get_config,
.transfer = i2c_litex_transfer,
.recover_bus = i2c_litex_recover_bus,
#ifdef CONFIG_I2C_RTIO
.iodev_submit = i2c_iodev_submit_fallback,
#endif
};
/* Device Instantiation */
#define I2C_LITEX_IRQ(n) \
BUILD_ASSERT(DT_INST_REG_HAS_NAME(n, master_ev_pending) && \
DT_INST_REG_HAS_NAME(n, master_ev_enable), "registers for interrupts missing"); \
\
static void i2c_litex_irq_config##n(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(n), DT_INST_IRQ(n, priority), i2c_litex_irq_handler, \
DEVICE_DT_INST_GET(n), 0); \
\
irq_enable(DT_INST_IRQN(n)); \
};
#define I2C_LITEC_IRQ_DATA(n) \
.sem_rx_ready = Z_SEM_INITIALIZER(i2c_litex_litei2c_data_##n.sem_rx_ready, 0, 1),
#define I2C_LITEC_IRQ_CONFIG(n) \
IF_DISABLED(I2C_LITEX_ALL_HAS_IRQ, (.has_irq = DT_INST_IRQ_HAS_IDX(n, 0),)) \
.master_ev_pending_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_pending, 0), \
.master_ev_enable_addr = DT_INST_REG_ADDR_BY_NAME_OR(n, master_ev_enable, 0), \
.irq_config_func = COND_CODE_1(DT_INST_IRQ_HAS_IDX(n, 0), \
(i2c_litex_irq_config##n), (NULL)),
#define I2C_LITEX_INIT(n) \
IF_ENABLED(DT_INST_IRQ_HAS_IDX(n, 0), (I2C_LITEX_IRQ(n))) \
\
static struct i2c_litex_litei2c_data i2c_litex_litei2c_data_##n = { \
.mutex = Z_MUTEX_INITIALIZER(i2c_litex_litei2c_data_##n.mutex), \
IF_ENABLED(I2C_LITEX_ANY_HAS_IRQ, (I2C_LITEC_IRQ_DATA(n))) \
}; \
\
static const struct i2c_litex_litei2c_config i2c_litex_litei2c_config_##n = { \
.phy_speed_mode_addr = DT_INST_REG_ADDR_BY_NAME(n, phy_speed_mode), \
.master_active_addr = DT_INST_REG_ADDR_BY_NAME(n, master_active), \
.master_settings_addr = DT_INST_REG_ADDR_BY_NAME(n, master_settings), \
.master_addr_addr = DT_INST_REG_ADDR_BY_NAME(n, master_addr), \
.master_rxtx_addr = DT_INST_REG_ADDR_BY_NAME(n, master_rxtx), \
.master_status_addr = DT_INST_REG_ADDR_BY_NAME(n, master_status), \
.bitrate = DT_INST_PROP(n, clock_frequency), \
IF_ENABLED(I2C_LITEX_ANY_HAS_IRQ, (I2C_LITEC_IRQ_CONFIG(n))) \
}; \
\
I2C_DEVICE_DT_INST_DEFINE(n, i2c_litex_init, NULL, &i2c_litex_litei2c_data_##n, \
&i2c_litex_litei2c_config_##n, POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, &i2c_litex_litei2c_driver_api);
DT_INST_FOREACH_STATUS_OKAY(I2C_LITEX_INIT)