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:
Ryan McClelland 2024-10-02 16:00:17 -07:00 committed by Anas Nashif
parent 31e821bef6
commit ae63c62f0e
2 changed files with 58 additions and 34 deletions

View file

@ -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, \

View file

@ -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