drivers: i3c: i3c_mcux: reduce timeout busy waiting

For many of the state checks with timeout, the check
should be very fast (sub 1 microseconds) and the
previous implemention involving busy waits between
checks could add unneeded latency. Change the
timeout checking method to use WAIT_FOR(), with
no extra delays between checks.

Signed-off-by: Mike J. Chen <mjchen@google.com>
This commit is contained in:
Mike J. Chen 2024-03-22 12:41:09 -07:00 committed by Johan Hedberg
parent 78150c8e1b
commit 6b2fae0d01

View file

@ -148,38 +148,24 @@ struct mcux_i3c_data {
* @param reg Pointer to 32-bit Register.
* @param mask Mask to the register value.
* @param match Value to match for masked register value.
* @param init_delay_us Initial delay in microsecond before reading register
* (can be 0).
* @param step_delay_us Delay in microsecond between each read of register
* (cannot be 0).
* @param total_delay_us Total delay in microsecond before bailing out.
* @param timeout_us Timeout in microsecond before bailing out.
*
* @retval 0 If masked register value matches before time out.
* @retval -ETIMEDOUT Exhausted all delays without matching.
* @retval -ETIMEDOUT Timedout without matching.
*/
static int reg32_poll_timeout(volatile uint32_t *reg,
uint32_t mask, uint32_t match,
uint32_t init_delay_us, uint32_t step_delay_us,
uint32_t total_delay_us)
uint32_t timeout_us)
{
uint32_t delayed = init_delay_us;
int ret = -ETIMEDOUT;
if (init_delay_us > 0U) {
k_busy_wait(init_delay_us);
/*
* These polling checks are typically satisfied
* quickly (some sub-microseconds) so no extra
* delay between checks.
*/
if (!WAIT_FOR((sys_read32((mm_reg_t)reg) & mask) == match, timeout_us, /*nop*/)) {
return -ETIMEDOUT;
}
while (delayed <= total_delay_us) {
if ((sys_read32((mm_reg_t)reg) & mask) == match) {
ret = 0;
break;
}
k_busy_wait(step_delay_us);
delayed += step_delay_us;
}
return ret;
return 0;
}
/**
@ -362,22 +348,15 @@ static inline void mcux_i3c_status_wait(I3C_Type *base, uint32_t mask)
*
* @param base Pointer to controller registers.
* @param mask Bits to be tested.
* @param init_delay_us Initial delay in microsecond before reading register
* (can be 0).
* @param step_delay_us Delay in microsecond between each read of register
* (cannot be 0).
* @param total_delay_us Total delay in microsecond before bailing out.
* @param timeout_us Timeout in microsecond before bailing out.
*
* @retval 0 If bits are set before time out.
* @retval -ETIMEDOUT Exhausted all delays.
* @retval -ETIMEDOUT
*/
static inline int mcux_i3c_status_wait_timeout(I3C_Type *base, uint32_t mask,
uint32_t init_delay_us,
uint32_t step_delay_us,
uint32_t total_delay_us)
uint32_t timeout_us)
{
return reg32_poll_timeout(&base->MSTATUS, mask, mask,
init_delay_us, step_delay_us, total_delay_us);
return reg32_poll_timeout(&base->MSTATUS, mask, mask, timeout_us);
}
/**
@ -429,37 +408,27 @@ static inline void mcux_i3c_status_clear_all(I3C_Type *base)
*
* @param base Pointer to controller registers.
* @param mask Bits to be cleared.
* @param init_delay_us Initial delay in microsecond before reading register
* (can be 0).
* @param step_delay_us Delay in microsecond between each read of register
* (cannot be 0).
* @param total_delay_us Total delay in microsecond before bailing out.
* @param timeout_us Timeout in microsecond before bailing out.
*
* @retval 0 If bits are cleared before time out.
* @retval -ETIMEDOUT Exhausted all delays.
* @retval -ETIMEDOUT
*/
static inline int mcux_i3c_status_clear_timeout(I3C_Type *base, uint32_t mask,
uint32_t init_delay_us,
uint32_t step_delay_us,
uint32_t total_delay_us)
uint32_t timeout_us)
{
uint32_t delayed = init_delay_us;
int ret = -ETIMEDOUT;
bool result;
/* Try to clear bit until it is cleared */
while (delayed <= total_delay_us) {
base->MSTATUS = mask;
if (!mcux_i3c_status_is_set(base, mask)) {
ret = 0;
break;
}
k_busy_wait(step_delay_us);
delayed += step_delay_us;
base->MSTATUS = mask;
/*
* Status should clear quickly so no extra delays between
* checks. Use the delay_stmt to retry clearing the
* status by writing to the MSTATUS register.
*/
result = WAIT_FOR(!mcux_i3c_status_is_set(base, mask), timeout_us, base->MSTATUS = mask);
if (!result) {
return -ETIMEDOUT;
}
return ret;
return 0;
}
/**
@ -488,30 +457,22 @@ static inline void mcux_i3c_status_wait_clear(I3C_Type *base, uint32_t mask)
*
* @param base Pointer to controller registers.
* @param mask Bits to be set and to be cleared.
* @param init_delay_us Initial delay in microsecond before reading register
* (can be 0).
* @param step_delay_us Delay in microsecond between each read of register
* (cannot be 0).
* @param total_delay_us Total delay in microsecond before bailing out.
* @param timeout_us Timeout in microsecond before bailing out.
*
* @retval 0 If masked register value matches before time out.
* @retval -ETIMEDOUT Exhausted all delays without matching.
* @retval -ETIMEDOUT Timedout without matching.
*/
static inline int mcux_i3c_status_wait_clear_timeout(I3C_Type *base, uint32_t mask,
uint32_t init_delay_us,
uint32_t step_delay_us,
uint32_t total_delay_us)
uint32_t timeout_us)
{
int ret;
ret = mcux_i3c_status_wait_timeout(base, mask, init_delay_us,
step_delay_us, total_delay_us);
ret = mcux_i3c_status_wait_timeout(base, mask, timeout_us);
if (ret != 0) {
goto out;
}
ret = mcux_i3c_status_clear_timeout(base, mask, init_delay_us,
step_delay_us, total_delay_us);
ret = mcux_i3c_status_clear_timeout(base, mask, timeout_us);
out:
return ret;
@ -580,12 +541,10 @@ static inline uint32_t mcux_i3c_state_get(I3C_Type *base)
}
/**
* @brief Wait for MSTATUS bit to be set, and clear it afterwards with time out.
* @brief Wait for MSTATUS state
*
* @param base Pointer to controller registers.
* @param mask Bits to be set.
* @param init_delay_us Initial delay in microsecond before reading register
* (can be 0).
* @param state MSTATUS state to wait for.
* @param step_delay_us Delay in microsecond between each read of register
* (cannot be 0).
* @param total_delay_us Total delay in microsecond before bailing out.
@ -594,14 +553,12 @@ static inline uint32_t mcux_i3c_state_get(I3C_Type *base)
* @retval -ETIMEDOUT Exhausted all delays without matching.
*/
static inline int mcux_i3c_state_wait_timeout(I3C_Type *base, uint32_t state,
uint32_t init_delay_us,
uint32_t step_delay_us,
uint32_t total_delay_us)
{
uint32_t delayed = init_delay_us;
uint32_t delayed = 0;
int ret = -ETIMEDOUT;
/* Try to clear bit until it is cleared */
while (delayed <= total_delay_us) {
if (mcux_i3c_state_get(base) == state) {
ret = 0;
@ -664,7 +621,7 @@ static int mcux_i3c_request_emit_start(I3C_Type *base, uint8_t addr, bool is_i2c
/* Wait for controller to say the operation is done */
ret = mcux_i3c_status_wait_clear_timeout(base, I3C_MSTATUS_MCTRLDONE_MASK,
0, 10, 1000);
1000);
if (ret == 0) {
/* Check for NACK */
if (mcux_i3c_error_is_nack(base)) {
@ -918,7 +875,7 @@ static int mcux_i3c_recover_bus(const struct device *dev)
mcux_i3c_request_auto_ibi(base);
if (mcux_i3c_status_wait_clear_timeout(base, I3C_MSTATUS_COMPLETE_MASK,
0, 10, 1000) == -ETIMEDOUT) {
1000) == -ETIMEDOUT) {
break;
}
@ -934,7 +891,7 @@ static int mcux_i3c_recover_bus(const struct device *dev)
}
if (reg32_poll_timeout(&base->MSTATUS, I3C_MSTATUS_STATE_MASK,
I3C_MSTATUS_STATE_IDLE, 0, 10, 1000) == -ETIMEDOUT) {
I3C_MSTATUS_STATE_IDLE, 1000) == -ETIMEDOUT) {
ret = -EBUSY;
}
@ -1023,8 +980,7 @@ static int mcux_i3c_do_one_xfer_write(I3C_Type *base, uint8_t *buf, uint8_t buf_
int ret = 0;
while (remaining > 0) {
ret = reg32_poll_timeout(&base->MDATACTRL, I3C_MDATACTRL_TXFULL_MASK, 0,
0, 10, 1000);
ret = reg32_poll_timeout(&base->MDATACTRL, I3C_MDATACTRL_TXFULL_MASK, 0, 1000);
if (ret == -ETIMEDOUT) {
goto one_xfer_write_out;
}
@ -1097,9 +1053,11 @@ static int mcux_i3c_do_one_xfer(I3C_Type *base, struct mcux_i3c_data *data,
}
if (is_read || !no_ending) {
/* Wait for controller to say the operation is done */
ret = mcux_i3c_status_wait_clear_timeout(base, I3C_MSTATUS_COMPLETE_MASK,
0, 10, 1000);
/*
* Wait for controller to say the operation is done.
* Save time by not clearing the bit.
*/
ret = mcux_i3c_status_wait_timeout(base, I3C_MSTATUS_COMPLETE_MASK, 1000);
if (ret != 0) {
LOG_DBG("%s: timed out addr 0x%02x, buf_sz %u",
__func__, addr, buf_sz);
@ -1263,7 +1221,7 @@ static int mcux_i3c_do_daa(const struct device *dev)
k_mutex_lock(&data->lock, K_FOREVER);
ret = mcux_i3c_state_wait_timeout(base, I3C_MSTATUS_STATE_IDLE, 0, 100, 100000);
ret = mcux_i3c_state_wait_timeout(base, I3C_MSTATUS_STATE_IDLE, 100, 100000);
if (ret == -ETIMEDOUT) {
goto out_daa_unlock;
}
@ -1459,7 +1417,7 @@ static int mcux_i3c_do_ccc(const struct device *dev,
}
/* Wait for controller to say the operation is done */
ret = mcux_i3c_status_wait_clear_timeout(base, I3C_MSTATUS_COMPLETE_MASK, 0, 10, 1000);
ret = mcux_i3c_status_wait_clear_timeout(base, I3C_MSTATUS_COMPLETE_MASK, 1000);
if (ret != 0) {
goto out_ccc_stop;
}
@ -1563,7 +1521,7 @@ static void mcux_i3c_ibi_work(struct k_work *work)
case I3C_MSTATUS_IBITYPE_MR:
if (mcux_i3c_status_wait_timeout(base, I3C_MSTATUS_COMPLETE_MASK,
0, 10, 1000) == -ETIMEDOUT) {
1000) == -ETIMEDOUT) {
LOG_ERR("Timeout waiting for COMPLETE");
mcux_i3c_request_emit_stop(data, base, true);