diff --git a/arch/arm/src/stm32/Kconfig b/arch/arm/src/stm32/Kconfig index 8dea045fda..de2c8fe23b 100644 --- a/arch/arm/src/stm32/Kconfig +++ b/arch/arm/src/stm32/Kconfig @@ -6190,6 +6190,16 @@ config STM32_I2C_DUTY16_9 default n depends on STM32_I2C +config STM32_I2C_DMA + bool "I2C DMA Support" + default n + depends on STM32_I2C && STM32_STM32F40XX && STM32_DMA1 + ---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. + Note: This option is compatible with CONFIG_I2C_POLLED. + endmenu menu "SDIO Configuration" diff --git a/arch/arm/src/stm32/stm32f40xxx_i2c.c b/arch/arm/src/stm32/stm32f40xxx_i2c.c index c0d1a2d6fd..3d7d5567f8 100644 --- a/arch/arm/src/stm32/stm32f40xxx_i2c.c +++ b/arch/arm/src/stm32/stm32f40xxx_i2c.c @@ -63,7 +63,6 @@ * - 1 x 10 bit addresses + 1 x 7 bit address (?) * - plus the broadcast address (general call) * - Multi-master support - * - DMA (to get rid of too many CPU wake-ups and interventions) * - Be ready for IPMI */ @@ -95,6 +94,7 @@ #include "stm32_rcc.h" #include "stm32_i2c.h" #include "stm32_waste.h" +#include "stm32_dma.h" /* At least one I2C peripheral must be enabled */ @@ -162,6 +162,21 @@ #define MKI2C_OUTPUT(p) (((p) & (GPIO_PORT_MASK | GPIO_PIN_MASK)) | I2C_OUTPUT) +/* I2C DMA priority */ + +#ifdef CONFIG_STM32_I2C_DMA + +# if defined(CONFIG_I2C_DMAPRIO) +# if (CONFIG_I2C_DMAPRIO & ~DMA_SCR_PL_MASK) != 0 +# error "Illegal value for CONFIG_I2C_DMAPRIO" +# endif +# define I2C_DMA_PRIO CONFIG_I2C_DMAPRIO +# else +# define I2C_DMA_PRIO DMA_SCR_PRIMED +# endif + +#endif + /* Debug ****************************************************************************/ /* I2C event trace logic. NOTE: trace uses the internal, non-standard, low-level @@ -253,7 +268,7 @@ struct stm32_i2c_priv_s struct i2c_msg_s *msgv; /* Message list */ uint8_t *ptr; /* Current message buffer */ uint32_t frequency; /* Current I2C frequency */ - int dcnt; /* Current message length */ + volatile int dcnt; /* Current message length */ uint16_t flags; /* Current message flags */ bool check_addr_ACK; /* Flag to signal if on next interrupt address has ACKed */ uint8_t total_msg_len; /* Flag to signal a short read sequence */ @@ -270,6 +285,15 @@ struct stm32_i2c_priv_s #endif uint32_t status; /* End of transfer SR2|SR1 status */ + + /* I2C DMA support */ + +#ifdef CONFIG_STM32_I2C_DMA + DMA_HANDLE txdma; /* TX DMA handle */ + DMA_HANDLE rxdma; /* RX DMA handle */ + uint8_t txch; /* TX DMA channel */ + uint8_t rxch; /* RX DMA channel */ +#endif }; /************************************************************************************ @@ -337,6 +361,13 @@ static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s static int stm32_i2c_reset(FAR struct i2c_master_s *dev); #endif +/* DMA support */ + +#ifdef CONFIG_STM32_I2C_DMA +static void stm32_i2c_dmarxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +static void stm32_i2c_dmatxcallback(DMA_HANDLE handle, uint8_t isr, void *arg); +#endif + /************************************************************************************ * Private Data ************************************************************************************/ @@ -398,7 +429,16 @@ static struct stm32_i2c_priv_s stm32_i2c1_priv = .ptr = NULL, .dcnt = 0, .flags = 0, - .status = 0 + .status = 0, +#ifdef CONFIG_STM32_I2C_DMA +# ifndef CONFIG_STM32_DMA1 +# error "I2C1 enabled with DMA but corresponding DMA controller 1 is not enabled!" +# endif + /* TODO: ch for i2c 1 and 2 could be *X_2 based on stream priority */ + + .rxch = DMAMAP_I2C1_RX, + .txch = DMAMAP_I2C1_TX, +#endif }; #endif @@ -428,7 +468,14 @@ static struct stm32_i2c_priv_s stm32_i2c2_priv = .ptr = NULL, .dcnt = 0, .flags = 0, - .status = 0 + .status = 0, +#ifdef CONFIG_STM32_I2C_DMA +# ifndef CONFIG_STM32_DMA1 +# error "I2C2 enabled with DMA but corresponding DMA controller 1 is not enabled!" +# endif + .rxch = DMAMAP_I2C2_RX, + .txch = DMAMAP_I2C2_TX, +#endif }; #endif @@ -458,7 +505,14 @@ static struct stm32_i2c_priv_s stm32_i2c3_priv = .ptr = NULL, .dcnt = 0, .flags = 0, - .status = 0 + .status = 0, +#ifdef CONFIG_STM32_I2C_DMA +# ifndef CONFIG_STM32_DMA1 +# error "I2C3 enabled with DMA but corresponding DMA controller 1 is not enabled!" +# endif + .rxch = DMAMAP_I2C3_RX, + .txch = DMAMAP_I2C3_TX, +#endif }; #endif @@ -521,7 +575,7 @@ static inline void stm32_i2c_sem_wait(FAR struct stm32_i2c_priv_s *priv) { while (sem_wait(&priv->sem_excl) != 0) { - ASSERT(errno == EINTR); + DEBUGASSERT(errno == EINTR); } } @@ -1185,6 +1239,12 @@ static inline void stm32_i2c_enablefsmc(uint32_t ahbenr) static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) { uint32_t status; +#ifndef CONFIG_I2C_POLLED + uint32_t regval; +#endif +#ifdef CONFIG_STM32_I2C_DMA + uint16_t cr2; +#endif i2cinfo("I2C ISR called\n"); @@ -1228,6 +1288,23 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) * stm32_i2c_sem_waitdone() waiting process. */ +#ifdef CONFIG_STM32_I2C_DMA + /* If ISR gets called (ex. polling mode) while DMA is still in + * progress, we should just return and let the DMA finish. + */ + + cr2 = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET); + if ((cr2 & I2C_CR2_DMAEN) != 0) + { +#ifdef CONFIG_DEBUG_I2C_INFO + size_t left = stm32_dmaresidual(priv->rxdma); + + i2cinfo("DMA in progress: %ld [bytes] remainining. Returning.\n", left); +#endif + return OK; + } +#endif + if (priv->dcnt == -1 && priv->msgc > 0) { i2cinfo("Switch to new message\n"); @@ -1484,6 +1561,46 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) /* Trace */ stm32_i2c_traceevent(priv, I2CEVENT_ADDRESS_ACKED, 0); + +#ifdef CONFIG_STM32_I2C_DMA + /* DMA only when not doing a short read */ + + i2cinfo("Starting dma transfer and disabling interrupts\n"); + + /* The DMA must be initialized and enabled before the I2C data transfer. + * The DMAEN bit must be set in the I2C_CR2 register before the ADDR event. + */ + + stm32_dmasetup(priv->rxdma, priv->config->base+STM32_I2C_DR_OFFSET, + (uint32_t) priv->ptr, priv->dcnt, + DMA_SCR_DIR_P2M | + DMA_SCR_MSIZE_8BITS | + DMA_SCR_PSIZE_8BITS | + DMA_SCR_MINC | + I2C_DMA_PRIO); + + /* Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is + * used. + */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_ITBUFEN, 0); + +#ifndef CONFIG_I2C_POLLED + /* Now let DMA do all the work, disable i2c interrupts */ + + regval = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET); + regval &= ~I2C_CR2_ALLINTS; + stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, regval); +#endif + + /* The user can generate a Stop condition in the DMA Transfer Complete + * interrupt routine if enabled. This will be done in the dma rx callback + * Start DMA. + */ + + stm32_dmastart(priv->rxdma, stm32_i2c_dmarxcallback, priv, false); + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_DMAEN); +#endif } } @@ -1520,19 +1637,67 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) if (priv->dcnt >= 1) { - /* Transmitting message. Send byte == write data into write register */ +#ifdef CONFIG_STM32_I2C_DMA + /* if DMA is enabled, only makes sense to make use of it for longer + than 1 B transfers.. */ - stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, *priv->ptr++); + if (priv->dcnt > 1) + { + i2cinfo("Starting dma transfer and disabling interrupts\n"); - /* Decrease current message length */ + /* The DMA must be initialized and enabled before the I2C data transfer. + * The DMAEN bit must be set in the I2C_CR2 register before the ADDR event. + */ - stm32_i2c_traceevent(priv, I2CEVENT_WRITE_TO_DR, priv->dcnt); - priv->dcnt--; + stm32_dmasetup(priv->txdma, priv->config->base+STM32_I2C_DR_OFFSET, + (uint32_t) priv->ptr, priv->dcnt, + DMA_SCR_DIR_M2P | + DMA_SCR_MSIZE_8BITS | + DMA_SCR_PSIZE_8BITS | + DMA_SCR_MINC | + I2C_DMA_PRIO ); + /* Do not enable the ITBUFEN bit in the I2C_CR2 register if DMA is + * used. + */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_ITBUFEN, 0); + +#ifndef CONFIG_I2C_POLLED + /* Now let DMA do all the work, disable i2c interrupts */ + + regval = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET); + regval &= ~I2C_CR2_ALLINTS; + stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, regval); +#endif + + /* In the interrupt routine after the EOT interrupt, disable DMA + * requests then wait for a BTF event before programming the Stop + * condition. To do this, we'll just call the ISR again in + * dma tx callback, in which point we fall into the msgc==0 case + * which ultimately sends the stop..TODO: but we don't explicitly + * wait for BTF bit being set... + * Start DMA. + */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, 0, I2C_CR2_DMAEN); + stm32_dmastart(priv->txdma, stm32_i2c_dmatxcallback, priv, false); + } + else +#endif /* CONFIG_STM32_I2C_DMA */ + { + /* Transmitting message. Send byte == write data into write register */ + + stm32_i2c_putreg(priv, STM32_I2C_DR_OFFSET, *priv->ptr++); + + /* Decrease current message length */ + + stm32_i2c_traceevent(priv, I2CEVENT_WRITE_TO_DR, priv->dcnt); + priv->dcnt--; + } } else if (priv->dcnt == 0) { - /* After last byte, check what to do based on next message flags */ if (priv->msgc == 0) @@ -1678,6 +1843,7 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) stm32_i2c_traceevent(priv, I2CEVENT_READ_2, 0); } +#ifndef CONFIG_STM32_I2C_DMA /* Case total message length >= 3 */ else if (priv->dcnt >= 4 && priv->total_msg_len >= 3) @@ -1757,6 +1923,7 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) priv->dcnt = -1; } +#endif /* CONFIG_STM32_I2C_DMA */ /* Error handling for read mode */ @@ -1765,7 +1932,8 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) i2cinfo("I2C read mode no correct state detected\n"); i2cinfo(" state %i, dcnt=%i\n", status, priv->dcnt); - /* set condition to terminate ISR and wake waiting thread */ + /* Set condition to terminate ISR and wake waiting thread */ + priv->dcnt = -1; priv->msgc = 0; stm32_i2c_traceevent(priv, I2CEVENT_READ_ERROR, 0); @@ -1804,9 +1972,9 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) else { - #ifdef CONFIG_I2C_POLLED +#ifdef CONFIG_I2C_POLLED stm32_i2c_traceevent(priv, I2CEVENT_POLL_DEV_NOT_RDY, 0); - #else +#else /* Read rest of the state */ status |= (stm32_i2c_getreg(priv, STM32_I2C_SR2_OFFSET) << 16); @@ -1814,12 +1982,12 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) i2cinfo(" No correct state detected(start bit, read or write) \n"); i2cinfo(" state %i\n", status); - /* set condition to terminate ISR and wake waiting thread */ + /* Set condition to terminate ISR and wake waiting thread */ priv->dcnt = -1; priv->msgc = 0; stm32_i2c_traceevent(priv, I2CEVENT_STATE_ERROR, 0); - #endif +#endif } /* Messages handling(2/2) @@ -1842,9 +2010,9 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) priv->msgv = NULL; - #ifdef CONFIG_I2C_POLLED +#ifdef CONFIG_I2C_POLLED priv->intstate = INTSTATE_DONE; - #else +#else /* Clear all interrupts */ uint32_t regval; @@ -1863,12 +2031,98 @@ static int stm32_i2c_isr(struct stm32_i2c_priv_s *priv) sem_post(&priv->sem_isr); priv->intstate = INTSTATE_DONE; } - #endif +#endif } return OK; } +/***************************************************************************** + * Name: stm32_i2c_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + *****************************************************************************/ + +#ifdef CONFIG_STM32_I2C_DMA +static void stm32_i2c_dmarxcallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ +#ifndef CONFIG_I2C_POLLED + uint32_t regval; +#endif + + i2cinfo("DMA rx callback, status = %d \n", status); + + FAR struct stm32_i2c_priv_s *priv = (FAR struct stm32_i2c_priv_s *)arg; + + priv->dcnt = -1; + + /* The user can generate a Stop condition in the DMA Transfer Complete + * interrupt routine if enabled. + */ + + stm32_i2c_sendstop(priv); + + /* Let the I2C periph know to stop DMA transfers, also is used by ISR to check + * if DMA is done. + */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_DMAEN, 0); + +#ifndef CONFIG_I2C_POLLED + /* Re-enable interrupts */ + + regval = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET); + regval |= (I2C_CR2_ITERREN | I2C_CR2_ITEVFEN); + stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, regval); +#endif + + /* let the ISR routine take care of shutting down or switching to next msg */ + + stm32_i2c_isr(priv); +} +#endif /* ifdef CONFIG_STM32_I2C_DMA */ + +/***************************************************************************** + * Name: stm32_i2c_dmarxcallback + * + * Description: + * Called when the RX DMA completes + * + *****************************************************************************/ + +#ifdef CONFIG_STM32_I2C_DMA +static void stm32_i2c_dmatxcallback(DMA_HANDLE handle, uint8_t status, void *arg) +{ +#ifndef CONFIG_I2C_POLLED + uint32_t regval; +#endif + + i2cinfo("DMA tx callback, status = %d \n", status); + + FAR struct stm32_i2c_priv_s *priv = (FAR struct stm32_i2c_priv_s *)arg; + + priv->dcnt = 0; + + /* In the interrupt routine after the EOT interrupt, disable DMA requests */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_DMAEN, 0); + +#ifndef CONFIG_I2C_POLLED + /* re-enable interrupts */ + + regval = stm32_i2c_getreg(priv, STM32_I2C_CR2_OFFSET); + regval |= (I2C_CR2_ITERREN | I2C_CR2_ITEVFEN); + stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, regval); +#endif + + /* let the ISR routine take care of shutting down or switching to next msg */ + + stm32_i2c_isr(priv); +} +#endif /* ifdef CONFIG_STM32_I2C_DMA */ + /************************************************************************************ * Name: stm32_i2c1_isr * @@ -1914,7 +2168,7 @@ static int stm32_i2c3_isr(int irq, void *context, FAR void *arg) return stm32_i2c_isr(&stm32_i2c3_priv); } #endif -#endif +#endif /* CONFIG_I2C_POLLED */ /************************************************************************************ * Private Initialization and Deinitialization @@ -1972,6 +2226,15 @@ static int stm32_i2c_init(FAR struct stm32_i2c_priv_s *priv) stm32_i2c_setclock(priv, 100000); +#ifdef CONFIG_STM32_I2C_DMA + /* If, in the I2C_CR2 register, the LAST bit is set, I2C automatically + * sends a NACK after the next byte following EOT_1. + * Clear DMA en just in case. + */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_DMAEN, I2C_CR2_LAST); +#endif + /* Enable I2C */ stm32_i2c_putreg(priv, STM32_I2C_CR1_OFFSET, I2C_CR1_PE); @@ -1991,6 +2254,7 @@ static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) /* Disable I2C */ stm32_i2c_putreg(priv, STM32_I2C_CR1_OFFSET, 0); + stm32_i2c_putreg(priv, STM32_I2C_CR2_OFFSET, 0); /* Unconfigure GPIO pins */ @@ -2006,6 +2270,13 @@ static int stm32_i2c_deinit(FAR struct stm32_i2c_priv_s *priv) irq_detach(priv->config->er_irq); #endif +#ifdef CONFIG_STM32_I2C_DMA + /* Disable DMA */ + + stm32_dmastop(priv->txdma); + stm32_dmastop(priv->rxdma); +#endif + /* Disable clocking */ modifyreg32(STM32_RCC_APB1ENR, priv->config->clk_bit, 0); @@ -2035,7 +2306,15 @@ static int stm32_i2c_transfer(FAR struct i2c_master_s *dev, FAR struct i2c_msg_s #endif int ret = 0; - ASSERT(count); + DEBUGASSERT(count); + +#ifdef CONFIG_STM32_I2C_DMA + /* stop DMA just in case */ + + stm32_i2c_modifyreg(priv, STM32_I2C_CR2_OFFSET, I2C_CR2_DMAEN, 0); + stm32_dmastop(priv->rxdma); + stm32_dmastop(priv->txdma); +#endif #ifdef I2C1_FSMC_CONFLICT /* Disable FSMC that shares a pin with I2C1 (LBAR) */ @@ -2246,11 +2525,11 @@ static int stm32_i2c_reset(FAR struct i2c_master_s *dev) uint32_t frequency; int ret = ERROR; - ASSERT(dev); + DEBUGASSERT(dev); /* Our caller must own a ref */ - ASSERT(priv->refs > 0); + DEBUGASSERT(priv->refs > 0); /* Lock out other clients */ @@ -2412,6 +2691,19 @@ FAR struct i2c_master_s *stm32_i2cbus_initialize(int port) { stm32_i2c_sem_init(priv); stm32_i2c_init(priv); + +#ifdef CONFIG_STM32_I2C_DMA + /* Get DMA channels. NOTE: stm32_dmachannel() will always assign the DMA channel. + * if the channel is not available, then stm32_dmachannel() will block and wait + * until the channel becomes available. WARNING: If you have another device sharing + * a DMA channel with SPI and the code never releases that channel, then the call + * to stm32_dmachannel() will hang forever in this function! Don't let your + * design do that! + */ + priv->rxdma = stm32_dmachannel(priv->rxch); + priv->txdma = stm32_dmachannel(priv->txch); + DEBUGASSERT(priv->rxdma && priv->txdma); +#endif /* #ifdef CONFIG_STM32_I2C_DMA */ } leave_critical_section(flags); @@ -2431,7 +2723,7 @@ int stm32_i2cbus_uninitialize(FAR struct i2c_master_s *dev) FAR struct stm32_i2c_priv_s *priv = (struct stm32_i2c_priv_s *)dev; irqstate_t flags; - ASSERT(dev); + DEBUGASSERT(dev); /* Decrement reference count and check for underflow */ @@ -2454,6 +2746,11 @@ int stm32_i2cbus_uninitialize(FAR struct i2c_master_s *dev) stm32_i2c_deinit(priv); +#ifdef CONFIG_STM32_I2C_DMA + stm32_dmafree(priv->rxdma); + stm32_dmafree(priv->txdma); +#endif + /* Release unused resources */ stm32_i2c_sem_destroy(priv); diff --git a/arch/arm/src/xmc4/chip/xmc4_scu.h b/arch/arm/src/xmc4/chip/xmc4_scu.h index 38d26051b7..2d22084252 100644 --- a/arch/arm/src/xmc4/chip/xmc4_scu.h +++ b/arch/arm/src/xmc4/chip/xmc4_scu.h @@ -889,7 +889,7 @@ #define SCU_PBCLKCR_PBDIV (1 << 0) /* Bit 0: PB Clock Divider Enable */ # define SCU_PBCLKCR_PBDIV_FCPU (0) /* 0=fCPU */ -# define SCU_PBCLKCR_PBDIV_DIV2 ((1 << 0) /* 1=fCPU/2 */ +# define SCU_PBCLKCR_PBDIV_DIV2 (1 << 0) /* 1=fCPU/2 */ /* USB Clock Control */ diff --git a/arch/arm/src/xmc4/chip/xmc4_usic.h b/arch/arm/src/xmc4/chip/xmc4_usic.h index 462ac44c95..274d0fae53 100644 --- a/arch/arm/src/xmc4/chip/xmc4_usic.h +++ b/arch/arm/src/xmc4/chip/xmc4_usic.h @@ -563,7 +563,7 @@ # define USIC_SCTR_TRM_ACTIVE (3 << USIC_SCTR_TRM_SHIFT) /* Active without regard to signal level */ #define USIC_SCTR_FLE_SHIFT (16) /* Bits 16-21: Frame Length */ #define USIC_SCTR_FLE_MASK (0x3f << USIC_SCTR_FLE_SHIFT) -# define USIC_SCTR_FLE(n) ((uint32_t)(n) << USIC_SCTR_FLE_SHIFT) +# define USIC_SCTR_FLE(n) ((uint32_t)((n)-1) << USIC_SCTR_FLE_SHIFT) #define USIC_SCTR_WLE_SHIFT (24) /* Bits 24-27: Word Length */ #define USIC_SCTR_WLE_MASK (15 << USIC_SCTR_WLE_SHIFT) # define USIC_SCTR_WLE(n) ((uint32_t)((n)-1) << USIC_SCTR_WLE_SHIFT) diff --git a/arch/arm/src/xmc4/xmc4_clockconfig.c b/arch/arm/src/xmc4/xmc4_clockconfig.c index a28cf7fd51..519276f2cd 100644 --- a/arch/arm/src/xmc4/xmc4_clockconfig.c +++ b/arch/arm/src/xmc4/xmc4_clockconfig.c @@ -105,13 +105,18 @@ #define CLKSET_VALUE (0x00000000) #define SYSCLKCR_VALUE (0x00010001) #define CPUCLKCR_VALUE (0x00000000) -#define PBCLKCR_VALUE (0x00000000) #define CCUCLKCR_VALUE (0x00000000) #define WDTCLKCR_VALUE (0x00000000) #define EBUCLKCR_VALUE (0x00000003) #define USBCLKCR_VALUE (0x00010000) #define EXTCLKCR_VALUE (0x01200003) +#if BOARD_PBDIV == 1 +# define PBCLKCR_VALUE SCU_PBCLKCR_PBDIV_FCPU +#else /* BOARD_PBDIV == 2 */ +# define PBCLKCR_VALUE SCU_PBCLKCR_PBDIV_DIV2 +#endif + #if ((USBCLKCR_VALUE & SCU_USBCLKCR_USBSEL) == SCU_USBCLKCR_USBSEL_USBPLL) # define USB_DIV 3 #else @@ -387,7 +392,7 @@ void xmc4_clock_configure(void) /* Before scaling to final frequency we need to setup the clock dividers */ putreg32(SYSCLKCR_VALUE, XMC4_SCU_SYSCLKCR); - putreg32(PBCLKCR_VALUE, XMC4_SCU_PBCLKCR); + putreg32(PBCLKCR_VALUE, XMC4_SCU_PBCLKCR); putreg32(CPUCLKCR_VALUE, XMC4_SCU_CPUCLKCR); putreg32(CCUCLKCR_VALUE, XMC4_SCU_CCUCLKCR); putreg32(WDTCLKCR_VALUE, XMC4_SCU_WDTCLKCR); diff --git a/arch/arm/src/xmc4/xmc4_usic.c b/arch/arm/src/xmc4/xmc4_usic.c index e22434a637..925c9c0f41 100644 --- a/arch/arm/src/xmc4/xmc4_usic.c +++ b/arch/arm/src/xmc4/xmc4_usic.c @@ -448,14 +448,14 @@ int xmc4_usic_baudrate(enum usic_channel_e channel, uint32_t baud, /* Select and setup the fractional divider */ - regval = USIC_FDR_DM_FRACTIONAL | (clkdiv_min << USIC_FDR_STEP_SHIFT); + regval = USIC_FDR_DM_FRACTIONAL | USIC_FDR_STEP(clkdiv_min); putreg32(regval, base + XMC4_USIC_FDR_OFFSET); /* Setup and enable the baud rate generator */ regval = getreg32(base + XMC4_USIC_BRG_OFFSET); regval &= ~(USIC_BRG_DCTQ_MASK | USIC_BRG_PDIV_MASK | USIC_BRG_PCTQ_MASK | USIC_BRG_PPPEN); - regval |= (USIC_BRG_DCTQ(oversampling - 1) | USIC_BRG_PDIV(pdiv_int_min - 1)); + regval |= (USIC_BRG_DCTQ(oversampling - 1) | USIC_BRG_PDIV(pdiv_int_min - 1)); putreg32(regval, base + XMC4_USIC_BRG_OFFSET); ret = OK; diff --git a/configs/xmc4500-relax/README.txt b/configs/xmc4500-relax/README.txt index c6d014b666..9fb7e29c2e 100644 --- a/configs/xmc4500-relax/README.txt +++ b/configs/xmc4500-relax/README.txt @@ -7,6 +7,13 @@ README for the XMC4500 Relax The current configurations support only the Lite version of the board. +Status +====== + + 2017-03-21: The XMC4500 Relax boots into NSH, provides the NSH prompt, + and the LEDs are working. But there is a problem with sserial input. + The most likely reason for this is there are no serial RX interripts. + Serial Console ============== @@ -20,6 +27,9 @@ Serial Console VDD5 - Available on pins 39-40 of either connector X1 or X2 A TTL to RS-232 convertor or a USB TTL-to-USB serial adaptor is required. + The notion of what is TX and what is RX depends on your point of view. + With the TTL to RS-232 converter, I connect pin 17 to the pin labeled + TX on the converter and pin 16 to the RX pin on the converter. LEDs ==== diff --git a/configs/xmc4500-relax/include/board.h b/configs/xmc4500-relax/include/board.h index 9c3121a4b6..751e803170 100644 --- a/configs/xmc4500-relax/include/board.h +++ b/configs/xmc4500-relax/include/board.h @@ -53,16 +53,21 @@ /* Clocking *************************************************************************/ /* Default clock initialization - * fPLL = 288MHz => fSYS = 288MHz => fCPU = 144MHz - * => fPB = 144MHz - * => fCCU = 144MHz - * => fETH = 72MHz - * => fUSB = 48MHz - * => fEBU = 72MHz + * + * fXTAL = 12Mhz + * -> fPLL = (fXTAL / (2 * 1) * 48) = 288MHz + * -> fSYS = (fPLL / 1) = 288MHz + * -> fCPU = (fSYS / 2) = 144MHz + * -> fPERIPH = (fCPU / 1) = 144MHz + * -> fCCU = (fSYS / 2) = 144MHz + * -> fETH = 72MHz (REVISIT) + * -> fUSB = 48MHz (REVISIT) + * -> fEBU = 72MHz (REVISIT) * * fUSBPLL Disabled, only enabled if SCU_CLK_USBCLKCR_USBSEL_USBPLL is selected * - * fOFI = 24MHz => fWDT = 24MHz + * fOFI = 24MHz + * -> fWDT = 24MHz (REVISIT) */ #undef BOARD_FOFI_CALIBRATION /* Enable factory calibration */ @@ -79,7 +84,7 @@ /* Select the external crystal as the PLL clock source */ #define BOARD_PLL_CLOCKSRC_XTAL 1 /* PLL Clock source == extnernal crystal */ -#undef BOARD_PLL_CLOCKSRC_OFI /* PLL Clock source != internal fast oscillator */ +#undef BOARD_PLL_CLOCKSRC_OFI /* PLL Clock source != internal fast oscillator */ /* PLL Configuration: * @@ -95,16 +100,21 @@ #define BOARD_PLL_K2DIV 1 #define BOARD_PLL_FREQUENCY 288000000 -/* System frequency is divided down from PLL output */ +/* System frequency, fSYS, is divided down from PLL output */ #define BOARD_SYSDIV 1 /* PLL Output divider to get fSYS */ #define BOARD_SYS_FREQUENCY 288000000 -/* CPU frequency may be divided down from system frequency */ +/* CPU frequency, fCPU, may be divided down from system frequency */ #define BOARD_CPUDIV_ENABLE 1 /* Enable PLL dive by 2 for fCPU */ #define BOARD_CPU_FREQUENCY 144000000 +/* The peripheral clock, fPERIPH, derives from fCPU with no division */ + +#define BOARD_PBDIV 1 /* No division */ +#define BOARD_PERIPH_FREQUENCY 144000000 + /* Standby clock source selection * * BOARD_STDBY_CLOCKSRC_OSI - Internal 32.768KHz slow oscillator @@ -112,7 +122,7 @@ */ #define BOARD_STDBY_CLOCKSRC_OSI 1 -#undef BOARD_STDBY_CLOCKSRC_OSCULP +#undef BOARD_STDBY_CLOCKSRC_OSCULP #define BOARD_STDBY_FREQUENCY 32768 /* USB PLL settings. diff --git a/drivers/lcd/st7565.c b/drivers/lcd/st7565.c index 1153c1568b..d25583a745 100644 --- a/drivers/lcd/st7565.c +++ b/drivers/lcd/st7565.c @@ -1041,7 +1041,7 @@ FAR struct lcd_dev_s *st7565_initialize(FAR struct st7565_lcd_s *lcd, up_mdelay(2); (void)st7565_send_one_data(priv, ST7565_POWERCTRL_BR); up_mdelay(2); - (void)st7565_send_one_data(priv, ST7565_POWERCTRL_BRF); + (void)st7565_send_one_data(priv, ST7565_POWERCTRL_INT); (void)st7565_send_one_data(priv, ST7565_REG_RES_4_5); (void)st7565_send_one_data(priv, ST7565_SETEVMODE); diff --git a/drivers/lcd/st7565.h b/drivers/lcd/st7565.h index e4788379b9..cc9d17dd0f 100644 --- a/drivers/lcd/st7565.h +++ b/drivers/lcd/st7565.h @@ -102,8 +102,6 @@ */ #define ST7565_POWERCTRL_B 0x2c /* 0x2c: Booster=ON */ #define ST7565_POWERCTRL_BR 0x2e /* 0x2e: Booster=ON V/R=ON */ -#define ST7565_POWERCTRL_BRF 0x2f /* 0x23: Booster=ON V/R=ON V/F=ON */ - #define ST7565_POWERCTRL_INT 0x2f /* 0x2f: Only the internal power supply is used */ /* Regulation Resistior ratio V0 = (1+Rb/Ra)*Vev */ diff --git a/sched/Kconfig b/sched/Kconfig index 3dc0e91eba..3a5807d4dc 100644 --- a/sched/Kconfig +++ b/sched/Kconfig @@ -263,11 +263,17 @@ if SMP config SMP_NCPUS int "Number of CPUs" default 4 - range 2 32 + range 1 32 if DEBUG_FEATURES + range 2 32 if !DEBUG_FEATURES ---help--- This value identifies the number of CPUs supported by the processor that will be used for SMP. + If CONFIG_DEBUG_FEATURES is enbled, then the value one is permitted + for CONFIG_SMP_NCPUS. This is not normally a valid setting for an + SMP configuration. However, running the SMP logic in a single CPU + configuration is useful during certain testing. + config SMP_IDLETHREAD_STACKSIZE int "CPU IDLE stack size" default 2048 diff --git a/sched/pthread/pthread_mutexlock.c b/sched/pthread/pthread_mutexlock.c index f94890505d..d5c8af2db2 100644 --- a/sched/pthread/pthread_mutexlock.c +++ b/sched/pthread/pthread_mutexlock.c @@ -136,8 +136,13 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) else #endif { - /* No, then we would deadlock... return an error (default behavior - * is like PTHREAD_MUTEX_ERRORCHECK) + /* No, then we would deadlock... return an error (default + * behavior is like PTHREAD_MUTEX_ERRORCHECK) + * + * NOTE: This is non-compliant behavior for the case of a + * NORMAL mutex. In that case, it the deadlock condition should + * not be detected and the thread should be permitted to + * deadlock. */ serr("ERROR: Returning EDEADLK\n"); @@ -169,4 +174,3 @@ int pthread_mutex_lock(FAR pthread_mutex_t *mutex) sinfo("Returning %d\n", ret); return ret; } - diff --git a/sched/pthread/pthread_mutexunlock.c b/sched/pthread/pthread_mutexunlock.c index 078d9094b9..56c14bb036 100644 --- a/sched/pthread/pthread_mutexunlock.c +++ b/sched/pthread/pthread_mutexunlock.c @@ -100,13 +100,22 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) if (mutex->pid != (int)getpid()) { - /* No... return an error (default behavior is like PTHREAD_MUTEX_ERRORCHECK) */ + /* No... return an EPERM error. + * + * Per POSIX: "EPERM should be returned if the mutex type is + * PTHREAD_MUTEX_ERRORCHECK or PTHREAD_MUTEX_RECURSIVE, or the + * mutex is a robust mutex, and the current thread does not own + * the mutex." + * + * For the case of the non-robust PTHREAD_MUTEX_NORMAL mutex, + * the behavior is undefined. Here we treat that type as though + * it were PTHREAD_MUTEX_ERRORCHECK type and just return an error. + */ serr("ERROR: Holder=%d returning EPERM\n", mutex->pid); ret = EPERM; } - /* Yes, the caller owns the semaphore.. Is this a recursive mutex? */ #ifdef CONFIG_MUTEX_TYPES @@ -116,6 +125,7 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) * the mutex lock, just decrement the count of locks held, and return * success. */ + mutex->nlocks--; } #endif @@ -134,6 +144,7 @@ int pthread_mutex_unlock(FAR pthread_mutex_t *mutex) #endif ret = pthread_givesemaphore((FAR sem_t *)&mutex->sem); } + sched_unlock(); } diff --git a/sched/semaphore/sem_holder.c b/sched/semaphore/sem_holder.c index bc5c186e6b..a46cf3a8be 100644 --- a/sched/semaphore/sem_holder.c +++ b/sched/semaphore/sem_holder.c @@ -156,7 +156,7 @@ static FAR struct semholder_s *sem_findholder(sem_t *sem, int i; pholder = NULL; - /* We have two hard-allocated holder structuse in sem_t */ + /* We have two hard-allocated holder structures in sem_t */ for (i = 0; i < 2; i++) { @@ -338,7 +338,7 @@ static int sem_boostholderprio(FAR struct semholder_s *pholder, if (!sched_verifytcb(htcb)) { serr("ERROR: TCB 0x%08x is a stale handle, counts lost\n", htcb); - DEBUGASSERT(!sched_verifytcb(htcb)); + DEBUGPANIC(); sem_freeholder(sem, pholder); } @@ -498,7 +498,7 @@ static int sem_restoreholderprio(FAR struct tcb_s *htcb, if (!sched_verifytcb(htcb)) { serr("ERROR: TCB 0x%08x is a stale handle, counts lost\n", htcb); - DEBUGASSERT(!sched_verifytcb(htcb)); + DEBUGPANIC(); pholder = sem_findholder(sem, htcb); if (pholder != NULL) { @@ -787,7 +787,6 @@ static inline void sem_restorebaseprio_task(FAR struct tcb_s *stcb, FAR sem_t *sem) { FAR struct tcb_s *rtcb = this_task(); - FAR struct semholder_s *pholder; /* Perform the following actions only if a new thread was given a count. * The thread that received the count should be the highest priority @@ -831,7 +830,6 @@ static inline void sem_restorebaseprio_task(FAR struct tcb_s *stcb, */ sem_findandfreeholder(sem, rtcb); - } /**************************************************************************** @@ -905,14 +903,15 @@ void sem_destroyholder(FAR sem_t *sem) if (sem->hhead != NULL) { serr("ERROR: Semaphore destroyed with holders\n"); - DEBUGASSERT(sem->hhead != NULL); + DEBUGPANIC(); (void)sem_foreachholder(sem, sem_recoverholders, NULL); } + #else - if (sem->holder[0].htcb != NULL || sem->holder[0].htcb != NULL) + if (sem->holder[0].htcb != NULL || sem->holder[1].htcb != NULL) { - DEBUGASSERT(sem->holder[0].htcb != NULL || sem->holder[0].htcb != NULL); serr("ERROR: Semaphore destroyed with holder\n"); + DEBUGPANIC(); } sem->holder[0].htcb = NULL;