Skip to content

Commit 7d15053

Browse files
mmahadevan108fabiobaltieri
authored andcommitted
drivers: i3c: Update NXP drivers transfer function
Change the transfer implementation to use interrupts instead of a busy wait loop. Added a Kconfig to specify a timeout period to wait for a transfer to complete. Signed-off-by: Mahesh Mahadevan <[email protected]>
1 parent 6f301ca commit 7d15053

File tree

2 files changed

+86
-34
lines changed

2 files changed

+86
-34
lines changed

drivers/i3c/Kconfig.nxp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,12 @@ config I3C_MCUX
1717
default y
1818
help
1919
Enable mcux I3C driver.
20+
21+
config I3C_NXP_TRANSFER_TIMEOUT
22+
int "Transfer timeout [ms]"
23+
default 500
24+
depends on I3C_MCUX
25+
help
26+
Timeout in milliseconds used for each I3C transfer.
27+
0 means that the driver should use the K_FOREVER value,
28+
i.e. it should wait as long as necessary.

drivers/i3c/i3c_mcux.c

Lines changed: 77 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*
22
* Copyright (c) 2016 Freescale Semiconductor, Inc.
3-
* Copyright (c) 2019 NXP
3+
* Copyright (c) 2019, 2025 NXP
44
* Copyright (c) 2022 Intel Corporation
55
*
66
* SPDX-License-Identifier: Apache-2.0
@@ -65,6 +65,10 @@ LOG_MODULE_REGISTER(i3c_mcux, CONFIG_I3C_MCUX_LOG_LEVEL);
6565

6666
#define I3C_MAX_STOP_RETRIES 5
6767

68+
#define I3C_TRANSFER_TIMEOUT_MSEC \
69+
COND_CODE_0(CONFIG_I3C_NXP_TRANSFER_TIMEOUT, (K_FOREVER), \
70+
(K_MSEC(CONFIG_I3C_NXP_TRANSFER_TIMEOUT)))
71+
6872
struct mcux_i3c_config {
6973
/** Common I3C Driver Config */
7074
struct i3c_driver_config common;
@@ -95,6 +99,9 @@ struct mcux_i3c_data {
9599
/** Mutex to serialize access */
96100
struct k_mutex lock;
97101

102+
/** Semaphore to synchronize data transfers */
103+
struct k_sem device_sync_sem;
104+
98105
/** Condvar for waiting for bus to be in IDLE state */
99106
struct k_condvar condvar;
100107

@@ -886,58 +893,71 @@ static int mcux_i3c_recover_bus(const struct device *dev)
886893
* or time out.
887894
*
888895
* @param base Pointer to controller registers.
896+
* @param data Pointer to controller device instance data.
889897
* @param buf Buffer to store data.
890898
* @param buf_sz Buffer size in bytes.
891899
*
892900
* @return Number of bytes read, or negative if error.
893901
*/
894-
static int mcux_i3c_do_one_xfer_read(I3C_Type *base, uint8_t *buf, uint8_t buf_sz, bool ibi)
902+
static int mcux_i3c_do_one_xfer_read(I3C_Type *base, struct mcux_i3c_data *data,
903+
uint8_t *buf, uint8_t buf_sz, bool ibi)
895904
{
896905
int ret = 0;
897906
int offset = 0;
898907

899908
while (offset < buf_sz) {
900909
/*
901910
* Transfer data from FIFO into buffer. Read
902-
* in a tight loop to reduce chance of losing
903-
* FIFO data when the i3c speed is high.
911+
* in a loop until data is unavailable in the FIFO.
904912
*/
905913
while (offset < buf_sz) {
906914
if (mcux_i3c_fifo_rx_count_get(base) == 0) {
915+
/* Enable Receive pending interrupt */
916+
base->MINTSET = I3C_MSTATUS_RXPEND_MASK;
917+
918+
/* Wait for data to arrive or an error */
919+
if (k_sem_take(&data->device_sync_sem, I3C_TRANSFER_TIMEOUT_MSEC)) {
920+
ret = -ETIMEDOUT;
921+
}
922+
/* We break out of the loop to see if the interrupt
923+
* was due to an error.
924+
*/
907925
break;
926+
} else {
927+
buf[offset++] = (uint8_t)base->MRDATAB;
908928
}
909-
buf[offset++] = (uint8_t)base->MRDATAB;
910929
}
911-
912930
/*
913931
* If controller says timed out, we abort the transaction.
914932
*/
915-
if (mcux_i3c_has_error(base)) {
933+
if (mcux_i3c_has_error(base) || ret) {
916934
if (mcux_i3c_error_is_timeout(base)) {
917935
ret = -ETIMEDOUT;
918936
}
919-
/* clear error */
937+
/* clear error */
920938
base->MERRWARN = base->MERRWARN;
921939

922-
/* for ibi, ignore timeout err if any bytes were
923-
* read, since the code doesn't know how many
924-
* bytes will be sent by device. for regular
925-
* application read request, return err always.
926-
*/
927-
if ((ret == -ETIMEDOUT) && ibi && offset) {
928-
break;
929-
} else {
930-
if (ret == -ETIMEDOUT) {
940+
if (ret == -ETIMEDOUT) {
941+
/* for ibi, ignore timeout err if any bytes were
942+
* read, since the code doesn't know how many
943+
* bytes will be sent by device.
944+
*/
945+
if (ibi && offset) {
946+
ret = offset;
947+
} else {
931948
LOG_ERR("Timeout error");
932949
}
933-
goto one_xfer_read_out;
950+
break;
934951
}
935952
}
953+
936954
}
937955

938-
ret = offset;
956+
/* If no errors, then return the number of bytes read */
957+
if (ret > 0) {
958+
ret = offset;
959+
}
939960

940-
one_xfer_read_out:
941961
return ret;
942962
}
943963

@@ -948,22 +968,30 @@ static int mcux_i3c_do_one_xfer_read(I3C_Type *base, uint8_t *buf, uint8_t buf_s
948968
* waiting for FIFO spaces.
949969
*
950970
* @param base Pointer to controller registers.
971+
* @param data Pointer to controller device instance data.
951972
* @param buf Buffer containing data to be sent.
952973
* @param buf_sz Number of bytes in @p buf to send.
953974
* @param no_ending True if not to signal end of write message.
954975
*
955976
* @return Number of bytes written, or negative if error.
956977
*/
957-
static int mcux_i3c_do_one_xfer_write(I3C_Type *base, uint8_t *buf, uint8_t buf_sz, bool no_ending)
978+
static int mcux_i3c_do_one_xfer_write(I3C_Type *base, struct mcux_i3c_data *data,
979+
uint8_t *buf, uint8_t buf_sz, bool no_ending)
958980
{
959981
int offset = 0;
960982
int remaining = buf_sz;
961983
int ret = 0;
962984

963985
while (remaining > 0) {
964-
ret = reg32_poll_timeout(&base->MDATACTRL, I3C_MDATACTRL_TXFULL_MASK, 0, 1000);
965-
if (ret == -ETIMEDOUT) {
966-
goto one_xfer_write_out;
986+
if (base->MDATACTRL & I3C_MDATACTRL_TXFULL_MASK) {
987+
/* Enable TX buffer ready interrupt */
988+
base->MINTSET = I3C_MSTATUS_TXNOTFULL_MASK;
989+
990+
/* Wait for the transfer to complete */
991+
ret = k_sem_take(&data->device_sync_sem, I3C_TRANSFER_TIMEOUT_MSEC);
992+
if (ret) {
993+
break;
994+
}
967995
}
968996

969997
if ((remaining > 1) || no_ending) {
@@ -976,9 +1004,11 @@ static int mcux_i3c_do_one_xfer_write(I3C_Type *base, uint8_t *buf, uint8_t buf_
9761004
remaining -= 1;
9771005
}
9781006

979-
ret = offset;
1007+
if (!ret) {
1008+
/* Return the number of bytes received */
1009+
ret = offset;
1010+
}
9801011

981-
one_xfer_write_out:
9821012
return ret;
9831013
}
9841014

@@ -1024,9 +1054,9 @@ static int mcux_i3c_do_one_xfer(I3C_Type *base, struct mcux_i3c_data *data,
10241054
}
10251055

10261056
if (is_read) {
1027-
ret = mcux_i3c_do_one_xfer_read(base, buf, buf_sz, false);
1057+
ret = mcux_i3c_do_one_xfer_read(base, data, buf, buf_sz, false);
10281058
} else {
1029-
ret = mcux_i3c_do_one_xfer_write(base, buf, buf_sz, no_ending);
1059+
ret = mcux_i3c_do_one_xfer_write(base, data, buf, buf_sz, no_ending);
10301060
}
10311061

10321062
if (ret < 0) {
@@ -1367,7 +1397,7 @@ static int mcux_i3c_do_ccc(const struct device *dev,
13671397
/* Write the CCC code */
13681398
mcux_i3c_status_clear_all(base);
13691399
mcux_i3c_errwarn_clear_all_nowait(base);
1370-
ret = mcux_i3c_do_one_xfer_write(base, &payload->ccc.id, 1,
1400+
ret = mcux_i3c_do_one_xfer_write(base, data, &payload->ccc.id, 1,
13711401
payload->ccc.data_len > 0);
13721402
if (ret < 0) {
13731403
LOG_ERR("CCC[0x%02x] %s command error (%d)",
@@ -1382,7 +1412,7 @@ static int mcux_i3c_do_ccc(const struct device *dev,
13821412
if (payload->ccc.data_len > 0) {
13831413
mcux_i3c_status_clear_all(base);
13841414
mcux_i3c_errwarn_clear_all_nowait(base);
1385-
ret = mcux_i3c_do_one_xfer_write(base, payload->ccc.data,
1415+
ret = mcux_i3c_do_one_xfer_write(base, data, payload->ccc.data,
13861416
payload->ccc.data_len, false);
13871417
if (ret < 0) {
13881418
LOG_ERR("CCC[0x%02x] %s command payload error (%d)",
@@ -1518,7 +1548,7 @@ static void mcux_i3c_ibi_work(struct k_work *work)
15181548
case I3C_MSTATUS_IBITYPE_IBI:
15191549
target = i3c_dev_list_i3c_addr_find(dev, (uint8_t)ibiaddr);
15201550
if (target != NULL) {
1521-
ret = mcux_i3c_do_one_xfer_read(base, &payload[0],
1551+
ret = mcux_i3c_do_one_xfer_read(base, data, &payload[0],
15221552
sizeof(payload), true);
15231553
if (ret >= 0) {
15241554
payload_sz = (size_t)ret;
@@ -1801,10 +1831,12 @@ int mcux_i3c_ibi_disable(const struct device *dev,
18011831
*/
18021832
static void mcux_i3c_isr(const struct device *dev)
18031833
{
1804-
#ifdef CONFIG_I3C_USE_IBI
18051834
const struct mcux_i3c_config *config = dev->config;
18061835
I3C_Type *base = config->base;
1836+
struct mcux_i3c_data *dev_data = dev->data;
1837+
uint32_t interrupt_enable = base->MINTSET;
18071838

1839+
#ifdef CONFIG_I3C_USE_IBI
18081840
/* Target initiated IBIs */
18091841
if (mcux_i3c_status_is_set(base, I3C_MSTATUS_SLVSTART_MASK)) {
18101842
int err;
@@ -1827,9 +1859,19 @@ static void mcux_i3c_isr(const struct device *dev)
18271859
base->MINTSET = I3C_MINTCLR_SLVSTART_MASK;
18281860
}
18291861
}
1830-
#else
1831-
ARG_UNUSED(dev);
18321862
#endif
1863+
1864+
if (interrupt_enable & I3C_MSTATUS_RXPEND_MASK) {
1865+
/* Disable RX buffer ready interrupt */
1866+
base->MINTCLR = I3C_MSTATUS_RXPEND_MASK;
1867+
k_sem_give(&dev_data->device_sync_sem);
1868+
} else if (interrupt_enable & I3C_MSTATUS_TXNOTFULL_MASK) {
1869+
/* Disable TX buffer ready interrupt */
1870+
base->MINTCLR = I3C_MSTATUS_TXNOTFULL_MASK;
1871+
k_sem_give(&dev_data->device_sync_sem);
1872+
} else {
1873+
/* Nothing to do right now */
1874+
}
18331875
}
18341876

18351877
/**
@@ -1966,6 +2008,7 @@ static int mcux_i3c_init(const struct device *dev)
19662008

19672009
k_mutex_init(&data->lock);
19682010
k_condvar_init(&data->condvar);
2011+
k_sem_init(&data->device_sync_sem, 0, K_SEM_MAX_LIMIT);
19692012

19702013
I3C_MasterGetDefaultConfig(&ctrl_config_hal);
19712014

0 commit comments

Comments
 (0)