summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_hard_i2c.c123
-rw-r--r--bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_nand.c29
-rw-r--r--bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_qspi.c209
-rw-r--r--bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.c50
-rw-r--r--bsp/n32/n32hxxx/libraries/N32_Drivers/drivers/drv_spi.h2
-rw-r--r--bsp/n32/n32hxxx/n32h487zgl7-evb/board/Kconfig10
-rw-r--r--bsp/n32/n32hxxx/n32h497zgl7-evb/board/Kconfig11
-rw-r--r--bsp/n32/n32hxxx/n32h760zil7-stb/board/Kconfig12
8 files changed, 358 insertions, 88 deletions
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