summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorWayne <[email protected]>2022-06-13 21:16:55 +0800
committerGitHub <[email protected]>2022-06-13 21:16:55 +0800
commit2006785394e72a8f865be6c80b91b86f73424f8a (patch)
treed8ac3e00f4f11dac0c46d9a36127c17276c6d1b1
parentc16eaf55de6d6ef193c208a8caa5fed4d93fde76 (diff)
[bsp/nuvoton] Upload missing files. (#6052)
* [bsp/nuvoton] Upload missing files. Co-authored-by: Wayne Lin <[email protected]>
-rw-r--r--bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h83
-rw-r--r--bsp/nuvoton/libraries/m2354/StdDriver/src/nu_trng.c177
-rw-r--r--bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c796
-rw-r--r--bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c796
-rw-r--r--bsp/nuvoton/libraries/nu_packages/FSA506/SConscript19
-rw-r--r--bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c124
-rw-r--r--bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c392
-rw-r--r--bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h57
-rw-r--r--bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c342
-rw-r--r--bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h133
-rw-r--r--bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c337
-rw-r--r--bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h73
-rw-r--r--bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c195
-rw-r--r--bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h64
14 files changed, 3588 insertions, 0 deletions
diff --git a/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h b/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h
new file mode 100644
index 0000000000..3766e882c9
--- /dev/null
+++ b/bsp/nuvoton/libraries/m2354/StdDriver/inc/nu_trng.h
@@ -0,0 +1,83 @@
+/**************************************************************************//**
+ * @file nu_trng.h
+ * @version V3.00
+ * @brief TRNG driver header file
+ *
+ * @copyright SPDX-License-Identifier: Apache-2.0
+ * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
+ ******************************************************************************/
+#ifndef __NU_TRNG_H__
+#define __NU_TRNG_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+/** @addtogroup Standard_Driver Standard Driver
+ @{
+*/
+
+/** @addtogroup TRNG_Driver TRNG Driver
+ @{
+*/
+
+
+/** @addtogroup M460_TRNG_EXPORTED_MACROS TRNG Exported Macros
+ @{
+*/
+
+/*----------------------------------------------------------------------------------------------*/
+/* Macros */
+/*----------------------------------------------------------------------------------------------*/
+
+/**
+ * @brief Let TRNG engine know the currrent PCLK frequency. The CLKPSC is the peripheral
+ * clock frequency range for the selected value , the CLKPSC setting must be higher
+ * than or equal to the actual peripheral clock frequency (for correct random generation).
+ * @param clkpsc 0: PCLK is 80~100 MHz
+ * 1: PCLK is 60~80 MHz
+ * 2: PCLK is 50~60 MHz
+ * 3: PCLK is 40~50 MHz
+ * 4: PCLK is 30~40 MHz
+ * 5: PCLK is 25~30 MHz
+ * 6: PCLK is 20~25 MHz
+ * 7: PCLK is 15~20 MHz
+ * 8: PCLK is 12~15 MHz
+ * 9: PCLK is 9~12 MHz
+ *
+ * @return None
+ * \hideinitializer
+ */
+#define TRNG_SET_CLKP(clkpsc) do { TRNG->CTL = (TRNG->CTL&~TRNG_CTL_CLKP_Msk)|((clkpsc & 0xf)<<TRNG_CTL_CLKP_Pos); } while(0);
+
+
+/*@}*/ /* end of group M460_TRNG_EXPORTED_MACROS */
+
+
+/** @addtogroup TRNG_EXPORTED_FUNCTIONS TRNG Exported Functions
+ @{
+*/
+
+
+/*---------------------------------------------------------------------------------------------------------*/
+/* Functions */
+/*---------------------------------------------------------------------------------------------------------*/
+
+int32_t TRNG_Open(void);
+int32_t TRNG_GenWord(uint32_t *u32RndNum);
+int32_t TRNG_GenBignum(uint8_t u8BigNum[], int32_t i32Len);
+int32_t TRNG_GenBignumHex(char cBigNumHex[], int32_t i32Len);
+
+
+/*@}*/ /* end of group TRNG_EXPORTED_FUNCTIONS */
+
+/*@}*/ /* end of group TRNG_Driver */
+
+/*@}*/ /* end of group Standard_Driver */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __NU_TRNG_H__ */
diff --git a/bsp/nuvoton/libraries/m2354/StdDriver/src/nu_trng.c b/bsp/nuvoton/libraries/m2354/StdDriver/src/nu_trng.c
new file mode 100644
index 0000000000..56318b9447
--- /dev/null
+++ b/bsp/nuvoton/libraries/m2354/StdDriver/src/nu_trng.c
@@ -0,0 +1,177 @@
+/**************************************************************************//**
+ * @file trng.c
+ * @version V3.00
+ * @brief M460 series TRNG driver source file
+ *
+ * @copyright SPDX-License-Identifier: Apache-2.0
+ * @copyright Copyright (C) 2021 Nuvoton Technology Corp. All rights reserved.
+*****************************************************************************/
+
+#include <stdio.h>
+
+#include "NuMicro.h"
+
+
+/** @addtogroup Standard_Driver Standard Driver
+ @{
+*/
+
+/** @addtogroup TRNG_Driver TRNG Driver
+ @{
+*/
+
+
+/** @addtogroup TRNG_EXPORTED_FUNCTIONS TRNG Exported Functions
+ @{
+*/
+
+
+/**
+ * @brief Initialize TRNG hardware.
+ * @return TRNG hardware enable success or failed.
+ * @retval 0 Success
+ * @retval -1 Time-out. TRNG hardware may not be enabled.
+ */
+int32_t TRNG_Open(void)
+{
+ uint32_t u32TimeOutCount = SystemCoreClock; /* 1 second time-out */
+
+ SYS->IPRST1 |= SYS_IPRST1_TRNGRST_Msk;
+ SYS->IPRST1 ^= SYS_IPRST1_TRNGRST_Msk;
+
+ TRNG->CTL |= TRNG_CTL_TRNGEN_Msk;
+
+ TRNG->ACT |= TRNG_ACT_ACT_Msk;
+
+ /* Waiting for ready */
+ while ((TRNG->CTL & TRNG_CTL_READY_Msk) == 0)
+ {
+ if (--u32TimeOutCount == 0) return -1; /* Time-out error */
+ }
+
+ return 0;
+}
+
+
+/**
+ * @brief Generate a 32-bits random number word.
+ * @param[out] u32RndNum The output 32-bits word random number.
+ *
+ * @return Success or time-out.
+ * @retval 0 Success
+ * @retval -1 Time-out. TRNG hardware may not be enabled.
+ */
+int32_t TRNG_GenWord(uint32_t *u32RndNum)
+{
+ uint32_t i, u32Reg, timeout;
+
+ *u32RndNum = 0;
+ u32Reg = TRNG->CTL;
+
+ for (i = 0; i < 4; i++)
+ {
+ TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
+
+ /* TRNG should generate one byte per 125*8 us */
+ for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
+ {
+ if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
+ break;
+ }
+
+ if (timeout == 0)
+ return -1;
+
+ *u32RndNum |= ((TRNG->DATA & 0xff) << i * 8);
+
+ }
+ return 0;
+}
+
+/**
+ * @brief Generate a big number in binary format.
+ * @param[out] u8BigNum The output big number.
+ * @param[in] i32Len Request bit length of the output big number. It must be multiple of 8.
+ *
+ * @return Success or time-out.
+ * @retval 0 Success
+ * @retval -1 Time-out. TRNG hardware may not be enabled.
+ */
+int32_t TRNG_GenBignum(uint8_t u8BigNum[], int32_t i32Len)
+{
+ uint32_t i, u32Reg, timeout;
+
+ u32Reg = TRNG->CTL;
+
+ for (i = 0; i < i32Len / 8; i++)
+ {
+ TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
+
+ /* TRNG should generate one byte per 125*8 us */
+ for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
+ {
+ if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
+ break;
+ }
+
+ if (timeout == 0)
+ return -1;
+
+ u8BigNum[i] = (TRNG->DATA & 0xff);
+ }
+ return 0;
+}
+
+/**
+ * @brief Generate a big number in hex format.
+ * @param[out] cBigNumHex The output hex format big number.
+ * @param[in] i32Len Request bit length of the output big number. It must be multiple of 8.
+ *
+ * @return Success or time-out.
+ * @retval 0 Success
+ * @retval -1 Time-out. TRNG hardware may not be enabled.
+ */
+int32_t TRNG_GenBignumHex(char cBigNumHex[], int32_t i32Len)
+{
+ uint32_t i, idx, u32Reg, timeout;
+ uint32_t data;
+
+ u32Reg = TRNG->CTL;
+ idx = 0;
+ for (i = 0; i < i32Len / 8; i++)
+ {
+ TRNG->CTL = TRNG_CTL_TRNGEN_Msk | u32Reg;
+
+ /* TRNG should generate one byte per 125*8 us */
+ for (timeout = (CLK_GetHCLKFreq() / 100); timeout > 0; timeout--)
+ {
+ if (TRNG->CTL & TRNG_CTL_DVIF_Msk)
+ break;
+ }
+
+ if (timeout == 0)
+ return -1;
+
+ data = (TRNG->DATA & 0xff);
+
+ if (data >= 0xA0)
+ cBigNumHex[idx++] = ((data >> 4) & 0xf) - 10 + 'A';
+ else
+ cBigNumHex[idx++] = ((data >> 4) & 0xf) + '0';
+
+ data &= 0xf;
+ if (data >= 0xA)
+ cBigNumHex[idx++] = data - 10 + 'A';
+ else
+ cBigNumHex[idx++] = data + '0';
+ }
+ cBigNumHex[idx] = 0;
+ return 0;
+}
+
+
+/*@}*/ /* end of group TRNG_EXPORTED_FUNCTIONS */
+
+/*@}*/ /* end of group TRNG_Driver */
+
+/*@}*/ /* end of group Standard_Driver */
diff --git a/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c b/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c
new file mode 100644
index 0000000000..3e982bea4f
--- /dev/null
+++ b/bsp/nuvoton/libraries/m2354/rtt_port/drv_sdio.c
@@ -0,0 +1,796 @@
+/**************************************************************************//**
+*
+* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
+*
+* SPDX-License-Identifier: Apache-2.0
+*
+* Change Logs:
+* Date Author Notes
+* 2022-4-21 Wayne First version
+*
+******************************************************************************/
+
+#include "rtconfig.h"
+
+#if defined(BSP_USING_SDH)
+
+#include <rtdevice.h>
+#include <drivers/mmcsd_core.h>
+#include <drivers/sdio.h>
+
+#include "NuMicro.h"
+
+#define LOG_TAG "drv.sdh"
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME LOG_TAG
+#define DBG_LEVEL LOG_LVL_ASSERT
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#define SDH_ALIGN_LEN 4
+#define SDH_BUFF_SIZE 512
+#define SDH_BLOCK_SIZE 512
+
+enum
+{
+ SDH_START = -1,
+#if defined(BSP_USING_SDH0)
+ SDH0_IDX,
+#endif
+#if defined(BSP_USING_SDH1)
+ SDH1_IDX,
+#endif
+ SDH_CNT
+};
+
+struct nu_sdh
+{
+ struct rt_mmcsd_host *host;
+ char *name;
+ SDH_T *base;
+ IRQn_Type irqn;
+ uint32_t rstidx;
+ uint32_t modid;
+
+ uint8_t *cachebuf;
+ uint32_t u32CmdResp0;
+ uint32_t u32CmdResp1;
+ uint32_t u32CurClk;
+ rt_tick_t LastNotice;
+};
+typedef struct nu_sdh *nu_sdh_t;
+
+/* Private variables ------------------------------------------------------------*/
+static struct nu_sdh nu_sdh_arr [] =
+{
+#if defined(BSP_USING_SDH0)
+ {
+ .name = "sdh0",
+ .base = SDH0,
+ .irqn = SDH0_IRQn,
+ .rstidx = SDH0_RST,
+ .modid = SDH0_MODULE,
+ .cachebuf = RT_NULL,
+ },
+#endif
+#if defined(BSP_USING_SDH1)
+ {
+ .name = "sdh1",
+ .base = SDH1,
+ .irqn = SDH1_IRQn,
+ .rstidx = SDH1_RST,
+ .modid = SDH1_MODULE,
+ .cachebuf = RT_NULL,
+ },
+#endif
+}; /* struct nu_sdh nu_sdh_arr [] */
+
+#define SDH_SetClock SDH_Set_clock
+
+static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw)
+{
+ if (bw == 4)
+ {
+ sdh->CTL |= SDH_CTL_DBW_Msk;
+ }
+ else if (bw == 1)
+ {
+ sdh->CTL &= ~SDH_CTL_DBW_Msk;
+ }
+ else
+ {
+ goto exit_SDH_SetBusWidth;
+ }
+
+ return 0;
+
+exit_SDH_SetBusWidth:
+
+ return -1;
+
+}
+
+static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask)
+{
+ int cnt = 0x100000;
+ while (cnt-- > 0)
+ {
+ sdh->CTL |= SDH_CTL_CLK8OEN_Msk;
+ while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { }
+
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk))
+ break;
+ }
+ return (cnt == 0) ? -1 : 0 ;
+}
+
+static int SDH_GetCD(SDH_T *sdh)
+{
+ int i32CD = 0;
+ if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk)
+ {
+ if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */
+ {
+ i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1;
+ }
+ else /* Card detect pin from DAT3 mode */
+ {
+ __IO uint32_t i;
+
+ sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
+ for (i = 0ul; i < 5000ul; i++) { }
+
+ i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0;
+ sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
+ }
+ }
+ return i32CD;
+}
+
+static void SDH_Enable(SDH_T *sdh)
+{
+ /* Reset sdh and its DMA engine at first. */
+ sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
+ while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
+ sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
+ sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
+
+ sdh->GCTL = SDH_GCTL_GCTLRST_Msk;
+ while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag
+ sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk;
+ sdh->GCTL = SDH_GCTL_SDEN_Msk;
+
+ sdh->CTL |= SDH_CTL_CTLRST_Msk;
+ while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { }
+
+ sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag
+
+ sdh->INTEN |= SDH_INTEN_CDSRC_Msk;
+ sdh->INTEN |= SDH_INTEN_CDIEN_Msk;
+}
+
+/**
+ * @brief This function get command responding.
+ * @param sdh SDH instance
+ * @param cmd rt_mmcsd_cmd
+ * @retval none
+ */
+static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd)
+{
+ if (resp_type(cmd) == RESP_R2)
+ {
+ uint8_t *c = (uint8_t *)&sdh->FB[0];
+ int i, j, tmp[5];
+
+ for (i = 0, j = 0; j < 5; i += 4, j++)
+ {
+ tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3));
+ }
+ for (i = 0; i < 4; i++)
+ {
+ cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) |
+ ((tmp[i + 1] & 0xff000000) >> 24);
+ }
+ }
+ else
+ {
+ cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff);
+ cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
+ }
+}
+
+/**
+ * @brief This function wait data-sending/receiving.
+ * @param sdh SDH instance
+ * @param data rt_mmcsd_data
+ * @retval error code
+ */
+static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data)
+{
+ while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { }
+
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk);
+
+ if (data->flags & DATA_DIR_WRITE)
+ {
+ sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
+
+ while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { }
+
+ sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief This function send command and wait its response.
+ * @param host rt_mmcsd_host
+ * @param cmd rt_mmcsd_cmd
+ * @param data rt_mmcsd_data
+ * @retval error code
+ */
+static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data)
+{
+ int ret;
+ nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ volatile uint32_t ctl = 0, tout = 0;
+
+ switch (host->io_cfg.bus_width)
+ {
+ case MMCSD_BUS_WIDTH_1:
+ ctl &= ~SDH_CTL_DBW_Msk;
+ break;
+
+ case MMCSD_BUS_WIDTH_4:
+ ctl |= SDH_CTL_DBW_Msk;
+ break;
+
+ case MMCSD_BUS_WIDTH_8:
+ default:
+ return -1;
+ }
+
+ /* Reset sdh and its DMA engine at first. */
+ sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
+ while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
+ sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
+ sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
+
+ if (resp_type(cmd) != RESP_NONE)
+ {
+ if (resp_type(cmd) == RESP_R2)
+ {
+ ctl |= SDH_CTL_R2EN_Msk;
+ }
+ else
+ {
+ ctl |= SDH_CTL_RIEN_Msk;
+ }
+ tout = 0xFFFF;
+ }
+
+ /* Set SDNWR and BLK_CNT to 1 */
+ ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos));
+ ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk);
+
+ /* Set Transfer mode regarding to data flag */
+ if (data != RT_NULL)
+ {
+ sdh->BLEN = data->blksize - 1;
+
+ if (data->blksize <= 0x200)
+ {
+ if (data->blks < 256)
+ {
+ ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos);
+ }
+ else
+ {
+ LOG_E("SD Max block transfer is 255!!");
+ }
+ }
+
+ if (data->flags & DATA_DIR_READ)
+ {
+ tout = 0xFFFFFF;
+ ctl |= SDH_CTL_DIEN_Msk; // Data-in
+ sdh->DMASA = (uint32_t)data->buf; // Read from dest
+ }
+ else if (data->flags & DATA_DIR_WRITE)
+ {
+ ctl |= SDH_CTL_DOEN_Msk; // Data-out
+ sdh->DMASA = (uint32_t)data->buf; // Write to dest
+ }
+ }
+ else if (resp_type(cmd) == RESP_R1B)
+ {
+ }
+
+ /* Clear response-timeout flag first for safty and reset new timeout value. */
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk);
+ sdh->TOUT = tout;
+
+ /* Set argument and start a transaction. */
+ sdh->CMDARG = cmd->arg;
+ sdh->CTL = ctl;
+
+ /* Wait a command done. */
+ while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { }
+
+ if (resp_type(cmd) != RESP_NONE)
+ {
+ if (resp_type(cmd) == RESP_R2)
+ {
+ /* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */
+ while (sdh->CTL & SDH_CTL_R2EN_Msk)
+ {
+ /* When get a Response timeout, break the polling. */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
+ {
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+ }
+ else
+ {
+ /* Wait to receive a response from SD card. */
+ while ((sdh->CTL & SDH_CTL_RIEN_Msk))
+ {
+ /* When get a Response timeout, break the polling. */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
+ {
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ /* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */
+ /* Workaround: To check previous cmd's response with CMD's. */
+ if (cmd->cmd_code == 5)
+ {
+ if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1))
+ {
+ LOG_E("False CMD5-RESP issue occured.\n");
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+ NuSdh->u32CmdResp0 = sdh->RESP0;
+ NuSdh->u32CmdResp1 = sdh->RESP1;
+ }
+
+ /* Get response from FB or register */
+ nu_sdh_sendcmd_done(sdh, cmd);
+ }
+
+ if (data != RT_NULL)
+ {
+ /* Wait data processing done */
+ nu_sdh_xfer_data(sdh, data);
+
+ ret = SDH_GetBusStatus(sdh, 0);
+ if (ret)
+ {
+ LOG_E("ERROR: Busy %d\n", ret);
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ /* Handle CRC flag */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault
+ {
+ uint32_t u32INTSTS = sdh->INTSTS;
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk);
+ ret = __LINE__;
+
+ if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3
+ {
+ LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd));
+ goto exit_nu_sdh_sendcmd;
+ }
+
+ if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16
+ {
+ LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd));
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ return 0;
+
+
+exit_nu_sdh_sendcmd:
+
+ LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret);
+
+ cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
+ sdh->TOUT = 0;
+ SDH_Enable(sdh);
+
+ return -ret;
+}
+
+/**
+ * @brief This function send request.
+ * @param host rt_mmcsd_host
+ * @param req request
+ * @retval None
+ */
+static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+ nu_sdh_t NuSdh;
+ SDH_T *sdh;
+
+ RT_ASSERT(host);
+ RT_ASSERT(req);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ sdh = NuSdh->base;
+
+ if (!SDH_GetCD(sdh)) // card is not present
+ {
+ LOG_E("Card is not present");
+ req->cmd->err = -RT_EIO;
+ goto exit_nu_sdh_request;
+ }
+
+ if (req->cmd != RT_NULL)
+ {
+ struct rt_mmcsd_cmd *cmd = req->cmd;
+ struct rt_mmcsd_data *data = req->data;
+
+ LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d",
+ (host->card == RT_NULL) ? "Unknown" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "",
+ cmd->cmd_code,
+ cmd->arg,
+ resp_type(cmd),
+ data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
+ data ? data->buf : 0,
+ data ? data->blks : 0,
+ data ? data->blksize : 0,
+ data ? data->blks * data->blksize : 0);
+
+ if (data != RT_NULL)
+ {
+ rt_uint32_t size;
+ rt_int32_t IsNonaligned = 0;
+ rt_uint32_t *org_data_buf = data->buf;
+
+ size = data->blksize * data->blks;
+
+ RT_ASSERT(org_data_buf);
+
+ IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0;
+ if (IsNonaligned)
+ {
+ /* Allocate memory temp buffer on demand. */
+ RT_ASSERT(size <= SDH_BUFF_SIZE);
+
+ if (NuSdh->cachebuf == RT_NULL)
+ {
+ NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN);
+ RT_ASSERT(NuSdh->cachebuf);
+ }
+
+ data->buf = (rt_uint32_t *)NuSdh->cachebuf;
+ if (data->flags & DATA_DIR_WRITE)
+ {
+ LOG_D("Un-aligned, prepare into cache buf(%d)", size);
+ rt_memcpy(data->buf, org_data_buf, size);
+ }
+ }
+
+ cmd->err = nu_sdh_sendcmd(host, cmd, data);
+
+ if (!cmd->err && IsNonaligned)
+ {
+ if (data->flags & DATA_DIR_READ)
+ {
+ LOG_D("Un-aligned, restore from cache buf(%d)", size);
+ rt_memcpy(org_data_buf, data->buf, size);
+ }
+ }
+ data->buf = org_data_buf;
+
+ LOG_HEX("data.dest", 16, (void *)data->buf, size);
+ }
+ else
+ {
+ cmd->err = nu_sdh_sendcmd(host, cmd, NULL);
+ }
+
+ if (resp_type(cmd) != RESP_NONE)
+ LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16);
+ }
+
+ if (req->stop != RT_NULL)
+ {
+ struct rt_mmcsd_cmd *stop = req->stop;
+
+ stop->err = nu_sdh_sendcmd(host, stop, NULL);
+
+ if (resp_type(stop) != RESP_NONE)
+ LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16);
+
+ }
+
+exit_nu_sdh_request:
+
+ mmcsd_req_complete(host);
+}
+
+/**
+ * @brief This function config.
+ * @param host rt_mmcsd_host
+ * @param io_cfg rt_mmcsd_io_cfg
+ * @retval None
+ */
+static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
+{
+ nu_sdh_t NuSdh;
+ rt_uint32_t clk;
+ SDH_T *sdh;
+
+ RT_ASSERT(host);
+ RT_ASSERT(io_cfg);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ sdh = NuSdh->base;
+ clk = io_cfg->clock;
+
+ LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s",
+ NuSdh->name,
+ clk,
+ io_cfg->bus_width,
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "",
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "",
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "",
+ io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
+ io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
+ io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
+
+ /* Clock */
+ if (clk > host->freq_max)
+ clk = host->freq_max;
+
+ if (clk < host->freq_min)
+ clk = host->freq_min;
+
+ LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000);
+ if (NuSdh->u32CurClk != (clk / 1000))
+ {
+ SDH_SetClock(sdh, clk / 1000);
+ NuSdh->u32CurClk = (clk / 1000);
+ }
+
+ switch (io_cfg->power_mode)
+ {
+ case MMCSD_POWER_UP:
+ if (clk <= 400000)
+ {
+ /* power ON 74 clock */
+ sdh->CTL |= SDH_CTL_CLK74OEN_Msk;
+
+ while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk)
+ {
+ }
+ }
+ break;
+
+ case MMCSD_POWER_ON:
+ break;
+
+ case MMCSD_POWER_OFF:
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bus width */
+ switch ((io_cfg->bus_width))
+ {
+ case MMCSD_BUS_WIDTH_1:
+ SDH_SetBusWidth(sdh, 1);
+ break;
+
+ case MMCSD_BUS_WIDTH_4:
+ SDH_SetBusWidth(sdh, 4);
+ break;
+
+ case MMCSD_BUS_WIDTH_8:
+ default:
+ break;
+ }
+
+}
+
+/**
+ * @brief This function detect sdcard.
+ * @param host rt_mmcsd_host
+ * @retval card detection status
+ */
+static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host)
+{
+ nu_sdh_t NuSdh;
+ RT_ASSERT(host);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ LOG_D("try to detect device");
+ return SDH_GetCD(sdh);
+}
+
+static void nu_sdh_isr(nu_sdh_t NuSdh)
+{
+ SDH_T *sdh = NuSdh->base;
+ uint32_t isr = sdh->INTSTS;
+
+ /* card detected */
+ if (isr & SDH_INTSTS_CDIF_Msk)
+ {
+ rt_tick_t cur_tick = rt_tick_get();
+ rt_tick_t diff_tick;
+
+ /* ready to change */
+ if (cur_tick >= NuSdh->LastNotice)
+ diff_tick = (cur_tick - NuSdh->LastNotice);
+ else
+ diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick;
+
+ if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms
+ {
+ NuSdh->LastNotice = cur_tick;
+ mmcsd_change(NuSdh->host);
+ }
+
+ /* Clear CDIF interrupt flag */
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+}
+
+#if defined(BSP_USING_SDH0)
+void SDH0_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+#if defined(BSP_USING_SDH1)
+void SDH1_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+
+/**
+ * @brief This function update sdh interrupt.
+ * @param host rt_mmcsd_host
+ * @param enable
+ * @retval None
+ */
+void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
+{
+ nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ if (enable)
+ {
+ LOG_D("Enable %s irq", NuSdh->name);
+ SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+ else
+ {
+ LOG_D("Disable %s irq", NuSdh->name);
+ SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+}
+
+static const struct rt_mmcsd_host_ops ops =
+{
+ nu_sdh_request,
+ nu_sdh_iocfg,
+ nu_sdh_card_detect,
+ nu_sdh_irq_update,
+};
+
+/**
+ * @brief This function create mmcsd host.
+ * @param sdh nu_sdh_t
+ * @retval nuvton
+ */
+void nu_sdh_host_init(nu_sdh_t sdh)
+{
+ struct rt_mmcsd_host *host = mmcsd_alloc_host();
+ RT_ASSERT(host);
+
+ /* set host default attributes */
+ host->ops = &ops;
+ host->freq_min = 300 * 1000;
+ host->freq_max = 48 * 1000 * 1000;
+ host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34;
+
+ host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED;
+
+ host->max_seg_size = SDH_BUFF_SIZE;
+ host->max_dma_segs = 1;
+ host->max_blk_size = SDH_BLOCK_SIZE;
+ host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE);
+
+ /* link up host and sdio */
+ host->private_data = sdh;
+ sdh->host = host;
+
+ /* Enable DMA engine at first. */
+ SDH_Enable(sdh->base);
+
+ /* Install ISR. */
+ NVIC_EnableIRQ(sdh->irqn);
+
+ /* ready to change */
+ //mmcsd_change(host);
+}
+
+static int rt_hw_sdh_init(void)
+{
+ int i;
+
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ CLK_EnableModuleClock(nu_sdh_arr[i].modid);
+ SYS_ResetModule(nu_sdh_arr[i].rstidx);
+
+ nu_sdh_host_init(&nu_sdh_arr[i]);
+ }
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(rt_hw_sdh_init);
+
+void nu_sd_attach(void)
+{
+ int i;
+ /* ready to change */
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ if (nu_sdh_arr[i].host)
+ mmcsd_change(nu_sdh_arr[i].host);
+ }
+}
+MSH_CMD_EXPORT(nu_sd_attach, attach card);
+
+void nu_sd_regdump(void)
+{
+ int i;
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ if (nu_sdh_arr[i].host)
+ LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T));
+ }
+}
+MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers);
+
+#endif
diff --git a/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c
new file mode 100644
index 0000000000..3e982bea4f
--- /dev/null
+++ b/bsp/nuvoton/libraries/m480/rtt_port/drv_sdio.c
@@ -0,0 +1,796 @@
+/**************************************************************************//**
+*
+* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
+*
+* SPDX-License-Identifier: Apache-2.0
+*
+* Change Logs:
+* Date Author Notes
+* 2022-4-21 Wayne First version
+*
+******************************************************************************/
+
+#include "rtconfig.h"
+
+#if defined(BSP_USING_SDH)
+
+#include <rtdevice.h>
+#include <drivers/mmcsd_core.h>
+#include <drivers/sdio.h>
+
+#include "NuMicro.h"
+
+#define LOG_TAG "drv.sdh"
+#undef DBG_ENABLE
+#define DBG_SECTION_NAME LOG_TAG
+#define DBG_LEVEL LOG_LVL_ASSERT
+#define DBG_COLOR
+#include <rtdbg.h>
+
+#define SDH_ALIGN_LEN 4
+#define SDH_BUFF_SIZE 512
+#define SDH_BLOCK_SIZE 512
+
+enum
+{
+ SDH_START = -1,
+#if defined(BSP_USING_SDH0)
+ SDH0_IDX,
+#endif
+#if defined(BSP_USING_SDH1)
+ SDH1_IDX,
+#endif
+ SDH_CNT
+};
+
+struct nu_sdh
+{
+ struct rt_mmcsd_host *host;
+ char *name;
+ SDH_T *base;
+ IRQn_Type irqn;
+ uint32_t rstidx;
+ uint32_t modid;
+
+ uint8_t *cachebuf;
+ uint32_t u32CmdResp0;
+ uint32_t u32CmdResp1;
+ uint32_t u32CurClk;
+ rt_tick_t LastNotice;
+};
+typedef struct nu_sdh *nu_sdh_t;
+
+/* Private variables ------------------------------------------------------------*/
+static struct nu_sdh nu_sdh_arr [] =
+{
+#if defined(BSP_USING_SDH0)
+ {
+ .name = "sdh0",
+ .base = SDH0,
+ .irqn = SDH0_IRQn,
+ .rstidx = SDH0_RST,
+ .modid = SDH0_MODULE,
+ .cachebuf = RT_NULL,
+ },
+#endif
+#if defined(BSP_USING_SDH1)
+ {
+ .name = "sdh1",
+ .base = SDH1,
+ .irqn = SDH1_IRQn,
+ .rstidx = SDH1_RST,
+ .modid = SDH1_MODULE,
+ .cachebuf = RT_NULL,
+ },
+#endif
+}; /* struct nu_sdh nu_sdh_arr [] */
+
+#define SDH_SetClock SDH_Set_clock
+
+static int SDH_SetBusWidth(SDH_T *sdh, uint32_t bw)
+{
+ if (bw == 4)
+ {
+ sdh->CTL |= SDH_CTL_DBW_Msk;
+ }
+ else if (bw == 1)
+ {
+ sdh->CTL &= ~SDH_CTL_DBW_Msk;
+ }
+ else
+ {
+ goto exit_SDH_SetBusWidth;
+ }
+
+ return 0;
+
+exit_SDH_SetBusWidth:
+
+ return -1;
+
+}
+
+static int SDH_GetBusStatus(SDH_T *sdh, uint32_t mask)
+{
+ int cnt = 0x100000;
+ while (cnt-- > 0)
+ {
+ sdh->CTL |= SDH_CTL_CLK8OEN_Msk;
+ while (sdh->CTL & SDH_CTL_CLK8OEN_Msk) { }
+
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk))
+ break;
+ }
+ return (cnt == 0) ? -1 : 0 ;
+}
+
+static int SDH_GetCD(SDH_T *sdh)
+{
+ int i32CD = 0;
+ if ((sdh->GCTL & SDH_GCTL_SDEN_Msk) == SDH_GCTL_SDEN_Msk)
+ {
+ if ((sdh->INTEN & SDH_INTEN_CDSRC_Msk) == SDH_INTEN_CDSRC_Msk) /* Card detect pin from GPIO */
+ {
+ i32CD = (sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) ? 0 : 1;
+ }
+ else /* Card detect pin from DAT3 mode */
+ {
+ __IO uint32_t i;
+
+ sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
+ for (i = 0ul; i < 5000ul; i++) { }
+
+ i32CD = ((sdh->INTSTS & SDH_INTSTS_CDSTS_Msk) == SDH_INTSTS_CDSTS_Msk) ? 1 : 0;
+ sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
+ }
+ }
+ return i32CD;
+}
+
+static void SDH_Enable(SDH_T *sdh)
+{
+ /* Reset sdh and its DMA engine at first. */
+ sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
+ while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
+ sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
+ sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
+
+ sdh->GCTL = SDH_GCTL_GCTLRST_Msk;
+ while ((sdh->GCTL & SDH_GCTL_GCTLRST_Msk) == SDH_GCTL_GCTLRST_Msk) { }// clear all interrupt flag
+ sdh->GINTSTS = SDH_GINTSTS_DTAIF_Msk;
+ sdh->GCTL = SDH_GCTL_SDEN_Msk;
+
+ sdh->CTL |= SDH_CTL_CTLRST_Msk;
+ while ((sdh->CTL & SDH_CTL_CTLRST_Msk) == SDH_CTL_CTLRST_Msk) { }
+
+ sdh->INTSTS = 0xFFFFFFFF; // clear all interrupt flag
+
+ sdh->INTEN |= SDH_INTEN_CDSRC_Msk;
+ sdh->INTEN |= SDH_INTEN_CDIEN_Msk;
+}
+
+/**
+ * @brief This function get command responding.
+ * @param sdh SDH instance
+ * @param cmd rt_mmcsd_cmd
+ * @retval none
+ */
+static void nu_sdh_sendcmd_done(SDH_T *sdh, struct rt_mmcsd_cmd *cmd)
+{
+ if (resp_type(cmd) == RESP_R2)
+ {
+ uint8_t *c = (uint8_t *)&sdh->FB[0];
+ int i, j, tmp[5];
+
+ for (i = 0, j = 0; j < 5; i += 4, j++)
+ {
+ tmp[j] = (*(c + i) << 24) | (*(c + i + 1) << 16) | (*(c + i + 2) << 8) | (*(c + i + 3));
+ }
+ for (i = 0; i < 4; i++)
+ {
+ cmd->resp[i] = ((tmp[i] & 0x00ffffff) << 8) |
+ ((tmp[i + 1] & 0xff000000) >> 24);
+ }
+ }
+ else
+ {
+ cmd->resp[0] = (sdh->RESP0 << 8) | (sdh->RESP1 & 0xff);
+ cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
+ }
+}
+
+/**
+ * @brief This function wait data-sending/receiving.
+ * @param sdh SDH instance
+ * @param data rt_mmcsd_data
+ * @retval error code
+ */
+static int nu_sdh_xfer_data(SDH_T *sdh, struct rt_mmcsd_data *data)
+{
+ while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk)) { }
+
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_BLKDIF_Msk);
+
+ if (data->flags & DATA_DIR_WRITE)
+ {
+ sdh->CTL |= SDH_CTL_CLKKEEP_Msk;
+
+ while (!SDH_GET_INT_FLAG(sdh, SDH_INTSTS_DAT0STS_Msk)) { }
+
+ sdh->CTL &= ~SDH_CTL_CLKKEEP_Msk;
+ }
+
+ return 0;
+}
+
+/**
+ * @brief This function send command and wait its response.
+ * @param host rt_mmcsd_host
+ * @param cmd rt_mmcsd_cmd
+ * @param data rt_mmcsd_data
+ * @retval error code
+ */
+static int nu_sdh_sendcmd(struct rt_mmcsd_host *host, struct rt_mmcsd_cmd *cmd, struct rt_mmcsd_data *data)
+{
+ int ret;
+ nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ volatile uint32_t ctl = 0, tout = 0;
+
+ switch (host->io_cfg.bus_width)
+ {
+ case MMCSD_BUS_WIDTH_1:
+ ctl &= ~SDH_CTL_DBW_Msk;
+ break;
+
+ case MMCSD_BUS_WIDTH_4:
+ ctl |= SDH_CTL_DBW_Msk;
+ break;
+
+ case MMCSD_BUS_WIDTH_8:
+ default:
+ return -1;
+ }
+
+ /* Reset sdh and its DMA engine at first. */
+ sdh->DMACTL |= SDH_DMACTL_DMARST_Msk | SDH_DMACTL_DMAEN_Msk;
+ while ((sdh->DMACTL & SDH_DMACTL_DMARST_Msk) == SDH_DMACTL_DMARST_Msk) { }
+ sdh->DMACTL = SDH_DMACTL_DMAEN_Msk;
+ sdh->DMAINTSTS = SDH_DMAINTSTS_ABORTIF_Msk | SDH_DMAINTSTS_WEOTIF_Msk; // clear all interrupt flag
+
+ if (resp_type(cmd) != RESP_NONE)
+ {
+ if (resp_type(cmd) == RESP_R2)
+ {
+ ctl |= SDH_CTL_R2EN_Msk;
+ }
+ else
+ {
+ ctl |= SDH_CTL_RIEN_Msk;
+ }
+ tout = 0xFFFF;
+ }
+
+ /* Set SDNWR and BLK_CNT to 1 */
+ ctl |= ((9 << SDH_CTL_SDNWR_Pos) | (1 << SDH_CTL_BLKCNT_Pos));
+ ctl |= ((cmd->cmd_code << SDH_CTL_CMDCODE_Pos) | SDH_CTL_COEN_Msk);
+
+ /* Set Transfer mode regarding to data flag */
+ if (data != RT_NULL)
+ {
+ sdh->BLEN = data->blksize - 1;
+
+ if (data->blksize <= 0x200)
+ {
+ if (data->blks < 256)
+ {
+ ctl = (ctl & ~SDH_CTL_BLKCNT_Msk) | (data->blks << SDH_CTL_BLKCNT_Pos);
+ }
+ else
+ {
+ LOG_E("SD Max block transfer is 255!!");
+ }
+ }
+
+ if (data->flags & DATA_DIR_READ)
+ {
+ tout = 0xFFFFFF;
+ ctl |= SDH_CTL_DIEN_Msk; // Data-in
+ sdh->DMASA = (uint32_t)data->buf; // Read from dest
+ }
+ else if (data->flags & DATA_DIR_WRITE)
+ {
+ ctl |= SDH_CTL_DOEN_Msk; // Data-out
+ sdh->DMASA = (uint32_t)data->buf; // Write to dest
+ }
+ }
+ else if (resp_type(cmd) == RESP_R1B)
+ {
+ }
+
+ /* Clear response-timeout flag first for safty and reset new timeout value. */
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk);
+ sdh->TOUT = tout;
+
+ /* Set argument and start a transaction. */
+ sdh->CMDARG = cmd->arg;
+ sdh->CTL = ctl;
+
+ /* Wait a command done. */
+ while ((sdh->CTL & (SDH_CTL_COEN_Msk)) == SDH_CTL_COEN_Msk) { }
+
+ if (resp_type(cmd) != RESP_NONE)
+ {
+ if (resp_type(cmd) == RESP_R2)
+ {
+ /* Wait to receive a response R2 from SD card and store the response data into DMC's Flash buffer (exclude CRC7). */
+ while (sdh->CTL & SDH_CTL_R2EN_Msk)
+ {
+ /* When get a Response timeout, break the polling. */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
+ {
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+ }
+ else
+ {
+ /* Wait to receive a response from SD card. */
+ while ((sdh->CTL & SDH_CTL_RIEN_Msk))
+ {
+ /* When get a Response timeout, break the polling. */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_RTOIF_Msk))
+ {
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ /* TOFIX: ISSUE: Sometimes, SDH's RIEN is cleared automatically by controller after host sending CMD5 to SD card. */
+ /* Workaround: To check previous cmd's response with CMD's. */
+ if (cmd->cmd_code == 5)
+ {
+ if ((NuSdh->u32CmdResp0 == sdh->RESP0) && (NuSdh->u32CmdResp1 == sdh->RESP1))
+ {
+ LOG_E("False CMD5-RESP issue occured.\n");
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+ NuSdh->u32CmdResp0 = sdh->RESP0;
+ NuSdh->u32CmdResp1 = sdh->RESP1;
+ }
+
+ /* Get response from FB or register */
+ nu_sdh_sendcmd_done(sdh, cmd);
+ }
+
+ if (data != RT_NULL)
+ {
+ /* Wait data processing done */
+ nu_sdh_xfer_data(sdh, data);
+
+ ret = SDH_GetBusStatus(sdh, 0);
+ if (ret)
+ {
+ LOG_E("ERROR: Busy %d\n", ret);
+ ret = __LINE__;
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ /* Handle CRC flag */
+ if (SDH_GET_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk)) // Fault
+ {
+ uint32_t u32INTSTS = sdh->INTSTS;
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CRCIF_Msk);
+ ret = __LINE__;
+
+ if ((resp_type(cmd) != RESP_R3) && (u32INTSTS & SDH_INTSTS_CRC7_Msk) == 0) //CRC7, Ignore R3
+ {
+ LOG_E("CRC7 error! (resp_type=%d)", resp_type(cmd));
+ goto exit_nu_sdh_sendcmd;
+ }
+
+ if ((u32INTSTS & SDH_INTSTS_CRC16_Msk) == 0) //CRC16
+ {
+ LOG_E("CRC16 error! (resp_type=%d)", resp_type(cmd));
+ goto exit_nu_sdh_sendcmd;
+ }
+ }
+
+ return 0;
+
+
+exit_nu_sdh_sendcmd:
+
+ LOG_D("[%s %d] cmdid=%d error line=%d", __func__, __LINE__, cmd->cmd_code, ret);
+
+ cmd->resp[0] = cmd->resp[1] = cmd->resp[2] = cmd->resp[3] = 0;
+ sdh->TOUT = 0;
+ SDH_Enable(sdh);
+
+ return -ret;
+}
+
+/**
+ * @brief This function send request.
+ * @param host rt_mmcsd_host
+ * @param req request
+ * @retval None
+ */
+static void nu_sdh_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+ nu_sdh_t NuSdh;
+ SDH_T *sdh;
+
+ RT_ASSERT(host);
+ RT_ASSERT(req);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ sdh = NuSdh->base;
+
+ if (!SDH_GetCD(sdh)) // card is not present
+ {
+ LOG_E("Card is not present");
+ req->cmd->err = -RT_EIO;
+ goto exit_nu_sdh_request;
+ }
+
+ if (req->cmd != RT_NULL)
+ {
+ struct rt_mmcsd_cmd *cmd = req->cmd;
+ struct rt_mmcsd_data *data = req->data;
+
+ LOG_D("[%s%s%s%s%s]REQ: CMD:%d ARG:0x%08x RESP_TYPE:%d rw:%c addr:%08x, blks:%d, blksize:%d datalen:%d",
+ (host->card == RT_NULL) ? "Unknown" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_MMC) ? "MMC" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SD) ? "SD" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SDIO) ? "SDIO" : "",
+ (host->card) && (host->card->card_type == CARD_TYPE_SDIO_COMBO) ? "SDIO_COMBO" : "",
+ cmd->cmd_code,
+ cmd->arg,
+ resp_type(cmd),
+ data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
+ data ? data->buf : 0,
+ data ? data->blks : 0,
+ data ? data->blksize : 0,
+ data ? data->blks * data->blksize : 0);
+
+ if (data != RT_NULL)
+ {
+ rt_uint32_t size;
+ rt_int32_t IsNonaligned = 0;
+ rt_uint32_t *org_data_buf = data->buf;
+
+ size = data->blksize * data->blks;
+
+ RT_ASSERT(org_data_buf);
+
+ IsNonaligned = (((rt_uint32_t)data->buf & (SDH_ALIGN_LEN - 1)) > 0) ? 1 : 0;
+ if (IsNonaligned)
+ {
+ /* Allocate memory temp buffer on demand. */
+ RT_ASSERT(size <= SDH_BUFF_SIZE);
+
+ if (NuSdh->cachebuf == RT_NULL)
+ {
+ NuSdh->cachebuf = rt_malloc_align(SDH_BUFF_SIZE, SDH_ALIGN_LEN);
+ RT_ASSERT(NuSdh->cachebuf);
+ }
+
+ data->buf = (rt_uint32_t *)NuSdh->cachebuf;
+ if (data->flags & DATA_DIR_WRITE)
+ {
+ LOG_D("Un-aligned, prepare into cache buf(%d)", size);
+ rt_memcpy(data->buf, org_data_buf, size);
+ }
+ }
+
+ cmd->err = nu_sdh_sendcmd(host, cmd, data);
+
+ if (!cmd->err && IsNonaligned)
+ {
+ if (data->flags & DATA_DIR_READ)
+ {
+ LOG_D("Un-aligned, restore from cache buf(%d)", size);
+ rt_memcpy(org_data_buf, data->buf, size);
+ }
+ }
+ data->buf = org_data_buf;
+
+ LOG_HEX("data.dest", 16, (void *)data->buf, size);
+ }
+ else
+ {
+ cmd->err = nu_sdh_sendcmd(host, cmd, NULL);
+ }
+
+ if (resp_type(cmd) != RESP_NONE)
+ LOG_HEX("cmd->resp", 16, (void *)&cmd->resp[0], 16);
+ }
+
+ if (req->stop != RT_NULL)
+ {
+ struct rt_mmcsd_cmd *stop = req->stop;
+
+ stop->err = nu_sdh_sendcmd(host, stop, NULL);
+
+ if (resp_type(stop) != RESP_NONE)
+ LOG_HEX("stop->resp", 16, (void *)&stop->resp[0], 16);
+
+ }
+
+exit_nu_sdh_request:
+
+ mmcsd_req_complete(host);
+}
+
+/**
+ * @brief This function config.
+ * @param host rt_mmcsd_host
+ * @param io_cfg rt_mmcsd_io_cfg
+ * @retval None
+ */
+static void nu_sdh_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
+{
+ nu_sdh_t NuSdh;
+ rt_uint32_t clk;
+ SDH_T *sdh;
+
+ RT_ASSERT(host);
+ RT_ASSERT(io_cfg);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ sdh = NuSdh->base;
+ clk = io_cfg->clock;
+
+ LOG_D("[%s]clk:%d width(%d):%s%s%s power:%s%s%s",
+ NuSdh->name,
+ clk,
+ io_cfg->bus_width,
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_8 ? "8" : "",
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_4 ? "4" : "",
+ (io_cfg->bus_width) == MMCSD_BUS_WIDTH_1 ? "1" : "",
+ io_cfg->power_mode == MMCSD_POWER_OFF ? "OFF" : "",
+ io_cfg->power_mode == MMCSD_POWER_UP ? "UP" : "",
+ io_cfg->power_mode == MMCSD_POWER_ON ? "ON" : "");
+
+ /* Clock */
+ if (clk > host->freq_max)
+ clk = host->freq_max;
+
+ if (clk < host->freq_min)
+ clk = host->freq_min;
+
+ LOG_D("[%s] ExceptedFreq: %d kHz", NuSdh->name, clk / 1000);
+ if (NuSdh->u32CurClk != (clk / 1000))
+ {
+ SDH_SetClock(sdh, clk / 1000);
+ NuSdh->u32CurClk = (clk / 1000);
+ }
+
+ switch (io_cfg->power_mode)
+ {
+ case MMCSD_POWER_UP:
+ if (clk <= 400000)
+ {
+ /* power ON 74 clock */
+ sdh->CTL |= SDH_CTL_CLK74OEN_Msk;
+
+ while ((sdh->CTL & SDH_CTL_CLK74OEN_Msk) == SDH_CTL_CLK74OEN_Msk)
+ {
+ }
+ }
+ break;
+
+ case MMCSD_POWER_ON:
+ break;
+
+ case MMCSD_POWER_OFF:
+ break;
+
+ default:
+ break;
+ }
+
+ /* Bus width */
+ switch ((io_cfg->bus_width))
+ {
+ case MMCSD_BUS_WIDTH_1:
+ SDH_SetBusWidth(sdh, 1);
+ break;
+
+ case MMCSD_BUS_WIDTH_4:
+ SDH_SetBusWidth(sdh, 4);
+ break;
+
+ case MMCSD_BUS_WIDTH_8:
+ default:
+ break;
+ }
+
+}
+
+/**
+ * @brief This function detect sdcard.
+ * @param host rt_mmcsd_host
+ * @retval card detection status
+ */
+static rt_int32_t nu_sdh_card_detect(struct rt_mmcsd_host *host)
+{
+ nu_sdh_t NuSdh;
+ RT_ASSERT(host);
+
+ NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ LOG_D("try to detect device");
+ return SDH_GetCD(sdh);
+}
+
+static void nu_sdh_isr(nu_sdh_t NuSdh)
+{
+ SDH_T *sdh = NuSdh->base;
+ uint32_t isr = sdh->INTSTS;
+
+ /* card detected */
+ if (isr & SDH_INTSTS_CDIF_Msk)
+ {
+ rt_tick_t cur_tick = rt_tick_get();
+ rt_tick_t diff_tick;
+
+ /* ready to change */
+ if (cur_tick >= NuSdh->LastNotice)
+ diff_tick = (cur_tick - NuSdh->LastNotice);
+ else
+ diff_tick = ((rt_tick_t) -1) - NuSdh->LastNotice + cur_tick;
+
+ if (!NuSdh->LastNotice || (diff_tick > (RT_TICK_PER_SECOND / 5))) // Debounce 200ms
+ {
+ NuSdh->LastNotice = cur_tick;
+ mmcsd_change(NuSdh->host);
+ }
+
+ /* Clear CDIF interrupt flag */
+ SDH_CLR_INT_FLAG(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+}
+
+#if defined(BSP_USING_SDH0)
+void SDH0_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ nu_sdh_isr(&nu_sdh_arr[SDH0_IDX]);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+#if defined(BSP_USING_SDH1)
+void SDH1_IRQHandler(void)
+{
+ /* enter interrupt */
+ rt_interrupt_enter();
+
+ nu_sdh_isr(&nu_sdh_arr[SDH1_IDX]);
+
+ /* leave interrupt */
+ rt_interrupt_leave();
+}
+#endif
+
+
+/**
+ * @brief This function update sdh interrupt.
+ * @param host rt_mmcsd_host
+ * @param enable
+ * @retval None
+ */
+void nu_sdh_irq_update(struct rt_mmcsd_host *host, rt_int32_t enable)
+{
+ nu_sdh_t NuSdh = (nu_sdh_t)host->private_data;
+ SDH_T *sdh = NuSdh->base;
+
+ if (enable)
+ {
+ LOG_D("Enable %s irq", NuSdh->name);
+ SDH_ENABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+ else
+ {
+ LOG_D("Disable %s irq", NuSdh->name);
+ SDH_DISABLE_INT(sdh, SDH_INTSTS_CDIF_Msk);
+ }
+}
+
+static const struct rt_mmcsd_host_ops ops =
+{
+ nu_sdh_request,
+ nu_sdh_iocfg,
+ nu_sdh_card_detect,
+ nu_sdh_irq_update,
+};
+
+/**
+ * @brief This function create mmcsd host.
+ * @param sdh nu_sdh_t
+ * @retval nuvton
+ */
+void nu_sdh_host_init(nu_sdh_t sdh)
+{
+ struct rt_mmcsd_host *host = mmcsd_alloc_host();
+ RT_ASSERT(host);
+
+ /* set host default attributes */
+ host->ops = &ops;
+ host->freq_min = 300 * 1000;
+ host->freq_max = 48 * 1000 * 1000;
+ host->valid_ocr = VDD_26_27 | VDD_27_28 | VDD_28_29 | VDD_29_30 | VDD_30_31 | VDD_31_32 | VDD_32_33 | VDD_33_34;
+
+ host->flags = MMCSD_BUSWIDTH_4 | MMCSD_MUTBLKWRITE | MMCSD_SUP_HIGHSPEED;
+
+ host->max_seg_size = SDH_BUFF_SIZE;
+ host->max_dma_segs = 1;
+ host->max_blk_size = SDH_BLOCK_SIZE;
+ host->max_blk_count = (SDH_BUFF_SIZE / SDH_BLOCK_SIZE);
+
+ /* link up host and sdio */
+ host->private_data = sdh;
+ sdh->host = host;
+
+ /* Enable DMA engine at first. */
+ SDH_Enable(sdh->base);
+
+ /* Install ISR. */
+ NVIC_EnableIRQ(sdh->irqn);
+
+ /* ready to change */
+ //mmcsd_change(host);
+}
+
+static int rt_hw_sdh_init(void)
+{
+ int i;
+
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ CLK_EnableModuleClock(nu_sdh_arr[i].modid);
+ SYS_ResetModule(nu_sdh_arr[i].rstidx);
+
+ nu_sdh_host_init(&nu_sdh_arr[i]);
+ }
+
+ return 0;
+}
+INIT_DEVICE_EXPORT(rt_hw_sdh_init);
+
+void nu_sd_attach(void)
+{
+ int i;
+ /* ready to change */
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ if (nu_sdh_arr[i].host)
+ mmcsd_change(nu_sdh_arr[i].host);
+ }
+}
+MSH_CMD_EXPORT(nu_sd_attach, attach card);
+
+void nu_sd_regdump(void)
+{
+ int i;
+ for (i = (SDH_START + 1); i < SDH_CNT; i++)
+ {
+ if (nu_sdh_arr[i].host)
+ LOG_HEX("sdh_reg", 16, (void *)nu_sdh_arr[i].base, sizeof(SDH_T));
+ }
+}
+MSH_CMD_EXPORT(nu_sd_regdump, dump sdh registers);
+
+#endif
diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript b/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript
new file mode 100644
index 0000000000..9534778e6f
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/FSA506/SConscript
@@ -0,0 +1,19 @@
+Import('RTT_ROOT')
+from building import *
+
+cwd = GetCurrentDir()
+group = []
+
+src = Split("""
+lcd_fsa506.c
+""")
+CPPPATH = [cwd]
+
+if GetDepend('NU_PKG_USING_FSA506_EBI'):
+ src += Glob('fsa506_ebi.c')
+
+if GetDepend('NU_PKG_USING_FSA506'):
+ group = DefineGroup('nu_pkgs_fsa506', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
+
diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c b/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c
new file mode 100644
index 0000000000..de851408a9
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/FSA506/fsa506_ebi.c
@@ -0,0 +1,124 @@
+/**************************************************************************//**
+*
+* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
+*
+* SPDX-License-Identifier: Apache-2.0
+*
+* Change Logs:
+* Date Author Notes
+* 2022-2-23 Wayne First version
+*
+******************************************************************************/
+
+#include <rtconfig.h>
+
+#if defined(NU_PKG_USING_FSA506_EBI)
+
+#include <lcd_fsa506.h>
+#include "drv_pdma.h"
+
+#define FSA506_ADDR_CMD 0x0
+#define FSA506_ADDR_DATA 0x0
+
+#if defined(FSA506_EBI_16BIT)
+ #define fsa506_reg_write(RegAddr) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr))
+ #define fsa506_read_data() (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))))
+ #define fsa506_write_data(Data) (*((volatile uint16_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data))
+#else
+ #define fsa506_reg_write(RegAddr) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_CMD))) = (RegAddr))
+ #define fsa506_read_data() (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))))
+ #define fsa506_write_data(Data) (*((volatile uint8_t *)(s_u32AccessBase+(FSA506_ADDR_DATA))) = (Data))
+#endif
+
+static rt_uint32_t s_u32AccessBase = 0;
+
+void fsa506_send_cmd(rt_uint8_t cmd)
+{
+ CLR_RS;
+ fsa506_reg_write(cmd);
+ SET_RS;
+}
+
+void fsa506_send_cmd_parameter(rt_uint8_t data)
+{
+ fsa506_write_data(data);
+}
+
+void fsa506_send_cmd_done(void)
+{
+ CLR_RS;
+ fsa506_reg_write(0x80);
+ SET_RS;
+}
+
+void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data)
+{
+ fsa506_send_cmd(cmd);
+ fsa506_send_cmd_parameter(data);
+ fsa506_send_cmd_done();
+}
+
+void fsa506_send_pixel_data(rt_uint16_t color)
+{
+#if 1
+ // for LV_COLOR_16_SWAP
+ //BGR, B is high byte
+ fsa506_write_data(color & 0xffu);
+ fsa506_write_data((color >> 8) & 0xffu);
+#else
+ //RGB, R is high byte
+ fsa506_write_data((color >> 8) & 0xffu);
+ fsa506_write_data(color & 0xffu);
+#endif
+}
+
+void fsa506_send_pixels(rt_uint16_t *pixels, int len)
+{
+ int count = len / sizeof(rt_uint16_t);
+ if (count < 512)
+ {
+ // CPU feed
+ int i = 0;
+ while (i < count)
+ {
+ fsa506_send_pixel_data(pixels[i]);
+ i++;
+ }
+ }
+ else
+ {
+ // PDMA-M2M feed
+ // Must enable LV_COLOR_16_SWAP definition in LVGL.
+ nu_pdma_mempush((void *)(s_u32AccessBase + (FSA506_ADDR_DATA)), (void *)pixels, 8, len);
+ }
+}
+
+void fsa506_set_column(uint16_t StartCol, uint16_t EndCol)
+{
+ fsa506_write_reg(0x0, (StartCol >> 8) & 0xFF);
+ fsa506_write_reg(0x1, StartCol & 0xFF);
+ fsa506_write_reg(0x2, (EndCol >> 8) & 0xFF);
+ fsa506_write_reg(0x3, EndCol & 0xFF);
+}
+
+void fsa506_set_page(uint16_t StartPage, uint16_t EndPage)
+{
+ fsa506_write_reg(0x4, (StartPage >> 8) & 0xFF);
+ fsa506_write_reg(0x5, StartPage & 0xFF);
+ fsa506_write_reg(0x6, (EndPage >> 8) & 0xFF);
+ fsa506_write_reg(0x7, EndPage & 0xFF);
+}
+
+void fsa506_lcd_get_pixel(char *color, int x, int y)
+{
+ //Not supported.
+ return;
+}
+
+rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t fsa506_base)
+{
+ s_u32AccessBase = fsa506_base;
+ return RT_EOK;
+}
+
+#endif /* if defined(NU_PKG_USING_FSA506_EBI) */
diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c
new file mode 100644
index 0000000000..d172d7da6a
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.c
@@ -0,0 +1,392 @@
+/**************************************************************************//**
+*
+* @copyright (C) 2020 Nuvoton Technology Corp. All rights reserved.
+*
+* SPDX-License-Identifier: Apache-2.0
+*
+* Change Logs:
+* Date Author Notes
+* 2022-2-23 Wayne First version
+*
+******************************************************************************/
+
+#include <rtconfig.h>
+
+#if defined(NU_PKG_USING_FSA506)
+
+#include <rtdevice.h>
+#include <lcd_fsa506.h>
+
+#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
+ #if !defined(NU_PKG_FSA506_LINE_BUFFER_NUMBER)
+ #define NU_PKG_FSA506_LINE_BUFFER_NUMBER YSIZE_PHYS
+ #endif
+#endif
+
+#define fsa506_delay_ms(ms) rt_thread_mdelay(ms)
+static void fsa506_fillscreen(rt_uint16_t color);
+
+static struct rt_device_graphic_info g_FSA506Info =
+{
+ .bits_per_pixel = 16,
+ .pixel_format = RTGRAPHIC_PIXEL_FORMAT_RGB565,
+ .framebuffer = RT_NULL,
+ .width = XSIZE_PHYS,
+ .pitch = XSIZE_PHYS * 2,
+ .height = YSIZE_PHYS
+};
+
+static rt_err_t fsa506_pin_init(void)
+{
+ rt_pin_mode(BOARD_USING_FSA506_PIN_DC, PIN_MODE_OUTPUT);
+ rt_pin_mode(BOARD_USING_FSA506_PIN_RESET, PIN_MODE_OUTPUT);
+ rt_pin_mode(BOARD_USING_FSA506_PIN_BACKLIGHT, PIN_MODE_OUTPUT);
+ rt_pin_mode(BOARD_USING_FSA506_PIN_DISPLAY, PIN_MODE_OUTPUT);
+
+ CLR_RS;
+ CLR_RST;
+ SET_BACKLIGHT_OFF;
+ SET_DISP_OFF;
+
+ return RT_EOK;
+}
+
+static rt_err_t fsa506_lcd_init(rt_device_t dev)
+{
+ /* Hardware reset */
+ CLR_RST;
+ fsa506_delay_ms(100); // Delay 100ms
+
+ SET_RST;
+ fsa506_delay_ms(100); // Delay 100ms
+
+ fsa506_write_reg(0x40, 0x12); // Underspece
+ fsa506_write_reg(0x41, 0x05); // Underspece
+ fsa506_write_reg(0x42, 0x06); // Underspece
+
+ /* Set the panel X size */
+ fsa506_write_reg(0x08, (uint8_t)(XSIZE_PHYS >> 8)); //Set the panel X size H[1.0]
+ fsa506_write_reg(0x09, (uint8_t)(XSIZE_PHYS)); //Set the panel X size L[7:0]
+
+ /* Memory write start address */
+ fsa506_write_reg(0x0a, 0x00); //[17:16] bits of memory write start address
+ fsa506_write_reg(0x0b, 0x00); //[15:8] bits of memory write start address
+ fsa506_write_reg(0x0c, 0x00); //[7:0] bits of memory write start address
+
+ /* Clock & format */
+ fsa506_write_reg(0x10, 0x0D); //[0-1] : 20MHz, [2]: Parallel panel, [3]: Normal operation
+ fsa506_write_reg(0x11, 0x05); //[3-5]: RGB, [0-2]BGR
+
+ /* For TFT output timing adjust */
+ fsa506_write_reg(0x12, 0x00); //Hsync start position H-Byte
+ fsa506_write_reg(0x13, 0x00); //Hsync start position L-Byte
+ fsa506_write_reg(0x14, (uint8_t)(41 >> 8)); //Hsync pulse width H-Byte
+ fsa506_write_reg(0x15, (uint8_t)(41)); //Hsync pulse width L-Byte
+
+ fsa506_write_reg(0x16, (uint8_t)(43 >> 8)); //DE pulse start position H-Byte
+ fsa506_write_reg(0x17, (uint8_t)(43)); //DE pulse start position L-Byte
+ fsa506_write_reg(0x18, (uint8_t)(XSIZE_PHYS >> 8)); //DE pulse width H-Byte
+ fsa506_write_reg(0x19, (uint8_t)(XSIZE_PHYS)); //DE pulse width L-Byte
+ fsa506_write_reg(0x1a, (uint8_t)(525 >> 8)); //Hsync total clocks H-Byte
+ fsa506_write_reg(0x1b, (uint8_t)(525)); //Hsync total clocks H-Byte
+ fsa506_write_reg(0x1c, 0x00); //Vsync start position H-Byte
+ fsa506_write_reg(0x1d, 0x00); //Vsync start position L-Byte
+ fsa506_write_reg(0x1e, (uint8_t)(10 >> 8)); //Vsync pulse width H-Byte
+ fsa506_write_reg(0x1f, (uint8_t)(10)); //Vsync pulse width L-Byte
+ fsa506_write_reg(0x20, (uint8_t)(12 >> 8)); //Vertical DE pulse start position H-Byte
+ fsa506_write_reg(0x21, (uint8_t)(12)); //Vertical DE pulse start position L-Byte
+ fsa506_write_reg(0x22, (uint8_t)(YSIZE_PHYS >> 8)); //Vertical Active width H-Byte
+ fsa506_write_reg(0x23, (uint8_t)(YSIZE_PHYS)); //Vertical Active width H-Byte
+ fsa506_write_reg(0x24, (uint8_t)(286 >> 8)); //Vertical total width H-Byte
+ fsa506_write_reg(0x25, (uint8_t)(286)); //Vertical total width L-Byte
+
+ fsa506_write_reg(0x26, 0x00); //Memory read start address
+ fsa506_write_reg(0x27, 0x00); //Memory read start address
+ fsa506_write_reg(0x28, 0x00); //Memory read start address
+ fsa506_write_reg(0x29, 0x01); //[0] Load output timing related setting (H sync., V sync. and DE) to take effect
+
+ //[7:4] Reserved
+ //[3] Output pin X_DCON level control
+ //[2] Output clock inversion 0: Normal 1: Inverse
+ //[1:0] Image rotate
+ // 00: 0? 01: 90? 10: 270?11: 180?
+ fsa506_write_reg(0x2d, (1 << 2) | 0x08);
+
+ /* Set the Horizontal offset */
+ fsa506_write_reg(0x30, 0x00); //_H byte H-Offset[3:0]
+ fsa506_write_reg(0x31, 0x00); //_L byte H-Offset[7:0]
+ fsa506_write_reg(0x32, 0x00); //_H byte V-Offset[3:0]
+ fsa506_write_reg(0x33, 0x00); //_L byte V-Offset[7:0]
+ fsa506_write_reg(0x34, (uint8_t)(XSIZE_PHYS >> 8)); //H byte H-def[3:0]
+ fsa506_write_reg(0x35, (uint8_t)(XSIZE_PHYS)); //_L byte H-def[7:0]
+ fsa506_write_reg(0x36, (uint8_t)((2 * YSIZE_PHYS) >> 8)); //[3:0] MSB of image vertical physical resolution in memory
+ fsa506_write_reg(0x37, (uint8_t)(2 * YSIZE_PHYS)); //[7:0] LSB of image vertical physical resolution in memory
+
+ fsa506_fillscreen(0);
+
+ SET_DISP_ON;
+
+ SET_BACKLIGHT_ON;
+
+ return RT_EOK;
+}
+
+#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
+static void fsa506_fillrect(uint16_t *pixels, struct rt_device_rect_info *pRectInfo)
+{
+ fsa506_set_column(pRectInfo->x, pRectInfo->x + pRectInfo->width - 1);
+ fsa506_set_page(pRectInfo->y, pRectInfo->y + pRectInfo->height - 1);
+
+ fsa506_send_cmd(0xC1);
+ fsa506_send_pixels(pixels, pRectInfo->height * pRectInfo->width * 2);
+ fsa506_send_cmd_done();
+}
+#endif
+
+static void fsa506_fillscreen(rt_uint16_t color)
+{
+#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
+ struct rt_device_rect_info rectinfo;
+ int filled_line_num = 0;
+
+ while (filled_line_num < YSIZE_PHYS)
+ {
+ int pixel_count;
+ rectinfo.x = 0;
+ rectinfo.y = filled_line_num;
+ rectinfo.width = XSIZE_PHYS;
+ rectinfo.height = (NU_PKG_FSA506_LINE_BUFFER_NUMBER < YSIZE_PHYS) ? NU_PKG_FSA506_LINE_BUFFER_NUMBER : YSIZE_PHYS;
+
+ pixel_count = XSIZE_PHYS * NU_PKG_FSA506_LINE_BUFFER_NUMBER;
+ rt_uint16_t *pu16ShadowBuf = (rt_uint16_t *)g_FSA506Info.framebuffer;
+
+ while (pixel_count > 0)
+ {
+ *pu16ShadowBuf++ = color;
+ pixel_count--;
+ }
+ fsa506_fillrect((uint16_t *)g_FSA506Info.framebuffer, &rectinfo);
+ filled_line_num += NU_PKG_FSA506_LINE_BUFFER_NUMBER;
+ }
+#else
+ fsa506_set_column(0, (XSIZE_PHYS - 1));
+ fsa506_set_page(0, (YSIZE_PHYS - 1));
+
+ fsa506_send_cmd(0xC1);
+ for (int i = 0; i < (XSIZE_PHYS * YSIZE_PHYS); i++)
+ fsa506_send_pixel_data(color);
+ fsa506_send_cmd_done();
+#endif
+}
+
+static void fsa506_lcd_set_pixel(const char *color, int x, int y)
+{
+ fsa506_set_column(x, x);
+ fsa506_set_page(y, y);
+
+ fsa506_send_cmd(0xC1);
+ fsa506_send_pixel_data(*(uint16_t *)color);
+ fsa506_send_cmd_done();
+}
+
+static void fsa506_lcd_draw_hline(const char *pixel, int x1, int x2, int y)
+{
+ fsa506_set_column(x1, x2);
+ fsa506_set_page(y, y);
+
+ fsa506_send_cmd(0xC1);
+ for (; x1 < x2; x1++)
+ fsa506_send_pixel_data(*(uint16_t *)pixel);
+ fsa506_send_cmd_done();
+}
+
+static void fsa506_lcd_draw_vline(const char *pixel, int x, int y1, int y2)
+{
+ fsa506_set_column(x, x);
+ fsa506_set_page(y1, y2);
+
+ fsa506_send_cmd(0xC1);
+ for (; y1 < y2; y1++)
+ fsa506_send_pixel_data(*(uint16_t *)pixel);
+ fsa506_send_cmd_done();
+}
+
+static void fsa506_lcd_blit_line(const char *pixels, int x, int y, rt_size_t size)
+{
+ rt_uint16_t *ptr = (rt_uint16_t *)pixels;
+
+ fsa506_set_column(x, x + size);
+ fsa506_set_page(y, y);
+
+ fsa506_send_cmd(0xC1);
+ while (size--)
+ fsa506_send_pixel_data(*ptr++);
+ fsa506_send_cmd_done();
+}
+
+static rt_err_t fsa506_lcd_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ return RT_EOK;
+}
+
+static rt_err_t fsa506_lcd_close(rt_device_t dev)
+{
+ return RT_EOK;
+}
+
+static rt_err_t fsa506_lcd_control(rt_device_t dev, int cmd, void *args)
+{
+ switch (cmd)
+ {
+ case RTGRAPHIC_CTRL_GET_INFO:
+ {
+ struct rt_device_graphic_info *info;
+
+ info = (struct rt_device_graphic_info *) args;
+ RT_ASSERT(info != RT_NULL);
+ rt_memcpy(args, (void *)&g_FSA506Info, sizeof(struct rt_device_graphic_info));
+ }
+ break;
+
+ case RTGRAPHIC_CTRL_RECT_UPDATE:
+ {
+#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
+ struct rt_device_rect_info *psRectInfo = (struct rt_device_rect_info *)args;
+ rt_uint16_t *pixels = (rt_uint16_t *)g_FSA506Info.framebuffer;
+ RT_ASSERT(args);
+
+ fsa506_fillrect(pixels, psRectInfo);
+#else
+ /* nothong to be done */
+#endif
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return RT_EOK;
+}
+
+static struct rt_device lcd_device;
+static struct rt_device_graphic_ops fsa506_ops =
+{
+ fsa506_lcd_set_pixel,
+ fsa506_lcd_get_pixel,
+ fsa506_lcd_draw_hline,
+ fsa506_lcd_draw_vline,
+ fsa506_lcd_blit_line
+};
+
+int rt_hw_lcd_fsa506_init(void)
+{
+ fsa506_pin_init();
+
+ /* register lcd device */
+ lcd_device.type = RT_Device_Class_Graphic;
+ lcd_device.init = fsa506_lcd_init;
+ lcd_device.open = fsa506_lcd_open;
+ lcd_device.close = fsa506_lcd_close;
+ lcd_device.control = fsa506_lcd_control;
+ lcd_device.read = RT_NULL;
+ lcd_device.write = RT_NULL;
+
+ lcd_device.user_data = &fsa506_ops;
+
+#if defined(NU_PKG_FSA506_WITH_OFFSCREEN_FRAMEBUFFER)
+ g_FSA506Info.framebuffer = rt_malloc_align((g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER) + 32, 32);
+ RT_ASSERT(g_FSA506Info.framebuffer != RT_NULL);
+ g_FSA506Info.smem_len = g_FSA506Info.pitch * NU_PKG_FSA506_LINE_BUFFER_NUMBER;
+#endif
+
+ /* register graphic device driver */
+ rt_device_register(&lcd_device, "lcd", RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_STANDALONE);
+
+ return 0;
+}
+
+#ifdef RT_USING_FINSH
+#define LINE_LEN 32
+static void lcd_test(int argc, char *argv[])
+{
+ uint16_t pixels[LINE_LEN];
+ uint16_t color;
+ int x, y, i;
+ x = y = 100;
+
+ fsa506_lcd_init(NULL);
+
+ color = 0x0; //Black, RGB
+ rt_kprintf("Brush 0x%X on screen.\n", color);
+ fsa506_fillscreen(color);
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ color = 0xffff; //White, RGB
+ rt_kprintf("Brush 0x%X on screen.\n", color);
+ fsa506_fillscreen(color);
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ color = 0x1f; //Blue, RGB
+ rt_kprintf("Brush 0x%X on screen.\n", color);
+ fsa506_fillscreen(color);
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ color = 0x07e0; //Green, RGB
+ rt_kprintf("Brush 0x%X on screen.\n", color);
+ fsa506_fillscreen(color);
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ color = 0xf800; //Red, RGB
+ rt_kprintf("Brush 0x%X on screen.\n", color);
+ fsa506_fillscreen(color);
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ color = 0xffff; //White, RGB
+ rt_kprintf("lcd draw hline, pixel: 0x%X, x1: %d, x2: %d, y: %d\n", color, x, x + 20, y);
+ fsa506_lcd_draw_hline((const char *)&color, x, x + 20, y);
+
+ color = 0xffff; //White, RGB
+ rt_kprintf("lcd draw vline, pixel: 0x%X, x: %d, y: %d\n", color, y, y + 20);
+ fsa506_lcd_draw_vline((const char *)&color, x, y, y + 20);
+
+ for (i = 0; i < LINE_LEN; i++)
+ pixels[i] = 20 + i * 5;
+
+ x = y = 50;
+ rt_kprintf("lcd blit line, start: x: %d, y: %d\n", x, y);
+ fsa506_lcd_blit_line((const char *)&pixels[0], x, y, LINE_LEN);
+
+ x = y = 200;
+ color = 0x07E0; //Green, RGB
+ rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+ fsa506_lcd_set_pixel((const char *)&color, x, y);
+ color = 0x0;
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ x = y = 200;
+ color = 0x1f; //Blue, RGB
+ rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+ fsa506_lcd_set_pixel((const char *)&color, x, y);
+ color = 0x0;
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+
+ x = y = 200;
+ color = 0xf800; //Red, RGB
+ rt_kprintf("lcd set pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+ fsa506_lcd_set_pixel((const char *)&color, x, y);
+ color = 0x0;
+ fsa506_lcd_get_pixel((char *)&color, x, y);
+ rt_kprintf("lcd get pixel, pixel: 0x%X, x: %d, y: %d\n", color, x, y);
+}
+MSH_CMD_EXPORT(lcd_test, test lcd display);
+#endif
+
+#endif /* if defined(NU_PKG_USING_FSA506) */
diff --git a/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h
new file mode 100644
index 0000000000..42bd117801
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/FSA506/lcd_fsa506.h
@@ -0,0 +1,57 @@
+/**************************************************************************//**
+*
+* @copyright (C) 2019 Nuvoton Technology Corp. All rights reserved.
+*
+* SPDX-License-Identifier: Apache-2.0
+*
+* Change Logs:
+* Date Author Notes
+* 2022-2-23 Wayne First version
+*
+******************************************************************************/
+
+#ifndef __LCD_FSA506_H__
+#define __LCD_FSA506_H__
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#define SET_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 1)
+#define CLR_RS rt_pin_write(BOARD_USING_FSA506_PIN_DC, 0)
+
+#define SET_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 1)
+#define CLR_RST rt_pin_write(BOARD_USING_FSA506_PIN_RESET, 0)
+
+#define SET_BACKLIGHT_ON rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 1)
+#define SET_BACKLIGHT_OFF rt_pin_write(BOARD_USING_FSA506_PIN_BACKLIGHT, 0)
+
+#define SET_DISP_ON rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 1)
+#define SET_DISP_OFF rt_pin_write(BOARD_USING_FSA506_PIN_DISPLAY, 0)
+
+//
+// Physical display size
+//
+//#if defined(NU_PKG_FSA506_HORIZONTAL)
+#define XSIZE_PHYS 480
+#define YSIZE_PHYS 272
+//#else
+// #define XSIZE_PHYS 272
+// #define YSIZE_PHYS 480
+//#endif
+
+int rt_hw_lcd_fsa506_init(void);
+void fsa506_send_cmd(rt_uint8_t cmd);
+void fsa506_send_cmd_parameter(rt_uint8_t data);
+void fsa506_send_cmd_done(void);
+void fsa506_write_reg(rt_uint8_t cmd, rt_uint8_t data);
+void fsa506_set_column(rt_uint16_t StartCol, rt_uint16_t EndCol);
+void fsa506_set_page(rt_uint16_t StartPage, rt_uint16_t EndPage);
+void fsa506_send_pixel_data(rt_uint16_t color);
+void fsa506_lcd_get_pixel(char *color, int x, int y);
+void fsa506_send_pixels(rt_uint16_t *pixels, int len);
+
+#if defined(NU_PKG_USING_FSA506_EBI)
+ rt_err_t rt_hw_lcd_fsa506_ebi_init(rt_uint32_t ebi_base);
+#endif
+
+#endif /* __LCD_FSA506_H__ */
diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c
new file mode 100644
index 0000000000..f600f1bbd6
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.c
@@ -0,0 +1,342 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-04-11 Wayne First version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <string.h>
+
+#define DBG_TAG "ft5446"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "ft5446.h"
+
+static struct rt_i2c_client ft5446_client;
+static void ft5446_touch_up(void *buf, rt_int8_t id);
+static rt_err_t ft5446_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value)
+{
+ struct rt_i2c_msg msgs;
+ rt_uint8_t buf[2];
+
+ buf[0] = reg;
+ buf[1] = value;
+
+ msgs.addr = dev->client_addr;
+ msgs.flags = RT_I2C_WR;
+ msgs.buf = buf;
+ msgs.len = sizeof(buf);
+
+ if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static rt_err_t ft5446_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len)
+{
+ struct rt_i2c_msg msgs[2];
+
+ msgs[0].addr = dev->client_addr;
+ msgs[0].flags = RT_I2C_WR;
+ msgs[0].buf = &reg;
+ msgs[0].len = FT_REGITER_LEN;
+
+ msgs[1].addr = dev->client_addr;
+ msgs[1].flags = RT_I2C_RD;
+ msgs[1].buf = data;
+ msgs[1].len = len;
+
+ if (rt_i2c_transfer(dev->bus, msgs, 2) == 2)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static rt_int16_t pre_x[FT_MAX_TOUCH];
+static rt_int16_t pre_y[FT_MAX_TOUCH];
+static rt_int16_t pre_w[FT_MAX_TOUCH];
+static rt_uint8_t s_tp_dowm[FT_MAX_TOUCH];
+
+static void ft5446_touch_up(void *buf, rt_int8_t id)
+{
+ struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
+
+ if (s_tp_dowm[id] == 1)
+ {
+ s_tp_dowm[id] = 0;
+ read_data[id].event = RT_TOUCH_EVENT_UP;
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_NONE;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = pre_w[id];
+ read_data[id].x_coordinate = pre_x[id];
+ read_data[id].y_coordinate = pre_y[id];
+ read_data[id].track_id = id;
+
+ pre_x[id] = -1; /* last point is none */
+ pre_y[id] = -1;
+ pre_w[id] = -1;
+
+ //LOG_I("%s (%d)\n", __func__, id);
+}
+
+static void ft5446_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w)
+{
+ struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
+
+ if (s_tp_dowm[id] == 1)
+ {
+ read_data[id].event = RT_TOUCH_EVENT_MOVE;
+
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_DOWN;
+ s_tp_dowm[id] = 1;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = w;
+ read_data[id].x_coordinate = x;
+ read_data[id].y_coordinate = y;
+ read_data[id].track_id = id;
+
+ pre_x[id] = x; /* save last point */
+ pre_y[id] = y;
+ pre_w[id] = w;
+
+ //LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w );
+}
+
+static int8_t pre_id[FT_MAX_TOUCH];
+static S_FT_REGMAP sFtRegMap;
+static rt_uint8_t pre_touch = 0;
+
+static rt_size_t ft5446_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num)
+{
+ int i;
+
+ rt_err_t error = 0;
+ rt_int32_t touch_event, touchid;
+
+ RT_ASSERT(touch);
+ RT_ASSERT(buf);
+ RT_ASSERT(read_num != 0);
+ RT_ASSERT(read_num <= FT_MAX_TOUCH);
+
+ error = ft5446_read_reg(&ft5446_client, 0, (rt_uint8_t *)&sFtRegMap, sizeof(sFtRegMap));
+ if (error)
+ {
+ LOG_E("get touch data failed, err:%d\n", error);
+ goto exit_read_point;
+ }
+
+ if (sFtRegMap.u8TDStatus > FT_MAX_TOUCH)
+ {
+ LOG_E("FW report max point:%d > panel info. max:%d\n", sFtRegMap.u8TDStatus, FT_MAX_TOUCH);
+ goto exit_read_point;
+ }
+
+ if (pre_touch > sFtRegMap.u8TDStatus) /* point up */
+ {
+ for (i = 0; i < FT_MAX_TOUCH; i++)
+ {
+ rt_uint8_t j;
+ for (j = 0; j < sFtRegMap.u8TDStatus; j++) /* this time touch num */
+ {
+ touchid = sFtRegMap.m_sTP[j].u8TouchID;
+ if (touchid >= 0x0f)
+ continue;
+
+ if (pre_id[i] == touchid) /* this id is not free */
+ break;
+ }
+
+ if ((j == sFtRegMap.u8TDStatus) && (pre_id[i] != -1)) /* free this node */
+ {
+ // LOG_I("free %d tid=%d\n", i, pre_id[i]);
+ ft5446_touch_up(buf, pre_id[i]);
+ pre_id[i] = -1;
+ }
+ }
+ }
+
+ for (i = 0; i < sFtRegMap.u8TDStatus; i++)
+ {
+ touch_event = sFtRegMap.m_sTP[i].u8EvtFlag;
+ touchid = sFtRegMap.m_sTP[i].u8TouchID;
+
+ //LOG_I("(%d/%d) %d %d\n", i, sFtRegMap.u8TDStatus, touchid, touch_event );
+
+ if (touchid >= 0x0f)
+ continue;
+
+ pre_id[i] = touchid;
+
+ if ((touch_event == FT_EVENTFLAG_PRESS_DOWN) || (touch_event == FT_EVENTFLAG_CONTACT))
+ {
+ rt_uint16_t x, y, w;
+
+ x = ((uint16_t)sFtRegMap.m_sTP[i].u8X_11_8 << 8) | sFtRegMap.m_sTP[i].u8X_7_0;
+ y = ((uint16_t)sFtRegMap.m_sTP[i].u8Y_11_8 << 8) | sFtRegMap.m_sTP[i].u8Y_7_0;
+ w = sFtRegMap.m_sTP[i].m_u8Weight;
+
+ //LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w );
+
+ if (x >= touch->info.range_x || y >= touch->info.range_y)
+ {
+ LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
+ 0, x, touch->info.range_x,
+ 0, y, touch->info.range_y);
+ continue;
+ }
+
+ ft5446_touch_down(buf, touchid, x, y, w);
+ }
+ else
+ {
+ // Up
+ ft5446_touch_up(buf, touchid);
+ }
+
+ } // for (i = 0; i < sFtRegMap.u8TDStatus; i++)
+
+ pre_touch = sFtRegMap.u8TDStatus;
+
+ return read_num;
+
+exit_read_point:
+
+ pre_touch = 0;
+
+ return 0;
+}
+
+static rt_err_t ft5446_control(struct rt_touch_device *touch, int cmd, void *arg)
+{
+ switch (cmd)
+ {
+ case RT_TOUCH_CTRL_GET_INFO:
+ {
+ struct rt_touch_info *info = (struct rt_touch_info *)arg;
+ RT_ASSERT(arg);
+
+ rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info));
+ break;
+ }
+ case RT_TOUCH_CTRL_GET_ID:
+ break;
+ case RT_TOUCH_CTRL_SET_X_RANGE:
+ break;
+ case RT_TOUCH_CTRL_SET_Y_RANGE:
+ break;
+ case RT_TOUCH_CTRL_SET_X_TO_Y:
+ break;
+ case RT_TOUCH_CTRL_SET_MODE:
+ break;
+ default:
+ break;
+ }
+ return RT_EOK;
+}
+
+static struct rt_touch_ops ft5446_touch_ops =
+{
+ .touch_readpoint = ft5446_read_point,
+ .touch_control = ft5446_control,
+};
+
+static void ft5446_init(struct rt_i2c_client *dev)
+{
+ ft5446_write_reg(dev, 0x0, 0);
+}
+
+int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg)
+{
+ struct rt_touch_device *touch_device = RT_NULL;
+ rt_uint32_t bus_speed = 400000;
+
+ touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device));
+ if (touch_device == RT_NULL)
+ {
+ LOG_E("touch device malloc fail");
+ return -RT_ERROR;
+ }
+ rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device));
+
+ /* hw init*/
+ rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT);
+
+ rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW);
+ rt_thread_delay(5);
+ rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH);
+ rt_thread_delay(200);
+
+ rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode);
+
+ ft5446_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
+
+ if (ft5446_client.bus == RT_NULL)
+ {
+ LOG_E("Can't find %s device", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ if (rt_device_open((rt_device_t)ft5446_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
+ {
+ LOG_E("open %s device failed", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ if (rt_device_control((rt_device_t)ft5446_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK)
+ {
+ LOG_E("control %s device failed", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ ft5446_client.client_addr = FT5446_ADDRESS;
+
+ ft5446_init(&ft5446_client);
+
+ rt_memset(&pre_x[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_y[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_w[0], 0xff, FT_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&s_tp_dowm[0], 0, FT_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_id[0], 0xff, FT_MAX_TOUCH * sizeof(int8_t));
+
+ /* register touch device */
+ touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE;
+ touch_device->info.vendor = RT_TOUCH_VENDOR_FT;
+ touch_device->info.range_x = BSP_LCD_WIDTH;
+ touch_device->info.range_y = BSP_LCD_HEIGHT;
+ touch_device->info.point_num = FT_MAX_TOUCH;
+
+ rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config));
+ touch_device->ops = &ft5446_touch_ops;
+
+ rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL);
+
+ LOG_I("touch device ft5446 init success");
+
+ return RT_EOK;
+}
diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h
new file mode 100644
index 0000000000..e662ad5156
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/TPC/ft5446.h
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-04-11 Wayne the first version
+ */
+
+#ifndef __FT5446_H__
+#define __FT5446_H__
+
+#include "touch.h"
+
+#define FT_REGITER_LEN 1
+#define FT_MAX_TOUCH 5
+#define FT5446_ADDRESS 0x38
+
+#pragma anon_unions
+
+typedef struct
+{
+ //03H
+ union
+ {
+ uint8_t m_u8XH;
+ struct
+ {
+ uint8_t u8X_11_8: 4;
+ uint8_t : 2;
+ uint8_t u8EvtFlag: 2;
+#define FT_EVENTFLAG_PRESS_DOWN 0x0
+#define FT_EVENTFLAG_LIFT_UP 0x1
+#define FT_EVENTFLAG_CONTACT 0x2
+#define FT_EVENTFLAG_NONE 0x3
+ };
+ };
+
+ //04H
+ union
+ {
+ uint8_t m_u8XL;
+ struct
+ {
+ uint8_t u8X_7_0;
+ };
+ };
+
+ //05H
+ union
+ {
+ uint8_t m_u8YH;
+ struct
+ {
+ uint8_t u8Y_11_8: 4;
+ uint8_t u8TouchID: 4; /* Touch ID of Touch Point, this value is 0x0F when the ID is invalid */
+ };
+ };
+
+ //06H
+ union
+ {
+ uint8_t m_u8YL;
+ struct
+ {
+ uint8_t u8Y_7_0;
+ };
+ };
+
+ //07H
+ uint8_t m_u8Weight; /* Touch pressure value */
+
+ //08H
+ union
+ {
+ uint8_t m_u8Misc;
+ struct
+ {
+ uint8_t : 4;
+ uint8_t u8TouchArea: 4; /* Touch area value */
+ };
+ };
+
+} S_FT_TP;
+
+#pragma pack(push)
+#pragma pack(4)
+
+typedef struct
+{
+ union
+ {
+ uint8_t m_u8ModeSwitch;
+#define FT_DEVICE_MODE_WORKING 0x0
+#define FT_DEVICE_MODE_TEST 0x4
+
+ struct
+ {
+ uint8_t : 4;
+ uint8_t u8DevMode: 3;
+ uint8_t : 1;
+ };
+ };
+
+ uint8_t m_u8Guesture;
+#define FT_GESTURE_ID_MOVE_UP 0x10
+#define FT_GESTURE_ID_MOVE_RIGHT 0x14
+#define FT_GESTURE_ID_MOVE_DOWN 0x18
+#define FT_GESTURE_ID_MOVE_LEFT 0x1C
+#define FT_GESTURE_ID_MOVE_IN 0x48
+#define FT_GESTURE_ID_MOVE_OUT 0x49
+#define FT_GESTURE_ID_MOVE_NONE 0x00
+
+ union
+ {
+ uint8_t m_u8Status;
+
+ struct
+ {
+ uint8_t u8TDStatus: 4;
+ uint8_t : 4;
+ };
+ };
+
+ S_FT_TP m_sTP[FT_MAX_TOUCH];
+
+} S_FT_REGMAP;
+#pragma pack(pop)
+
+int rt_hw_ft5446_init(const char *name, struct rt_touch_config *cfg);
+
+#endif /* __FT5446_H__ */
diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c
new file mode 100644
index 0000000000..3b516dc7a4
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.c
@@ -0,0 +1,337 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-04-11 Wayne First version
+ */
+
+#include <rtthread.h>
+#include <rtdevice.h>
+
+#include <string.h>
+
+#define DBG_TAG "st1663i"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+#include "st1663i.h"
+
+static struct rt_i2c_client st1663i_client;
+static void st1663i_touch_up(void *buf, rt_int8_t id);
+static rt_err_t st1663i_write_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t value)
+{
+ struct rt_i2c_msg msgs;
+ rt_uint8_t buf[2];
+
+ buf[0] = reg;
+ buf[1] = value;
+
+ msgs.addr = dev->client_addr;
+ msgs.flags = RT_I2C_WR;
+ msgs.buf = buf;
+ msgs.len = sizeof(buf);
+
+ if (rt_i2c_transfer(dev->bus, &msgs, 1) == 1)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static rt_err_t st1663i_read_reg(struct rt_i2c_client *dev, rt_uint8_t reg, rt_uint8_t *data, rt_uint8_t len)
+{
+ struct rt_i2c_msg msgs[2];
+
+ msgs[0].addr = dev->client_addr;
+ msgs[0].flags = RT_I2C_WR;
+ msgs[0].buf = &reg;
+ msgs[0].len = ST_REGITER_LEN;
+
+ msgs[1].addr = dev->client_addr;
+ msgs[1].flags = RT_I2C_RD;
+ msgs[1].buf = data;
+ msgs[1].len = len;
+
+ if (rt_i2c_transfer(dev->bus, msgs, 2) == 2)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static rt_int16_t pre_x[ST_MAX_TOUCH];
+static rt_int16_t pre_y[ST_MAX_TOUCH];
+static rt_int16_t pre_w[ST_MAX_TOUCH];
+static rt_uint8_t s_tp_dowm[ST_MAX_TOUCH];
+
+static void st1663i_touch_up(void *buf, rt_int8_t id)
+{
+ struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
+
+ if (s_tp_dowm[id] == 1)
+ {
+ s_tp_dowm[id] = 0;
+ read_data[id].event = RT_TOUCH_EVENT_UP;
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_NONE;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = pre_w[id];
+ read_data[id].x_coordinate = pre_x[id];
+ read_data[id].y_coordinate = pre_y[id];
+ read_data[id].track_id = id;
+
+ pre_x[id] = -1; /* last point is none */
+ pre_y[id] = -1;
+ pre_w[id] = -1;
+
+ //LOG_I("%s (%d)\n", __func__, id);
+}
+
+static void st1663i_touch_down(void *buf, rt_int8_t id, rt_int16_t x, rt_int16_t y, rt_int16_t w)
+{
+ struct rt_touch_data *read_data = (struct rt_touch_data *)buf;
+
+ if (s_tp_dowm[id] == 1)
+ {
+ read_data[id].event = RT_TOUCH_EVENT_MOVE;
+
+ }
+ else
+ {
+ read_data[id].event = RT_TOUCH_EVENT_DOWN;
+ s_tp_dowm[id] = 1;
+ }
+
+ read_data[id].timestamp = rt_touch_get_ts();
+ read_data[id].width = w;
+ read_data[id].x_coordinate = x;
+ read_data[id].y_coordinate = y;
+ read_data[id].track_id = id;
+
+ pre_x[id] = x; /* save last point */
+ pre_y[id] = y;
+ pre_w[id] = w;
+
+ //LOG_I("%s (%d %d %d %d)\n", __func__, id, x, y, w );
+}
+
+static int8_t pre_id[ST_MAX_TOUCH];
+static S_ST_REGMAP sStRegMap;
+static rt_uint8_t pre_touch = 0;
+
+static rt_size_t st1663i_read_point(struct rt_touch_device *touch, void *buf, rt_size_t read_num)
+{
+ int i;
+
+ rt_err_t error = 0;
+ rt_int32_t touch_event, touchid;
+
+ RT_ASSERT(touch);
+ RT_ASSERT(buf);
+ RT_ASSERT(read_num != 0);
+ RT_ASSERT(read_num <= ST_MAX_TOUCH);
+
+ error = st1663i_read_reg(&st1663i_client, 0x10, (rt_uint8_t *)&sStRegMap, sizeof(sStRegMap));
+ if (error)
+ {
+ LOG_E("get touch data failed, err:%d\n", error);
+ goto exit_read_point;
+ }
+
+ if (sStRegMap.u8Fingers > ST_MAX_TOUCH)
+ {
+ LOG_E("FW report max point:%d > panel info. max:%d\n", sStRegMap.u8Fingers, ST_MAX_TOUCH);
+ goto exit_read_point;
+ }
+
+ if (pre_touch > sStRegMap.u8Fingers) /* point up */
+ {
+ for (i = 0; i < ST_MAX_TOUCH; i++)
+ {
+ rt_uint8_t j;
+ for (j = 0; j < sStRegMap.u8Fingers; j++) /* this time touch num */
+ {
+ touchid = i;
+
+ if (pre_id[i] == touchid) /* this id is not free */
+ break;
+ }
+
+ if ((j == sStRegMap.u8Fingers) && (pre_id[i] != -1)) /* free this node */
+ {
+ // LOG_I("free %d tid=%d\n", i, pre_id[i]);
+ st1663i_touch_up(buf, pre_id[i]);
+ pre_id[i] = -1;
+ }
+ }
+ }
+
+ for (i = 0; i < sStRegMap.u8Fingers; i++)
+ {
+ touch_event = sStRegMap.m_sTP[i].u8Valid;
+ touchid = i;
+
+ //LOG_I("(%d/%d) %d %d\n", i, sStRegMap.u8Fingers, touchid, touch_event);
+
+ pre_id[i] = touchid;
+
+ if (touch_event)
+ {
+ rt_uint16_t x, y, w;
+
+ x = ((uint16_t)sStRegMap.m_sTP[i].u8X0_H << 8) | sStRegMap.m_sTP[i].m_u8X0_L;
+ y = ((uint16_t)sStRegMap.m_sTP[i].u8Y0_H << 8) | sStRegMap.m_sTP[i].m_u8Y0_L;
+ w = sStRegMap.m_sTP[i].m_u8Z;
+
+ //LOG_I("[%d] (%d %d %d %d)\n", touch_event, touchid, x, y, w);
+
+ if (x >= touch->info.range_x || y >= touch->info.range_y)
+ {
+ LOG_E("invalid position, X[%d,%u,%d], Y[%d,%u,%d]\n",
+ 0, x, touch->info.range_x,
+ 0, y, touch->info.range_y);
+ continue;
+ }
+
+ st1663i_touch_down(buf, touchid, x, y, w);
+ }
+ else
+ {
+ // Up
+ st1663i_touch_up(buf, touchid);
+ }
+
+ } // for (i = 0; i < sStRegMap.u8TDStatus; i++)
+
+ pre_touch = sStRegMap.u8Fingers;
+
+ return read_num;
+
+exit_read_point:
+
+ pre_touch = 0;
+
+ return 0;
+}
+
+static rt_err_t st1663i_control(struct rt_touch_device *touch, int cmd, void *arg)
+{
+ switch (cmd)
+ {
+ case RT_TOUCH_CTRL_GET_INFO:
+ {
+ struct rt_touch_info *info = (struct rt_touch_info *)arg;
+ RT_ASSERT(arg);
+
+ rt_memcpy(info, &touch->info, sizeof(struct rt_touch_info));
+ break;
+ }
+ case RT_TOUCH_CTRL_GET_ID:
+ break;
+ case RT_TOUCH_CTRL_SET_X_RANGE:
+ break;
+ case RT_TOUCH_CTRL_SET_Y_RANGE:
+ break;
+ case RT_TOUCH_CTRL_SET_X_TO_Y:
+ break;
+ case RT_TOUCH_CTRL_SET_MODE:
+ break;
+ default:
+ break;
+ }
+ return RT_EOK;
+}
+
+static struct rt_touch_ops st1663i_touch_ops =
+{
+ .touch_readpoint = st1663i_read_point,
+ .touch_control = st1663i_control,
+};
+
+static void st1663i_init(struct rt_i2c_client *dev)
+{
+ st1663i_write_reg(dev, 0x0, 0);
+}
+
+int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg)
+{
+ struct rt_touch_device *touch_device = RT_NULL;
+ rt_uint32_t bus_speed = 400000;
+
+ touch_device = (struct rt_touch_device *)rt_malloc(sizeof(struct rt_touch_device));
+ if (touch_device == RT_NULL)
+ {
+ LOG_E("touch device malloc fail");
+ return -RT_ERROR;
+ }
+ rt_memset((void *)touch_device, 0, sizeof(struct rt_touch_device));
+
+ /* hw init*/
+ rt_pin_mode(*(rt_uint8_t *)cfg->user_data, PIN_MODE_OUTPUT);
+
+ rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_LOW);
+ rt_thread_delay(5);
+ rt_pin_write(*(rt_uint8_t *)cfg->user_data, PIN_HIGH);
+ rt_thread_delay(200);
+
+ rt_pin_mode(cfg->irq_pin.pin, cfg->irq_pin.mode);
+
+ st1663i_client.bus = (struct rt_i2c_bus_device *)rt_device_find(cfg->dev_name);
+
+ if (st1663i_client.bus == RT_NULL)
+ {
+ LOG_E("Can't find %s device", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ if (rt_device_open((rt_device_t)st1663i_client.bus, RT_DEVICE_FLAG_RDWR) != RT_EOK)
+ {
+ LOG_E("open %s device failed", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ if (rt_device_control((rt_device_t)st1663i_client.bus, RT_I2C_DEV_CTRL_CLK, &bus_speed) != RT_EOK)
+ {
+ LOG_E("control %s device failed", cfg->dev_name);
+ return -RT_ERROR;
+ }
+
+ st1663i_client.client_addr = ST1663I_ADDRESS;
+
+ st1663i_init(&st1663i_client);
+
+ rt_memset(&pre_x[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_y[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_w[0], 0xff, ST_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&s_tp_dowm[0], 0, ST_MAX_TOUCH * sizeof(int16_t));
+ rt_memset(&pre_id[0], 0xff, ST_MAX_TOUCH * sizeof(int8_t));
+
+ /* register touch device */
+ touch_device->info.type = RT_TOUCH_TYPE_CAPACITANCE;
+ touch_device->info.vendor = RT_TOUCH_VENDOR_UNKNOWN;
+ touch_device->info.range_x = BSP_LCD_WIDTH;
+ touch_device->info.range_y = BSP_LCD_HEIGHT;
+ touch_device->info.point_num = ST_MAX_TOUCH;
+
+ rt_memcpy(&touch_device->config, cfg, sizeof(struct rt_touch_config));
+ touch_device->ops = &st1663i_touch_ops;
+
+ rt_hw_touch_register(touch_device, name, RT_DEVICE_FLAG_INT_RX, RT_NULL);
+
+ LOG_I("touch device st1663i init success");
+
+ return RT_EOK;
+}
diff --git a/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h
new file mode 100644
index 0000000000..5bfbd40601
--- /dev/null
+++ b/bsp/nuvoton/libraries/nu_packages/TPC/st1663i.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (c) 2006-2018, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-04-11 Wayne the first version
+ */
+
+#ifndef __ST1663I_H__
+#define __ST1663I_H__
+
+#include "touch.h"
+
+#define ST_REGITER_LEN 1
+#define ST_MAX_TOUCH 5
+#define ST1663I_ADDRESS 0x55
+
+#pragma anon_unions
+
+typedef struct
+{
+ //012H*n+0 (n=0, 1, ...,4)
+ union
+ {
+ uint8_t m_u8XY0_H;
+ struct
+ {
+ uint8_t u8Y0_H: 3;
+ uint8_t : 1;
+ uint8_t u8X0_H: 3;
+ uint8_t u8Valid: 1;
+ };
+ };
+
+ //012H*n+1 (n=0, 1, ...,4)
+ uint8_t m_u8X0_L;
+
+ //012H*n+2 (n=0, 1, ...,4)
+ uint8_t m_u8Y0_L;
+
+ //012H*n+3 (n=0, 1, ...,4)
+ uint8_t m_u8Z;
+
+} S_ST_TP;
+
+
+#pragma pack(push)
+#pragma pack(4)
+
+typedef struct
+{
+ union
+ {
+ uint8_t m_u8TouchInfo;
+ struct
+ {
+ uint8_t u8Fingers: 4;
+ uint8_t : 4;
+ };
+ };
+
+ uint8_t m_u8Keys;
+
+ S_ST_TP m_sTP[ST_MAX_TOUCH];
+
+} S_ST_REGMAP;
+#pragma pack(pop)
+
+int rt_hw_st1663i_init(const char *name, struct rt_touch_config *cfg);
+
+#endif /* __ST1663I_H__ */
diff --git a/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c
new file mode 100644
index 0000000000..e0dca96f55
--- /dev/null
+++ b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.c
@@ -0,0 +1,195 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-12-17 Wayne The first version
+ */
+/**
+ * @file lv_gpu_n9h30_ge2d.c
+ *
+ */
+
+/*********************
+ * INCLUDES
+ *********************/
+#include <lvgl.h>
+
+#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)
+
+#include "lv_gpu_n9h30_ge2d.h"
+#include "nu_2d.h"
+#include "mmu.h"
+
+/*********************
+ * DEFINES
+ *********************/
+
+#if LV_COLOR_16_SWAP
+ #error "Can't use GE2D with LV_COLOR_16_SWAP 1"
+#endif
+
+#if !((LV_COLOR_DEPTH == 16) || (LV_COLOR_DEPTH == 32))
+ /*Can't use GPU with other formats*/
+ #error "Can't use GPU with other formats"
+#endif
+
+/**********************
+ * TYPEDEFS
+ **********************/
+
+/**********************
+ * STATIC PROTOTYPES
+ **********************/
+
+static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area,
+ lv_color_t color);
+
+
+static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride,
+ const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa);
+
+/**********************
+ * STATIC VARIABLES
+ **********************/
+
+/**********************
+ * MACROS
+ **********************/
+
+/**********************
+ * GLOBAL FUNCTIONS
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_n9h30_ge2d_ctx_init(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx)
+{
+ lv_draw_sw_init_ctx(drv, draw_ctx);
+
+ lv_draw_n9h30_ge2d_ctx_t *ge2d_draw_ctx = (lv_draw_sw_ctx_t *)draw_ctx;
+
+ ge2d_draw_ctx->blend = lv_draw_n9h30_ge2d_blend;
+ ge2d_draw_ctx->base_draw.wait_for_finish = lv_gpu_n9h30_ge2d_wait_cb;
+}
+
+void lv_draw_n9h30_ge2d_ctx_deinit(lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx)
+{
+ LV_UNUSED(drv);
+ LV_UNUSED(draw_ctx);
+}
+
+void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc)
+{
+ lv_area_t blend_area;
+ int32_t blend_area_w;
+ bool done = false;
+
+ if (!_lv_area_intersect(&blend_area, dsc->blend_area, draw_ctx->clip_area)) return;
+
+ blend_area_w = lv_area_get_width(&blend_area);
+
+ if ((lv_area_get_size(&blend_area) > 7200) &&
+ (((blend_area_w * (LV_COLOR_DEPTH / 2)) & 0x3) == 0) &&
+ (dsc->mask_buf == NULL) && (dsc->blend_mode == LV_BLEND_MODE_NORMAL))
+ {
+ lv_coord_t dest_stride = lv_area_get_width(draw_ctx->buf_area);
+
+ lv_color_t *dest_buf = draw_ctx->buf;
+ dest_buf += dest_stride * (blend_area.y1 - draw_ctx->buf_area->y1) + (blend_area.x1 - draw_ctx->buf_area->x1);
+
+ const lv_color_t *src_buf = dsc->src_buf;
+ if (src_buf)
+ {
+ lv_coord_t src_stride;
+ src_stride = lv_area_get_width(dsc->blend_area);
+ src_buf += src_stride * (blend_area.y1 - dsc->blend_area->y1) + (blend_area.x1 - dsc->blend_area->x1);
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_n9h30_ge2d_blend_map(dest_buf, &blend_area, dest_stride, src_buf, src_stride, dsc->opa);
+ done = true;
+ }
+ else if (dsc->opa >= LV_OPA_MAX)
+ {
+ lv_area_move(&blend_area, -draw_ctx->buf_area->x1, -draw_ctx->buf_area->y1);
+ lv_draw_n9h30_ge2d_blend_fill(dest_buf, dest_stride, &blend_area, dsc->color);
+ done = true;
+ }
+ }
+
+ if (!done) lv_draw_sw_blend_basic(draw_ctx, dsc);
+}
+
+static void lv_draw_n9h30_ge2d_blend_fill(lv_color_t *dest_buf, lv_coord_t dest_stride, const lv_area_t *fill_area,
+ lv_color_t color)
+{
+ int32_t fill_area_w = lv_area_get_width(fill_area);
+ int32_t fill_area_h = lv_area_get_height(fill_area);
+
+ lv_color_t *start_buf = dest_buf - (fill_area->y1 * dest_stride) - fill_area->x1;
+
+ //rt_kprintf("[blend_fill %d %08x] %dx%d %d %d\n", lv_area_get_size(fill_area), dest_buf, fill_area_w, fill_area_h, fill_area->x1, fill_area->y1 );
+
+ if (IS_CACHEABLE_VRAM(dest_buf))
+ mmu_clean_invalidated_dcache((uint32_t)dest_buf, sizeof(lv_color_t) * (dest_stride * fill_area_h + fill_area_w));
+
+ /*Hardware filling*/
+ // Enter GE2D ->
+ ge2dInit(sizeof(lv_color_t) * 8, dest_stride, fill_area->y2, (void *)start_buf);
+
+ ge2dClip_SetClip(fill_area->x1, fill_area->y1, fill_area->x2, fill_area->y2);
+
+ if (sizeof(lv_color_t) == 4)
+ ge2dFill_Solid(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full);
+ else if (sizeof(lv_color_t) == 2)
+ ge2dFill_Solid_RGB565(fill_area->x1, fill_area->y1, fill_area_w, fill_area_h, color.full);
+
+ ge2dClip_SetClip(-1, 0, 0, 0);
+ // -> Leave GE2D
+}
+
+static void lv_draw_n9h30_ge2d_blend_map(lv_color_t *dest_buf, const lv_area_t *dest_area, lv_coord_t dest_stride,
+ const lv_color_t *src_buf, lv_coord_t src_stride, lv_opa_t opa)
+{
+ /*Simple copy*/
+ int32_t dest_x = dest_area->x1;
+ int32_t dest_y = dest_area->y1;
+ int32_t dest_w = lv_area_get_width(dest_area);
+ int32_t dest_h = lv_area_get_height(dest_area);
+
+ const lv_color_t *dest_start_buf = dest_buf - (dest_area->y1 * dest_stride) - dest_area->x1;
+
+ //rt_kprintf("[blend_map %d %08x -> %08x] (x:%d y:%d, %dx%d) <stride src:%d dst:%d>\n", lv_area_get_size(dest_area), src_buf, dest_buf, dest_x, dest_y, dest_w, dest_h, src_stride, dest_stride);
+
+ // Enter GE2D ->
+ ge2dInit(sizeof(lv_color_t) * 8, dest_stride, dest_area->y2, (void *)dest_start_buf);
+
+ if (opa >= LV_OPA_MAX)
+ {
+ ge2dBitblt_SetAlphaMode(0, 0, 0);
+ ge2dBitblt_SetDrawMode(0, 0, 0);
+ }
+ else
+ {
+ ge2dBitblt_SetAlphaMode(1, opa, opa);
+ }
+
+ // flush
+ mmu_clean_dcache((uint32_t)src_buf, sizeof(lv_color_t) * (src_stride * dest_h + dest_w));
+
+ ge2dSpriteBlt_Screen(dest_x, dest_y, dest_w, dest_h, (void *)src_buf);
+ // -> Leave GE2D
+}
+
+void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx)
+{
+ lv_draw_sw_wait_for_finish(draw_ctx);
+}
+
+/**********************
+ * STATIC FUNCTIONS
+ **********************/
+
+#endif // #if (LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0))
diff --git a/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h
new file mode 100644
index 0000000000..0852168ebd
--- /dev/null
+++ b/bsp/nuvoton/nk-n9h30/applications/lvgl/lv_gpu_n9h30_ge2d.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (c) 2006-2022, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2022-3-29 Wayne The first version
+ */
+#ifndef LV_GPU_N9H_GE2D_H
+#define LV_GPU_N9H_GE2D_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*********************
+ * INCLUDES
+ *********************/
+#include "../../misc/lv_color.h"
+#include "../../hal/lv_hal_disp.h"
+#include "../sw/lv_draw_sw.h"
+
+#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)
+
+/*********************
+ * DEFINES
+ *********************/
+
+/**********************
+ * TYPEDEFS
+ **********************/
+struct _lv_disp_drv_t;
+typedef lv_draw_sw_ctx_t lv_draw_n9h30_ge2d_ctx_t;
+
+/**********************
+ * GLOBAL PROTOTYPES
+ **********************/
+
+/**
+ * Turn on the peripheral and set output color mode, this only needs to be done once
+ */
+void lv_draw_n9h30_ge2d_init(void);
+
+void lv_draw_n9h30_ge2d_ctx_init(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx);
+
+void lv_draw_n9h30_ge2d_ctx_deinit(struct _lv_disp_drv_t *drv, lv_draw_ctx_t *draw_ctx);
+
+void lv_draw_n9h30_ge2d_blend(lv_draw_ctx_t *draw_ctx, const lv_draw_sw_blend_dsc_t *dsc);
+
+void lv_gpu_n9h30_ge2d_wait_cb(lv_draw_ctx_t *draw_ctx);
+
+/**********************
+ * MACROS
+ **********************/
+
+#endif /*#if LV_USE_GPU_N9H30_GE2D && LV_VERSION_CHECK(8, 2, 0)*/
+
+#ifdef __cplusplus
+} /*extern "C"*/
+#endif
+
+#endif /*LV_GPU_N9H_GE2D_H*/
+