diff --git a/arch/arm/src/imxrt/imxrt_lpi2c.c b/arch/arm/src/imxrt/imxrt_lpi2c.c index 499d26e502..d81333c9a6 100644 --- a/arch/arm/src/imxrt/imxrt_lpi2c.c +++ b/arch/arm/src/imxrt/imxrt_lpi2c.c @@ -51,13 +51,13 @@ #include "hardware/imxrt_dmamux.h" #include "hardware/imxrt_pinmux.h" +#include "imxrt_clockconfig.h" #include "hardware/imxrt_ccm.h" #include "imxrt_periphclks.h" /* At least one I2C peripheral must be enabled */ -#if defined(CONFIG_IMXRT_LPI2C1) || defined(CONFIG_IMXRT_LPI2C2) || \ - defined(CONFIG_IMXRT_LPI2C3) || defined(CONFIG_IMXRT_LPI2C4) +#ifdef CONFIG_IMXRT_LPI2C /**************************************************************************** * Pre-processor Definitions @@ -119,6 +119,8 @@ #define LPI2C_MASTER 1 #define LPI2C_SLAVE 2 +#define LPI2C_MSR_LIMITED_ERROR_MASK (LPI2C_MSR_ERROR_MASK & ~(LPI2C_MSR_FEF)) + /**************************************************************************** * Private Types ****************************************************************************/ @@ -500,6 +502,90 @@ static struct imxrt_lpi2c_priv_s imxrt_lpi2c4_priv = }; #endif +#ifdef CONFIG_IMXRT_LPI2C5 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c5_config = +{ + .base = IMXRT_LPI2C5_BASE, + .busy_idle = CONFIG_LPI2C5_BUSYIDLE, + .filtscl = CONFIG_LPI2C5_FILTSCL, + .filtsda = CONFIG_LPI2C5_FILTSDA, + .scl_pin = GPIO_LPI2C5_SCL, + .sda_pin = GPIO_LPI2C5_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C5_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C5_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C5, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c5_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c5_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + +#ifdef CONFIG_IMXRT_LPI2C6 +static const struct imxrt_lpi2c_config_s imxrt_lpi2c6_config = +{ + .base = IMXRT_LPI2C6_BASE, + .busy_idle = CONFIG_LPI2C6_BUSYIDLE, + .filtscl = CONFIG_LPI2C6_FILTSCL, + .filtsda = CONFIG_LPI2C6_FILTSDA, + .scl_pin = GPIO_LPI2C6_SCL, + .sda_pin = GPIO_LPI2C6_SDA, +#if defined(CONFIG_I2C_RESET) + .reset_scl_pin = GPIO_LPI2C6_SCL_RESET, + .reset_sda_pin = GPIO_LPI2C6_SDA_RESET, +#endif +#ifndef CONFIG_I2C_SLAVE + .mode = LPI2C_MASTER, +#else + .mode = LPI2C_SLAVE, +#endif +#ifndef CONFIG_I2C_POLLED + .irq = IMXRT_IRQ_LPI2C6, +#endif +}; + +static struct imxrt_lpi2c_priv_s imxrt_lpi2c6_priv = +{ + .ops = &imxrt_lpi2c_ops, + .config = &imxrt_lpi2c6_config, + .refs = 0, + .lock = NXMUTEX_INITIALIZER, +#ifndef CONFIG_I2C_POLLED + .sem_isr = SEM_INITIALIZER(0), +#endif + .intstate = INTSTATE_IDLE, + .msgc = 0, + .msgv = NULL, + .ptr = NULL, + .dcnt = 0, + .flags = 0, + .status = 0 +}; +#endif + /**************************************************************************** * Private Functions ****************************************************************************/ @@ -599,6 +685,11 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) if (priv->dma == NULL) { #endif + /* Clear the TX and RX FIFOs */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + /* Enable Interrupts when master mode */ if (priv->config->mode == LPI2C_MASTER) @@ -624,21 +715,10 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) { #warning Missing logic for I2C Slave mode } - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_timedwait() sleeps. - */ #ifdef CONFIG_IMXRT_LPI2C_DMA } #endif - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_tickwait_uninterruptible() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; do { /* Wait until either the transfer is complete or the timeout expires */ @@ -671,15 +751,10 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) /* Disable I2C interrupts */ - /* Enable Interrupts when master mode */ - if (priv->config->mode == LPI2C_MASTER) { imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0); } - - /* Enable Interrupts when slave mode */ - else { imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_SIER_OFFSET, 0); @@ -704,13 +779,6 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) #else timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; #endif - - /* Signal the interrupt handler that we are waiting. NOTE: Interrupts - * are currently disabled but will be temporarily re-enabled below when - * nxsem_tickwait_uninterruptible() sleeps. - */ - - priv->intstate = INTSTATE_WAITING; start = clock_systime_ticks(); do @@ -741,6 +809,95 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) } #endif +/**************************************************************************** + * Name: imxrt_lpi2c_sem_waitstop + * + * Description: + * Wait for a STOP to complete + * + ****************************************************************************/ + +static inline void +imxrt_lpi2c_sem_waitstop(struct imxrt_lpi2c_priv_s *priv) +{ + clock_t start; + clock_t elapsed; + clock_t timeout; + uint32_t regval; + + /* Select a timeout */ + +#ifdef CONFIG_IMXRT_LPI2C_DYNTIMEO + timeout = USEC2TICK(CONFIG_IMXRT_LPI2C_DYNTIMEO_STARTSTOP); +#else + timeout = CONFIG_IMXRT_LPI2C_TIMEOTICKS; +#endif + + /* Wait as stop might still be in progress; but stop might also + * be set because of a timeout error: "The [STOP] bit is set and + * cleared by software, cleared by hardware when a Stop condition is + * detected, set by hardware when a timeout error is detected." + */ + + start = clock_systime_ticks(); + do + { + /* Calculate the elapsed time */ + + elapsed = clock_systime_ticks() - start; + + /* Check for STOP condition */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_SDF) == LPI2C_MSR_SDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_SSR_OFFSET); + if ((regval & LPI2C_SSR_SDF) == LPI2C_SSR_SDF) + { + return; + } + } + + /* Check for NACK error */ + + if (priv->config->mode == LPI2C_MASTER) + { + regval = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MSR_OFFSET); + if ((regval & LPI2C_MSR_NDF) == LPI2C_MSR_NDF) + { + return; + } + } + + /* Enable Interrupts when slave mode */ + + else + { +#warning Missing logic for I2C Slave + } + } + + /* Loop until the stop is complete or a timeout occurs. */ + + while (elapsed < timeout); + + /* If we get here then a timeout occurred with the STOP condition + * still pending. + */ + + i2cinfo("Timeout with Status Register: %" PRIx32 "\n", regval); +} + /**************************************************************************** * Name: imxrt_dma_callback * @@ -748,7 +905,6 @@ imxrt_lpi2c_sem_waitdone(struct imxrt_lpi2c_priv_s *priv) * This function performs the next I2C operation * ****************************************************************************/ - #ifdef CONFIG_IMXRT_LPI2C_DMA static void imxrt_dma_callback(DMACH_HANDLE handle, void *arg, bool done, int result) @@ -758,38 +914,23 @@ static void imxrt_dma_callback(DMACH_HANDLE handle, void *arg, bool done, imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0, LPI2C_MIER_SDIE); - if (result != OK) + if (result == OK) { - priv->status = imxrt_lpi2c_getstatus(priv); - - if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + if ((priv->flags & I2C_M_NOSTOP) == 0) { - i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status); + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + imxrt_lpi2c_sendstop(priv); + } + } + else + { + uint32_t status = imxrt_lpi2c_getstatus(priv); + + if ((status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", status); imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* Clear the TX and RX FIFOs */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, - LPI2C_MCR_RTF | LPI2C_MCR_RRF); - - /* Clear the error */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, - (priv->status & (LPI2C_MSR_NDF | - LPI2C_MSR_ALF | - LPI2C_MSR_FEF | - LPI2C_MSR_PLTF))); - - if (priv->intstate == INTSTATE_WAITING) - { - /* inform the thread that transfer is complete - * and wake it up - */ - - priv->intstate = INTSTATE_DONE; - nxsem_post(&priv->sem_isr); - } } } } @@ -923,9 +1064,11 @@ static void imxrt_lpi2c_tracedump(struct imxrt_lpi2c_priv_s *priv) static void imxrt_lpi2c_setclock(struct imxrt_lpi2c_priv_s *priv, uint32_t frequency) { - uint32_t src_freq = 0; +#ifndef CONFIG_ARCH_FAMILY_IMXRT117x uint32_t pll3_div = 0; uint32_t lpi2c_clk_div; +#endif + uint32_t src_freq = 0; uint32_t regval; uint32_t men; uint32_t prescale = 0; @@ -957,6 +1100,32 @@ static void imxrt_lpi2c_setclock(struct imxrt_lpi2c_priv_s *priv, /* Get the LPI2C clock source frequency */ +#ifdef CONFIG_ARCH_FAMILY_IMXRT117x + if (priv->config->base == IMXRT_LPI2C1_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C1, &src_freq); + } + else if (priv->config->base == IMXRT_LPI2C2_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C2, &src_freq); + } + else if (priv->config->base == IMXRT_LPI2C3_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C3, &src_freq); + } + else if (priv->config->base == IMXRT_LPI2C4_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C4, &src_freq); + } + else if (priv->config->base == IMXRT_LPI2C5_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C5, &src_freq); + } + else if (priv->config->base == IMXRT_LPI2C6_BASE) + { + imxrt_get_rootclock(CCM_CR_LPI2C6, &src_freq); + } +#else if ((getreg32(IMXRT_CCM_CSCDR2) & CCM_CSCDR2_LPI2C_CLK_SEL) == CCM_CSCDR2_LPI2C_CLK_SEL_OSC_CLK) { @@ -979,8 +1148,9 @@ static void imxrt_lpi2c_setclock(struct imxrt_lpi2c_priv_s *priv, CCM_CSCDR2_LPI2C_CLK_PODF_SHIFT; lpi2c_clk_div = lpi2c_clk_div + 1; src_freq = (BOARD_XTAL_FREQUENCY * pll3_div) / - (8 * lpi2c_clk_div); + (8 * lpi2c_clk_div) ; } +#endif /* LPI2C output frequency = (Source Clock (Hz)/ 2^prescale) / * (CLKLO + 1 + CLKHI + 1 + ROUNDDOWN((2 + FILTSCL) / 2^prescale) @@ -1090,10 +1260,10 @@ static inline void imxrt_lpi2c_sendstart(struct imxrt_lpi2c_priv_s *priv, /* Generate START condition and send the address */ - /* Turn off auto_stop option */ + /* Disable AUTOSTOP and turn NAK Ignore off */ imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, - LPI2C_MCFGR1_IGNACK, 0); + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP, 0); do { @@ -1159,13 +1329,11 @@ imxrt_lpi2c_getstatus(struct imxrt_lpi2c_priv_s *priv) * ****************************************************************************/ -#ifdef CONFIG_IMXRT_LPI2C_DMA static inline uint32_t imxrt_lpi2c_getenabledints(struct imxrt_lpi2c_priv_s *priv) { return imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MIER_OFFSET); } -#endif /**************************************************************************** * Name: imxrt_lpi2c_isr_process @@ -1188,6 +1356,17 @@ static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) if (priv->dma != NULL) { + /* Is there an Error condition */ + + if (current_status & LPI2C_MSR_LIMITED_ERROR_MASK) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Return the full error status */ + + priv->status = current_status; + } + /* End of packet or Stop */ if ((status & (LPI2C_MSR_SDF | LPI2C_MSR_EPF)) != 0) @@ -1199,55 +1378,32 @@ static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, status & (LPI2C_MSR_SDF | LPI2C_MSR_EPF)); + + /* Mark that this transaction stopped */ + + priv->msgv = NULL; + priv->msgc = 0; + priv->dcnt = -1; + + if (priv->intstate == INTSTATE_WAITING) + { + /* inform the thread that transfer is complete + * and wake it up + */ + + imxrt_dmach_stop(priv->dma); + + priv->intstate = INTSTATE_DONE; + nxsem_post(&priv->sem_isr); + } } - /* Is there an Error condition */ - - if (current_status & LPI2C_MSR_ERROR_MASK) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - - /* Shutdown DMA */ - - imxrt_dmach_stop(priv->dma); - - /* Clear the TX and RX FIFOs */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, - LPI2C_MCR_RTF | LPI2C_MCR_RRF); - - /* Clear the error */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, - (current_status & (LPI2C_MSR_NDF | - LPI2C_MSR_ALF | - LPI2C_MSR_FEF))); - - /* Return the full error status */ - - status = current_status; - } - - /* Mark that this transaction stopped */ - - priv->msgv = NULL; - priv->msgc = 0; - priv->dcnt = -1; - - if (priv->intstate == INTSTATE_WAITING) - { - /* Update Status once at the end */ - - priv->status = status; - - /* inform the thread that transfer is complete - * and wake it up - */ - - priv->intstate = INTSTATE_DONE; - nxsem_post(&priv->sem_isr); - } + /* Clear the error */ + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, + (current_status & (LPI2C_MSR_NDF | + LPI2C_MSR_ALF | + LPI2C_MSR_FEF))); return OK; } @@ -1256,200 +1412,209 @@ static int imxrt_lpi2c_isr_process(struct imxrt_lpi2c_priv_s *priv) imxrt_lpi2c_tracenew(priv, status); - /* After an error we can get an SDF */ - - if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0) + if ((status & LPI2C_MSR_LIMITED_ERROR_MASK) == 0) { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); - } + /* Check if there is more bytes to send */ - /* Check if there is more bytes to send */ - - else if (((priv->flags & I2C_M_READ) == 0) && - (status & LPI2C_MSR_TDF) != 0) - { - if (priv->dcnt > 0) + if (((priv->flags & I2C_M_READ) == 0) && + (status & LPI2C_MSR_TDF) != 0) { - imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - LPI2C_MTDR_CMD_TXD | - LPI2C_MTDR_DATA(*priv->ptr++)); - priv->dcnt--; - - if ((priv->msgc <= 0) && (priv->dcnt == 0)) + if (priv->dcnt > 0) { - imxrt_lpi2c_sendstop(priv); - } - } - } - - /* Check if there is more bytes to read */ - - else if (((priv->flags & I2C_M_READ) != 0) && - (status & LPI2C_MSR_RDF) != 0) - { - /* Read a byte, if dcnt goes < 0, then read dummy bytes to ack ISRs */ - - if (priv->dcnt > 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - - /* No interrupts or context switches should occur in the following - * sequence. Otherwise, additional bytes may be sent by the device. - */ - -#ifdef CONFIG_I2C_POLLED - irqstate_t flags = enter_critical_section(); -#endif - - /* Receive a byte */ - - *priv->ptr++ = imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET) & - LPI2C_MRDR_DATA_MASK; - priv->dcnt--; - -#ifdef CONFIG_I2C_POLLED - leave_critical_section(flags); -#endif - if ((priv->msgc <= 0) && (priv->dcnt == 0)) - { - imxrt_lpi2c_sendstop(priv); - } - } - else - { - imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET); - } - } - - if (priv->dcnt <= 0) - { - if (priv->msgc > 0 && priv->msgv != NULL) - { - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; - - if ((priv->msgv->flags & I2C_M_NOSTART) == 0) - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, - priv->msgc); - imxrt_lpi2c_sendstart(priv, priv->msgv->addr); - } - else - { - imxrt_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); - } - - priv->msgv++; - priv->msgc--; - - if ((priv->flags & I2C_M_READ) != 0) - { -#ifndef CONFIG_I2C_POLLED - /* Stop TX interrupt */ - - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); -#endif - /* Set LPI2C in read mode */ - - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, - LPI2C_MTDR_CMD_RXD | - LPI2C_MTDR_DATA((priv->dcnt - 1))); - } - else - { - /* Send the first byte from tx buffer */ - imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, priv->dcnt); imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_TXD | LPI2C_MTDR_DATA(*priv->ptr++)); priv->dcnt--; + + /* Last byte of last message? */ + if ((priv->msgc <= 0) && (priv->dcnt == 0)) { - imxrt_lpi2c_sendstop(priv); + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imxrt_lpi2c_sendstop(priv); + } } } } - else if (priv->msgv && ((status & LPI2C_MSR_SDF) != 0)) + + /* Check if there is more bytes to read */ + + else if (((priv->flags & I2C_M_READ) != 0) && + (status & LPI2C_MSR_RDF) != 0) { - imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_SDF); + /* Read a byte, if dcnt goes < 0, read dummy bytes to ack ISRs */ - /* Check is there thread waiting for this event (there should be) */ - -#ifndef CONFIG_I2C_POLLED - if (priv->intstate == INTSTATE_WAITING) + if (priv->dcnt > 0) { - /* Update Status once at the end */ + imxrt_lpi2c_traceevent(priv, I2CEVENT_RCVBYTE, priv->dcnt); - priv->status = status; - - /* inform the thread that transfer is complete - * and wake it up + /* No interrupts or context switches should occur in the + * following sequence. Otherwise, additional bytes may be + * sent by the device. */ - priv->intstate = INTSTATE_DONE; - nxsem_post(&priv->sem_isr); + #ifdef CONFIG_I2C_POLLED + irqstate_t flags = enter_critical_section(); + #endif + + /* Receive a byte */ + + *priv->ptr++ = imxrt_lpi2c_getreg(priv, + IMXRT_LPI2C_MRDR_OFFSET) & + LPI2C_MRDR_DATA_MASK; + priv->dcnt--; + + #ifdef CONFIG_I2C_POLLED + leave_critical_section(flags); + #endif + /* Last byte of last message? */ + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imxrt_lpi2c_sendstop(priv); + } + } } -#else - priv->status = status; - priv->intstate = INTSTATE_DONE; -#endif - /* Mark that this transaction stopped */ + else + { + /* Read and discard data */ - priv->msgv = NULL; + imxrt_lpi2c_getreg(priv, IMXRT_LPI2C_MRDR_OFFSET); + } } -#ifndef CONFIG_I2C_POLLED - else + + /* Start the first or next message */ + + if (priv->dcnt <= 0 && (status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) == 0) { - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, - LPI2C_MIER_TDIE | LPI2C_MIER_RDIE, 0); - } + if (priv->msgc > 0 && priv->msgv != NULL) + { + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; + + if ((priv->flags & I2C_M_NOSTART) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STARTRESTART, + priv->msgc); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTART; + + imxrt_lpi2c_sendstart(priv, priv->msgv->addr); + } + else + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_NOSTART, priv->msgc); + } + + priv->msgv++; + priv->msgc--; + + if ((priv->flags & I2C_M_READ) != 0) + { +#ifndef CONFIG_I2C_POLLED + /* Stop TX interrupt */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE, LPI2C_MIER_RDIE); #endif + /* Set LPI2C in read mode */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA((priv->dcnt - 1))); + } + else + { + /* Send the first byte from tx buffer */ + + imxrt_lpi2c_traceevent(priv, I2CEVENT_SENDBYTE, + priv->dcnt); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MTDR_OFFSET, + LPI2C_MTDR_CMD_TXD | + LPI2C_MTDR_DATA(*priv->ptr++)); + priv->dcnt--; + + /* Last byte of last message? */ + + if ((priv->msgc <= 0) && (priv->dcnt == 0)) + { + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imxrt_lpi2c_sendstop(priv); + } + } + } + } + } } - - /* Check for errors */ - - if ((status & LPI2C_MSR_EPF) != 0) - { - imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_EPF); - } - - if ((status & LPI2C_MSR_ERROR_MASK) != 0) + else { imxrt_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); - /* Clear the TX and RX FIFOs */ + priv->status = status; - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, - LPI2C_MCR_RTF | LPI2C_MCR_RRF); + if ((priv->flags & I2C_M_NOSTOP) == 0) + { + imxrt_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Do this once */ + + priv->flags |= I2C_M_NOSTOP; + imxrt_lpi2c_sendstop(priv); + } /* Clear the error */ imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, (status & (LPI2C_MSR_NDF | LPI2C_MSR_ALF | - LPI2C_MSR_FEF))); + LPI2C_MSR_FEF | LPI2C_MSR_EPF))); + } + + /* Check for endof packet */ + + if ((status & (LPI2C_MSR_EPF | LPI2C_MSR_SDF)) != 0) + { + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, status & + (LPI2C_MSR_EPF | LPI2C_MSR_SDF)); #ifndef CONFIG_I2C_POLLED if (priv->intstate == INTSTATE_WAITING) { - /* Update Status once at the end */ - - priv->status = status; - /* inform the thread that transfer is complete * and wake it up */ priv->intstate = INTSTATE_DONE; + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, + LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | + LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | + LPI2C_MIER_SDIE | LPI2C_MIER_EPIE, 0); nxsem_post(&priv->sem_isr); } #else - priv->status = status; priv->intstate = INTSTATE_DONE; #endif } @@ -1500,7 +1665,19 @@ void imxrt_lpi2c_clock_enable (uint32_t base) } else if (base == IMXRT_LPI2C4_BASE) { +#ifndef CONFIG_ARCH_FAMILY_IMXRT117x imxrt_clockall_lpi2c4_serial(); +#else + imxrt_clockall_lpi2c4(); + } + else if (base == IMXRT_LPI2C5_BASE) + { + imxrt_clockall_lpi2c5(); + } + else if (base == IMXRT_LPI2C6_BASE) + { + imxrt_clockall_lpi2c6(); +#endif } } @@ -1528,7 +1705,19 @@ void imxrt_lpi2c_clock_disable (uint32_t base) } else if (base == IMXRT_LPI2C4_BASE) { +#ifndef CONFIG_ARCH_FAMILY_IMXRT117x imxrt_clockoff_lpi2c4_serial(); +#else + imxrt_clockoff_lpi2c4(); + } + else if (base == IMXRT_LPI2C5_BASE) + { + imxrt_clockoff_lpi2c5(); + } + else if (base == IMXRT_LPI2C6_BASE) + { + imxrt_clockoff_lpi2c6(); +#endif } } @@ -1568,10 +1757,10 @@ static int imxrt_lpi2c_init(struct imxrt_lpi2c_priv_s *priv) LPI2C_MCFG0_HREN | LPI2C_MCFG0_HRSEL, LPI2C_MCFG0_HRPOL); - /* Pin config and ignore NACK disable */ + /* Disable AUTOSTOP and turn NAK Ignore off */ imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, - LPI2C_MCFGR1_IGNACK, LPI2C_MCFGR1_AUTOSTOP); + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP, 0); /* Set tx and rx watermarks */ @@ -1753,10 +1942,10 @@ static int imxrt_lpi2c_dma_data_configure(struct imxrt_lpi2c_priv_s *priv, #endif /**************************************************************************** - * Name: imxrt_lpi2c_configure_dma_transfer + * Name: imxrt_lpi2c_form_command_list * * Description: - * DMA based I2C transfer function + * Form the DMA command list * ****************************************************************************/ @@ -1830,20 +2019,19 @@ static int imxrt_lpi2c_dma_transfer(struct imxrt_lpi2c_priv_s *priv) /* Disable Interrupts */ - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MIER_OFFSET, - LPI2C_MIER_RDIE | LPI2C_MIER_TDIE, 0); + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, 0); /* Disable DMA */ imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MDER_OFFSET, LPI2C_MDER_TDDE | LPI2C_MDER_RDDE, 0); - /* Turn off auto_stop option */ + /* Enable AUTOSTOP and NAK Ignore */ imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCFGR1_OFFSET, 0, LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP); - /* Form chains of tcd to process the messages */ + /* Form chains of TCDs to process the messages */ for (m = 0; m < priv->msgc; m++) { @@ -1883,14 +2071,30 @@ static int imxrt_lpi2c_dma_transfer(struct imxrt_lpi2c_priv_s *priv) } } + /* Clear the TX and RX FIFOs */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Reset the Error bits */ + + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MSR_OFFSET, LPI2C_MSR_NDF | + LPI2C_MSR_ALF | + LPI2C_MSR_FEF); + + /* Enable the Iterrupts */ + imxrt_lpi2c_putreg(priv, IMXRT_LPI2C_MIER_OFFSET, LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | - LPI2C_MIER_PLTIE | LPI2C_MIER_FEIE); + LPI2C_MIER_PLTIE); - imxrt_dmach_start(priv->dma, imxrt_dma_callback, priv); + /* Start The DMA */ - imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MDER_OFFSET, 0, - LPI2C_MDER_TDDE | LPI2C_MDER_RDDE); + imxrt_dmach_start(priv->dma, imxrt_dma_callback, (void *)priv); + + /* Enable the DMA Request */ + + imxrt_lpi2c_modifyreg(priv, IMXRT_LPI2C_MDER_OFFSET, 0, LPI2C_MDER_TDDE); return OK; } #endif @@ -1908,6 +2112,9 @@ static int imxrt_lpi2c_transfer(struct i2c_master_s *dev, { struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; int ret; +#ifdef CONFIG_IMXRT_LPI2C_DMA + int m; +#endif DEBUGASSERT(count > 0); @@ -1948,6 +2155,13 @@ static int imxrt_lpi2c_transfer(struct i2c_master_s *dev, priv->status = 0; + /* Signal the interrupt handler that we are waiting. NOTE: Interrupts + * are currently disabled but will be temporarily re-enabled below when + * nxsem_tickwait_uninterruptible() sleeps. + */ + + priv->intstate = INTSTATE_WAITING; + /* Wait for an ISR, if there was a timeout, fetch latest status to get * the BUSY flag. */ @@ -1962,14 +2176,13 @@ static int imxrt_lpi2c_transfer(struct i2c_master_s *dev, if (imxrt_lpi2c_sem_waitdone(priv) < 0) { #ifdef CONFIG_IMXRT_LPI2C_DMA - if (priv->dma) - { - imxrt_dmach_stop(priv->dma); - } + if (priv->dma) + { + imxrt_dmach_stop(priv->dma); + } #endif ret = -ETIMEDOUT; - i2cerr("ERROR: Timed out: MSR: status: 0x0%" PRIx32 "\n", priv->status); } @@ -2014,6 +2227,20 @@ static int imxrt_lpi2c_transfer(struct i2c_master_s *dev, priv->dcnt = 0; priv->ptr = NULL; +#ifdef CONFIG_IMXRT_LPI2C_DMA + if (priv->dma) + { + for (m = 0; m < count; m++) + { + if (msgs[m].flags & I2C_M_READ) + { + up_invalidate_dcache((uintptr_t)msgs[m].buffer, + (uintptr_t)msgs[m].buffer + msgs[m].length); + } + } + } +#endif + nxmutex_unlock(&priv->lock); return ret; } @@ -2168,7 +2395,8 @@ out: struct i2c_master_s *imxrt_i2cbus_initialize(int port) { - struct imxrt_lpi2c_priv_s *priv = NULL; + struct imxrt_lpi2c_priv_s * priv = NULL; + irqstate_t flags; /* Get I2C private structure */ @@ -2193,6 +2421,16 @@ struct i2c_master_s *imxrt_i2cbus_initialize(int port) case 4: priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c4_priv; break; +#endif +#ifdef CONFIG_IMXRT_LPI2C5 + case 5: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c5_priv; + break; +#endif +#ifdef CONFIG_IMXRT_LPI2C6 + case 6: + priv = (struct imxrt_lpi2c_priv_s *)&imxrt_lpi2c6_priv; + break; #endif default: return NULL; @@ -2202,8 +2440,9 @@ struct i2c_master_s *imxrt_i2cbus_initialize(int port) * power-up hardware and configure GPIOs. */ - nxmutex_lock(&priv->lock); - if (priv->refs++ == 0) + flags = enter_critical_section(); + + if ((volatile int)priv->refs++ == 0) { imxrt_lpi2c_init(priv); @@ -2217,7 +2456,8 @@ struct i2c_master_s *imxrt_i2cbus_initialize(int port) #endif } - nxmutex_unlock(&priv->lock); + leave_critical_section(flags); + return (struct i2c_master_s *)priv; } @@ -2232,6 +2472,7 @@ struct i2c_master_s *imxrt_i2cbus_initialize(int port) int imxrt_i2cbus_uninitialize(struct i2c_master_s *dev) { struct imxrt_lpi2c_priv_s *priv = (struct imxrt_lpi2c_priv_s *)dev; + irqstate_t flags; DEBUGASSERT(dev); @@ -2242,13 +2483,16 @@ int imxrt_i2cbus_uninitialize(struct i2c_master_s *dev) return ERROR; } - nxmutex_lock(&priv->lock); + flags = enter_critical_section(); + if (--priv->refs > 0) { - nxmutex_unlock(&priv->lock); + leave_critical_section(flags); return OK; } + leave_critical_section(flags); + /* Disable power and other HW resource (GPIO's) */ #ifdef CONFIG_IMXRT_LPI2C_DMA @@ -2261,10 +2505,8 @@ int imxrt_i2cbus_uninitialize(struct i2c_master_s *dev) #endif imxrt_lpi2c_deinit(priv); - nxmutex_unlock(&priv->lock); return OK; } -#endif /* CONFIG_IMXRT_LPI2C1 || CONFIG_IMXRT_LPI2C2 || \ - * CONFIG_IMXRT_LPI2C3 || CONFIG_IMXRT_LPI2C4 */ +#endif /* CONFIG_IMXRT_LPI2C */