diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 3ee359e41e..754e698df5 100644 --- a/arch/arm/Kconfig +++ b/arch/arm/Kconfig @@ -318,6 +318,7 @@ config ARCH_CHIP_S32K3XX select ARCH_HAVE_MPU select ARCH_HAVE_RAMFUNCS select ARM_HAVE_MPU_UNIFIED + select ARCH_HAVE_I2CRESET ---help--- NPX S32K3XX architectures (ARM Cortex-M7). diff --git a/arch/arm/src/s32k3xx/Kconfig b/arch/arm/src/s32k3xx/Kconfig index 654c512cf1..95ddde0ed4 100644 --- a/arch/arm/src/s32k3xx/Kconfig +++ b/arch/arm/src/s32k3xx/Kconfig @@ -1210,6 +1210,58 @@ config S32K3XX_LPSPI5_PINCFG endmenu # LPSPI Configuration +menu "LPI2C Configuration" + depends on S32K3XX_LPI2C + +config S32K3XX_LPI2C_DMA + bool "I2C DMA Support" + default n + depends on S32K3XX_LPI2C && S32K3XX_EDMA && !I2C_POLLED + ---help--- + This option enables the DMA for I2C transfers. + Note: The user can define CONFIG_I2C_DMAPRIO: a custom priority value + for the I2C dma streams, else the default priority level is set to + medium. + +config S32K3XX_LPI2C_DMA_MAXMSG + int "Maximum number messages that will be DMAed" + default 8 + depends on S32K3XX_LPI2C_DMA + ---help--- + This option set the mumber of mesg that can be in a transfer. + It is used to allocate space for the 16 bit LPI2C commands + that will be DMA-ed to the LPI2C device. + +config S32K3XX_LPI2C_DYNTIMEO + bool "Use dynamic timeouts" + default n + depends on S32K3XX_LPI2C + +config S32K3XX_LPI2C_DYNTIMEO_USECPERBYTE + int "Timeout Microseconds per Byte" + default 500 + depends on S32K3XX_LPI2C_DYNTIMEO + +config S32K3XX_LPI2C_DYNTIMEO_STARTSTOP + int "Timeout for Start/Stop (Milliseconds)" + default 1000 + depends on S32K3XX_LPI2C_DYNTIMEO + +config S32K3XX_LPI2C_TIMEOSEC + int "Timeout seconds" + default 0 + depends on S32K3XX_LPI2C + +config S32K3XX_LPI2C_TIMEOMS + int "Timeout Milliseconds" + default 500 + depends on S32K3XX_LPI2C && !S32K3XX_LPI2C_DYNTIMEO + +config S32K3XX_LPI2C_TIMEOTICKS + int "Timeout for Done and Stop (ticks)" + default 500 + depends on S32K3XX_LPI2C && !S32K3XX_LPI2C_DYNTIMEO + menu "LPI2C0 Configuration" depends on S32K3XX_LPI2C0 @@ -1217,6 +1269,11 @@ config LPI2C0_BUSYIDLE int "Bus idle timeout period in clock cycles" default 0 +config LPI2C0_DMA + bool "Enable DMA for I2C0" + default n + depends on S32K3XX_LPI2C_DMA + config LPI2C0_FILTSCL int "I2C master digital glitch filters for SCL input in clock cycles" default 0 @@ -1234,6 +1291,11 @@ config LPI2C1_BUSYIDLE int "Bus idle timeout period in clock cycles" default 0 +config LPI2C1_DMA + bool "Enable DMA for I2C1" + default n + depends on S32K3XX_LPI2C_DMA + config LPI2C1_FILTSCL int "I2C master digital glitch filters for SCL input in clock cycles" default 0 @@ -1243,7 +1305,7 @@ config LPI2C1_FILTSDA default 0 endmenu # LPI2C1 Configuration - +endmenu # LPI2C Configuration menu "LPUART Configuration" depends on S32K3XX_LPUART diff --git a/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c b/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c index e1eb35a3f8..6881db08df 100644 --- a/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c +++ b/arch/arm/src/s32k3xx/s32k3xx_lpi2c.c @@ -46,8 +46,9 @@ #include #include "arm_internal.h" - +#include "s32k3xx_edma.h" #include "s32k3xx_pin.h" +#include "hardware/s32k3xx_dmamux.h" #include "hardware/s32k3xx_pinmux.h" #include "hardware/s32k3xx_lpi2c.h" #include "s32k3xx_lpi2c.h" @@ -117,15 +118,11 @@ #define LPI2C_MASTER 1 #define LPI2C_SLAVE 2 -#define MKI2C_OUTPUT(p) (((p) & GPIO_PADMUX_MASK) | \ - IOMUX_OPENDRAIN | IOMUX_DRIVE_33OHM | \ - IOMUX_SLEW_SLOW | (5 << GPIO_ALT_SHIFT) | \ - IOMUX_PULL_NONE | GPIO_OUTPUT_ONE) +#define MKI2C_OUTPUT(p) (((p) & (_PIN_PORT_MASK | _PIN_MASK)) | \ + GPIO_OUTPUT | GPIO_OUTPUT_ONE) -#define MKI2C_INPUT(p) (((p) & GPIO_PADMUX_MASK) | \ - IOMUX_DRIVE_HIZ | IOMUX_SLEW_SLOW | \ - IOMUX_CMOS_INPUT | (5 << GPIO_ALT_SHIFT) | \ - IOMUX_PULL_NONE) +#define MKI2C_INPUT(p) (((p) & (_PIN_PORT_MASK | _PIN_MASK)) | \ + GPIO_INPUT) /**************************************************************************** * Private Types @@ -179,6 +176,10 @@ struct s32k3xx_lpi2c_config_s #ifndef CONFIG_I2C_POLLED uint32_t irq; /* Event IRQ */ #endif +#ifdef CONFIG_S32K3XX_LPI2C_DMA + uint32_t dma_rxreqsrc; /* DMA mux rx source */ + uint32_t dma_txreqsrc; /* DMA mux tx source */ +#endif }; /* I2C Device Private Data */ @@ -218,6 +219,11 @@ struct s32k3xx_lpi2c_priv_s struct s32k3xx_trace_s trace[CONFIG_I2C_NTRACE]; #endif +#ifdef CONFIG_S32K3XX_LPI2C_DMA + DMACH_HANDLE rxdma; /* rx DMA handle */ + DMACH_HANDLE txdma; /* tx DMA handle */ + uint16_t cmnds[CONFIG_S32K3XX_LPI2C_DMA_MAXMSG]; /* Commands */ +#endif uint32_t status; /* End of transfer SR2|SR1 status */ }; @@ -273,6 +279,13 @@ static int s32k3xx_lpi2c_transfer(struct i2c_master_s *dev, static int s32k3xx_lpi2c_reset(struct i2c_master_s *dev); #endif +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static void s32k3xx_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result); +static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result); +#endif + /**************************************************************************** * Private Data ****************************************************************************/ @@ -322,6 +335,10 @@ static const struct s32k3xx_lpi2c_config_s s32k3xx_lpi2c0_config = #ifndef CONFIG_I2C_POLLED .irq = S32K3XX_IRQ_LPI2C0, #endif +#ifdef CONFIG_LPI2C0_DMA + .dma_rxreqsrc = DMA_REQ_LPI2C0_RX, + .dma_txreqsrc = DMA_REQ_LPI2C0_TX, +#endif }; static struct s32k3xx_lpi2c_priv_s s32k3xx_lpi2c0_priv = @@ -360,6 +377,10 @@ static const struct s32k3xx_lpi2c_config_s s32k3xx_lpi2c1_config = #ifndef CONFIG_I2C_POLLED .irq = S32K3XX_IRQ_LPI2C1, #endif +#ifdef CONFIG_LPI2C1_DMA + .dma_rxreqsrc = DMA_REQ_LPI2C1_RX, + .dma_txreqsrc = DMA_REQ_LPI2C1_TX, +#endif }; static struct s32k3xx_lpi2c_priv_s s32k3xx_lpi2c1_priv = @@ -476,13 +497,17 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv) flags = enter_critical_section(); +#ifdef CONFIG_S32K3XX_LPI2C_DMA + if (priv->rxdma == NULL && priv->txdma == NULL) + { +#endif /* Enable Interrupts when master mode */ if (priv->config->mode == LPI2C_MASTER) { if ((priv->flags & I2C_M_READ) != 0) { - regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE | \ + regval = LPI2C_MIER_TDIE | LPI2C_MIER_RDIE | LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | LPI2C_MIER_SDIE; s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, regval); } @@ -505,7 +530,10 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv) * are currently disabled but will be temporarily re-enabled below when * nxsem_tickwait_uninterruptible() sleeps. */ +#ifdef CONFIG_S32K3XX_LPI2C_DMA + } +#endif priv->intstate = INTSTATE_WAITING; do { @@ -518,7 +546,6 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv) ret = nxsem_tickwait_uninterruptible(&priv->sem_isr, CONFIG_S32K3XX_I2CTIMEOTICKS); #endif - if (ret < 0) { /* Break out of the loop on irrecoverable errors. This would @@ -610,6 +637,112 @@ s32k3xx_lpi2c_sem_waitdone(struct s32k3xx_lpi2c_priv_s *priv) } #endif +/**************************************************************************** + * Name: s32k3xx_rxdma_callback + * + * Description: + * This function performs the next I2C operation + * + ****************************************************************************/ +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static void s32k3xx_rxdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg; + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0, + LPI2C_MIER_SDIE); + + if (result != OK) + { + priv->status = s32k3xx_lpi2c_getstatus(priv); + + if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status); + + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Clear the TX and RX FIFOs */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Clear the error */ + + s32k3xx_lpi2c_putreg(priv, S32K3XX_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); + } + } + } +} +#endif + +/**************************************************************************** + * Name: s32k3xx_txdma_callback + * + * Description: + * This function performs the next I2C operation + * + ****************************************************************************/ +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static void s32k3xx_txdma_callback(DMACH_HANDLE handle, void *arg, bool done, + int result) +{ + struct s32k3xx_lpi2c_priv_s *priv = (struct s32k3xx_lpi2c_priv_s *)arg; + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, 0, + LPI2C_MIER_SDIE); + + if (result != OK) + { + priv->status = s32k3xx_lpi2c_getstatus(priv); + + if ((priv->status & LPI2C_MSR_ERROR_MASK) != 0) + { + i2cerr("ERROR: MSR: status: 0x0%" PRIx32 "\n", priv->status); + + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Clear the TX and RX FIFOs */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Clear the error */ + + s32k3xx_lpi2c_putreg(priv, S32K3XX_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); + } + } + } +} +#endif + /**************************************************************************** * Name: s32k3xx_lpi2c_trace* * @@ -732,7 +865,7 @@ static void s32k3xx_lpi2c_tracedump(struct s32k3xx_lpi2c_priv_s *priv) * Name: s32k3xx_lpi2c_pckfreq * * Description: - * Get the peripheral clock frequency for the LPSPI peripheral + * Get the peripheral clock frequency for the LPI2C peripheral * * Input Parameters: * base - The base address of the LPI2C peripheral registers @@ -956,6 +1089,20 @@ static inline void s32k3xx_lpi2c_sendstop(struct s32k3xx_lpi2c_priv_s *priv) s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MTDR_OFFSET, LPI2C_MTDR_CMD_STOP); } +/**************************************************************************** + * Name: s32k3xx_lpi2c_getenabledints + * + * Description: + * Get 32-bit status + * + ****************************************************************************/ + +static inline uint32_t +s32k3xx_lpi2c_getenabledints(struct s32k3xx_lpi2c_priv_s *priv) +{ + return s32k3xx_lpi2c_getreg(priv, S32K3XX_LPI2C_MIER_OFFSET); +} + /**************************************************************************** * Name: s32k3xx_lpi2c_getstatus * @@ -982,11 +1129,93 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv) { uint32_t status = s32k3xx_lpi2c_getstatus(priv); +#ifdef CONFIG_S32K3XX_LPI2C_DMA + uint32_t current_status = status; + + /* Condition the status with only the enabled interrupts */ + + status &= s32k3xx_lpi2c_getenabledints(priv); + + if (priv->rxdma != NULL || priv->txdma != NULL) + { + /* End of packet or Stop */ + + if ((status & (LPI2C_MSR_SDF | LPI2C_MSR_EPF)) != 0) + { + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_STOP, 0); + + /* Acknowledge End of packet or Stop */ + + s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MSR_OFFSET, status & + (LPI2C_MSR_SDF | + LPI2C_MSR_EPF)); + } + + /* Is there an Error condition */ + + if (current_status & LPI2C_MSR_ERROR_MASK) + { + s32k3xx_lpi2c_traceevent(priv, I2CEVENT_ERROR, 0); + + /* Shutdown DMA */ + + if (priv->rxdma != NULL) + { + s32k3xx_dmach_stop(priv->rxdma); + } + + if (priv->txdma != NULL) + { + s32k3xx_dmach_stop(priv->txdma); + } + + /* Clear the TX and RX FIFOs */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCR_OFFSET, 0, + LPI2C_MCR_RTF | LPI2C_MCR_RRF); + + /* Clear the error */ + + s32k3xx_lpi2c_putreg(priv, S32K3XX_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); + } + + return OK; + } + +#endif + /* Check for new trace setup */ s32k3xx_lpi2c_tracenew(priv, status); - /* After an error we can get an SDF */ + /* After an error we can get a STOP Detect Flag */ if (priv->intstate == INTSTATE_DONE && (status & LPI2C_MSR_SDF) != 0) { @@ -1059,9 +1288,9 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv) { if (priv->msgc > 0 && priv->msgv != NULL) { - priv->ptr = priv->msgv->buffer; - priv->dcnt = priv->msgv->length; - priv->flags = priv->msgv->flags; + priv->ptr = priv->msgv->buffer; + priv->dcnt = priv->msgv->length; + priv->flags = priv->msgv->flags; if ((priv->msgv->flags & I2C_M_NOSTART) == 0) { @@ -1125,8 +1354,8 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv) * and wake it up */ - nxsem_post(&priv->sem_isr); priv->intstate = INTSTATE_DONE; + nxsem_post(&priv->sem_isr); } #else priv->status = status; @@ -1178,8 +1407,8 @@ static int s32k3xx_lpi2c_isr_process(struct s32k3xx_lpi2c_priv_s *priv) * and wake it up */ - nxsem_post(&priv->sem_isr); priv->intstate = INTSTATE_DONE; + nxsem_post(&priv->sem_isr); } #else priv->status = status; @@ -1220,7 +1449,7 @@ static int s32k3xx_lpi2c_init(struct s32k3xx_lpi2c_priv_s *priv) { /* Power-up and configure GPIOs . * - * NOTE: Clocking to the LPSPI peripheral must be provided by + * NOTE: Clocking to the LPI2C peripheral must be provided by * board-specific logic as part of the clock configuration logic. */ @@ -1252,7 +1481,7 @@ static int s32k3xx_lpi2c_init(struct s32k3xx_lpi2c_priv_s *priv) /* Set tx and rx watermarks */ s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MFCR_OFFSET, - LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); + LPI2C_MFCR_TXWATER(0) | LPI2C_MFCR_RXWATER(0)); /* Force a frequency update */ @@ -1262,14 +1491,14 @@ static int s32k3xx_lpi2c_init(struct s32k3xx_lpi2c_priv_s *priv) /* Set scl, sda glitch filters and busy idle */ s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MCFGR2_OFFSET, - LPI2C_MCFGR2_BUSIDLE(priv->config->busy_idle) | - LPI2C_MCFGR2_FILTSCL_CYCLES(priv->config->filtscl) | - LPI2C_MCFGR2_FILTSDA_CYCLES(priv->config->filtsda)); + LPI2C_MCFGR2_BUSIDLE(priv->config->busy_idle) | + LPI2C_MCFGR2_FILTSCL_CYCLES(priv->config->filtscl) | + LPI2C_MCFGR2_FILTSDA_CYCLES(priv->config->filtsda)); /* Set pin low cycles to 0 (disable) */ s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MCFGR3_OFFSET, - LPI2C_MCFGR3_PINLOW_CYCLES(0)); + LPI2C_MCFGR3_PINLOW_CYCLES(0)); /* Attach ISRs */ @@ -1319,6 +1548,223 @@ static int s32k3xx_lpi2c_deinit(struct s32k3xx_lpi2c_priv_s *priv) * Device Driver Operations ****************************************************************************/ +/**************************************************************************** + * Name: s32k3xx_lpi2c_dma_command_configure + * + * Description: + * Create a command TCD + * + ****************************************************************************/ + +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static int +s32k3xx_lpi2c_dma_command_configure(struct s32k3xx_lpi2c_priv_s + *priv, uint16_t *ccmd, + uint32_t ncmd) +{ + struct s32k3xx_edma_xfrconfig_s config; + memset(&config, 0, sizeof(config)); + + config.saddr = (uint32_t) ccmd; + config.daddr = priv->config->base + S32K3XX_LPI2C_MTDR_OFFSET; + config.soff = sizeof(uint16_t); + config.doff = 0; + config.iter = 1; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = EDMA_16BIT; + config.dsize = EDMA_16BIT; + config.nbytes = sizeof(uint16_t) * ncmd; + + up_clean_dcache((uintptr_t)config.saddr, + (uintptr_t)config.saddr + config.nbytes); + + return s32k3xx_dmach_xfrsetup(priv->txdma, &config); +} +#endif + +/**************************************************************************** + * Name: s32k3xx_lpi2c_dma_data_configure + * + * Description: + * Create a data TCD + * + ****************************************************************************/ + +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static int s32k3xx_lpi2c_dma_data_configure(struct s32k3xx_lpi2c_priv_s + *priv, + struct i2c_msg_s *msg) +{ + DMACH_HANDLE dma; + struct s32k3xx_edma_xfrconfig_s config; + memset(&config, 0, sizeof(config)); + + config.iter = msg->length; + config.flags = EDMA_CONFIG_LINKTYPE_LINKNONE; + config.ssize = EDMA_8BIT; + config.dsize = EDMA_8BIT; + config.nbytes = sizeof(msg->buffer[0]); + + if (msg->flags & I2C_M_READ) + { + dma = priv->rxdma; + config.saddr = priv->config->base + S32K3XX_LPI2C_MRDR_OFFSET; + config.daddr = (uint32_t) msg->buffer; + config.soff = 0; + config.doff = sizeof(msg->buffer[0]); + up_invalidate_dcache((uintptr_t)msg->buffer, + (uintptr_t)msg->buffer + msg->length); + } + else + { + dma = priv->txdma; + config.saddr = (uint32_t) msg->buffer; + config.daddr = priv->config->base + S32K3XX_LPI2C_MTDR_OFFSET; + config.soff = sizeof(msg->buffer[0]); + config.doff = 0; + up_clean_dcache((uintptr_t)msg->buffer, + (uintptr_t)msg->buffer + msg->length); + } + + return s32k3xx_dmach_xfrsetup(dma, &config) ? 0 : msg->length; +} +#endif + +/**************************************************************************** + * Name: s32k3xx_lpi2c_configure_dma_transfer + * + * Description: + * DMA based I2C transfer function + * + ****************************************************************************/ + +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static int s32k3xx_lpi2c_form_command_list(struct s32k3xx_lpi2c_priv_s + *priv, struct i2c_msg_s *msg, + int ncmds) +{ + ssize_t length = 0; + + if (priv->flags & I2C_M_NOSTART) + { + if (priv->flags & I2C_M_READ) + { + /* No start read operation */ + + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(msg->length - 1); + } + } + else + { + /* A start based read or write operation */ + + /* Create bus address with R/W */ + + uint16_t badd = (priv->flags & I2C_M_READ) ? I2C_READADDR8(msg->addr) : + I2C_WRITEADDR8(msg->addr); + + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_START | LPI2C_MTDR_DATA(badd); + + if (badd & I2C_READBIT) + { + length = msg->length; + while (length) + { + if (length > 256u) + { + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(256u - 1); + length -= 256u; + } + else + { + priv->cmnds[ncmds++] = LPI2C_MTDR_CMD_RXD | + LPI2C_MTDR_DATA(length - 1); + length = 0; + } + } + } + } + + return ncmds; +} +#endif + +/**************************************************************************** + * Name: s32k3xx_lpi2c_dma_transfer + * + * Description: + * DMA based I2C transfer function + * + ****************************************************************************/ + +#ifdef CONFIG_S32K3XX_LPI2C_DMA +static int s32k3xx_lpi2c_dma_transfer(struct s32k3xx_lpi2c_priv_s *priv) +{ + int m; + int ntotcmds = 0; + int ncmds = 0; + uint16_t *ccmnd = NULL; + + /* Disable Interrupts */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MIER_OFFSET, + LPI2C_MIER_RDIE | LPI2C_MIER_TDIE, 0); + + /* Disable DMA */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MDER_OFFSET, LPI2C_MDER_TDDE | + LPI2C_MDER_RDDE, 0); + + /* Turn off auto_stop option */ + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MCFGR1_OFFSET, 0, + LPI2C_MCFGR1_IGNACK | LPI2C_MCFGR1_AUTOSTOP); + + /* Form chains of tcd to process the messages */ + + for (m = 0; m < priv->msgc; m++) + { + ncmds = 0; + priv->flags = priv->msgv[m].flags; + + /* Form a command list */ + + ccmnd = &priv->cmnds[ntotcmds]; + + ncmds = s32k3xx_lpi2c_form_command_list(priv, &priv->msgv[m], + ntotcmds); + + /* Have commands for this message ? */ + + if (ncmds != 0) + { + /* Build up a TCD with the command from this message */ + + s32k3xx_lpi2c_dma_command_configure(priv, ccmnd, ncmds - ntotcmds); + + ntotcmds += ncmds; + + DEBUGASSERT(ntotcmds < CONFIG_S32K3XX_LPI2C_DMA_MAXMSG); + + s32k3xx_lpi2c_dma_data_configure(priv, &priv->msgv[m]); + } + } + + s32k3xx_lpi2c_putreg(priv, S32K3XX_LPI2C_MIER_OFFSET, + LPI2C_MIER_NDIE | LPI2C_MIER_ALIE | + LPI2C_MIER_PLTIE | LPI2C_MIER_FEIE); + + s32k3xx_dmach_start(priv->rxdma, s32k3xx_rxdma_callback, (void *)priv); + s32k3xx_dmach_start(priv->txdma, s32k3xx_txdma_callback, (void *)priv); + + s32k3xx_lpi2c_modifyreg(priv, S32K3XX_LPI2C_MDER_OFFSET, 0, + LPI2C_MDER_TDDE | LPI2C_MDER_RDDE); + return OK; +} +#endif + /**************************************************************************** * Name: s32k3xx_lpi2c_transfer * @@ -1376,8 +1822,27 @@ static int s32k3xx_lpi2c_transfer(struct i2c_master_s *dev, * the BUSY flag. */ +#ifdef CONFIG_S32K3XX_LPI2C_DMA + if (priv->rxdma || priv->txdma) + { + s32k3xx_lpi2c_dma_transfer(priv); + } +#endif + if (s32k3xx_lpi2c_sem_waitdone(priv) < 0) { +#ifdef CONFIG_S32K3XX_LPI2C_DMA + if (priv->rxdma != NULL) + { + s32k3xx_dmach_stop(priv->rxdma); + } + + if (priv->txdma != NULL) + { + s32k3xx_dmach_stop(priv->txdma); + } + +#endif ret = -ETIMEDOUT; i2cerr("ERROR: Timed out: MCR: status: 0x%" PRIx32 "\n", priv->status); @@ -1486,12 +1951,12 @@ static int s32k3xx_lpi2c_reset(struct i2c_master_s *dev) /* Let SDA go high */ - s32k3xx_gpio_write(sda_gpio, 1); + s32k3xx_gpiowrite(sda_gpio, 1); /* Clock the bus until any slaves currently driving it let it go. */ clock_count = 0; - while (!s32k3xx_gpio_read(sda_gpio)) + while (!s32k3xx_gpioread(sda_gpio)) { /* Give up if we have tried too hard */ @@ -1506,7 +1971,7 @@ static int s32k3xx_lpi2c_reset(struct i2c_master_s *dev) */ stretch_count = 0; - while (!s32k3xx_gpio_read(scl_gpio)) + while (!s32k3xx_gpioread(scl_gpio)) { /* Give up if we have tried too hard */ @@ -1520,12 +1985,12 @@ static int s32k3xx_lpi2c_reset(struct i2c_master_s *dev) /* Drive SCL low */ - s32k3xx_gpio_write(scl_gpio, 0); + s32k3xx_gpiowrite(scl_gpio, 0); up_udelay(10); /* Drive SCL high again */ - s32k3xx_gpio_write(scl_gpio, 1); + s32k3xx_gpiowrite(scl_gpio, 1); up_udelay(10); } @@ -1533,13 +1998,13 @@ static int s32k3xx_lpi2c_reset(struct i2c_master_s *dev) * state machines. */ - s32k3xx_gpio_write(sda_gpio, 0); + s32k3xx_gpiowrite(sda_gpio, 0); up_udelay(10); - s32k3xx_gpio_write(scl_gpio, 0); + s32k3xx_gpiowrite(scl_gpio, 0); up_udelay(10); - s32k3xx_gpio_write(scl_gpio, 1); + s32k3xx_gpiowrite(scl_gpio, 1); up_udelay(10); - s32k3xx_gpio_write(sda_gpio, 1); + s32k3xx_gpiowrite(sda_gpio, 1); up_udelay(10); /* Revert the GPIO configuration. */ @@ -1612,6 +2077,22 @@ struct i2c_master_s *s32k3xx_i2cbus_initialize(int port) if (priv->refs++ == 0) { s32k3xx_lpi2c_init(priv); + +#ifdef CONFIG_S32K3XX_LPI2C_DMA + if (priv->config->dma_txreqsrc != 0) + { + priv->txdma = s32k3xx_dmach_alloc(priv->config->dma_txreqsrc | + DMAMUX_CHCFG_ENBL, 0); + DEBUGASSERT(priv->txdma != NULL); + } + + if (priv->config->dma_rxreqsrc != 0) + { + priv->rxdma = s32k3xx_dmach_alloc(priv->config->dma_rxreqsrc | + DMAMUX_CHCFG_ENBL, 0); + DEBUGASSERT(priv->rxdma != NULL); + } +#endif } nxmutex_unlock(&priv->lock); @@ -1648,6 +2129,22 @@ int s32k3xx_i2cbus_uninitialize(struct i2c_master_s *dev) /* Disable power and other HW resource (GPIO's) */ +#ifdef CONFIG_S32K3XX_LPI2C_DMA + if (priv->rxdma != NULL) + { + s32k3xx_dmach_stop(priv->rxdma); + s32k3xx_dmach_free(priv->rxdma); + priv->rxdma = NULL; + } + + if (priv->txdma != NULL) + { + s32k3xx_dmach_stop(priv->txdma); + s32k3xx_dmach_free(priv->txdma); + priv->txdma = NULL; + } +#endif + s32k3xx_lpi2c_deinit(priv); nxmutex_unlock(&priv->lock);