Browse Source

[bsp/nuvoton] Upload missing files. (#6052)

* [bsp/nuvoton] Upload missing files.

Co-authored-by: Wayne Lin <wclin@nuvoton.com>
Wayne 3 years ago
parent
commit
2006785394

+ 83 - 0
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__ */

+ 177 - 0
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 */

+ 796 - 0
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

+ 796 - 0
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

+ 19 - 0
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')
+

+ 124 - 0
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) */

+ 392 - 0
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) */

+ 57 - 0
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__ */

+ 342 - 0
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;
+}

+ 133 - 0
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__ */

+ 337 - 0
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;
+}

+ 73 - 0
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__ */

+ 195 - 0
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))

+ 64 - 0
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*/
+