summaryrefslogtreecommitdiff
path: root/bsp/ft2004/drivers
diff options
context:
space:
mode:
authorhuanghe <[email protected]>2021-05-21 18:43:59 +0800
committerhuanghe <[email protected]>2021-05-26 16:34:37 +0800
commit0c5692eecee7899071ba3454bd4854976770ebc7 (patch)
tree67a528c990531ace112934c5ea5e996274cdda2c /bsp/ft2004/drivers
parent17cae02ad5e67a4dabcfa431dee2d10d2d8bcbed (diff)
修改部分格式问题
[add] 增加ft2004 BSP
Diffstat (limited to 'bsp/ft2004/drivers')
-rw-r--r--bsp/ft2004/drivers/SConscript14
-rw-r--r--bsp/ft2004/drivers/board.c155
-rw-r--r--bsp/ft2004/drivers/board.h29
-rw-r--r--bsp/ft2004/drivers/drv_can.c270
-rw-r--r--bsp/ft2004/drivers/drv_can.h38
-rw-r--r--bsp/ft2004/drivers/drv_eth.c691
-rw-r--r--bsp/ft2004/drivers/drv_eth.h25
-rw-r--r--bsp/ft2004/drivers/drv_log.h27
-rw-r--r--bsp/ft2004/drivers/drv_qspi.c424
-rw-r--r--bsp/ft2004/drivers/drv_qspi.h29
-rw-r--r--bsp/ft2004/drivers/drv_qspi_flash.c39
-rw-r--r--bsp/ft2004/drivers/drv_sdcard.c154
-rw-r--r--bsp/ft2004/drivers/drv_sdctrl.c659
-rw-r--r--bsp/ft2004/drivers/drv_sdctrl.h47
-rw-r--r--bsp/ft2004/drivers/drv_spi.c449
-rw-r--r--bsp/ft2004/drivers/drv_spi.h29
-rw-r--r--bsp/ft2004/drivers/drv_spi_flash.c43
-rw-r--r--bsp/ft2004/drivers/drv_usart.c192
-rw-r--r--bsp/ft2004/drivers/drv_usart.h25
-rw-r--r--bsp/ft2004/drivers/ft2004.c88
-rw-r--r--bsp/ft2004/drivers/ft2004.h25
-rw-r--r--bsp/ft2004/drivers/ft2004_cpu.S44
-rw-r--r--bsp/ft2004/drivers/secondary_cpu.c86
-rw-r--r--bsp/ft2004/drivers/serial.h20
24 files changed, 3602 insertions, 0 deletions
diff --git a/bsp/ft2004/drivers/SConscript b/bsp/ft2004/drivers/SConscript
new file mode 100644
index 0000000000..b18c98833e
--- /dev/null
+++ b/bsp/ft2004/drivers/SConscript
@@ -0,0 +1,14 @@
+
+from building import *
+
+cwd = GetCurrentDir()
+src = Glob('*.S')
+src += Glob('*.c')
+
+
+
+CPPPATH = [cwd]
+
+group = DefineGroup('Drivers', src, depend = [''], CPPPATH = CPPPATH)
+
+Return('group')
diff --git a/bsp/ft2004/drivers/board.c b/bsp/ft2004/drivers/board.c
new file mode 100644
index 0000000000..408a58d344
--- /dev/null
+++ b/bsp/ft2004/drivers/board.c
@@ -0,0 +1,155 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-04 Carl the first version
+ *
+ */
+
+#include <rtthread.h>
+#include "ft_printf.h"
+#include "ft_assert.h"
+#include "ft_cpu.h"
+#include "ft_psci.h"
+#include "ft_parameters.h"
+#include "board.h"
+#include "gtimer.h"
+#include "ft_generic_timer.h"
+#include <gicv3.h>
+
+#include "interrupt.h"
+#include <mmu.h>
+#include "cp15.h"
+#include "ft2004.h"
+
+#define DDR_MEM (SHARED | AP_RW | DOMAIN0 | MEMWT | DESC_SEC)
+
+struct mem_desc platform_mem_desc[] = {
+ {0x80000000,
+ 0x80000000 + 0x7f000000,
+ 0x80000000,
+ DDR_MEM},
+ {0, //< QSPI
+ 0x1FFFFFFF,
+ 0,
+ DEVICE_MEM},
+ {0x20000000, //<! LPC
+ 0x27FFFFFF,
+ 0x20000000,
+ DEVICE_MEM},
+ {FT_DEV_BASE_ADDR, //<! Device register
+ FT_DEV_END_ADDR,
+ FT_DEV_BASE_ADDR,
+ DEVICE_MEM},
+ {0x30000000, //<! debug
+ 0x39FFFFFF,
+ 0x30000000,
+ DEVICE_MEM},
+ {0x3A000000, //<! Internal register space in the on-chip network
+ 0x3AFFFFFF,
+ 0x3A000000,
+ DEVICE_MEM},
+ {FT_PCI_CONFIG_BASEADDR,
+ FT_PCI_CONFIG_BASEADDR + FT_PCI_CONFIG_REG_LENGTH,
+ FT_PCI_CONFIG_BASEADDR,
+ DEVICE_MEM},
+ {FT_PCI_IO_CONFIG_BASEADDR,
+ FT_PCI_IO_CONFIG_BASEADDR + FT_PCI_IO_CONFIG_REG_LENGTH,
+ FT_PCI_IO_CONFIG_BASEADDR,
+ DEVICE_MEM},
+ {FT_PCI_MEM32_BASEADDR,
+ FT_PCI_MEM32_BASEADDR + FT_PCI_MEM32_REG_LENGTH,
+ FT_PCI_MEM32_BASEADDR,
+ DEVICE_MEM},
+};
+
+const rt_uint32_t platform_mem_desc_size = sizeof(platform_mem_desc) / sizeof(platform_mem_desc[0]);
+
+rt_uint32_t platform_get_gic_dist_base(void)
+{
+ return FT_GICV3_DISTRIBUTOR_BASEADDRESS;
+}
+
+static rt_uint32_t timerStep;
+
+void rt_hw_timer_isr(int vector, void *parameter)
+{
+ gtimer_set_load_value(timerStep);
+ rt_tick_increase();
+}
+
+int rt_hw_timer_init(void)
+{
+ rt_hw_interrupt_install(30, rt_hw_timer_isr, RT_NULL, "tick");
+ rt_hw_interrupt_umask(30);
+ timerStep = gtimer_get_counter_frequency();
+ timerStep /= RT_TICK_PER_SECOND;
+ gtimer_set_load_value(timerStep);
+ gtimer_set_control(1);
+ return 0;
+}
+INIT_BOARD_EXPORT(rt_hw_timer_init);
+
+static void AssertCallback(const char *File, s32 Line)
+{
+ Ft_printf("Assert Error is %s : %d \r\n", File, Line);
+}
+
+#ifdef RT_USING_SMP
+void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler);
+#endif
+
+/**
+ * This function will initialize hardware board
+ */
+void rt_hw_board_init(void)
+{
+ /* bsp debug */
+ FCpu_SpinLockInit();
+ Ft_GenericTimer_Init(0, RT_NULL);
+ Ft_vsprintfRegister((vsprintf_p)rt_vsprintf);
+ Ft_assertSetCallBack((Ft_assertCallback)AssertCallback);
+
+ /* interrupt init */
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + 0, 0);
+
+#if RT_CPUS_NR == 2
+ Ft_printf("arm_gic_redist_address_set is 2 \r\n");
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + (2U << 16), 1);
+#elif RT_CPUS_NR == 3
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + (2U << 16), 1);
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + 2 * (2U << 16), 2);
+#elif RT_CPUS_NR == 4
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + (2U << 16), 1);
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + 2 * (2U << 16), 2);
+ arm_gic_redist_address_set(0, FT_GICV3_RD_BASEADDRESS + 3 * (2U << 16), 3);
+#endif
+
+ rt_hw_interrupt_init();
+
+ rt_components_board_init();
+ rt_console_set_device(RT_CONSOLE_DEVICE_NAME);
+
+/* 初始化内存池 */
+#ifdef RT_USING_HEAP
+ rt_system_heap_init(HEAP_BEGIN, HEAP_END);
+#endif
+
+#ifdef RT_USING_SMP
+ /* install IPI handle */
+ rt_hw_interrupt_set_priority(RT_SCHEDULE_IPI, 16);
+ rt_hw_ipi_handler_install(RT_SCHEDULE_IPI, rt_scheduler_ipi_handler);
+ rt_hw_interrupt_umask(RT_SCHEDULE_IPI);
+#endif
+}
+
+static void ft_reset(void)
+{
+ FPsci_Reset();
+}
+MSH_CMD_EXPORT_ALIAS(ft_reset, ft_reset, ft_reset);
+
+/*@}*/
diff --git a/bsp/ft2004/drivers/board.h b/bsp/ft2004/drivers/board.h
new file mode 100644
index 0000000000..4900ed2e79
--- /dev/null
+++ b/bsp/ft2004/drivers/board.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2013-07-06 Bernard the first version
+ */
+
+#ifndef __BOARD_H__
+#define __BOARD_H__
+
+#include "ft_parameters.h"
+#include "ft2004.h"
+
+#if defined(__CC_ARM)
+extern int Image$$RW_IRAM1$$ZI$$Limit;
+#define HEAP_BEGIN ((void *)&Image$$RW_IRAM1$$ZI$$Limit)
+#elif defined(__GNUC__)
+extern int __bss_end;
+#define HEAP_BEGIN ((void *)&__bss_end)
+#endif
+
+#define HEAP_END (void *)(0x80000000 + 1024 * 1024 * 1024)
+
+void rt_hw_board_init(void);
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_can.c b/bsp/ft2004/drivers/drv_can.c
new file mode 100644
index 0000000000..ff03755761
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_can.c
@@ -0,0 +1,270 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-05-11 Carl the first version
+ */
+
+#include "drv_can.h"
+#include "interrupt.h"
+#include <string.h>
+
+#ifdef BSP_USE_CAN
+
+#define LOG_TAG "drv_can"
+#include <drv_log.h>
+
+#define _CAN0_NAME "can0"
+#define _CAN1_NAME "can1"
+
+#define RTHW_CAN_WAIT(_can) rt_sem_take(&_can->recv_semaphore, RT_WAITING_FOREVER);
+#define RTHW_CAN_SEND(_can) rt_sem_release(&_can->recv_semaphore);
+
+#ifdef BSP_USING_CAN0
+struct ft2004_can drv_can0 =
+ {
+ .name = _CAN0_NAME,
+ .can_handle.Config.InstanceId = 0};
+#endif
+
+#ifdef BSP_USING_CAN1
+struct ft2004_can drv_can1 =
+ {
+ .name = _CAN1_NAME,
+ .can_handle.Config.InstanceId = 1};
+#endif
+
+static void _can_recv_irq(void *args)
+{
+ struct ft2004_can *drv_can = (struct ft2004_can *)args;
+ RTHW_CAN_SEND(drv_can);
+}
+
+static void rt_hw_inner_can_isr(int irqno, void *param)
+{
+ FCan_IntrHandler(param);
+}
+
+static rt_err_t _can_config(struct rt_can_device *can, struct can_configure *cfg)
+{
+ struct FCan_Bittiming bit_timing = {0};
+ struct ft2004_can *drv_can;
+ RT_ASSERT(can);
+ RT_ASSERT(cfg);
+ drv_can = (struct ft2004_can *)can->parent.user_data;
+ RT_ASSERT(drv_can);
+
+ FCan_CfgInitialize(&drv_can->can_handle, FCan_LookupConfig(drv_can->can_handle.Config.InstanceId));
+
+ FCan_SetHandler(&drv_can->can_handle, FCAN_HANDLER_RECV, _can_recv_irq, drv_can);
+
+ bit_timing.bitrate = cfg->baud_rate;
+
+ if (FCan_CalcBittiming(&bit_timing) != FCAN_SUCCESS)
+ {
+ LOG_E("Setting baud rate %x is not valid \r\n", bit_timing.bitrate);
+ return -RT_ERROR;
+ }
+
+ FCan_SetTiming(&drv_can->can_handle, &bit_timing);
+
+ rt_hw_interrupt_set_priority(drv_can->can_handle.Config.IrqNum, 16);
+ rt_hw_interrupt_install(drv_can->can_handle.Config.IrqNum, rt_hw_inner_can_isr, &drv_can->can_handle, drv_can->name);
+ rt_hw_interrupt_umask(drv_can->can_handle.Config.IrqNum);
+
+ FCan_Enable(&drv_can->can_handle);
+
+ return RT_EOK;
+}
+
+static rt_err_t _can_control(struct rt_can_device *can, int cmd, void *arg)
+{
+ return RT_EOK;
+}
+
+static int _can_sendmsg(struct rt_can_device *can, const void *buf, rt_uint32_t box_num)
+{
+ struct ft2004_can *drv_can;
+ struct rt_can_msg *pmsg = (struct rt_can_msg *)buf;
+ struct FCan_Frame can_frame = {0};
+ RT_ASSERT(can);
+ drv_can = (struct ft2004_can *)can->parent.user_data;
+ RT_ASSERT(drv_can);
+
+ /* Check the parameters */
+ RT_ASSERT(pmsg->len <= 8U);
+
+ if (RT_CAN_STDID == pmsg->ide)
+ {
+ can_frame.CanId = pmsg->id;
+ }
+ else
+ {
+ can_frame.CanId = pmsg->id;
+ can_frame.CanId |= CAN_EFF_FLAG;
+ }
+
+ if (RT_CAN_DTR == pmsg->rtr)
+ {
+ }
+ else
+ {
+ can_frame.CanId |= CAN_RTR_FLAG;
+ }
+
+ can_frame.CanDlc = pmsg->len & 0x0FU;
+ memcpy(can_frame.data, pmsg->data, 8);
+
+ return (FCan_SendByIrq(&drv_can->can_handle, &can_frame, 1, RT_NULL) == 1) ? RT_EOK : -RT_ERROR;
+}
+
+static int _can_recvmsg(struct rt_can_device *can, void *buf, rt_uint32_t fifo)
+{
+ struct ft2004_can *drv_can;
+ struct rt_can_msg *pmsg = (struct rt_can_msg *)buf;
+ RT_ASSERT(can);
+ struct FCan_Frame recv_frame = {0};
+ drv_can = (struct ft2004_can *)can->parent.user_data;
+ RT_ASSERT(drv_can);
+
+ RTHW_CAN_WAIT(drv_can);
+
+ if (FCan_RecvByIrq(&drv_can->can_handle, &recv_frame, 1) == 0)
+ {
+ LOG_E("rx msg is error");
+ return -RT_ERROR;
+ }
+
+ if (CAN_EFF_FLAG & recv_frame.CanId)
+ {
+ pmsg->ide = RT_CAN_EXTID;
+ pmsg->id = (recv_frame.CanId & ~(RT_CAN_EXTID));
+ }
+ else
+ {
+ pmsg->ide = RT_CAN_STDID;
+ pmsg->id = recv_frame.CanId;
+ }
+
+ if (CAN_RTR_FLAG & recv_frame.CanId)
+ {
+ pmsg->id &= ~CAN_RTR_FLAG;
+ pmsg->rtr = RT_CAN_RTR;
+ }
+ else
+ {
+ pmsg->rtr = RT_CAN_DTR;
+ }
+
+ /* get len */
+ pmsg->len = recv_frame.CanDlc;
+ return RT_EOK;
+}
+
+static const struct rt_can_ops _can_ops =
+ {
+ _can_config,
+ _can_control,
+ _can_sendmsg,
+ _can_recvmsg,
+};
+
+int rt_hw_can_init(void)
+{
+#ifdef BSP_USING_CAN0
+ drv_can0.can_handle.Config.InstanceId = 0;
+ rt_sem_init(&drv_can0.recv_semaphore, "can0_recv", 0, RT_IPC_FLAG_FIFO);
+ drv_can0.device.config.ticks = 20000;
+ drv_can0.device.config.baud_rate = 1000000;
+ rt_hw_can_register(&drv_can0.device,
+ drv_can0.name,
+ &_can_ops,
+ &drv_can0);
+
+#endif
+
+#ifdef BSP_USING_CAN1
+ drv_can1.can_handle.Config.InstanceId = 1;
+ drv_can0.device.config.baud_rate = 1000000;
+ rt_sem_init(&drv_can1.recv_semaphore, "can1_recv", 0, RT_IPC_FLAG_FIFO);
+ rt_hw_can_register(&drv_can1.device,
+ drv_can1.name,
+ &_can_ops,
+ &drv_can1);
+
+#endif
+ return 0;
+}
+
+INIT_BOARD_EXPORT(rt_hw_can_init);
+
+#ifdef BSP_USING_CAN0_DEBUG
+
+struct can_test_struct
+{
+ const char *name;
+ struct rt_can_filter_config *filter;
+ rt_device_t candev;
+ struct rt_semaphore _sem;
+};
+
+static struct can_test_struct can0_test_obj = {
+ .name = _CAN0_NAME};
+
+void can_recv_irq(void *param)
+{
+ struct can_test_struct *_can_obj = (struct can_test_struct *)param;
+ rt_kprintf("can_recv_iqr \r\n");
+ rt_sem_release(&_can_obj->_sem);
+}
+
+static void rt_can_test_loopback_thread_entry(void *param)
+{
+ struct can_test_struct *_can_obj = (struct can_test_struct *)param;
+ struct FCan_Frame recv_frame;
+ struct ft2004_can *drv_can;
+ rt_uint32_t i;
+ _can_obj->candev = rt_device_find(_can_obj->name);
+ RT_ASSERT(_can_obj->candev);
+ drv_can = (struct ft2004_can *)_can_obj->candev->user_data;
+ rt_sem_init(&_can_obj->_sem, "canrx_wait", 0, RT_IPC_FLAG_FIFO);
+ rt_device_open(_can_obj->candev, RT_DEVICE_OFLAG_RDWR);
+
+ while (1)
+ {
+ rt_kprintf(" start to wait loopback \r\n");
+ RTHW_CAN_WAIT(drv_can);
+ while (0 != FCan_RecvByIrq(&drv_can->can_handle, &recv_frame, 1))
+ {
+ rt_kprintf("CanId %x \r\n", recv_frame.CanId);
+ rt_kprintf("CanDlc %x \r\n", recv_frame.CanDlc);
+ for (i = 0; i < recv_frame.CanDlc; i++)
+ {
+ rt_kprintf("data [%d] %x \r\n", i, recv_frame.data[i]);
+ }
+ FCan_SendByIrq(&drv_can->can_handle, &recv_frame, 1, RT_NULL);
+ }
+ }
+}
+
+int rt_can0_test(void)
+{
+ rt_thread_t tid;
+
+ tid = rt_thread_create("can0_loopback",
+ rt_can_test_loopback_thread_entry, &can0_test_obj,
+ 1024, 16, 20);
+ if (tid != RT_NULL)
+ rt_thread_startup(tid);
+
+ return 0;
+}
+
+INIT_APP_EXPORT(rt_can0_test);
+
+#endif
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_can.h b/bsp/ft2004/drivers/drv_can.h
new file mode 100644
index 0000000000..92e809eb1e
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_can.h
@@ -0,0 +1,38 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-05-11 Carl the first version
+ */
+
+#ifndef __DRV_CAN_H__
+#define __DRV_CAN_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <board.h>
+#include <rtdevice.h>
+#include <rtthread.h>
+#include "ft_can.h"
+
+ struct ft2004_can
+ {
+ const char *name;
+ FCan_t can_handle;
+ struct rt_semaphore recv_semaphore;
+ struct rt_can_device device; /* inherit from can device */
+ };
+
+ int rt_hw_can_init(void);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // !
diff --git a/bsp/ft2004/drivers/drv_eth.c b/bsp/ft2004/drivers/drv_eth.c
new file mode 100644
index 0000000000..e7148631e4
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_eth.c
@@ -0,0 +1,691 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-09 Carl the first version
+ */
+
+#include "board.h"
+#include <netif/ethernetif.h>
+#include "lwipopts.h"
+#include "ft_parameters.h"
+#include "ft_gmac.h"
+#include "ft_cache.h"
+#include "ft_gmac_hw.h"
+#include "ft_status.h"
+#include "ft_io.h"
+#include "drv_eth.h"
+
+#ifdef BSP_USING_GMAC
+
+#define LOG_TAG "drv.gmac"
+#include <drv_log.h>
+
+#define MAX_ADDR_LEN 6
+
+#define LINK_THREAD_STACK_LENGTH 0x400
+
+struct drv_gmac
+{
+ struct eth_device parent; /* inherit from ethernet device */
+ Ft_Gmac_t Gmac; /* Gmac driver */
+#ifndef PHY_USING_INTERRUPT_MODE
+ rt_timer_t poll_link_timer;
+#endif
+ rt_uint8_t *rx_buffer; /* Buffer for RxDesc */
+ rt_uint8_t *tx_buffer; /* Buffer for TxDesc */
+ uint32_t eth_speed; /* eth_speed */
+ uint32_t eth_mode; /* ETH_Duplex_Mode */
+ rt_uint8_t dev_addr[MAX_ADDR_LEN]; /* MAC address */
+ struct rt_event link_event;
+
+ struct rt_thread _link_thread;
+ rt_uint8_t _link_thread_stack[LINK_THREAD_STACK_LENGTH];
+
+ rt_thread_t _debug_tid;
+};
+
+static void rt_ft2004_status_check(void *Args, u32 MacPhyStatus);
+
+//
+#if defined(RAW_DATA_PRINT)
+#if defined(ETH_RX_DUMP) || defined(ETH_TX_DUMP)
+#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ')
+static void dump_hex(const rt_uint8_t *ptr, rt_size_t buflen)
+{
+ unsigned char *buf = (unsigned char *)ptr;
+ int i, j;
+
+ for (i = 0; i < buflen; i += 16)
+ {
+ rt_kprintf("%08X: ", i);
+
+ for (j = 0; j < 16; j++)
+ if (i + j < buflen)
+ rt_kprintf("%02X ", buf[i + j]);
+ else
+ rt_kprintf(" ");
+ rt_kprintf(" ");
+
+ for (j = 0; j < 16; j++)
+ if (i + j < buflen)
+ rt_kprintf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.');
+ rt_kprintf("\n");
+ }
+}
+#endif
+#endif
+
+/**
+ * @name: rt_gmacmem_create
+ * @msg: Initialize the Gmac TX/Rx Describe Memory 。
+ * @param {*}
+ * @return {*}
+ */
+static void rt_gmacmem_create(struct drv_gmac *pOsGmac)
+{
+ pOsGmac->rx_buffer = rt_calloc(1, RX_DESCNUM * GMAC_MAX_PACKET_SIZE);
+ if (pOsGmac->rx_buffer == NULL)
+ {
+ LOG_E("rx_buffer Malloc is error ");
+ RT_ASSERT(0)
+ }
+
+ pOsGmac->tx_buffer = rt_calloc(1, TX_DESCNUM * GMAC_MAX_PACKET_SIZE);
+ if (pOsGmac->tx_buffer == NULL)
+ {
+ LOG_E("tx_buffer Malloc is error ");
+ RT_ASSERT(0)
+ }
+
+ pOsGmac->Gmac.TxDesc = rt_calloc(1, TX_DESCNUM * sizeof(FGmac_DmaDesc_t));
+ if (pOsGmac->Gmac.TxDesc == NULL)
+ {
+ LOG_E("TxDesc Malloc is error ");
+ RT_ASSERT(0)
+ }
+
+ pOsGmac->Gmac.RxDesc = rt_calloc(1, RX_DESCNUM * sizeof(FGmac_DmaDesc_t) + 128);
+ if (pOsGmac->Gmac.RxDesc == NULL)
+ {
+ LOG_E("RxDesc Malloc is error ");
+ RT_ASSERT(0)
+ }
+
+#define ROUND_UP(x, align) (((long)(x) + ((long)align - 1)) & \
+ ~((long)align - 1))
+
+ pOsGmac->Gmac.RxDesc = (FGmac_DmaDesc_t *)ROUND_UP(pOsGmac->Gmac.RxDesc, 128);
+ LOG_D("RxDesc fit after addr %x ", pOsGmac->Gmac.RxDesc);
+}
+
+static void rt_gmacmem_free(struct drv_gmac *pOsGmac)
+{
+ if (pOsGmac->rx_buffer)
+ {
+ rt_free(pOsGmac->rx_buffer);
+ }
+
+ if (pOsGmac->tx_buffer)
+ {
+ rt_free(pOsGmac->tx_buffer);
+ }
+
+ if (pOsGmac->Gmac.RxDesc)
+ {
+ rt_free(pOsGmac->Gmac.RxDesc);
+ }
+
+ if (pOsGmac->Gmac.TxDesc)
+ {
+ rt_free(pOsGmac->Gmac.TxDesc);
+ }
+}
+
+static void rt_hw_gmac_isr(int irqno, void *param)
+{
+ FGmac_IntrHandler(param);
+}
+
+static void rt_hw_gmac_recv_isr(void *Args)
+{
+ struct drv_gmac *pOsMac;
+ rt_err_t result = 0;
+
+ if (RT_NULL == Args)
+ {
+ LOG_E("Args is NULL");
+ return;
+ }
+
+ pOsMac = (struct drv_gmac *)Args;
+ result = eth_device_ready(&(pOsMac->parent));
+ if (result != RT_EOK)
+ {
+ LOG_I("RxCpltCallback err = %d", result);
+ }
+}
+
+static rt_err_t
+rt_ft2004_gmac_start(struct drv_gmac *pOsMac)
+{
+ Ft_Gmac_t *pGmac;
+ pGmac = &pOsMac->Gmac;
+
+ if (FST_SUCCESS != Ft_Gmac_HwInitialize(pGmac))
+ {
+ return -RT_ERROR;
+ }
+
+ FGmac_SetHandler(pGmac, FT_GMAC_RX_COMPLETE_CB_ID, rt_hw_gmac_recv_isr, pOsMac);
+ FGmac_SetHandler(pGmac, FT_GMAC_MAC_PHY_STATUS_CB_ID, rt_ft2004_status_check, pOsMac);
+
+ /* Initialize Rx Description list : ring Mode */
+ FGmac_DmaRxDescRingInit(pGmac, pGmac->RxDesc, pOsMac->rx_buffer, GMAC_MAX_PACKET_SIZE, RX_DESCNUM);
+
+ /* Initialize Tx Description list : ring Mode */
+ FGmac_DmaTxDescRingInit(pGmac, pGmac->TxDesc, pOsMac->tx_buffer, GMAC_MAX_PACKET_SIZE, TX_DESCNUM);
+
+ Ft_Gmac_Start(pGmac);
+ /* Gmac interrupt init */
+ rt_hw_interrupt_install(pGmac->Config.IRQ_NUM, rt_hw_gmac_isr, pGmac, "Gmac");
+ rt_hw_interrupt_umask(pGmac->Config.IRQ_NUM);
+ return RT_EOK;
+}
+
+void rt_ft2004_gmac_stop(struct drv_gmac *pOsMac)
+{
+ Ft_Gmac_t *pGmac;
+ pGmac = &pOsMac->Gmac;
+ Ft_Gmac_Stop(pGmac);
+}
+
+/* GMAC initialization function */
+static rt_err_t rt_ft2004_gmac_init(rt_device_t dev)
+{
+ struct drv_gmac *pOsMac;
+ struct eth_device *pGmacParent;
+ FGmac_Config_t *pConfig;
+
+ pGmacParent = rt_container_of(dev, struct eth_device, parent);
+ if (NULL == pGmacParent)
+ {
+ return -RT_ENOMEM;
+ }
+
+ pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent);
+ if (NULL == pOsMac)
+ {
+ return -RT_ENOMEM;
+ }
+
+ pConfig = Ft_Gmac_LookupConfig(pOsMac->Gmac.Config.InstanceId);
+ if (NULL == pConfig)
+ {
+ return -RT_ENOMEM;
+ }
+
+ Ft_Gmac_UseDefaultMacAddr(&pOsMac->Gmac, pOsMac->Gmac.Config.MacAddr);
+
+ if (FST_SUCCESS != Ft_GmacCfgInitialize(&pOsMac->Gmac, pConfig))
+ {
+ return -RT_ERROR;
+ }
+
+ return rt_ft2004_gmac_start(pOsMac);
+}
+
+static rt_err_t rt_ft2004_gmac_open(rt_device_t dev, rt_uint16_t oflag)
+{
+ LOG_D("gmac open");
+ return RT_EOK;
+}
+
+static rt_err_t rt_ft2004_gmac_close(rt_device_t dev)
+{
+ LOG_D("gmac close");
+ return RT_EOK;
+}
+
+static rt_size_t rt_ft2004_gmac_read(rt_device_t dev, rt_off_t pos, void *buffer, rt_size_t size)
+{
+ LOG_D("gmac read");
+ rt_set_errno(-RT_ENOSYS);
+ return 0;
+}
+
+static rt_size_t rt_ft2004_gmac_write(rt_device_t dev, rt_off_t pos, const void *buffer, rt_size_t size)
+{
+ LOG_D("gmac write");
+ rt_set_errno(-RT_ENOSYS);
+ return 0;
+}
+
+static rt_err_t rt_ft2004_gmac_control(rt_device_t dev, int cmd, void *args)
+{
+
+ struct drv_gmac *pOsMac;
+ struct eth_device *pGmacParent;
+
+ pGmacParent = rt_container_of(dev, struct eth_device, parent);
+ if (NULL == pGmacParent)
+ {
+ return -RT_ENOMEM;
+ }
+
+ pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent);
+ if (NULL == pOsMac)
+ {
+ return -RT_ENOMEM;
+ }
+
+ switch (cmd)
+ {
+ case NIOCTL_GADDR:
+ /* get mac address */
+ if (args)
+ rt_memcpy(args, pOsMac->dev_addr, 6);
+ else
+ return -RT_ERROR;
+ break;
+
+ default:
+ break;
+ }
+
+ return RT_EOK;
+}
+
+rt_err_t rt_ft2004_gmac_tx(rt_device_t dev, struct pbuf *p)
+{
+ struct drv_gmac *pOsMac;
+ Ft_Gmac_t *pGmac;
+ struct eth_device *pGmacParent;
+
+ err_t errval;
+ struct pbuf *q;
+ u8 *Buffer = NULL;
+ volatile FGmac_DmaDesc_t *DmaTxDesc;
+
+ u32 FrameLength = 0;
+ u32 BufferOffset = 0;
+ u32 BytesLeftToCopy = 0;
+ u32 PayLoadOffset = 0;
+
+ pGmacParent = rt_container_of(dev, struct eth_device, parent);
+ if (NULL == pGmacParent)
+ {
+ return -RT_ENOMEM;
+ }
+
+ pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent);
+ if (NULL == pOsMac)
+ {
+ return -RT_ENOMEM;
+ }
+
+ pGmac = &pOsMac->Gmac;
+ DmaTxDesc = &pGmac->TxDesc[pGmac->TxDescRingData.DescBufIndex];
+ Buffer = (u8 *)DmaTxDesc->Buffer1Addr;
+
+ if (Buffer == NULL)
+ {
+ LOG_E("Buffer is NULL \r\n");
+ RT_ASSERT(0)
+ }
+
+#if RT_LWIP_ETH_PAD_SIZE
+ pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+ for (q = p; q != NULL; q = q->next)
+ {
+ /* Is this buffer available? If not, goto error */
+ if ((DmaTxDesc->Status & DMA_TDES0_OWN) != 0)
+ {
+ errval = ERR_USE;
+ LOG_E("error errval = ERR_USE; \r\n");
+ goto error;
+ }
+
+ /* Get bytes in current lwIP buffer */
+ BytesLeftToCopy = q->len;
+ PayLoadOffset = 0;
+ /* Check if the length of data to copy is bigger than Tx buffer size*/
+ while ((BytesLeftToCopy + BufferOffset) > GMAC_MAX_PACKET_SIZE)
+ {
+ /* Copy data to Tx buffer*/
+ memcpy((u8 *)((u8 *)Buffer + BufferOffset), (u8 *)((u8 *)q->payload + PayLoadOffset), (GMAC_MAX_PACKET_SIZE - BufferOffset));
+ FCache_cpuDcacheClean((rt_uint32_t *)DmaTxDesc->Buffer1Addr, GMAC_MAX_PACKET_SIZE);
+ GMAC_INC_DESC(pGmac->TxDescRingData.DescBufIndex, pGmac->TxDescRingData.DescMaxNumber);
+ /* Point to next descriptor */
+ DmaTxDesc = &pGmac->TxDesc[pGmac->TxDescRingData.DescBufIndex];
+
+ /* Check if the Bufferis available */
+ if ((DmaTxDesc->Status & DMA_TDES0_OWN) != (u32)0)
+ {
+ errval = ERR_USE;
+ LOG_E("Check if the Bufferis available \r\n");
+ goto error;
+ }
+
+ Buffer = (u8 *)(DmaTxDesc->Buffer1Addr);
+ BytesLeftToCopy = BytesLeftToCopy - (GMAC_MAX_PACKET_SIZE - BufferOffset);
+ PayLoadOffset = PayLoadOffset + (GMAC_MAX_PACKET_SIZE - BufferOffset);
+ FrameLength = FrameLength + (GMAC_MAX_PACKET_SIZE - BufferOffset);
+ BufferOffset = 0;
+
+ if (Buffer == NULL)
+ {
+ LOG_E(" error Buffer is 0 \r\n");
+ RT_ASSERT(0)
+ }
+ }
+
+ /* Copy the remaining bytes */
+ memcpy((u8 *)((u8 *)Buffer + BufferOffset), (u8 *)((u8 *)q->payload + PayLoadOffset), BytesLeftToCopy);
+ BufferOffset = BufferOffset + BytesLeftToCopy;
+ FrameLength = FrameLength + BytesLeftToCopy;
+ }
+ /* 指向下一个位置 */
+ FCache_cpuDcacheClean((rt_uint32_t *)DmaTxDesc->Buffer1Addr, GMAC_MAX_PACKET_SIZE);
+ GMAC_INC_DESC(pGmac->TxDescRingData.DescBufIndex, pGmac->TxDescRingData.DescMaxNumber);
+#if RT_LWIP_ETH_PAD_SIZE
+ pbuf_header(p, RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+
+#ifdef ETH_TX_DUMP
+ dump_hex(Buffer, p->tot_len);
+#endif
+
+ FGmac_TransmitframeRingPoll(pGmac, FrameLength);
+error:
+ FGmac_SetTransmitUnderflow(pGmac);
+ return errval;
+}
+
+struct pbuf *rt_ft2004_gmac_rx(rt_device_t dev)
+{
+ struct drv_gmac *pOsMac;
+ Ft_Gmac_t *pGmac;
+ struct eth_device *pGmacParent;
+
+ struct pbuf *p = NULL;
+ struct pbuf *q = NULL;
+ u16 Length = 0;
+ u8 *Buffer;
+ volatile FGmac_DmaDesc_t *DmaRxDesc;
+ u32 BufferOffset = 0;
+ u32 PayLoadOffset = 0;
+ u32 BytesLeftToCopy = 0;
+ u32 DescBufIndex; /* For Current Desc buffer buf position */
+
+ pGmacParent = rt_container_of(dev, struct eth_device, parent);
+ if (NULL == pGmacParent)
+ {
+ return RT_NULL;
+ }
+
+ pOsMac = rt_container_of(pGmacParent, struct drv_gmac, parent);
+ if (NULL == pOsMac)
+ {
+ return RT_NULL;
+ }
+
+ pGmac = &pOsMac->Gmac;
+
+ /* get received frame */
+ if (FST_SUCCESS != FGmac_RingGetReceivedFrame_IT(pGmac))
+ {
+ return NULL;
+ }
+
+ DescBufIndex = pGmac->RxDescRingData.DescBufIndex;
+ Length = (pGmac->RxDesc[DescBufIndex].Status & DMA_RDES0_FRAME_LEN_MASK) >> DMA_RDES0_FRAME_LEN_SHIFT;
+ Buffer = (u8 *)pGmac->RxDesc[DescBufIndex].Buffer1Addr;
+
+#if RT_LWIP_ETH_PAD_SIZE
+ Length += RT_LWIP_ETH_PAD_SIZE; /* allow room for Ethernet padding */
+#endif
+
+ if (Length > 0)
+ {
+ /* We allocate a pbuf chain of pbufs from the Lwip buffer pool */
+ p = pbuf_alloc(PBUF_RAW, Length, PBUF_POOL);
+ }
+
+#ifdef ETH_RX_DUMP
+ dump_hex(Buffer, (u32)Length);
+#endif
+
+ if (p != NULL)
+ {
+#if RT_LWIP_ETH_PAD_SIZE
+ pbuf_header(p, -RT_LWIP_ETH_PAD_SIZE); /* drop the padding word */
+#endif
+ DmaRxDesc = &pGmac->RxDesc[DescBufIndex];
+ BufferOffset = 0;
+ for (q = p; q != NULL; q = q->next)
+ {
+ BytesLeftToCopy = q->len;
+ PayLoadOffset = 0;
+ /* Check if the length of bytes to copy in current pbuf is bigger than Rx buffer size*/
+ while ((BytesLeftToCopy + BufferOffset) > GMAC_MAX_PACKET_SIZE)
+ {
+ /* Copy data to pbuf */
+ memcpy((u8 *)((u8 *)q->payload + PayLoadOffset), (u8 *)((u8 *)Buffer + BufferOffset), (GMAC_MAX_PACKET_SIZE - BufferOffset));
+
+ /* Point to next descriptor */
+ GMAC_INC_DESC(DescBufIndex, pGmac->RxDescRingData.DescMaxNumber);
+ if (DescBufIndex == pGmac->RxDescRingData.DescIndex)
+ {
+ break;
+ }
+
+ DmaRxDesc = &pGmac->RxDesc[DescBufIndex];
+ Buffer = (u8 *)(DmaRxDesc->Buffer1Addr);
+
+ BytesLeftToCopy = BytesLeftToCopy - (GMAC_MAX_PACKET_SIZE - BufferOffset);
+ PayLoadOffset = PayLoadOffset + (GMAC_MAX_PACKET_SIZE - BufferOffset);
+ BufferOffset = 0;
+ }
+ /* Copy remaining data in pbuf */
+ memcpy((u8 *)((u8 *)q->payload + PayLoadOffset), (u8 *)((u8 *)Buffer + BufferOffset), BytesLeftToCopy);
+ BufferOffset = BufferOffset + BytesLeftToCopy;
+ }
+
+#if RT_LWIP_ETH_PAD_SIZE
+ pbuf_header(p, RT_LWIP_ETH_PAD_SIZE); /* reclaim the padding word */
+#endif
+ }
+
+ /* Release descriptors to DMA */
+ /* Point to first descriptor */
+ DmaRxDesc = &pGmac->RxDesc[DescBufIndex];
+ /* Set Own bit in Rx descriptors: gives the buffers back to DMA */
+ for (DescBufIndex = pGmac->RxDescRingData.DescBufIndex; DescBufIndex != pGmac->RxDescRingData.DescIndex; GMAC_INC_DESC(DescBufIndex, pGmac->RxDescRingData.DescMaxNumber))
+ {
+ FCache_cpuDcacheInvalidate((rt_uint32_t *)pGmac->RxDesc[DescBufIndex].Buffer1Addr, GMAC_MAX_PACKET_SIZE);
+ DmaRxDesc->Status |= DMA_RDES0_OWN;
+ DmaRxDesc = &pGmac->RxDesc[DescBufIndex];
+ }
+
+ /* Sync index */
+ pGmac->RxDescRingData.DescBufIndex = pGmac->RxDescRingData.DescIndex;
+ FGmac_ResumeTransmissionReception(pGmac);
+
+ return p;
+}
+
+static void rt_ft2004_status_check(void *Args, u32 MacPhyStatus)
+{
+ struct drv_gmac *pOsMac;
+ pOsMac = (struct drv_gmac *)Args;
+
+ if (MacPhyStatus & 0x8)
+ {
+ rt_event_send(&pOsMac->link_event, FT_NETIF_LINKUP);
+ }
+ else
+ {
+ rt_event_send(&pOsMac->link_event, FT_NETIF_DOWN);
+ }
+}
+
+static void ethernet_link_thread(void *Args)
+{
+ struct drv_gmac *pOsMac;
+ rt_uint32_t status;
+ u32 LastStatus = FT_NETIF_DOWN;
+ u32 Flg;
+ if (RT_NULL == Args)
+ {
+ return;
+ }
+
+ pOsMac = (struct drv_gmac *)Args;
+
+ while (1)
+ {
+ status = 0;
+ if (rt_event_recv(&pOsMac->link_event, FT_NETIF_LINKUP | FT_NETIF_DOWN, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR,
+ RT_WAITING_FOREVER, &status) != RT_EOK)
+ {
+ LOG_E("wait completed timeout");
+ continue;
+ }
+
+ if (status & FT_NETIF_DOWN)
+ {
+ eth_device_linkchange(&pOsMac->parent, RT_FALSE);
+ LastStatus = FT_NETIF_DOWN;
+ }
+ else if (status & FT_NETIF_LINKUP)
+ {
+ Flg = (LastStatus == FT_NETIF_LINKUP) ? 0 : 1;
+ LastStatus = FT_NETIF_LINKUP;
+ }
+ else
+ {
+ LOG_I(" EventGroup is error \r\n");
+ RT_ASSERT(0)
+ }
+
+ if (Flg)
+ {
+ Flg = 0;
+ // eth_device_linkchange(&pOsMac->parent, RT_FALSE);
+ LOG_I(" Start Linkup \r\n");
+ rt_ft2004_gmac_stop(pOsMac);
+ rt_ft2004_gmac_start(pOsMac);
+ LOG_I(" HardWare is ok \r\n");
+ if (LastStatus == FT_NETIF_LINKUP)
+ {
+ rt_thread_mdelay(5000);
+ eth_device_linkchange(&pOsMac->parent, RT_TRUE);
+ }
+ }
+ }
+}
+
+#ifdef BSP_USING_GMAC0
+struct drv_gmac os_drv_gmac0;
+static char *os_drv_gmac0_name = "gmac0";
+#endif
+
+#ifdef BSP_USING_GMAC1
+struct drv_gmac os_drv_gmac1;
+static char *os_drv_gmac1_name = "gmac1";
+
+#endif
+
+static int rt_hw_gmac_init(struct drv_gmac *pOsMac, const char *name)
+{
+ rt_err_t state = RT_EOK;
+ // rt_thread_t tid;
+ rt_gmacmem_free(pOsMac);
+ rt_gmacmem_create(pOsMac);
+
+ pOsMac->eth_speed = GMAC_SPEED_1000M;
+ pOsMac->eth_mode = GMAC_MODE_FULLDUPLEX;
+
+ pOsMac->parent.parent.init = rt_ft2004_gmac_init;
+ pOsMac->parent.parent.open = rt_ft2004_gmac_open;
+ pOsMac->parent.parent.close = rt_ft2004_gmac_close;
+ pOsMac->parent.parent.read = rt_ft2004_gmac_read;
+ pOsMac->parent.parent.write = rt_ft2004_gmac_write;
+ pOsMac->parent.parent.control = rt_ft2004_gmac_control;
+ pOsMac->parent.parent.user_data = RT_NULL;
+
+ pOsMac->parent.eth_rx = rt_ft2004_gmac_rx;
+ pOsMac->parent.eth_tx = rt_ft2004_gmac_tx;
+ Ft_Gmac_UseDefaultMacAddr(&pOsMac->Gmac, pOsMac->dev_addr);
+ state = rt_event_init(&pOsMac->link_event, name, RT_IPC_FLAG_FIFO);
+ LOG_I("rt_event_init is ok \r\n");
+ if (RT_EOK != state)
+ {
+ rt_kprintf("init gmac0 event failed.\n");
+ return -RT_ERROR;
+ }
+
+ /* register eth device */
+ state = eth_device_init(&(pOsMac->parent), name);
+ if (RT_EOK != state)
+ {
+ LOG_E("gmac device init faild: %d", state);
+ return -RT_ERROR;
+ }
+
+ state = rt_thread_init(&pOsMac->_link_thread,
+ name,
+ ethernet_link_thread,
+ pOsMac,
+ &pOsMac->_link_thread_stack[0],
+ sizeof(pOsMac->_link_thread_stack),
+ 10, 2);
+
+ if (RT_EOK == state)
+ {
+ rt_thread_startup(&pOsMac->_link_thread);
+ }
+ else
+ {
+ LOG_E("rt_thread_init is error");
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+
+static int rt_hw_ft2004_eth_init(void)
+{
+ rt_err_t state = RT_EOK;
+
+#ifdef BSP_USING_GMAC0
+ os_drv_gmac0.Gmac.Config.InstanceId = 0;
+ state = rt_hw_gmac_init(&os_drv_gmac0, os_drv_gmac0_name);
+ if (RT_EOK != state)
+ {
+ goto __exit;
+ }
+#endif
+
+#ifdef BSP_USING_GMAC1
+ os_drv_gmac1.Gmac.Config.InstanceId = 1;
+ state = rt_hw_gmac_init(&os_drv_gmac1, os_drv_gmac1_name);
+ if (RT_EOK != state)
+ {
+ goto __exit;
+ }
+#endif
+
+__exit:
+ return state;
+}
+
+INIT_DEVICE_EXPORT(rt_hw_ft2004_eth_init);
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_eth.h b/bsp/ft2004/drivers/drv_eth.h
new file mode 100644
index 0000000000..ed68b71728
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_eth.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-09 Carl the first version
+ */
+#ifndef __DRV_ETH_H__
+#define __DRV_ETH_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define FT_NETIF_LINKUP 0x1U
+#define FT_NETIF_DOWN 0x2U
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // !
diff --git a/bsp/ft2004/drivers/drv_log.h b/bsp/ft2004/drivers/drv_log.h
new file mode 100644
index 0000000000..3fe511789b
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_log.h
@@ -0,0 +1,27 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2018-11-15 SummerGift first version
+ */
+
+/*
+ * NOTE: DO NOT include this file on the header file.
+ */
+
+#ifndef LOG_TAG
+#define DBG_TAG "drv"
+#else
+#define DBG_TAG LOG_TAG
+#endif /* LOG_TAG */
+
+#ifdef DRV_DEBUG
+#define DBG_LVL DBG_LOG
+#else
+#define DBG_LVL DBG_INFO
+#endif /* DRV_DEBUG */
+
+#include <rtdbg.h>
diff --git a/bsp/ft2004/drivers/drv_qspi.c b/bsp/ft2004/drivers/drv_qspi.c
new file mode 100644
index 0000000000..9c5cbf4a9f
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_qspi.c
@@ -0,0 +1,424 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-11 Carl the first version
+ */
+
+#include "drv_qspi.h"
+#include <rtthread.h>
+#include "rtdevice.h"
+#include "ft_qspi.h"
+#include "ft_parameters.h"
+
+#ifdef BSP_USE_QSPI
+
+#define DRV_DEBUG
+#define LOG_TAG "drv.qspi"
+#include <drv_log.h>
+
+struct ft2004_qspi_bus
+{
+ FQSpi_t fqspi;
+ char *name;
+ rt_uint32_t init; /* 1 is init already */
+};
+
+static struct rt_spi_bus _qspi_bus;
+static struct ft2004_qspi_bus _ft2004_qspi_bus;
+
+static int ft2004_qspi_init(struct rt_qspi_device *device, struct rt_qspi_configuration *qspi_cfg)
+{
+
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(qspi_cfg != RT_NULL);
+
+ // struct rt_spi_configuration *cfg = &qspi_cfg->parent;
+ struct ft2004_qspi_bus *qspi_bus_p = device->parent.bus->parent.user_data;
+
+ if (qspi_bus_p->init == 0)
+ {
+ qspi_bus_p->init = 1;
+ FQSpi_CfgInitialize(&qspi_bus_p->fqspi, FQSpi_LookupConfig(0));
+ }
+
+ return RT_EOK;
+}
+
+static rt_err_t ft2004_cmdOperation(struct ft2004_qspi_bus *qspi_bus_p, struct rt_spi_message *message)
+{
+ struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message;
+ const rt_uint8_t *sndb = message->send_buf;
+ rt_uint8_t *rcvb = message->recv_buf;
+ ft_error_t ret;
+ RT_ASSERT(qspi_bus_p != RT_NULL);
+ RT_ASSERT(message != RT_NULL);
+
+ struct FQSpi_CmdPack cmd_pack = {0};
+
+ if (qspi_message->instruction.qspi_lines == 0)
+ {
+ LOG_E("instruction is not valid");
+ return RT_ERROR;
+ }
+
+ cmd_pack.cmd = qspi_message->instruction.content;
+
+ if (qspi_message->address.qspi_lines != 0)
+ {
+ cmd_pack.flags |= FQSPI_CMD_NEED_ADDR_MASK;
+ cmd_pack.addr = qspi_message->address.content;
+ }
+
+ if (qspi_message->address.size == 24)
+ {
+ cmd_pack.flags |= FQSPI_CMD_ADDRESS_3BYTE_MASK;
+ }
+ else if (qspi_message->address.size == 32)
+ {
+ cmd_pack.flags |= FQSPI_CMD_ADDRESS_4BYTE_MASK;
+ }
+
+ if (qspi_message->qspi_data_lines != 0)
+ {
+ if (sndb && (message->length > 0))
+ {
+ cmd_pack.flags |= FQSPI_CMD_NEED_SET_MASK;
+ cmd_pack.txBuf = sndb;
+ cmd_pack.length = message->length;
+ }
+ else if (rcvb && (message->length > 0))
+ {
+ cmd_pack.flags |= FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rcvb;
+ cmd_pack.length = message->length;
+ }
+ else
+ {
+ cmd_pack.flags &= ~(FQSPI_CMD_NEED_GET_MASK | FQSPI_CMD_NEED_SET_MASK);
+ }
+ }
+
+ if (qspi_message->dummy_cycles)
+ {
+ cmd_pack.flags |= FQSPI_CMD_NEED_DUMMY_MASK;
+ cmd_pack.dummyCycle = qspi_message->dummy_cycles;
+ }
+
+ if (cmd_pack.cmd == 0x20)
+ {
+ if (qspi_message->address.size == 32)
+ {
+ cmd_pack.cmd = 0xdc;
+ }
+ }
+
+#ifdef BSP_QSPI_DEBUG
+ LOG_I("flags %x", cmd_pack.flags);
+#endif
+
+ ret = FQSpi_CmdOperation(&qspi_bus_p->fqspi, &cmd_pack);
+
+#ifdef BSP_QSPI_DEBUG
+ if (ret == FQSPI_SUCCESS)
+ if (cmd_pack.cmd == 5)
+ {
+ LOG_I("cmd05 0x%x", cmd_pack.rxBuf[0]);
+ }
+#endif
+
+ return (ret == FQSPI_SUCCESS) ? RT_EOK : RT_ERROR;
+}
+
+static rt_uint32_t ft2004_qspi_xfer(struct ft2004_qspi_bus *qspi_bus_p, struct rt_spi_message *message)
+{
+ struct rt_qspi_message *qspi_message = (struct rt_qspi_message *)message;
+ rt_uint32_t ret_length = 0;
+ const rt_uint8_t *sndb = message->send_buf;
+ rt_uint8_t *rcvb = message->recv_buf;
+ rt_int32_t length = message->length;
+ rt_uint32_t cmd;
+ rt_uint32_t addr;
+ FQSpi_t *qspi_p;
+ FQSpi_Config_t *qspi_config_p;
+ struct FQSpi_DataPack data_pack = {0};
+ qspi_p = &qspi_bus_p->fqspi;
+ qspi_config_p = &qspi_bus_p->fqspi.config;
+
+ cmd = qspi_message->instruction.content;
+ addr = qspi_message->address.content;
+
+#ifdef BSP_QSPI_DEBUG
+ LOG_I("cmd is %x ", cmd);
+ LOG_I("length %d , rcvb %x sndb %x addr %x dummy_cycles %x ", length, rcvb, sndb, addr, qspi_message->dummy_cycles);
+#endif
+
+ if (qspi_config_p->channel >= FT_QSPI_MAX_CS_NUM)
+ {
+ LOG_E("invalid channel[%x] ", qspi_config_p->channel);
+ return RT_ERROR;
+ }
+ switch (cmd)
+ {
+ case FQSPI_FLASH_CMD_PP:
+ {
+ if (RT_NULL != sndb)
+ {
+ data_pack.cmd = cmd;
+ data_pack.addr = addr;
+ if (qspi_message->address.size == 24)
+ {
+ data_pack.flags |= FQSPI_DATA_ADDRESS_3BYTE_MASK;
+ }
+ else
+ {
+ data_pack.flags |= FQSPI_DATA_ADDRESS_4BYTE_MASK;
+ }
+
+ LOG_E("write flags %x ", data_pack.flags);
+ data_pack.txBuf = sndb;
+ data_pack.length = length;
+ ret_length = ((FQSpi_Write(qspi_p, &data_pack) == FQSPI_SUCCESS) ? length : 0);
+ }
+ else
+ {
+ LOG_E("pp cmd %x sndb is null", cmd);
+ ret_length = 0;
+ }
+ }
+ break;
+ case FQSPI_FLASH_CMD_WRDI: /* for sufd qspi fast read */
+ FQSpi_FlashRegSet(qspi_p, cmd, RT_NULL, 0);
+ case FQSPI_FLASH_CMD_READ:
+ {
+ if (RT_NULL != rcvb)
+ {
+ data_pack.cmd = FQSPI_FLASH_CMD_READ;
+ data_pack.addr = addr;
+ if (qspi_message->address.size == 24)
+ {
+ data_pack.flags |= FQSPI_DATA_ADDRESS_3BYTE_MASK;
+ }
+ else
+ {
+ data_pack.flags |= FQSPI_DATA_ADDRESS_4BYTE_MASK;
+ }
+
+ if (qspi_message->dummy_cycles)
+ {
+ data_pack.flags |= FQSPI_DATA_NEED_DUMMY_MASK;
+ data_pack.dummyCycle = qspi_message->dummy_cycles;
+ }
+ data_pack.rxBuf = rcvb;
+ data_pack.length = length;
+
+ ret_length = ((FQSpi_Read(qspi_p, &data_pack) == FQSPI_SUCCESS) ? length : 0);
+ }
+ else
+ {
+ // LOG_E("read cmd %x rcvb is null", cmd);
+ ret_length = 0;
+ }
+ }
+ break;
+
+ default:
+ {
+ if (ft2004_cmdOperation(qspi_bus_p, message) == RT_EOK)
+ {
+ ret_length = 1;
+ }
+ else
+ {
+ LOG_E("ft2004_cmdOperation error");
+ ret_length = 0;
+ }
+ }
+ }
+
+ return ret_length;
+}
+
+static rt_uint32_t qspixfer(struct rt_spi_device *device, struct rt_spi_message *message)
+{
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(device->bus != RT_NULL);
+ struct ft2004_qspi_bus *qspi_bus_p = device->bus->parent.user_data;
+
+ return ft2004_qspi_xfer(qspi_bus_p, message);
+}
+
+static rt_err_t qspi_configure(struct rt_spi_device *device, struct rt_spi_configuration *configuration)
+{
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(configuration != RT_NULL);
+
+ struct rt_qspi_device *qspi_device = (struct rt_qspi_device *)device;
+ return ft2004_qspi_init(qspi_device, &qspi_device->config);
+}
+
+static const struct rt_spi_ops ft2004_qspi_ops =
+ {
+ .configure = qspi_configure,
+ .xfer = qspixfer,
+};
+
+static int ft2004_qspi_register_bus(struct ft2004_qspi_bus *qspi_bus, const char *name)
+{
+ RT_ASSERT(qspi_bus != RT_NULL);
+ RT_ASSERT(name != RT_NULL);
+
+ _qspi_bus.parent.user_data = qspi_bus;
+ return rt_qspi_bus_register(&_qspi_bus, name, &ft2004_qspi_ops);
+}
+
+rt_err_t ft2004_qspi_bus_attach_device(const char *bus_name, const char *device_name, rt_uint8_t data_line_width, void (*enter_qspi_mode)(), void (*exit_qspi_mode)())
+{
+ struct rt_qspi_device *qspi_device = RT_NULL;
+ rt_err_t result = RT_EOK;
+
+ RT_ASSERT(bus_name != RT_NULL);
+ RT_ASSERT(device_name != RT_NULL);
+ RT_ASSERT(data_line_width == 1 || data_line_width == 2 || data_line_width == 4);
+
+ qspi_device = (struct rt_qspi_device *)rt_malloc(sizeof(struct rt_qspi_device));
+ if (qspi_device == RT_NULL)
+ {
+ LOG_E("no memory, qspi bus attach device failed!");
+ result = RT_ENOMEM;
+ goto __exit;
+ }
+
+ qspi_device->enter_qspi_mode = enter_qspi_mode;
+ qspi_device->exit_qspi_mode = exit_qspi_mode;
+ qspi_device->config.qspi_dl_width = data_line_width;
+
+ result = rt_spi_bus_attach_device(&qspi_device->parent, device_name, bus_name, RT_NULL);
+
+__exit:
+ if (result != RT_EOK)
+ {
+ if (qspi_device)
+ {
+ rt_free(qspi_device);
+ }
+ }
+
+ return result;
+}
+
+static int rt_hw_qspi_bus_init(void)
+{
+ return ft2004_qspi_register_bus(&_ft2004_qspi_bus, FT2004_QSPI_NAME);
+}
+INIT_BOARD_EXPORT(rt_hw_qspi_bus_init);
+#ifdef BSP_QSPI_DEBUG
+static void cmd05_check(void)
+{
+ struct FQSpi_CmdPack cmd_pack = {0};
+ u8 rx_buffer[1];
+
+ cmd_pack.cmd = 0x6;
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x5;
+ cmd_pack.flags = FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rx_buffer;
+ cmd_pack.length = sizeof(rx_buffer);
+
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ for (u32 i = 0; i < cmd_pack.length; i++)
+ {
+ LOG_I("cnt %d, 0x%x ", i, rx_buffer[i]);
+ }
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x4;
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x5;
+ cmd_pack.flags = FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rx_buffer;
+ cmd_pack.length = sizeof(rx_buffer);
+
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ for (u32 i = 0; i < cmd_pack.length; i++)
+ {
+ LOG_I("cnt %d, 0x%x ", i, rx_buffer[i]);
+ }
+}
+MSH_CMD_EXPORT_ALIAS(cmd05_check, cmd05_check, cmd05_check);
+#endif
+
+#ifdef BSP_QSPI_DEBUG
+static void cmd35_check(void)
+{
+ struct FQSpi_CmdPack cmd_pack = {0};
+ u8 rx_buffer[1];
+
+ cmd_pack.cmd = 0x6;
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x5;
+ cmd_pack.flags = FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rx_buffer;
+ cmd_pack.length = sizeof(rx_buffer);
+
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ for (u32 i = 0; i < cmd_pack.length; i++)
+ {
+ LOG_I("cnt %d, 0x%x ", i, rx_buffer[i]);
+ }
+
+ cmd_pack.cmd = 0xB7;
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x35;
+ cmd_pack.flags = FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rx_buffer;
+ cmd_pack.length = sizeof(rx_buffer);
+
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ for (u32 i = 0; i < cmd_pack.length; i++)
+ {
+ LOG_I("cnt %d, 0x%x ", i, rx_buffer[i]);
+ }
+}
+MSH_CMD_EXPORT_ALIAS(cmd35_check, cmd35_check, cmd35_check);
+#endif
+#ifdef BSP_QSPI_DEBUG
+static void cmd15_check(void)
+{
+ struct FQSpi_CmdPack cmd_pack = {0};
+ u8 rx_buffer[1];
+
+ // cmd_pack.cmd = 0xB7;
+ // FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ rt_memset(&cmd_pack, 0, sizeof(&cmd_pack));
+ cmd_pack.cmd = 0x15;
+ cmd_pack.flags = FQSPI_CMD_NEED_GET_MASK;
+ cmd_pack.rxBuf = rx_buffer;
+ cmd_pack.length = sizeof(rx_buffer);
+
+ FQSpi_CmdOperation(&_ft2004_qspi_bus.fqspi, &cmd_pack);
+
+ for (u32 i = 0; i < cmd_pack.length; i++)
+ {
+ LOG_I("cnt %d, 0x%x ", i, rx_buffer[i]);
+ }
+}
+MSH_CMD_EXPORT_ALIAS(cmd15_check, cmd15_check, cmd15_check);
+#endif
+#endif
diff --git a/bsp/ft2004/drivers/drv_qspi.h b/bsp/ft2004/drivers/drv_qspi.h
new file mode 100644
index 0000000000..29566f9af6
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_qspi.h
@@ -0,0 +1,29 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-11 Carl the first version
+ */
+
+#ifndef __DRT_QSPI_H__
+#define __DRT_QSPI_H__
+
+#include <rtthread.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define FT2004_QSPI_NAME "qspi"
+
+ rt_err_t ft2004_qspi_bus_attach_device(const char *bus_name, const char *device_name, rt_uint8_t data_line_width, void (*enter_qspi_mode)(), void (*exit_qspi_mode)());
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif // !DRT_QSPI_H
diff --git a/bsp/ft2004/drivers/drv_qspi_flash.c b/bsp/ft2004/drivers/drv_qspi_flash.c
new file mode 100644
index 0000000000..102cd5fc5b
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_qspi_flash.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-08 Carl the first version
+ */
+
+#include <board.h>
+#include <drv_qspi.h>
+#include <rtdevice.h>
+#include <rthw.h>
+#include <finsh.h>
+
+#ifdef BSP_USE_QSPI
+
+#include "spi_flash.h"
+#include "spi_flash_sfud.h"
+#define _QSPI_DEVICE_NAME "qspiflash"
+
+static int
+rt_hw_qspi_flash_with_sfud_init(void)
+{
+ ft2004_qspi_bus_attach_device(FT2004_QSPI_NAME, _QSPI_DEVICE_NAME, 1, RT_NULL, RT_NULL);
+
+ /* init gd */
+ rt_kprintf("start rt_sfud_flash_probe \r\n");
+ if (RT_NULL == rt_sfud_flash_probe("GD25LQ256D", _QSPI_DEVICE_NAME))
+ {
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+INIT_COMPONENT_EXPORT(rt_hw_qspi_flash_with_sfud_init);
+
+#endif /* BSP_USING_QSPI_FLASH */
diff --git a/bsp/ft2004/drivers/drv_sdcard.c b/bsp/ft2004/drivers/drv_sdcard.c
new file mode 100644
index 0000000000..a6494089f6
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_sdcard.c
@@ -0,0 +1,154 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-18 Carl the first version
+ */
+
+#include <rtthread.h>
+
+#ifdef BSP_USING_SDC
+
+#include <dfs_elm.h>
+#include <dfs_fs.h>
+#include <dfs_posix.h>
+#include "drv_sdctrl.h"
+
+#define DBG_TAG "app.card"
+#define DBG_LVL DBG_INFO
+#include <rtdbg.h>
+
+static rt_err_t _sdcard_mount(void)
+{
+ rt_device_t device;
+
+ device = rt_device_find("sd0");
+ rt_kprintf("rt_device_find %x \r\n", device);
+ if (device == NULL)
+ {
+ mmcsd_wait_cd_changed(0);
+ ft2004_mmcsd_change();
+ if (mmcsd_wait_cd_changed(rt_tick_from_millisecond(5000)) == -RT_ETIMEOUT)
+ {
+ rt_kprintf("timeout \r\n");
+ return RT_ERROR;
+ }
+ device = rt_device_find("sd0");
+ }
+
+ rt_thread_mdelay(1000);
+ LOG_I("dfs_mount \r\n");
+ if (device != RT_NULL)
+ {
+ if (dfs_mount("sd0", "/", "elm", 0, 0) == RT_EOK)
+ {
+ LOG_I("sd card mount to '/'");
+ }
+ else
+ {
+ LOG_W("sd card mount to '/' failed!");
+ return RT_ERROR;
+ }
+ }
+
+ return RT_EOK;
+}
+
+static void _sdcard_unmount(void)
+{
+ rt_thread_mdelay(200);
+ dfs_unmount("/");
+ LOG_I("Unmount \"/\"");
+
+ mmcsd_wait_cd_changed(0);
+ ft2004_mmcsd_change();
+ mmcsd_wait_cd_changed(rt_tick_from_millisecond(5000));
+ LOG_I("Unmount is over \r\n");
+}
+
+static void sd_mount(void *parameter)
+{
+ rt_uint8_t state = 0; /* 1. is valid card ,0 is removal */
+#ifdef BSP_SDC_IRQ_CARD_REMOVE
+ rt_uint32_t status;
+#endif
+ while (1)
+ {
+ switch (state)
+ {
+ case 0:
+ if (ft2004_card_status() == 1)
+ {
+#ifdef BSP_SDC_IRQ_CARD_REMOVE
+ ft2004_card_remove_check(0, RT_NULL); /* Clear removal flag bit */
+#endif
+ if (_sdcard_mount() == RT_EOK)
+ {
+ state = 1;
+ }
+ else
+ {
+ /* For the critical case of frequent plug */
+ rt_kprintf("dfs_unmount \r\n");
+ _sdcard_unmount();
+ ft2004_sdctrl_reset();
+ }
+ }
+ else
+ {
+ rt_thread_mdelay(100);
+ }
+ break;
+ case 1:
+
+#ifdef BSP_SDC_IRQ_CARD_REMOVE
+ if (ft2004_card_remove_check(RT_WAITING_FOREVER, &status) == RT_EOK)
+ {
+ if (status & SDCTR_CARD_REMOVE_FLG)
+ {
+ state = 0;
+ _sdcard_unmount();
+ }
+ }
+#else
+ if (ft2004_card_status() == 0)
+ {
+ state = 0;
+ _sdcard_unmount();
+ }
+#endif
+ else
+ {
+ rt_thread_mdelay(100);
+ }
+ break;
+ default:
+ state = 0;
+ break;
+ }
+ }
+}
+
+int ft2004_sdcard_mount(void)
+{
+ rt_thread_t tid;
+
+ tid = rt_thread_create("sd_mount", sd_mount, RT_NULL,
+ 8192, 2, 20);
+
+ if (tid != RT_NULL)
+ {
+ rt_thread_startup(tid);
+ }
+ else
+ {
+ LOG_E("create sd_mount thread err!");
+ }
+ return RT_EOK;
+}
+INIT_APP_EXPORT(ft2004_sdcard_mount);
+
+#endif /* BSP_USING_SDCARD */
diff --git a/bsp/ft2004/drivers/drv_sdctrl.c b/bsp/ft2004/drivers/drv_sdctrl.c
new file mode 100644
index 0000000000..1f6a4df7e5
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_sdctrl.c
@@ -0,0 +1,659 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-18 Carl the first version
+ */
+
+#include "drv_sdctrl.h"
+#include "ft_sdctrl_hw.h"
+#include "ft_sdctrl.h"
+#include "ft_debug.h"
+#include "ft_types.h"
+#include "ft_generic_timer.h"
+#include <drivers/mmcsd_core.h>
+#include "interrupt.h"
+#include "rtconfig.h"
+#include "ft_cache.h"
+
+#ifdef BSP_USING_SDC
+
+#define LOG_TAG "drv.sdmmc"
+#include <drv_log.h>
+
+#define RTHW_SDCTRL_LOCK(_sdctrl) rt_mutex_take(&_sdctrl->mutex, RT_WAITING_FOREVER)
+#define RTHW_SDCTRL_UNLOCK(_sdctrl) rt_mutex_release(&_sdctrl->mutex);
+
+struct mmcsd_pkg
+{
+ struct rt_mmcsd_cmd *cmd;
+ void *buff;
+ rt_uint32_t flag;
+};
+
+typedef struct
+{
+ FtsdCtrl_t ft_sdctrl;
+ struct rt_mmcsd_host *host;
+ struct rt_event event;
+ struct rt_mutex mutex;
+ struct mmcsd_pkg *pkg;
+} ft_sdctrl_class_t;
+
+ft_sdctrl_class_t sdctrl_class;
+
+ALIGN(SDCTR_ALIGN_LEN)
+static rt_uint8_t cache_buf[SDCTR_BUFF_SIZE];
+
+static void rthw_sdctrl_send_command(ft_sdctrl_class_t *class_p, struct mmcsd_pkg *pkg);
+
+static void demo_dump_sdc(void)
+{
+ Ft_DumpHexWord((const rt_uint32_t *)(0x28207C00), 256);
+}
+MSH_CMD_EXPORT_ALIAS(demo_dump_sdc, dump_sdc, output all dump_sdc);
+
+static void rthw_sdctrl_delay(u32 delayCnt)
+{
+ Ft_GenericTimer_UsDelay(delayCnt);
+}
+
+static u32 rthw_sdctrl_rasp2type(u32 rasp)
+{
+
+ switch (rasp)
+ {
+ case RESP_NONE:
+ return FTSDCTRL_CMD_RES_NONE;
+ case RESP_R2:
+ return FTSDCTRL_CMD_RES_LONG;
+ default:
+ return FTSDCTRL_CMD_RES_SHORT;
+ }
+
+ return FTSDCTRL_CMD_RES_SHORT;
+}
+
+static void rthw_sdctrl_transfer_by_dma(ft_sdctrl_class_t *class_p, struct mmcsd_pkg *pkg)
+{
+ struct rt_mmcsd_data *data;
+ struct rt_mmcsd_cmd *cmd;
+ u32 rasp;
+ u32 *buff;
+ FtsdCtrl_t *ft_sdctrl_p;
+
+ if ((RT_NULL == class_p))
+ {
+ LOG_E("rthw_sdctrl_transfer_by_dma invalid class_p");
+ return;
+ }
+ ft_sdctrl_p = &class_p->ft_sdctrl;
+
+ if ((RT_NULL == pkg))
+ {
+ LOG_E("rthw_sdctrl_transfer_by_dma invalid args");
+ return;
+ }
+
+ data = pkg->cmd->data;
+ if (RT_NULL == data)
+ {
+ LOG_E("rthw_sdctrl_transfer_by_dma invalid args");
+ return;
+ }
+
+ buff = pkg->buff;
+ if (RT_NULL == buff)
+ {
+ LOG_E("rthw_sdctrl_transfer_by_dma invalid args");
+ return;
+ }
+
+ cmd = pkg->cmd;
+ rasp = resp_type(pkg->cmd);
+ rasp = rthw_sdctrl_rasp2type(rasp);
+
+ if (data->flags & DATA_DIR_WRITE)
+ {
+#ifdef BSP_SDC_DEBUG_PRINT
+ rt_kprintf("DATA_DIR_WRITE %x \r\n", cmd->arg);
+#endif
+ FCache_cpuDcacheClean(buff, data->blks * data->blksize);
+
+ /* data, card, blk: card : data + blk */
+ FSdCtrl_WriteData(ft_sdctrl_p, (UINTPTR)buff, cmd->arg, data->blks);
+ cmd->err = FSdCtrl_WaitCmdEnd(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay, rasp, cmd->resp);
+
+#ifdef BSP_SDC_DEBUG_PRINT
+ for (int i = 0; i < 4; i++)
+ {
+ rt_kprintf("cmdRsp[%d] %x \r\n", i, cmd->resp[i]);
+ }
+ Ft_DumpHexWord(buff, 256);
+#endif
+ FSdCtrl_WaitWriteDataEnd(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay, data->blks);
+ FCache_cpuDcacheInvalidate(buff, data->blks * data->blksize);
+ }
+ else if (data->flags & DATA_DIR_READ)
+ {
+#ifdef BSP_SDC_DEBUG_PRINT
+ rt_kprintf("DATA_DIR_READ %x \r\n", cmd->arg);
+#endif
+ if ((cmd->flags & CMD_ADTC) && (data->blksize < 512))
+ {
+#ifdef BSP_SDC_DEBUG_PRINT
+ LOG_E("CMD_ADTC \r\n");
+#endif
+ FSdCtrl_DoACmd(ft_sdctrl_p, cmd->cmd_code, rasp, cmd->arg);
+ rt_thread_mdelay(10);
+ }
+
+ FCache_cpuDcacheInvalidate(buff, data->blks * data->blksize);
+ FSdCtrl_ReadData(ft_sdctrl_p, (UINTPTR)buff, cmd->arg, data->blks);
+ cmd->err = FSdCtrl_WaitCmdEnd(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay, rasp, cmd->resp);
+#ifdef BSP_SDC_DEBUG_PRINT
+ for (int i = 0; i < 4; i++)
+ {
+ rt_kprintf("cmdRsp[%d] %x \r\n", i, cmd->resp[i]);
+ }
+#endif
+ FSdCtrl_WaitReadDataEnd(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay, data->blks);
+ FCache_cpuDcacheClean(buff, data->blks * data->blksize);
+#ifdef BSP_SDC_DEBUG_PRINT
+ Ft_DumpHexWord(buff, data->blks * data->blksize);
+#endif
+ }
+}
+
+static void rthw_sdctrl_docmd(ft_sdctrl_class_t *class_p, struct mmcsd_pkg *pkg)
+{
+ struct rt_mmcsd_cmd *cmd;
+ u32 rasp;
+ FtsdCtrl_t *ft_sdctrl_p;
+
+ if ((RT_NULL == class_p))
+ {
+ LOG_E("rthw_sdctrl_docmd invalid class_p");
+ return;
+ }
+
+ ft_sdctrl_p = &class_p->ft_sdctrl;
+
+ if ((RT_NULL == pkg))
+ {
+ LOG_E("rthw_sdctrl_docmd invalid args");
+ return;
+ }
+
+ cmd = pkg->cmd;
+ rasp = resp_type(pkg->cmd);
+ rasp = rthw_sdctrl_rasp2type(rasp);
+ FSdCtrl_DoCmd(ft_sdctrl_p, pkg->cmd->cmd_code, rasp, cmd->arg);
+ cmd->err = FSdCtrl_WaitCmdEnd(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay, rasp, cmd->resp);
+
+#ifdef BSP_SDC_DEBUG_PRINT
+ for (int i = 0; i < 4; i++)
+ {
+ rt_kprintf("cmdRsp[%d] %x \r\n", i, cmd->resp[i]);
+ }
+#endif
+}
+
+static void rthw_sdctrl_send_command(ft_sdctrl_class_t *class_p, struct mmcsd_pkg *pkg)
+{
+ struct rt_mmcsd_cmd *cmd = pkg->cmd;
+ struct rt_mmcsd_data *data = cmd->data;
+ /* save pkg */
+ class_p->pkg = pkg;
+
+ /* config data reg */
+ if (data != RT_NULL && data->blks)
+ {
+ /* transfer config */
+ rthw_sdctrl_transfer_by_dma(class_p, pkg);
+ }
+ else
+ {
+ rthw_sdctrl_docmd(class_p, pkg);
+ }
+}
+
+/**
+ * @brief This function send sdio request.
+ * @param host rt_mmcsd_host
+ * @param req request
+ * @retval None
+ */
+static void rthw_sdctrl_request(struct rt_mmcsd_host *host, struct rt_mmcsd_req *req)
+{
+ struct mmcsd_pkg pkg;
+ ft_sdctrl_class_t *class_p = host->private_data;
+ struct rt_mmcsd_data *data;
+
+ RTHW_SDCTRL_LOCK(class_p);
+ if (req->cmd != RT_NULL)
+ {
+ rt_memset(&pkg, 0, sizeof(pkg));
+ data = req->cmd->data;
+ pkg.cmd = req->cmd;
+
+ if (pkg.cmd->cmd_code == 5 || pkg.cmd->cmd_code == 1)
+ {
+ rt_kprintf("cmd_code is not vaild %x \r\n", pkg.cmd->cmd_code);
+ pkg.cmd->err = RT_EINVAL;
+ goto _exit;
+ }
+
+#ifdef BSP_SDC_DEBUG_PRINT
+ struct rt_mmcsd_cmd *cmd;
+ cmd = req->cmd;
+ LOG_E("CMD:%d ARG:0x%08x RES:%s%s%s%s%s%s%s%s%s rw:%c len:%d blksize:%d",
+ cmd->cmd_code,
+ cmd->arg,
+ resp_type(cmd) == RESP_NONE ? "NONE" : "",
+ resp_type(cmd) == RESP_R1 ? "R1" : "",
+ resp_type(cmd) == RESP_R1B ? "R1B" : "",
+ resp_type(cmd) == RESP_R2 ? "R2" : "",
+ resp_type(cmd) == RESP_R3 ? "R3" : "",
+ resp_type(cmd) == RESP_R4 ? "R4" : "",
+ resp_type(cmd) == RESP_R5 ? "R5" : "",
+ resp_type(cmd) == RESP_R6 ? "R6" : "",
+ resp_type(cmd) == RESP_R7 ? "R7" : "",
+ data ? (data->flags & DATA_DIR_WRITE ? 'w' : 'r') : '-',
+ data ? data->blks * data->blksize : 0,
+ data ? data->blksize : 0);
+#endif
+
+ if (data != RT_NULL)
+ {
+ rt_uint32_t size = data->blks * data->blksize;
+
+ RT_ASSERT(size <= SDCTR_BUFF_SIZE);
+ pkg.buff = data->buf;
+ if ((rt_uint32_t)data->buf & (SDCTR_ALIGN_LEN - 1))
+ {
+ pkg.buff = cache_buf;
+ if (data->flags & DATA_DIR_WRITE)
+ {
+ rt_memcpy(cache_buf, data->buf, size);
+ }
+ }
+ }
+
+ rthw_sdctrl_send_command(class_p, &pkg);
+
+ if ((data != RT_NULL) && (data->flags & DATA_DIR_READ) && ((rt_uint32_t)data->buf & (SDCTR_ALIGN_LEN - 1)))
+ {
+ rt_memcpy(data->buf, cache_buf, data->blksize * data->blks);
+ }
+ }
+
+ if (req->stop != RT_NULL)
+ {
+ rt_memset(&pkg, 0, sizeof(pkg));
+ pkg.cmd = req->stop;
+ rthw_sdctrl_send_command(class_p, &pkg);
+ }
+
+_exit:
+
+ RTHW_SDCTRL_UNLOCK(class_p);
+ mmcsd_req_complete(class_p->host);
+}
+
+static void rthw_sdctrl_clk_divider(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
+{
+ ft_sdctrl_class_t *class_p = host->private_data;
+ FtsdCtrl_t *sd_ctrl = &(class_p->ft_sdctrl);
+
+ /* bus mode is pull push */
+ FSdCtrl_ClkFreqSetup(sd_ctrl, io_cfg->clock);
+ return;
+}
+
+static void rthw_sdctrl_iocfg(struct rt_mmcsd_host *host, struct rt_mmcsd_io_cfg *io_cfg)
+{
+ ft_sdctrl_class_t *class_p = host->private_data;
+ RTHW_SDCTRL_LOCK(class_p);
+
+ /* calculate and set clk divider */
+ rthw_sdctrl_clk_divider(host, io_cfg);
+
+ RTHW_SDCTRL_UNLOCK(class_p);
+}
+
+rt_int32_t rthw_sdctrl_detect(struct rt_mmcsd_host *host)
+{
+ ft_sdctrl_class_t *class_p = host->private_data;
+
+ return FSdCtrl_CardDetect(&class_p->ft_sdctrl);
+}
+
+static const struct rt_mmcsd_host_ops ops =
+ {
+ rthw_sdctrl_request,
+ rthw_sdctrl_iocfg,
+ rthw_sdctrl_detect,
+ RT_NULL,
+};
+
+void rthw_sdctrl_nomarl_callback(void *args)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)args;
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ status = FSdCtrl_GetNormalIrqStatus(pFtsdCtrl);
+
+ if (status & NORMAL_INT_STATUS_CR)
+ {
+ rt_event_send(&class_p->event, SDCTR_CARD_REMOVE_FLG);
+ }
+ else if (status & NORMAL_INT_STATUS_CC)
+ {
+ rt_event_send(&class_p->event, SDCTR_CMD_IS_COMPLETE_FLG);
+ }
+ else if (status & NORMAL_INT_STATUS_EI)
+ {
+ rt_event_send(&class_p->event, SDCTR_CMD_IS_ERROR_FLG);
+ }
+
+ return;
+}
+
+void rthw_sdctrl_dma_callback(void *args)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)args;
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ status = FSdCtrl_GetDataIrqStatus(pFtsdCtrl);
+
+ if (status & BD_ISR_REG_TRS)
+ {
+ /* send write complete event */
+ rt_event_send(&class_p->event, SDCTR_WRITE_IS_COMPLETE_FLG);
+ }
+
+ if (status & BD_ISR_REG_RESPE)
+ {
+ /* send read complete event */
+ rt_event_send(&class_p->event, SDCTR_READ_IS_COMPLETE_FLG);
+ }
+
+ if (status & BD_ISR_REG_DAIS)
+ {
+ /* send dma errror event */
+ rt_event_send(&class_p->event, SDCTR_DMA_IS_ERROR_FLG);
+ }
+}
+
+void rthw_sdctrl_error_callback(void *args)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)args;
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ status = FSdCtrl_GetErrorIrqStatus(pFtsdCtrl);
+
+ if (status & SDCTR_CMD_TIMEOUT_FLG)
+ {
+ rt_event_send(&class_p->event, SDCTR_CMD_TIMEOUT_FLG);
+ }
+
+ if (status & ERROR_INT_EN_CNR)
+ {
+ rt_event_send(&class_p->event, SDCTR_CMD_RECEIVE_IS_ERROR_FLG);
+ }
+
+ if (status & ERROR_INT_EN_CCRCE)
+ {
+ rt_event_send(&class_p->event, SDCTR_CMD_CRC_IS_ERROR_FLG);
+ }
+}
+
+void rthw_sdctrl_normal_irq(int vector, void *param)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)param;
+ FSdCtrl_NormalIrq(pFtsdCtrl);
+}
+
+void rthw_sdctrl_dma_irq(int vector, void *param)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)param;
+ FSdCtrl_DmaIrq(pFtsdCtrl);
+}
+
+void rthw_sdctrl_err_irq(int vector, void *param)
+{
+ FtsdCtrl_t *pFtsdCtrl = (FtsdCtrl_t *)param;
+ FSdCtrl_ErrIrq(pFtsdCtrl);
+}
+
+ft_error_t rthw_sdctrl_cmd_wait(FtsdCtrl_t *pFtsdCtrl)
+{
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return FTSDC_INVALID_PARAM;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ if (rt_event_recv(&class_p->event, SDCTR_CMD_IS_COMPLETE_FLG | SDCTR_CMD_IS_ERROR_FLG | SDCTR_CMD_CRC_IS_ERROR_FLG, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR,
+ rt_tick_from_millisecond(50000), &status) != RT_EOK)
+ {
+ /* wait cmd completed timeout */
+ LOG_E("wait cmd completed timeout");
+ return FTSDC_TIMEOUT;
+ }
+
+ if (SDCTR_CMD_IS_COMPLETE_FLG == (status & SDCTR_CMD_IS_COMPLETE_FLG))
+ {
+ return FTSDC_SUCCESS;
+ }
+ else
+ {
+ LOG_E("wait cmd is error %x ", status);
+ return FTSDC_FAILURE;
+ }
+}
+
+ft_error_t rthw_sdctrl_read_wait(FtsdCtrl_t *pFtsdCtrl)
+{
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return FTSDC_INVALID_PARAM;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ if (rt_event_recv(&class_p->event, SDCTR_READ_IS_COMPLETE_FLG | SDCTR_CMD_RECEIVE_IS_ERROR_FLG,
+ RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR,
+ rt_tick_from_millisecond(50000), &status) != RT_EOK)
+ {
+ /* wait read completed timeout */
+ LOG_E("wait read completed timeout");
+ return FTSDC_TIMEOUT;
+ }
+
+ if (SDCTR_READ_IS_COMPLETE_FLG == (status & SDCTR_READ_IS_COMPLETE_FLG))
+ {
+ return FTSDC_SUCCESS;
+ }
+ else
+ {
+ LOG_E("wait read is error %x ", status);
+ return FTSDC_FAILURE;
+ }
+}
+
+ft_error_t rthw_sdctrl_write_wait(FtsdCtrl_t *pFtsdCtrl)
+{
+ rt_uint32_t status;
+ ft_sdctrl_class_t *class_p;
+
+ if (RT_NULL == pFtsdCtrl)
+ {
+ return FTSDC_INVALID_PARAM;
+ }
+
+ class_p = rt_container_of(pFtsdCtrl, ft_sdctrl_class_t, ft_sdctrl);
+
+ if (rt_event_recv(&class_p->event, SDCTR_WRITE_IS_COMPLETE_FLG, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR,
+ rt_tick_from_millisecond(50000), &status) != RT_EOK)
+ {
+ /* wait write completed timeout */
+ LOG_E("wait write completed timeout");
+ return FTSDC_TIMEOUT;
+ }
+
+ if (SDCTR_WRITE_IS_COMPLETE_FLG == (status & SDCTR_WRITE_IS_COMPLETE_FLG))
+ {
+ return FTSDC_SUCCESS;
+ }
+ else
+ {
+ LOG_E("wait write is error %x ", status);
+ return FTSDC_FAILURE;
+ }
+}
+
+static rt_err_t rthw_sdctrl_create(ft_sdctrl_class_t *class_p)
+{
+ struct rt_mmcsd_host *host;
+
+ host = mmcsd_alloc_host();
+ if (host == RT_NULL)
+ {
+ LOG_E("L:%d F:%s mmcsd alloc host fail");
+ return RT_ENOMEM;
+ }
+
+ class_p->ft_sdctrl.config = *(FSdCtrl_Config_t *)FSdCtrl_LookupConfig(0);
+ rt_event_init(&class_p->event, "sdctrl", RT_IPC_FLAG_FIFO);
+ rt_mutex_init(&class_p->mutex, "sdctrl", RT_IPC_FLAG_FIFO);
+
+ class_p->host = host;
+ host->ops = &ops;
+ /* range of sd work speed */
+ host->freq_min = 400 * 1000;
+ host->freq_max = 48 * 1000000;
+ host->valid_ocr = 0X00FFFF80; /* The voltage range supported is 1.65v-3.6v */
+ host->flags = MMCSD_BUSWIDTH_4;
+ host->private_data = class_p;
+ /* ready to change */
+
+ return RT_EOK;
+}
+
+int rthw_sdctrl_init(void)
+{
+
+ FtsdCtrl_t *ft_sdctrl_p;
+#ifdef BSP_SDC_USE_IRQ
+ FSdCtrl_Config_t *config_p;
+ FSdCtrl_NormalIrqSelect_t normalIrqFlgs = 0;
+#endif
+
+ rt_kprintf("rthw_sdctrl_init \r\n");
+ RT_ASSERT(rthw_sdctrl_create(&sdctrl_class) == RT_EOK);
+ ft_sdctrl_p = &sdctrl_class.ft_sdctrl;
+
+ FSdCtrl_Reset(ft_sdctrl_p, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay);
+ FsdCtrl_Init(ft_sdctrl_p);
+
+#ifdef BSP_SDC_USE_IRQ
+ config_p = &ft_sdctrl_p->config;
+#ifdef BSP_SDC_IRQ_CARD_REMOVE
+ normalIrqFlgs |= NORMAL_IRQ_CR;
+
+#endif
+ normalIrqFlgs |= NORMAL_IRQ_CC;
+ /* register handler、irq enable bit and wait callback */
+ FSdCtrl_SetHandler(ft_sdctrl_p, FTSDCTRL_CMDIRQID, rthw_sdctrl_nomarl_callback, ft_sdctrl_p);
+ FSdCtrl_NormalIrqSet(ft_sdctrl_p, normalIrqFlgs);
+ FSdCtrl_CmdWaitRegister(ft_sdctrl_p, rthw_sdctrl_cmd_wait);
+
+ FSdCtrl_SetHandler(ft_sdctrl_p, FTSDCTRL_DMADATAIRQID, rthw_sdctrl_dma_callback, ft_sdctrl_p);
+ FSdCtrl_BdIrqSet(ft_sdctrl_p, BD_IRQ_TRS | BD_IRQ_RESPE);
+ FSdCtrl_WriteWaitRegister(ft_sdctrl_p, rthw_sdctrl_write_wait);
+ FSdCtrl_ReadWaitRegister(ft_sdctrl_p, rthw_sdctrl_read_wait);
+
+ config_p->workMode = FTSDCTRL_CMD_IRQ_MASK | FTSDCTRL_DATA_WRITE_IRQ_MASK | FTSDCTRL_DATA_READ_IRQ_MASK;
+
+#else
+
+#endif
+
+ /* install normal irq */
+
+ rt_hw_interrupt_install(ft_sdctrl_p->config.normalIrqNum, rthw_sdctrl_normal_irq,
+ &sdctrl_class.ft_sdctrl, "normalIrq");
+ rt_hw_interrupt_umask(ft_sdctrl_p->config.normalIrqNum);
+
+ rt_hw_interrupt_install(ft_sdctrl_p->config.dmaIrqNum, rthw_sdctrl_dma_irq,
+ &sdctrl_class.ft_sdctrl, "dmaIrq");
+ rt_hw_interrupt_umask(ft_sdctrl_p->config.dmaIrqNum);
+
+ return 0;
+}
+
+INIT_DEVICE_EXPORT(rthw_sdctrl_init);
+
+void ft2004_mmcsd_change(void)
+{
+ mmcsd_change(sdctrl_class.host);
+}
+
+rt_bool_t ft2004_card_status(void)
+{
+ return FSdCtrl_CardDetect(&sdctrl_class.ft_sdctrl);
+}
+
+rt_err_t ft2004_card_remove_check(rt_int32_t timeout, rt_uint32_t *status)
+{
+ return rt_event_recv(&sdctrl_class.event, SDCTR_CARD_REMOVE_FLG, RT_EVENT_FLAG_CLEAR | RT_EVENT_FLAG_OR,
+ timeout, status);
+}
+
+void ft2004_sdctrl_reset(void)
+{
+ FSdCtrl_Reset(&sdctrl_class.ft_sdctrl, (pFtsdCtrl_delayTimer_t)rthw_sdctrl_delay);
+ FsdCtrl_Init(&sdctrl_class.ft_sdctrl);
+
+#ifdef BSP_SDC_USE_IRQ
+ FSdCtrl_NormalIrqSet(&sdctrl_class.ft_sdctrl, NORMAL_IRQ_CC | NORMAL_IRQ_CR | NORMAL_IRQ_EI);
+ FSdCtrl_BdIrqSet(&sdctrl_class.ft_sdctrl, BD_IRQ_TRS | BD_IRQ_RESPE);
+#endif
+}
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_sdctrl.h b/bsp/ft2004/drivers/drv_sdctrl.h
new file mode 100644
index 0000000000..dba34f2525
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_sdctrl.h
@@ -0,0 +1,47 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-18 Carl the first version
+ */
+
+#ifndef __DRV_SDCTRL_H__
+#define __DRV_SDCTRL_H__
+
+#include <rtthread.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define SDCTR_CMD_IS_COMPLETE_FLG 0x1UL /* Command is complete */
+#define SDCTR_WRITE_IS_COMPLETE_FLG 0x2UL
+#define SDCTR_READ_IS_COMPLETE_FLG 0x4UL
+#define SDCTR_CMD_IS_ERROR_FLG 0x8UL
+#define SDCTR_CMD_CRC_IS_ERROR_FLG 0x10UL /* Command CRC error */
+#define SDCTR_DMA_IS_ERROR_FLG 0x20UL /* */
+#define SDCTR_CARD_REMOVE_FLG 0x40UL /* Card remove */
+#define SDCTR_CMD_TIMEOUT_FLG 0x70UL /* command timeout */
+#define SDCTR_CMD_RECEIVE_IS_ERROR_FLG 0x80UL /* CMD receive is error */
+
+#ifndef SDCTR_BUFF_SIZE
+#define SDCTR_BUFF_SIZE (512 * 128)
+#endif
+
+#ifndef SDCTR_ALIGN_LEN
+#define SDCTR_ALIGN_LEN (32)
+#endif
+
+ void ft2004_mmcsd_change(void);
+ rt_bool_t ft2004_card_status(void);
+ rt_err_t ft2004_card_remove_check(rt_int32_t timeout, rt_uint32_t *status);
+ void ft2004_sdctrl_reset(void);
+#ifdef __cplusplus
+}
+#endif
+
+#endif // !
diff --git a/bsp/ft2004/drivers/drv_spi.c b/bsp/ft2004/drivers/drv_spi.c
new file mode 100644
index 0000000000..0ac38709e4
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_spi.c
@@ -0,0 +1,449 @@
+/*
+ * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0.
+ *
+ * @Date: 2021-04-25 14:01:29
+ * @LastEditTime: 2021-05-26 15:42:52
+ * @Description:  This files is for
+ *
+ * @Modify History:
+ * Ver   Who        Date         Changes
+ * ----- ------     --------    --------------------------------------
+ */
+
+#include "drv_spi.h"
+#include <rtthread.h>
+#include <rtdevice.h>
+#include <string.h>
+#include "ft_spi.h"
+#include "ft_mux.h"
+#include "ft_trace.h"
+#include "ft_generic_timer.h"
+
+#ifdef BSP_USE_SPI
+
+#define DRV_DEBUG
+#define LOG_TAG "drv.spi"
+#include <drv_log.h>
+
+typedef void (*spi_cs_handler_t)(const rt_bool_t select);
+typedef struct
+{
+ FSpi_Ctrl_t spi_ctrl;
+ struct rt_spi_bus spi_bus;
+ uint16_t spi_cs_pin;
+ spi_cs_handler_t spi_cs_handler;
+} ft2004_spi_class;
+
+void ft2004_spi_cs(const rt_bool_t select);
+static ft2004_spi_class spi_obj = {
+ .spi_cs_handler = ft2004_spi_cs,
+ .spi_ctrl = {
+ .CtrlId = SPI_CTRL_ID_0,
+ .DevId = SPI_DEV_ID_0,
+ .IsReady = FALSE,
+ .CsPin = 5, /* use pin 5 in gpio group a as cs signal pin */
+ },
+};
+static const FSpi_Conf_t spi_conf[NUM_OF_SPI_CTRL] =
+ {
+ {
+ .DevAddr = {0x00, 0x00, 0x00, 0x00},
+ .DevAddrLen = SPI_4_BYTE_ADDR,
+ .WorkMode = SPI_CTRL_MASTER_MODE,
+ /* mode 2 CPOL = 1, CPHA = 0 */
+ .Cpol = SPI_CTRL_CPOL_HIGH,
+ .Cpha = SPI_CTRL_CPHA_1EDGE,
+ .BaudRDiv = SPI_SCKDV_4,
+ },
+ {
+ .DevAddr = {0x00, 0x00, 0x00, 0x00},
+ .DevAddrLen = SPI_4_BYTE_ADDR,
+ .WorkMode = SPI_CTRL_MASTER_MODE,
+ .Cpol = SPI_CTRL_CPOL_HIGH,
+ .Cpha = SPI_CTRL_CPHA_1EDGE,
+ .BaudRDiv = SPI_SCKDV_MAX,
+ }};
+
+inline static ft2004_spi_class *ft2004_spi_get_class()
+{
+ return &spi_obj;
+}
+
+inline static FSpi_Ctrl_t *ft2004_spi_get_ctrl()
+{
+ return &(ft2004_spi_get_class()->spi_ctrl);
+}
+
+static const FSpi_Conf_t *ft2004_lookup_conf(FT_IN FSpi_CtrlId_t CtrlId)
+{
+ return &spi_conf[CtrlId];
+}
+
+void ft2004_spi_cs(const rt_bool_t select)
+{
+ FSpi_Ctrl_t *ctrl_p = ft2004_spi_get_ctrl();
+ FSpi_SelectSlave(ctrl_p, ctrl_p->DevId, (bool_t)select);
+}
+
+/**spi flash operations***/
+u32 ft2004_spi_transcation(const u8 tx_data, u8 *rx_data_p)
+{
+ FSpi_Ctrl_t *ctrl_p = ft2004_spi_get_ctrl();
+ u32 ret = ERR_SPI_OK;
+
+ ret = FSpi_ReadWriteByte(ctrl_p, tx_data, rx_data_p);
+ return ret;
+}
+/**spi flash operations***/
+
+static rt_err_t ft2004_spi_init(struct rt_spi_configuration *cfg)
+{
+ FSpi_Ctrl_t *ctrl_p = ft2004_spi_get_ctrl();
+ FSpi_DevId_t dev_id;
+ u32 ret = ERR_SPI_OK;
+
+ //RT_ASSERT(cfg != RT_NULL);
+ RT_ASSERT(ctrl_p != RT_NULL);
+ dev_id = ctrl_p->DevId;
+
+ /* get spi flash default config */
+ ctrl_p->Config = *(ft2004_lookup_conf(dev_id));
+
+ /* change config according to inputs, cfg could be RT_NULL */
+
+ /* reset ctrl block */
+ ctrl_p->IsReady = FALSE;
+
+ /* set spi pin mux */
+ Ft_setSpiMux(ctrl_p->CtrlId);
+
+ /* init spi ctrl */
+ ret = FSpi_Init(ctrl_p);
+
+ if (ERR_SPI_OK == ret)
+ {
+ return RT_EOK;
+ }
+ else
+ {
+ return -RT_ERROR;
+ }
+}
+
+static rt_uint32_t spi_xfer(struct rt_spi_device *device, struct rt_spi_message *message)
+{
+ rt_size_t message_length, loop;
+ rt_uint8_t *recv_buf;
+ const rt_uint8_t *send_buf;
+ u32 tx_rx_result = ERR_SPI_OK;
+ spi_cs_handler_t cs_handler = ft2004_spi_get_class()->spi_cs_handler;
+
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(device->bus != RT_NULL);
+ RT_ASSERT(device->bus->parent.user_data != RT_NULL);
+ RT_ASSERT(message != RT_NULL);
+
+ if (message->cs_take && cs_handler)
+ {
+ cs_handler(TRUE);
+ }
+
+ message_length = message->length;
+ recv_buf = message->recv_buf;
+ send_buf = message->send_buf;
+
+ /* handle msg */
+ for (loop = 0; loop < message_length; loop++)
+ {
+ /* start data exchange */
+ if ((message->recv_buf) && (message->send_buf))
+ {
+ /* need tx and rx */
+ tx_rx_result |= ft2004_spi_transcation(*send_buf, recv_buf);
+ send_buf++;
+ recv_buf++;
+ }
+ else if (message->send_buf)
+ {
+ /* tx only */
+ tx_rx_result |= ft2004_spi_transcation(*send_buf, RT_NULL);
+ send_buf++;
+ }
+ else
+ {
+ /* rx only */
+ tx_rx_result |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, recv_buf);
+ recv_buf++;
+ }
+ }
+
+ if (ERR_SPI_OK != tx_rx_result)
+ {
+ LOG_E("spi transfer error : 0x%x", tx_rx_result);
+ message->length = 0;
+ }
+ else
+ {
+ }
+
+ if (message->cs_release && cs_handler)
+ {
+ cs_handler(FALSE);
+ }
+
+ return message->length;
+}
+
+static rt_err_t spi_configure(struct rt_spi_device *device,
+ struct rt_spi_configuration *configuration)
+{
+ RT_ASSERT(device != RT_NULL);
+ RT_ASSERT(configuration != RT_NULL);
+
+ return ft2004_spi_init(configuration);
+}
+
+static const struct rt_spi_ops ft2004_spi_ops =
+ {
+ .configure = spi_configure,
+ .xfer = spi_xfer,
+};
+
+/**
+ * Attach the spi device to SPI bus, this function must be used after initialization.
+ */
+rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, uint16_t cs_gpio_pin)
+{
+ rt_err_t result;
+ struct rt_spi_device *spi_device;
+ ft2004_spi_class *spi_class = ft2004_spi_get_class();
+
+ RT_ASSERT(spi_class != RT_NULL);
+
+ spi_device = (struct rt_spi_device *)rt_malloc(sizeof(struct rt_spi_device));
+ RT_ASSERT(spi_device != RT_NULL);
+
+ result = rt_spi_bus_attach_device(spi_device, device_name, bus_name, RT_NULL);
+
+ LOG_I("attach result 0x%x", result);
+
+ if (result != RT_EOK)
+ {
+ if (spi_device)
+ {
+ rt_free(spi_device);
+ }
+ }
+ return result;
+}
+
+static int rt_hw_spi_bus_init(void)
+{
+ rt_err_t result;
+ ft2004_spi_class *spi_class = ft2004_spi_get_class();
+
+ LOG_I("init spi ctrl");
+ spi_class->spi_bus.parent.user_data = &spi_class->spi_bus;
+ result = rt_spi_bus_register(&spi_class->spi_bus, SPI_BUS_NAME, &ft2004_spi_ops);
+ return result;
+}
+
+int rt_hw_spi_init(void)
+{
+ return rt_hw_spi_bus_init();
+}
+INIT_BOARD_EXPORT(rt_hw_spi_init);
+
+static void rthw_spi_delay(u32 delayCnt)
+{
+ Ft_GenericTimer_UsDelay(delayCnt);
+}
+
+/************spi flash operatiosn implemented for sample test****************/
+/* definition of s25fs maunfactor id */
+typedef struct
+{
+ u8 Mid;
+ u8 MemoryType;
+ u8 Density;
+ u8 RemainBytes;
+ u8 PhySectArch;
+ u8 FamilyID;
+} ft2004_manuid_t;
+
+/* definition of cmd for s25fs */
+#define S25FS_ENABLE_WR 0x06
+#define S25FS_DISABLE_WR 0x04
+#define S25FS_READ_ID 0x9F
+#define S25FS_READ_4BYTE_ADD 0x13
+#define S25FS_ERASE_4BYTE_ADD 0x21
+#define S25FS_READ_STATUS_1 0x05
+#define S25FS_READ_FLASH_PARAM 0x5A
+
+static void ft2004_dump_manuid(const ft2004_manuid_t *pId)
+{
+ rt_kprintf("0x%x, 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\r\n",
+ pId->Mid, pId->MemoryType, pId->Density, pId->RemainBytes,
+ pId->PhySectArch, pId->FamilyID);
+}
+
+static u32 ft2004_read_in_4byte_addr(const u32 ReadAddr, const u32 BytesToRead, u8 *pBuf)
+{
+ u32 ret = ERR_SPI_OK;
+ u32 loop;
+
+ RT_ASSERT(RT_NULL != pBuf);
+
+ ft2004_spi_cs(TRUE);
+ ret |= ft2004_spi_transcation(S25FS_READ_4BYTE_ADD, RT_NULL);
+ /* only 4-bytes address, MSB first */
+ ret |= ft2004_spi_transcation((u8)(ReadAddr >> 24), RT_NULL);
+ ret |= ft2004_spi_transcation((u8)(ReadAddr >> 16), RT_NULL);
+ ret |= ft2004_spi_transcation((u8)(ReadAddr >> 8), RT_NULL);
+ ret |= ft2004_spi_transcation((u8)ReadAddr, RT_NULL);
+ /* read out data */
+ for (loop = 0; loop < BytesToRead; loop++)
+ {
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, pBuf + loop);
+ if (ERR_SPI_OK != ret)
+ {
+ break;
+ }
+ }
+ ft2004_spi_cs(FALSE);
+ return ret;
+}
+
+u32 ft2004_spi_enable_wr(const bool_t enable)
+{
+ u32 ret = ERR_SPI_OK;
+ ft2004_spi_cs(TRUE);
+ if (enable)
+ {
+ ret |= ft2004_spi_transcation(S25FS_ENABLE_WR, RT_NULL);
+ }
+ else
+ {
+ ret |= ft2004_spi_transcation(S25FS_DISABLE_WR, RT_NULL);
+ }
+ ft2004_spi_cs(FALSE);
+ return ret;
+}
+
+u32 ft2004_erase_sector_in_4byte_addr(const u32 sector_addr)
+{
+ u32 Ret = ERR_SPI_OK;
+
+ ft2004_spi_enable_wr(TRUE);
+ LOG_I("erase sector 0x%x", Ret);
+ if (ERR_SPI_OK != Ret)
+ {
+ return Ret;
+ }
+
+ ft2004_spi_cs(TRUE);
+ Ret |= ft2004_spi_transcation(S25FS_ERASE_4BYTE_ADD, RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(sector_addr >> 24), RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(sector_addr >> 16), RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(sector_addr >> 8), RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(sector_addr), RT_NULL);
+ ft2004_spi_cs(FALSE);
+
+ return Ret;
+}
+
+u32 ft2004_spi_read_params(const u32 Addr)
+{
+ u32 Ret = ERR_SPI_OK;
+ u8 dat[8] = {0};
+ u32 loop;
+
+ ft2004_spi_cs(TRUE);
+ Ret |= ft2004_spi_transcation(S25FS_READ_FLASH_PARAM, RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(Addr >> 16), RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(Addr >> 8), RT_NULL);
+ Ret |= ft2004_spi_transcation((u8)(Addr), RT_NULL);
+ for (loop = 0; loop < 8; loop++)
+ {
+ Ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, dat + loop);
+ rt_kprintf("%d: 0x%x", loop, *(dat + loop));
+ }
+
+ ft2004_spi_cs(FALSE);
+ return Ret;
+}
+
+static u32 ft2004_spi_readid_for_test(ft2004_manuid_t *pId)
+{
+ FSpi_Ctrl_t *ctrl_p = ft2004_spi_get_ctrl();
+ u32 ret = ERR_SPI_OK;
+
+ if (!ctrl_p->IsReady)
+ {
+ return ERR_SPI_NOT_READY;
+ }
+
+ RT_ASSERT(RT_NULL != pId);
+
+ ft2004_spi_cs(TRUE);
+
+ /* shifting the command code “90H” followed by a 24-bit address */
+ ret |= ft2004_spi_transcation(S25FS_READ_ID, RT_NULL);
+
+ /* Manufacturer ID and the Device ID are shifted out */
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->Mid);
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->MemoryType);
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->Density);
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->RemainBytes);
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->PhySectArch);
+ ret |= ft2004_spi_transcation(SPI_DUMMY_TX_DATA, &pId->FamilyID);
+ ft2004_spi_cs(FALSE);
+
+ if (ERR_SPI_OK == ret)
+ {
+ ft2004_dump_manuid(pId);
+ }
+
+ return ret;
+}
+
+static void spi_9f_s25fs_sample(int argc, char *argv[])
+{
+ ft2004_manuid_t dev_id;
+ u32 ret = ERR_SPI_OK;
+ u32 delay = SPI_TIMEOUT * 10;
+
+ rt_kprintf("test s25fs spi flash\r\n");
+ ret |= ft2004_spi_init(RT_NULL);
+ ret |= ft2004_spi_readid_for_test(&dev_id);
+
+ rt_kprintf("result is: 0x%x \r\n", ret);
+ while (--delay)
+ {
+ rthw_spi_delay(10);
+ }
+}
+MSH_CMD_EXPORT(spi_9f_s25fs_sample, "spi s25fs cmd 9fH sample");
+
+static u8 read_buf[256];
+static void spi_5a_s25fs_sample(int argc, char *argv[])
+{
+ u32 ret = ERR_SPI_OK;
+ u32 delay = SPI_TIMEOUT * 10;
+ u32 read_addr = 0x0000;
+
+ rt_kprintf("test s25fs spi flash\r\n");
+ ret |= ft2004_spi_init(RT_NULL);
+ ret |= ft2004_spi_read_params(read_addr);
+ ret |= ft2004_read_in_4byte_addr(read_addr, 256, read_buf);
+ rt_kprintf("result is: 0x%x \r\n", ret);
+ while (--delay)
+ {
+ rthw_spi_delay(10);
+ }
+}
+MSH_CMD_EXPORT(spi_5a_s25fs_sample, "spi s25fs cmd 5aH sample");
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_spi.h b/bsp/ft2004/drivers/drv_spi.h
new file mode 100644
index 0000000000..5b31ab678c
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_spi.h
@@ -0,0 +1,29 @@
+/*
+ * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0.
+ *
+ * @Date: 2021-04-25 14:01:39
+ * @LastEditTime: 2021-04-29 09:40:13
+ * @Description:  This files is for
+ *
+ * @Modify History:
+ * Ver   Who        Date         Changes
+ * ----- ------     --------    --------------------------------------
+ */
+#ifndef FT_DRIVERS_RTT_SPI_H
+#define FT_DRIVERS_RTT_SPI_H
+
+#include <rtthread.h>
+
+#define SPI_BUS_NAME "spi0"
+#define SPI_DEV_NAME "S25FS256"
+
+rt_err_t rt_hw_spi_device_attach(const char *bus_name, const char *device_name, uint16_t cs_gpio_pin);
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#endif
diff --git a/bsp/ft2004/drivers/drv_spi_flash.c b/bsp/ft2004/drivers/drv_spi_flash.c
new file mode 100644
index 0000000000..26d8b44712
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_spi_flash.c
@@ -0,0 +1,43 @@
+/*
+ * @ : Copyright (c) 2021 Phytium Information Technology, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0.
+ *
+ * @Date: 2021-04-25 14:01:16
+ * @LastEditTime: 2021-04-30 14:43:12
+ * @Description:  This files is for
+ *
+ * @Modify History:
+ * Ver   Who        Date         Changes
+ * ----- ------     --------    --------------------------------------
+ */
+
+#include <board.h>
+#include <drv_spi.h>
+#include <rtdevice.h>
+#include <rthw.h>
+#include <finsh.h>
+#include "ft_spi.h"
+
+#ifdef BSP_USE_SPI
+
+#include "spi_flash.h"
+#include "spi_flash_sfud.h"
+
+static int rt_hw_spi_flash_init(void)
+{
+ uint16_t cs_pin = 5;
+ rt_hw_spi_device_attach(SPI_BUS_NAME, SPI_DEV_NAME, cs_pin);
+
+ rt_kprintf("attach spi flash\r\n");
+ /* lookup flah */
+ if (RT_NULL == rt_sfud_flash_probe("S25FS256S", SPI_DEV_NAME))
+ {
+ rt_kprintf("attach spi flash failed\r\n");
+ return -RT_ERROR;
+ }
+
+ return RT_EOK;
+}
+INIT_COMPONENT_EXPORT(rt_hw_spi_flash_init);
+#endif
diff --git a/bsp/ft2004/drivers/drv_usart.c b/bsp/ft2004/drivers/drv_usart.c
new file mode 100644
index 0000000000..96ac3c97eb
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_usart.c
@@ -0,0 +1,192 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-04 Carl the first version
+ */
+
+#include "board.h"
+#include "drv_usart.h"
+#include "interrupt.h"
+#include "serial.h"
+#include "rtconfig.h"
+
+#ifdef RT_USING_SERIAL
+
+extern u32 FUart_GetInterruptMask(Ft_Uart *uart_ptr);
+
+static void Ft_Os_Uart_Callback(void *Args, u32 Event, u32 EventData);
+
+static void rt_hw_uart_isr(int irqno, void *param)
+{
+ Ft_Uart *uart_ptr = (Ft_Uart *)param;
+ FUart_InterruptHandler(uart_ptr);
+}
+
+static rt_err_t uart_configure(struct rt_serial_device *serial, struct serial_configure *cfg)
+{
+ struct drv_usart *uart = RT_NULL;
+ Ft_Uart *uart_ptr = RT_NULL;
+ u32 RegTemp;
+ u32 ret;
+ RT_ASSERT(serial != RT_NULL);
+ RT_ASSERT(cfg != RT_NULL);
+ uart = rt_container_of(serial, struct drv_usart, serial);
+ uart_ptr = uart->handle;
+
+ RT_ASSERT(FUart_CfgInitialize(uart_ptr, FUart_LookupConfig(uart_ptr->Config.InstanceId)) == FST_SUCCESS);
+ FUart_SetHandler(uart_ptr, Ft_Os_Uart_Callback, serial);
+ rt_hw_interrupt_install(uart_ptr->Config.IsrNum, rt_hw_uart_isr, uart_ptr, "uart");
+ rt_hw_interrupt_umask(uart_ptr->Config.IsrNum);
+
+ //<! 设置波特率
+ ret = FUart_SetBaudRate(uart_ptr, cfg->baud_rate);
+ RT_ASSERT(ret == FST_SUCCESS);
+
+ //<! 打开接收中断
+ RegTemp = FUart_GetInterruptMask(uart_ptr);
+ RegTemp |= UARTMIS_RTMIS;
+ FUart_SetInterruptMask(uart_ptr, RegTemp);
+ FUart_SetOptions(uart_ptr, FUART_OPTION_UARTEN | FUART_OPTION_RXEN | FUART_OPTION_TXEN | FUART_OPTION_FIFOEN);
+ return RT_EOK;
+}
+
+static rt_err_t uart_control(struct rt_serial_device *serial, int cmd, void *arg)
+{
+ struct drv_usart *uart = RT_NULL;
+ Ft_Uart *uart_ptr = RT_NULL;
+ RT_ASSERT(serial != RT_NULL);
+
+ uart = rt_container_of(serial, struct drv_usart, serial);
+ uart_ptr = uart->handle;
+
+ switch (cmd)
+ {
+ case RT_DEVICE_CTRL_CLR_INT:
+ /* disable rx irq */
+ rt_hw_interrupt_mask(uart_ptr->Config.IsrNum);
+ break;
+
+ case RT_DEVICE_CTRL_SET_INT:
+ /* enable rx irq */
+ rt_hw_interrupt_umask(uart_ptr->Config.IsrNum);
+ break;
+ }
+
+ return RT_EOK;
+}
+
+static void Ft_Os_Uart_Callback(void *Args, u32 Event, u32 EventData)
+{
+ struct rt_serial_device *serial = (struct rt_serial_device *)Args;
+
+ if (FUART_EVENT_RECV_DATA == Event || FUART_EVENT_RECV_TOUT == Event)
+ {
+ rt_hw_serial_isr(serial, RT_SERIAL_EVENT_RX_IND);
+ }
+ else if (FUART_EVENT_RECV_ERROR == Event)
+ {
+ }
+ else if (FUART_EVENT_SENT_DATA == Event)
+ {
+ }
+ else if (FUART_EVENT_PARE_FRAME_BRKE == Event)
+ {
+ }
+ else if (FUART_EVENT_RECV_ORERR == Event)
+ {
+ }
+
+ if (FUART_EVENT_SENT_DATA == Event)
+ {
+ }
+ else
+ {
+ }
+}
+
+static int uart_putc(struct rt_serial_device *serial, char c)
+{
+ struct drv_usart *uart = RT_NULL;
+ Ft_Uart *uart_ptr = RT_NULL;
+ RT_ASSERT(serial != RT_NULL);
+
+ uart = rt_container_of(serial, struct drv_usart, serial);
+ uart_ptr = uart->handle;
+
+ FUart_SendByte(uart_ptr->Config.BaseAddress, c);
+
+ return 1;
+}
+
+static int uart_getc(struct rt_serial_device *serial)
+{
+ int ch;
+ struct drv_usart *uart = RT_NULL;
+ Ft_Uart *uart_ptr = RT_NULL;
+ RT_ASSERT(serial != RT_NULL);
+
+ uart = rt_container_of(serial, struct drv_usart, serial);
+ uart_ptr = uart->handle;
+
+ ch = FUart_GetChar(uart_ptr->Config.BaseAddress);
+ if (ch == 0xff)
+ ch = -1;
+
+ return ch;
+}
+
+static const struct rt_uart_ops _uart_ops =
+ {
+ uart_configure,
+ uart_control,
+ uart_putc,
+ uart_getc,
+};
+
+#ifdef RT_USING_UART0
+static Ft_Uart Ft_Uart0;
+static struct drv_usart _RtUart0;
+#endif
+
+#ifdef RT_USING_UART1
+static Ft_Uart Ft_Uart1;
+static struct drv_usart _RtUart1;
+#endif
+
+int rt_hw_uart_init(void)
+{
+
+ struct serial_configure config = RT_SERIAL_CONFIG_DEFAULT;
+
+#ifdef RT_USING_UART0
+ config.bufsz = RT_SERIAL_RB_BUFSZ;
+ _RtUart0.serial.ops = &_uart_ops;
+ _RtUart0.serial.config = config;
+ Ft_Uart0.Config.InstanceId = FT_UART0_ID;
+ _RtUart0.Handle = &Ft_Uart0;
+
+ rt_hw_serial_register(&_RtUart0.serial, "uart0",
+ RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+ &_RtUart0);
+#endif
+
+#ifdef RT_USING_UART1
+ config.bufsz = RT_SERIAL_RB_BUFSZ;
+ _RtUart1.serial.ops = &_uart_ops;
+ _RtUart1.serial.config = config;
+ Ft_Uart1.Config.InstanceId = FT_UART1_ID;
+ _RtUart1.handle = &Ft_Uart1;
+ rt_hw_serial_register(&_RtUart1.serial, "uart1",
+ RT_DEVICE_FLAG_RDWR | RT_DEVICE_FLAG_INT_RX,
+ &_RtUart1);
+#endif
+
+ return 0;
+}
+INIT_BOARD_EXPORT(rt_hw_uart_init);
+
+#endif /* RT_USING_SERIAL */
diff --git a/bsp/ft2004/drivers/drv_usart.h b/bsp/ft2004/drivers/drv_usart.h
new file mode 100644
index 0000000000..aff47caa29
--- /dev/null
+++ b/bsp/ft2004/drivers/drv_usart.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-03-04 Carl the first version
+ */
+
+#ifndef __DRV_USART_H__
+#define __DRV_USART_H__
+
+#include <rtthread.h>
+#include "rtdevice.h"
+#include "ft_uart.h"
+
+struct drv_usart
+{
+ Ft_Uart *handle;
+
+ struct rt_serial_device serial;
+};
+
+#endif // !
diff --git a/bsp/ft2004/drivers/ft2004.c b/bsp/ft2004/drivers/ft2004.c
new file mode 100644
index 0000000000..da04993276
--- /dev/null
+++ b/bsp/ft2004/drivers/ft2004.c
@@ -0,0 +1,88 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-29 Carl the first version
+ *
+ */
+
+#include <rtthread.h>
+#include "ft2004.h"
+#include "gicv3.h"
+
+rt_uint64_t get_main_cpu_affval(void)
+{
+ return 0;
+}
+
+rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list)
+{
+
+ if (*cpu_mask == 0)
+ {
+ return 0;
+ }
+
+ *target_list = 0;
+ *cluster_id = 0;
+
+ if (*cpu_mask & 0x3)
+ {
+ if ((*cpu_mask & 0x3) == 0x3)
+ {
+ *target_list = 3;
+ }
+ else if ((*cpu_mask & 0x1))
+ {
+ *target_list = 1;
+ }
+ else
+ {
+ *target_list = 2;
+ }
+ *cpu_mask &= ~0x3;
+ }
+ else if (*cpu_mask & 0xc)
+ {
+ *cluster_id = 0x100;
+ if ((*cpu_mask & 0xc) == 0xc)
+ {
+ *target_list = 3;
+ }
+ else if ((*cpu_mask & 0x4))
+ {
+ *target_list = 1;
+ }
+ else
+ {
+ *target_list = 2;
+ }
+ *cpu_mask &= ~0xc;
+ }
+ else
+ {
+ *cpu_mask = 0;
+ return 0;
+ }
+
+ return 1;
+}
+
+#ifdef RT_USING_SMP
+
+void send_core_isg(void)
+{
+ for (size_t i = 0; i <= 0xf; i++)
+ {
+ /* code */
+ rt_kprintf("i %x \r\n", i);
+ arm_gic_send_affinity_sgi(0, 0, i, 0);
+ rt_thread_mdelay(100);
+ }
+}
+MSH_CMD_EXPORT(send_core_isg, send_core_isg);
+
+#endif
diff --git a/bsp/ft2004/drivers/ft2004.h b/bsp/ft2004/drivers/ft2004.h
new file mode 100644
index 0000000000..fe3293ab94
--- /dev/null
+++ b/bsp/ft2004/drivers/ft2004.h
@@ -0,0 +1,25 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-29 Carl the first version
+ *
+ */
+
+#ifndef __FT2004_H__
+#define __FT2004_H__
+
+#include <rthw.h>
+#include <rtthread.h>
+
+#define ARM_GIC_NR_IRQS 160
+#define ARM_GIC_MAX_NR 1
+#define MAX_HANDLERS 160
+#define GIC_IRQ_START 0
+
+rt_uint64_t get_main_cpu_affval(void);
+
+#endif // !
diff --git a/bsp/ft2004/drivers/ft2004_cpu.S b/bsp/ft2004/drivers/ft2004_cpu.S
new file mode 100644
index 0000000000..2e713a5b20
--- /dev/null
+++ b/bsp/ft2004/drivers/ft2004_cpu.S
@@ -0,0 +1,44 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-29 Carl the first version
+ *
+ */
+
+#include "rtconfig.h"
+
+.globl rt_hw_cpu_id
+rt_hw_cpu_id:
+ mrc p15, 0, r0, c0, c0, 5
+ ubfx r0, r0, #0, #12
+ cmp r0, #0
+ beq core0
+ cmp r0, #1
+ beq core1
+ cmp r0, #256
+ beq core2
+ mov r1 ,#257
+ cmp r0, r1
+ beq core3
+ b default
+core0:
+ mov r0, #0
+ b return
+core1:
+ mov r0, #1
+ b return
+core2:
+ mov r0, #2
+ b return
+core3:
+ mov r0, #3
+ b return
+default:
+ and r0, r0, #15
+return:
+ bx lr
+
diff --git a/bsp/ft2004/drivers/secondary_cpu.c b/bsp/ft2004/drivers/secondary_cpu.c
new file mode 100644
index 0000000000..5ffcadbefe
--- /dev/null
+++ b/bsp/ft2004/drivers/secondary_cpu.c
@@ -0,0 +1,86 @@
+/*
+ * @ : Copyright (c) 2020 Phytium Information Technology, Inc.
+ *
+ * SPDX-License-Identifier: Apache-2.0.
+ *
+ * @Date: 2021-05-26 10:09:45
+ * @LastEditTime: 2021-05-26 10:31:44
+ * @Description:  This files is for
+ *
+ * @Modify History:
+ *  Ver   Who        Date         Changes
+ * ----- ------     --------    --------------------------------------
+ */
+
+#include <rtthread.h>
+#include "board.h"
+#include <gicv3.h>
+
+#ifdef RT_USING_SMP
+#include <interrupt.h>
+#include "ft_psci.h"
+#include "ft_generic_timer.h"
+
+extern int rt_hw_timer_init(void);
+extern void secondary_cpu_start(void);
+
+void rt_hw_secondary_cpu_up(void)
+{
+
+ rt_uint32_t i;
+ rt_uint32_t cpu_mask = 0;
+
+ rt_kprintf("rt_hw_secondary_cpu_up is processing \r\n");
+ for (i = 1; i < RT_CPUS_NR; i++)
+ {
+ if (i == 1)
+ {
+ /* code */
+ FPsci_CpuOn(1 << i, (rt_uint32_t)secondary_cpu_start);
+ cpu_mask = 2;
+ }
+ else if (i == 2)
+ {
+ FPsci_CpuOn(1 << i, (rt_uint32_t)secondary_cpu_start);
+ cpu_mask = 4;
+ }
+ else if (i == 3)
+ {
+ FPsci_CpuOn(1 << i, (rt_uint32_t)secondary_cpu_start);
+ cpu_mask = 8;
+ }
+ else
+ {
+ continue;
+ }
+
+ __asm__ volatile("dsb" ::
+ : "memory");
+ rt_hw_ipi_send(RT_SCHEDULE_IPI, cpu_mask);
+ Ft_GenericTimer_UsDelay(1000000);
+ }
+}
+
+void secondary_cpu_c_start(void)
+{
+ rt_hw_vector_init();
+ rt_hw_spin_lock(&_cpus_lock);
+
+ arm_gic_cpu_init(0);
+ arm_gic_redist_init(0);
+
+ rt_hw_timer_init();
+
+ rt_hw_interrupt_set_priority(RT_SCHEDULE_IPI, 16);
+ rt_hw_interrupt_umask(RT_SCHEDULE_IPI);
+
+ rt_system_scheduler_start();
+}
+
+void rt_hw_secondary_cpu_idle_exec(void)
+{
+ asm volatile("wfe" ::
+ : "memory", "cc");
+}
+
+#endif
diff --git a/bsp/ft2004/drivers/serial.h b/bsp/ft2004/drivers/serial.h
new file mode 100644
index 0000000000..cf4e337a1f
--- /dev/null
+++ b/bsp/ft2004/drivers/serial.h
@@ -0,0 +1,20 @@
+/*
+ * Copyright (c) 2006-2021, RT-Thread Development Team
+ *
+ * SPDX-License-Identifier: Apache-2.0
+ *
+ * Change Logs:
+ * Date Author Notes
+ * 2021-04-29 Carl the first version
+ *
+ */
+
+#ifndef __UART_H__
+#define __UART_H__
+
+#include <board.h>
+
+int rt_hw_uart_init(void);
+
+#endif
+