drivers: i3c: implement support for ibi thr interrupts
Some IBI TIR packets can be larger than the ibi data fifo size which can prevent it from receiving the full packet. This adds a data struct in to the driver data where data can be pushed to as data is being transfered. Signed-off-by: Ryan McClelland <ryanmcclelland@meta.com>
This commit is contained in:
parent
31e821bef6
commit
ae63c62f0e
2 changed files with 58 additions and 34 deletions
|
|
@ -547,6 +547,14 @@ struct cdns_i3c_xfer {
|
||||||
struct cdns_i3c_cmd cmds[I3C_MAX_MSGS];
|
struct cdns_i3c_cmd cmds[I3C_MAX_MSGS];
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#ifdef CONFIG_I3C_USE_IBI
|
||||||
|
/* IBI transferred data */
|
||||||
|
struct cdns_i3c_ibi_buf {
|
||||||
|
uint8_t ibi_data[CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE];
|
||||||
|
uint8_t ibi_data_cnt;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
/* Driver config */
|
/* Driver config */
|
||||||
struct cdns_i3c_config {
|
struct cdns_i3c_config {
|
||||||
struct i3c_driver_config common;
|
struct i3c_driver_config common;
|
||||||
|
|
@ -556,12 +564,17 @@ struct cdns_i3c_config {
|
||||||
uint32_t input_frequency;
|
uint32_t input_frequency;
|
||||||
/** Interrupt configuration function. */
|
/** Interrupt configuration function. */
|
||||||
void (*irq_config_func)(const struct device *dev);
|
void (*irq_config_func)(const struct device *dev);
|
||||||
|
/** IBID Threshold value */
|
||||||
|
uint8_t ibid_thr;
|
||||||
};
|
};
|
||||||
|
|
||||||
/* Driver instance data */
|
/* Driver instance data */
|
||||||
struct cdns_i3c_data {
|
struct cdns_i3c_data {
|
||||||
struct i3c_driver_data common;
|
struct i3c_driver_data common;
|
||||||
struct cdns_i3c_hw_config hw_cfg;
|
struct cdns_i3c_hw_config hw_cfg;
|
||||||
|
#ifdef CONFIG_I3C_USE_IBI
|
||||||
|
struct cdns_i3c_ibi_buf ibi_buf;
|
||||||
|
#endif
|
||||||
struct k_mutex bus_lock;
|
struct k_mutex bus_lock;
|
||||||
struct cdns_i3c_i2c_dev_data cdns_i3c_i2c_priv_data[I3C_MAX_DEVS];
|
struct cdns_i3c_i2c_dev_data cdns_i3c_i2c_priv_data[I3C_MAX_DEVS];
|
||||||
struct cdns_i3c_xfer xfer;
|
struct cdns_i3c_xfer xfer;
|
||||||
|
|
@ -2298,7 +2311,7 @@ static int cdns_i3c_read_ibi_fifo(const struct cdns_i3c_config *config, void *bu
|
||||||
static void cdns_i3c_handle_ibi(const struct device *dev, uint32_t ibir)
|
static void cdns_i3c_handle_ibi(const struct device *dev, uint32_t ibir)
|
||||||
{
|
{
|
||||||
const struct cdns_i3c_config *config = dev->config;
|
const struct cdns_i3c_config *config = dev->config;
|
||||||
uint8_t ibi_data[CONFIG_I3C_IBI_MAX_PAYLOAD_SIZE];
|
struct cdns_i3c_data *data = dev->data;
|
||||||
|
|
||||||
/* The slave ID returned here is the device ID in the SIR map NOT the device ID
|
/* The slave ID returned here is the device ID in the SIR map NOT the device ID
|
||||||
* in the RR map.
|
* in the RR map.
|
||||||
|
|
@ -2326,20 +2339,25 @@ static void cdns_i3c_handle_ibi(const struct device *dev, uint32_t ibir)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (ibir & IBIR_ERROR) {
|
if (ibir & IBIR_ERROR) {
|
||||||
LOG_ERR("%s: Data overflow", dev->name);
|
/* Controller issued an Abort */
|
||||||
return;
|
LOG_ERR("%s: IBI Data overflow", dev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Read out any payload bytes */
|
/* Read out any payload bytes */
|
||||||
uint8_t ibi_len = IBIR_XFER_BYTES(ibir);
|
uint8_t ibi_len = IBIR_XFER_BYTES(ibir);
|
||||||
|
|
||||||
if (ibi_len > 0) {
|
if (ibi_len > 0) {
|
||||||
if (cdns_i3c_read_ibi_fifo(config, ibi_data, ibi_len) < 0) {
|
if (ibi_len - data->ibi_buf.ibi_data_cnt > 0) {
|
||||||
|
if (cdns_i3c_read_ibi_fifo(
|
||||||
|
config, &data->ibi_buf.ibi_data[data->ibi_buf.ibi_data_cnt],
|
||||||
|
ibi_len - data->ibi_buf.ibi_data_cnt) < 0) {
|
||||||
LOG_ERR("%s: Failed to get payload", dev->name);
|
LOG_ERR("%s: Failed to get payload", dev->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
data->ibi_buf.ibi_data_cnt = 0;
|
||||||
|
}
|
||||||
|
|
||||||
if (i3c_ibi_work_enqueue_target_irq(desc, ibi_data, ibi_len) != 0) {
|
if (i3c_ibi_work_enqueue_target_irq(desc, data->ibi_buf.ibi_data, ibi_len) != 0) {
|
||||||
LOG_ERR("%s: Error enqueue IBI IRQ work", dev->name);
|
LOG_ERR("%s: Error enqueue IBI IRQ work", dev->name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -2445,40 +2463,24 @@ static void cdns_i3c_target_sdr_tx_thr_int_handler(const struct device *dev,
|
||||||
static void cdns_i3c_irq_handler(const struct device *dev)
|
static void cdns_i3c_irq_handler(const struct device *dev)
|
||||||
{
|
{
|
||||||
const struct cdns_i3c_config *config = dev->config;
|
const struct cdns_i3c_config *config = dev->config;
|
||||||
|
struct cdns_i3c_data *data = dev->data;
|
||||||
|
|
||||||
if (sys_read32(config->base + MST_STATUS0) & MST_STATUS0_MASTER_MODE) {
|
if (sys_read32(config->base + MST_STATUS0) & MST_STATUS0_MASTER_MODE) {
|
||||||
uint32_t int_st = sys_read32(config->base + MST_ISR);
|
uint32_t int_st = sys_read32(config->base + MST_ISR);
|
||||||
|
sys_write32(int_st, config->base + MST_ICR);
|
||||||
|
|
||||||
/* Command queue empty */
|
/* Command queue empty */
|
||||||
if (int_st & MST_INT_HALTED) {
|
if (int_st & MST_INT_HALTED) {
|
||||||
LOG_WRN("Core Halted, 2 read aborts");
|
LOG_WRN("Core Halted, 2 read aborts");
|
||||||
sys_write32(MST_INT_HALTED, config->base + MST_ICR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Command queue empty */
|
/* Command queue empty */
|
||||||
if (int_st & MST_INT_CMDD_EMP) {
|
if (int_st & MST_INT_CMDD_EMP) {
|
||||||
cdns_i3c_complete_transfer(dev);
|
cdns_i3c_complete_transfer(dev);
|
||||||
sys_write32(MST_INT_CMDD_EMP, config->base + MST_ICR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Command queue threshold */
|
|
||||||
if (int_st & MST_INT_CMDD_THR) {
|
|
||||||
sys_write32(MST_INT_CMDD_THR, config->base + MST_ICR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Command response threshold hit */
|
|
||||||
if (int_st & MST_INT_CMDR_THR) {
|
|
||||||
sys_write32(MST_INT_CMDR_THR, config->base + MST_ICR);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RX data ready */
|
|
||||||
if (int_st & MST_INT_RX_THR) {
|
|
||||||
sys_write32(MST_INT_RX_THR, config->base + MST_ICR);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In-band interrupt */
|
/* In-band interrupt */
|
||||||
if (int_st & MST_INT_IBIR_THR) {
|
if (int_st & MST_INT_IBIR_THR) {
|
||||||
sys_write32(MST_INT_IBIR_THR, config->base + MST_ICR);
|
|
||||||
#ifdef CONFIG_I3C_USE_IBI
|
#ifdef CONFIG_I3C_USE_IBI
|
||||||
cnds_i3c_master_demux_ibis(dev);
|
cnds_i3c_master_demux_ibis(dev);
|
||||||
#else
|
#else
|
||||||
|
|
@ -2486,25 +2488,39 @@ static void cdns_i3c_irq_handler(const struct device *dev)
|
||||||
dev->name);
|
dev->name);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* In-band interrupt data threshold */
|
||||||
|
if (int_st & MST_INT_IBID_THR) {
|
||||||
|
#ifdef CONFIG_I3C_USE_IBI
|
||||||
|
/* pop data out of the IBI FIFO */
|
||||||
|
while (!cdns_i3c_ibi_fifo_empty(config)) {
|
||||||
|
uint32_t *ptr = (uint32_t *)&data->ibi_buf
|
||||||
|
.ibi_data[data->ibi_buf.ibi_data_cnt];
|
||||||
|
*ptr = sys_le32_to_cpu(sys_read32(config->base + IBI_DATA_FIFO));
|
||||||
|
data->ibi_buf.ibi_data_cnt += 4;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
LOG_ERR("%s: IBI received - Kconfig for using IBIs is not enabled",
|
||||||
|
dev->name);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* In-band interrupt response overflow */
|
||||||
|
if (int_st & MST_INT_IBIR_OVF) {
|
||||||
|
LOG_ERR("%s: controller ibir overflow,", dev->name);
|
||||||
|
}
|
||||||
|
|
||||||
/* In-band interrupt data */
|
/* In-band interrupt data */
|
||||||
if (int_st & MST_INT_TX_OVF) {
|
if (int_st & MST_INT_TX_OVF) {
|
||||||
sys_write32(MST_INT_TX_OVF, config->base + MST_ICR);
|
|
||||||
LOG_ERR("%s: controller tx buffer overflow,", dev->name);
|
LOG_ERR("%s: controller tx buffer overflow,", dev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In-band interrupt data */
|
/* In-band interrupt data */
|
||||||
if (int_st & MST_INT_RX_UNF) {
|
if (int_st & MST_INT_RX_UNF) {
|
||||||
sys_write32(MST_INT_RX_UNF, config->base + MST_ICR);
|
|
||||||
LOG_ERR("%s: controller rx buffer underflow,", dev->name);
|
LOG_ERR("%s: controller rx buffer underflow,", dev->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* In-band interrupt data */
|
|
||||||
if (int_st & MST_INT_IBID_THR) {
|
|
||||||
sys_write32(MST_INT_IBID_THR, config->base + MST_ICR);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
uint32_t int_sl = sys_read32(config->base + SLV_ISR);
|
uint32_t int_sl = sys_read32(config->base + SLV_ISR);
|
||||||
struct cdns_i3c_data *data = dev->data;
|
|
||||||
const struct i3c_target_callbacks *target_cb =
|
const struct i3c_target_callbacks *target_cb =
|
||||||
data->target_config ? data->target_config->callbacks : NULL;
|
data->target_config ? data->target_config->callbacks : NULL;
|
||||||
/* Clear interrupts */
|
/* Clear interrupts */
|
||||||
|
|
@ -3185,7 +3201,7 @@ static int cdns_i3c_bus_init(const struct device *dev)
|
||||||
|
|
||||||
/* Set fifo thresholds. */
|
/* Set fifo thresholds. */
|
||||||
sys_write32(CMD_THR(I3C_CMDD_THR) | IBI_THR(I3C_IBID_THR) | CMDR_THR(I3C_CMDR_THR) |
|
sys_write32(CMD_THR(I3C_CMDD_THR) | IBI_THR(I3C_IBID_THR) | CMDR_THR(I3C_CMDR_THR) |
|
||||||
IBIR_THR(I3C_IBIR_THR),
|
IBIR_THR(config->ibid_thr),
|
||||||
config->base + CMD_IBI_THR_CTRL);
|
config->base + CMD_IBI_THR_CTRL);
|
||||||
|
|
||||||
/* Set TX/RX interrupt thresholds. */
|
/* Set TX/RX interrupt thresholds. */
|
||||||
|
|
@ -3197,6 +3213,7 @@ static int cdns_i3c_bus_init(const struct device *dev)
|
||||||
sys_write32(SLV_DDR_TX_THR(0) | SLV_DDR_RX_THR(1),
|
sys_write32(SLV_DDR_TX_THR(0) | SLV_DDR_RX_THR(1),
|
||||||
config->base + SLV_DDR_TX_RX_THR_CTRL);
|
config->base + SLV_DDR_TX_RX_THR_CTRL);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* enable target interrupts */
|
/* enable target interrupts */
|
||||||
sys_write32(SLV_INT_DA_UPD | SLV_INT_SDR_RD_COMP | SLV_INT_SDR_WR_COMP |
|
sys_write32(SLV_INT_DA_UPD | SLV_INT_SDR_RD_COMP | SLV_INT_SDR_WR_COMP |
|
||||||
SLV_INT_SDR_RX_THR | SLV_INT_SDR_TX_THR | SLV_INT_SDR_RX_UNF |
|
SLV_INT_SDR_RX_THR | SLV_INT_SDR_TX_THR | SLV_INT_SDR_RX_UNF |
|
||||||
|
|
@ -3205,7 +3222,8 @@ static int cdns_i3c_bus_init(const struct device *dev)
|
||||||
config->base + SLV_IER);
|
config->base + SLV_IER);
|
||||||
|
|
||||||
/* Enable IBI interrupts. */
|
/* Enable IBI interrupts. */
|
||||||
sys_write32(MST_INT_IBIR_THR | MST_INT_RX_UNF | MST_INT_HALTED | MST_INT_TX_OVF,
|
sys_write32(MST_INT_IBIR_THR | MST_INT_RX_UNF | MST_INT_HALTED | MST_INT_TX_OVF |
|
||||||
|
MST_INT_IBIR_OVF | MST_INT_IBID_THR,
|
||||||
config->base + MST_IER);
|
config->base + MST_IER);
|
||||||
|
|
||||||
int ret = i3c_addr_slots_init(dev);
|
int ret = i3c_addr_slots_init(dev);
|
||||||
|
|
@ -3272,6 +3290,7 @@ static struct i3c_driver_api api = {
|
||||||
.base = DT_INST_REG_ADDR(n), \
|
.base = DT_INST_REG_ADDR(n), \
|
||||||
.input_frequency = DT_INST_PROP(n, input_clock_frequency), \
|
.input_frequency = DT_INST_PROP(n, input_clock_frequency), \
|
||||||
.irq_config_func = cdns_i3c_config_func_##n, \
|
.irq_config_func = cdns_i3c_config_func_##n, \
|
||||||
|
.ibid_thr = DT_INST_PROP(n, ibid_thr), \
|
||||||
.common.dev_list.i3c = cdns_i3c_device_array_##n, \
|
.common.dev_list.i3c = cdns_i3c_device_array_##n, \
|
||||||
.common.dev_list.num_i3c = ARRAY_SIZE(cdns_i3c_device_array_##n), \
|
.common.dev_list.num_i3c = ARRAY_SIZE(cdns_i3c_device_array_##n), \
|
||||||
.common.dev_list.i2c = cdns_i3c_i2c_device_array_##n, \
|
.common.dev_list.i2c = cdns_i3c_i2c_device_array_##n, \
|
||||||
|
|
|
||||||
|
|
@ -19,3 +19,8 @@ properties:
|
||||||
type: int
|
type: int
|
||||||
required: true
|
required: true
|
||||||
description: The controller input clock frequency
|
description: The controller input clock frequency
|
||||||
|
|
||||||
|
ibid-thr:
|
||||||
|
type: int
|
||||||
|
default: 1
|
||||||
|
description: IBI Data Fifo Threashold Value
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue