zephyr/drivers/i2c/i2c_numaker.c

785 lines
25 KiB
C

/*
* Copyright (c) 2023 Nuvoton Technology Corporation.
*
* SPDX-License-Identifier: Apache-2.0
*/
#define DT_DRV_COMPAT nuvoton_numaker_i2c
#include <zephyr/drivers/i2c.h>
#include <zephyr/drivers/clock_control.h>
#include <zephyr/drivers/clock_control/clock_control_numaker.h>
#include <zephyr/drivers/reset.h>
#include <zephyr/drivers/pinctrl.h>
#include <zephyr/logging/log.h>
LOG_MODULE_REGISTER(i2c_numaker, CONFIG_I2C_LOG_LEVEL);
#include "i2c-priv.h"
#include <soc.h>
#include <NuMicro.h>
/* i2c Master Mode Status */
#define M_START 0x08 /* Start */
#define M_REPEAT_START 0x10 /* Master Repeat Start */
#define M_TRAN_ADDR_ACK 0x18 /* Master Transmit Address ACK */
#define M_TRAN_ADDR_NACK 0x20 /* Master Transmit Address NACK */
#define M_TRAN_DATA_ACK 0x28 /* Master Transmit Data ACK */
#define M_TRAN_DATA_NACK 0x30 /* Master Transmit Data NACK */
#define M_ARB_LOST 0x38 /* Master Arbitration Los */
#define M_RECE_ADDR_ACK 0x40 /* Master Receive Address ACK */
#define M_RECE_ADDR_NACK 0x48 /* Master Receive Address NACK */
#define M_RECE_DATA_ACK 0x50 /* Master Receive Data ACK */
#define M_RECE_DATA_NACK 0x58 /* Master Receive Data NACK */
#define BUS_ERROR 0x00 /* Bus error */
/* i2c Slave Mode Status */
#define S_REPEAT_START_STOP 0xA0 /* Slave Transmit Repeat Start or Stop */
#define S_TRAN_ADDR_ACK 0xA8 /* Slave Transmit Address ACK */
#define S_TRAN_DATA_ACK 0xB8 /* Slave Transmit Data ACK */
#define S_TRAN_DATA_NACK 0xC0 /* Slave Transmit Data NACK */
#define S_TRAN_LAST_DATA_ACK 0xC8 /* Slave Transmit Last Data ACK */
#define S_RECE_ADDR_ACK 0x60 /* Slave Receive Address ACK */
#define S_RECE_ARB_LOST 0x68 /* Slave Receive Arbitration Lost */
#define S_RECE_DATA_ACK 0x80 /* Slave Receive Data ACK */
#define S_RECE_DATA_NACK 0x88 /* Slave Receive Data NACK */
/* i2c GC Mode Status */
#define GC_ADDR_ACK 0x70 /* GC mode Address ACK */
#define GC_ARB_LOST 0x78 /* GC mode Arbitration Lost */
#define GC_DATA_ACK 0x90 /* GC mode Data ACK */
#define GC_DATA_NACK 0x98 /* GC mode Data NACK */
/* i2c Other Status */
#define ADDR_TRAN_ARB_LOST 0xB0 /* Address Transmit Arbitration Lost */
#define BUS_RELEASED 0xF8 /* Bus Released */
struct i2c_numaker_config {
I2C_T *i2c_base;
const struct reset_dt_spec reset;
uint32_t clk_modidx;
uint32_t clk_src;
uint32_t clk_div;
const struct device *clkctrl_dev;
uint32_t irq_n;
void (*irq_config_func)(const struct device *dev);
const struct pinctrl_dev_config *pincfg;
uint32_t bitrate;
};
struct i2c_numaker_data {
struct k_sem lock;
uint32_t dev_config;
/* Master transfer context */
struct {
struct k_sem xfer_sync;
uint16_t addr;
struct i2c_msg *msgs_beg;
struct i2c_msg *msgs_pos;
struct i2c_msg *msgs_end;
uint8_t *buf_beg;
uint8_t *buf_pos;
uint8_t *buf_end;
} master_xfer;
#ifdef CONFIG_I2C_TARGET
/* Slave transfer context */
struct {
struct i2c_target_config *slave_config;
bool slave_addressed;
} slave_xfer;
#endif
};
/* ACK/NACK last data byte, dependent on whether or not message merge is allowed */
static void m_numaker_i2c_master_xfer_msg_read_last_byte(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
/* Shouldn't invoke with message pointer OOB */
__ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end);
/* Should invoke with exactly one data byte remaining for read */
__ASSERT_NO_MSG((data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ);
__ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1);
/* Flags of previous message */
bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP;
/* Advance to next messages temporarily */
data->master_xfer.msgs_pos++;
/* Has next message? */
if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) {
/* Flags of next message */
struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos;
bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ;
bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART;
/*
* Different R/W bit so message merge is disallowed.
* Force I2C Repeat Start on I2C Stop/Repeat Start missing
*/
if (!is_read_next) {
if (!do_stop_prev && !do_restart_next) {
do_restart_next = true;
}
}
if (do_stop_prev || do_restart_next) {
/* NACK last data byte (required for Master Receiver) */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
} else {
/* ACK last data byte, so to merge adjacent messages into one transaction */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
}
} else {
/* NACK last data byte (required for Master Receiver) */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
}
/* Roll back message pointer */
data->master_xfer.msgs_pos--;
}
/* End the transfer, involving I2C Stop and signal to thread */
static void m_numaker_i2c_master_xfer_end(const struct device *dev, bool do_stop)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
if (do_stop) {
/* Do I2C Stop */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk);
}
/* Signal master transfer end */
k_sem_give(&data->master_xfer.xfer_sync);
}
static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev);
/* Read next data byte, involving ACK/NACK last data byte and message merge */
static void m_numaker_i2c_master_xfer_msg_read_next_byte(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
switch (data->master_xfer.buf_end - data->master_xfer.buf_pos) {
case 0:
/* Last data byte ACKed, we'll do message merge */
m_numaker_i2c_master_xfer_msg_end(dev);
break;
case 1:
/* Read last data byte for this message */
m_numaker_i2c_master_xfer_msg_read_last_byte(dev);
break;
default:
/* ACK non-last data byte */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
}
}
/* End one message transfer, involving message merge and transfer end */
static void m_numaker_i2c_master_xfer_msg_end(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
/* Shouldn't invoke with message pointer OOB */
__ASSERT_NO_MSG(data->master_xfer.msgs_pos < data->master_xfer.msgs_end);
/* Should have transferred up */
__ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 0);
/* Flags of previous message */
bool is_read_prev = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ;
bool do_stop_prev = data->master_xfer.msgs_pos->flags & I2C_MSG_STOP;
/* Advance to next messages */
data->master_xfer.msgs_pos++;
/* Has next message? */
if (data->master_xfer.msgs_pos < data->master_xfer.msgs_end) {
/* Flags of next message */
struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos;
bool is_read_next = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ;
bool do_restart_next = data->master_xfer.msgs_pos->flags & I2C_MSG_RESTART;
/*
* Different R/W bit so message merge is disallowed.
* Force I2C Repeat Start on I2C Stop/Repeat Start missing
*/
if (!is_read_prev != !is_read_next) { /* Logical XOR idiom */
if (!do_stop_prev && !do_restart_next) {
LOG_WRN("Cannot merge adjacent messages, force I2C Repeat Start");
do_restart_next = true;
}
}
if (do_stop_prev) {
/* Do I2C Stop and then Start */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk |
I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk);
} else if (do_restart_next) {
/* Do I2C Repeat Start */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk);
} else {
/* Merge into the same transaction */
/* Prepare buffer for current message */
data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf;
data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf;
data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf +
data->master_xfer.msgs_pos->len;
if (is_read_prev) {
m_numaker_i2c_master_xfer_msg_read_next_byte(dev);
} else {
/*
* Interrupt flag not cleared, expect to re-enter ISR with
* context unchanged, except buffer changed for message change.
*/
}
}
} else {
if (!do_stop_prev) {
LOG_WRN("Last message not marked I2C Stop");
}
m_numaker_i2c_master_xfer_end(dev, do_stop_prev);
}
}
static int i2c_numaker_configure(const struct device *dev, uint32_t dev_config)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
uint32_t bitrate;
/* Check address size */
if (dev_config & I2C_ADDR_10_BITS) {
LOG_ERR("10-bits address not supported");
return -ENOTSUP;
}
switch (I2C_SPEED_GET(dev_config)) {
case I2C_SPEED_STANDARD:
bitrate = KHZ(100);
break;
case I2C_SPEED_FAST:
bitrate = KHZ(400);
break;
case I2C_SPEED_FAST_PLUS:
bitrate = MHZ(1);
break;
default:
LOG_ERR("Speed code %d not supported", I2C_SPEED_GET(dev_config));
return -ENOTSUP;
}
I2C_T *i2c_base = config->i2c_base;
int err = 0;
k_sem_take(&data->lock, K_FOREVER);
irq_disable(config->irq_n);
#ifdef CONFIG_I2C_TARGET
if (data->slave_xfer.slave_addressed) {
LOG_ERR("Reconfigure with slave being busy");
err = -EBUSY;
goto done;
}
#endif
I2C_Open(i2c_base, bitrate);
/* INTEN bit and FSM control bits (STA, STO, SI, AA) are packed in one register CTL0. */
i2c_base->CTL0 |= (I2C_CTL0_INTEN_Msk | I2C_CTL0_I2CEN_Msk);
data->dev_config = dev_config;
done:
irq_enable(config->irq_n);
k_sem_give(&data->lock);
return err;
}
static int i2c_numaker_get_config(const struct device *dev, uint32_t *dev_config)
{
struct i2c_numaker_data *data = dev->data;
if (!dev_config) {
return -EINVAL;
}
k_sem_take(&data->lock, K_FOREVER);
*dev_config = data->dev_config;
k_sem_give(&data->lock);
return 0;
}
/*
* Master active transfer:
* 1. Do I2C Start to start the transfer (thread)
* 2. I2C FSM (ISR)
* 3. Force I2C Stop to end the transfer (thread)
* Slave passive transfer:
* 1. Prepare callback (thread)
* 2. Do data transfer via above callback (ISR)
*/
static int i2c_numaker_transfer(const struct device *dev, struct i2c_msg *msgs,
uint8_t num_msgs, uint16_t addr)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
int err = 0;
k_sem_take(&data->lock, K_FOREVER);
irq_disable(config->irq_n);
if (data->slave_xfer.slave_addressed) {
LOG_ERR("Master transfer with slave being busy");
err = -EBUSY;
goto cleanup;
}
if (num_msgs == 0) {
goto cleanup;
}
/* Prepare to start transfer */
data->master_xfer.addr = addr;
data->master_xfer.msgs_beg = msgs;
data->master_xfer.msgs_pos = msgs;
data->master_xfer.msgs_end = msgs + num_msgs;
/* Do I2C Start to start the transfer */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STA_Msk | I2C_CTL0_SI_Msk);
irq_enable(config->irq_n);
k_sem_take(&data->master_xfer.xfer_sync, K_FOREVER);
irq_disable(config->irq_n);
/* Check transfer result */
if (data->master_xfer.msgs_pos != data->master_xfer.msgs_end) {
bool is_read;
bool is_10bit;
is_read = (data->master_xfer.msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ;
is_10bit = data->master_xfer.msgs_pos->flags & I2C_MSG_ADDR_10_BITS;
LOG_ERR("Failed message:");
LOG_ERR("MSG IDX: %d", data->master_xfer.msgs_pos - data->master_xfer.msgs_beg);
LOG_ERR("ADDR (%d-bit): 0x%04X", is_10bit ? 10 : 7, addr);
LOG_ERR("DIR: %s", is_read ? "R" : "W");
LOG_ERR("Expected %d bytes transferred, but actual %d",
data->master_xfer.msgs_pos->len,
data->master_xfer.buf_pos - data->master_xfer.buf_beg);
err = -EIO;
goto i2c_stop;
}
i2c_stop:
/* Do I2C Stop to release bus ownership */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk);
#ifdef CONFIG_I2C_TARGET
/* Enable slave mode if one slave is registered */
if (data->slave_xfer.slave_config) {
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
}
#endif
cleanup:
irq_enable(config->irq_n);
k_sem_give(&data->lock);
return err;
}
#ifdef CONFIG_I2C_TARGET
static int i2c_numaker_slave_register(const struct device *dev,
struct i2c_target_config *slave_config)
{
if (!slave_config || !slave_config->callbacks) {
return -EINVAL;
}
if (slave_config->flags & I2C_ADDR_10_BITS) {
LOG_ERR("10-bits address not supported");
return -ENOTSUP;
}
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
int err = 0;
k_sem_take(&data->lock, K_FOREVER);
irq_disable(config->irq_n);
if (data->slave_xfer.slave_config) {
err = -EBUSY;
goto cleanup;
}
data->slave_xfer.slave_config = slave_config;
/* Slave address */
I2C_SetSlaveAddr(i2c_base,
0,
slave_config->address,
I2C_GCMODE_DISABLE);
/* Slave address state */
data->slave_xfer.slave_addressed = false;
/* Enable slave mode */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
cleanup:
irq_enable(config->irq_n);
k_sem_give(&data->lock);
return err;
}
static int i2c_numaker_slave_unregister(const struct device *dev,
struct i2c_target_config *slave_config)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
int err = 0;
if (!slave_config) {
return -EINVAL;
}
k_sem_take(&data->lock, K_FOREVER);
irq_disable(config->irq_n);
if (data->slave_xfer.slave_config != slave_config) {
err = -EINVAL;
goto cleanup;
}
if (data->slave_xfer.slave_addressed) {
LOG_ERR("Unregister slave driver with slave being busy");
err = -EBUSY;
goto cleanup;
}
/* Slave address: Zero */
I2C_SetSlaveAddr(i2c_base,
0,
0,
I2C_GCMODE_DISABLE);
/* Slave address state */
data->slave_xfer.slave_addressed = false;
/* Disable slave mode */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
data->slave_xfer.slave_config = NULL;
cleanup:
irq_enable(config->irq_n);
k_sem_give(&data->lock);
return err;
}
#endif
static int i2c_numaker_recover_bus(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
k_sem_take(&data->lock, K_FOREVER);
/* Do I2C Stop to release bus ownership */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_STO_Msk | I2C_CTL0_SI_Msk);
k_sem_give(&data->lock);
return 0;
}
static void i2c_numaker_isr(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
I2C_T *i2c_base = config->i2c_base;
#ifdef CONFIG_I2C_TARGET
struct i2c_target_config *slave_config = data->slave_xfer.slave_config;
const struct i2c_target_callbacks *slave_callbacks =
slave_config ? slave_config->callbacks : NULL;
uint8_t data_byte;
#endif
uint32_t status;
if (I2C_GET_TIMEOUT_FLAG(i2c_base)) {
I2C_ClearTimeoutFlag(i2c_base);
return;
}
status = I2C_GET_STATUS(i2c_base);
switch (status) {
case M_START: /* Start */
case M_REPEAT_START: /* Master Repeat Start */
/* Prepare buffer for current message */
data->master_xfer.buf_beg = data->master_xfer.msgs_pos->buf;
data->master_xfer.buf_pos = data->master_xfer.msgs_pos->buf;
data->master_xfer.buf_end = data->master_xfer.msgs_pos->buf +
data->master_xfer.msgs_pos->len;
/* Write I2C address */
struct i2c_msg *msgs_pos = data->master_xfer.msgs_pos;
bool is_read = (msgs_pos->flags & I2C_MSG_RW_MASK) == I2C_MSG_READ;
uint16_t addr = data->master_xfer.addr;
int addr_rw = is_read ? ((addr << 1) | 1) : (addr << 1);
I2C_SET_DATA(i2c_base, (uint8_t) (addr_rw & 0xFF));
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
break;
case M_TRAN_ADDR_ACK: /* Master Transmit Address ACK */
case M_TRAN_DATA_ACK: /* Master Transmit Data ACK */
__ASSERT_NO_MSG(data->master_xfer.buf_pos);
if (data->master_xfer.buf_pos < data->master_xfer.buf_end) {
I2C_SET_DATA(i2c_base, *data->master_xfer.buf_pos++);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
} else {
/* End this message */
m_numaker_i2c_master_xfer_msg_end(dev);
}
break;
case M_TRAN_ADDR_NACK: /* Master Transmit Address NACK */
case M_TRAN_DATA_NACK: /* Master Transmit Data NACK */
case M_RECE_ADDR_NACK: /* Master Receive Address NACK */
case M_ARB_LOST: /* Master Arbitration Lost */
m_numaker_i2c_master_xfer_end(dev, true);
break;
case M_RECE_ADDR_ACK: /* Master Receive Address ACK */
case M_RECE_DATA_ACK: /* Master Receive Data ACK */
__ASSERT_NO_MSG(data->master_xfer.buf_pos);
if (status == M_RECE_ADDR_ACK) {
__ASSERT_NO_MSG(data->master_xfer.buf_pos < data->master_xfer.buf_end);
} else if (status == M_RECE_DATA_ACK) {
__ASSERT_NO_MSG((data->master_xfer.buf_end -
data->master_xfer.buf_pos) >= 1);
*data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base);
}
m_numaker_i2c_master_xfer_msg_read_next_byte(dev);
break;
case M_RECE_DATA_NACK: /* Master Receive Data NACK */
__ASSERT_NO_MSG((data->master_xfer.buf_end - data->master_xfer.buf_pos) == 1);
*data->master_xfer.buf_pos++ = I2C_GET_DATA(i2c_base);
/* End this message */
m_numaker_i2c_master_xfer_msg_end(dev);
break;
case BUS_ERROR: /* Bus error */
m_numaker_i2c_master_xfer_end(dev, true);
break;
#ifdef CONFIG_I2C_TARGET
/* NOTE: Don't disable interrupt here because slave mode relies on */
/* for passive transfer in ISR. */
/* Slave Transmit */
case S_TRAN_ADDR_ACK: /* Slave Transmit Address ACK */
case ADDR_TRAN_ARB_LOST: /* Slave Transmit Arbitration Lost */
data->slave_xfer.slave_addressed = true;
if (slave_callbacks->read_requested(slave_config, &data_byte) == 0) {
/* Non-last data byte */
I2C_SET_DATA(i2c_base, data_byte);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
} else {
/* Go S_TRAN_LAST_DATA_ACK on error */
I2C_SET_DATA(i2c_base, 0xFF);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
}
break;
case S_TRAN_DATA_ACK: /* Slave Transmit Data ACK */
if (slave_callbacks->read_processed(slave_config, &data_byte) == 0) {
/* Non-last data byte */
I2C_SET_DATA(i2c_base, data_byte);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
} else {
/* Go S_TRAN_LAST_DATA_ACK on error */
I2C_SET_DATA(i2c_base, 0xFF);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
}
break;
case S_TRAN_DATA_NACK: /* Slave Transmit Data NACK */
case S_TRAN_LAST_DATA_ACK: /* Slave Transmit Last Data ACK */
/* Go slave end */
data->slave_xfer.slave_addressed = false;
slave_callbacks->stop(slave_config);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
break;
/* Slave Receive */
case S_RECE_DATA_ACK: /* Slave Receive Data ACK */
data_byte = I2C_GET_DATA(i2c_base);
if (slave_callbacks->write_received(slave_config, data_byte) == 0) {
/* Write OK, ACK next data byte */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
} else {
/* Write FAILED, NACK next data byte */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
}
break;
case S_RECE_DATA_NACK: /* Slave Receive Data NACK */
/* Go slave end */
data->slave_xfer.slave_addressed = false;
slave_callbacks->stop(slave_config);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
break;
case S_RECE_ADDR_ACK: /* Slave Receive Address ACK */
case S_RECE_ARB_LOST: /* Slave Receive Arbitration Lost */
data->slave_xfer.slave_addressed = true;
if (slave_callbacks->write_requested(slave_config) == 0) {
/* Write ready, ACK next byte */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
} else {
/* Write not ready, NACK next byte */
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk);
}
break;
case S_REPEAT_START_STOP: /* Slave Transmit/Receive Repeat Start or Stop */
/* Go slave end */
data->slave_xfer.slave_addressed = false;
slave_callbacks->stop(slave_config);
I2C_SET_CONTROL_REG(i2c_base, I2C_CTL0_SI_Msk | I2C_CTL0_AA_Msk);
break;
#endif /* CONFIG_I2C_TARGET */
case BUS_RELEASED: /* Bus Released */
/* Ignore the interrupt raised by BUS_RELEASED. */
break;
default:
__ASSERT(false, "Uncaught I2C FSM state");
m_numaker_i2c_master_xfer_end(dev, true);
}
}
static int i2c_numaker_init(const struct device *dev)
{
const struct i2c_numaker_config *config = dev->config;
struct i2c_numaker_data *data = dev->data;
int err = 0;
struct numaker_scc_subsys scc_subsys;
/* Validate this module's reset object */
if (!device_is_ready(config->reset.dev)) {
LOG_ERR("reset controller not ready");
return -ENODEV;
}
/* Clean mutable context */
memset(data, 0x00, sizeof(*data));
k_sem_init(&data->lock, 1, 1);
k_sem_init(&data->master_xfer.xfer_sync, 0, 1);
SYS_UnlockReg();
memset(&scc_subsys, 0x00, sizeof(scc_subsys));
scc_subsys.subsys_id = NUMAKER_SCC_SUBSYS_ID_PCC;
scc_subsys.pcc.clk_modidx = config->clk_modidx;
scc_subsys.pcc.clk_src = config->clk_src;
scc_subsys.pcc.clk_div = config->clk_div;
/* Equivalent to CLK_EnableModuleClock() */
err = clock_control_on(config->clkctrl_dev, (clock_control_subsys_t) &scc_subsys);
if (err != 0) {
goto cleanup;
}
/* Equivalent to CLK_SetModuleClock() */
err = clock_control_configure(config->clkctrl_dev,
(clock_control_subsys_t) &scc_subsys,
NULL);
if (err != 0) {
goto cleanup;
}
/* Configure pinmux (NuMaker's SYS MFP) */
err = pinctrl_apply_state(config->pincfg, PINCTRL_STATE_DEFAULT);
if (err != 0) {
goto cleanup;
}
/* Reset I2C to default state, same as BSP's SYS_ResetModule(id_rst) */
reset_line_toggle_dt(&config->reset);
err = i2c_numaker_configure(dev, I2C_MODE_CONTROLLER | i2c_map_dt_bitrate(config->bitrate));
if (err != 0) {
goto cleanup;
}
config->irq_config_func(dev);
cleanup:
SYS_LockReg();
return err;
}
static const struct i2c_driver_api i2c_numaker_driver_api = {
.configure = i2c_numaker_configure,
.get_config = i2c_numaker_get_config,
.transfer = i2c_numaker_transfer,
#ifdef CONFIG_I2C_TARGET
.target_register = i2c_numaker_slave_register,
.target_unregister = i2c_numaker_slave_unregister,
#endif
.recover_bus = i2c_numaker_recover_bus,
};
#define I2C_NUMAKER_INIT(inst) \
PINCTRL_DT_INST_DEFINE(inst); \
\
static void i2c_numaker_irq_config_func_##inst(const struct device *dev) \
{ \
IRQ_CONNECT(DT_INST_IRQN(inst), \
DT_INST_IRQ(inst, priority), \
i2c_numaker_isr, \
DEVICE_DT_INST_GET(inst), \
0); \
\
irq_enable(DT_INST_IRQN(inst)); \
} \
\
static const struct i2c_numaker_config i2c_numaker_config_##inst = { \
.i2c_base = (I2C_T *) DT_INST_REG_ADDR(inst), \
.reset = RESET_DT_SPEC_INST_GET(inst), \
.clk_modidx = DT_INST_CLOCKS_CELL(inst, clock_module_index), \
.clk_src = DT_INST_CLOCKS_CELL(inst, clock_source), \
.clk_div = DT_INST_CLOCKS_CELL(inst, clock_divider), \
.clkctrl_dev = DEVICE_DT_GET(DT_PARENT(DT_INST_CLOCKS_CTLR(inst))),\
.irq_n = DT_INST_IRQN(inst), \
.irq_config_func = i2c_numaker_irq_config_func_##inst, \
.pincfg = PINCTRL_DT_INST_DEV_CONFIG_GET(inst), \
.bitrate = DT_INST_PROP(inst, clock_frequency), \
}; \
\
static struct i2c_numaker_data i2c_numaker_data_##inst; \
\
I2C_DEVICE_DT_INST_DEFINE(inst, \
i2c_numaker_init, \
NULL, \
&i2c_numaker_data_##inst, \
&i2c_numaker_config_##inst, \
POST_KERNEL, \
CONFIG_I2C_INIT_PRIORITY, \
&i2c_numaker_driver_api);
DT_INST_FOREACH_STATUS_OKAY(I2C_NUMAKER_INIT);