diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c index e5f88e87a5..ec106e4d7e 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c @@ -626,6 +626,24 @@ static rt_err_t n32_i2c_master_seq_receive_it(struct n32_i2c *i2c, uint16_t DevA if (i2c->transfer.state == I2C_READY) { + /* A master receive fits in a single BYTENUM window: the counter is + * loaded once below and this path never reloads it, so the BSF + * completion ends the transfer after that many bytes. A longer message + * would be silently truncated yet still reported as success; refuse it + * instead. BYTENUM is 8 bits (the SDK's own setter takes a uint8_t), so + * the count cannot be widened either. Only the DMA path reloads the + * window as it drains. + * + * n32_i2c_master_xfer screens the same condition before dispatching, so + * the caller gets an errno; this check keeps the invariant stated where + * the window is actually programmed. + */ + if (Size > MAX_NBYTE_SIZE) + { + LOG_E("I2C IT receive of %u bytes exceeds the %u-byte BYTENUM window, enable RX DMA for longer reads", (unsigned int)Size, (unsigned int)MAX_NBYTE_SIZE); + return -RT_EINVAL; + } + /* Set transfer parameters */ i2c->transfer.state = I2C_BUSY_RX; i2c->transfer.pBuffPtr = pData; @@ -633,16 +651,8 @@ static rt_err_t n32_i2c_master_seq_receive_it(struct n32_i2c *i2c, uint16_t DevA i2c->transfer.XferOptions = XferOptions; i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; - /* If Size > MAX_NBYTE_SIZE, use reload mode */ - if (Size > MAX_NBYTE_SIZE) - { - i2c->transfer.XferSize = MAX_NBYTE_SIZE; - } - else - { - i2c->transfer.XferSize = i2c->transfer.XferCount; - } - + /* One window covers the whole transfer - see the size check above */ + i2c->transfer.XferSize = i2c->transfer.XferCount; #if defined(SOC_SERIES_N32H49x) I2C_EnableByteNum(i2c->config->Instance, ENABLE); @@ -769,15 +779,14 @@ static rt_err_t n32_i2c_master_seq_send_it(struct n32_i2c *i2c, uint16_t DevAddr i2c->transfer.XferOptions = XferOptions; i2c->i2c_isr_callback = i2c_master_ev_isr_handler_it; - /* If Size > MAX_NBYTE_SIZE, use reload mode */ - if (Size > MAX_NBYTE_SIZE) - { - i2c->transfer.XferSize = MAX_NBYTE_SIZE; - } - else - { - i2c->transfer.XferSize = i2c->transfer.XferCount; - } + /* No 255-byte window here: BYTENUM counts received bytes only, and this + * ISR feeds DAT one byte per TXDATE interrupt, so XferSize is nothing + * more than the remaining byte count. Capping it at MAX_NBYTE_SIZE made + * the TXDATE branch stop feeding after the 255th byte of a longer + * message - the tail was never sent while TXDATE stayed asserted and + * stormed this handler until the caller timed out. + */ + i2c->transfer.XferSize = i2c->transfer.XferCount; /* Wait for the previous STOP to complete. Toggling PE here would not * reset the state machine while BUSY is set, and could hold the lines. @@ -904,7 +913,14 @@ static rt_err_t n32_i2c_master_seq_receive_dma(struct n32_i2c *i2c, uint16_t Dev rt_err_t start_ret = n32_i2c_master_start_addr(i2c, (uint8_t)DevAddress, I2C_DIRECTION_RECV); if (start_ret != RT_EOK) { - i2c->transfer.state = I2C_READY; + /* The DMA request and the BUF/ERR interrupts are armed by + * now and the DMA channel still targets the caller's + * buffer, so resetting only the state would let a late + * byte write through a stack frame that is about to + * unwind. The timeout paths of n32_i2c_master_start_addr + * raise no error interrupt that could tear this down, so + * do it here. */ + I2C_ABORT_ON_TIMEOUT(i2c); return start_ret; } } @@ -1010,7 +1026,14 @@ static rt_err_t n32_i2c_master_seq_send_dma(struct n32_i2c *i2c, uint16_t DevAdd rt_err_t start_ret = n32_i2c_master_start_addr(i2c, (uint8_t)DevAddress, I2C_DIRECTION_SEND); if (start_ret != RT_EOK) { - i2c->transfer.state = I2C_READY; + /* The DMA request and the BUF/ERR interrupts are armed by + * now and the DMA channel still targets the caller's + * buffer, so resetting only the state would let a late + * byte write through a stack frame that is about to + * unwind. The timeout paths of n32_i2c_master_start_addr + * raise no error interrupt that could tear this down, so + * do it here. */ + I2C_ABORT_ON_TIMEOUT(i2c); return start_ret; } } @@ -1075,6 +1098,51 @@ static rt_ssize_t n32_i2c_master_xfer(struct rt_i2c_bus_device *bus, i2c_obj = rt_container_of(bus, struct n32_i2c, i2c_bus); completion = &i2c_obj->completion; +#if defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) + /* This series cannot chain frames, so neither flag can be honoured. The + * mode constants the translate step below derives from them are all + * 0x00000000U on this series (see the definitions at the top of this file), + * so XferOptions is always zero here and changes nothing in the CTRL2 + * write. Every message instead re-runs the START + 7-bit address sequence + * in its own per-message setup, and completion always ends in a STOP + * (STOPGEN in i2c_it_completion_done, or the BYTENUM auto-stop on the DMA + * receive). A caller asking for RT_I2C_NO_START or RT_I2C_NO_STOP would + * therefore silently get a fresh START and a STOP instead of a repeated + * start -- a different bus transaction than the one requested, which some + * slaves reject. Refuse it rather than change it behind the caller's back; + * a repeated-start sequence needs the frame chaining this controller + * lacks. + */ + for (i = 0; i < num; i++) + { + if (msgs[i].flags & (RT_I2C_NO_START | RT_I2C_NO_STOP)) + { + LOG_E("I2C: RT_I2C_NO_START/RT_I2C_NO_STOP are not supported on this series (no frame chaining), msg[%d] flags=0x%x", i, msgs[i].flags); + return -RT_ENOSYS; + } + + /* A receive that the interrupt path has to serve fits in one BYTENUM + * window, and that path never reloads the counter. Screen it here as + * well as in the receive setup below, because a rejection raised from + * inside the transfer is reported as a message count rather than an + * errno (see "out:"), so the caller would only see a silent short + * read. The test mirrors this function's own DMA dispatch: the + * interrupt path is taken when RX DMA is off or the message is too + * short for it. + */ + if ((msgs[i].flags & RT_I2C_RD) && (msgs[i].len > MAX_NBYTE_SIZE)) + { + rt_bool_t rx_dma_ready = (i2c_obj->i2c_dma_flag & I2C_USING_RX_DMA_FLAG) ? RT_TRUE : RT_FALSE; + + if ((rx_dma_ready != RT_TRUE) || (msgs[i].len < DMA_TRANS_MIN_LEN)) + { + LOG_E("I2C IT receive of %u bytes exceeds the %u-byte BYTENUM window, enable RX DMA for longer reads", (unsigned int)msgs[i].len, (unsigned int)MAX_NBYTE_SIZE); + return -RT_EINVAL; + } + } + } +#endif + LOG_D("xfer start %d mags", num); for (i = 0; i < (num - 1); i++) { @@ -1890,12 +1958,23 @@ static void i2c_master_ev_isr_handler_it(struct n32_i2c *drv_i2c) } } - /* Byte Sequence Finished - independent check, handles completion when XferCount == 0 */ + /* Byte Sequence Finished - only reachable with XferCount == 0, so completing + * unconditionally is correct: the RX setup refuses sizes beyond the single + * BYTENUM window it programs, and a master transmit is not windowed at all - + * its last byte is fed to DAT exactly when XferCount reaches zero. A nonzero + * XferCount here means the two setups above drifted out of sync, which is + * worth a trace rather than silently truncating the message. + */ if ((itflags & I2C_STS1_BSF) && (itsources & I2C_CTRL2_BUFINTEN)) { /* Clear BSF flag */ I2C_ClrIntPendingBit(drv_i2c->config->Instance, I2C_INT_BSF); + if (drv_i2c->transfer.XferCount != 0U) + { + LOG_W("I2C BSF with %u bytes still queued", (unsigned int)drv_i2c->transfer.XferCount); + } + /* Transfer complete - BYTENUM expired (RX) or all bytes sent (TX) */ i2c_it_completion_done(drv_i2c); } diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c index 9b2c781a11..2f7d0cfbb5 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c @@ -662,6 +662,20 @@ static rt_err_t _write_page(struct rt_mtd_nand_device *device, uint32_t i; rt_err_t ret; +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + /* 16-bit NAND has no 8-bit store (see the data phase below), so an odd + * data_len has no halfword-only encoding: its trailing byte could only + * leave as an 8-bit write, and padding would program a byte the caller + * never asked for. Refuse it before the command phase, so the chip is + * never left in the middle of a program sequence. + */ + if (data_len & 1U) + { + LOG_E("page %d: 16-bit NAND needs an even data length, got %u", (int)page, (unsigned int)data_len); + return -RT_EINVAL; + } +#endif + /* Command phase: WRITE_1ST + column(2x 0) + row(NAND_ROW_ADDR_CYCLES) */ *(__IO uint8_t *)(bank | NAND_CMD_AREA) = NAND_CMD_WRITE_1ST; *(__IO uint8_t *)(bank | NAND_ADDR_AREA) = 0x00; @@ -686,7 +700,8 @@ static rt_err_t _write_page(struct rt_mtd_nand_device *device, * 16-bit NAND: 8-bit AHB writes are NOT supported (see FEMC manual * "supported memories and operations"), so data must be written as * 16-bit accesses. If the source buffer is not 16-bit aligned, copy - * it into an aligned temporary buffer first. + * it into an aligned temporary buffer first. An odd data_len was + * refused at the top of this function. */ if (((uint32_t)data & 0x1U) == 0U) { @@ -716,11 +731,6 @@ static rt_err_t _write_page(struct rt_mtd_nand_device *device, } rt_free(tmp); } - /* odd trailing byte */ - if (data_len & 1) - { - *(__IO uint8_t *)(bank | NAND_DATA_AREA) = data[data_len - 1]; - } #else for (i = 0; i < data_len; i++) { @@ -730,7 +740,14 @@ static rt_err_t _write_page(struct rt_mtd_nand_device *device, } else { + /* No payload: one store still has to be issued so the data phase clocks + * out. 16-bit NAND rejects 8-bit AHB writes (see the note above), so the + * dummy element must be halfword-sized on that bus. */ +#ifdef BSP_USING_NAND_BUS_WIDTH_16B + *(__IO uint16_t *)(bank | NAND_DATA_AREA) = 0x0000; +#else *(__IO uint8_t *)(bank | NAND_DATA_AREA) = 0x00; +#endif } /* spare area write not implemented yet */ diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c index f8b5d12019..7b2b6e03db 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c @@ -45,18 +45,6 @@ static rt_err_t xspi_wait_flag(XSPI_Module *xspi, uint32_t flag, uint32_t timeou return RT_EOK; } -static void xspi_wait_tx_complete(XSPI_Module *xspi) -{ - while (XSPI_GetFlagStatus(xspi, XSPI_TXFE_FLAG) != SET) - { - } - - while (XSPI_GetFlagStatus(xspi, XSPI_BUSY_FLAG) != RESET) - { - } -} - - static rt_err_t xspi_wait_busy(XSPI_Module *xspi, uint32_t timeout) { uint32_t tickstart = rt_tick_get(); @@ -73,6 +61,24 @@ static rt_err_t xspi_wait_busy(XSPI_Module *xspi, uint32_t timeout) return RT_EOK; } +/* TX FIFO drained + transfer finished. Both waits carry the caller's timeout: + * an unresponsive bus (TXFE that never sets after a failed arm, or a slave + * that stretches the clock and never releases BUSY) must not spin here + * forever, because that would silently defeat the timeout its caller was + * given. */ +static rt_err_t xspi_wait_tx_complete(XSPI_Module *xspi, uint32_t timeout) +{ + rt_err_t result; + + result = xspi_wait_flag(xspi, XSPI_TXFE_FLAG, timeout); + if (result != RT_EOK) + { + return result; + } + + return xspi_wait_busy(xspi, timeout); +} + /* ---- multi-line helpers ---- */ static uint32_t xspi_get_transfer_type(uint8_t instr_lines, uint8_t addr_lines) @@ -215,12 +221,15 @@ static rt_err_t xspi_qspi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mes XSPI_SendData(xspi, qspi_msg->instruction.content); XSPI_SendData(xspi, qspi_msg->address.content); - while (i < len) + for (i = 0; i < len; i++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) == SET) + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { - XSPI_SendData(xspi, buf[i++]); + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, buf[i]); } ret = xspi_wait_busy(xspi, timeout); @@ -270,8 +279,13 @@ static rt_err_t xspi_qspi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_mess } - while (XSPI_GetFlagStatus(xspi, XSPI_BUSY_FLAG) != RESET) + /* Bound the post-transfer wait like the receive loop above: a slave that + * stretches the clock and never releases BUSY must not spin here forever, + * defeating the timeout the caller was given. */ + if (xspi_wait_busy(xspi, timeout) != RT_EOK) { + ret = -RT_ETIMEOUT; + goto exit; } XSPI_ClearRxFIFO(xspi); @@ -305,7 +319,10 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess XSPI_SetNumberOfDataFrame(xspi, qspi_msg->parent.length); - xspi_wait_tx_complete(xspi); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + return -RT_ETIMEOUT; + } XSPI_Enable(xspi, ENABLE); @@ -324,17 +341,24 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess if (qspi_msg->parent.length <= 16) { - while (i < qspi_msg->parent.length) + for (i = 0; i < qspi_msg->parent.length; i++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) != RESET) - { - uint8_t tx = send_buf ? send_buf[i++] : 0xFF; + uint8_t tx = send_buf ? send_buf[i] : 0xFF; - XSPI_SendData(xspi, tx); + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, tx); } - xspi_wait_tx_complete(xspi); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } if (recv_buf) { @@ -359,6 +383,7 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { + XSPI_Enable(xspi, DISABLE); return -RT_ETIMEOUT; } @@ -368,6 +393,7 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess { if (xspi_wait_flag(xspi, XSPI_RXFNE_FLAG, timeout) != RT_EOK) { + XSPI_Enable(xspi, DISABLE); return -RT_ETIMEOUT; } @@ -408,7 +434,11 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message XSPI_Enable(xspi, ENABLE); - xspi_wait_tx_complete(xspi); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } XSPI_ClearRxFIFO(xspi); @@ -432,15 +462,22 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message XSPI_SendData(xspi, qspi_msg->address.content & 0xff); - while (number < qspi_msg->parent.length) + for (number = 0; number < qspi_msg->parent.length; number++) { - if (XSPI_GetFlagStatus(xspi, XSPI_TXFNF_FLAG) != RESET) + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) { - XSPI_SendData(xspi, buf[number++]); + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(xspi, buf[number]); } - xspi_wait_tx_complete(xspi); + if (xspi_wait_tx_complete(xspi, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } XSPI_Enable(xspi, DISABLE); @@ -494,7 +531,12 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa while (i < qspi_msg->parent.length + 4) /*Tx Fifo not full*/ { - while ((xspi->STS & XSPI_TXFNF_FLAG) != XSPI_TXFNF_FLAG); /*wait tx FIFO not full flag set*/ + /* Wait for TX FIFO not full, bounded: the write below assumes room. */ + if (xspi_wait_flag(xspi, XSPI_TXFNF_FLAG, timeout) != RT_EOK) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } if (i < 4) { @@ -522,6 +564,12 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa i++; } + /* Drain the RX FIFO until the transfer finishes. The reads must stay on + * every iteration -- the waits in this file stop as soon as a flag + * clears, which here would strand the last bytes in the FIFO -- so only + * the exit condition is bounded. */ + uint32_t tickstart = rt_tick_get(); + do { if ((xspi->STS & XSPI_RXFNE_FLAG)) /*Rx Fifo not empty set*/ @@ -529,6 +577,11 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa *(buf++) = xspi->DAT0; /*read data register*/ } + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Enable(xspi, DISABLE); + return -RT_ETIMEOUT; + } } while ((xspi->STS & XSPI_BUSY_FLAG) == SET); XSPI_Enable(xspi, DISABLE); @@ -636,17 +689,6 @@ static rt_err_t xspi_wait_flag(uint32_t flag, uint32_t timeout) return RT_EOK; } -static void xspi_wait_tx_complete(void) -{ - while (XSPI_GetFlagStatus(XSPI_STS_TXFE) != SET) /* TX FIFO empty */ - { - } - - while (XSPI_GetFlagStatus(XSPI_STS_BUSY) != RESET) /* transfer done */ - { - } -} - static rt_err_t xspi_wait_busy(uint32_t timeout) { uint32_t tickstart = rt_tick_get(); @@ -662,6 +704,24 @@ static rt_err_t xspi_wait_busy(uint32_t timeout) return RT_EOK; } +/* TX FIFO drained + transfer finished. Both waits carry the caller's timeout: + * an unresponsive bus (TXFE that never sets after a failed arm, or a slave + * that stretches the clock and never releases BUSY) must not spin here + * forever, because that would silently defeat the timeout its caller was + * given. */ +static rt_err_t xspi_wait_tx_complete(uint32_t timeout) +{ + rt_err_t result; + + result = xspi_wait_flag(XSPI_STS_TXFE, timeout); + if (result != RT_EOK) + { + return result; + } + + return xspi_wait_busy(timeout); +} + /* data line width -> SPIFRF frame format */ static uint32_t xspi_get_spifrf(uint8_t lines) { @@ -733,6 +793,11 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess } XSPI_Cmd(ENABLE); + /* The sends below stay non-blocking on purpose: a blocking wait would stop + * draining the RX FIFO, which the comment inside warns would overflow on + * large full-duplex transfers. Only the loop's exit is bounded. */ + uint32_t tickstart = rt_tick_get(); + while (i < len) { if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) @@ -753,9 +818,19 @@ static rt_err_t xspi_spi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mess (void)XSPI_ReceiveData(); } } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } } - xspi_wait_tx_complete(); + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } if (recv_buf) { @@ -798,6 +873,10 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message s_data[2] = (qspi_msg->address.content & 0xff00) >> 8; s_data[3] = qspi_msg->address.content & 0xff; + /* Non-blocking sends + lockstep RX discard; only the exit is bounded (a + * blocking wait would stall the discard and let the RX FIFO overflow). */ + uint32_t tickstart = rt_tick_get(); + while (i < len + 4) { if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) @@ -810,6 +889,12 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message { (void)XSPI_ReceiveData(); /* discard received bytes */ } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } } /* drain the remaining received bytes */ @@ -818,7 +903,11 @@ static rt_err_t xspi_spi_send(struct n32_xspi *xspi_drv, struct rt_qspi_message (void)XSPI_ReceiveData(); } - xspi_wait_tx_complete(); + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } XSPI_Cmd(DISABLE); return RT_EOK; } @@ -852,6 +941,9 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa s_data[2] = (qspi_msg->address.content & 0xff00) >> 8; s_data[3] = qspi_msg->address.content & 0xff; + /* Non-blocking sends + lockstep RX collect; only the exits are bounded. */ + uint32_t tickstart = rt_tick_get(); + while (i < len + 4) { if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) @@ -869,6 +961,12 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa } j++; } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } } /* drain the remaining received bytes (len + 4 in total) */ @@ -883,6 +981,12 @@ static rt_err_t xspi_spi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_messa } j++; } + + if (((rt_tick_get() - tickstart) >= timeout) && (timeout != 0xFFFFFFFFU)) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } } XSPI_Cmd(DISABLE); @@ -931,15 +1035,22 @@ static rt_err_t xspi_qspi_transmit(struct n32_xspi *xspi_drv, struct rt_qspi_mes XSPI_SendData(qspi_msg->instruction.content); XSPI_SendData(qspi_msg->address.content); - while (i < len) + for (i = 0; i < len; i++) { - if (XSPI_GetFlagStatus(XSPI_STS_TXFNF) == SET) + if (xspi_wait_flag(XSPI_STS_TXFNF, timeout) != RT_EOK) { - XSPI_SendData(buf[i++]); + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; } + + XSPI_SendData(buf[i]); } - xspi_wait_tx_complete(); + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } XSPI_Cmd(DISABLE); return RT_EOK; } @@ -969,7 +1080,11 @@ static rt_err_t xspi_qspi_receive(struct n32_xspi *xspi_drv, struct rt_qspi_mess buf[i] = (uint8_t)XSPI_ReceiveData(); } - xspi_wait_tx_complete(); + if (xspi_wait_tx_complete(timeout) != RT_EOK) + { + XSPI_Cmd(DISABLE); + return -RT_ETIMEOUT; + } XSPI_Cmd(DISABLE); return RT_EOK; } diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c index 734d45fbc3..893436a328 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c @@ -104,9 +104,10 @@ N32_SPI_DUMMY_ALIGN32 static rt_uint8_t spi_fd_rxonly_dummy[8192]; #if defined(SOC_SERIES_N32H7xx) -/* Build an LLI chain for a 'size'-byte transfer: the memory side advances - * 4095B per node while the peripheral (DAT) address stays fixed. Only the - * last node has IntEn=1, so the whole chain raises a single TC interrupt. */ +/* Build an LLI chain for a 'size'-element transfer: the memory side advances + * SPI_DMA_BLOCK_MAX elements per node while the peripheral (DAT) address + * stays fixed. Only the last node has IntEn=1, so the whole chain raises a + * single TC interrupt. */ static rt_uint16_t n32_spi_lli_build(DMA_LinkListItemType *lli, const DMA_ChInitType *ch, rt_uint32_t periph_addr, rt_uint32_t mem_addr, rt_bool_t mem_is_src, rt_uint16_t size) @@ -121,8 +122,17 @@ static rt_uint16_t n32_spi_lli_build(DMA_LinkListItemType *lli, const DMA_ChInit rt_uint16_t blk = (rest > SPI_DMA_BLOCK_MAX) ? SPI_DMA_BLOCK_MAX : rest; rt_bool_t has_next = (i + 1U < n) ? RT_TRUE : RT_FALSE; - node->SrcAddr = mem_is_src ? (mem_addr + (rt_uint32_t)i * SPI_DMA_BLOCK_MAX) : periph_addr; - node->DstAddr = mem_is_src ? periph_addr : (mem_addr + (rt_uint32_t)i * SPI_DMA_BLOCK_MAX); + /* BlkTfrSize counts data items, so the memory side advances one block + * worth of ELEMENTS per node: the byte stride is SPI_DMA_BLOCK_MAX + * items times the memory-side transfer width. That width field is the + * DMA transfer width code (0 = 8-bit, 1 = 16-bit, 2 = 32-bit), i.e. + * 1 << width bytes -- the very value programmed into the node below, + * so stride and width cannot disagree. Advancing by SPI_DMA_BLOCK_MAX + * BYTES instead overlaps the nodes by half a word at 16-bit width. */ + rt_uint32_t mem_stride = (rt_uint32_t)SPI_DMA_BLOCK_MAX * + (1UL << (mem_is_src ? ch->SrcTfrWidth : ch->DstTfrWidth)); + node->SrcAddr = mem_is_src ? (mem_addr + (rt_uint32_t)i * mem_stride) : periph_addr; + node->DstAddr = mem_is_src ? periph_addr : (mem_addr + (rt_uint32_t)i * mem_stride); node->pNext = has_next ? &lli[i + 1U] : RT_NULL; node->IntEn = has_next ? 0U : 1U; node->DstTfrWidth = ch->DstTfrWidth; @@ -596,7 +606,10 @@ static rt_err_t SPI_DMA_Transmit(struct n32_spi *spi_drv, uint8_t *pData, uint16 { #if defined(SOC_SERIES_N32H7xx) /* SPI TX DMA send (single block or seamless LLI chain) */ - n32_spi_dma_arm(spi_drv, RT_FALSE, RT_TRUE, pData, Size); + if (n32_spi_dma_arm(spi_drv, RT_FALSE, RT_TRUE, pData, Size) != RT_EOK) + { + return -RT_ERROR; + } #elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) /* SPI TX DMA Send Data for H49X */ DMA_EnableChannel(spi_drv->config->dma_tx->DMAChx, DISABLE); @@ -697,7 +710,10 @@ static rt_err_t SPI_DMA_Receive(struct n32_spi *spi_drv, uint8_t *pData, uint16_ { #if defined(SOC_SERIES_N32H7xx) /* SPI RX DMA receive (single block or seamless LLI chain) */ - n32_spi_dma_arm(spi_drv, RT_TRUE, RT_TRUE, pData, Size); + if (n32_spi_dma_arm(spi_drv, RT_TRUE, RT_TRUE, pData, Size) != RT_EOK) + { + return -RT_ERROR; + } #elif defined(SOC_SERIES_N32H49x) || defined(SOC_SERIES_N32H47x_48x) /* SPI RX DMA Receive Data for H49X */ DMA_EnableChannel(spi_drv->config->dma_rx->DMAChx, DISABLE); @@ -1840,14 +1856,19 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m * grid (see the warm re-arm note). Slaves never chunk below * SPI_DMA_CHAIN_MAX and ignore the flag anyway. */ spi_drv->fd_chunk_cont = (fd_msg && (already_send_length != 0U)) ? RT_TRUE : RT_FALSE; - /* avoid null pointer problems */ + /* avoid null pointer problems. + * already_send_length counts elements while the buffers are byte + * addressed, so this chunk's offset must be scaled by the data width + * (16-bit data = 2 bytes per element). Without it every chunk after + * the first starts one byte early per element -- half the real offset + * -- and the DMA walks the wrong half of the buffer. */ if (message->send_buf) { - send_buf = (rt_uint8_t *)message->send_buf + already_send_length; + send_buf = (rt_uint8_t *)message->send_buf + (already_send_length * (spi_drv->cfg->data_width / 8u)); } if (message->recv_buf) { - recv_buf = (rt_uint8_t *)message->recv_buf + already_send_length; + recv_buf = (rt_uint8_t *)message->recv_buf + (already_send_length * (spi_drv->cfg->data_width / 8u)); } rt_uint32_t *dma_aligned_buffer = RT_NULL; /* TX staging buffer (copy path only) */ @@ -1917,6 +1938,11 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m rt_hw_cpu_dcache_ops(RT_HW_CACHE_FLUSH, p_tx_buffer, send_bytes); } #else + /* send_length counts elements: the staging buffer must be sized and + * filled in bytes (x2 for 16-bit data). Sized in elements the + * halfword DMA reads twice as far as the allocation and the copy + * only carries half of the payload. */ + rt_uint32_t send_bytes = send_length * (spi_drv->cfg->data_width / 8u); if (RT_IS_ALIGN((rt_uint32_t)send_buf, 4) && send_buf != RT_NULL) /* aligned with 4 bytes? */ { p_tx_buffer = (rt_uint32_t *)send_buf; /* send_buf aligns with 4 bytes, no more operations */ @@ -1924,14 +1950,14 @@ static rt_ssize_t spixfer(struct rt_spi_device *device, struct rt_spi_message *m else { /* send_buf doesn't align with 4 bytes, so creat a cache buffer with 4 bytes aligned */ - dma_aligned_buffer = (rt_uint32_t *)rt_malloc(send_length); /* aligned with RT_ALIGN_SIZE (8 bytes by default) */ + dma_aligned_buffer = (rt_uint32_t *)rt_malloc(send_bytes); /* aligned with RT_ALIGN_SIZE (8 bytes by default) */ if (dma_aligned_buffer == RT_NULL) { LOG_E("SPI DMA TX buffer malloc failed!"); state = -RT_ENOMEM; goto spi_staging_free; } - rt_memcpy(dma_aligned_buffer, send_buf, send_length); + rt_memcpy(dma_aligned_buffer, send_buf, send_bytes); p_tx_buffer = dma_aligned_buffer; } #endif diff --git a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h index c018bc76c0..5e726fe4e4 100644 --- a/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h +++ b/bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h @@ -48,7 +48,7 @@ struct n32_spi_device #define SPI_USING_RX_DMA_FLAG (1 << 0) #define SPI_USING_TX_DMA_FLAG (1 << 1) -/* Max bytes of a single DMA block (12-bit BlkTfrSize) */ +/* Max elements (data items) of a single DMA block (12-bit BlkTfrSize) */ #define SPI_DMA_BLOCK_MAX (4095U) /* Number of LLI chain nodes per DMA leg (sizes lli_tx/lli_rx) */ #define SPI_DMA_CHAIN_NODES (16U) diff --git a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig index 5a884858f3..b291cb34f3 100644 --- a/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig +++ b/bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig @@ -1,10 +1,18 @@ menu "Hardware Drivers Config" + config BSP_PERIPH_PIN_CFG_READY + bool "Optional peripheral AF pins are configured on this board" + default n + help + The peripheral clock and IO must be configured in + board/Cube_Config/USER/src/n32h47x_48x_cfg.c. + menu "Onboard Peripheral Drivers" menuconfig BSP_USING_ETH bool "Enable Ethernet" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_LWIP if BSP_USING_ETH choice BSP_ETH_INTERFACE_SEL @@ -231,6 +239,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_QSPI bool "Enable QSPI (XSPI) BUS" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_QSPI select RT_USING_SPI @@ -683,6 +692,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_NAND bool "Enable Nand" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_MTD_NAND if BSP_USING_NAND choice BSP_NAND_BANK_SEL diff --git a/bsp/n32/n32hxxx/n32h497zgl7-evb/board/Kconfig b/bsp/n32/n32hxxx/n32h497zgl7-evb/board/Kconfig index aa5e67a533..7be0abd4ec 100644 --- a/bsp/n32/n32hxxx/n32h497zgl7-evb/board/Kconfig +++ b/bsp/n32/n32hxxx/n32h497zgl7-evb/board/Kconfig @@ -1,10 +1,18 @@ menu "Hardware Drivers Config" + config BSP_PERIPH_PIN_CFG_READY + bool "Optional peripheral AF pins are configured on this board" + default n + help + The peripheral clock and IO must be configured in + board/Cube_Config/USER/src/n32h49x_cfg.c. + menu "Onboard Peripheral Drivers" menuconfig BSP_USING_ETH bool "Enable Ethernet" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_LWIP if BSP_USING_ETH choice BSP_ETH_INTERFACE_SEL @@ -48,6 +56,7 @@ menu "Onboard Peripheral Drivers" menuconfig BSP_USING_SDRAM bool "Enable SDRAM" default n + depends on BSP_PERIPH_PIN_CFG_READY if BSP_USING_SDRAM choice BSP_SDRAM_BANK_SEL prompt "Select SDRAM Bank" @@ -340,6 +349,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_QSPI bool "Enable QSPI (XSPI) BUS" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_QSPI select RT_USING_SPI @@ -764,6 +774,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_NAND bool "Enable Nand" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_MTD_NAND if BSP_USING_NAND choice BSP_NAND_BANK_SEL diff --git a/bsp/n32/n32hxxx/n32h760zil7-stb/board/Kconfig b/bsp/n32/n32hxxx/n32h760zil7-stb/board/Kconfig index 2a76fec34b..a67a40229c 100644 --- a/bsp/n32/n32hxxx/n32h760zil7-stb/board/Kconfig +++ b/bsp/n32/n32hxxx/n32h760zil7-stb/board/Kconfig @@ -1,10 +1,18 @@ menu "Hardware Drivers Config" + config BSP_PERIPH_PIN_CFG_READY + bool "Optional peripheral AF pins are configured on this board" + default n + help + The peripheral clock and IO must be configured in + board/Cube_Config/USER/src/n32h7xx_cfg.c. + menu "Onboard Peripheral Drivers" menuconfig BSP_USING_SDRAM bool "Enable SDRAM" default n + depends on BSP_PERIPH_PIN_CFG_READY if BSP_USING_SDRAM choice BSP_SDRAM_BANK_SEL prompt "Select SDRAM Bank" @@ -85,6 +93,7 @@ menu "Onboard Peripheral Drivers" menuconfig BSP_USING_LCD bool "Enable LCD" default n + depends on BSP_PERIPH_PIN_CFG_READY select BSP_USING_SDRAM if BSP_USING_LCD config BSP_BITS_PER_PIXEL @@ -175,6 +184,7 @@ menu "Onboard Peripheral Drivers" menuconfig BSP_USING_ETH bool "Enable Ethernet" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_LWIP if BSP_USING_ETH choice BSP_ETH_SEL @@ -650,6 +660,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_QSPI bool "Enable QSPI Quad SPI BUS" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_QSPI select RT_USING_SPI if BSP_USING_QSPI @@ -1933,6 +1944,7 @@ menu "On-chip Peripheral Drivers" menuconfig BSP_USING_NAND bool "Enable Nand" default n + depends on BSP_PERIPH_PIN_CFG_READY select RT_USING_MTD_NAND if BSP_USING_NAND choice BSP_NAND_BANK_SEL