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.
799 lines
20 KiB
799 lines
20 KiB
/* |
|
* Copyright (c) 2022 Andes Technology Corporation |
|
* |
|
* SPDX-License-Identifier: Apache-2.0 |
|
*/ |
|
|
|
/** |
|
* @file I2C driver for AndesTech atciic100 IP |
|
*/ |
|
|
|
#include <string.h> |
|
#include <zephyr/drivers/i2c.h> |
|
#include <zephyr/irq.h> |
|
#include "i2c_andes_atciic100.h" |
|
|
|
#define DT_DRV_COMPAT andestech_atciic100 |
|
|
|
typedef void (*atciic100_dt_init_func_t)(void); |
|
|
|
struct i2c_atciic100_config { |
|
uint32_t base; |
|
uint32_t irq_num; |
|
atciic100_dt_init_func_t dt_init_fn; |
|
}; |
|
|
|
static int i2c_atciic100_controller_send(const struct device *dev, |
|
uint16_t addr, const uint8_t *data, uint32_t num, uint8_t flags); |
|
static int i2c_atciic100_controller_receive(const struct device *dev, |
|
uint16_t addr, uint8_t *data, uint32_t num, uint8_t flags); |
|
static void i2c_controller_fifo_write(const struct device *dev, |
|
uint8_t is_init); |
|
static void i2c_controller_fifo_read(const struct device *dev); |
|
static int i2c_atciic100_init(const struct device *dev); |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
static void i2c_atciic100_target_send(const struct device *dev, |
|
const uint8_t *data); |
|
static void i2c_atciic100_target_receive(const struct device *dev, |
|
uint8_t *data); |
|
#endif |
|
|
|
static void i2c_atciic100_default_control(const struct device *dev) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg = 0; |
|
|
|
k_sem_init(&dev_data->bus_lock, 1, 1); |
|
k_sem_init(&dev_data->device_sync_sem, 0, 1); |
|
|
|
/* Reset I2C bus */ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_RESET_I2C); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
/* I2C query FIFO depth */ |
|
reg = sys_read32(I2C_CFG(dev)); |
|
switch (reg & 0x3) { |
|
case 0x0: |
|
dev_data->fifo_depth = 2; |
|
break; |
|
case 0x1: |
|
dev_data->fifo_depth = 4; |
|
break; |
|
case 0x2: |
|
dev_data->fifo_depth = 8; |
|
break; |
|
case 0x3: |
|
dev_data->fifo_depth = 16; |
|
break; |
|
} |
|
|
|
/* |
|
* I2C setting: target mode(default), standard speed |
|
* 7-bit, CPU mode |
|
*/ |
|
sys_write32(0x0, I2C_SET(dev)); |
|
reg = sys_read32(I2C_SET(dev)); |
|
reg |= ((SETUP_T_SUDAT_STD << 24) | |
|
(SETUP_T_SP_STD << 21) | |
|
(SETUP_T_HDDAT_STD << 16) | |
|
(SETUP_T_SCL_RATIO_STD << 13) | |
|
(SETUP_T_SCLHI_STD << 4) | |
|
SETUP_I2C_EN); |
|
|
|
sys_write32(reg, I2C_SET(dev)); |
|
|
|
dev_data->driver_state = I2C_DRV_INIT; |
|
dev_data->status.mode = 0; |
|
dev_data->status.arbitration_lost = 0; |
|
dev_data->status.target_ack = 0; |
|
} |
|
|
|
static int i2c_atciic100_configure(const struct device *dev, |
|
uint32_t dev_config) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg = 0; |
|
int ret = 0; |
|
|
|
reg = sys_read32(I2C_SET(dev)); |
|
|
|
switch (I2C_SPEED_GET(dev_config)) { |
|
case I2C_SPEED_STANDARD: |
|
reg |= SETUP_SPEED_STD; |
|
break; |
|
|
|
case I2C_SPEED_FAST: |
|
reg |= SETUP_SPEED_FAST; |
|
break; |
|
|
|
case I2C_SPEED_FAST_PLUS: |
|
reg |= SETUP_SPEED_FAST_PLUS; |
|
|
|
case I2C_SPEED_HIGH: |
|
ret = -EIO; |
|
goto unlock; |
|
case 0x00: |
|
break; |
|
default: |
|
ret = -EIO; |
|
goto unlock; |
|
} |
|
|
|
if (dev_config & I2C_MODE_CONTROLLER) { |
|
reg |= SETUP_CONTROLLER; |
|
dev_data->status.mode = 1; |
|
} else { |
|
reg &= ~SETUP_CONTROLLER; |
|
dev_data->status.mode = 0; |
|
} |
|
|
|
if (dev_config & I2C_ADDR_10_BITS) { |
|
reg |= SETUP_ADDRESSING; |
|
} else { |
|
reg &= ~SETUP_ADDRESSING; |
|
} |
|
|
|
sys_write32(reg, I2C_SET(dev)); |
|
|
|
dev_data->driver_state |= I2C_DRV_CFG_PARAM; |
|
|
|
unlock: |
|
k_sem_give(&dev_data->bus_lock); |
|
|
|
return ret; |
|
} |
|
|
|
static int i2c_atciic100_transfer(const struct device *dev, |
|
struct i2c_msg *msgs, uint8_t num_msgs, uint16_t addr) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
int ret = 0; |
|
int count = 0; |
|
uint8_t burst_write_len = msgs[0].len + msgs[1].len; |
|
uint8_t burst_write_buf[I2C_MAX_COUNT + BURST_CMD_COUNT]; |
|
|
|
k_sem_take(&dev_data->bus_lock, K_FOREVER); |
|
|
|
if ((msgs[0].flags == I2C_MSG_WRITE) |
|
&& (msgs[1].flags == (I2C_MSG_WRITE | I2C_MSG_STOP))) { |
|
|
|
burst_write_len = msgs[0].len + msgs[1].len; |
|
if (burst_write_len > MAX_XFER_SZ) { |
|
return -EIO; |
|
} |
|
for (count = 0; count < burst_write_len; count++) { |
|
if (count < msgs[0].len) { |
|
burst_write_buf[count] = msgs[0].buf[count]; |
|
} else { |
|
burst_write_buf[count] = |
|
msgs[1].buf[count - msgs[0].len]; |
|
} |
|
} |
|
ret = i2c_atciic100_controller_send(dev, addr, burst_write_buf, |
|
burst_write_len, true); |
|
goto exit; |
|
} |
|
|
|
for (uint8_t i = 0; i < num_msgs; i++) { |
|
if ((msgs[i].flags & I2C_MSG_RW_MASK) == I2C_MSG_WRITE) { |
|
ret = i2c_atciic100_controller_send(dev, |
|
addr, msgs[i].buf, msgs[i].len, msgs[i].flags); |
|
} else { |
|
ret = i2c_atciic100_controller_receive(dev, |
|
addr, msgs[i].buf, msgs[i].len, msgs[i].flags); |
|
} |
|
|
|
if (ret < 0) { |
|
goto exit; |
|
} |
|
} |
|
|
|
exit: |
|
/* Wait for transfer complete */ |
|
k_sem_give(&dev_data->bus_lock); |
|
return ret; |
|
} |
|
|
|
|
|
static int i2c_atciic100_controller_send(const struct device *dev, |
|
uint16_t addr, const uint8_t *data, uint32_t num, uint8_t flags) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg = 0; |
|
|
|
/* |
|
* Max to 10-bit address. |
|
* Parameters data = null or num = 0 means no payload for |
|
* acknowledge polling. If no I2C payload, set Phase_data=0x0. |
|
*/ |
|
if (addr > 0x3FF) { |
|
return -EIO; |
|
} |
|
|
|
/* Disable all I2C interrupts */ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_ALL); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
dev_data->status.mode = 1; |
|
reg = sys_read32(I2C_SET(dev)); |
|
reg |= SETUP_CONTROLLER; |
|
sys_write32(reg, I2C_SET(dev)); |
|
|
|
/* Direction => tx:0, rx:1 */ |
|
dev_data->status.arbitration_lost = 0; |
|
dev_data->status.target_ack = 0; |
|
dev_data->driver_state = I2C_DRV_CONTROLLER_TX; |
|
|
|
/* Step1, Clear FIFO */ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_CLEAR_FIFO); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
/* |
|
* Step2 |
|
* Enable START, ADDRESS, DATA and STOP phase. |
|
* If no payload, clear DATA phase. |
|
* STOP condition triggered when transmission finish in controller mode. |
|
* The bus is busy until STOP condition triggered. |
|
* For 10-bit target address, we must set STOP bit. |
|
* I2C direction : controller tx, set xfer DATA count. |
|
*/ |
|
reg = sys_read32(I2C_CTRL(dev)); |
|
reg &= (~(CTRL_PHASE_START | CTRL_PHASE_ADDR | CTRL_PHASE_STOP | |
|
CTRL_DIR | CTRL_DATA_COUNT)); |
|
|
|
if (flags & I2C_MSG_STOP) { |
|
reg |= CTRL_PHASE_STOP; |
|
} |
|
if ((flags & I2C_MSG_RESTART) == 0) { |
|
reg |= (CTRL_PHASE_START | CTRL_PHASE_ADDR); |
|
} |
|
if (num) { |
|
reg |= (CTRL_PHASE_DATA | (num & CTRL_DATA_COUNT)); |
|
} |
|
|
|
sys_write32(reg, I2C_CTRL(dev)); |
|
|
|
/* Step3 init I2C info */ |
|
dev_data->target_addr = addr; |
|
dev_data->xfered_data_wt_ptr = 0; |
|
dev_data->xfer_wt_num = num; |
|
dev_data->middleware_tx_buf = (uint8_t *)data; |
|
|
|
/* In I2C target address, general call address = 0x0(7-bit or 10-bit) */ |
|
reg = sys_read32(I2C_ADDR(dev)); |
|
reg &= (~TARGET_ADDR_MSK); |
|
reg |= (dev_data->target_addr & (TARGET_ADDR_MSK)); |
|
sys_write32(reg, I2C_ADDR(dev)); |
|
|
|
/* |
|
* Step4 Enable Interrupts: Complete, Arbitration Lose |
|
* Enable/Disable the FIFO Empty Interrupt |
|
* Fill the FIFO before enabling FIFO Empty Interrupt |
|
*/ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
|
|
i2c_controller_fifo_write(dev, 1); |
|
|
|
reg |= (IEN_CMPL | IEN_ARB_LOSE | IEN_ADDR_HIT); |
|
|
|
if (num > 0) { |
|
reg |= IEN_FIFO_EMPTY; |
|
} else { |
|
reg &= (~IEN_FIFO_EMPTY); |
|
} |
|
|
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
/* |
|
* Step5, |
|
* I2C Write 0x1 to the Command register to issue the transaction |
|
*/ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_ISSUE_TRANSACTION); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
k_sem_take(&dev_data->device_sync_sem, K_FOREVER); |
|
|
|
if (dev_data->status.target_ack != 1) { |
|
return -EIO; |
|
} |
|
dev_data->status.target_ack = 0; |
|
return 0; |
|
} |
|
|
|
static int i2c_atciic100_controller_receive(const struct device *dev, |
|
uint16_t addr, uint8_t *data, uint32_t num, uint8_t flags) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg = 0; |
|
|
|
/* |
|
* Max to 10-bit address. |
|
* Parameters data = null or num = 0 means no payload for |
|
* acknowledge polling. If no I2C payload, set Phase_data=0x0. |
|
*/ |
|
if (addr > 0x3FF) { |
|
return -EIO; |
|
} |
|
|
|
/* Disable all I2C interrupts */ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_ALL); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
dev_data->status.mode = 1; |
|
reg = sys_read32(I2C_SET(dev)); |
|
reg |= SETUP_CONTROLLER; |
|
sys_write32(reg, I2C_SET(dev)); |
|
|
|
/* Direction => tx:0, rx:1 */ |
|
dev_data->status.arbitration_lost = 0; |
|
dev_data->status.target_ack = 0; |
|
dev_data->driver_state = I2C_DRV_CONTROLLER_RX; |
|
|
|
/* Step1, Clear FIFO */ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_CLEAR_FIFO); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
/* |
|
* Step2 |
|
* Enable START, ADDRESS, DATA and STOP phase. |
|
* If no payload, clear DATA phase. |
|
* STOP condition triggered when transmission finish in Controller mode. |
|
* The bus is busy until STOP condition triggered. |
|
* For 10-bit target address, we must set STOP bit. |
|
* I2C direction : controller rx, set xfer data count. |
|
*/ |
|
reg = sys_read32(I2C_CTRL(dev)); |
|
reg &= (~(CTRL_PHASE_START | CTRL_PHASE_ADDR | CTRL_PHASE_STOP | |
|
CTRL_DIR | CTRL_DATA_COUNT)); |
|
reg |= (CTRL_PHASE_START | CTRL_PHASE_ADDR | CTRL_DIR); |
|
|
|
if (flags & I2C_MSG_STOP) { |
|
reg |= CTRL_PHASE_STOP; |
|
} |
|
if (num) { |
|
reg |= (CTRL_PHASE_DATA | (num & CTRL_DATA_COUNT)); |
|
} |
|
|
|
sys_write32(reg, I2C_CTRL(dev)); |
|
|
|
/* Step3 init I2C info */ |
|
dev_data->target_addr = addr; |
|
dev_data->xfered_data_rd_ptr = 0; |
|
dev_data->xfer_rd_num = num; |
|
dev_data->middleware_rx_buf = (uint8_t *)data; |
|
|
|
/* In I2C target address, general call address = 0x0(7-bit or 10-bit) */ |
|
reg = sys_read32(I2C_ADDR(dev)); |
|
reg &= (~TARGET_ADDR_MSK); |
|
reg |= (dev_data->target_addr & (TARGET_ADDR_MSK)); |
|
sys_write32(reg, I2C_ADDR(dev)); |
|
|
|
/* |
|
* Step4 Enable Interrupts: Complete, Arbitration Lose |
|
* Enable/Disable the FIFO Full Interrupt |
|
*/ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg |= (IEN_CMPL | IEN_FIFO_FULL | IEN_ARB_LOSE | IEN_ADDR_HIT); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
/* |
|
* Step5, |
|
* I2C write 0x1 to the Command register to issue the transaction |
|
*/ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_ISSUE_TRANSACTION); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
k_sem_take(&dev_data->device_sync_sem, K_FOREVER); |
|
if (dev_data->status.target_ack != 1) { |
|
return -EIO; |
|
} |
|
dev_data->status.target_ack = 0; |
|
return 0; |
|
} |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
static void i2c_atciic100_target_send(const struct device *dev, |
|
const uint8_t *data) |
|
{ |
|
uint32_t reg = 0; |
|
|
|
/* Clear FIFO */ |
|
reg = sys_read32(I2C_CMD(dev)); |
|
reg &= (~CMD_MSK); |
|
reg |= (CMD_CLEAR_FIFO); |
|
sys_write32(reg, I2C_CMD(dev)); |
|
|
|
sys_write32(*data, I2C_DATA(dev)); |
|
} |
|
|
|
static void i2c_atciic100_target_receive(const struct device *dev, |
|
uint8_t *data) |
|
{ |
|
*data = sys_read32(I2C_DATA(dev)); |
|
} |
|
#endif |
|
|
|
static void i2c_controller_fifo_write(const struct device *dev, |
|
uint8_t is_init) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t i = 0, write_fifo_count = 0, reg = 0; |
|
uint8_t write_data; |
|
|
|
write_fifo_count = dev_data->xfer_wt_num - dev_data->xfered_data_wt_ptr; |
|
|
|
if (write_fifo_count >= dev_data->fifo_depth) { |
|
write_fifo_count = dev_data->fifo_depth; |
|
} |
|
|
|
if (is_init) { |
|
write_fifo_count = 2; |
|
} |
|
|
|
/* I2C write a patch of data(FIFO_Depth) to FIFO */ |
|
for (i = 0; i < write_fifo_count; i++) { |
|
|
|
write_data = |
|
dev_data->middleware_tx_buf[dev_data->xfered_data_wt_ptr]; |
|
sys_write32((write_data & DATA_MSK), I2C_DATA(dev)); |
|
dev_data->xfered_data_wt_ptr++; |
|
|
|
/* Disable the FIFO Empty Interrupt if no more data to send */ |
|
if (dev_data->xfered_data_wt_ptr == dev_data->xfer_wt_num) { |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_FIFO_EMPTY); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
} |
|
} |
|
} |
|
|
|
/* Basic fifo read function */ |
|
static void i2c_controller_fifo_read(const struct device *dev) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t i = 0, read_fifo_count = 0, reg = 0; |
|
uint8_t read_data; |
|
|
|
read_fifo_count = dev_data->xfer_rd_num - dev_data->xfered_data_rd_ptr; |
|
|
|
if (read_fifo_count >= dev_data->fifo_depth) { |
|
read_fifo_count = dev_data->fifo_depth; |
|
} |
|
|
|
/* I2C read a patch of data(FIFO_Depth) from FIFO */ |
|
for (i = 0; i < read_fifo_count; i++) { |
|
|
|
read_data = sys_read32(I2C_DATA(dev)) & DATA_MSK; |
|
|
|
dev_data->middleware_rx_buf[dev_data->xfered_data_rd_ptr] = |
|
read_data; |
|
dev_data->xfered_data_rd_ptr++; |
|
|
|
/* Disable the FIFO Full Interrupt if no more data to receive */ |
|
if (dev_data->xfered_data_rd_ptr == dev_data->xfer_rd_num) { |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_FIFO_FULL); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
} |
|
} |
|
} |
|
|
|
static void i2c_fifo_empty_handler(const struct device *dev) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
|
|
if (dev_data->driver_state & I2C_DRV_CONTROLLER_TX) { |
|
i2c_controller_fifo_write(dev, 0); |
|
} |
|
} |
|
|
|
static void i2c_fifo_full_handler(const struct device *dev) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
|
|
if (dev_data->driver_state & I2C_DRV_CONTROLLER_RX) { |
|
i2c_controller_fifo_read(dev); |
|
} |
|
} |
|
|
|
static void i2c_cmpl_handler(const struct device *dev, uint32_t reg_stat) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg_set = 0, reg_ctrl = 0, reg = 0; |
|
|
|
reg_set = sys_read32(I2C_SET(dev)); |
|
|
|
/* Controller mode */ |
|
if (dev_data->status.mode == 1) { |
|
/* Disable all I2C interrupts */ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_ALL); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
} |
|
|
|
if (dev_data->driver_state & |
|
(I2C_DRV_CONTROLLER_TX | I2C_DRV_CONTROLLER_RX)) { |
|
|
|
/* Get the remain number of data */ |
|
reg_ctrl = sys_read32(I2C_CTRL(dev)) & CTRL_DATA_COUNT; |
|
|
|
if (dev_data->driver_state & I2C_DRV_CONTROLLER_TX) { |
|
/* Clear & set driver state to controller tx complete */ |
|
dev_data->driver_state = I2C_DRV_CONTROLLER_TX_CMPL; |
|
} |
|
|
|
if (dev_data->driver_state & I2C_DRV_CONTROLLER_RX) { |
|
i2c_controller_fifo_read(dev); |
|
/* Clear & set driver state to controller rx complete */ |
|
dev_data->driver_state = I2C_DRV_CONTROLLER_RX_CMPL; |
|
} |
|
|
|
k_sem_give(&dev_data->device_sync_sem); |
|
} |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
if (dev_data->driver_state & (I2C_DRV_TARGET_TX | I2C_DRV_TARGET_RX)) { |
|
reg_set = sys_read32(I2C_SET(dev)); |
|
reg_ctrl = sys_read32(I2C_CTRL(dev)); |
|
|
|
if (dev_data->driver_state & I2C_DRV_TARGET_TX) { |
|
dev_data->driver_state = I2C_DRV_TARGET_TX_CMPL; |
|
} |
|
|
|
if (dev_data->driver_state & I2C_DRV_TARGET_RX) { |
|
dev_data->driver_state = I2C_DRV_TARGET_RX_CMPL; |
|
} |
|
|
|
/* If the Completion Interrupt asserts, |
|
* clear the FIFO and go next transaction. |
|
*/ |
|
uint32_t reg_cmd = 0; |
|
|
|
reg_cmd = sys_read32(I2C_CMD(dev)); |
|
reg_cmd &= (~CMD_MSK); |
|
reg_cmd |= (CMD_CLEAR_FIFO); |
|
sys_write32(reg_cmd, I2C_CMD(dev)); |
|
} |
|
|
|
/* Enable Completion & Address Hit Interrupt */ |
|
/* Enable Byte Receive & Transfer for default target mode */ |
|
reg = 0x0; |
|
reg |= (IEN_CMPL | IEN_ADDR_HIT | STATUS_BYTE_RECV | STATUS_BYTE_TRANS); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
reg = sys_read32(I2C_SET(dev)); |
|
reg &= ~(SETUP_CONTROLLER); |
|
sys_write32(reg, I2C_SET(dev)); |
|
|
|
reg &= (~TARGET_ADDR_MSK); |
|
reg |= (dev_data->target_config->address & (TARGET_ADDR_MSK)); |
|
sys_write32(reg, I2C_ADDR(dev)); |
|
|
|
dev_data->driver_state = I2C_DRV_INIT; |
|
dev_data->status.mode = 0; |
|
dev_data->status.arbitration_lost = 0; |
|
#endif |
|
|
|
} |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
static void andes_i2c_target_event(const struct device *dev, |
|
uint32_t reg_stat, uint32_t reg_ctrl) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint32_t reg_set = 0; |
|
uint8_t val; |
|
/* |
|
* Here is the entry for target mode driver to detect |
|
* target RX/TX action depend on controller TX/RX action. |
|
* A new I2C data transaction(START-ADDRESS-DATA-STOP) |
|
*/ |
|
if (reg_stat & STATUS_ADDR_HIT) { |
|
if (k_sem_take(&dev_data->bus_lock, K_NO_WAIT) != 0) { |
|
return; |
|
} |
|
|
|
if (((reg_ctrl & CTRL_DIR) >> 8) == I2C_TARGET_TX) { |
|
/* Notify middleware to do target rx action */ |
|
dev_data->driver_state = I2C_DRV_TARGET_TX; |
|
dev_data->target_callbacks->read_requested |
|
(dev_data->target_config, &val); |
|
i2c_atciic100_target_send(dev, &val); |
|
|
|
} else if (((reg_ctrl & CTRL_DIR) >> 8) == I2C_TARGET_RX) { |
|
/* Notify middleware to do target tx action */ |
|
dev_data->driver_state = I2C_DRV_TARGET_RX; |
|
dev_data->target_callbacks->write_requested |
|
(dev_data->target_config); |
|
} |
|
reg_set |= (CMD_ACK); |
|
sys_write32(reg_set, I2C_CMD(dev)); |
|
} |
|
|
|
if (reg_stat & STATUS_BYTE_RECV) { |
|
i2c_atciic100_target_receive(dev, &val); |
|
dev_data->target_callbacks->write_received |
|
(dev_data->target_config, val); |
|
|
|
reg_set = 0; |
|
if ((reg_stat & STATUS_CMPL) == 0) { |
|
reg_set |= (CMD_ACK); |
|
sys_write32(reg_set, I2C_CMD(dev)); |
|
} else { |
|
reg_set |= (CMD_NACK); |
|
sys_write32(reg_set, I2C_CMD(dev)); |
|
} |
|
|
|
} else if (reg_stat & STATUS_BYTE_TRANS) { |
|
dev_data->target_callbacks->read_processed |
|
(dev_data->target_config, &val); |
|
i2c_atciic100_target_send(dev, &val); |
|
} |
|
|
|
if (reg_stat & STATUS_CMPL) { |
|
i2c_cmpl_handler(dev, reg_stat); |
|
k_sem_give(&dev_data->bus_lock); |
|
} |
|
} |
|
|
|
static int i2c_atciic100_target_register(const struct device *dev, |
|
struct i2c_target_config *cfg) |
|
{ |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
uint16_t reg_addr = 0; |
|
uint32_t reg; |
|
|
|
reg_addr &= (~TARGET_ADDR_MSK); |
|
reg_addr |= (cfg->address & (TARGET_ADDR_MSK)); |
|
|
|
sys_write32(reg_addr, I2C_ADDR(dev)); |
|
|
|
dev_data->target_callbacks = cfg->callbacks; |
|
dev_data->target_config = cfg; |
|
|
|
/* Enable Completion & Address Hit Interrupt */ |
|
/* Enable Byte Receive & Transfer for default target mode */ |
|
reg = 0x0; |
|
reg |= (IEN_CMPL | IEN_ADDR_HIT | STATUS_BYTE_RECV | STATUS_BYTE_TRANS); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
return 0; |
|
} |
|
|
|
static int i2c_atciic100_target_unregister(const struct device *dev, |
|
struct i2c_target_config *cfg) |
|
{ |
|
uint32_t reg; |
|
|
|
/* Disable all I2C interrupts */ |
|
reg = sys_read32(I2C_INTE(dev)); |
|
reg &= (~IEN_ALL); |
|
sys_write32(reg, I2C_INTE(dev)); |
|
|
|
sys_write32(0x0, I2C_ADDR(dev)); |
|
|
|
return 0; |
|
} |
|
#endif |
|
|
|
static void i2c_atciic100_irq_handler(void *arg) |
|
{ |
|
const struct device *dev = (struct device *)arg; |
|
struct i2c_atciic100_dev_data_t *dev_data = dev->data; |
|
|
|
uint32_t reg_set, reg_stat = 0, reg_ctrl = 0; |
|
|
|
reg_stat = sys_read32(I2C_STAT(dev)); |
|
reg_set = sys_read32(I2C_SET(dev)); |
|
reg_ctrl = sys_read32(I2C_CTRL(dev)); |
|
|
|
/* Clear interrupts status */ |
|
sys_write32((reg_stat & STATUS_W1C_ALL), I2C_STAT(dev)); |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
if (dev_data->status.mode == 0) { |
|
andes_i2c_target_event(dev, reg_stat, reg_ctrl); |
|
return; |
|
} |
|
#endif |
|
if (reg_stat & STATUS_ADDR_HIT) { |
|
dev_data->status.target_ack = 1; |
|
} |
|
|
|
if (reg_stat & STATUS_FIFO_EMPTY) { |
|
i2c_fifo_empty_handler(dev); |
|
} |
|
|
|
if (reg_stat & STATUS_FIFO_FULL) { |
|
/* Store hw receive data count quickly */ |
|
i2c_fifo_full_handler(dev); |
|
} |
|
|
|
if (reg_stat & STATUS_CMPL) { |
|
/* Store hw receive data count quickly */ |
|
i2c_cmpl_handler(dev, reg_stat); |
|
} |
|
|
|
if ((reg_stat & STATUS_ARB_LOSE) && (reg_set & SETUP_CONTROLLER)) { |
|
dev_data->status.arbitration_lost = 1; |
|
} |
|
} |
|
|
|
static DEVICE_API(i2c, i2c_atciic100_driver) = { |
|
.configure = (i2c_api_configure_t)i2c_atciic100_configure, |
|
.transfer = (i2c_api_full_io_t)i2c_atciic100_transfer, |
|
#if defined(CONFIG_I2C_TARGET) |
|
.target_register = (i2c_api_target_register_t)i2c_atciic100_target_register, |
|
.target_unregister = (i2c_api_target_unregister_t)i2c_atciic100_target_unregister, |
|
#endif |
|
#ifdef CONFIG_I2C_RTIO |
|
.iodev_submit = i2c_iodev_submit_fallback, |
|
#endif |
|
}; |
|
|
|
static int i2c_atciic100_init(const struct device *dev) |
|
{ |
|
const struct i2c_atciic100_config *dev_cfg = dev->config; |
|
|
|
/* Disable all interrupts. */ |
|
sys_write32(0x00000000, I2C_INTE(dev)); |
|
/* Clear interrupts status. */ |
|
sys_write32(0xFFFFFFFF, I2C_STAT(dev)); |
|
|
|
dev_cfg->dt_init_fn(); |
|
|
|
i2c_atciic100_default_control(dev); |
|
|
|
#if defined(CONFIG_I2C_TARGET) |
|
i2c_atciic100_configure(dev, I2C_SPEED_SET(I2C_SPEED_STANDARD)); |
|
#else |
|
i2c_atciic100_configure(dev, I2C_SPEED_SET(I2C_SPEED_STANDARD) |
|
| I2C_MODE_CONTROLLER); |
|
#endif |
|
irq_enable(dev_cfg->irq_num); |
|
|
|
return 0; |
|
} |
|
|
|
#define I2C_INIT(n) \ |
|
static struct i2c_atciic100_dev_data_t \ |
|
i2c_atciic100_dev_data_##n; \ |
|
static void i2c_dt_init_##n(void); \ |
|
static const struct i2c_atciic100_config \ |
|
i2c_atciic100_config_##n = { \ |
|
.base = DT_INST_REG_ADDR(n), \ |
|
.irq_num = DT_INST_IRQN(n), \ |
|
.dt_init_fn = i2c_dt_init_##n \ |
|
}; \ |
|
I2C_DEVICE_DT_INST_DEFINE(n, \ |
|
i2c_atciic100_init, \ |
|
NULL, \ |
|
&i2c_atciic100_dev_data_##n, \ |
|
&i2c_atciic100_config_##n, \ |
|
POST_KERNEL, \ |
|
CONFIG_I2C_INIT_PRIORITY, \ |
|
&i2c_atciic100_driver); \ |
|
\ |
|
static void i2c_dt_init_##n(void) \ |
|
{ \ |
|
IRQ_CONNECT(DT_INST_IRQN(n), \ |
|
DT_INST_IRQ(n, priority), \ |
|
i2c_atciic100_irq_handler, \ |
|
DEVICE_DT_INST_GET(n), \ |
|
0); \ |
|
} |
|
|
|
DT_INST_FOREACH_STATUS_OKAY(I2C_INIT)
|
|
|