From 8a41e487e63a15cef883eccafc464cae5c7de391 Mon Sep 17 00:00:00 2001 From: huanghe Date: Tue, 18 May 2021 17:05:25 +0800 Subject: [PATCH 1/4] init ft2004 project --- bsp/ft2004/.config | 690 ++++++++++++++++ bsp/ft2004/Kconfig | 30 + bsp/ft2004/README.md | 236 ++++++ bsp/ft2004/SConscript | 14 + bsp/ft2004/SConstruct | 40 + bsp/ft2004/applications/SConscript | 11 + bsp/ft2004/applications/startup.c | 78 ++ bsp/ft2004/cpu/armv7.h | 72 ++ bsp/ft2004/cpu/cache.c | 100 +++ bsp/ft2004/cpu/context_gcc.S | 182 +++++ bsp/ft2004/cpu/cp15.h | 41 + bsp/ft2004/cpu/cp15_gcc.S | 138 ++++ bsp/ft2004/cpu/cpu.c | 89 +++ bsp/ft2004/cpu/gic.c | 495 ++++++++++++ bsp/ft2004/cpu/gic.h | 62 ++ bsp/ft2004/cpu/gicv3.c | 718 +++++++++++++++++ bsp/ft2004/cpu/gicv3.h | 188 +++++ bsp/ft2004/cpu/gtimer.c | 171 ++++ bsp/ft2004/cpu/gtimer.h | 26 + bsp/ft2004/cpu/interrupt.c | 316 ++++++++ bsp/ft2004/cpu/interrupt.h | 60 ++ bsp/ft2004/cpu/mmu.c | 183 +++++ bsp/ft2004/cpu/mmu.h | 53 ++ bsp/ft2004/cpu/pmu.c | 20 + bsp/ft2004/cpu/pmu.h | 174 ++++ bsp/ft2004/cpu/stack.c | 71 ++ bsp/ft2004/cpu/start_gcc.S | 586 ++++++++++++++ bsp/ft2004/cpu/trap.c | 213 +++++ bsp/ft2004/cpu/vector_gcc.S | 51 ++ bsp/ft2004/drivers/SConscript | 12 + bsp/ft2004/drivers/board.c | 241 ++++++ bsp/ft2004/drivers/board.h | 29 + bsp/ft2004/drivers/ft2004.c | 107 +++ bsp/ft2004/drivers/ft2004.h | 25 + bsp/ft2004/drivers/ft2004_cpu.S | 40 + bsp/ft2004/drivers/secondary_cpu.cold | 43 + bsp/ft2004/drivers/serial.h | 20 + bsp/ft2004/figures/onchipPeripheral.png | Bin 0 -> 22293 bytes ...2\350\277\207\347\225\214\351\235\242.png" | Bin 0 -> 10980 bytes .../rttsd\350\260\203\350\257\225.png" | Bin 0 -> 22374 bytes ...0\346\274\224\347\244\272\345\233\276.png" | Bin 0 -> 24488 bytes bsp/ft2004/ft_aarch32.lds | 110 +++ bsp/ft2004/libraries/.gitignore | 4 + bsp/ft2004/libraries/Kconfig | 146 ++++ bsp/ft2004/libraries/LICENSE | 202 +++++ bsp/ft2004/libraries/SConscript | 145 ++++ bsp/ft2004/libraries/bsp/bsp.mk | 21 + bsp/ft2004/libraries/bsp/ft_can/ft_can.c | 336 ++++++++ bsp/ft2004/libraries/bsp/ft_can/ft_can.h | 166 ++++ bsp/ft2004/libraries/bsp/ft_can/ft_can_calc.c | 298 +++++++ bsp/ft2004/libraries/bsp/ft_can/ft_can_g.c | 63 ++ bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.c | 79 ++ bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.h | 185 +++++ bsp/ft2004/libraries/bsp/ft_can/ft_can_intr.c | 142 ++++ .../libraries/bsp/ft_can/ft_can_sinit.c | 64 ++ bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c | 188 +++++ bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.h | 85 ++ .../libraries/bsp/ft_gicv3/ft_gicv3_g.c | 25 + .../libraries/bsp/ft_gicv3/ft_gicv3_hw.c | 745 ++++++++++++++++++ .../libraries/bsp/ft_gicv3/ft_gicv3_hw.h | 248 ++++++ .../libraries/bsp/ft_gicv3/ft_gicv3_sinit.c | 47 ++ .../libraries/bsp/ft_gicv3/ft_gicv3_test.c | 13 + bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.c | 171 ++++ bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h | 311 ++++++++ .../libraries/bsp/ft_gmac/ft_gmac_desc.c | 529 +++++++++++++ .../libraries/bsp/ft_gmac/ft_gmac_desc.h | 20 + bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_g.c | 47 ++ bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.c | 582 ++++++++++++++ bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.h | 577 ++++++++++++++ .../libraries/bsp/ft_gmac/ft_gmac_intr.c | 194 +++++ .../libraries/bsp/ft_gmac/ft_gmac_sinit.c | 43 + bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.c | 108 +++ bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.h | 73 ++ bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio_hw.h | 93 +++ bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.c | 516 ++++++++++++ bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.h | 201 +++++ bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_g.c | 13 + bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_hw.h | 322 ++++++++ bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_irq.c | 193 +++++ .../libraries/bsp/ft_i2c/ft_i2c_selftest.c | 21 + bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c | 626 +++++++++++++++ bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.h | 208 +++++ bsp/ft2004/libraries/bsp/ft_qspi/qspi_g.c | 50 ++ bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c | 49 ++ bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.h | 223 ++++++ bsp/ft2004/libraries/bsp/ft_qspi/qspi_sinit.c | 58 ++ bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.c | 718 +++++++++++++++++ bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.h | 235 ++++++ bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_g.c | 31 + bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.c | 51 ++ bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.h | 210 +++++ .../libraries/bsp/ft_sd/ft_sdctrl_intr.c | 143 ++++ .../libraries/bsp/ft_sd/ft_sdctrl_option.c | 91 +++ .../libraries/bsp/ft_sd/ft_sdctrl_sinit.c | 42 + bsp/ft2004/libraries/bsp/ft_spi/ft_spi.c | 192 +++++ bsp/ft2004/libraries/bsp/ft_spi/ft_spi.h | 95 +++ bsp/ft2004/libraries/bsp/ft_spi/ft_spi_hw.h | 331 ++++++++ bsp/ft2004/libraries/bsp/ft_spi/ft_spi_irq.c | 13 + bsp/ft2004/libraries/bsp/ft_uart/ft_uart.c | 287 +++++++ bsp/ft2004/libraries/bsp/ft_uart/ft_uart.h | 111 +++ bsp/ft2004/libraries/bsp/ft_uart/ft_uart_g.c | 34 + bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.c | 66 ++ bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.h | 222 ++++++ .../libraries/bsp/ft_uart/ft_uart_intr.c | 209 +++++ .../libraries/bsp/ft_uart/ft_uart_options.c | 60 ++ .../libraries/bsp/ft_uart/ft_uart_selftest.c | 13 + .../libraries/bsp/ft_uart/ft_uart_sinit.c | 41 + .../libraries/bsp/include/ft_parameters.h | 204 +++++ .../libraries/bsp/standlone/ft_assert.c | 43 + .../libraries/bsp/standlone/ft_assert.h | 154 ++++ bsp/ft2004/libraries/bsp/standlone/ft_cache.c | 87 ++ bsp/ft2004/libraries/bsp/standlone/ft_cache.h | 56 ++ bsp/ft2004/libraries/bsp/standlone/ft_cpu.c | 170 ++++ bsp/ft2004/libraries/bsp/standlone/ft_cpu.h | 50 ++ bsp/ft2004/libraries/bsp/standlone/ft_debug.c | 91 +++ bsp/ft2004/libraries/bsp/standlone/ft_debug.h | 78 ++ .../libraries/bsp/standlone/ft_error_code.h | 72 ++ .../bsp/standlone/ft_generic_timer.c | 328 ++++++++ .../bsp/standlone/ft_generic_timer.h | 58 ++ bsp/ft2004/libraries/bsp/standlone/ft_io.h | 164 ++++ bsp/ft2004/libraries/bsp/standlone/ft_list.h | 24 + bsp/ft2004/libraries/bsp/standlone/ft_math.c | 43 + bsp/ft2004/libraries/bsp/standlone/ft_math.h | 48 ++ bsp/ft2004/libraries/bsp/standlone/ft_mux.c | 96 +++ bsp/ft2004/libraries/bsp/standlone/ft_mux.h | 87 ++ .../libraries/bsp/standlone/ft_printf.c | 97 +++ .../libraries/bsp/standlone/ft_printf.h | 31 + bsp/ft2004/libraries/bsp/standlone/ft_psci.c | 102 +++ bsp/ft2004/libraries/bsp/standlone/ft_psci.h | 55 ++ bsp/ft2004/libraries/bsp/standlone/ft_smc.S | 36 + bsp/ft2004/libraries/bsp/standlone/ft_smc.h | 67 ++ .../libraries/bsp/standlone/ft_status.h | 82 ++ bsp/ft2004/libraries/bsp/standlone/ft_trace.c | 68 ++ bsp/ft2004/libraries/bsp/standlone/ft_trace.h | 53 ++ bsp/ft2004/libraries/bsp/standlone/ft_types.h | 75 ++ bsp/ft2004/libraries/bsp/standlone/inbyte.c | 22 + bsp/ft2004/libraries/bsp/standlone/outbyte.c | 21 + bsp/ft2004/libraries/component/component.mk | 4 + .../libraries/component/sdmmc/diskio_sdmmc.c | 426 ++++++++++ .../libraries/component/sdmmc/diskio_sdmmc.h | 102 +++ .../libraries/component/sdmmc/ft_sdmmc.h | 239 ++++++ .../libraries/component/sdmmc/ft_sdmmc_cmd.c | 610 ++++++++++++++ .../libraries/component/sdmmc/ft_sdmmc_cmd.h | 88 +++ .../component/sdmmc/ft_sdmmc_common.c | 318 ++++++++ .../component/sdmmc/ft_sdmmc_common.h | 178 +++++ .../libraries/component/sdmmc/ft_sdmmc_defs.h | 501 ++++++++++++ .../libraries/component/sdmmc/ft_sdmmc_init.c | 147 ++++ .../libraries/component/sdmmc/ft_sdmmc_mmc.c | 261 ++++++ .../libraries/component/sdmmc/ft_sdmmc_port.c | 64 ++ .../libraries/component/sdmmc/ft_sdmmc_sd.c | 376 +++++++++ bsp/ft2004/libraries/cpu/ft_aarch32_asm.h | 316 ++++++++ bsp/ft2004/libraries/device.mk | 12 + bsp/ft2004/libraries/doc/ChangeLog.md | 90 +++ .../libraries/doc/figures/v0.3.0_add.png | Bin 0 -> 297513 bytes bsp/ft2004/libraries/drivers/drivers.mk | 13 + .../drivers/freertos/FreeRTOSConfig.h | 235 ++++++ .../libraries/drivers/freertos/ft_os_gmac.c | 186 +++++ .../libraries/drivers/freertos/ft_os_gmac.h | 84 ++ .../libraries/drivers/freertos/ft_os_i2c.c | 232 ++++++ .../libraries/drivers/freertos/ft_os_i2c.h | 55 ++ .../libraries/drivers/freertos/ft_os_uart.c | 229 ++++++ .../libraries/drivers/freertos/ft_os_uart.h | 67 ++ .../drivers/freertos/system_ft2004.c | 88 +++ .../drivers/freertos/system_ft2004.h | 38 + .../libraries/drivers/rtthread/drv_can.c | 270 +++++++ .../libraries/drivers/rtthread/drv_can.h | 38 + .../libraries/drivers/rtthread/drv_eth.c | 709 +++++++++++++++++ .../libraries/drivers/rtthread/drv_eth.h | 25 + .../libraries/drivers/rtthread/drv_log.h | 27 + .../libraries/drivers/rtthread/drv_qspi.c | 424 ++++++++++ .../libraries/drivers/rtthread/drv_qspi.h | 29 + .../libraries/drivers/rtthread/drv_sdcard.c | 155 ++++ .../libraries/drivers/rtthread/drv_sdctrl.c | 693 ++++++++++++++++ .../libraries/drivers/rtthread/drv_sdctrl.h | 47 ++ .../libraries/drivers/rtthread/drv_spi.c | 447 +++++++++++ .../libraries/drivers/rtthread/drv_spi.h | 29 + .../libraries/drivers/rtthread/drv_usart.c | 193 +++++ .../libraries/drivers/rtthread/drv_usart.h | 26 + .../drivers/rtthread/ports/drv_qspi_flash.c | 39 + .../drivers/rtthread/ports/drv_spi_flash.c | 43 + bsp/ft2004/libraries/drivers/system_gic.c | 120 +++ bsp/ft2004/libraries/drivers/system_gic.h | 47 ++ .../libraries/gcc/assembly_functions.olsd | 178 +++++ bsp/ft2004/libraries/gcc/lib_test.c | 134 ++++ bsp/ft2004/libraries/gcc/ram_ft_aarch32.lds | 86 ++ bsp/ft2004/libraries/gcc/startup_aarch32.S | 244 ++++++ bsp/ft2004/libraries/gcc/traps.c | 35 + bsp/ft2004/libraries/include/asmArm.h | 34 + bsp/ft2004/libraries/readme.md | 69 ++ bsp/ft2004/make.sh | 16 + bsp/ft2004/rtconfig.h | 268 +++++++ bsp/ft2004/rtconfig.py | 84 ++ 192 files changed, 29390 insertions(+) create mode 100644 bsp/ft2004/.config create mode 100644 bsp/ft2004/Kconfig create mode 100644 bsp/ft2004/README.md create mode 100644 bsp/ft2004/SConscript create mode 100644 bsp/ft2004/SConstruct create mode 100644 bsp/ft2004/applications/SConscript create mode 100644 bsp/ft2004/applications/startup.c create mode 100644 bsp/ft2004/cpu/armv7.h create mode 100644 bsp/ft2004/cpu/cache.c create mode 100644 bsp/ft2004/cpu/context_gcc.S create mode 100644 bsp/ft2004/cpu/cp15.h create mode 100644 bsp/ft2004/cpu/cp15_gcc.S create mode 100644 bsp/ft2004/cpu/cpu.c create mode 100644 bsp/ft2004/cpu/gic.c create mode 100644 bsp/ft2004/cpu/gic.h create mode 100644 bsp/ft2004/cpu/gicv3.c create mode 100644 bsp/ft2004/cpu/gicv3.h create mode 100644 bsp/ft2004/cpu/gtimer.c create mode 100644 bsp/ft2004/cpu/gtimer.h create mode 100644 bsp/ft2004/cpu/interrupt.c create mode 100644 bsp/ft2004/cpu/interrupt.h create mode 100644 bsp/ft2004/cpu/mmu.c create mode 100644 bsp/ft2004/cpu/mmu.h create mode 100644 bsp/ft2004/cpu/pmu.c create mode 100644 bsp/ft2004/cpu/pmu.h create mode 100644 bsp/ft2004/cpu/stack.c create mode 100644 bsp/ft2004/cpu/start_gcc.S create mode 100644 bsp/ft2004/cpu/trap.c create mode 100644 bsp/ft2004/cpu/vector_gcc.S create mode 100644 bsp/ft2004/drivers/SConscript create mode 100644 bsp/ft2004/drivers/board.c create mode 100644 bsp/ft2004/drivers/board.h create mode 100644 bsp/ft2004/drivers/ft2004.c create mode 100644 bsp/ft2004/drivers/ft2004.h create mode 100644 bsp/ft2004/drivers/ft2004_cpu.S create mode 100644 bsp/ft2004/drivers/secondary_cpu.cold create mode 100644 bsp/ft2004/drivers/serial.h create mode 100644 bsp/ft2004/figures/onchipPeripheral.png create mode 100644 "bsp/ft2004/figures/rttPing\351\200\232\350\277\207\347\225\214\351\235\242.png" create mode 100644 "bsp/ft2004/figures/rttsd\350\260\203\350\257\225.png" create mode 100644 "bsp/ft2004/figures/\345\220\257\345\212\250\346\274\224\347\244\272\345\233\276.png" create mode 100644 bsp/ft2004/ft_aarch32.lds create mode 100644 bsp/ft2004/libraries/.gitignore create mode 100644 bsp/ft2004/libraries/Kconfig create mode 100644 bsp/ft2004/libraries/LICENSE create mode 100644 bsp/ft2004/libraries/SConscript create mode 100644 bsp/ft2004/libraries/bsp/bsp.mk create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can.c create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can.h create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_calc.c create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_intr.c create mode 100644 bsp/ft2004/libraries/bsp/ft_can/ft_can_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_test.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_intr.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.c create mode 100644 bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.h create mode 100644 bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.c create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.h create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_irq.c create mode 100644 bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_selftest.c create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.h create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/qspi_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_qspi/qspi_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.h create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_intr.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_option.c create mode 100644 bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/ft_spi/ft_spi.c create mode 100644 bsp/ft2004/libraries/bsp/ft_spi/ft_spi.h create mode 100644 bsp/ft2004/libraries/bsp/ft_spi/ft_spi_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_spi/ft_spi_irq.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart.h create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_g.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.h create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_intr.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_options.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_selftest.c create mode 100644 bsp/ft2004/libraries/bsp/ft_uart/ft_uart_sinit.c create mode 100644 bsp/ft2004/libraries/bsp/include/ft_parameters.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_assert.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_assert.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_cache.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_cache.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_cpu.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_cpu.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_debug.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_debug.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_error_code.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_generic_timer.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_generic_timer.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_io.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_list.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_math.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_math.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_mux.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_mux.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_printf.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_printf.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_psci.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_psci.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_smc.S create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_smc.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_status.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_trace.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_trace.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/ft_types.h create mode 100644 bsp/ft2004/libraries/bsp/standlone/inbyte.c create mode 100644 bsp/ft2004/libraries/bsp/standlone/outbyte.c create mode 100644 bsp/ft2004/libraries/component/component.mk create mode 100644 bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.h create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc.h create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.h create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.h create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_defs.h create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_init.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_mmc.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_port.c create mode 100644 bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_sd.c create mode 100644 bsp/ft2004/libraries/cpu/ft_aarch32_asm.h create mode 100644 bsp/ft2004/libraries/device.mk create mode 100644 bsp/ft2004/libraries/doc/ChangeLog.md create mode 100644 bsp/ft2004/libraries/doc/figures/v0.3.0_add.png create mode 100644 bsp/ft2004/libraries/drivers/drivers.mk create mode 100644 bsp/ft2004/libraries/drivers/freertos/FreeRTOSConfig.h create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.c create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.h create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.c create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.h create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_uart.c create mode 100644 bsp/ft2004/libraries/drivers/freertos/ft_os_uart.h create mode 100644 bsp/ft2004/libraries/drivers/freertos/system_ft2004.c create mode 100644 bsp/ft2004/libraries/drivers/freertos/system_ft2004.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_can.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_can.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_eth.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_eth.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_log.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_qspi.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_qspi.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_sdcard.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_sdctrl.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_sdctrl.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_spi.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_spi.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_usart.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/drv_usart.h create mode 100644 bsp/ft2004/libraries/drivers/rtthread/ports/drv_qspi_flash.c create mode 100644 bsp/ft2004/libraries/drivers/rtthread/ports/drv_spi_flash.c create mode 100644 bsp/ft2004/libraries/drivers/system_gic.c create mode 100644 bsp/ft2004/libraries/drivers/system_gic.h create mode 100644 bsp/ft2004/libraries/gcc/assembly_functions.olsd create mode 100644 bsp/ft2004/libraries/gcc/lib_test.c create mode 100644 bsp/ft2004/libraries/gcc/ram_ft_aarch32.lds create mode 100644 bsp/ft2004/libraries/gcc/startup_aarch32.S create mode 100644 bsp/ft2004/libraries/gcc/traps.c create mode 100644 bsp/ft2004/libraries/include/asmArm.h create mode 100644 bsp/ft2004/libraries/readme.md create mode 100644 bsp/ft2004/make.sh create mode 100644 bsp/ft2004/rtconfig.h create mode 100644 bsp/ft2004/rtconfig.py diff --git a/bsp/ft2004/.config b/bsp/ft2004/.config new file mode 100644 index 0000000000..9a7018d70c --- /dev/null +++ b/bsp/ft2004/.config @@ -0,0 +1,690 @@ +# +# Automatically generated file; DO NOT EDIT. +# RT-Thread Project Configuration +# + +# +# RT-Thread Kernel +# +CONFIG_RT_NAME_MAX=32 +# CONFIG_RT_USING_ARCH_DATA_TYPE is not set +# CONFIG_RT_USING_SMP is not set +CONFIG_RT_ALIGN_SIZE=128 +# CONFIG_RT_THREAD_PRIORITY_8 is not set +CONFIG_RT_THREAD_PRIORITY_32=y +# CONFIG_RT_THREAD_PRIORITY_256 is not set +CONFIG_RT_THREAD_PRIORITY_MAX=32 +CONFIG_RT_TICK_PER_SECOND=100 +CONFIG_RT_USING_OVERFLOW_CHECK=y +CONFIG_RT_USING_HOOK=y +CONFIG_RT_USING_IDLE_HOOK=y +CONFIG_RT_IDLE_HOOK_LIST_SIZE=4 +CONFIG_IDLE_THREAD_STACK_SIZE=4096 +CONFIG_RT_USING_TIMER_SOFT=y +CONFIG_RT_TIMER_THREAD_PRIO=4 +CONFIG_RT_TIMER_THREAD_STACK_SIZE=512 + +# +# kservice optimization +# +# CONFIG_RT_KSERVICE_USING_STDLIB is not set +# CONFIG_RT_KSERVICE_USING_TINY_SIZE is not set +CONFIG_RT_DEBUG=y +CONFIG_RT_DEBUG_COLOR=y +# CONFIG_RT_DEBUG_INIT_CONFIG is not set +# CONFIG_RT_DEBUG_THREAD_CONFIG is not set +# CONFIG_RT_DEBUG_SCHEDULER_CONFIG is not set +# CONFIG_RT_DEBUG_IPC_CONFIG is not set +# CONFIG_RT_DEBUG_TIMER_CONFIG is not set +# CONFIG_RT_DEBUG_IRQ_CONFIG is not set +# CONFIG_RT_DEBUG_MEM_CONFIG is not set +# CONFIG_RT_DEBUG_SLAB_CONFIG is not set +# CONFIG_RT_DEBUG_MEMHEAP_CONFIG is not set +# CONFIG_RT_DEBUG_MODULE_CONFIG is not set + +# +# Inter-Thread communication +# +CONFIG_RT_USING_SEMAPHORE=y +CONFIG_RT_USING_MUTEX=y +CONFIG_RT_USING_EVENT=y +CONFIG_RT_USING_MAILBOX=y +CONFIG_RT_USING_MESSAGEQUEUE=y +# CONFIG_RT_USING_SIGNALS is not set + +# +# Memory Management +# +# CONFIG_RT_USING_MEMPOOL is not set +CONFIG_RT_USING_MEMHEAP=y +# CONFIG_RT_USING_NOHEAP is not set +# CONFIG_RT_USING_SMALL_MEM is not set +CONFIG_RT_USING_SLAB=y +# CONFIG_RT_USING_MEMHEAP_AS_HEAP is not set +# CONFIG_RT_USING_USERHEAP is not set +# CONFIG_RT_USING_MEMTRACE is not set +CONFIG_RT_USING_HEAP=y + +# +# Kernel Device Object +# +CONFIG_RT_USING_DEVICE=y +# CONFIG_RT_USING_DEVICE_OPS is not set +CONFIG_RT_USING_INTERRUPT_INFO=y +CONFIG_RT_USING_CONSOLE=y +CONFIG_RT_CONSOLEBUF_SIZE=4096 +CONFIG_RT_CONSOLE_DEVICE_NAME="uart1" +CONFIG_RT_VER_NUM=0x40004 +CONFIG_ARCH_ARM=y +# CONFIG_RT_USING_CPU_FFS is not set +CONFIG_ARCH_ARM_CORTEX_A=y +# CONFIG_RT_SMP_AUTO_BOOT is not set +# CONFIG_RT_USING_GIC_V2 is not set +CONFIG_RT_USING_GIC_V3=y +# CONFIG_RT_NO_USING_GIC is not set +# CONFIG_ARCH_CPU_STACK_GROWS_UPWARD is not set + +# +# RT-Thread Components +# +CONFIG_RT_USING_COMPONENTS_INIT=y +CONFIG_RT_USING_USER_MAIN=y +CONFIG_RT_MAIN_THREAD_STACK_SIZE=2048 +CONFIG_RT_MAIN_THREAD_PRIORITY=10 + +# +# C++ features +# +# CONFIG_RT_USING_CPLUSPLUS is not set + +# +# Command shell +# +CONFIG_RT_USING_FINSH=y +CONFIG_FINSH_THREAD_NAME="tshell" +CONFIG_FINSH_USING_HISTORY=y +CONFIG_FINSH_HISTORY_LINES=5 +CONFIG_FINSH_USING_SYMTAB=y +CONFIG_FINSH_USING_DESCRIPTION=y +# CONFIG_FINSH_ECHO_DISABLE_DEFAULT is not set +CONFIG_FINSH_THREAD_PRIORITY=20 +CONFIG_FINSH_THREAD_STACK_SIZE=4096 +CONFIG_FINSH_CMD_SIZE=128 +# CONFIG_FINSH_USING_AUTH is not set +CONFIG_FINSH_USING_MSH=y +CONFIG_FINSH_USING_MSH_DEFAULT=y +# CONFIG_FINSH_USING_MSH_ONLY is not set +CONFIG_FINSH_ARG_MAX=10 + +# +# Device virtual file system +# +CONFIG_RT_USING_DFS=y +CONFIG_DFS_USING_WORKDIR=y +CONFIG_DFS_FILESYSTEMS_MAX=2 +CONFIG_DFS_FILESYSTEM_TYPES_MAX=2 +CONFIG_DFS_FD_MAX=16 +# CONFIG_RT_USING_DFS_MNTTABLE is not set +CONFIG_RT_USING_DFS_ELMFAT=y + +# +# elm-chan's FatFs, Generic FAT Filesystem Module +# +CONFIG_RT_DFS_ELM_CODE_PAGE=437 +CONFIG_RT_DFS_ELM_WORD_ACCESS=y +# CONFIG_RT_DFS_ELM_USE_LFN_0 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_1 is not set +# CONFIG_RT_DFS_ELM_USE_LFN_2 is not set +CONFIG_RT_DFS_ELM_USE_LFN_3=y +CONFIG_RT_DFS_ELM_USE_LFN=3 +CONFIG_RT_DFS_ELM_LFN_UNICODE_0=y +# CONFIG_RT_DFS_ELM_LFN_UNICODE_1 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_2 is not set +# CONFIG_RT_DFS_ELM_LFN_UNICODE_3 is not set +CONFIG_RT_DFS_ELM_LFN_UNICODE=0 +CONFIG_RT_DFS_ELM_MAX_LFN=255 +CONFIG_RT_DFS_ELM_DRIVES=2 +CONFIG_RT_DFS_ELM_MAX_SECTOR_SIZE=512 +# CONFIG_RT_DFS_ELM_USE_ERASE is not set +CONFIG_RT_DFS_ELM_REENTRANT=y +CONFIG_RT_USING_DFS_DEVFS=y +# CONFIG_RT_USING_DFS_ROMFS is not set +# CONFIG_RT_USING_DFS_RAMFS is not set +# CONFIG_RT_USING_DFS_NFS is not set + +# +# Device Drivers +# +CONFIG_RT_USING_DEVICE_IPC=y +CONFIG_RT_PIPE_BUFSZ=512 +# CONFIG_RT_USING_SYSTEM_WORKQUEUE is not set +CONFIG_RT_USING_SERIAL=y +CONFIG_RT_SERIAL_USING_DMA=y +CONFIG_RT_SERIAL_RB_BUFSZ=256 +CONFIG_RT_USING_CAN=y +# CONFIG_RT_CAN_USING_HDR is not set +# CONFIG_RT_USING_HWTIMER is not set +# CONFIG_RT_USING_CPUTIME is not set +# CONFIG_RT_USING_I2C is not set +# CONFIG_RT_USING_PHY is not set +# CONFIG_RT_USING_PIN is not set +# CONFIG_RT_USING_ADC is not set +# CONFIG_RT_USING_DAC is not set +# CONFIG_RT_USING_PWM is not set +# CONFIG_RT_USING_MTD_NOR is not set +# CONFIG_RT_USING_MTD_NAND is not set +# CONFIG_RT_USING_PM is not set +# CONFIG_RT_USING_RTC is not set +CONFIG_RT_USING_SDIO=y +CONFIG_RT_SDIO_STACK_SIZE=512 +CONFIG_RT_SDIO_THREAD_PRIORITY=15 +CONFIG_RT_MMCSD_STACK_SIZE=1024 +CONFIG_RT_MMCSD_THREAD_PREORITY=22 +CONFIG_RT_MMCSD_MAX_PARTITION=16 +# CONFIG_RT_SDIO_DEBUG is not set +CONFIG_RT_USING_SPI=y +CONFIG_RT_USING_QSPI=y +# CONFIG_RT_USING_SPI_MSD is not set +CONFIG_RT_USING_SFUD=y +CONFIG_RT_SFUD_USING_SFDP=y +CONFIG_RT_SFUD_USING_FLASH_INFO_TABLE=y +CONFIG_RT_SFUD_USING_QSPI=y +CONFIG_RT_SFUD_SPI_MAX_HZ=50000000 +# CONFIG_RT_DEBUG_SFUD is not set +# CONFIG_RT_USING_ENC28J60 is not set +# CONFIG_RT_USING_SPI_WIFI is not set +# CONFIG_RT_USING_WDT is not set +# CONFIG_RT_USING_AUDIO is not set +# CONFIG_RT_USING_SENSOR is not set +# CONFIG_RT_USING_TOUCH is not set +# CONFIG_RT_USING_HWCRYPTO is not set +# CONFIG_RT_USING_PULSE_ENCODER is not set +# CONFIG_RT_USING_INPUT_CAPTURE is not set +# CONFIG_RT_USING_WIFI is not set + +# +# Using USB +# +# CONFIG_RT_USING_USB_HOST is not set +# CONFIG_RT_USING_USB_DEVICE is not set + +# +# POSIX layer and C standard library +# +# CONFIG_RT_USING_LIBC is not set +# CONFIG_RT_USING_PTHREADS is not set +CONFIG_RT_LIBC_USING_TIME=y +CONFIG_RT_LIBC_FIXED_TIMEZONE=8 + +# +# Network +# + +# +# Socket abstraction layer +# +# CONFIG_RT_USING_SAL is not set + +# +# Network interface device +# +CONFIG_RT_USING_NETDEV=y +CONFIG_NETDEV_USING_IFCONFIG=y +CONFIG_NETDEV_USING_PING=y +CONFIG_NETDEV_USING_NETSTAT=y +CONFIG_NETDEV_USING_AUTO_DEFAULT=y +# CONFIG_NETDEV_USING_IPV6 is not set +CONFIG_NETDEV_IPV4=1 +CONFIG_NETDEV_IPV6=0 +# CONFIG_NETDEV_IPV6_SCOPES is not set + +# +# light weight TCP/IP stack +# +CONFIG_RT_USING_LWIP=y +# CONFIG_RT_USING_LWIP141 is not set +# CONFIG_RT_USING_LWIP202 is not set +CONFIG_RT_USING_LWIP212=y +# CONFIG_RT_USING_LWIP_IPV6 is not set +CONFIG_RT_LWIP_MEM_ALIGNMENT=4 +CONFIG_RT_LWIP_IGMP=y +CONFIG_RT_LWIP_ICMP=y +# CONFIG_RT_LWIP_SNMP is not set +CONFIG_RT_LWIP_DNS=y +# CONFIG_RT_LWIP_DHCP is not set + +# +# Static IPv4 Address +# +CONFIG_RT_LWIP_IPADDR="192.168.3.20" +CONFIG_RT_LWIP_GWADDR="192.168.3.1" +CONFIG_RT_LWIP_MSKADDR="255.255.255.0" +CONFIG_RT_LWIP_UDP=y +CONFIG_RT_LWIP_TCP=y +CONFIG_RT_LWIP_RAW=y +# CONFIG_RT_LWIP_PPP is not set +CONFIG_RT_MEMP_NUM_NETCONN=8 +CONFIG_RT_LWIP_PBUF_NUM=16 +CONFIG_RT_LWIP_RAW_PCB_NUM=4 +CONFIG_RT_LWIP_UDP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_PCB_NUM=4 +CONFIG_RT_LWIP_TCP_SEG_NUM=40 +CONFIG_RT_LWIP_TCP_SND_BUF=8196 +CONFIG_RT_LWIP_TCP_WND=8196 +CONFIG_RT_LWIP_TCPTHREAD_PRIORITY=10 +CONFIG_RT_LWIP_TCPTHREAD_MBOX_SIZE=8 +CONFIG_RT_LWIP_TCPTHREAD_STACKSIZE=1024 +# CONFIG_LWIP_NO_RX_THREAD is not set +# CONFIG_LWIP_NO_TX_THREAD is not set +CONFIG_RT_LWIP_ETHTHREAD_PRIORITY=12 +CONFIG_RT_LWIP_ETHTHREAD_STACKSIZE=1024 +CONFIG_RT_LWIP_ETHTHREAD_MBOX_SIZE=8 +# CONFIG_RT_LWIP_REASSEMBLY_FRAG is not set +CONFIG_LWIP_NETIF_STATUS_CALLBACK=1 +CONFIG_LWIP_NETIF_LINK_CALLBACK=1 +CONFIG_SO_REUSE=1 +CONFIG_LWIP_SO_RCVTIMEO=1 +CONFIG_LWIP_SO_SNDTIMEO=1 +CONFIG_LWIP_SO_RCVBUF=1 +CONFIG_LWIP_SO_LINGER=0 +# CONFIG_RT_LWIP_NETIF_LOOPBACK is not set +CONFIG_LWIP_NETIF_LOOPBACK=0 +# CONFIG_RT_LWIP_STATS is not set +# CONFIG_RT_LWIP_USING_HW_CHECKSUM is not set +CONFIG_RT_LWIP_USING_PING=y +# CONFIG_RT_LWIP_DEBUG is not set + +# +# AT commands +# +# CONFIG_RT_USING_AT is not set +# CONFIG_LWIP_USING_DHCPD is not set + +# +# VBUS(Virtual Software BUS) +# +# CONFIG_RT_USING_VBUS is not set + +# +# Utilities +# +# CONFIG_RT_USING_RYM is not set +# CONFIG_RT_USING_ULOG is not set +# CONFIG_ULOG_OUTPUT_LVL_A is not set +# CONFIG_ULOG_OUTPUT_LVL_E is not set +# CONFIG_ULOG_OUTPUT_LVL_W is not set +# CONFIG_ULOG_OUTPUT_LVL_I is not set +# CONFIG_ULOG_OUTPUT_LVL_D is not set +# CONFIG_RT_USING_UTEST is not set +# CONFIG_RT_USING_RT_LINK is not set +# CONFIG_RT_USING_LWP is not set + +# +# RT-Thread Utestcases +# +# CONFIG_RT_USING_UTESTCASES is not set + +# +# RT-Thread online packages +# + +# +# IoT - internet of things +# +# CONFIG_PKG_USING_LORAWAN_DRIVER is not set +# CONFIG_PKG_USING_PAHOMQTT is not set +# CONFIG_PKG_USING_UMQTT is not set +# CONFIG_PKG_USING_WEBCLIENT is not set +# CONFIG_PKG_USING_WEBNET is not set +# CONFIG_PKG_USING_MONGOOSE is not set +# CONFIG_PKG_USING_MYMQTT is not set +# CONFIG_PKG_USING_KAWAII_MQTT is not set +# CONFIG_PKG_USING_BC28_MQTT is not set +# CONFIG_PKG_USING_WEBTERMINAL is not set +# CONFIG_PKG_USING_CJSON is not set +# CONFIG_PKG_USING_JSMN is not set +# CONFIG_PKG_USING_LIBMODBUS is not set +# CONFIG_PKG_USING_FREEMODBUS is not set +# CONFIG_PKG_USING_LJSON is not set +# CONFIG_PKG_USING_EZXML is not set +# CONFIG_PKG_USING_NANOPB is not set + +# +# Wi-Fi +# + +# +# Marvell WiFi +# +# CONFIG_PKG_USING_WLANMARVELL is not set + +# +# Wiced WiFi +# +# CONFIG_PKG_USING_WLAN_WICED is not set +# CONFIG_PKG_USING_RW007 is not set +# CONFIG_PKG_USING_COAP is not set +# CONFIG_PKG_USING_NOPOLL is not set +# CONFIG_PKG_USING_NETUTILS is not set +# CONFIG_PKG_USING_CMUX is not set +# CONFIG_PKG_USING_PPP_DEVICE is not set +# CONFIG_PKG_USING_AT_DEVICE is not set +# CONFIG_PKG_USING_ATSRV_SOCKET is not set +# CONFIG_PKG_USING_WIZNET is not set + +# +# IoT Cloud +# +# CONFIG_PKG_USING_ONENET is not set +# CONFIG_PKG_USING_GAGENT_CLOUD is not set +# CONFIG_PKG_USING_ALI_IOTKIT is not set +# CONFIG_PKG_USING_AZURE is not set +# CONFIG_PKG_USING_TENCENT_IOT_EXPLORER is not set +# CONFIG_PKG_USING_JIOT-C-SDK is not set +# CONFIG_PKG_USING_UCLOUD_IOT_SDK is not set +# CONFIG_PKG_USING_JOYLINK is not set +# CONFIG_PKG_USING_NIMBLE is not set +# CONFIG_PKG_USING_OTA_DOWNLOADER is not set +# CONFIG_PKG_USING_IPMSG is not set +# CONFIG_PKG_USING_LSSDP is not set +# CONFIG_PKG_USING_AIRKISS_OPEN is not set +# CONFIG_PKG_USING_LIBRWS is not set +# CONFIG_PKG_USING_TCPSERVER is not set +# CONFIG_PKG_USING_PROTOBUF_C is not set +# CONFIG_PKG_USING_DLT645 is not set +# CONFIG_PKG_USING_QXWZ is not set +# CONFIG_PKG_USING_SMTP_CLIENT is not set +# CONFIG_PKG_USING_ABUP_FOTA is not set +# CONFIG_PKG_USING_LIBCURL2RTT is not set +# CONFIG_PKG_USING_CAPNP is not set +# CONFIG_PKG_USING_RT_CJSON_TOOLS is not set +# CONFIG_PKG_USING_AGILE_TELNET is not set +# CONFIG_PKG_USING_NMEALIB is not set +# CONFIG_PKG_USING_AGILE_JSMN is not set +# CONFIG_PKG_USING_PDULIB is not set +# CONFIG_PKG_USING_BTSTACK is not set +# CONFIG_PKG_USING_LORAWAN_ED_STACK is not set +# CONFIG_PKG_USING_WAYZ_IOTKIT is not set +# CONFIG_PKG_USING_MAVLINK is not set +# CONFIG_PKG_USING_RAPIDJSON is not set +# CONFIG_PKG_USING_BSAL is not set +# CONFIG_PKG_USING_AGILE_MODBUS is not set +# CONFIG_PKG_USING_AGILE_FTP is not set +# CONFIG_PKG_USING_EMBEDDEDPROTO is not set + +# +# security packages +# +# CONFIG_PKG_USING_MBEDTLS is not set +# CONFIG_PKG_USING_libsodium is not set +# CONFIG_PKG_USING_TINYCRYPT is not set +# CONFIG_PKG_USING_TFM is not set +# CONFIG_PKG_USING_YD_CRYPTO is not set + +# +# language packages +# +# CONFIG_PKG_USING_LUA is not set +# CONFIG_PKG_USING_JERRYSCRIPT is not set +# CONFIG_PKG_USING_MICROPYTHON is not set + +# +# multimedia packages +# +# CONFIG_PKG_USING_OPENMV is not set +# CONFIG_PKG_USING_MUPDF is not set +# CONFIG_PKG_USING_STEMWIN is not set +# CONFIG_PKG_USING_WAVPLAYER is not set +# CONFIG_PKG_USING_TJPGD is not set +# CONFIG_PKG_USING_PDFGEN is not set +# CONFIG_PKG_USING_HELIX is not set +# CONFIG_PKG_USING_AZUREGUIX is not set +# CONFIG_PKG_USING_TOUCHGFX2RTT is not set + +# +# tools packages +# +# CONFIG_PKG_USING_CMBACKTRACE is not set +# CONFIG_PKG_USING_EASYFLASH is not set +# CONFIG_PKG_USING_EASYLOGGER is not set +# CONFIG_PKG_USING_SYSTEMVIEW is not set +# CONFIG_PKG_USING_RDB is not set +# CONFIG_PKG_USING_QRCODE is not set +# CONFIG_PKG_USING_ULOG_EASYFLASH is not set +# CONFIG_PKG_USING_ULOG_FILE is not set +# CONFIG_PKG_USING_LOGMGR is not set +# CONFIG_PKG_USING_ADBD is not set +# CONFIG_PKG_USING_COREMARK is not set +# CONFIG_PKG_USING_DHRYSTONE is not set +# CONFIG_PKG_USING_MEMORYPERF is not set +# CONFIG_PKG_USING_NR_MICRO_SHELL is not set +# CONFIG_PKG_USING_CHINESE_FONT_LIBRARY is not set +# CONFIG_PKG_USING_LUNAR_CALENDAR is not set +# CONFIG_PKG_USING_BS8116A is not set +# CONFIG_PKG_USING_GPS_RMC is not set +# CONFIG_PKG_USING_URLENCODE is not set +# CONFIG_PKG_USING_UMCN is not set +# CONFIG_PKG_USING_LWRB2RTT is not set +# CONFIG_PKG_USING_CPU_USAGE is not set +# CONFIG_PKG_USING_GBK2UTF8 is not set +# CONFIG_PKG_USING_VCONSOLE is not set +# CONFIG_PKG_USING_KDB is not set +# CONFIG_PKG_USING_WAMR is not set +# CONFIG_PKG_USING_MICRO_XRCE_DDS_CLIENT is not set +# CONFIG_PKG_USING_LWLOG is not set +# CONFIG_PKG_USING_ANV_TRACE is not set +# CONFIG_PKG_USING_ANV_MEMLEAK is not set +# CONFIG_PKG_USING_ANV_TESTSUIT is not set +# CONFIG_PKG_USING_ANV_BENCH is not set +# CONFIG_PKG_USING_DEVMEM is not set +# CONFIG_PKG_USING_MEM_SANDBOX is not set + +# +# system packages +# +# CONFIG_PKG_USING_GUIENGINE is not set +# CONFIG_PKG_USING_PERSIMMON is not set +# CONFIG_PKG_USING_CAIRO is not set +# CONFIG_PKG_USING_PIXMAN is not set +# CONFIG_PKG_USING_PARTITION is not set +# CONFIG_PKG_USING_FAL is not set +# CONFIG_PKG_USING_FLASHDB is not set +# CONFIG_PKG_USING_SQLITE is not set +# CONFIG_PKG_USING_RTI is not set +# CONFIG_PKG_USING_LITTLEVGL2RTT is not set +# CONFIG_PKG_USING_CMSIS is not set +# CONFIG_PKG_USING_DFS_YAFFS is not set +# CONFIG_PKG_USING_LITTLEFS is not set +# CONFIG_PKG_USING_DFS_JFFS2 is not set +# CONFIG_PKG_USING_DFS_UFFS is not set +# CONFIG_PKG_USING_LWEXT4 is not set +# CONFIG_PKG_USING_THREAD_POOL is not set +# CONFIG_PKG_USING_ROBOTS is not set +# CONFIG_PKG_USING_EV is not set +# CONFIG_PKG_USING_SYSWATCH is not set +# CONFIG_PKG_USING_SYS_LOAD_MONITOR is not set +# CONFIG_PKG_USING_PLCCORE is not set +# CONFIG_PKG_USING_RAMDISK is not set +# CONFIG_PKG_USING_MININI is not set +# CONFIG_PKG_USING_QBOOT is not set + +# +# Micrium: Micrium software products porting for RT-Thread +# +# CONFIG_PKG_USING_UCOSIII_WRAPPER is not set +# CONFIG_PKG_USING_UCOSII_WRAPPER is not set +# CONFIG_PKG_USING_UC_CRC is not set +# CONFIG_PKG_USING_UC_CLK is not set +# CONFIG_PKG_USING_UC_COMMON is not set +# CONFIG_PKG_USING_UC_MODBUS is not set +# CONFIG_PKG_USING_PPOOL is not set +# CONFIG_PKG_USING_OPENAMP is not set +# CONFIG_PKG_USING_RT_KPRINTF_THREADSAFE is not set +# CONFIG_PKG_USING_RT_MEMCPY_CM is not set +# CONFIG_PKG_USING_QFPLIB_M0_FULL is not set +# CONFIG_PKG_USING_QFPLIB_M0_TINY is not set +# CONFIG_PKG_USING_QFPLIB_M3 is not set +# CONFIG_PKG_USING_LPM is not set +# CONFIG_PKG_USING_TLSF is not set +# CONFIG_PKG_USING_EVENT_RECORDER is not set + +# +# peripheral libraries and drivers +# +# CONFIG_PKG_USING_SENSORS_DRIVERS is not set +# CONFIG_PKG_USING_REALTEK_AMEBA is not set +# CONFIG_PKG_USING_SHT2X is not set +# CONFIG_PKG_USING_SHT3X is not set +# CONFIG_PKG_USING_AS7341 is not set +# CONFIG_PKG_USING_STM32_SDIO is not set +# CONFIG_PKG_USING_ICM20608 is not set +# CONFIG_PKG_USING_U8G2 is not set +# CONFIG_PKG_USING_BUTTON is not set +# CONFIG_PKG_USING_PCF8574 is not set +# CONFIG_PKG_USING_SX12XX is not set +# CONFIG_PKG_USING_SIGNAL_LED is not set +# CONFIG_PKG_USING_LEDBLINK is not set +# CONFIG_PKG_USING_LITTLED is not set +# CONFIG_PKG_USING_LKDGUI is not set +# CONFIG_PKG_USING_NRF5X_SDK is not set +# CONFIG_PKG_USING_NRFX is not set +# CONFIG_PKG_USING_WM_LIBRARIES is not set +# CONFIG_PKG_USING_KENDRYTE_SDK is not set +# CONFIG_PKG_USING_INFRARED is not set +# CONFIG_PKG_USING_ROSSERIAL is not set +# CONFIG_PKG_USING_AGILE_BUTTON is not set +# CONFIG_PKG_USING_AGILE_LED is not set +# CONFIG_PKG_USING_AT24CXX is not set +# CONFIG_PKG_USING_MOTIONDRIVER2RTT is not set +# CONFIG_PKG_USING_AD7746 is not set +# CONFIG_PKG_USING_PCA9685 is not set +# CONFIG_PKG_USING_I2C_TOOLS is not set +# CONFIG_PKG_USING_NRF24L01 is not set +# CONFIG_PKG_USING_TOUCH_DRIVERS is not set +# CONFIG_PKG_USING_MAX17048 is not set +# CONFIG_PKG_USING_RPLIDAR is not set +# CONFIG_PKG_USING_AS608 is not set +# CONFIG_PKG_USING_RC522 is not set +# CONFIG_PKG_USING_WS2812B is not set +# CONFIG_PKG_USING_EMBARC_BSP is not set +# CONFIG_PKG_USING_EXTERN_RTC_DRIVERS is not set +# CONFIG_PKG_USING_MULTI_RTIMER is not set +# CONFIG_PKG_USING_MAX7219 is not set +# CONFIG_PKG_USING_BEEP is not set +# CONFIG_PKG_USING_EASYBLINK is not set +# CONFIG_PKG_USING_PMS_SERIES is not set +# CONFIG_PKG_USING_CAN_YMODEM is not set +# CONFIG_PKG_USING_LORA_RADIO_DRIVER is not set +# CONFIG_PKG_USING_QLED is not set +# CONFIG_PKG_USING_PAJ7620 is not set +# CONFIG_PKG_USING_AGILE_CONSOLE is not set +# CONFIG_PKG_USING_LD3320 is not set +# CONFIG_PKG_USING_WK2124 is not set +# CONFIG_PKG_USING_LY68L6400 is not set +# CONFIG_PKG_USING_DM9051 is not set +# CONFIG_PKG_USING_SSD1306 is not set +# CONFIG_PKG_USING_QKEY is not set +# CONFIG_PKG_USING_RS485 is not set +# CONFIG_PKG_USING_NES is not set +# CONFIG_PKG_USING_VIRTUAL_SENSOR is not set +# CONFIG_PKG_USING_VDEVICE is not set +# CONFIG_PKG_USING_SGM706 is not set +# CONFIG_PKG_USING_STM32WB55_SDK is not set +# CONFIG_PKG_USING_RDA58XX is not set +# CONFIG_PKG_USING_LIBNFC is not set +# CONFIG_PKG_USING_MFOC is not set +# CONFIG_PKG_USING_TMC51XX is not set + +# +# AI packages +# +# CONFIG_PKG_USING_LIBANN is not set +# CONFIG_PKG_USING_NNOM is not set +# CONFIG_PKG_USING_ONNX_BACKEND is not set +# CONFIG_PKG_USING_ONNX_PARSER is not set +# CONFIG_PKG_USING_TENSORFLOWLITEMICRO is not set +# CONFIG_PKG_USING_ELAPACK is not set +# CONFIG_PKG_USING_ULAPACK is not set +# CONFIG_PKG_USING_QUEST is not set +# CONFIG_PKG_USING_NAXOS is not set + +# +# miscellaneous packages +# +# CONFIG_PKG_USING_LIBCSV is not set +# CONFIG_PKG_USING_OPTPARSE is not set +# CONFIG_PKG_USING_FASTLZ is not set +# CONFIG_PKG_USING_MINILZO is not set +# CONFIG_PKG_USING_QUICKLZ is not set +# CONFIG_PKG_USING_LZMA is not set +# CONFIG_PKG_USING_MULTIBUTTON is not set +# CONFIG_PKG_USING_FLEXIBLE_BUTTON is not set +# CONFIG_PKG_USING_CANFESTIVAL is not set +# CONFIG_PKG_USING_ZLIB is not set +# CONFIG_PKG_USING_DSTR is not set +# CONFIG_PKG_USING_TINYFRAME is not set +# CONFIG_PKG_USING_KENDRYTE_DEMO is not set +# CONFIG_PKG_USING_DIGITALCTRL is not set +# CONFIG_PKG_USING_UPACKER is not set +# CONFIG_PKG_USING_UPARAM is not set + +# +# samples: kernel and components samples +# +# CONFIG_PKG_USING_KERNEL_SAMPLES is not set +# CONFIG_PKG_USING_FILESYSTEM_SAMPLES is not set +# CONFIG_PKG_USING_NETWORK_SAMPLES is not set +# CONFIG_PKG_USING_PERIPHERAL_SAMPLES is not set +# CONFIG_PKG_USING_HELLO is not set +# CONFIG_PKG_USING_VI is not set +# CONFIG_PKG_USING_KI is not set +# CONFIG_PKG_USING_ARMv7M_DWT is not set +# CONFIG_PKG_USING_VT100 is not set +# CONFIG_PKG_USING_UKAL is not set +# CONFIG_PKG_USING_CRCLIB is not set + +# +# entertainment: terminal games and other interesting software packages +# +# CONFIG_PKG_USING_THREES is not set +# CONFIG_PKG_USING_2048 is not set +# CONFIG_PKG_USING_SNAKE is not set +# CONFIG_PKG_USING_TETRIS is not set +# CONFIG_PKG_USING_DONUT is not set +# CONFIG_PKG_USING_ACLOCK is not set +# CONFIG_PKG_USING_LWGPS is not set +# CONFIG_PKG_USING_STATE_MACHINE is not set +# CONFIG_PKG_USING_MCURSES is not set +# CONFIG_PKG_USING_COWSAY is not set +CONFIG_FT2004=y + +# +# Hardware Drivers Config +# + +# +# On-chip Peripheral Drivers +# +CONFIG_BSP_USING_UART=y +CONFIG_RT_USING_UART1=y +# CONFIG_RT_USING_UART0 is not set +CONFIG_BSP_USING_SDC=y +# CONFIG_BSP_SDC_DEBUG_PRINT is not set +# CONFIG_BSP_SDC_USE_IRQ is not set +CONFIG_BSP_USING_GMAC=y +# CONFIG_BSP_USING_GMAC0 is not set +CONFIG_BSP_USING_GMAC1=y +CONFIG_RT_LWIP_ETH_PAD_SIZE=2 +# CONFIG_RAW_DATA_PRINT is not set +CONFIG_BSP_USE_SPI=y +# CONFIG_BSP_SPI_DEBUG is not set +CONFIG_BSP_USE_GPIO=y +# CONFIG_BSP_GPIO_DEBUG is not set +CONFIG_BSP_USE_CAN=y +CONFIG_BSP_USING_CAN0=y +# CONFIG_BSP_USING_CAN1 is not set +# CONFIG_BSP_USING_CAN0_DEBUG is not set + +# +# Board extended module Drivers +# diff --git a/bsp/ft2004/Kconfig b/bsp/ft2004/Kconfig new file mode 100644 index 0000000000..1bee562e48 --- /dev/null +++ b/bsp/ft2004/Kconfig @@ -0,0 +1,30 @@ +mainmenu "RT-Thread Project Configuration" + +config BSP_DIR + string + option env="BSP_ROOT" + default "." + +config RTT_DIR + string + option env="RTT_ROOT" + default "../.." + +config PKGS_DIR + string + option env="PKGS_ROOT" + default "packages" + +source "$RTT_DIR/Kconfig" +source "$PKGS_DIR/Kconfig" + + +config FT2004 + bool + select ARCH_ARM_CORTEX_A + select RT_USING_COMPONENTS_INIT + select RT_USING_USER_MAIN + default y + + +source "./libraries/Kconfig" diff --git a/bsp/ft2004/README.md b/bsp/ft2004/README.md new file mode 100644 index 0000000000..96229d3c18 --- /dev/null +++ b/bsp/ft2004/README.md @@ -0,0 +1,236 @@ +# ft2004 四核开发板 BSP 说明 + +## 简介 + +本文档为 飞腾技术公司 ft2000/4 开发板的 BSP (板级支持包) 说明。 + +主要内容如下: + +- 开发板资源介绍 +- BSP 外设支持 +- 使用方法 +- 相关实验 + +### 1. 开发板资源介绍 + +FT-2000/4 是一款面向桌面应用的高性能通用 4 核处理器。每 2 个核构成 1 +个处理器核簇(Cluster),并共享 L2 Cache。主要技术特征如下: + +- 兼容 ARM v8 64 位指令系统,兼容 32 位指令 +- 支持单精度、双精度浮点运算指令 +- 支持 ASIMD 处理指令 +- 集成 2 个 DDR4 通道,可对 DDR 存储数据进行实时加密 +- 集成 34 Lane PCIE3.0 接口:2 个 X16(每个可拆分成 2 个 X8),2 个 X1 +- 集成 2 个 GMAC,RGMII 接口,支持 10/100/1000 自适应 +- 集成 1 个 SD 卡控制器,兼容 SD 2.0 规范 +- 集成 1 个 HDAudio,支持音频输出,可同时支持最多 4 个 Codec +- 集成 SM2、SM3、SM4 模块 +- 集成 4 个 UART,1 个 LPC,32 个 GPIO,4 个 I2C,1 个 QSPI,2 个通 + 用 SPI,2 个 WDT,16 个外部中断(和 GPIO 共用 IO) +- 集成温度传感器 + +### 2. BSP 外设支持 + +| 外设名 | 支持情况 | 备注 | +| -------- | -------- | ---------------------- | +| ft_gicv3 | 支持 | gicv3 中断控制器 | +| ft_gmac | 支持 | ft gmac 千兆网卡控制器 | +| ft_i2c | 支持 | FT I2C | +| ft_qspi | 支持 | FT qspi 控制器 | +| ft_sd | 支持 | FT mmcsd 控制器 | +| ft_uart | 支持 | PrimeCell PL011 | +| ft_spi | 支持 | FT spi 控制器 | +| ft_gpio | 支持 | FT gpio 控制器 | +| ft_can | 支持 | FT can 控制器 | + +### 3. 使用方法 + +#### ubuntu 上环境搭建 + +1. 在 ubuntu 环境下通过指令,下载并安装交叉编译链 + +``` +sudo apt-get install gcc-arm-none-eabi +``` + +2. 安装之后,通过指令,确定交叉编译链安装完毕 + +``` +arm-none-eabi-gcc -v +``` + +3. 搭建 tftp 环境 + + - 在主机安装 tftp 服务 + > 使用 ubuntu 完成下列操作 + + ``` + sudo apt-get install tftp-hpa tftpd-hpa + sudo apt-get install xinetd + ``` + + - 新建 tftboot 目录,如: + `/mnt/d/tftboot` + + > 需要给 tftboot 目录执行权限`chmod 777 /**/tftboot` + + - 配置主机 tftpboot 服务 + + 新建并配置文件/etc/xinetd.d/tftp + + ``` + # /etc/xinetd.d/tftp + + server tftp + { + socket_type = dgram + protocol = udp + wait = yes + user = root + server = /usr/sbin/in.tftpd + server_args = -s /mnt/d/tftboot + disable = no + per_source = 11 + cps = 100 2 + flags = IPv4 + } + ``` + + - 启动主机 tftp 服务 + + ``` + sudo service tftpd-hpa start + ``` + + - 修改主机 tftp 配置 + 修改/etc/default/tftpd-hpa + + ``` + sudo nano /etc/default/tftpd-hpa + # /etc/default/tftpd-hpa + + TFTP_USERNAME="tftp" + TFTP_DIRECTORY="/mnt/d/tftboot" + TFTP_ADDRESS=":69" + TFTP_OPTIONS="-l -c -s" + ``` + + - 重启主机 tftp 服务 + > 每次开机要重启一次 + + ``` + sudo service tftpd-hpa restart + ``` + + - 测试主机 tftp 服务的可用性 + > 登录 tftp 服务,获取一个文件 + + ``` + $ tftp 192.168.4.50 + tftp> get test1234 + tftp> q + ``` + +#### 执行 + +1. 将本 bsp 包拷贝至 RT-THREAD bsp/目录下 + +1. 在 Ubuntu 终端下,切换至 bsp 目录 + +``` +cd xxx/bsp/ft2004 +``` + +3. 使用 scons -c 清空工程缓存 + +4. 使用 scons --menuconfig 配置需要的外设 + +![](./figures/onchipPeripheral.png) + +5. 使用 scons 编译代码,得到 rtthread.bin,并将 rtthread.bin 放入之前配置的 tftp 路径下。 + +6. 连接开发板对应串口到 PC, 在终端工具里打开相应的串口(115200-8-1-N)。 + +7. 将开发板网线接入局域网中 + +8. 本开发板自带 uboot,使用 uboot 自带 指令进行将 bin 文件下载至 ram 中 + +``` +setenv ipaddr 192.168.x.x +setenv serverip 192.168.x.x +setenv gatewayip 192.168.x.x +tftpboot 80100000 rtthread.bin +``` + +7. 执行跳转指令,便可以正常执行 + +``` +bootvx32 80100000 +或 +boot32 80100000 +``` + +![](./figures/启动演示图.png) + +### 5. 相关实验 + +#### 网卡 + +- 主机 ping 本机 指令 sudo ping 192.168.3.20 (默认) + +- rtt ping 主机 指令 ping 192.168.x.x (根据实际情况) + +- 通过界面 + +![](./figures/rttPing通过界面.png) + +#### sd 卡调试 + +- 通过基本命令进行,mv ,echo ,ls ,cd ,rm .... + +![](./figures/rttsd调试.png) + +#### spi flash 卡调试 + +- 找一块有 spi flash 插槽的 ft-2004 开发板,插入 sf25s 或 gd25q 系列 spi flash +- 配置 rt-thread 的编译选项,打开 BSP_USE_SPI 和 BSP_USE_GPIO 配置,关闭 BSP_USE_QSPI 配置,打开 rt-thread 的 SFUD 调试开关 +- 编译 rt-thread,加载版本启动,启动后显示 spi flash probe 成功 +- 执行 sf 基本操作,read, write, erase + +#### 推荐指令 + +1. sf probe S25FS256 + +2. sf read 0x1FFF000 16 + +3. sf write 0x1FFF000 16 25 68 78 95 15 75 20 + +4. sf read 0x1FFF000 16 + +5. sf erase 0x1FFF000 16 + +#### can 测试 + +1. 使用 scons menuconfig 选中 Enable Can + +2. 然后选中 Enable can0 ,Enable can0 work in loop back + +3. 烧录程序并且烧录 + +4. 打开 can 盒,将波特率设为 1000000 + +5. 然后通过 can 盒发送对应的数据(标准帧,扩展帧),就可以看见回复同样的内容 + +## 6. 参考资源 + +- ARM Architecture Reference Manual + +- FT-2000/4 软件编程手册-V1.4 + +## 7. 联系人信息 + +请联系飞腾嵌入式软件部 + +huanghe@phytium.com.cn + +zhugengyu@phytium.com.cn diff --git a/bsp/ft2004/SConscript b/bsp/ft2004/SConscript new file mode 100644 index 0000000000..fe0ae941ae --- /dev/null +++ b/bsp/ft2004/SConscript @@ -0,0 +1,14 @@ +# for module compiling +import os +Import('RTT_ROOT') + +cwd = str(Dir('#')) +objs = [] +list = os.listdir(cwd) + +for d in list: + path = os.path.join(cwd, d) + if os.path.isfile(os.path.join(path, 'SConscript')): + objs = objs + SConscript(os.path.join(d, 'SConscript')) + +Return('objs') diff --git a/bsp/ft2004/SConstruct b/bsp/ft2004/SConstruct new file mode 100644 index 0000000000..4b65c0b4d5 --- /dev/null +++ b/bsp/ft2004/SConstruct @@ -0,0 +1,40 @@ +''' +Author: your name +Date: 2021-03-01 18:57:53 +LastEditTime: 2021-04-25 09:47:24 +LastEditors: Please set LastEditors +Description: In User Settings Edit +FilePath: \rtthread\bsp\ft2004\SConstruct +''' +import os +import sys +import rtconfig + +if os.getenv('RTT_ROOT'): + RTT_ROOT = os.getenv('RTT_ROOT') +else: + RTT_ROOT = os.path.join(os.getcwd(), '..', '..') + +sys.path = sys.path + [os.path.join(RTT_ROOT, 'tools')] +from building import * + +TARGET = 'ft2004.' + rtconfig.TARGET_EXT + +DefaultEnvironment(tools=[]) +env = Environment(tools = ['mingw'], + AS = rtconfig.AS, ASFLAGS = rtconfig.AFLAGS, + CC = rtconfig.CC, CCFLAGS = rtconfig.CFLAGS, + CXX= rtconfig.CXX, CXXFLAGS = rtconfig.CFLAGS, + AR = rtconfig.AR, ARFLAGS = '-rc', + LINK = rtconfig.LINK, LINKFLAGS = rtconfig.LFLAGS) +env.PrependENVPath('PATH', rtconfig.EXEC_PATH) +env['ASCOM'] = env['ASPPCOM'] + +Export('RTT_ROOT') +Export('rtconfig') + +# prepare building environment +objs = PrepareBuilding(env, RTT_ROOT) + +# make a building +DoBuilding(TARGET, objs) diff --git a/bsp/ft2004/applications/SConscript b/bsp/ft2004/applications/SConscript new file mode 100644 index 0000000000..01eb940dfb --- /dev/null +++ b/bsp/ft2004/applications/SConscript @@ -0,0 +1,11 @@ +Import('RTT_ROOT') +Import('rtconfig') +from building import * + +cwd = os.path.join(str(Dir('#')), 'applications') +src = Glob('*.c') +CPPPATH = [cwd, str(Dir('#'))] + +group = DefineGroup('Applications', src, depend = [''], CPPPATH = CPPPATH) + +Return('group') diff --git a/bsp/ft2004/applications/startup.c b/bsp/ft2004/applications/startup.c new file mode 100644 index 0000000000..5b40540234 --- /dev/null +++ b/bsp/ft2004/applications/startup.c @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-12-05 Bernard the first version + */ + +#include +#include +#include "ft_cpu.h" +#include "ft_generic_timer.h" + +#include + +#ifdef RT_USING_SMP + +struct rt_thread test_core[RT_CPUS_NR]; +static char *core_thread_name[RT_CPUS_NR] = { + "core0_test", + "core1_test", + "core2_test", + "core3_test"}; +static rt_uint8_t core_stack[RT_CPUS_NR][1024]; + +static void demo_core_thread(void *parameter) +{ + while (1) + { + /* code */ + FCpu_SpinLock(); + rt_kprintf("Hi, core%d \r\n", FCpu_IdGet()); + FCpu_SpinUnlock(); + rt_thread_mdelay(20000); + } +} + +void demo_core(void) +{ + rt_ubase_t i; + rt_uint8_t cpu_id = 0; + for (i = 0; i < RT_CPUS_NR; i++) + { + cpu_id = i; + rt_thread_init(&test_core[i], + core_thread_name[i], + demo_core_thread, + RT_NULL, + &core_stack[i], + 1024, + 20, + 32); + + rt_thread_control(&test_core[i], RT_THREAD_CTRL_BIND_CPU, (void *)cpu_id); + rt_thread_startup(&test_core[i]); + } +} +#endif + +extern long gic_dis_dump(void); + +int main(void) +{ + int count = 1; + +#ifdef RT_USING_SMP + demo_core(); +#endif + + while (count++) + { + rt_thread_mdelay(2000); + } + + return RT_EOK; +} diff --git a/bsp/ft2004/cpu/armv7.h b/bsp/ft2004/cpu/armv7.h new file mode 100644 index 0000000000..2a945f3312 --- /dev/null +++ b/bsp/ft2004/cpu/armv7.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __ARMV7_H__ +#define __ARMV7_H__ + +/* the exception stack without VFP registers */ +struct rt_hw_exp_stack +{ + unsigned long r0; + unsigned long r1; + unsigned long r2; + unsigned long r3; + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long fp; + unsigned long ip; + unsigned long sp; + unsigned long lr; + unsigned long pc; + unsigned long cpsr; +}; + +struct rt_hw_stack +{ + unsigned long cpsr; + unsigned long r0; + unsigned long r1; + unsigned long r2; + unsigned long r3; + unsigned long r4; + unsigned long r5; + unsigned long r6; + unsigned long r7; + unsigned long r8; + unsigned long r9; + unsigned long r10; + unsigned long fp; + unsigned long ip; + unsigned long lr; + unsigned long pc; +}; + +#define USERMODE 0x10 +#define FIQMODE 0x11 +#define IRQMODE 0x12 +#define SVCMODE 0x13 +#define MONITORMODE 0x16 +#define ABORTMODE 0x17 +#define HYPMODE 0x1b +#define UNDEFMODE 0x1b +#define MODEMASK 0x1f +#define NOINT 0xc0 + +#define T_Bit (1 << 5) +#define F_Bit (1 << 6) +#define I_Bit (1 << 7) +#define A_Bit (1 << 8) +#define E_Bit (1 << 9) +#define J_Bit (1 << 24) + +#endif diff --git a/bsp/ft2004/cpu/cache.c b/bsp/ft2004/cpu/cache.c new file mode 100644 index 0000000000..0a7e702dbb --- /dev/null +++ b/bsp/ft2004/cpu/cache.c @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-29 quanzhao the first version + */ +#include +#include + +rt_inline rt_uint32_t rt_cpu_icache_line_size(void) +{ + rt_uint32_t ctr; + asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr)); + return 4 << (ctr & 0xF); +} + +rt_inline rt_uint32_t rt_cpu_dcache_line_size(void) +{ + rt_uint32_t ctr; + asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr)); + return 4 << ((ctr >> 16) & 0xF); +} + +void rt_hw_cpu_icache_invalidate(void *addr, int size) +{ + rt_uint32_t line_size = rt_cpu_icache_line_size(); + rt_uint32_t start_addr = (rt_uint32_t)addr; + rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; + + asm volatile ("dmb":::"memory"); + start_addr &= ~(line_size-1); + end_addr &= ~(line_size-1); + while (start_addr < end_addr) + { + asm volatile ("mcr p15, 0, %0, c7, c5, 1" :: "r"(start_addr)); /* icimvau */ + start_addr += line_size; + } + asm volatile ("dsb\n\tisb":::"memory"); +} + +void rt_hw_cpu_dcache_invalidate(void *addr, int size) +{ + rt_uint32_t line_size = rt_cpu_dcache_line_size(); + rt_uint32_t start_addr = (rt_uint32_t)addr; + rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; + + asm volatile ("dmb":::"memory"); + start_addr &= ~(line_size-1); + end_addr &= ~(line_size-1); + while (start_addr < end_addr) + { + asm volatile ("mcr p15, 0, %0, c7, c6, 1" :: "r"(start_addr)); /* dcimvac */ + start_addr += line_size; + } + asm volatile ("dsb":::"memory"); +} + +void rt_hw_cpu_dcache_clean(void *addr, int size) +{ + rt_uint32_t line_size = rt_cpu_dcache_line_size(); + rt_uint32_t start_addr = (rt_uint32_t)addr; + rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; + + asm volatile ("dmb":::"memory"); + start_addr &= ~(line_size-1); + end_addr &= ~(line_size-1); + while (start_addr < end_addr) + { + asm volatile ("mcr p15, 0, %0, c7, c10, 1" :: "r"(start_addr)); /* dccmvac */ + start_addr += line_size; + } + asm volatile ("dsb":::"memory"); +} + +void rt_hw_cpu_icache_ops(int ops, void *addr, int size) +{ + if (ops == RT_HW_CACHE_INVALIDATE) + rt_hw_cpu_icache_invalidate(addr, size); +} + +void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) +{ + if (ops == RT_HW_CACHE_FLUSH) + rt_hw_cpu_dcache_clean(addr, size); + else if (ops == RT_HW_CACHE_INVALIDATE) + rt_hw_cpu_dcache_invalidate(addr, size); +} + +rt_base_t rt_hw_cpu_icache_status(void) +{ + return 0; +} + +rt_base_t rt_hw_cpu_dcache_status(void) +{ + return 0; +} diff --git a/bsp/ft2004/cpu/context_gcc.S b/bsp/ft2004/cpu/context_gcc.S new file mode 100644 index 0000000000..768c5138e9 --- /dev/null +++ b/bsp/ft2004/cpu/context_gcc.S @@ -0,0 +1,182 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-05 Bernard the first version + */ + +#include "rtconfig.h" +.section .text, "ax" + +#ifdef RT_USING_SMP +#define rt_hw_interrupt_disable rt_hw_local_irq_disable +#define rt_hw_interrupt_enable rt_hw_local_irq_enable +#endif + +/* + * rt_base_t rt_hw_interrupt_disable(); + */ +.globl rt_hw_interrupt_disable +rt_hw_interrupt_disable: + mrs r0, cpsr + cpsid i + bx lr + +/* + * void rt_hw_interrupt_enable(rt_base_t level); + */ +.globl rt_hw_interrupt_enable +rt_hw_interrupt_enable: + msr cpsr, r0 + bx lr + +/* + * void rt_hw_context_switch_to(rt_uint32 to, struct rt_thread *to_thread); + * r0 --> to (thread stack) + * r1 --> to_thread + */ +.globl rt_hw_context_switch_to +rt_hw_context_switch_to: + ldr sp, [r0] @ get new task stack pointer + +#ifdef RT_USING_SMP + mov r0, r1 + bl rt_cpus_lock_status_restore +#endif /*RT_USING_SMP*/ + b rt_hw_context_switch_exit + +.section .bss.share.isr +_guest_switch_lvl: + .word 0 + +.globl vmm_virq_update + +.section .text.isr, "ax" +/* + * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to, struct rt_thread *to_thread); + * r0 --> from (from_thread stack) + * r1 --> to (to_thread stack) + * r2 --> to_thread + */ +.globl rt_hw_context_switch +rt_hw_context_switch: + stmfd sp!, {lr} @ push pc (lr should be pushed in place of PC) + stmfd sp!, {r0-r12, lr} @ push lr & register file + + mrs r4, cpsr + tst lr, #0x01 + orrne r4, r4, #0x20 @ it's thumb code + + stmfd sp!, {r4} @ push cpsr + +#ifdef RT_USING_LWP + stmfd sp, {r13, r14}^ @ push usr_sp usr_lr + sub sp, #8 +#endif +#ifdef RT_USING_FPU + /* fpu context */ + vmrs r6, fpexc + tst r6, #(1<<30) + beq 1f + vstmdb sp!, {d0-d15} + vstmdb sp!, {d16-d31} + vmrs r5, fpscr + stmfd sp!, {r5} +1: + stmfd sp!, {r6} +#endif + + str sp, [r0] @ store sp in preempted tasks TCB + ldr sp, [r1] @ get new task stack pointer + +#ifdef RT_USING_SMP + mov r0, r2 + bl rt_cpus_lock_status_restore +#endif /*RT_USING_SMP*/ + b rt_hw_context_switch_exit + +/* + * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); + */ +.equ Mode_USR, 0x10 +.equ Mode_FIQ, 0x11 +.equ Mode_IRQ, 0x12 +.equ Mode_SVC, 0x13 +.equ Mode_ABT, 0x17 +.equ Mode_UND, 0x1B +.equ Mode_SYS, 0x1F + +.equ I_Bit, 0x80 @ when I bit is set, IRQ is disabled +.equ F_Bit, 0x40 @ when F bit is set, FIQ is disabled + +.globl rt_thread_switch_interrupt_flag +.globl rt_interrupt_from_thread +.globl rt_interrupt_to_thread +.globl rt_hw_context_switch_interrupt +rt_hw_context_switch_interrupt: +#ifdef RT_USING_SMP + /* r0 :svc_mod context + * r1 :addr of from_thread's sp + * r2 :addr of to_thread's sp + * r3 :to_thread's tcb + */ + + str r0, [r1] + + ldr sp, [r2] + mov r0, r3 + bl rt_cpus_lock_status_restore + + b rt_hw_context_switch_exit + +#else /*RT_USING_SMP*/ + ldr r2, =rt_thread_switch_interrupt_flag + ldr r3, [r2] + cmp r3, #1 + beq _reswitch + ldr ip, =rt_interrupt_from_thread @ set rt_interrupt_from_thread + mov r3, #1 @ set rt_thread_switch_interrupt_flag to 1 + str r0, [ip] + str r3, [r2] +_reswitch: + ldr r2, =rt_interrupt_to_thread @ set rt_interrupt_to_thread + str r1, [r2] + bx lr +#endif /*RT_USING_SMP*/ + +.global rt_hw_context_switch_exit +rt_hw_context_switch_exit: + +#ifdef RT_USING_SMP +#ifdef RT_USING_SIGNALS + mov r0, sp + cps #Mode_IRQ + bl rt_signal_check + cps #Mode_SVC + mov sp, r0 +#endif +#endif +#ifdef RT_USING_FPU +/* fpu context */ + ldmfd sp!, {r6} + vmsr fpexc, r6 + tst r6, #(1<<30) + beq 1f + ldmfd sp!, {r5} + vmsr fpscr, r5 + vldmia sp!, {d16-d31} + vldmia sp!, {d0-d15} +1: +#endif + +#ifdef RT_USING_LWP + ldmfd sp, {r13, r14}^ /* usr_sp, usr_lr */ + add sp, #8 +#endif + ldmfd sp!, {r1} + msr spsr_cxsf, r1 /* original mode */ + ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ + diff --git a/bsp/ft2004/cpu/cp15.h b/bsp/ft2004/cpu/cp15.h new file mode 100644 index 0000000000..e0909eea36 --- /dev/null +++ b/bsp/ft2004/cpu/cp15.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2018-03-25 quanzhao the first version + */ +#ifndef __CP15_H__ +#define __CP15_H__ + +#define __get_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MRC p" #cp ", " #op1 ", %0, c" #CRn ", c" #CRm ", " #op2 \ + : "=r"(Rt) \ + : \ + : "memory") +#define __set_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MCR p" #cp ", " #op1 ", %0, c" #CRn ", c" #CRm ", " #op2 \ + : \ + : "r"(Rt) \ + : "memory") +#define __get_cp64(cp, op1, Rt, CRm) __asm__ volatile("MRRC p" #cp ", " #op1 ", %Q0, %R0, c" #CRm \ + : "=r"(Rt) \ + : \ + : "memory") +#define __set_cp64(cp, op1, Rt, CRm) __asm__ volatile("MCRR p" #cp ", " #op1 ", %Q0, %R0, c" #CRm \ + : \ + : "r"(Rt) \ + : "memory") + +unsigned long rt_cpu_get_smp_id(void); + +void rt_cpu_mmu_disable(void); +void rt_cpu_mmu_enable(void); +void rt_cpu_tlb_set(volatile unsigned long *); + +void rt_cpu_dcache_clean_flush(void); +void rt_cpu_icache_flush(void); + +void rt_cpu_vector_set_base(unsigned int addr); + +#endif diff --git a/bsp/ft2004/cpu/cp15_gcc.S b/bsp/ft2004/cpu/cp15_gcc.S new file mode 100644 index 0000000000..dd2436ffee --- /dev/null +++ b/bsp/ft2004/cpu/cp15_gcc.S @@ -0,0 +1,138 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-05 Bernard the first version + */ + +.globl rt_cpu_get_smp_id +rt_cpu_get_smp_id: + mrc p15, #0, r0, c0, c0, #5 + bx lr + +.globl rt_cpu_vector_set_base +rt_cpu_vector_set_base: + /* clear SCTRL.V to customize the vector address */ + mrc p15, #0, r1, c1, c0, #0 + bic r1, #(1 << 13) + mcr p15, #0, r1, c1, c0, #0 + /* set up the vector address */ + mcr p15, #0, r0, c12, c0, #0 + dsb + bx lr + +.globl rt_hw_cpu_dcache_enable +rt_hw_cpu_dcache_enable: + mrc p15, #0, r0, c1, c0, #0 + orr r0, r0, #0x00000004 + mcr p15, #0, r0, c1, c0, #0 + bx lr + +.globl rt_hw_cpu_icache_enable +rt_hw_cpu_icache_enable: + mrc p15, #0, r0, c1, c0, #0 + orr r0, r0, #0x00001000 + mcr p15, #0, r0, c1, c0, #0 + bx lr + +_FLD_MAX_WAY: + .word 0x3ff +_FLD_MAX_IDX: + .word 0x7fff + +.globl rt_cpu_dcache_clean_flush +rt_cpu_dcache_clean_flush: + push {r4-r11} + dmb + mrc p15, #1, r0, c0, c0, #1 @ read clid register + ands r3, r0, #0x7000000 @ get level of coherency + mov r3, r3, lsr #23 + beq finished + mov r10, #0 +loop1: + add r2, r10, r10, lsr #1 + mov r1, r0, lsr r2 + and r1, r1, #7 + cmp r1, #2 + blt skip + mcr p15, #2, r10, c0, c0, #0 + isb + mrc p15, #1, r1, c0, c0, #0 + and r2, r1, #7 + add r2, r2, #4 + ldr r4, _FLD_MAX_WAY + ands r4, r4, r1, lsr #3 + clz r5, r4 + ldr r7, _FLD_MAX_IDX + ands r7, r7, r1, lsr #13 +loop2: + mov r9, r4 +loop3: + orr r11, r10, r9, lsl r5 + orr r11, r11, r7, lsl r2 + mcr p15, #0, r11, c7, c14, #2 + subs r9, r9, #1 + bge loop3 + subs r7, r7, #1 + bge loop2 +skip: + add r10, r10, #2 + cmp r3, r10 + bgt loop1 + +finished: + dsb + isb + pop {r4-r11} + bx lr + +.globl rt_cpu_icache_flush +rt_cpu_icache_flush: + mov r0, #0 + mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate + dsb + isb + bx lr + +.globl rt_hw_cpu_dcache_disable +rt_hw_cpu_dcache_disable: + push {r4-r11, lr} + bl rt_cpu_dcache_clean_flush + mrc p15, #0, r0, c1, c0, #0 + bic r0, r0, #0x00000004 + mcr p15, #0, r0, c1, c0, #0 + pop {r4-r11, lr} + bx lr + +.globl rt_hw_cpu_icache_disable +rt_hw_cpu_icache_disable: + mrc p15, #0, r0, c1, c0, #0 + bic r0, r0, #0x00001000 + mcr p15, #0, r0, c1, c0, #0 + bx lr + +.globl rt_cpu_mmu_disable +rt_cpu_mmu_disable: + mcr p15, #0, r0, c8, c7, #0 @ invalidate tlb + mrc p15, #0, r0, c1, c0, #0 + bic r0, r0, #1 + mcr p15, #0, r0, c1, c0, #0 @ clear mmu bit + dsb + bx lr + +.globl rt_cpu_mmu_enable +rt_cpu_mmu_enable: + mrc p15, #0, r0, c1, c0, #0 + orr r0, r0, #0x001 + mcr p15, #0, r0, c1, c0, #0 @ set mmu enable bit + dsb + bx lr + +.globl rt_cpu_tlb_set +rt_cpu_tlb_set: + mcr p15, #0, r0, c2, c0, #0 + dmb + bx lr diff --git a/bsp/ft2004/cpu/cpu.c b/bsp/ft2004/cpu/cpu.c new file mode 100644 index 0000000000..cb3c9aa7b8 --- /dev/null +++ b/bsp/ft2004/cpu/cpu.c @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-09-15 Bernard first version + * 2018-11-22 Jesven add rt_hw_cpu_id() + */ + +#include +#include +#include + +#ifdef RT_USING_SMP + +RT_WEAK int rt_hw_cpu_id(void) +{ + int cpu_id; + __asm__ volatile ( + "mrc p15, 0, %0, c0, c0, 5" + :"=r"(cpu_id) + ); + cpu_id &= 0xf; + return cpu_id; +}; + +void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock) +{ + lock->slock = 0; +} + +void rt_hw_spin_lock(rt_hw_spinlock_t *lock) +{ + unsigned long tmp; + unsigned long newval; + rt_hw_spinlock_t lockval; + + __asm__ __volatile__( + "pld [%0]" + ::"r"(&lock->slock) + ); + + __asm__ __volatile__( + "1: ldrex %0, [%3]\n" + " add %1, %0, %4\n" + " strex %2, %1, [%3]\n" + " teq %2, #0\n" + " bne 1b" + : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) + : "r" (&lock->slock), "I" (1 << 16) + : "cc"); + + while (lockval.tickets.next != lockval.tickets.owner) { + __asm__ __volatile__("wfe":::"memory"); + lockval.tickets.owner = *(volatile unsigned short *)(&lock->tickets.owner); + } + + __asm__ volatile ("dmb":::"memory"); +} + +void rt_hw_spin_unlock(rt_hw_spinlock_t *lock) +{ + __asm__ volatile ("dmb":::"memory"); + lock->tickets.owner++; + __asm__ volatile ("dsb ishst\nsev":::"memory"); +} +#endif /*RT_USING_SMP*/ + +/** + * @addtogroup ARM CPU + */ +/*@{*/ + +/** shutdown CPU */ +RT_WEAK void rt_hw_cpu_shutdown() +{ + rt_uint32_t level; + rt_kprintf("shutdown...\n"); + + level = rt_hw_interrupt_disable(); + while (level) + { + RT_ASSERT(0); + } +} + +/*@}*/ diff --git a/bsp/ft2004/cpu/gic.c b/bsp/ft2004/cpu/gic.c new file mode 100644 index 0000000000..60e924d405 --- /dev/null +++ b/bsp/ft2004/cpu/gic.c @@ -0,0 +1,495 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-20 Bernard first version + * 2014-04-03 Grissiom many enhancements + * 2018-11-22 Jesven add rt_hw_ipi_send() + * add rt_hw_ipi_handler_install() + */ + +#include +#include + +#include "gic.h" +#include "cp15.h" + +struct arm_gic +{ + rt_uint32_t offset; /* the first interrupt index in the vector table */ + + rt_uint32_t dist_hw_base; /* the base address of the gic distributor */ + rt_uint32_t cpu_hw_base; /* the base addrees of the gic cpu interface */ +}; + +/* 'ARM_GIC_MAX_NR' is the number of cores */ +static struct arm_gic _gic_table[ARM_GIC_MAX_NR]; + +/** Macro to access the Generic Interrupt Controller Interface (GICC) +*/ +#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U) +#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U) +#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U) +#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU) +#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U) +#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U) +#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U) +#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU) + +/** Macro to access the Generic Interrupt Controller Distributor (GICD) +*/ +#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) +#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U) +#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n)/32U) * 4U) +#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n)/32U) * 4U) +#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n)/32U) * 4U) +#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n)/32U) * 4U) +#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n)/32U) * 4U) +#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n)/32U) * 4U) +#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n)/32U) * 4U) +#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n)/4U) * 4U) +#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n)/4U) * 4U) +#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n)/16U) * 4U) +#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U) +#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n)/4U) * 4U) +#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n)/4U) * 4U) +#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U) + +static unsigned int _gic_max_irq; + +int arm_gic_get_active_irq(rt_uint32_t index) +{ + int irq; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = GIC_CPU_INTACK(_gic_table[index].cpu_hw_base); + irq += _gic_table[index].offset; + return irq; +} + +void arm_gic_ack(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; + GIC_CPU_EOI(_gic_table[index].cpu_hw_base) = irq; +} + +void arm_gic_mask(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; +} + +void arm_gic_umask(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask; +} + +rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq) +{ + rt_uint32_t pend; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + } + else + { + /* INTID 0-15 Software Generated Interrupt */ + pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; + /* No CPU identification offered */ + if (pend != 0U) + { + pend = 1U; + } + else + { + pend = 0U; + } + } + + return (pend); +} + +void arm_gic_set_pending_irq(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U); + } + else + { + /* INTID 0-15 Software Generated Interrupt */ + /* Forward the interrupt to the CPU interface that requested it */ + GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U); + } +} + +void arm_gic_clear_pending_irq(rt_uint32_t index, int irq) +{ + rt_uint32_t mask; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + mask = 1U << (irq % 32U); + GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; + } + else + { + mask = 1U << ((irq % 4U) * 8U); + GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask; + } +} + +void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config) +{ + rt_uint32_t icfgr; + rt_uint32_t shift; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq); + shift = (irq % 16U) << 1U; + + icfgr &= (~(3U << shift)); + icfgr |= (config << shift); + + GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr; +} + +rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U)); +} + +void arm_gic_clear_active(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; +} + +/* Set up the cpu mask for the specific interrupt */ +void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask) +{ + rt_uint32_t old_tgt; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq); + + old_tgt &= ~(0x0FFUL << ((irq % 4U)*8U)); + old_tgt |= cpumask << ((irq % 4U)*8U); + + GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt; +} + +rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; +} + +void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority) +{ + rt_uint32_t mask; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq); + mask &= ~(0xFFUL << ((irq % 4U) * 8U)); + mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); + GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask; +} + +rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; +} + +void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + /* set priority mask */ + GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base) = priority & 0xFFUL; +} + +rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + return GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base); +} + +void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point) +{ + GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base) = binary_point & 0x7U; +} + +rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index) +{ + return GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base); +} + +rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq) +{ + rt_uint32_t pending; + rt_uint32_t active; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + + return ((active << 1U) | pending); +} + +void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = ((filter_list & 0x3U) << 24U) | ((target_list & 0xFFUL) << 16U) | (irq & 0x0FUL); +} + +rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + return GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base); +} + +rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base); +} + +void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group) +{ + uint32_t igroupr; + uint32_t shift; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT(group <= 1U); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq); + shift = (irq % 32U); + igroupr &= (~(1U << shift)); + igroupr |= ( (group & 0x1U) << shift); + + GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr; +} + +rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; +} + +int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start) +{ + unsigned int gic_type, i; + rt_uint32_t cpumask = 1U << 0U; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + _gic_table[index].dist_hw_base = dist_base; + _gic_table[index].offset = irq_start; + + /* Find out how many interrupts are supported. */ + gic_type = GIC_DIST_TYPE(dist_base); + _gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U; + + /* + * The GIC only supports up to 1020 interrupt sources. + * Limit this to either the architected maximum, or the + * platform maximum. + */ + if (_gic_max_irq > 1020U) + _gic_max_irq = 1020U; + if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */ + _gic_max_irq = ARM_GIC_NR_IRQS; + + cpumask |= cpumask << 8U; + cpumask |= cpumask << 16U; + cpumask |= cpumask << 24U; + + GIC_DIST_CTRL(dist_base) = 0x0U; + + /* Set all global interrupts to be level triggered, active low. */ + for (i = 32U; i < _gic_max_irq; i += 16U) + GIC_DIST_CONFIG(dist_base, i) = 0x0U; + + /* Set all global interrupts to this CPU only. */ + for (i = 32U; i < _gic_max_irq; i += 4U) + GIC_DIST_TARGET(dist_base, i) = cpumask; + + /* Set priority on all interrupts. */ + for (i = 0U; i < _gic_max_irq; i += 4U) + GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U; + + /* Disable all interrupts. */ + for (i = 0U; i < _gic_max_irq; i += 32U) + GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU; + +#if 0 + /* All interrupts defaults to IGROUP1(IRQ). */ + for (i = 0; i < _gic_max_irq; i += 32) + GIC_DIST_IGROUP(dist_base, i) = 0xffffffff; +#endif + for (i = 0U; i < _gic_max_irq; i += 32U) + GIC_DIST_IGROUP(dist_base, i) = 0U; + + /* Enable group0 and group1 interrupt forwarding. */ + GIC_DIST_CTRL(dist_base) = 0x01U; + + return 0; +} + +int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + _gic_table[index].cpu_hw_base = cpu_base; + + GIC_CPU_PRIMASK(cpu_base) = 0xf0U; + GIC_CPU_BINPOINT(cpu_base) = 0x7U; + /* Enable CPU interrupt */ + GIC_CPU_CTRL(cpu_base) = 0x01U; + + return 0; +} + +void arm_gic_dump_type(rt_uint32_t index) +{ + unsigned int gic_type; + + gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base); + rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n", + (GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL, + _gic_table[index].dist_hw_base, + _gic_max_irq, + gic_type & (1U << 10U) ? "has" : "no", + gic_type); +} + +void arm_gic_dump(rt_uint32_t index) +{ + unsigned int i, k; + + k = GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base); + rt_kprintf("--- high pending priority: %d(%08x)\n", k, k); + rt_kprintf("--- hw mask ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n--- hw pending ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n--- hw active ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n"); +} + +long gic_dump(void) +{ + arm_gic_dump_type(0); + arm_gic_dump(0); + + return 0; +} +MSH_CMD_EXPORT(gic_dump, show gic status); + diff --git a/bsp/ft2004/cpu/gic.h b/bsp/ft2004/cpu/gic.h new file mode 100644 index 0000000000..e74b760f45 --- /dev/null +++ b/bsp/ft2004/cpu/gic.h @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-20 Bernard first version + */ + +#ifndef __GIC_H__ +#define __GIC_H__ + +#include +#include + +int arm_gic_get_active_irq(rt_uint32_t index); +void arm_gic_ack(rt_uint32_t index, int irq); + +void arm_gic_mask(rt_uint32_t index, int irq); +void arm_gic_umask(rt_uint32_t index, int irq); + +rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq); +void arm_gic_set_pending_irq(rt_uint32_t index, int irq); +void arm_gic_clear_pending_irq(rt_uint32_t index, int irq); + +void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config); +rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq); + +void arm_gic_clear_active(rt_uint32_t index, int irq); + +void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask); +rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq); + +void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority); +rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq); + +void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority); +rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index); + +void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point); +rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index); + +rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq); + +void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list); + +rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index); + +rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index); + +void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group); +rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq); + +int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start); +int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base); + +void arm_gic_dump_type(rt_uint32_t index); +void arm_gic_dump(rt_uint32_t index); + +#endif + diff --git a/bsp/ft2004/cpu/gicv3.c b/bsp/ft2004/cpu/gicv3.c new file mode 100644 index 0000000000..27663f18df --- /dev/null +++ b/bsp/ft2004/cpu/gicv3.c @@ -0,0 +1,718 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-20 Bernard first version + * 2014-04-03 Grissiom many enhancements + * 2018-11-22 Jesven add rt_hw_ipi_send() + * add rt_hw_ipi_handler_install() + */ + +#include +#include + +#include "gicv3.h" +#include "cp15.h" + +#ifndef RT_CPUS_NR +#define RT_CPUS_NR 1 +#endif + +struct arm_gic_v3 +{ + rt_uint32_t offset; /* the first interrupt index in the vector table */ + rt_uint32_t redist_hw_base[RT_CPUS_NR]; /* the pointer of the gic redistributor */ + rt_uint32_t dist_hw_base; /* the base address of the gic distributor */ + rt_uint32_t cpu_hw_base[RT_CPUS_NR]; /* the base addrees of the gic cpu interface */ +}; + +/* 'ARM_GIC_MAX_NR' is the number of cores */ +static struct arm_gic_v3 _gic_table[ARM_GIC_MAX_NR]; +static unsigned int _gic_max_irq; + +/** + * @name: arm_gic_cpumask_to_affval + * @msg: + * @in param cpu_mask: + * @out param cluster_id: aff1 [0:7],aff2 [8:15],aff3 [16:23] + * @out param target_list: Target List. The set of PEs for which SGI interrupts will be generated. Each bit corresponds to the + * PE within a cluster with an Affinity 0 value equal to the bit number. + * @return {rt_uint32_t} 0 is finish , 1 is data valid + */ +RT_WEAK rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list) +{ + return 0; +} + +RT_WEAK rt_uint64_t get_main_cpu_affval(void) +{ + return 0; +} + +int arm_gic_get_active_irq(rt_uint32_t index) +{ + int irq; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + __get_gicv3_reg(ICC_IAR1, irq); + + irq = (irq & 0x1FFFFFF) + _gic_table[index].offset; + return irq; +} + +void arm_gic_ack(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT(irq >= 0U); + + __asm__ volatile("dsb 0xF" :: + : "memory"); + __set_gicv3_reg(ICC_EOIR1, irq); +} + +void arm_gic_mask(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq < 32U) + { +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + + RT_ASSERT((cpu_id) < RT_CPUS_NR); + GIC_RDISTSGI_ICENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask; + } + else + { + GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; + } +} + +void arm_gic_umask(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq < 32U) + { +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + RT_ASSERT((cpu_id) < RT_CPUS_NR); + GIC_RDISTSGI_ISENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask; + } + else + { + GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask; + } +} + +rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq) +{ + rt_uint32_t pend; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + } + else + { + /* INTID 0-15 Software Generated Interrupt */ + pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; + /* No CPU identification offered */ + if (pend != 0U) + { + pend = 1U; + } + else + { + pend = 0U; + } + } + + return (pend); +} + +void arm_gic_set_pending_irq(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U); + } + else + { + /* INTID 0-15 Software Generated Interrupt */ + /* Forward the interrupt to the CPU interface that requested it */ + GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U); + } +} + +void arm_gic_clear_pending_irq(rt_uint32_t index, int irq) +{ + rt_uint32_t mask; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq >= 16U) + { + mask = 1U << (irq % 32U); + GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; + } + else + { + mask = 1U << ((irq % 4U) * 8U); + GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask; + } +} + +void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config) +{ + rt_uint32_t icfgr; + rt_uint32_t shift; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq); + shift = (irq % 16U) << 1U; + + icfgr &= (~(3U << shift)); + icfgr |= (config << shift); + + GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr; +} + +rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U)); +} + +void arm_gic_clear_active(rt_uint32_t index, int irq) +{ + rt_uint32_t mask = 1U << (irq % 32U); + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; +} + +/* Set up the cpu mask for the specific interrupt */ +void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask) +{ + rt_uint32_t old_tgt; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq); + + old_tgt &= ~(0x0FFUL << ((irq % 4U) * 8U)); + old_tgt |= cpumask << ((irq % 4U) * 8U); + + GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt; +} + +rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; +} + +void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority) +{ + rt_uint32_t mask; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq < 32U) + { +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + RT_ASSERT((cpu_id) < RT_CPUS_NR); + mask = GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq); + mask &= ~(0xFFUL << ((irq % 4U) * 8U)); + mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); + GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) = mask; + } + else + { + mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq); + mask &= ~(0xFFUL << ((irq % 4U) * 8U)); + mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); + GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask; + } +} + +rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + if (irq < 32U) + { +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + RT_ASSERT((cpu_id) < RT_CPUS_NR); + return (GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) >> ((irq % 4U) * 8U)) & 0xFFUL; + } + else + { + return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; + } +} + +void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + value &= 0xFFUL; + /* set priority mask */ + __set_gicv3_reg(ICC_SRE, value); +} + +rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + rt_uint32_t value; + + __get_gicv3_reg(ICC_SRE, value); + return value; +} + +void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + priority &= 0xFFUL; + /* set priority mask */ + __set_gicv3_reg(ICC_PMR, priority); +} + +rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + rt_uint32_t priority; + + __get_gicv3_reg(ICC_PMR, priority); + return priority; +} + +void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point) +{ + index = index; + binary_point &= 0x7U; + + __set_gicv3_reg(ICC_BPR1, binary_point); +} + +rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index) +{ + rt_uint32_t binary_point; + + index = index; + __get_gicv3_reg(ICC_BPR1, binary_point); + return binary_point; +} + +rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq) +{ + rt_uint32_t pending; + rt_uint32_t active; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; + + return ((active << 1U) | pending); +} + +void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode) +{ + rt_uint64_t sgi_val; + if (routing_mode) + { + sgi_val = (1 << 40) | ((irq & 0x0FUL) << 24); //Interrupts routed to all PEs in the system, excluding "self". + /* Write the ICC_SGI1R registers */ + __asm__ volatile("dsb 0xF" :: + : "memory"); + __set_cp64(15, 0, sgi_val, 12); + __asm__ volatile("isb 0xF" :: + : "memory"); + } + else + { + rt_uint32_t cluster_id, target_list; + while (arm_gic_cpumask_to_affval(&cpu_mask, &cluster_id, &target_list)) + { + sgi_val = ((irq & 0x0FUL) << 24 | + target_list | + ((cluster_id >> 8) & 0xff) << GIC_RSGI_AFF1_OFFSET | + ((cluster_id >> 16) & 0xff) << GIC_RSGI_AFF2_OFFSET | + ((cluster_id >> 24) & 0xff) << GIC_RSGI_AFF3_OFFSET); + + __asm__ volatile("dsb 0xF" :: + : "memory"); + __set_cp64(15, 0, sgi_val, 12); + __asm__ volatile("isb 0xF" :: + : "memory"); + } + } +} + +rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index) +{ + rt_uint32_t irq; + RT_ASSERT(index < ARM_GIC_MAX_NR); + + index = index; + __get_gicv3_reg(ICC_HPPIR1, irq); + return irq; +} + +rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base); +} + +void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group) +{ + uint32_t igroupr; + uint32_t shift; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT(group <= 1U); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq); + shift = (irq % 32U); + igroupr &= (~(1U << shift)); + igroupr |= ((group & 0x1U) << shift); + + GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr; +} + +rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + + irq = irq - _gic_table[index].offset; + RT_ASSERT(irq >= 0U); + + return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; +} + +static int arm_gicv3_wait_rwp(rt_uint32_t index, rt_uint32_t irq) +{ + rt_uint32_t rwp_bit; + rt_uint32_t base; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + if (irq < 32u) + { +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + RT_ASSERT((cpu_id) < RT_CPUS_NR); + base = _gic_table[index].redist_hw_base[cpu_id]; + rwp_bit = GICR_CTLR_RWP; + } + else + { + base = _gic_table[index].dist_hw_base; + rwp_bit = GICD_CTLR_RWP; + } + + while (__REG32(base) & rwp_bit) + { + ; + } + + return 0; +} + +int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start) +{ + rt_uint64_t cpu0_affval; + unsigned int gic_type, i; + + RT_ASSERT(index < ARM_GIC_MAX_NR); + + _gic_table[index].dist_hw_base = dist_base; + _gic_table[index].offset = irq_start; + + /* Find out how many interrupts are supported. */ + gic_type = GIC_DIST_TYPE(dist_base); + _gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U; + + /* + * The GIC only supports up to 1020 interrupt sources. + * Limit this to either the architected maximum, or the + * platform maximum. + */ + if (_gic_max_irq > 1020U) + _gic_max_irq = 1020U; + if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */ + _gic_max_irq = ARM_GIC_NR_IRQS; + + GIC_DIST_CTRL(dist_base) = 0x0U; + /* Wait for register write pending */ + arm_gicv3_wait_rwp(0, 32); + + /* Set all global interrupts to be level triggered, active low. */ + for (i = 32U; i < _gic_max_irq; i += 16U) + GIC_DIST_CONFIG(dist_base, i) = 0x0U; + + arm_gicv3_wait_rwp(0, 32); + + cpu0_affval = get_main_cpu_affval(); + /* Set all global interrupts to this CPU only. */ + for (i = 32U; i < _gic_max_irq; i++) + { + GIC_DIST_IROUTER_LOW(dist_base, i) = cpu0_affval; + GIC_DIST_IROUTER_HIGH(dist_base, i) = cpu0_affval >> 32; + } + + arm_gicv3_wait_rwp(0, 32); + + /* Set priority on spi interrupts. */ + for (i = 32U; i < _gic_max_irq; i += 4U) + GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U; + + arm_gicv3_wait_rwp(0, 32); + /* Disable all interrupts. */ + for (i = 0U; i < _gic_max_irq; i += 32U) + { + GIC_DIST_PENDING_CLEAR(dist_base, i) = 0xffffffffU; + GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU; + } + + arm_gicv3_wait_rwp(0, 32); + /* All interrupts defaults to IGROUP1(IRQ). */ + for (i = 0U; i < _gic_max_irq; i += 32U) + GIC_DIST_IGROUP(dist_base, i) = 0xffffffffU; + + arm_gicv3_wait_rwp(0, 32); + + /* + The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode. + Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled. + If affinity routing is not enabled, GICv3 can be configured for legacy operation . + Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state. + Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1: + GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts. + GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts. + GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts. + */ + GIC_DIST_CTRL(dist_base) = GICD_CTLR_ARE_NS | GICD_CTLR_ENGRP1NS; + + return 0; +} + +int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT((cpu_id) < RT_CPUS_NR); + _gic_table[index].redist_hw_base[cpu_id] = redist_addr; + + return 0; +} + +int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id) +{ + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT((cpu_id) < RT_CPUS_NR); + _gic_table[index].cpu_hw_base[cpu_id] = interface_addr; + + return 0; +} + +int arm_gic_redist_init(rt_uint32_t index) +{ + unsigned int i; + rt_uint32_t base; + +#ifdef RT_USING_SMP + rt_int32_t cpu_id = rt_hw_cpu_id(); +#else + rt_int32_t cpu_id = 0; +#endif + RT_ASSERT(index < ARM_GIC_MAX_NR); + RT_ASSERT((cpu_id) < RT_CPUS_NR); + + base = _gic_table[index].redist_hw_base[cpu_id]; + /* redistributor enable */ + GIC_RDIST_WAKER(base) &= ~(1U << 1); + while (GIC_RDIST_WAKER(base) & (1 << 2)) + { + ; + } + + /* Disable all sgi and ppi interrupt */ + GIC_RDISTSGI_ICENABLER0(base) = 0xFFFFFFFF; + arm_gicv3_wait_rwp(0, 0); + + /* Clear all inetrrupt pending */ + GIC_RDISTSGI_ICPENDR0(base) = 0xFFFFFFFF; + + /* the corresponding interrupt is Group 1 or Non-secure Group 1. */ + GIC_RDISTSGI_IGROUPR0(base, 0) = 0xFFFFFFFF; + GIC_RDISTSGI_IGRPMODR0(base, 0) = 0xFFFFFFFF; + + /* Configure default priorities for SGI 0:15 and PPI 16:31. */ + for (i = 0; i < 32; i += 4) + { + GIC_RDISTSGI_IPRIORITYR(base, i) = 0xa0a0a0a0U; + } + + /* Trigger level for PPI interrupts*/ + GIC_RDISTSGI_ICFGR1(base) = 0x0U; // PPI is level-sensitive. + return 0; +} + +int arm_gic_cpu_init(rt_uint32_t index) +{ + rt_uint32_t value; + RT_ASSERT(index < ARM_GIC_MAX_NR); + + value = arm_gic_get_system_register_enable_mask(index); + value |= (1U << 0); + arm_gic_set_system_register_enable_mask(index, value); + __set_gicv3_reg(ICC_CTLR, 0); + + arm_gic_set_interface_prior_mask(index, 0xFFU); + + /* Enable group1 interrupt */ + value = 0x1U; + __set_gicv3_reg(ICC_IGRPEN1, value); + + arm_gic_set_binary_point(0, 0); + + /* ICC_BPR0_EL1 determines the preemption group for both + Group 0 and Group 1 interrupts. + */ + value = 0x1U; + __set_gicv3_reg(ICC_CTLR, value); + + return 0; +} + +void arm_gic_dump_type(rt_uint32_t index) +{ + unsigned int gic_type; + + gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base); + rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n", + (GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL, + _gic_table[index].dist_hw_base, + _gic_max_irq, + gic_type & (1U << 10U) ? "has" : "no", + gic_type); +} + +void arm_gic_dump(rt_uint32_t index) +{ + unsigned int i, k; + + k = arm_gic_get_high_pending_irq(0); + rt_kprintf("--- high pending priority: %d(%08x)\n", k, k); + rt_kprintf("--- hw mask ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n--- hw pending ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n--- hw active ---\n"); + for (i = 0U; i < _gic_max_irq / 32U; i++) + { + rt_kprintf("0x%08x, ", + GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, + i * 32U)); + } + rt_kprintf("\n"); +} + +long gic_dump(void) +{ + arm_gic_dump_type(0); + arm_gic_dump(0); + + return 0; +} +MSH_CMD_EXPORT(gic_dump, show gic status); diff --git a/bsp/ft2004/cpu/gicv3.h b/bsp/ft2004/cpu/gicv3.h new file mode 100644 index 0000000000..0661a82a39 --- /dev/null +++ b/bsp/ft2004/cpu/gicv3.h @@ -0,0 +1,188 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-20 Bernard first version + */ + +#ifndef __GIC_V3_H__ +#define __GIC_V3_H__ + +#include +#include + +#define __get_gicv3_reg(CR, Rt) __asm__ volatile("MRC " CR \ + : "=r"(Rt) \ + : \ + : "memory") +#define __set_gicv3_reg(CR, Rt) __asm__ volatile("MCR " CR \ + : \ + : "r"(Rt) \ + : "memory") + +/* AArch32 System register interface to GICv3 */ +#define ICC_IAR0 "p15, 0, %0, c12, c8, 0" +#define ICC_IAR1 "p15, 0, %0, c12, c12, 0" +#define ICC_EOIR0 "p15, 0, %0, c12, c8, 1" +#define ICC_EOIR1 "p15, 0, %0, c12, c12, 1" +#define ICC_HPPIR0 "p15, 0, %0, c12, c8, 2" +#define ICC_HPPIR1 "p15, 0, %0, c12, c12, 2" +#define ICC_BPR0 "p15, 0, %0, c12, c8, 3" +#define ICC_BPR1 "p15, 0, %0, c12, c12, 3" +#define ICC_DIR "p15, 0, %0, c12, c11, 1" +#define ICC_PMR "p15, 0, %0, c4, c6, 0" +#define ICC_RPR "p15, 0, %0, c12, c11, 3" +#define ICC_CTLR "p15, 0, %0, c12, c12, 4" +#define ICC_MCTLR "p15, 6, %0, c12, c12, 4" +#define ICC_SRE "p15, 0, %0, c12, c12, 5" +#define ICC_HSRE "p15, 4, %0, c12, c9, 5" +#define ICC_MSRE "p15, 6, %0, c12, c12, 5" +#define ICC_IGRPEN0 "p15, 0, %0, c12, c12, 6" +#define ICC_IGRPEN1 "p15, 0, %0, c12, c12, 7" +#define ICC_MGRPEN1 "p15, 6, %0, c12, c12, 7" + +#define __REG32(x) (*((volatile unsigned int *)((rt_uint32_t)x))) + +#define ROUTED_TO_ALL (1) +#define ROUTED_TO_SPEC (0) + +/** Macro to access the Distributor Control Register (GICD_CTLR) +*/ +#define GICD_CTLR_RWP (1 << 31) +#define GICD_CTLR_E1NWF (1 << 7) +#define GICD_CTLR_DS (1 << 6) +#define GICD_CTLR_ARE_NS (1 << 5) +#define GICD_CTLR_ARE_S (1 << 4) +#define GICD_CTLR_ENGRP1S (1 << 2) +#define GICD_CTLR_ENGRP1NS (1 << 1) +#define GICD_CTLR_ENGRP0 (1 << 0) + +/** Macro to access the Redistributor Control Register (GICR_CTLR) +*/ +#define GICR_CTLR_UWP (1 << 31) +#define GICR_CTLR_DPG1S (1 << 26) +#define GICR_CTLR_DPG1NS (1 << 25) +#define GICR_CTLR_DPG0 (1 << 24) +#define GICR_CTLR_RWP (1 << 3) +#define GICR_CTLR_IR (1 << 2) +#define GICR_CTLR_CES (1 << 1) +#define GICR_CTLR_EnableLPI (1 << 0) + +/** Macro to access the Generic Interrupt Controller Interface (GICC) +*/ +#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U) +#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U) +#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U) +#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU) +#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U) +#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U) +#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U) +#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU) + +/** Macro to access the Generic Interrupt Controller Distributor (GICD) +*/ +#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) +#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U) +#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n) / 32U) * 4U) +#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n) / 32U) * 4U) +#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n) / 32U) * 4U) +#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n) / 32U) * 4U) +#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n) / 32U) * 4U) +#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n) / 32U) * 4U) +#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n) / 32U) * 4U) +#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n) / 4U) * 4U) +#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n) / 4U) * 4U) +#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n) / 16U) * 4U) +#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U) +#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n) / 4U) * 4U) +#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n) / 4U) * 4U) +#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U) +#define GIC_DIST_IROUTER_LOW(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U) +#define GIC_DIST_IROUTER_HIGH(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U + 4) + +/* SGI base address is at 64K offset from Redistributor base address */ +#define GIC_RSGI_OFFSET 0x10000 + +/** Macro to access the Generic Interrupt Controller Redistributor (GICD) +*/ +#define GIC_RDIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) +#define GIC_RDIST_IIDR(hw_base) __REG32((hw_base) + 0x004U) +#define GIC_RDIST_TYPER(hw_base) __REG32((hw_base) + 0x008U) +#define GIC_RDIST_TSTATUSR(hw_base) __REG32((hw_base) + 0x010U) +#define GIC_RDIST_WAKER(hw_base) __REG32((hw_base) + 0x014U) +#define GIC_RDIST_SETLPIR(hw_base) __REG32((hw_base) + 0x040U) +#define GIC_RDIST_CLRLPIR(hw_base) __REG32((hw_base) + 0x048U) +#define GIC_RDIST_PROPBASER(hw_base) __REG32((hw_base) + 0x070U) +#define GIC_RDIST_PENDBASER(hw_base) __REG32((hw_base) + 0x078U) +#define GIC_RDIST_INVLPIR(hw_base) __REG32((hw_base) + 0x0A0U) +#define GIC_RDIST_INVALLR(hw_base) __REG32((hw_base) + 0x0B0U) +#define GIC_RDIST_SYNCR(hw_base) __REG32((hw_base) + 0x0C0U) + +#define GIC_RDISTSGI_IGROUPR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x080U + (n)*4U) +#define GIC_RDISTSGI_ISENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x100U) +#define GIC_RDISTSGI_ICENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x180U) +#define GIC_RDISTSGI_ISPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x200U) +#define GIC_RDISTSGI_ICPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x280U) +#define GIC_RDISTSGI_ISACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x300U) +#define GIC_RDISTSGI_ICACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x380U) +#define GIC_RDISTSGI_IPRIORITYR(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x400U + ((n) / 4U) * 4U) +#define GIC_RDISTSGI_ICFGR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC00U) +#define GIC_RDISTSGI_ICFGR1(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC04U) +#define GIC_RDISTSGI_IGRPMODR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xD00U + (n)*4) +#define GIC_RDISTSGI_NSACR(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xE00U) + +rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list); +rt_uint64_t get_main_cpu_affval(void); +int arm_gic_get_active_irq(rt_uint32_t index); +void arm_gic_ack(rt_uint32_t index, int irq); + +void arm_gic_mask(rt_uint32_t index, int irq); +void arm_gic_umask(rt_uint32_t index, int irq); + +rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq); +void arm_gic_set_pending_irq(rt_uint32_t index, int irq); +void arm_gic_clear_pending_irq(rt_uint32_t index, int irq); + +void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config); +rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq); + +void arm_gic_clear_active(rt_uint32_t index, int irq); + +void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask); +rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq); + +void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority); +rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq); + +void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority); +rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index); + +void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point); +rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index); + +rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq); + +void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode); +rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index); + +rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index); + +void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group); +rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq); + +int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id); +int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id); +int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start); +int arm_gic_cpu_init(rt_uint32_t index); +int arm_gic_redist_init(rt_uint32_t index); + +void arm_gic_dump_type(rt_uint32_t index); +void arm_gic_dump(rt_uint32_t index); + +void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value); +rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index); + +#endif diff --git a/bsp/ft2004/cpu/gtimer.c b/bsp/ft2004/cpu/gtimer.c new file mode 100644 index 0000000000..b193a1593d --- /dev/null +++ b/bsp/ft2004/cpu/gtimer.c @@ -0,0 +1,171 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-03-30 huijie.feng first version + */ + +#include "cp15.h" +#include + +/** Set CNTFRQ + * This function assigns the given value to PL1 Physical Timer Counter Frequency Register (CNTFRQ). + * @param value: CNTFRQ Register value to set + */ +static inline void __set_cntfrq(rt_uint32_t value) +{ + __set_cp(15, 0, value, 14, 0, 0); +} + +/** Get CNTFRQ + * This function returns the value of the PL1 Physical Timer Counter Frequency Register (CNTFRQ). + * return CNTFRQ Register value + */ +static inline rt_uint32_t __get_cntfrq(void) +{ + rt_uint32_t result; + __get_cp(15, 0, result, 14, 0 , 0); + return result; +} + +/** Set CNTP_TVAL + * This function assigns the given value to PL1 Physical Timer Value Register (CNTP_TVAL). + * param value: CNTP_TVAL Register value to set + */ +static inline void __set_cntp_tval(rt_uint32_t value) +{ + __set_cp(15, 0, value, 14, 2, 0); +} + +/** Get CNTP_TVAL + * This function returns the value of the PL1 Physical Timer Value Register (CNTP_TVAL). + * return CNTP_TVAL Register value + */ +static inline rt_uint32_t __get_cntp_tval(void) +{ + rt_uint32_t result; + __get_cp(15, 0, result, 14, 2, 0); + return result; +} + +/** Get CNTPCT + * This function returns the value of the 64 bits PL1 Physical Count Register (CNTPCT). + * return CNTPCT Register value + */ +static inline rt_uint64_t __get_cntpct(void) +{ + rt_uint64_t result; + __get_cp64(15, 0, result, 14); + return result; +} + +/** Set CNTP_CVAL + * This function assigns the given value to 64bits PL1 Physical Timer CompareValue Register (CNTP_CVAL). + * param value: CNTP_CVAL Register value to set +*/ +static inline void __set_cntp_cval(rt_uint64_t value) +{ + __set_cp64(15, 2, value, 14); +} + +/** Get CNTP_CVAL + * This function returns the value of the 64 bits PL1 Physical Timer CompareValue Register (CNTP_CVAL). + * return CNTP_CVAL Register value + */ +static inline rt_uint64_t __get_cntp_cval(void) +{ + rt_uint64_t result; + __get_cp64(15, 2, result, 14); + return result; +} + +/** Set CNTP_CTL + * This function assigns the given value to PL1 Physical Timer Control Register (CNTP_CTL). + * param value: CNTP_CTL Register value to set + */ +static inline void __set_cntp_ctl(uint32_t value) +{ + __set_cp(15, 0, value, 14, 2, 1); +} + +/** Get CNTP_CTL register + * return CNTP_CTL Register value + */ +static inline rt_uint32_t __get_cntp_ctl(void) +{ + rt_uint32_t result; + __get_cp(15, 0, result, 14, 2, 1); + return result; +} + +/** Configures the frequency the timer shall run at. + * param value The timer frequency in Hz. + */ +void gtimer_set_counter_frequency(rt_uint32_t value) +{ + __set_cntfrq(value); + __asm__ volatile ("isb 0xF":::"memory"); +} + +/** Sets the reset value of the timer. + * param value: The value the timer is loaded with. + */ +void gtimer_set_load_value(rt_uint32_t value) +{ + __set_cntp_tval(value); + __asm__ volatile ("isb 0xF":::"memory"); +} + +/** Get the current counter value. + * return Current counter value. + */ +rt_uint32_t gtimer_get_current_value(void) +{ + return(__get_cntp_tval()); +} + +/** Get the current physical counter value. + * return Current physical counter value. + */ +rt_uint64_t gtimer_get_current_physical_value(void) +{ + return(__get_cntpct()); +} + +/** Set the physical compare value. + * param value: New physical timer compare value. + */ +void gtimer_set_physical_compare_value(rt_uint64_t value) +{ + __set_cntp_cval(value); + __asm__ volatile ("isb 0xF":::"memory"); +} + +/** Get the physical compare value. + * return Physical compare value. + */ +rt_uint64_t gtimer_get_physical_compare_value(void) +{ + return(__get_cntp_cval()); +} + +/** Configure the timer by setting the control value. + * param value: New timer control value. + */ +void gtimer_set_control(rt_uint32_t value) +{ + __set_cntp_ctl(value); + __asm__ volatile ("isb 0xF":::"memory"); +} + +/** Get the control value. + * return Control value. + */ +rt_uint32_t gtimer_get_control(void) +{ + return(__get_cntp_ctl()); +} + diff --git a/bsp/ft2004/cpu/gtimer.h b/bsp/ft2004/cpu/gtimer.h new file mode 100644 index 0000000000..160d0cefde --- /dev/null +++ b/bsp/ft2004/cpu/gtimer.h @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2021-03-30 huijie.feng first version + */ + +#ifndef __GTIMER_H__ +#define __GTIMER_H__ + +#include + +void gtimer_set_counter_frequency(rt_uint32_t value); +void gtimer_set_load_value(rt_uint32_t value); +rt_uint32_t gtimer_get_current_value(void); +rt_uint64_t gtimer_get_current_physical_value(void); +void gtimer_set_physical_compare_value(rt_uint64_t value); +rt_uint64_t gtimer_get_physical_compare_value(void); +void gtimer_set_control(rt_uint32_t value); +rt_uint32_t gtimer_get_control(void); + +#endif + diff --git a/bsp/ft2004/cpu/interrupt.c b/bsp/ft2004/cpu/interrupt.c new file mode 100644 index 0000000000..8dc9bd7c4b --- /dev/null +++ b/bsp/ft2004/cpu/interrupt.c @@ -0,0 +1,316 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-06 Bernard first version + * 2018-11-22 Jesven add smp support + */ + +#include +#include +#include "interrupt.h" + +#ifdef RT_USING_GIC_V2 +#include "gic.h" +#else +#include "gicv3.h" +#endif + +/* exception and interrupt handler table */ +struct rt_irq_desc isr_table[MAX_HANDLERS]; + +#ifndef RT_USING_SMP +/* Those varibles will be accessed in ISR, so we need to share them. */ +rt_uint32_t rt_interrupt_from_thread = 0; +rt_uint32_t rt_interrupt_to_thread = 0; +rt_uint32_t rt_thread_switch_interrupt_flag = 0; +#endif + +const unsigned int VECTOR_BASE = 0x00; +extern void rt_cpu_vector_set_base(unsigned int addr); +extern int system_vectors; + +void rt_hw_vector_init(void) +{ + rt_cpu_vector_set_base((unsigned int)&system_vectors); +} + +#ifdef RT_USING_GIC_V2 +/** + * This function will initialize hardware interrupt + */ +void rt_hw_interrupt_init(void) +{ + rt_uint32_t gic_cpu_base; + rt_uint32_t gic_dist_base; + rt_uint32_t gic_irq_start; + + /* initialize vector table */ + rt_hw_vector_init(); + + /* initialize exceptions table */ + rt_memset(isr_table, 0x00, sizeof(isr_table)); + + /* initialize ARM GIC */ + gic_dist_base = platform_get_gic_dist_base(); + gic_cpu_base = platform_get_gic_cpu_base(); + + gic_irq_start = GIC_IRQ_START; + + arm_gic_dist_init(0, gic_dist_base, gic_irq_start); + arm_gic_cpu_init(0, gic_cpu_base); +} +#else +/** + * This function will initialize hardware interrupt + * Called by the primary cpu(cpu0) + */ +void rt_hw_interrupt_init(void) +{ + rt_uint32_t gic_dist_base; + rt_uint32_t gic_irq_start; + + /* initialize vector table */ + rt_hw_vector_init(); + + /* initialize exceptions table */ + rt_memset(isr_table, 0x00, sizeof(isr_table)); + + /* initialize ARM GIC */ + gic_dist_base = platform_get_gic_dist_base(); + gic_irq_start = GIC_IRQ_START; + + arm_gic_dist_init(0, gic_dist_base, gic_irq_start); + + arm_gic_cpu_init(0); + arm_gic_redist_init(0); +} + +#endif + +/** + * This function will mask a interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_mask(int vector) +{ + arm_gic_mask(0, vector); +} + +/** + * This function will un-mask a interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_umask(int vector) +{ + arm_gic_umask(0, vector); +} + +/** + * This function returns the active interrupt number. + * @param none + */ +int rt_hw_interrupt_get_irq(void) +{ + return arm_gic_get_active_irq(0); +} + +/** + * This function acknowledges the interrupt. + * @param vector the interrupt number + */ +void rt_hw_interrupt_ack(int vector) +{ + arm_gic_ack(0, vector); +} + +/** + * This function set interrupt CPU targets. + * @param vector: the interrupt number + * cpu_mask: target cpus mask, one bit for one core + */ +void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask) +{ + arm_gic_set_cpu(0, vector, cpu_mask); +} + +/** + * This function get interrupt CPU targets. + * @param vector: the interrupt number + * @return target cpus mask, one bit for one core + */ +unsigned int rt_hw_interrupt_get_target_cpus(int vector) +{ + return arm_gic_get_target_cpu(0, vector); +} + +/** + * This function set interrupt triger mode. + * @param vector: the interrupt number + * mode: interrupt triger mode; 0: level triger, 1: edge triger + */ +void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode) +{ + arm_gic_set_configuration(0, vector, mode); +} + +/** + * This function get interrupt triger mode. + * @param vector: the interrupt number + * @return interrupt triger mode; 0: level triger, 1: edge triger + */ +unsigned int rt_hw_interrupt_get_triger_mode(int vector) +{ + return arm_gic_get_configuration(0, vector); +} + +/** + * This function set interrupt pending flag. + * @param vector: the interrupt number + */ +void rt_hw_interrupt_set_pending(int vector) +{ + arm_gic_set_pending_irq(0, vector); +} + +/** + * This function get interrupt pending flag. + * @param vector: the interrupt number + * @return interrupt pending flag, 0: not pending; 1: pending + */ +unsigned int rt_hw_interrupt_get_pending(int vector) +{ + return arm_gic_get_pending_irq(0, vector); +} + +/** + * This function clear interrupt pending flag. + * @param vector: the interrupt number + */ +void rt_hw_interrupt_clear_pending(int vector) +{ + arm_gic_clear_pending_irq(0, vector); +} + +/** + * This function set interrupt priority value. + * @param vector: the interrupt number + * priority: the priority of interrupt to set + */ +void rt_hw_interrupt_set_priority(int vector, unsigned int priority) +{ + arm_gic_set_priority(0, vector, priority); +} + +/** + * This function get interrupt priority. + * @param vector: the interrupt number + * @return interrupt priority value + */ +unsigned int rt_hw_interrupt_get_priority(int vector) +{ + return arm_gic_get_priority(0, vector); +} + +/** + * This function set priority masking threshold. + * @param priority: priority masking threshold + */ +void rt_hw_interrupt_set_priority_mask(unsigned int priority) +{ + arm_gic_set_interface_prior_mask(0, priority); +} + +/** + * This function get priority masking threshold. + * @param none + * @return priority masking threshold + */ +unsigned int rt_hw_interrupt_get_priority_mask(void) +{ + return arm_gic_get_interface_prior_mask(0); +} + +/** + * This function set priority grouping field split point. + * @param bits: priority grouping field split point + * @return 0: success; -1: failed + */ +int rt_hw_interrupt_set_prior_group_bits(unsigned int bits) +{ + int status; + + if (bits < 8) + { + arm_gic_set_binary_point(0, (7 - bits)); + status = 0; + } + else + { + status = -1; + } + + return (status); +} + +/** + * This function get priority grouping field split point. + * @param none + * @return priority grouping field split point + */ +unsigned int rt_hw_interrupt_get_prior_group_bits(void) +{ + unsigned int bp; + + bp = arm_gic_get_binary_point(0) & 0x07; + + return (7 - bp); +} + +/** + * This function will install a interrupt service routine to a interrupt. + * @param vector the interrupt number + * @param new_handler the interrupt service routine to be installed + * @param old_handler the old interrupt service routine + */ +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, + void *param, const char *name) +{ + rt_isr_handler_t old_handler = RT_NULL; + + if (vector < MAX_HANDLERS) + { + old_handler = isr_table[vector].handler; + + if (handler != RT_NULL) + { +#ifdef RT_USING_INTERRUPT_INFO + rt_strncpy(isr_table[vector].name, name, RT_NAME_MAX); +#endif /* RT_USING_INTERRUPT_INFO */ + isr_table[vector].handler = handler; + isr_table[vector].param = param; + } + } + + return old_handler; +} + +#ifdef RT_USING_SMP +void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask) +{ +#ifdef RT_USING_GIC_V2 + arm_gic_send_sgi(0, ipi_vector, cpu_mask, 0); +#else + arm_gic_send_affinity_sgi(0, ipi_vector, cpu_mask, ROUTED_TO_SPEC); +#endif +} + +void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler) +{ + /* note: ipi_vector maybe different with irq_vector */ + rt_hw_interrupt_install(ipi_vector, ipi_isr_handler, 0, "IPI_HANDLER"); +} +#endif diff --git a/bsp/ft2004/cpu/interrupt.h b/bsp/ft2004/cpu/interrupt.h new file mode 100644 index 0000000000..dda6c5534a --- /dev/null +++ b/bsp/ft2004/cpu/interrupt.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-06 Bernard first version + */ + +#ifndef __INTERRUPT_H__ +#define __INTERRUPT_H__ + +#include +#include + +#define INT_IRQ 0x00 +#define INT_FIQ 0x01 + +#define IRQ_MODE_TRIG_LEVEL (0x00) /* Trigger: level triggered interrupt */ +#define IRQ_MODE_TRIG_EDGE (0x01) /* Trigger: edge triggered interrupt */ + +void rt_hw_vector_init(void); + +void rt_hw_interrupt_init(void); +void rt_hw_interrupt_mask(int vector); +void rt_hw_interrupt_umask(int vector); + +int rt_hw_interrupt_get_irq(void); +void rt_hw_interrupt_ack(int vector); + +void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask); +unsigned int rt_hw_interrupt_get_target_cpus(int vector); + +void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode); +unsigned int rt_hw_interrupt_get_triger_mode(int vector); + +void rt_hw_interrupt_set_pending(int vector); +unsigned int rt_hw_interrupt_get_pending(int vector); +void rt_hw_interrupt_clear_pending(int vector); + +void rt_hw_interrupt_set_priority(int vector, unsigned int priority); +unsigned int rt_hw_interrupt_get_priority(int vector); + +void rt_hw_interrupt_set_priority_mask(unsigned int priority); +unsigned int rt_hw_interrupt_get_priority_mask(void); + +int rt_hw_interrupt_set_prior_group_bits(unsigned int bits); +unsigned int rt_hw_interrupt_get_prior_group_bits(void); + +rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, + void *param, const char *name); + +#ifdef RT_USING_SMP +void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask); +void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler); +#endif + +#endif + diff --git a/bsp/ft2004/cpu/mmu.c b/bsp/ft2004/cpu/mmu.c new file mode 100644 index 0000000000..edea7e6b8b --- /dev/null +++ b/bsp/ft2004/cpu/mmu.c @@ -0,0 +1,183 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2012-01-10 bernard porting to AM1808 + */ + +#include +#include +#include + +#include "cp15.h" +#include "mmu.h" + +/* dump 2nd level page table */ +void rt_hw_cpu_dump_page_table_2nd(rt_uint32_t *ptb) +{ + int i; + int fcnt = 0; + + for (i = 0; i < 256; i++) + { + rt_uint32_t pte2 = ptb[i]; + if ((pte2 & 0x3) == 0) + { + if (fcnt == 0) + rt_kprintf(" "); + rt_kprintf("%04x: ", i); + fcnt++; + if (fcnt == 16) + { + rt_kprintf("fault\n"); + fcnt = 0; + } + continue; + } + if (fcnt != 0) + { + rt_kprintf("fault\n"); + fcnt = 0; + } + + rt_kprintf(" %04x: %x: ", i, pte2); + if ((pte2 & 0x3) == 0x1) + { + rt_kprintf("L,ap:%x,xn:%d,texcb:%02x\n", + ((pte2 >> 7) | (pte2 >> 4)) & 0xf, + (pte2 >> 15) & 0x1, + ((pte2 >> 10) | (pte2 >> 2)) & 0x1f); + } + else + { + rt_kprintf("S,ap:%x,xn:%d,texcb:%02x\n", + ((pte2 >> 7) | (pte2 >> 4)) & 0xf, pte2 & 0x1, + ((pte2 >> 4) | (pte2 >> 2)) & 0x1f); + } + } +} + +void rt_hw_cpu_dump_page_table(rt_uint32_t *ptb) +{ + int i; + int fcnt = 0; + + rt_kprintf("page table@%p\n", ptb); + for (i = 0; i < 1024 * 4; i++) + { + rt_uint32_t pte1 = ptb[i]; + if ((pte1 & 0x3) == 0) + { + rt_kprintf("%03x: ", i); + fcnt++; + if (fcnt == 16) + { + rt_kprintf("fault\n"); + fcnt = 0; + } + continue; + } + if (fcnt != 0) + { + rt_kprintf("fault\n"); + fcnt = 0; + } + + rt_kprintf("%03x: %08x: ", i, pte1); + if ((pte1 & 0x3) == 0x3) + { + rt_kprintf("LPAE\n"); + } + else if ((pte1 & 0x3) == 0x1) + { + rt_kprintf("pte,ns:%d,domain:%d\n", + (pte1 >> 3) & 0x1, (pte1 >> 5) & 0xf); + /* + *rt_hw_cpu_dump_page_table_2nd((void*)((pte1 & 0xfffffc000) + * - 0x80000000 + 0xC0000000)); + */ + } + else if (pte1 & (1 << 18)) + { + rt_kprintf("super section,ns:%d,ap:%x,xn:%d,texcb:%02x\n", + (pte1 >> 19) & 0x1, + ((pte1 >> 13) | (pte1 >> 10)) & 0xf, + (pte1 >> 4) & 0x1, + ((pte1 >> 10) | (pte1 >> 2)) & 0x1f); + } + else + { + rt_kprintf("section,ns:%d,ap:%x," + "xn:%d,texcb:%02x,domain:%d\n", + (pte1 >> 19) & 0x1, + ((pte1 >> 13) | (pte1 >> 10)) & 0xf, + (pte1 >> 4) & 0x1, + (((pte1 & (0x7 << 12)) >> 10) | + ((pte1 & 0x0c) >> 2)) & 0x1f, + (pte1 >> 5) & 0xf); + } + } +} + +/* level1 page table, each entry for 1MB memory. */ +volatile static unsigned long MMUTable[4 * 1024] __attribute__((aligned(16 * 1024))); +void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart, + rt_uint32_t vaddrEnd, + rt_uint32_t paddrStart, + rt_uint32_t attr) +{ + volatile rt_uint32_t *pTT; + volatile int i, nSec; + pTT = (rt_uint32_t *)MMUTable + (vaddrStart >> 20); + nSec = (vaddrEnd >> 20) - (vaddrStart >> 20); + for (i = 0; i <= nSec; i++) + { + *pTT = attr | (((paddrStart >> 20) + i) << 20); + pTT++; + } +} + +unsigned long rt_hw_set_domain_register(unsigned long domain_val) +{ + unsigned long old_domain; + + asm volatile("mrc p15, 0, %0, c3, c0\n" + : "=r"(old_domain)); + asm volatile("mcr p15, 0, %0, c3, c0\n" + : + : "r"(domain_val) + : "memory"); + + return old_domain; +} + +void rt_hw_init_mmu_table(struct mem_desc *mdesc, rt_uint32_t size) +{ + /* set page table */ + for (; size > 0; size--) + { + rt_hw_mmu_setmtt(mdesc->vaddr_start, mdesc->vaddr_end, + mdesc->paddr_start, mdesc->attr); + mdesc++; + } +} + +void rt_hw_mmu_init(void) +{ + rt_cpu_dcache_clean_flush(); + rt_cpu_icache_flush(); + + rt_hw_cpu_dcache_disable(); + rt_hw_cpu_icache_disable(); + rt_cpu_mmu_disable(); + + // rt_hw_cpu_dump_page_table(MMUTable); + rt_hw_set_domain_register(0x55555555); + rt_cpu_tlb_set(MMUTable); + rt_cpu_mmu_enable(); + rt_hw_cpu_icache_enable(); + rt_hw_cpu_dcache_enable(); +} diff --git a/bsp/ft2004/cpu/mmu.h b/bsp/ft2004/cpu/mmu.h new file mode 100644 index 0000000000..b2875fa21b --- /dev/null +++ b/bsp/ft2004/cpu/mmu.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2019-03-25 quanzhao the first version + */ +#ifndef __MMU_H_ +#define __MMU_H_ + +#include + +#define DESC_SEC (0x2) +#define MEMWBWA ((1 << 12) | (3 << 2)) /* write back, write allocate */ +#define MEMWB (3 << 2) /* write back, no write allocate */ +#define MEMWT (2 << 2) /* write through, no write allocate */ +#define SHAREDEVICE (1 << 2) /* shared device */ +#define STRONGORDER (0 << 2) /* strong ordered */ +#define XN (1 << 4) /* eXecute Never */ +#define AP_RW (3 << 10) /* supervisor=RW, user=RW */ +#define AP_RO (2 << 10) /* supervisor=RW, user=RO */ +#define SHARED (1 << 16) /* shareable */ + +#define DOMAIN_FAULT (0x0) +#define DOMAIN_CHK (0x1) +#define DOMAIN_NOTCHK (0x3) +#define DOMAIN0 (0x0 << 5) +#define DOMAIN1 (0x1 << 5) + +#define DOMAIN0_ATTR (DOMAIN_CHK << 0) +#define DOMAIN1_ATTR (DOMAIN_FAULT << 2) + +/* device mapping type */ +#define DEVICE_MEM (SHARED | AP_RW | DOMAIN0 | SHAREDEVICE | DESC_SEC | XN) +/* normal memory mapping type */ +#define NORMAL_MEM (SHARED | AP_RW | DOMAIN0 | MEMWBWA | DESC_SEC) + +struct mem_desc +{ + rt_uint32_t vaddr_start; + rt_uint32_t vaddr_end; + rt_uint32_t paddr_start; + rt_uint32_t attr; +}; + +void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart, + rt_uint32_t vaddrEnd, + rt_uint32_t paddrStart, + rt_uint32_t attr); + +#endif diff --git a/bsp/ft2004/cpu/pmu.c b/bsp/ft2004/cpu/pmu.c new file mode 100644 index 0000000000..8ffc1dede6 --- /dev/null +++ b/bsp/ft2004/cpu/pmu.c @@ -0,0 +1,20 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#include +#include "pmu.h" + +void rt_hw_pmu_dump_feature(void) +{ + unsigned long reg; + + reg = rt_hw_pmu_get_control(); + rt_kprintf("ARM PMU Implementor: %c, ID code: %02x, %d counters\n", + reg >> 24, (reg >> 16) & 0xff, (reg >> 11) & 0x1f); + RT_ASSERT(ARM_PMU_CNTER_NR == ((reg >> 11) & 0x1f)); +} diff --git a/bsp/ft2004/cpu/pmu.h b/bsp/ft2004/cpu/pmu.h new file mode 100644 index 0000000000..b5aa032cd5 --- /dev/null +++ b/bsp/ft2004/cpu/pmu.h @@ -0,0 +1,174 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + */ +#ifndef __PMU_H__ +#define __PMU_H__ + +#include "board.h" + +/* Number of counters */ +#define ARM_PMU_CNTER_NR 4 + +enum rt_hw_pmu_event_type +{ + ARM_PMU_EVENT_PMNC_SW_INCR = 0x00, + ARM_PMU_EVENT_L1_ICACHE_REFILL = 0x01, + ARM_PMU_EVENT_ITLB_REFILL = 0x02, + ARM_PMU_EVENT_L1_DCACHE_REFILL = 0x03, + ARM_PMU_EVENT_L1_DCACHE_ACCESS = 0x04, + ARM_PMU_EVENT_DTLB_REFILL = 0x05, + ARM_PMU_EVENT_MEM_READ = 0x06, + ARM_PMU_EVENT_MEM_WRITE = 0x07, + ARM_PMU_EVENT_INSTR_EXECUTED = 0x08, + ARM_PMU_EVENT_EXC_TAKEN = 0x09, + ARM_PMU_EVENT_EXC_EXECUTED = 0x0A, + ARM_PMU_EVENT_CID_WRITE = 0x0B, +}; + +/* Enable bit */ +#define ARM_PMU_PMCR_E (0x01 << 0) +/* Event counter reset */ +#define ARM_PMU_PMCR_P (0x01 << 1) +/* Cycle counter reset */ +#define ARM_PMU_PMCR_C (0x01 << 2) +/* Cycle counter divider */ +#define ARM_PMU_PMCR_D (0x01 << 3) + +#ifdef __GNUC__ +rt_inline void rt_hw_pmu_enable_cnt(int divide64) +{ + unsigned long pmcr; + unsigned long pmcntenset; + + asm volatile("mrc p15, 0, %0, c9, c12, 0" + : "=r"(pmcr)); + pmcr |= ARM_PMU_PMCR_E | ARM_PMU_PMCR_P | ARM_PMU_PMCR_C; + if (divide64) + pmcr |= ARM_PMU_PMCR_D; + else + pmcr &= ~ARM_PMU_PMCR_D; + asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); + + /* enable all the counters */ + pmcntenset = ~0; + asm volatile("mcr p15, 0, %0, c9, c12, 1" ::"r"(pmcntenset)); + /* clear overflows(just in case) */ + asm volatile("mcr p15, 0, %0, c9, c12, 3" ::"r"(pmcntenset)); +} + +rt_inline unsigned long rt_hw_pmu_get_control(void) +{ + unsigned long pmcr; + asm("mrc p15, 0, %0, c9, c12, 0" + : "=r"(pmcr)); + return pmcr; +} + +rt_inline unsigned long rt_hw_pmu_get_ceid(void) +{ + unsigned long reg; + /* only PMCEID0 is supported, PMCEID1 is RAZ. */ + asm("mrc p15, 0, %0, c9, c12, 6" + : "=r"(reg)); + return reg; +} + +rt_inline unsigned long rt_hw_pmu_get_cnten(void) +{ + unsigned long pmcnt; + asm("mrc p15, 0, %0, c9, c12, 1" + : "=r"(pmcnt)); + return pmcnt; +} + +rt_inline void rt_hw_pmu_reset_cycle(void) +{ + unsigned long pmcr; + + asm volatile("mrc p15, 0, %0, c9, c12, 0" + : "=r"(pmcr)); + pmcr |= ARM_PMU_PMCR_C; + asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); + asm volatile("isb"); +} + +rt_inline void rt_hw_pmu_reset_event(void) +{ + unsigned long pmcr; + + asm volatile("mrc p15, 0, %0, c9, c12, 0" + : "=r"(pmcr)); + pmcr |= ARM_PMU_PMCR_P; + asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); + asm volatile("isb"); +} + +rt_inline unsigned long rt_hw_pmu_get_cycle(void) +{ + unsigned long cyc; + asm volatile("isb"); + asm volatile("mrc p15, 0, %0, c9, c13, 0" + : "=r"(cyc)); + return cyc; +} + +rt_inline void rt_hw_pmu_select_counter(int idx) +{ + RT_ASSERT(idx < ARM_PMU_CNTER_NR); + + asm volatile("mcr p15, 0, %0, c9, c12, 5" + : + : "r"(idx)); + /* Linux add an isb here, don't know why here. */ + asm volatile("isb"); +} + +rt_inline void rt_hw_pmu_select_event(int idx, + enum rt_hw_pmu_event_type eve) +{ + RT_ASSERT(idx < ARM_PMU_CNTER_NR); + + rt_hw_pmu_select_counter(idx); + asm volatile("mcr p15, 0, %0, c9, c13, 1" + : + : "r"(eve)); +} + +rt_inline unsigned long rt_hw_pmu_read_counter(int idx) +{ + unsigned long reg; + + rt_hw_pmu_select_counter(idx); + asm volatile("isb"); + asm volatile("mrc p15, 0, %0, c9, c13, 2" + : "=r"(reg)); + return reg; +} + +rt_inline unsigned long rt_hw_pmu_get_ovsr(void) +{ + unsigned long reg; + asm volatile("isb"); + asm("mrc p15, 0, %0, c9, c12, 3" + : "=r"(reg)); + return reg; +} + +rt_inline void rt_hw_pmu_clear_ovsr(unsigned long reg) +{ + asm("mcr p15, 0, %0, c9, c12, 3" + : + : "r"(reg)); + asm volatile("isb"); +} + +#endif + +void rt_hw_pmu_dump_feature(void); + +#endif /* end of include guard: __PMU_H__ */ diff --git a/bsp/ft2004/cpu/stack.c b/bsp/ft2004/cpu/stack.c new file mode 100644 index 0000000000..07d795ab5b --- /dev/null +++ b/bsp/ft2004/cpu/stack.c @@ -0,0 +1,71 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2011-09-23 Bernard the first version + * 2011-10-05 Bernard add thumb mode + */ +#include +#include +#include + +/** + * @addtogroup AM33xx + */ +/*@{*/ + +/** + * This function will initialize thread stack + * + * @param tentry the entry of thread + * @param parameter the parameter of entry + * @param stack_addr the beginning stack address + * @param texit the function will be called when thread exit + * + * @return stack address + */ +rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, + rt_uint8_t *stack_addr, void *texit) +{ + rt_uint32_t *stk; + + stack_addr += sizeof(rt_uint32_t); + stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8); + stk = (rt_uint32_t *)stack_addr; + *(--stk) = (rt_uint32_t)tentry; /* entry point */ + *(--stk) = (rt_uint32_t)texit; /* lr */ + *(--stk) = 0xdeadbeef; /* r12 */ + *(--stk) = 0xdeadbeef; /* r11 */ + *(--stk) = 0xdeadbeef; /* r10 */ + *(--stk) = 0xdeadbeef; /* r9 */ + *(--stk) = 0xdeadbeef; /* r8 */ + *(--stk) = 0xdeadbeef; /* r7 */ + *(--stk) = 0xdeadbeef; /* r6 */ + *(--stk) = 0xdeadbeef; /* r5 */ + *(--stk) = 0xdeadbeef; /* r4 */ + *(--stk) = 0xdeadbeef; /* r3 */ + *(--stk) = 0xdeadbeef; /* r2 */ + *(--stk) = 0xdeadbeef; /* r1 */ + *(--stk) = (rt_uint32_t)parameter; /* r0 : argument */ + /* cpsr */ + if ((rt_uint32_t)tentry & 0x01) + *(--stk) = SVCMODE | 0x20; /* thumb mode */ + else + *(--stk) = SVCMODE; /* arm mode */ + +#ifdef RT_USING_LWP + *(--stk) = 0; /* user lr */ + *(--stk) = 0; /* user sp*/ +#endif +#ifdef RT_USING_FPU + *(--stk) = 0; /* not use fpu*/ +#endif + + /* return task's current stack address */ + return (rt_uint8_t *)stk; +} + +/*@}*/ diff --git a/bsp/ft2004/cpu/start_gcc.S b/bsp/ft2004/cpu/start_gcc.S new file mode 100644 index 0000000000..57240226d5 --- /dev/null +++ b/bsp/ft2004/cpu/start_gcc.S @@ -0,0 +1,586 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-05 Bernard the first version + * 2018-11-22 Jesven in the interrupt context, use rt_scheduler_do_irq_switch checks + * and switches to a new thread + */ + +#include "rtconfig.h" +.equ Mode_USR, 0x10 +.equ Mode_FIQ, 0x11 +.equ Mode_IRQ, 0x12 +.equ Mode_SVC, 0x13 +.equ Mode_ABT, 0x17 +.equ Mode_UND, 0x1B +.equ Mode_SYS, 0x1F + +.equ I_Bit, 0x80 @ when I bit is set, IRQ is disabled +.equ F_Bit, 0x40 @ when F bit is set, FIQ is disabled + +#ifdef RT_USING_FPU +.equ UND_Stack_Size, 0x00000400 +#else +.equ UND_Stack_Size, 0x00000000 +#endif +.equ SVC_Stack_Size, 0x00000400 +.equ ABT_Stack_Size, 0x00000000 +.equ RT_FIQ_STACK_PGSZ, 0x00000000 +.equ RT_IRQ_STACK_PGSZ, 0x00000800 +.equ USR_Stack_Size, 0x00000400 + +#define ISR_Stack_Size (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \ + RT_FIQ_STACK_PGSZ + RT_IRQ_STACK_PGSZ) + + +.macro push_svc_reg + sub sp, sp, #17 * 4 @/* Sizeof(struct rt_hw_exp_stack) */ + stmia sp, {r0 - r12} @/* Calling r0-r12 */ + mov r0, sp + mrs r6, spsr @/* Save CPSR */ + str lr, [r0, #15*4] @/* Push PC */ + str r6, [r0, #16*4] @/* Push CPSR */ + cps #Mode_SVC + str sp, [r0, #13*4] @/* Save calling SP */ + str lr, [r0, #14*4] @/* Save calling PC */ +.endm + + +.section .data.share.isr +/* stack */ +.globl stack_start +.globl stack_top + +.align 3 +stack_start: +.rept ISR_Stack_Size +.byte 0 +.endr +stack_top: + +.text +/* reset entry */ +.globl _reset +_reset: +#ifdef ARCH_ARMV8 + /* Check for HYP mode */ + mrs r0, cpsr_all + and r0, r0, #0x1F + mov r8, #0x1A + cmp r0, r8 + beq overHyped + b continue + +overHyped: /* Get out of HYP mode */ + adr r1, continue + msr ELR_hyp, r1 + mrs r1, cpsr_all + and r1, r1, #0x1f ;@ CPSR_MODE_MASK + orr r1, r1, #0x13 ;@ CPSR_MODE_SUPERVISOR + msr SPSR_hyp, r1 + eret + +continue: +#endif + /* set the cpu to SVC32 mode and disable interrupt */ + cps #Mode_SVC + +#ifdef RT_USING_FPU + mov r4, #0xfffffff + mcr p15, 0, r4, c1, c0, 2 +#endif + + /* disable the data alignment check */ + mrc p15, 0, r1, c1, c0, 0 + bic r1, #(1<<0) /* Disable MMU */ + bic r1, #(1<<1) /* Disable Alignment fault checking */ + bic r1, #(1<<2) /* Disable data cache */ + bic r1, #(1<<11) /* Disable program flow prediction */ + bic r1, #(1<<12) /* Disable instruction cache */ + mcr p15, 0, r1, c1, c0, 0 + + @ get cpu id, and subtract the offset from the stacks base address + mrc p15, 0, r5, c0, c0, 5 @ read multiprocessor affinity register + and r5, r5, #3 @ mask off, leaving CPU ID field + + cmp r5, #0 @ cpu id == 0 + beq normal_setup + + @ cpu id > 0, stop or wait +#ifdef RT_SMP_AUTO_BOOT + ldr r0, =secondary_cpu_entry + mov r1, #0 + str r1, [r0] /* clean secondary_cpu_entry */ +#endif /* RT_SMP_AUTO_BOOT */ + +secondary_loop: + @ cpu core 1 goes into sleep until core 0 wakeup it + wfe +#ifdef RT_SMP_AUTO_BOOT + ldr r1, =secondary_cpu_entry + ldr r0, [r1] + cmp r0, #0 + blxne r0 /* if(secondary_cpu_entry) secondary_cpu_entry(); */ +#endif /* RT_SMP_AUTO_BOOT */ + b secondary_loop + +normal_setup: + /* setup stack */ + bl stack_setup + + /* clear .bss */ + mov r0,#0 /* get a zero */ + ldr r1,=__bss_start /* bss start */ + ldr r2,=__bss_end /* bss end */ + +bss_loop: + cmp r1,r2 /* check if data to clear */ + strlo r0,[r1],#4 /* clear 4 bytes */ + blo bss_loop /* loop until done */ + +#ifdef RT_USING_SMP + mrc p15, 0, r1, c1, c0, 1 + mov r0, #(1<<6) + orr r1, r0 + mcr p15, 0, r1, c1, c0, 1 //enable smp +#endif + + + + /* enable branch prediction */ + mrc p15, 0, r0, c1, c0, 0 + orr r0, r0, #(1<<11) + mcr p15, 0, r1, c1, c0, 0 + + push_svc_reg + bl test_check_debug + + + + /* initialize the mmu table and enable mmu */ + ldr r0, =platform_mem_desc + ldr r1, =platform_mem_desc_size + ldr r1, [r1] + bl rt_hw_init_mmu_table + bl rt_hw_mmu_init + + /* call C++ constructors of global objects */ + ldr r0, =__ctors_start__ + ldr r1, =__ctors_end__ + +ctor_loop: + cmp r0, r1 + beq ctor_end + ldr r2, [r0], #4 + stmfd sp!, {r0-r1} + mov lr, pc + bx r2 + ldmfd sp!, {r0-r1} + b ctor_loop +ctor_end: + + /* start RT-Thread Kernel */ + ldr pc, _rtthread_startup +_rtthread_startup: + .word rtthread_startup + +stack_setup: + ldr r0, =stack_top + + @ Set the startup stack for svc + mov sp, r0 + + @ Enter Undefined Instruction Mode and set its Stack Pointer + msr cpsr_c, #Mode_UND|I_Bit|F_Bit + mov sp, r0 + sub r0, r0, #UND_Stack_Size + + @ Enter Abort Mode and set its Stack Pointer + msr cpsr_c, #Mode_ABT|I_Bit|F_Bit + mov sp, r0 + sub r0, r0, #ABT_Stack_Size + + @ Enter FIQ Mode and set its Stack Pointer + msr cpsr_c, #Mode_FIQ|I_Bit|F_Bit + mov sp, r0 + sub r0, r0, #RT_FIQ_STACK_PGSZ + + @ Enter IRQ Mode and set its Stack Pointer + msr cpsr_c, #Mode_IRQ|I_Bit|F_Bit + mov sp, r0 + sub r0, r0, #RT_IRQ_STACK_PGSZ + + /* come back to SVC mode */ + msr cpsr_c, #Mode_SVC|I_Bit|F_Bit + bx lr + +/* exception handlers: undef, swi, padt, dabt, resv, irq, fiq */ +.section .text.isr, "ax" + .align 5 +.globl vector_fiq +vector_fiq: + stmfd sp!,{r0-r7,lr} + bl rt_hw_trap_fiq + ldmfd sp!,{r0-r7,lr} + subs pc, lr, #4 + +.globl rt_interrupt_enter +.globl rt_interrupt_leave +.globl rt_thread_switch_interrupt_flag +.globl rt_interrupt_from_thread +.globl rt_interrupt_to_thread + +.globl rt_current_thread +.globl vmm_thread +.globl vmm_virq_check + + .align 5 +.globl vector_irq +vector_irq: +#ifdef RT_USING_SMP + clrex + + stmfd sp!, {r0, r1} + cps #Mode_SVC + mov r0, sp /* svc_sp */ + mov r1, lr /* svc_lr */ + + cps #Mode_IRQ + sub lr, #4 + stmfd r0!, {r1, lr} /* svc_lr, svc_pc */ + stmfd r0!, {r2 - r12} + ldmfd sp!, {r1, r2} /* original r0, r1 */ + stmfd r0!, {r1 - r2} + mrs r1, spsr /* original mode */ + stmfd r0!, {r1} + +#ifdef RT_USING_LWP + stmfd r0, {r13, r14}^ /* usr_sp, usr_lr */ + sub r0, #8 +#endif +#ifdef RT_USING_FPU + /* fpu context */ + vmrs r6, fpexc + tst r6, #(1<<30) + beq 1f + vstmdb r0!, {d0-d15} + vstmdb r0!, {d16-d31} + vmrs r5, fpscr + stmfd r0!, {r5} +1: + stmfd r0!, {r6} +#endif + + /* now irq stack is clean */ + /* r0 is task svc_sp */ + /* backup r0 -> r8 */ + mov r8, r0 + + bl rt_interrupt_enter + bl rt_hw_trap_irq + bl rt_interrupt_leave + + cps #Mode_SVC + mov sp, r8 + mov r0, r8 + bl rt_scheduler_do_irq_switch + + b rt_hw_context_switch_exit + +#else + stmfd sp!, {r0-r12,lr} + + bl rt_interrupt_enter + bl rt_hw_trap_irq + bl rt_interrupt_leave + + @ if rt_thread_switch_interrupt_flag set, jump to + @ rt_hw_context_switch_interrupt_do and don't return + ldr r0, =rt_thread_switch_interrupt_flag + ldr r1, [r0] + cmp r1, #1 + beq rt_hw_context_switch_interrupt_do + + ldmfd sp!, {r0-r12,lr} + subs pc, lr, #4 + +rt_hw_context_switch_interrupt_do: + mov r1, #0 @ clear flag + str r1, [r0] + + mov r1, sp @ r1 point to {r0-r3} in stack + add sp, sp, #4*4 + ldmfd sp!, {r4-r12,lr}@ reload saved registers + mrs r0, spsr @ get cpsr of interrupt thread + sub r2, lr, #4 @ save old task's pc to r2 + + @ Switch to SVC mode with no interrupt. If the usr mode guest is + @ interrupted, this will just switch to the stack of kernel space. + @ save the registers in kernel space won't trigger data abort. + msr cpsr_c, #I_Bit|F_Bit|Mode_SVC + + stmfd sp!, {r2} @ push old task's pc + stmfd sp!, {r4-r12,lr}@ push old task's lr,r12-r4 + ldmfd r1, {r1-r4} @ restore r0-r3 of the interrupt thread + stmfd sp!, {r1-r4} @ push old task's r0-r3 + stmfd sp!, {r0} @ push old task's cpsr + +#ifdef RT_USING_LWP + stmfd sp, {r13, r14}^ @push usr_sp, usr_lr + sub sp, #8 +#endif +#ifdef RT_USING_FPU + /* fpu context */ + vmrs r6, fpexc + tst r6, #(1<<30) + beq 1f + vstmdb sp!, {d0-d15} + vstmdb sp!, {d16-d31} + vmrs r5, fpscr + stmfd sp!, {r5} +1: + stmfd sp!, {r6} +#endif + + ldr r4, =rt_interrupt_from_thread + ldr r5, [r4] + str sp, [r5] @ store sp in preempted tasks's TCB + + ldr r6, =rt_interrupt_to_thread + ldr r6, [r6] + ldr sp, [r6] @ get new task's stack pointer + +#ifdef RT_USING_FPU +/* fpu context */ + ldmfd sp!, {r6} + vmsr fpexc, r6 + tst r6, #(1<<30) + beq 1f + ldmfd sp!, {r5} + vmsr fpscr, r5 + vldmia sp!, {d16-d31} + vldmia sp!, {d0-d15} +1: +#endif + +#ifdef RT_USING_LWP + ldmfd sp, {r13, r14}^ @pop usr_sp, usr_lr + add sp, #8 +#endif + + ldmfd sp!, {r4} @ pop new task's cpsr to spsr + msr spsr_cxsf, r4 + + ldmfd sp!, {r0-r12,lr,pc}^ @ pop new task's r0-r12,lr & pc, copy spsr to cpsr + +#endif + + + .align 5 +.weak vector_swi +vector_swi: + push_svc_reg + bl rt_hw_trap_swi + b . + + .align 5 + .globl vector_undef +vector_undef: + push_svc_reg + cps #Mode_UND + bl rt_hw_trap_undef +#ifdef RT_USING_FPU + ldr lr, [sp, #15*4] + ldmia sp, {r0 - r12} + add sp, sp, #17 * 4 + movs pc, lr +#endif + b . + + .align 5 + .globl vector_pabt +vector_pabt: + push_svc_reg + bl rt_hw_trap_pabt + b . + + .align 5 + .globl vector_dabt +vector_dabt: + push_svc_reg + bl rt_hw_trap_dabt + b . + + .align 5 + .globl vector_resv +vector_resv: + push_svc_reg + bl rt_hw_trap_resv + b . + +#ifdef RT_USING_SMP +.global set_secondary_cpu_boot_address +.global secondary_cpu_start +secondary_cpu_start: + +#ifdef RT_USING_FPU + mov r4, #0xfffffff + mcr p15, 0, r4, c1, c0, 2 +#endif + + mrc p15, 0, r1, c1, c0, 1 + mov r0, #(1<<6) + orr r1, r0 + mcr p15, 0, r1, c1, c0, 1 //enable smp + + mrc p15, 0, r0, c1, c0, 0 + bic r0, #(1<<13) + mcr p15, 0, r0, c1, c0, 0 + +#ifdef RT_USING_FPU + cps #Mode_UND + ldr sp, =und_stack_2_limit +#endif + + cps #Mode_IRQ + ldr sp, =irq_stack_2_limit + + cps #Mode_FIQ + ldr sp, =irq_stack_2_limit + + cps #Mode_SVC + ldr sp, =svc_stack_2_limit + + /* initialize the mmu table and enable mmu */ + bl rt_hw_mmu_init + + b secondary_cpu_c_start +#endif + +#ifdef RT_USING_SMP + +.global secondary2_cpu_start +secondary2_cpu_start: + +#ifdef RT_USING_FPU + mov r4, #0xfffffff + mcr p15, 0, r4, c1, c0, 2 +#endif + + mrc p15, 0, r1, c1, c0, 1 + mov r0, #(1<<6) + orr r1, r0 + mcr p15, 0, r1, c1, c0, 1 //enable smp + + mrc p15, 0, r0, c1, c0, 0 + bic r0, #(1<<13) + mcr p15, 0, r0, c1, c0, 0 + +#ifdef RT_USING_FPU + cps #Mode_UND + ldr sp, =und_stack_3_limit +#endif + + cps #Mode_IRQ + ldr sp, =irq_stack_3_limit + + cps #Mode_FIQ + ldr sp, =irq_stack_3_limit + + cps #Mode_SVC + ldr sp, =svc_stack_3_limit + + /* initialize the mmu table and enable mmu */ + bl rt_hw_mmu_init + + b secondary_cpu_c_start +#endif + + +#ifdef RT_USING_SMP +.global secondary3_cpu_start +secondary3_cpu_start: + +#ifdef RT_USING_FPU + mov r4, #0xfffffff + mcr p15, 0, r4, c1, c0, 2 +#endif + + mrc p15, 0, r1, c1, c0, 1 + mov r0, #(1<<6) + orr r1, r0 + mcr p15, 0, r1, c1, c0, 1 //enable smp + + mrc p15, 0, r0, c1, c0, 0 + bic r0, #(1<<13) + mcr p15, 0, r0, c1, c0, 0 + +#ifdef RT_USING_FPU + cps #Mode_UND + ldr sp, =und_stack_4_limit +#endif + + cps #Mode_IRQ + ldr sp, =irq_stack_4_limit + + cps #Mode_FIQ + ldr sp, =irq_stack_4_limit + + cps #Mode_SVC + ldr sp, =svc_stack_4_limit + + /* initialize the mmu table and enable mmu */ + bl rt_hw_mmu_init + + b secondary_cpu_c_start +#endif + + +.bss +.align 2 //align to 2~2=4 +svc_stack_2: + .space (1 << 10) +svc_stack_2_limit: + +irq_stack_2: + .space (1 << 10) +irq_stack_2_limit: + +#ifdef RT_USING_FPU +und_stack_2: + .space (1 << 10) +und_stack_2_limit: +#endif + +svc_stack_3: + .space (1 << 10) +svc_stack_3_limit: + +irq_stack_3: + .space (1 << 10) +irq_stack_3_limit: + +#ifdef RT_USING_FPU +und_stack_3: + .space (1 << 10) +und_stack_3_limit: +#endif + + +svc_stack_4: + .space (1 << 10) +svc_stack_4_limit: + +irq_stack_4: + .space (1 << 10) +irq_stack_4_limit: + +#ifdef RT_USING_FPU +und_stack_4: + .space (1 << 10) +und_stack_4_limit: +#endif \ No newline at end of file diff --git a/bsp/ft2004/cpu/trap.c b/bsp/ft2004/cpu/trap.c new file mode 100644 index 0000000000..7b31c7f6c3 --- /dev/null +++ b/bsp/ft2004/cpu/trap.c @@ -0,0 +1,213 @@ +/* + * Copyright (c) 2006-2021, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-20 Bernard first version + */ + +#include +#include +#include + +#include "armv7.h" +#include "interrupt.h" + +#ifdef RT_USING_FINSH +extern long list_thread(void); +#endif + +/** + * this function will show registers of CPU + * + * @param regs the registers point + */ +void rt_hw_show_register(struct rt_hw_exp_stack *regs) +{ + rt_kprintf("Execption:\n"); + rt_kprintf("r00:0x%08x r01:0x%08x r02:0x%08x r03:0x%08x\n", regs->r0, regs->r1, regs->r2, regs->r3); + rt_kprintf("r04:0x%08x r05:0x%08x r06:0x%08x r07:0x%08x\n", regs->r4, regs->r5, regs->r6, regs->r7); + rt_kprintf("r08:0x%08x r09:0x%08x r10:0x%08x\n", regs->r8, regs->r9, regs->r10); + rt_kprintf("fp :0x%08x ip :0x%08x\n", regs->fp, regs->ip); + rt_kprintf("sp :0x%08x lr :0x%08x pc :0x%08x\n", regs->sp, regs->lr, regs->pc); + rt_kprintf("cpsr:0x%08x\n", regs->cpsr); +} + +/** + * When comes across an instruction which it cannot handle, + * it takes the undefined instruction trap. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_undef(struct rt_hw_exp_stack *regs) +{ +#ifdef RT_USING_FPU + { + uint32_t val; + uint32_t addr; + + if (regs->cpsr & (1 << 5)) + { + /* thumb mode */ + addr = regs->pc - 2; + } + else + { + addr = regs->pc - 4; + } + asm volatile("vmrs %0, fpexc" + : "=r"(val)::"memory"); + + if (!(val & 0x40000000)) + { + /* float ins */ + val = (1U << 30); + + asm volatile("vmsr fpexc, %0" ::"r"(val) + : "memory"); + regs->pc = addr; + return; + } + } +#endif + rt_kprintf("undefined instruction:\n"); + rt_hw_show_register(regs); +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * The software interrupt instruction (SWI) is used for entering + * Supervisor mode, usually to request a particular supervisor + * function. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_swi(struct rt_hw_exp_stack *regs) +{ + rt_kprintf("software interrupt:\n"); + rt_hw_show_register(regs); +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * An abort indicates that the current memory access cannot be completed, + * which occurs during an instruction prefetch. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_pabt(struct rt_hw_exp_stack *regs) +{ + rt_kprintf("prefetch abort:\n"); + rt_hw_show_register(regs); +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +void test_check_debug(struct rt_hw_exp_stack *regs) +{ + Ft_printf("error is here \r\n"); + rt_hw_cpu_shutdown(); +} + +/** + * An abort indicates that the current memory access cannot be completed, + * which occurs during a data access. + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_dabt(struct rt_hw_exp_stack *regs) +{ + rt_kprintf("data abort:"); + rt_hw_show_register(regs); +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +/** + * Normally, system will never reach here + * + * @param regs system registers + * + * @note never invoke this function in application + */ +void rt_hw_trap_resv(struct rt_hw_exp_stack *regs) +{ + rt_kprintf("reserved trap:\n"); + rt_hw_show_register(regs); +#ifdef RT_USING_FINSH + list_thread(); +#endif + rt_hw_cpu_shutdown(); +} + +void rt_hw_trap_irq(void) +{ + void *param; + int ir; + rt_isr_handler_t isr_func; + extern struct rt_irq_desc isr_table[]; + + ir = rt_hw_interrupt_get_irq(); + + if (ir == 1023) + { + /* Spurious interrupt */ + return; + } + + /* get interrupt service routine */ + isr_func = isr_table[ir].handler; +#ifdef RT_USING_INTERRUPT_INFO + isr_table[ir].counter++; +#endif + if (isr_func) + { + /* Interrupt for myself. */ + param = isr_table[ir].param; + /* turn to interrupt service routine */ + isr_func(ir, param); + } + + /* end of interrupt */ + rt_hw_interrupt_ack(ir); +} + +void rt_hw_trap_fiq(void) +{ + void *param; + int ir; + rt_isr_handler_t isr_func; + extern struct rt_irq_desc isr_table[]; + + ir = rt_hw_interrupt_get_irq(); + + /* get interrupt service routine */ + isr_func = isr_table[ir].handler; + param = isr_table[ir].param; + + /* turn to interrupt service routine */ + isr_func(ir, param); + + /* end of interrupt */ + rt_hw_interrupt_ack(ir); +} diff --git a/bsp/ft2004/cpu/vector_gcc.S b/bsp/ft2004/cpu/vector_gcc.S new file mode 100644 index 0000000000..60d3c6cf1d --- /dev/null +++ b/bsp/ft2004/cpu/vector_gcc.S @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2006-2018, RT-Thread Development Team + * + * SPDX-License-Identifier: Apache-2.0 + * + * Change Logs: + * Date Author Notes + * 2013-07-05 Bernard the first version + */ + +.section .vectors, "ax" +.code 32 + +.globl system_vectors +system_vectors: + ldr pc, _vector_reset + ldr pc, _vector_undef + ldr pc, _vector_swi + ldr pc, _vector_pabt + ldr pc, _vector_dabt + ldr pc, _vector_resv + ldr pc, _vector_irq + ldr pc, _vector_fiq + +.globl _reset +.globl vector_undef +.globl vector_swi +.globl vector_pabt +.globl vector_dabt +.globl vector_resv +.globl vector_irq +.globl vector_fiq + +_vector_reset: + .word _reset +_vector_undef: + .word vector_undef +_vector_swi: + .word vector_swi +_vector_pabt: + .word vector_pabt +_vector_dabt: + .word vector_dabt +_vector_resv: + .word vector_resv +_vector_irq: + .word vector_irq +_vector_fiq: + .word vector_fiq + +.balignl 16,0xdeadbeef diff --git a/bsp/ft2004/drivers/SConscript b/bsp/ft2004/drivers/SConscript new file mode 100644 index 0000000000..17cbe606b7 --- /dev/null +++ b/bsp/ft2004/drivers/SConscript @@ -0,0 +1,12 @@ + +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..e9e284f90d --- /dev/null +++ b/bsp/ft2004/drivers/board.c @@ -0,0 +1,241 @@ +/* + * 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 +#include "ft_printf.h" +#include "ft_assert.h" +#include "ft_cpu.h" +#include "ft_psci.h" +#include "ft_parameters.h" +#include "board.h" +#include "ft_generic_timer.h" +#include "ft_aarch32_asm.h" +#include + +#include "interrupt.h" +#include +#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, // + +extern void secondary_cpu_start(void); +extern void secondary2_cpu_start(void); +extern void secondary3_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_kprintf("cpu_id %x \r\n", FCpu_IdGet()); + 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); + + Ft_printf("secondary_cpu_c_start is ok \r\n"); + + rt_system_scheduler_start(); +} + +void rt_hw_secondary_cpu_idle_exec(void) +{ + asm volatile("wfe" :: + : "memory", "cc"); +} + +#endif + +/*@}*/ diff --git a/bsp/ft2004/drivers/board.h b/bsp/ft2004/drivers/board.h new file mode 100644 index 0000000000..e21f8b53bf --- /dev/null +++ b/bsp/ft2004/drivers/board.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2006-2018, 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/ft2004.c b/bsp/ft2004/drivers/ft2004.c new file mode 100644 index 0000000000..7ea513ce41 --- /dev/null +++ b/bsp/ft2004/drivers/ft2004.c @@ -0,0 +1,107 @@ +/* + * 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 +#include "ft2004.h" +#include "gicv3.h" + +// int rt_hw_cpu_id(void) +// { +// int cpu_id; +// __asm__ volatile( +// "mrc p15, 0, %0, c0, c0, 5" +// : "=r"(cpu_id)); +// switch ((cpu_id & 0xfff)) +// { +// case 1: +// return 1; +// case 0x100: +// return 2; +// case 0x101: +// return 3; +// default: +// return (cpu_id & 0xf); +// } +// } + +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 \ No newline at end of file diff --git a/bsp/ft2004/drivers/ft2004.h b/bsp/ft2004/drivers/ft2004.h new file mode 100644 index 0000000000..058c54fb8f --- /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 +#include + +#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..1f9e62c2c3 --- /dev/null +++ b/bsp/ft2004/drivers/ft2004_cpu.S @@ -0,0 +1,40 @@ + +#include "rtconfig.h" + +.globl rt_hw_cpu_id +rt_hw_cpu_id: + push {fp} + add fp, sp, #0 + sub sp, sp, #12 + mrc p15, 0, r3, c0, c0, 5 + str r3, [fp, #-8] + ldr r3, [fp, #-8] + ubfx r3, r3, #0, #12 + movw r2, #257 + cmp r3, r2 + beq core3 + movw r2, #257 + cmp r3, r2 + bgt default + cmp r3, #1 + beq core1 + cmp r3, #256 + beq core2 + b default +core1: + mov r3, #1 + b return +core2: + mov r3, #2 + b return +core3: + mov r3, #3 + b return +default: + ldr r3, [fp, #-8] + and r3, r3, #15 +return: + mov r0, r3 + add sp, fp, #0 + pop {fp} + bx lr diff --git a/bsp/ft2004/drivers/secondary_cpu.cold b/bsp/ft2004/drivers/secondary_cpu.cold new file mode 100644 index 0000000000..a644b617e6 --- /dev/null +++ b/bsp/ft2004/drivers/secondary_cpu.cold @@ -0,0 +1,43 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-19 08:52:58 + * @LastEditTime: 2021-04-19 08:52:58 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include + +#include "board.h" +#include "gic.h" diff --git a/bsp/ft2004/drivers/serial.h b/bsp/ft2004/drivers/serial.h new file mode 100644 index 0000000000..6abc1394fc --- /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 + +int rt_hw_uart_init(void); + +#endif + diff --git a/bsp/ft2004/figures/onchipPeripheral.png b/bsp/ft2004/figures/onchipPeripheral.png new file mode 100644 index 0000000000000000000000000000000000000000..3b5833bccae8cbdbf191f50921bac2c94d15823b GIT binary patch literal 22293 zcmeFZdtB0Y-#_kJtyL>`)jFAn+OF2sL0vh|3fF3@ZI*7C2UK8Tx7Q}#d;_?$_xfxL@b((| z=%M{?bdl!-@L?(Pqaz==xiyehEgWA4d|q+-QwZA4ZB2*kzt;p=_fELE#n*oJ@kht< zV7d`S9R^F-IH&es@k0aRuD(t5$@=%+`t!ZJD>to3 zUAvyM@KO5l?@EHcT=LNywSPMD^^V?}vt5HZ+HYcxvQK}tRf?>%=#8QC6oV0pVsOPE zwG0`_xVRC?Fxl-;6qRe?%{295bOSTti zxYgPw3NTL^=7Xo%D}Z)S|4e^>>c|apJIdqq=!MTO3mDhH=gQ#c(O+&$aM!isUK@RU zinFqp9y!IWm#T@+rwmulWtlLY_zz)qVmk{?m4yGyQW%#D%#%qfSA*>j2bS{I--EvU zDef&q2pQ}BNa!sYxd7j>ypoxN(cfIk%e5V~ou!RvPp6HAbArK^K1EA{+EYpp`$TO0 z^Kzhxv5AWHb0vzLp6OeYK?`-p+-=Ww^FDJpJ~jbFK%hMcBNvV^W)sFc^2X^4-@)dC zRJuWu_nhN))~N2`Nact=szCoeJe(3UKZ4n0&X8nXAI}yEyY%wA>7NOix&z+G=tFAu zhvcUbdP!vtN{c(0Lo88uakatIT^JuPNXOL?W3$?zf(V*%SE<=un8Xq0IyE+dfFooq zE>(!)={duPcm6)KwR*{2oWOS>^g&%C)uwvUvj3JI)}FJ)uj+{0j1Gh?L+5UjoG*R| z7urVil-~XGh%{Zic`_5sv(C)WXwjxk5$;S%$ehpI6*UsIhvr?He|W1DLN-JH6x^h}fE``+mQ=;g;SYP0I~KBRiBQG;LtN7LW#WvHgsvU?qE#}%?mE;a zb7~WHAUBJM43Z9k^r~Kxv@@B#rmz*7MOGqkK|6`Bc&;v<<{6hLR-(!vd6>_i1fYn> z4Phgdbs*%nzALrKdJ|_1c1fmop7G8#Z4mi*WdWTNn$w$nnmZfx#q2LCc`%t11k*a# zH=FdP1T^fXT4V3kd}8gMKQP8=;+3I`!AUp8S3LaDTe{<=ABd_-h6p0L;-=7KPBB5y z;SbTKw^uyLE;hRX)xyqieTa+?foq$Ro2uKuyT2Gllv^6j3L?Rj~WPXP;jE z;+|;7z0Dnz@4bf?0KfN{On(y_y_&!4UYUfr6!AgO>Lr56dqzQzyj~|e0oK#54N#cU zPe5^Ky@7i7u|B`b-#FY znr2&@?XcNOAJtJbFWlR@3uXCu05ZaaC^}N~CBeV-EL^vR*SzcSuJxnG%BL4fFChVU zmDD-(R&GYHNclxPB4u!daLl>>EH@%Ifx$oxGUJ#zC|o>(A0e8@;aROr`~Cohlo&3jJv$X2N32Ot=X|KTXOXGT9I+HwC)8_t^-nOb@S~^% zOd2ldM5ViIqsxRuxpKu6_uciGru%zdOWJ=IMpFB~7~{5ER*zRM0?iHNmN^m63-a(Q zhQN6BD+?NIFXjl?cTGnI8uaClBi(^U+V_fsriy=nu4w;z);awle$%5-@Yv$dzY;Rn z{EF^#um6tWZ_h1p-Ffnd_Qf82wWBg&%tj`TtCl-ueCH_mm%!aby0vw*ok*En?vP{+ z+YUX^ca1*A*91C;gVp2HDdzEhY$n9EolA*T<>VT;p5f>l67C)K89UJKx5$aF&l%JX zIm2iF)gN*(ek8-PJoka`uR!KeM^OeV$P>e%?^s0<^?xsW(uy=iPqY2;_S2-p2Y4tM zc?jFz>Jxa6TuvTQxQjAR~&fL5)-}Z zy`?n79`?V3$|t01d`(jPhtebJRMxRz=glddrtOaTur z);d3LKQko#zLVo?K#UJ_v)@3ty>oNucn@p(LW5qFs4KKHveyWrj450Eb)ERloj2I4 zUL!B5uj29|ov#xO!Gslx3UX=oAKSDPGb+!;y-Ij6u@ z)rj}7iQVL{7FBhkq)mEm$8@Mg#x~N}JraUGMSf#IKU$E)XHekxEx*o4o3Hb;!VT{X zuTpL}7LvX3!?tUJ6`R(O_zO>=EtlK3sol5rWqdXLJ=_j^b~UU}IrPDw!G=f0IR(%5 z*M2`V!xYpt4mB7$w+J@(EcuBRsg4)afjYC3sd%vat!E9`%tZLh>5;iZJ*qR+opg|G zTBqc2Pa0{ULl$#>3Nk!FIl!tAWu#I1Xwm3qb)X|E(84mAKxrCBUh0};D@E5mNH@f? z%5TmRn^oBqSaVl&8>5ai5g}-lBs#p@>fYz?03;y7$cRH24H2{jXC~Pc7sC= zZ%s%%_tCOHDV!Z(0>7=M{p(}r43X;-hu`>9;kUn}a;+*zXP77H;pZswedRMp&{)V1 zKF#BnP7ir~N$?7H!+x>`^~-fE?;7Y#W=2~rE@6kVje-Ivpn1t1-SadNI-_l*+8Ohw`8 zlig4bc@5fNk@Y;I>+3zKcqVXQeH!x=4QPev$H%ls5(7bMrjO9MR%#L3IF8+>5C=@sfpD- z_V&j;n&H~F?-Z_$e06Vz{;2rCS9@0^_kv9NM}OZS@w5`=LsB;@_0tJ(Gg_6$49dPY zq$#e25+v-sXKxngB5y^TE^k%eZ!+gm5T0_M&5TF4IYG_%5>u;(zj1RnjD4_Ky#~e5 z2CkTr0K`*2zDPXfP4F{AhvX)9&+sx-hCiwYp{>gwr&AlCxp&0h%Xo*aZJad&nW`oz z6=~Wl;(K;Ro}qeC!o&@<61aK#61zu!*8#2YMV+kWFVN7rsE}MZtQG{b{OXLOl`mt$ zj=lO|bwm7;ix2q01XS*uAHzT*#S)>ju>w5-R*SqCa#LXXmnehn7#jPj+cBf4pcgJA zVk(n6IdL{zbIdB3Osu+&n(aNV^Vdoj4Dx|&Nkgwglrx!m68gPj%nKu+}#XIIia(DISU2^dM~X1npkn$0qydwkm98 z2u$~so4?9hscKb$p3bklqPNoQtzL%f$|hbbAgBj4al~BvXp`31vb%0?JOvFG9FR1p zr3h|OS)eQ*OHg6Xs?=OOZL=WxFT^SZ+Ms1&OVBk+xwxw7P-S0DpVntj&-OL;xA14P zx_&j3N{RD=f6C33kMsXJ?Cf~7MH@66{=>J-8Ps&jqRyW>yrKXbpQd;-I`1!2>layX z>(uz>&l3lo!6t`#`)J~?ln7h<8zVmc?3^K&Z-W90>*nSy+e01sEhIXfnl?YV;Wq-^ z9ng(zc9H00zZ7iO3rPz-$YA(F7qV~WUKrImgAAq;a4@w8VDP>u{+*tkXxlC70{8o( ziqIYntQXBDxVcd>OU0Snfbc1A|N3WfAI#nOXr%NRQG0!uAgrx}`rxY`n43E%ey?{Z|6jm0i98k7T-7HypJ<@8_>2@N3cgDp}}$!l)yslpkV?@~ZT z$K3g@&3kG5+tQ8)lsct%rQc^;6Li*jUwr%%sXDMp`_WebW0>G5Z@~nnZnBVQ#l4o% zRhwx-YyQC-C;gm5&Ux(#HvE773Z~Dj0 zRQpin);M0OFVsW^?)nx)YE7z*#s9hGh{8S{;v8%=ue?}y#1txd-EAxa>`;RJT$UA` z|7LJyRD0=98NQT?3Dep^VVA^8IbVR_JW&EueG|etdi=N~ytHCVI$h|3xY=xAokwWm zT4t*6$eyYCKnlnIOLVtKA6#=se}Hk|Gg}YGyu|I>!gYZ0M+8KjJ6>W_!b;ufmc3Vl z5Z`P95Xseg_GnaR&PdXYmhIm}U|bYfO+u^pU8q{0f0r;IYb?!tt3Vrqr*izaqr?iO z4h_oH-LAJRb#ohXB_b+st?XK>`FYc)q9t9UgwN!0)B!^h)zs4bV^yODAq*KkIzMPg zdX}e;5U~5Rb?T!-h+9a_thM1o@)LFgiN0i%{z!qgRB4VW5H`pV0!3wgHu7M?`6CzN0)uZWHodMe*m#EU zi@&Pou|U@(053pBTYkiAlNGJ=k2dm78($RqM0Z<$xf%mvW7QS8Yf!>6B!(Ubz3q7X zZ6FrJ&zt^bEB_c0Q@LAQD7cQtm+*Hm&fAO;*kTcHTuoc3sjybQ7{q|h;q$#p=;x}| zar=u(IJ#1fNEyE?Ci?GHhqZx{Et5y3-<-|hGiSmQv<2Gw z=DV0d>TsVuQnaUM6a#ru2}(Epj?NCThzuzD9ywBDkyk}_A}_|K zPGQ^ktd zl#Z10wrmlqGmkC}|5>6X)F_?;;^Hx^QZ5M9CT~%$Mdf9BTfE4NErvrq0$$2i-NzqJMdF% z3EYDDb(C}nJ%Jt-3HN)Eq<l+N`zoxxV^tlH00+ot+ zs4cLHLzr9IIt}WBan3kp7#sR;X7P)6YNv;MXPU$oX#4a&0C zQyc&zqjZ{uNmy;8%(%@&weq}ew46B0&ViJ7Nk~lT2|P0g5~cfWHVjZ=j#st^Cx)5w zZkCoDq>jj{Bk+n4ddwSv5eFBmdy?H2O!4r>Kyy14G4}v2LEZaN;B;2YduT5Jc z$j8d7a3|Y#obm%=eEi*L!+X{n+#@0%9KA@_*-Ac!rgMuyWHh5g?jh;2h9HPhd`2ux zd#~xT`Ya2aHet^c9v`kwI`A@^6{;FGebQB%G0Q$!f|UNGijdUIK&S{AO|ff>dj4%C z=AM+kv>4;3eL9CtLH`&*CzL>v6utOMfs*O+Jgqoj( zReM(H$}3l0n_)G|=RDN~Uez#Boc-vyefXXQ5*%ytnP6Hg26P+iq!mK-4Goz8F*6t6 zc1^u8U1VvELf!Pw^dow-X0|Ldg8mJJJ;~xj1B1MPz8_z7fV=?u5z^apM65F2M_fZpSD`-}kltu97 zsF6qc^4Yx-I((oUY8f@OTtG15P`y&xs64VENDKt3`S_yQaC7MHa$#!0f@hBeeR2X< zK^KN(v#&`e z9nii+GN^(;nEw{_z7q6KruLFa#fo4|Yz{zwhE??q-JQIqz2C&+NwLE=UFv}Jn_+5X zN?`;YJ3bw=@Hl1|M~yp*)M8#Kk8!5CC+k@yDDFIjqz%E4bS60)4JUQc#B>QnfK3sQ zTbfQ@4K1mqvZh5((@Jz0f_XYN9#@LSIM$-TQWnoqvD-@MPK@BNqsSns1#vpJa}KT<7LfsEJJe^VTo2`tin1v$l zk^m$CNmY&U@d9$GgFO1|#IR%k+M7eASV?7nw&#%c!BgcdRhgd|KhhYa{!$~VJS0#Z z?Znix>hPPTBSQ0bU}buaa$*h42)g=AeU++==VRo-k~*}d*bJ^X4OQexLTN1+^H1ii zJ;{wZUK;hJRj5idX4hgCz`}gq!zD`h z3Rsq!+{rLnMe;7(t2`_PjqyZTAdy6zj7AD%2JR_N5TFp0)(a8Q0Eu{yw>V*+!^wG| zS&UzCbE+-?B@I${&Xf4LNC2*29XLBWwa8I$Pnp!smLAVooh*@Bb^vxRWCJp}{`e6M zjeEB`0&j94F-<)GzXV#sV95%JiPDSYm&BDCcZ%5$w5b#CscT6Hso~fJx~UKktvlav{xrZ1a1F0S`@! z#JUNU*JDcM+4Ehd1H%E^E^OEu#v?o&SOz_6>?y36 zCd0hd!*jRpSto;IRi)W=882Jpd+J%`I*2@u&zzraEQ|6+`ddN8_)m(ZwpYaEsEoEJ z%4{f*37HJL2BD&CXY_e?SnXbBF2>*2#10~}8pC*ZfQ)-RN+LAxYwAHu%!4rh;^5CK z9t^em*CZg~oXHGZ3!Rn2cG!PDk(N=+#AfbAY`2mlG55=_Ys#7jDhW^opuBUBL6Hm(OfZj{B#m0MFIvNtgMGl_vDi!%&k38#w z{jid0k888n8}ln^riL_1hdeJ1nNbugz2AQ~3d@qUHpyKcJ9t^*8Rjf8%Hiu6{4=_exb ze%p;t3as8e0NUln{9In(b&7}g#Zpp!0?g`WjN<#7ttAuH5(@qaVYaarj$<_;@r1Z| zG(%a1X4>gt14IV6%mg{Z6Hn}AWOP2o%Ps1`JQRu~vjTucI})Li$Ha*zKqSy8qW(pL zgiNrRZk%T)x0%~5gKzjlZ?GC=>uyt{6!Wz>Ik2-Ro7|X^5cA!GA4iFj4;=rYv99~3 z?KE`cPL$Rc74rn*6BpDZoR=kX2~V^6paK10-awA0da%+fK#R_Ri@f~tRpLeDsoHq2 zanXPXZ*BIM(n(vG+#+8ig%#N(jZ$K2`lyKNPs>|(G{Y>}&}!Jw^U^>wR@6nF&8Z$m zVpG&J0hm+t2}_M$i$O4uiuIXnv=2JzKG%H9@3kMR+6B)79 zmWDWf6Uy>f9-&pIi(<$Y3MdOc{L_e^;39ZVmMAephMM@&@r1P0#R zghb(>0w%ERT1>5TSNYB2bvL{79D5l6ph!Byo6I-ror5s3{IRx`fvk1%F6bMirzZd$ z!TT~l$h|1_o=f|asr!~Fwa*AYW6Ya)M7v&G4h=qC5?d;mucBt3@v}c3jFH-&WNV~t zb?NmaS^%mHf4iv&v8N`Vx#L7Vx2v&psRo}}Q*KJIhbg*ocGixElIc?n&2VXCGE<^X zU$lUMkdx((M%APsiJGjsqNK!Bw7qR1obo|UhB?0&$SU??(P91)$up85G@c{lJ?_oh zKJE_5EvD`*5mpsx!z6wtv_f3DE{>4C8ehqr+0Hb@`2e`o*h=7Lid;%HQxy3#9@^Q; zIG~EdeXtvT)HWaVnV-3#OuF-$0wU1DdYy{XOw#Zatpkibqpry(tM{rs0&7!ys#0DB`N!0?P1YF zgcRi`2BxY{ER^?0NRM#{uisIeDi;jEJA0gdeYSe`ESkywc;G3JTauhfZ)-5@3I{Ad z4evcfMt4TRPhM@B*kNK4=ToUORUneEO3&$!;jSF3({Crv7oT!$eowHuSxnS=e4RqP z?yHLeAl;j^FWx}BC`&}4i+DT&sEM|1CJ)$?EC+s$e%g=|W$BeK0HGMD=|Xjm2p``Y z#8)@M?FDvNMZG`CW^Wki#UyeFQ5h1e4qcB)uP`*Aw2lH#NR%1T=8h_65xJAcm^d!5 zp-nR+6G~DA^yf)b(q5Ps37;TK?|Eo>ct49uq&W*vVbLf7Z<=$MJcP&GjzvG@=H|Pw0+lwdQ`q=C(sGlIHB#&6R!99gI+H z)@92k=9$V1TDPn<=L`W?S8ZT6qJnMyQS(3DM035{%YOpp!IXv8!Z�d6wqLWvK$? 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 a!0@=QRKea$fQxo|=nQpDb&%TkBK{BNuKp+h literal 0 HcmV?d00001 diff --git "a/bsp/ft2004/figures/rttsd\350\260\203\350\257\225.png" "b/bsp/ft2004/figures/rttsd\350\260\203\350\257\225.png" new file mode 100644 index 0000000000000000000000000000000000000000..98d1b5fa2b1b8f6137584dad78699f48ae8e272a GIT binary patch literal 22374 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 BC|3Xg literal 0 HcmV?d00001 diff --git "a/bsp/ft2004/figures/\345\220\257\345\212\250\346\274\224\347\244\272\345\233\276.png" "b/bsp/ft2004/figures/\345\220\257\345\212\250\346\274\224\347\244\272\345\233\276.png" new file mode 100644 index 0000000000000000000000000000000000000000..46a117c96f43cc19da42c3b4e0ba72325eb3e204 GIT binary patch literal 24488 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 literal 0 HcmV?d00001 diff --git a/bsp/ft2004/ft_aarch32.lds b/bsp/ft2004/ft_aarch32.lds new file mode 100644 index 0000000000..d37332d97f --- /dev/null +++ b/bsp/ft2004/ft_aarch32.lds @@ -0,0 +1,110 @@ +OUTPUT_FORMAT("elf32-littlearm", "elf32-littlearm", "elf32-littlearm") +OUTPUT_ARCH(arm) + +SECTIONS +{ + . = 0x80100000; + + __text_start = .; + .text : + { + *(.vectors) + *(.text) + *(.text.*) + + /* section information for finsh shell */ + . = ALIGN(4); + __fsymtab_start = .; + KEEP(*(FSymTab)) + __fsymtab_end = .; + . = ALIGN(4); + __vsymtab_start = .; + KEEP(*(VSymTab)) + __vsymtab_end = .; + . = ALIGN(4); + + /* section information for modules */ + . = ALIGN(4); + __rtmsymtab_start = .; + KEEP(*(RTMSymTab)) + __rtmsymtab_end = .; + + /* section information for initialization */ + . = ALIGN(4); + __rt_init_start = .; + KEEP(*(SORT(.rti_fn*))) + __rt_init_end = .; + } =0 + __text_end = .; + + __rodata_start = .; + .rodata : { *(.rodata) *(.rodata.*) } + __rodata_end = .; + + . = ALIGN(4); + .ctors : + { + PROVIDE(__ctors_start__ = .); + KEEP(*(SORT(.ctors.*))) + KEEP(*(.ctors)) + PROVIDE(__ctors_end__ = .); + } + + .dtors : + { + PROVIDE(__dtors_start__ = .); + KEEP(*(SORT(.dtors.*))) + KEEP(*(.dtors)) + PROVIDE(__dtors_end__ = .); + } + + . = ALIGN(16 * 1024); + .l1_page_table : + { + __l1_page_table_start = .; + . += 16K; + } + + . = ALIGN(8); + __data_start = .; + .data : + { + *(.data) + *(.data.*) + } + __data_end = .; + + . = ALIGN(8); + __bss_start = .; + .bss : + { + *(.bss) + *(.bss.*) + *(COMMON) + . = ALIGN(4); + } + . = ALIGN(4); + __bss_end = .; + + .heap : + { + . = ALIGN(8); + __end__ = .; + PROVIDE(end = .); + __HeapBase = .; + . += 0x400; + __HeapLimit = .; + __heap_limit = .; /* Add for _sbrk */ + } + + /* Stabs debugging sections. */ + .stab 0 : { *(.stab) } + .stabstr 0 : { *(.stabstr) } + .stab.excl 0 : { *(.stab.excl) } + .stab.exclstr 0 : { *(.stab.exclstr) } + .stab.index 0 : { *(.stab.index) } + .stab.indexstr 0 : { *(.stab.indexstr) } + .comment 0 : { *(.comment) } + + _end = .; +} diff --git a/bsp/ft2004/libraries/.gitignore b/bsp/ft2004/libraries/.gitignore new file mode 100644 index 0000000000..fcfda9fc84 --- /dev/null +++ b/bsp/ft2004/libraries/.gitignore @@ -0,0 +1,4 @@ +*.o +*.elf +*.bin +*.map diff --git a/bsp/ft2004/libraries/Kconfig b/bsp/ft2004/libraries/Kconfig new file mode 100644 index 0000000000..c544395596 --- /dev/null +++ b/bsp/ft2004/libraries/Kconfig @@ -0,0 +1,146 @@ +menu "Hardware Drivers Config" + +menu "On-chip Peripheral Drivers" + menuconfig BSP_USING_UART + bool "Enable UART" + default y + select RT_USING_SERIAL + if BSP_USING_UART + config RT_USING_UART1 + bool "Enable UART1" + default y + + config RT_USING_UART0 + bool "Enable UART0" + default n + + endif + + menuconfig BSP_USING_SDC + bool "Enable sd controller" + select RT_USING_SDIO + select RT_USING_DFS + select RT_USING_DFS_ELMFAT + default n + + if BSP_USING_SDC + + config BSP_SDC_DEBUG_PRINT + bool "Enable sd controller debug print" + default n + + + config BSP_SDC_USE_IRQ + bool "Use interrupt to handle when cmd complete, dma complete" + default n + + if BSP_SDC_USE_IRQ + config BSP_SDC_IRQ_CARD_REMOVE + bool "Use interrupt to determine if the card is pulled out" + default n + + endif + + endif + + menuconfig BSP_USING_GMAC + bool "Enable gmac" + default n + select RT_USING_NETDEV + + + if BSP_USING_GMAC + config BSP_USING_GMAC0 + bool "Enable GMAC0" + default y + + config BSP_USING_GMAC1 + bool "Enable GMAC1" + default n + + config RT_LWIP_ETH_PAD_SIZE + int "set lwip ETH_PAD_SIZE" + range 2 256 + default 2 + + config RAW_DATA_PRINT + bool "Enable mac raw data print" + default n + + if RAW_DATA_PRINT + config ETH_RX_DUMP + bool "Enable gmac receive raw data print " + default n + + config ETH_TX_DUMP + bool "Enable gmac send raw data print " + default n + endif + + endif + + + # menuconfig BSP_USE_QSPI + # bool "Enable Qspi" + # select RT_USING_SFUD + # select RT_SFUD_USING_QSPI + # default n + + # if BSP_USE_QSPI + # config BSP_QSPI_DEBUG + # bool "Enable qspi debug print" + # default n + # endif + + menuconfig BSP_USE_SPI + bool "Enable Spi" + select RT_USING_SFUD + select RT_SFUD_USING_SPI + select RT_SFUD_USING_SFDP + select RT_SFUD_USING_FLASH_INFO_TABLE + select BSP_USE_GPIO + default n + if BSP_USE_SPI + config BSP_SPI_DEBUG + bool "Enable spi debug print" + default n + endif + + menuconfig BSP_USE_GPIO + bool "Enable Gpio" + default n + if BSP_USE_GPIO + config BSP_GPIO_DEBUG + bool "Enable gpio debug print" + default n + endif + + menuconfig BSP_USE_CAN + bool "Enable Can" + select RT_USING_CAN + default n + + if BSP_USE_CAN + config BSP_USING_CAN0 + bool "Enable can0" + default n + config BSP_USING_CAN1 + bool "Enable can1" + default n + + if BSP_USING_CAN0 + config BSP_USING_CAN0_DEBUG + bool "Enable can0 work in loop back" + default n + endif + + endif + + +endmenu + +menu "Board extended module Drivers" + +endmenu + +endmenu diff --git a/bsp/ft2004/libraries/LICENSE b/bsp/ft2004/libraries/LICENSE new file mode 100644 index 0000000000..7a4a3ea242 --- /dev/null +++ b/bsp/ft2004/libraries/LICENSE @@ -0,0 +1,202 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright [yyyy] [name of copyright owner] + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. \ No newline at end of file diff --git a/bsp/ft2004/libraries/SConscript b/bsp/ft2004/libraries/SConscript new file mode 100644 index 0000000000..49476ebd87 --- /dev/null +++ b/bsp/ft2004/libraries/SConscript @@ -0,0 +1,145 @@ +''' + : Copyright (c) 2020 Phytium Information Technology, Inc. +The right to copy, distribute, modify, or otherwise make use +of this software may be licensed only pursuant to the terms +of an applicable Phytium license agreement. +Date: 2021-04-25 09:19:07 +LastEditTime: 2021-04-30 15:36:54 +Description:  This files is for + +Modify History: + Ver   Who        Date         Changes +----- ------     --------    -------------------------------------- +''' +from building import * +import rtconfig +Import('RTT_ROOT') + +# get current directory +cwd = GetCurrentDir() + +# The set of source files associated with this SConscript file. +src = Split(""" +bsp/ft_gicv3/ft_gicv3_g.c +bsp/ft_gicv3/ft_gicv3_hw.c +bsp/ft_gicv3/ft_gicv3_sinit.c +bsp/ft_gicv3/ft_gicv3.c +bsp/standlone/ft_assert.c +bsp/standlone/ft_generic_timer.c +bsp/standlone/ft_printf.c +bsp/standlone/ft_trace.c +bsp/standlone/ft_mux.c +bsp/standlone/inbyte.c +bsp/standlone/outbyte.c +bsp/standlone/ft_cache.c +bsp/standlone/ft_cpu.c +bsp/standlone/ft_smc.S +bsp/standlone/ft_psci.c +bsp/standlone/ft_debug.c +/usr/lib/arm-none-eabi/lib/libnosys.a + +""") +# + +if GetDepend(['RT_USING_SERIAL']): + src += ['bsp/ft_uart/ft_uart_g.c'] + src += ['bsp/ft_uart/ft_uart_hw.c'] + src += ['bsp/ft_uart/ft_uart_intr.c'] + src += ['bsp/ft_uart/ft_uart_options.c'] + src += ['bsp/ft_uart/ft_uart_selftest.c'] + src += ['bsp/ft_uart/ft_uart_sinit.c'] + src += ['bsp/ft_uart/ft_uart.c'] + src += ['drivers/rtthread/drv_usart.c'] + +if GetDepend(['RT_USING_I2C']): + None + +if GetDepend(['RT_USING_USB_HOST']) or GetDepend(['RT_USING_USB_DEVICE']): + None + +if GetDepend(['BSP_USE_CAN']): + src += ['bsp/ft_can/ft_can_g.c'] + src += ['bsp/ft_can/ft_can_hw.c'] + src += ['bsp/ft_can/ft_can_intr.c'] + src += ['bsp/ft_can/ft_can_sinit.c'] + src += ['bsp/ft_can/ft_can.c'] + src += ['bsp/ft_can/ft_can_calc.c'] + src += ['drivers/rtthread/drv_can.c'] + None + + +if GetDepend(['RT_USING_HWTIMER']) or GetDepend(['RT_USING_PWM']): + None + +if GetDepend(['RT_USING_ADC']): + None + +if GetDepend(['RT_USING_RTC']): + None + +if GetDepend(['RT_USING_WDT']): + None + +if GetDepend(['RT_USING_AUDIO']): + None + +if GetDepend(['BSP_USING_ON_CHIP_FLASH']): + None + +if GetDepend(['BSP_USING_GMAC']): + src += ['bsp/ft_gmac/ft_gmac_desc.c'] + src += ['bsp/ft_gmac/ft_gmac_g.c'] + src += ['bsp/ft_gmac/ft_gmac_hw.c'] + src += ['bsp/ft_gmac/ft_gmac_intr.c'] + src += ['bsp/ft_gmac/ft_gmac_sinit.c'] + src += ['bsp/ft_gmac/ft_gmac.c'] + src += ['drivers/rtthread/drv_eth.c'] + +if GetDepend(['BSP_USING_SDC']): + src += ['bsp/ft_sd/ft_sdctrl_option.c'] + src += ['bsp/ft_sd/ft_sdctrl_sinit.c'] + src += ['bsp/ft_sd/ft_sdctrl_intr.c'] + src += ['bsp/ft_sd/ft_sdctrl_g.c'] + src += ['bsp/ft_sd/ft_sdctrl_hw.c'] + src += ['bsp/ft_sd/ft_sdctrl.c'] + src += ['drivers/rtthread/drv_sdctrl.c'] + src += ['drivers/rtthread/drv_sdcard.c'] + +if GetDepend(['BSP_USE_QSPI']): + src += ['bsp/ft_qspi/qspi_g.c'] + src += ['bsp/ft_qspi/qspi_hw.c'] + src += ['bsp/ft_qspi/ft_qspi.c'] + src += ['bsp/ft_qspi/qspi_sinit.c'] + src += ['drivers/rtthread/drv_qspi.c'] + src += ['drivers/rtthread/ports/drv_qspi_flash.c'] + +if GetDepend(['BSP_USE_SPI']): + src += ['bsp/ft_spi/ft_spi.c'] + src += ['bsp/ft_spi/ft_spi_irq.c'] + src += ['drivers/rtthread/drv_spi.c'] + src += ['drivers/rtthread/ports/drv_spi_flash.c'] + +if GetDepend(['BSP_USE_GPIO']): + src += ['bsp/ft_gpio/ft_gpio.c'] + +path = [cwd + '/drivers/', + cwd + '/bsp/standlone/', + cwd + '/bsp/ft_gicv3', + cwd + '/bsp/ft_gmac', + cwd + '/bsp/ft_uart', + cwd + '/bsp/ft_sd', + cwd + '/bsp/ft_qspi', + cwd + '/bsp/ft_can', + cwd + '/bsp/ft_spi', + cwd + '/bsp/ft_gpio', + cwd + '/bsp/include', + cwd + '/include', + cwd + '/drivers/rtthread', + cwd + '/cpu', ] + + +CPPDEFINES = ['USE_FT_DRIVER'] +group = DefineGroup('FT_DRIVER', src, depend=[ + ''], CPPPATH=path, CPPDEFINES=CPPDEFINES) + +Return('group') diff --git a/bsp/ft2004/libraries/bsp/bsp.mk b/bsp/ft2004/libraries/bsp/bsp.mk new file mode 100644 index 0000000000..f7910e96f8 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/bsp.mk @@ -0,0 +1,21 @@ + + +INCDIRS += $(DEVICE_PATH)/bsp/standlone \ + $(DEVICE_PATH)/bsp/include \ + $(DEVICE_PATH)/bsp/ft_uart \ + $(DEVICE_PATH)/bsp/ft_gicv3 \ + $(DEVICE_PATH)/bsp/ft_gmac \ + $(DEVICE_PATH)/bsp/ft_i2c \ + $(DEVICE_PATH)/bsp/ft_sd + + + + +SRCDIRS += $(DEVICE_PATH)/bsp/standlone \ + $(DEVICE_PATH)/bsp/ft_uart \ + $(DEVICE_PATH)/bsp/ft_gicv3 \ + $(DEVICE_PATH)/bsp/ft_gmac \ + $(DEVICE_PATH)/bsp/ft_i2c \ + $(DEVICE_PATH)/bsp/ft_sd + + \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can.c new file mode 100644 index 0000000000..9aafafb6a8 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can.c @@ -0,0 +1,336 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-29 10:21:53 + * @LastEditTime: 2021-04-29 10:21:53 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can.h" +#include "ft_can_hw.h" +#include "ft_assert.h" +#include "ft_debug.h" +#include "string.h" + +#define FT_CAN_DEBUG_TAG "FT_CAN" + +#define FT_CAN_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_CAN_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_CAN_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) + +ft_error_t +FCan_CfgInitialize(FCan_t *Can_p, FCan_Config_t *Config_p) +{ + Ft_assertNonvoid(Can_p != NULL); + Ft_assertNonvoid(Config_p != NULL); + Can_p->Config = *Config_p; + Can_p->IsReady = FT_COMPONENT_IS_READLY; + FCan_Reset(Can_p); + return FCAN_SUCCESS; +} + +void FCan_GetErrorCnt(FCan_t *Can_p, u32 *TxErr, u32 *RxErr) +{ + FCan_Config_t *Config_p; + Ft_assertVoid(Can_p != NULL); + Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + *RxErr = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_RFN_MASK; + *TxErr = (FCan_ReadReg(Config_p->CanBaseAddress, FCAN_ERR_CNT_OFFSET) & FCAN_ERR_CNT_TFN_MASK) >> FCAN_ERR_CNT_TFN_SHIFT; +} + +u32 FCan_RecvByIrq(FCan_t *Can_p, struct FCan_Frame *Frame_p, u32 FrameNumber) +{ + u32 FifoCnt = 0; + FCan_Config_t *Config_p; + u32 CanId; + u32 Dlc; + u32 CanFrameIndex = 0; + u32 RxValue; + Ft_assertZeroNum(Can_p != NULL); + Ft_assertZeroNum(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + FifoCnt = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_FIFO_CNT_OFFSET) & 0x3f; + + if (0 == FifoCnt) + { + return 0; + } + + FrameNumber = (FrameNumber > FifoCnt) ? FifoCnt : FrameNumber; + + while (FrameNumber) + { + /* Read a frame from Phytium CAN */ + CanId = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_RX_FIFO_OFFSET); + /* if CanId is big-endian ,use swap change to little-endian */ + CanId = FT_SWAP32(CanId); + /* Identifier extension */ + if (CanId & FCAN_IDR_IDE_MASK) + { + Dlc = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_RX_FIFO_OFFSET); + Dlc = FT_SWAP32(Dlc); + Dlc = ((Dlc & FCAN_IDR_EDLC_MASK) >> FCAN_IDR_EDLC_SHIFT); + + Frame_p[CanFrameIndex].CanId = (CanId & FCAN_IDR_ID1_MASK) >> 3; + Frame_p[CanFrameIndex].CanId |= (CanId & FCAN_IDR_ID2_MASK) >> FCAN_IDR_ID2_SHIFT; + Frame_p[CanFrameIndex].CanId |= CAN_EFF_FLAG; + + if (CanId & FCAN_IDR_RTR_MASK) + { + Frame_p[CanFrameIndex].CanId |= CAN_RTR_FLAG; + } + } + else + { + Dlc = ((CanId & FCAN_IDR_DLC_MASK) >> FCAN_IDR_SDLC_SHIFT); + + /* The received frame is a standard format frame */ + Frame_p[CanFrameIndex].CanId = (CanId & FCAN_IDR_ID1_MASK) >> FCAN_IDR_ID1_SHIFT; + if (CanId & FCAN_IDR_SRR_MASK) + { + Frame_p[CanFrameIndex].CanId |= CAN_RTR_FLAG; + } + } + + Frame_p[CanFrameIndex].CanDlc = (Dlc > sizeof(Frame_p[CanFrameIndex].data)) ? sizeof(Frame_p[CanFrameIndex].data) : Dlc; + + if (!(Frame_p[CanFrameIndex].CanId & CAN_RTR_FLAG)) + { + if (Frame_p[CanFrameIndex].CanDlc > 0) + { + RxValue = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_RX_FIFO_OFFSET); + memcpy(Frame_p[CanFrameIndex].data, &RxValue, sizeof(RxValue)); + } + + if (Frame_p[CanFrameIndex].CanDlc > 4) + { + RxValue = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_RX_FIFO_OFFSET); + memcpy(&Frame_p[CanFrameIndex].data[4], &RxValue, sizeof(RxValue)); + } + } + FrameNumber--; + CanFrameIndex++; + } + + return (CanFrameIndex + 1); +} + +static void FCan_SendFifo(FCan_t *Can_p, struct FCan_Frame *Frame_p) +{ + u32 Id, Dlc; + FCan_Config_t *Config_p; + u32 SendBuffer = 0; + Ft_assertVoid(Can_p != NULL); + Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + if (Frame_p->CanId & CAN_EFF_FLAG) + { + /* Extended CAN ID format */ + Id = ((Frame_p->CanId & CAN_EFF_MASK) << FCAN_IDR_ID2_SHIFT) & + FCAN_IDR_ID2_MASK; + Id |= (((Frame_p->CanId & CAN_EFF_MASK) >> + (CAN_EFF_ID_BITS - CAN_SFF_ID_BITS)) + << FCAN_IDR_ID1_SHIFT) & + FCAN_IDR_ID1_MASK; + + Id |= FCAN_IDR_IDE_MASK | FCAN_IDR_SRR_MASK; + if (Frame_p->CanId & CAN_RTR_FLAG) + { + Id |= FCAN_IDR_RTR_MASK; + } + + Dlc = Frame_p->CanDlc << FCAN_IDR_EDLC_SHIFT; + + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_TX_FIFO_OFFSET, FT_SWAP32(Id)); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_TX_FIFO_OFFSET, FT_SWAP32(Dlc)); + } + else + { + /* Standard CAN ID format */ + Id = ((Frame_p->CanId & CAN_SFF_MASK) << FCAN_IDR_ID1_SHIFT) & + FCAN_IDR_ID1_MASK; + if (Frame_p->CanId & CAN_RTR_FLAG) + Id |= FCAN_IDR_SRR_MASK; + + Dlc = ((Frame_p->CanDlc << FCAN_IDR_SDLC_SHIFT) | FCAN_IDR_PAD_MASK); + Id |= Dlc; + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_TX_FIFO_OFFSET, FT_SWAP32(Id)); + } + + if (!(Frame_p->CanId & CAN_RTR_FLAG)) + { + if (Frame_p->CanDlc > 0) + { + memcpy(&SendBuffer, Frame_p->data, 4); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_TX_FIFO_OFFSET, SendBuffer); + } + + if (Frame_p->CanDlc > 4) + { + memcpy(&SendBuffer, &Frame_p->data[4], 4); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_TX_FIFO_OFFSET, SendBuffer); + } + } +} + +u32 FCan_SendByIrq(FCan_t *Can_p, + struct FCan_Frame *Frame_p, + u32 FrameNumber, void (*UserIrqWait)(void)) +{ + FCan_Config_t *Config_p; + u32 FrameIndex = 0; + u32 NeedSendOnce; + u32 cnt = 0; + Ft_assertZeroNum(Can_p != NULL); + Ft_assertZeroNum(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + if (NULL == Frame_p) + { + FT_CAN_DEBUG_E("Frame_p is NULL , %s: %d", __FILE__, __LINE__); + return 0; + } + + if (0 == FrameNumber) + { + FT_CAN_DEBUG_E("FrameNumber is 0 , %s: %d", __FILE__, __LINE__); + return 0; + } + + for (; 0 < FrameNumber;) + { + if (FrameNumber > Config_p->TxFifoDeepth) + { + NeedSendOnce = Config_p->TxFifoDeepth; + FrameNumber -= Config_p->TxFifoDeepth; + } + else + { + NeedSendOnce = FrameNumber; + FrameNumber = 0; + } + Ft_printf("shut down tranmission \r\n"); + /*shut down tranmission*/ + FCan_ClearBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + FCan_ClearBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + Ft_printf("NeedSendOnce %d \r\n", NeedSendOnce); + for (cnt = 0; cnt < NeedSendOnce; cnt++) + { + FCan_SendFifo(Can_p, &Frame_p[FrameIndex]); + FrameIndex++; + } + Can_p->TxFifoCnt = NeedSendOnce; + + /* triggers tranmission */ + FCan_ClearBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK | FCAN_CTRL_XFER_MASK); + + if (UserIrqWait) + { + UserIrqWait(); + } + else + { + while (0 != Can_p->TxFifoCnt) + { + }; + } + } + + return FrameIndex + 1; +} + +ft_error_t FCan_SetTiming(FCan_t *Can_p, + struct FCan_Bittiming *Bittiming_p) +{ + u32 Btr = 0; + FCan_Config_t *Config_p; + u32 IsConfigMode; + Ft_assertNonvoid(Can_p != NULL); + Ft_assertNonvoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + Ft_assertNonvoid(Bittiming_p->brp != 0); + Ft_assertNonvoid(Bittiming_p->prop_seg != 0); + Ft_assertNonvoid(Bittiming_p->phase_seg1 != 0); + Ft_assertNonvoid(Bittiming_p->phase_seg2 != 0); + + /* Setting Baud Rate prescalar value in BRPR Register */ + Btr = (Bittiming_p->brp - 1) << 16; + Btr |= (Bittiming_p->prop_seg - 1) << 2; + Btr |= (Bittiming_p->phase_seg1 - 1) << 5; + Btr |= (Bittiming_p->phase_seg2 - 1) << 8; + Btr |= (Bittiming_p->sjw - 1); + + IsConfigMode = (FCan_ReadReg(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET) & FCAN_CTRL_XFER_MASK); + + if (IsConfigMode) + { + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + } + + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_DAT_RATE_CTRL_OFFSET, Btr); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_ARB_RATE_CTRL_OFFSET, Btr); + + /*Enable Transfer*/ + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + + return FCAN_SUCCESS; +} + +void FCan_Enable(FCan_t *Can_p) +{ + FCan_Config_t *Config_p; + Ft_assertVoid(Can_p != NULL); + Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); +} + +// ft_error_t FCan_SetIdFilter(FCan_t *Can_p, u32) +// { +// FCan_Config_t *Config_p; +// Ft_assertVoid(Can_p != NULL); +// Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); +// Config_p = &Can_p->Config; + +// u32 IdRegOffset[4] = {FCAN_ACC_ID0_OFFSET, FCAN_ACC_ID1_OFFSET, FCAN_ACC_ID2_OFFSET, FCAN_ACC_ID3_OFFSET}; +// u32 MaskRegOffset[4] = {FCAN_ACC_ID0_MASK_OFFSET, FCAN_ACC_ID1_MASK_OFFSET, FCAN_ACC_ID2_MASK_OFFSET, FCAN_ACC_ID3_MASK_OFFSET}; + +// return FCAN_SUCCESS; +// } diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can.h b/bsp/ft2004/libraries/bsp/ft_can/ft_can.h new file mode 100644 index 0000000000..acb8fc8b4c --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can.h @@ -0,0 +1,166 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-27 15:08:44 + * @LastEditTime: 2021-04-27 15:08:44 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_CAN_H +#define FT_CAN_H + +#include "ft_types.h" +#include "ft_error_code.h" + +#define FCAN_SUCCESS FST_SUCCESS /* SUCCESS */ +#define FCAN_FAILURE FT_MAKE_ERRCODE(errCan, errBspGeneral, FST_FAILURE) /* Normal */ +#define FCAN_TIMEOUT FT_MAKE_ERRCODE(errCan, errBspGeneral, FST_TIMEOUT) /* Timeout */ +#define FCAN_EILSEQ FT_MAKE_ERRCODE(errCan, errBspGeneral, FST_EILSEQ) /* Illegal byte sequence. */ +#define FCAN_INVALID_PARAM FT_MAKE_ERRCODE(errCan, errBspGeneral, FST_INVALID_PARAM) /* Invalid param. */ + +#define FCAN_HANDLER_SEND 1U /**< Handler type for frame sending interrupt */ +#define FCAN_HANDLER_RECV 2U /**< Handler type for frame reception interrupt*/ +#define FCAN_HANDLER_ERROR 3U /**< Handler type for error interrupt */ +#define FCAN_DATA_LENGTH 8U + +/* CAN payload length and DLC definitions according to ISO 11898-1 */ +#define CAN_MAX_DLC 8 +#define CAN_MAX_DLEN 8 +#define CAN_MAX_CTL 3 +#define CAN_SFF_ID_BITS 11 +#define CAN_EFF_ID_BITS 29 + +/* special address description flags for the CAN_ID */ +#define CAN_EFF_FLAG 0x80000000U /* EFF/SFF is set in the MSB */ +#define CAN_RTR_FLAG 0x40000000U /* remote transmission request */ +#define CAN_ERR_FLAG 0x20000000U /* error message frame */ + +/* valid bits in CAN ID for frame formats */ +#define CAN_SFF_MASK 0x000007FFU /* standard frame format (SFF) */ +#define CAN_EFF_MASK 0x1FFFFFFFU /* extended frame format (EFF) */ +#define CAN_ERR_MASK 0x1FFFFFFFU /* omit EFF, RTR, ERR flags */ + +/* Frame type */ +#define STANDARD_FRAME 0 /* standard frame */ +#define EXTEND_FRAME 1 /* extended frame */ + +typedef void (*FCan_irqHandler_t)(void *Args); + +struct FCan_Frame +{ + u32 CanId; + u8 CanDlc; + u8 data[FCAN_DATA_LENGTH]; +}; + +struct FCan_Bittiming +{ + u32 bitrate; /* Bit-rate in bits/second */ + u32 sample_point; /* Sample point in one-tenth of a percent */ + u32 tq; /* Time quanta (TQ) in nanoseconds */ + u32 prop_seg; /* Propagation segment in TQs */ + u32 phase_seg1; /* Phase buffer segment 1 in TQs */ + u32 phase_seg2; /* Phase buffer segment 2 in TQs */ + u32 sjw; /* Synchronisation jump width in TQs */ + u32 brp; /* Bit-rate prescaler */ +}; + +typedef struct +{ + u32 InstanceId; /* Id of device */ + u32 CanBaseAddress; /* Can base Address */ + u32 IrqNum; + u32 BaudRate; + u32 TxFifoDeepth; /* The depth of the full frame , */ +} FCan_Config_t; + +typedef struct +{ + FCan_Config_t Config; + u32 IsReady; /* Device is initialized and ready */ + + volatile u32 TxFifoCnt; + + FCan_irqHandler_t SendHandler; + void *SendRef; + + FCan_irqHandler_t RecvHandler; + void *RecvRef; + + FCan_irqHandler_t ErrorHandler; + void *ErrorRef; + +} FCan_t; + +FCan_Config_t *FCan_LookupConfig(u32 InstanceId); + +/** + * @name: FCan_CfgInitialize + * @msg: This function initializes a Can instance/driver. + * @in param Can_p: Can_p is a pointer to the FCan_t instance. + * @in param Config_p: Config_p points to the FCan_t device configuration structure. + * @return {*} + */ +ft_error_t FCan_CfgInitialize(FCan_t *Can_p, FCan_Config_t *Config_p); + +/** + * @name: FCan_SetHandler + * @msg: This routine installs an asynchronous callback function for the given + * @inout param Can_p: Can_p is a pointer to the FCan_t instance. + * @in param HandlerType: specifies which handler is to be attached. + * @in param IrqCallBackFunc: IrqCallBackFunc is the address of the callback function. + * @in param IrqCallBackRef: IrqCallBackRef is a user data item that will be passed to the + * callback function when it is invoked. + * @return {*} + * @param {FCan_t} *Can_p + * @param {u32} HandlerType + * @param {FCan_irqHandler_t} *IrqCallBackFunc + * @param {void} *IrqCallBackRef + */ +ft_error_t FCan_SetHandler(FCan_t *Can_p, u32 HandlerType, FCan_irqHandler_t IrqCallBackFunc, void *IrqCallBackRef); + +ft_error_t FCan_SetTiming(FCan_t *Can_p, + struct FCan_Bittiming *Bittiming_p); + +void FCan_IntrHandler(void *InstancePtr); + +ft_error_t FCan_CalcBittiming(struct FCan_Bittiming *Bt_p); + +u32 FCan_SendByIrq(FCan_t *Can_p, + struct FCan_Frame *Frame_p, + u32 FrameNumber, void (*UserIrqWait)(void)); + +u32 FCan_RecvByIrq(FCan_t *Can_p, struct FCan_Frame *Frame_p, u32 FrameNumber); + +void FCan_Enable(FCan_t *Can_p); + +#endif // !FT_CAN_H diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_calc.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can_calc.c new file mode 100644 index 0000000000..c2a9d7d8ee --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_calc.c @@ -0,0 +1,298 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-05-06 09:30:51 + * @LastEditTime: 2021-05-06 09:30:51 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can.h" + +#include "ft_debug.h" +#include "string.h" + +#ifndef max +#define max(x, y) (((x) < (y)) ? (y) : (x)) +#endif + +#ifndef min +#define min(x, y) (((x) < (y)) ? (x) : (y)) +#endif + +#define FT_CAN_DEBUG_TAG "FT_CAN" + +#define FT_CAN_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_CAN_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_CAN_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_CAN_DEBUG_TAG, format, ##__VA_ARGS__) + +#define CAN_CLK_FREQ 600000000 +#define CAN_CALC_SYNC_SEG 1 +#define FCAN_TSEG1_MIN 1 +#define FCAN_TSEG1_MAX 8 +#define FCAN_TSEG2_MIN 1 +#define FCAN_TSEG2_MAX 8 +#define FCAN_SJW_MAX 4 +#define FCAN_BRP_MIN 1 +#define FCAN_BRP_MAX 512 +#define FCAN_BRP_INC 1 +#define FCAN_CALC_SYNC_SEG 1 +#define CAN_CALC_MAX_ERROR 50 /* in one-tenth of a percent */ +#define BEST_BITRATE_ERROR (2147483647 * 2U + 1) + +#define clamp(x, low, high) (min(max(low, x), high)) + +typedef struct can_bittiming_const +{ + char name[16]; /* Name of the CAN controller hardware */ + u32 tseg1_min; /* Time segement 1 = prop_seg + phase_seg1 */ + u32 tseg1_max; + u32 tseg2_min; /* Time segement 2 = phase_seg2 */ + u32 tseg2_max; + u32 sjw_max; /* Synchronisation jump width */ + u32 brp_min; /* Bit-rate prescaler */ + u32 brp_max; + u32 brp_inc; +} FTCAN_BITTIMING_CONST; + +static const struct can_bittiming_const ftcan_bittiming_const = { + .name = "vxbftCan", + .tseg1_min = 1, + .tseg1_max = 8, + .tseg2_min = 1, + .tseg2_max = 8, + .sjw_max = 4, + .brp_min = 1, + .brp_max = 512, + .brp_inc = 1, +}; + +static int abs( + int i /* integer for which to return absolute value */ +) +{ + return (i >= 0 ? i : -i); +} + +static u32 div64_32(u64 *n, u32 base) +{ + u64 rem = *n; + u64 b = base; + u64 res, d = 1; + u32 high = rem >> 32; + + /* Reduce the thing a bit first */ + res = 0; + if (high >= base) + { + high /= base; + res = (u64)high << 32; + rem -= (u64)(high * base) << 32; + } + + while ((u64)b > 0 && b < rem) + { + b = b + b; + d = d + d; + } + + do + { + if (rem >= b) + { + rem -= b; + res += d; + } + b >>= 1; + d >>= 1; + } while (d); + + *n = res; + return rem; +} + +s32 can_update_sample_point(const struct can_bittiming_const *btc, + u32 sample_point_nominal, u32 tseg, + u32 *tseg1_ptr, u32 *tseg2_ptr, + u32 *sample_point_error_ptr) +{ + u32 sample_point_error, best_sample_point_error = BEST_BITRATE_ERROR; + u32 sample_point, best_sample_point = 0; + u32 tseg1, tseg2; + s32 i; + + for (i = 0; i <= 1; i++) + { + tseg2 = tseg + CAN_CALC_SYNC_SEG - (sample_point_nominal * (tseg + CAN_CALC_SYNC_SEG)) / 1000 - i; + tseg2 = clamp(tseg2, btc->tseg2_min, btc->tseg2_max); + tseg1 = tseg - tseg2; + if (tseg1 > btc->tseg1_max) + { + tseg1 = btc->tseg1_max; + tseg2 = tseg - tseg1; + } + + sample_point = 1000 * (tseg + CAN_CALC_SYNC_SEG - tseg2) / (tseg + CAN_CALC_SYNC_SEG); + sample_point_error = abs(sample_point_nominal - sample_point); + + if ((sample_point <= sample_point_nominal) && (sample_point_error < best_sample_point_error)) + { + best_sample_point = sample_point; + best_sample_point_error = sample_point_error; + *tseg1_ptr = tseg1; + *tseg2_ptr = tseg2; + } + } + + if (sample_point_error_ptr) + *sample_point_error_ptr = best_sample_point_error; + + return best_sample_point; +} + +ft_error_t FCan_CalcBittiming(struct FCan_Bittiming *Bt_p) +{ + u32 bitrate; /* current bitrate */ + u32 bitrate_error; /* difference between current and nominal value */ + u32 best_bitrate_error = BEST_BITRATE_ERROR; + u32 sample_point_error; /* difference between current and nominal value */ + u32 best_sample_point_error = BEST_BITRATE_ERROR; + u32 sample_point_nominal; /* nominal sample point */ + u32 best_tseg = 0; /* current best value for tseg */ + u32 best_brp = 0; /* current best value for brp */ + u32 brp, tsegall, tseg, tseg1 = 0, tseg2 = 0; + u64 v64; + const struct can_bittiming_const *btc = &ftcan_bittiming_const; + struct FCan_Bittiming *bt = Bt_p; + + if (bt->sample_point) + { + sample_point_nominal = bt->sample_point; + } + else + { + if (bt->bitrate > 800000) + sample_point_nominal = 750; + else if (bt->bitrate > 500000) + sample_point_nominal = 800; + else + sample_point_nominal = 875; + } + + for (tseg = (btc->tseg1_max + btc->tseg2_max) * 2 + 1; + tseg >= (btc->tseg1_min + btc->tseg2_min) * 2; tseg--) + { + tsegall = CAN_CALC_SYNC_SEG + tseg / 2; + + /* Compute all possible tseg choices (tseg=tseg1+tseg2) */ + brp = CAN_CLK_FREQ / (tsegall * bt->bitrate) + tseg % 2; + + /* choose brp step which is possible in system */ + brp = (brp / btc->brp_inc) * btc->brp_inc; + + if ((brp < btc->brp_min) || (brp > btc->brp_max)) + continue; + + bitrate = CAN_CLK_FREQ / (brp * tsegall); + + bitrate_error = abs(bt->bitrate - bitrate); + /* tseg brp biterror */ + if (bitrate_error > best_bitrate_error) + continue; + + /* reset sample point error if we have a better bitrate */ + if (bitrate_error < best_bitrate_error) + best_sample_point_error = BEST_BITRATE_ERROR; + + can_update_sample_point(btc, sample_point_nominal, tseg / 2, &tseg1, &tseg2, &sample_point_error); + if (sample_point_error > best_sample_point_error) + continue; + + best_sample_point_error = sample_point_error; + best_bitrate_error = bitrate_error; + best_tseg = tseg / 2; + best_brp = brp; + + if (bitrate_error == 0 && sample_point_error == 0) + break; + } + + if (best_bitrate_error) + { + /* Error in one-tenth of a percent */ + v64 = (u64)best_bitrate_error * 1000; + div64_32(&v64, bt->bitrate); + bitrate_error = (u32)v64; + if (bitrate_error > CAN_CALC_MAX_ERROR) + { + // FTCAN_LOGMSG("bitrate error %d.%d%% too high\n", + // bitrate_error / 10, bitrate_error % 10, 3, 4, 5, 6); + FT_CAN_DEBUG_E("bitrate error"); + } + return FCAN_FAILURE; + FT_CAN_DEBUG_E("bitrate error 2"); + // FTCAN_LOGMSG("bitrate error %d.%d%%\n", + // bitrate_error / 10, bitrate_error % 10, 3, 4, 5, 6); + } + + /* real sample point */ + bt->sample_point = can_update_sample_point(btc, sample_point_nominal, best_tseg, + &tseg1, &tseg2, NULL); + + v64 = (u64)best_brp * 1000 * 1000 * 1000; + div64_32(&v64, CAN_CLK_FREQ); + bt->tq = (u64)v64; + bt->prop_seg = tseg1 / 2; + bt->phase_seg1 = tseg1 - bt->prop_seg; + bt->phase_seg2 = tseg2; + + /* check for sjw user settings */ + if (!bt->sjw || !btc->sjw_max) + { + bt->sjw = 1; + } + else + { + /* bt->sjw is at least 1 -> sanitize upper bound to sjw_max */ + if (bt->sjw > btc->sjw_max) + bt->sjw = btc->sjw_max; + /* bt->sjw must not be higher than tseg2 */ + if (tseg2 < bt->sjw) + bt->sjw = tseg2; + } + + bt->brp = best_brp; + + /* real bitrate */ + bt->bitrate = CAN_CLK_FREQ / (bt->brp * (CAN_CALC_SYNC_SEG + tseg1 + tseg2)); + // FT_CAN_DEBUG_I(" tq 0x%x ,prop_seg 0x%x , phase_seg1 0x%x, phase_seg2 0x%x ,brp 0x%x \r\n", bt->tq, bt->prop_seg, bt->phase_seg1, bt->phase_seg2, bt->brp); + return FCAN_SUCCESS; +} diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_g.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can_g.c new file mode 100644 index 0000000000..daac715f95 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_g.c @@ -0,0 +1,63 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-27 15:31:44 + * @LastEditTime: 2021-04-27 15:31:44 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can.h" +#include "ft_parameters.h" + +FCan_Config_t FCan_Config[FT_CAN_NUM] = + { + { + .InstanceId = 0, /* Id of device */ + .CanBaseAddress = FT_CAN0_BASEADDR, /* Can base Address */ + .IrqNum = FT_CAN0_IRQNUM, + .BaudRate = 250000, + .TxFifoDeepth = 16, + }, + { + .InstanceId = 1, /* Id of device */ + .CanBaseAddress = FT_CAN1_BASEADDR, /* Can base Address */ + .IrqNum = FT_CAN1_IRQNUM, + .BaudRate = 250000, + .TxFifoDeepth = 16, + }, + { + .InstanceId = 2, /* Id of device */ + .CanBaseAddress = FT_CAN2_BASEADDR, /* Can base Address */ + .IrqNum = FT_CAN2_IRQNUM, + .BaudRate = 250000, + .TxFifoDeepth = 16, + }}; diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.c new file mode 100644 index 0000000000..3bbd2b53a4 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.c @@ -0,0 +1,79 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-27 13:52:41 + * @LastEditTime: 2021-04-27 13:52:41 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can_hw.h" +#include "ft_can.h" +#include "ft_mux.h" +#include "ft_parameters.h" +#include "ft_math.h" +#include "ft_assert.h" +#include "ft_debug.h" + +#define CAN_HW_DEBUG_TAG "CAN_HW" + +#define CAN_HW_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define CAN_HW_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define CAN_HW_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(CAN_HW_DEBUG_TAG, format, ##__VA_ARGS__) + +void FCan_Reset(FCan_t *Can_p) +{ + u32 RegValue; + FCan_Config_t *Config_p; + Ft_assertVoid(Can_p != NULL); + Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + + RegValue = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET); + + if (RegValue & FCAN_CTRL_XFER_MASK) + { + CAN_HW_DEBUG_E("FT can is not in configration mode\n"); + Ft_assertVoid(0); + return; + } + + FCan_WriteReg(FT_PIN_DEMUX_BASE, FT_PIN_DEMUX_REG204_OFFSET, 0x89999990); // Reuse can IO + + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_RESET_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_AIME_MASK); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_ACC_ID0_MASK_OFFSET, FCAN_ACC_IDN_MASK); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_ACC_ID1_MASK_OFFSET, FCAN_ACC_IDN_MASK); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_ACC_ID2_MASK_OFFSET, FCAN_ACC_IDN_MASK); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_ACC_ID3_MASK_OFFSET, FCAN_ACC_IDN_MASK); + FCan_ClearBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_RESET_MASK); + FCan_WriteReg(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, FCAN_INTR_TEIE_MASK | FCAN_INTR_REIE_MASK); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.h b/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.h new file mode 100644 index 0000000000..3378571f95 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_hw.h @@ -0,0 +1,185 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-27 13:52:47 + * @LastEditTime: 2021-04-27 13:52:47 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_CAN_HW_H +#define FT_CAN_HW_H + +#include "ft_types.h" +#include "ft_io.h" +#include "ft_can.h" + +/***ft CAN REGISTER offset*/ +#define FCAN_CTRL_OFFSET 0x00 /* Global control register */ +#define FCAN_INTR_OFFSET 0x04 /* Interrupt register */ +#define FCAN_ARB_RATE_CTRL_OFFSET 0x08 /* Arbitration rate control register */ +#define FCAN_DAT_RATE_CTRL_OFFSET 0x0C /* Data rate control register */ +#define FCAN_ACC_ID0_OFFSET 0x10 /* Acceptance identifier0 register */ +#define FCAN_ACC_ID1_OFFSET 0x14 /* Acceptance identifier1 register */ +#define FCAN_ACC_ID2_OFFSET 0x18 /* Acceptance identifier2 register */ +#define FCAN_ACC_ID3_OFFSET 0x1C /* Acceptance identifier3 register */ +#define FCAN_ACC_ID0_MASK_OFFSET 0x20 /* Acceptance identifier0 mask register */ +#define FCAN_ACC_ID1_MASK_OFFSET 0x24 /* Acceptance identifier1 mask register */ +#define FCAN_ACC_ID2_MASK_OFFSET 0x28 /* Acceptance identifier2 mask register */ +#define FCAN_ACC_ID3_MASK_OFFSET 0x2C /* Acceptance identifier3 mask register */ +#define FCAN_XFER_STS_OFFSET 0x30 /* Transfer status register */ +#define FCAN_ERR_CNT_OFFSET 0x34 /* Error counter register */ +#define FCAN_FIFO_CNT_OFFSET 0x38 /* FIFO counter register */ +#define FCAN_DMA_CTRL_OFFSET 0x3C /* DMA request control register */ +#define FCAN_TX_FIFO_OFFSET 0x100 /* TX FIFO shadow register */ +#define FCAN_RX_FIFO_OFFSET 0x200 /* RX FIFO shadow register */ + +/*----------------------------------------------------------------------------*/ +/* CAN register bit masks - FCAN___MASK */ +/*----------------------------------------------------------------------------*/ + +/* FCAN_CTRL mask */ +#define FCAN_CTRL_XFER_MASK (0x1 << 0) /* RW */ /*Transfer enable*/ +#define FCAN_CTRL_TXREQ_MASK (0x1 << 1) /* RW */ /*Transmit request*/ +#define FCAN_CTRL_AIME_MASK (0x1 << 2) /* RW */ /*Acceptance identifier mask enable*/ +#define FCAN_CTRL_RESET_MASK (0x1 << 6) + +/* FCAN_INTR mask */ +#define FCAN_INTR_STATUS_MASK (0xFF << 0) /* RO */ /*the interrupt status*/ +#define FCAN_INTR_BOIS_MASK (0x1 << 0) /* RO */ /*Bus off interrupt status*/ +#define FCAN_INTR_PWIS_MASK (0x1 << 1) /* RO */ /*Passive warning interrupt status*/ +#define FCAN_INTR_PEIS_MASK (0x1 << 2) /* RO */ /*Passive error interrupt status*/ +#define FCAN_INTR_RFIS_MASK (0x1 << 3) /* RO */ /*RX FIFO full interrupt status*/ +#define FCAN_INTR_TFIS_MASK (0x1 << 4) /* RO */ /*TX FIFO empty interrupt status*/ +#define FCAN_INTR_REIS_MASK (0x1 << 5) /* RO */ /*RX frame end interrupt status*/ +#define FCAN_INTR_TEIS_MASK (0x1 << 6) /* RO */ /*TX frame end interrupt status*/ +#define FCAN_INTR_EIS_MASK (0x1 << 7) /* RO */ /*Error interrupt status*/ + +#define FCAN_INTR_EN_MASK (0xFF << 8) /* RO */ /*the interrupt enable*/ +#define FCAN_INTR_BOIE_MASK (0x1 << 8) /* RW */ /*Bus off interrupt enable*/ +#define FCAN_INTR_PWIE_MASK (0x1 << 9) /* RW */ /*Passive warning interrupt enable*/ +#define FCAN_INTR_PEIE_MASK (0x1 << 10) /* RW */ /*Passive error interrupt enable*/ +#define FCAN_INTR_RFIE_MASK (0x1 << 11) /* RW */ /*RX FIFO full interrupt enable*/ +#define FCAN_INTR_TFIE_MASK (0x1 << 12) /* RW */ /*TX FIFO empty interrupt enable*/ +#define FCAN_INTR_REIE_MASK (0x1 << 13) /* RW */ /*RX frame end interrupt enable*/ +#define FCAN_INTR_TEIE_MASK (0x1 << 14) /* RW */ /*TX frame end interrupt enable*/ +#define FCAN_INTR_EIE_MASK (0x1 << 15) /* RW */ /*Error interrupt enable*/ + +#define FCAN_INTR_BOIC_MASK (0x1 << 16) /* WO */ /*Bus off interrupt clear*/ +#define FCAN_INTR_PWIC_MASK (0x1 << 17) /* WO */ /*Passive warning interrupt clear*/ +#define FCAN_INTR_PEIC_MASK (0x1 << 18) /* WO */ /*Passive error interrupt clear*/ +#define FCAN_INTR_RFIC_MASK (0x1 << 19) /* WO */ /*RX FIFO full interrupt clear*/ +#define FCAN_INTR_TFIC_MASK (0x1 << 20) /* WO */ /*TX FIFO empty interrupt clear*/ +#define FCAN_INTR_REIC_MASK (0x1 << 21) /* WO */ /*RX frame end interrupt clear*/ +#define FCAN_INTR_TEIC_MASK (0x1 << 22) /* WO */ /*TX frame end interrupt clear*/ +#define FCAN_INTR_EIC_MASK (0x1 << 23) /* WO */ /*Error interrupt clear*/ + +/* FCAN_ACC_ID(0-3)_MASK mask */ +#define FCAN_ACC_IDN_MASK 0x1FFFFFFF /* WO */ /*don’t care the matching */ +/* FCAN_DAT_RATE_CTRL mask */ + +/* FCAN_ERR_CNT_OFFSET mask */ +#define FCAN_ERR_CNT_RFN_MASK (0xFF << 0) /* RO */ /*Receive error counter*/ +#define FCAN_ERR_CNT_TFN_MASK (0xFF << 16) /* RO */ /*Transmit error counter*/ + +/* FCAN_FIFO_CNT_OFFSET mask */ +#define FCAN_FIFO_CNT_RFN_MASK (0xFF << 0) /* RO */ /*Receive FIFO valid data number*/ +#define FCAN_FIFO_CNT_TFN_MASK (0xFF << 16) /* RO */ /*Transmit FIFO valid data number*/ + +#define FCAN_ERR_CNT_TFN_SHIFT 16 /* Tx Error Count shift */ +#define FCAN_FIFO_CNT_TFN_SHIFT 16 /* Tx FIFO Count shift*/ +#define FCAN_IDR_ID1_SHIFT 21 /* Standard Messg Identifier */ +#define FCAN_IDR_ID2_SHIFT 1 /* Extended Message Identifier */ +#define FCAN_IDR_SDLC_SHIFT 14 +#define FCAN_IDR_EDLC_SHIFT 26 +#define FCAN_ACC_IDN_SHIFT 18 /*Standard ACC ID shift*/ + +#define FCAN_IDR_ID2_MASK 0x0007FFFE /* Extended message ident */ +#define FCAN_IDR_ID1_MASK 0xFFE00000 /* Standard msg identifier */ +#define FCAN_IDR_IDE_MASK 0x00080000 /* Identifier extension */ +#define FCAN_IDR_SRR_MASK 0x00100000 /* Substitute remote TXreq */ +#define FCAN_IDR_RTR_MASK 0x00000001 /* Extended frames remote TX request */ +#define FCAN_IDR_DLC_MASK 0x0003C000 /* Standard msg dlc */ +#define FCAN_IDR_PAD_MASK 0x00003FFF /* Standard msg padding 1 */ +#define FCAN_IDR_EDLC_MASK 0x3C000000 /* Extended msg dlc */ + +/* Can timming */ +#define FCAN_TSEG1_MIN 1 +#define FCAN_TSEG1_MAX 8 +#define FCAN_TSEG2_MIN 1 +#define FCAN_TSEG2_MAX 8 +#define FCAN_SJW_MAX 4 +#define FCAN_BRP_MIN 1 +#define FCAN_BRP_MAX 512 +#define FCAN_BRP_INC 1 +#define FCAN_CALC_SYNC_SEG 1 + +/** +* +* This macro reads the given register. +* +* @param BaseAddr is the base address of the device. +* @param RegOffset is the register offset to be read. +* +* @return The 32-bit value of the register +* +* @note None. +* +*****************************************************************************/ +#define FCan_ReadReg(BaseAddr, RegOffset) \ + Ft_in32((BaseAddr) + (u32)(RegOffset)) + +/****************************************************************************/ +/** +* +* This macro writes the given register. +* +* @param BaseAddr is the base address of the device. +* @param RegOffset is the register offset to be written. +* @param Data is the 32-bit value to write to the register. +* +* @return None. +* +* @note None. +* +*****************************************************************************/ +#define FCan_WriteReg(BaseAddr, RegOffset, Data) \ + Ft_out32((BaseAddr) + (u32)(RegOffset), (u32)(Data)) + +#define FCan_SetBit(BaseAddr, RegOffset, Data) \ + Ft_setBit32((BaseAddr) + (u32)(RegOffset), (u32)(Data)) + +#define FCan_ClearBit(BaseAddr, RegOffset, Data) \ + Ft_clearBit32((BaseAddr) + (u32)(RegOffset), (u32)(Data)) + +void FCan_Reset(FCan_t *Can_p); + +#endif // ! diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_intr.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can_intr.c new file mode 100644 index 0000000000..ddd3cb13b2 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_intr.c @@ -0,0 +1,142 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-29 10:40:47 + * @LastEditTime: 2021-04-29 10:40:47 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can.h" +#include "ft_can_hw.h" +#include "ft_assert.h" +#include "ft_types.h" + +ft_error_t FCan_SetHandler(FCan_t *Can_p, u32 HandlerType, FCan_irqHandler_t IrqCallBackFunc, void *IrqCallBackRef) +{ + ft_error_t status = FCAN_SUCCESS; + Ft_assertNonvoid(Can_p != NULL); + Ft_assertNonvoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + switch (HandlerType) + { + case FCAN_HANDLER_SEND: + Can_p->SendHandler = IrqCallBackFunc; + Can_p->SendRef = IrqCallBackRef; + break; + case FCAN_HANDLER_RECV: + Can_p->RecvHandler = IrqCallBackFunc; + Can_p->RecvRef = IrqCallBackRef; + break; + case FCAN_HANDLER_ERROR: + Can_p->ErrorHandler = IrqCallBackFunc; + Can_p->ErrorRef = IrqCallBackRef; + break; + default: + status = FCAN_FAILURE; + } + + return status; +} + +static void FCan_TxInterrupt(FCan_t *Can_p) +{ + FCan_Config_t *Config_p = &Can_p->Config; + FCan_SetBit(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, FCAN_INTR_TEIC_MASK | FCAN_INTR_REIC_MASK); + + if (0 != Can_p->TxFifoCnt) + { + Can_p->TxFifoCnt--; + FCan_ClearBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_TXREQ_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_CTRL_OFFSET, FCAN_CTRL_XFER_MASK); + } + else + { + if (Can_p->SendHandler) + { + Can_p->SendHandler(Can_p->SendRef); + } + } +} + +static void FCan_ErrorInterrupt(FCan_t *Can_p) +{ + if (Can_p->ErrorHandler) + { + Can_p->ErrorHandler(Can_p->ErrorRef); + } +} + +static void FCan_RxInterrupt(FCan_t *Can_p) +{ + if (Can_p->RecvHandler) + { + Can_p->RecvHandler(Can_p->RecvRef); + } +} + +void FCan_IntrHandler(void *InstancePtr) +{ + u32 Irq; + FCan_t *Can_p = (FCan_t *)InstancePtr; + FCan_Config_t *Config_p; + Ft_assertVoid(Can_p != NULL); + Ft_assertVoid(Can_p->IsReady == FT_COMPONENT_IS_READLY); + Config_p = &Can_p->Config; + Irq = FCan_ReadReg(Config_p->CanBaseAddress, FCAN_INTR_OFFSET); + + if (0 == Irq) + { + return; + } + + /* Check for the type of error interrupt and Processing it */ + if (Irq & FCAN_INTR_TEIS_MASK) + { + Irq &= ~FCAN_INTR_REIS_MASK; + FCan_TxInterrupt(Can_p); + } + + if (Irq & (FCAN_INTR_EIS_MASK | FCAN_INTR_RFIS_MASK | + FCAN_INTR_BOIS_MASK | FCAN_INTR_PEIS_MASK | FCAN_INTR_PWIS_MASK)) + { + FCan_SetBit(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, (FCAN_INTR_EIC_MASK | FCAN_INTR_RFIC_MASK | FCAN_INTR_BOIC_MASK | FCAN_INTR_PEIC_MASK | FCAN_INTR_PWIC_MASK)); + FCan_ErrorInterrupt(Can_p); + } + + if (Irq & FCAN_INTR_REIS_MASK) + { + FCan_SetBit(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK); + FCan_RxInterrupt(Can_p); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, FCAN_INTR_REIC_MASK); + FCan_SetBit(Config_p->CanBaseAddress, FCAN_INTR_OFFSET, FCAN_INTR_REIE_MASK); + } +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_can/ft_can_sinit.c b/bsp/ft2004/libraries/bsp/ft_can/ft_can_sinit.c new file mode 100644 index 0000000000..c413031a97 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_can/ft_can_sinit.c @@ -0,0 +1,64 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-27 15:31:57 + * @LastEditTime: 2021-04-27 15:31:57 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_can.h" +#include "ft_parameters.h" + +extern FCan_Config_t FCan_Config[FT_CAN_NUM]; + +/** + * @name: + * @msg: + * @in param: + * @return {*} + * @param {u32} InstanceId + */ +FCan_Config_t *FCan_LookupConfig(u32 InstanceId) +{ + FCan_Config_t *CfgPtr = NULL; + u32 Index; + + for (Index = 0; Index < (u32)FT_CAN_NUM; Index++) + { + if (FCan_Config[Index].InstanceId == InstanceId) + { + CfgPtr = &FCan_Config[Index]; + break; + } + } + return (FCan_Config_t *)CfgPtr; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c new file mode 100644 index 0000000000..fdaf53fd91 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c @@ -0,0 +1,188 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-22 13:43:03 + * @Description:  This files is for gic v3.0 + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_printf.h" +#include "ft_io.h" +#include "ft_gicv3_hw.h" +#include "ft_gicv3.h" + +/************************** Variable Definitions *****************************/ +static u32 CpuId = 0; + +/************************** Function Prototypes ******************************/ + +static void FGicv3_stubHandler(void *Args) +{ + Ft_assertVoid(Args != NULL); + + ((FGicv3 *)Args)->UnhandledInterrupts++; +} + +/** + * @name: Ft_GicV3_ConfigInitalize + * @msg: gicv3 initialization + * @param {FGicv3} *GicPtr + * @param {Ft_Gicv3_Config} *ConfigPtr + * @return {*} + */ +s32 FGicv3_Config_tInitalize(FGicv3 *GicPtr, FGicv3_Config_t *ConfigPtr) +{ + u32 InstanceId; + Ft_assertNonvoid(GicPtr != NULL); + Ft_assertNonvoid(ConfigPtr != NULL); + + if (GicPtr->IsReady != FT_COMPONENT_IS_READLY) + { + + GicPtr->IsReady = 0U; + GicPtr->Config = ConfigPtr; + + for (InstanceId = 0; InstanceId < FT_GICV3_VECTORTABLE_NUM; InstanceId++) + { + if (GicPtr->Config->HanderTable[InstanceId].Handler == NULL) + { + GicPtr->Config->HanderTable[InstanceId].Handler = FGicv3_stubHandler; + } + GicPtr->Config->HanderTable[InstanceId].Args = GicPtr; + } + + FGicV3_Stop(GicPtr->Config, CpuId); + FGicv3_InstanceInitialize(GicPtr->Config); + GicPtr->IsReady = FT_COMPONENT_IS_READLY; + } + else + { + FGicv3_InstanceInitialize(GicPtr->Config); + } + + return FST_SUCCESS; +} + +/** + * @name: Ft_GicV3_Connect + * @msg: 使中断处理函数与中断源进行连接,Args 作为参数,在调用时被传入中断处理函数 + * @param {FGicv3} GicPtr + * @param {u32} InterruptID + * @param {Ft_FGicv3_InterruptHandler} InterruptHandler + * @param {void *} Args + * @return {s32} + */ +s32 FGicV3_Connect(FGicv3 *GicPtr, + u32 InterruptID, + FGicv3_InterruptHandler_t InterruptHandler, + u32 Priority, + void *Args) +{ + Ft_assertNonvoid(GicPtr != NULL); + Ft_assertNonvoid(InterruptID < GIC_INT_MAX_NUM); + Ft_assertNonvoid(NULL != InterruptHandler); + Ft_assertNonvoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); + + if (FST_SUCCESS == FGicv3_RegisterHandler(GicPtr->Config, InterruptID, InterruptHandler, Args)) + { + return FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, InterruptID, GIC_TRIGGER_LEVEL_SENSITIVE, Priority); + } + else + { + return FST_FAILURE; + } +} + +/** + * @name: FGicV3_Disconnect + * @msg: 断开中断源与中断函数间联系,并且关闭中断 + * @param {FGicv3} + * @param {InterruptID} 中断ID号 + * @return {*} + */ +void FGicV3_Disconnect(FGicv3 *GicPtr, + u32 InterruptID) +{ + Ft_assertVoid(GicPtr != NULL); + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + Ft_assertVoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); + + FGicV3_DisableInterrupt(GicPtr->Config, InterruptID); + + GicPtr->Config->HanderTable[InterruptID].Handler = FGicv3_stubHandler; + GicPtr->Config->HanderTable[InterruptID].Args = GicPtr; +} + +/** + * @name: FGicV3_Enable + * @msg: 开启Gic 中对应InterruptID 的中断 + * @param {FGicv3 *} GicPtr + * @param {InterruptID} 中断ID + * @return {void} + */ +void FGicV3_Enable(FGicv3 *GicPtr, + u32 InterruptID) +{ + Ft_assertVoid(GicPtr != NULL); + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + Ft_assertVoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); + + FGicV3_EnableInterrupt(GicPtr->Config, InterruptID); +} + +/** + * @name: FGicV3_SetPriority + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +void FGicV3_SetPriority(FGicv3 *GicPtr, + u32 InterruptID, u32 Priority) +{ + Ft_assertVoid(GicPtr != NULL); + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + Ft_assertVoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); + FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, InterruptID, GIC_TRIGGER_LEVEL_SENSITIVE, Priority); +} + +/** + * @name: FGicV3_Disable + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +void FGicV3_Disable(FGicv3 *GicPtr, + u32 InterruptID) +{ + Ft_assertVoid(GicPtr != NULL); + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + Ft_assertVoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); + + FGicV3_DisableInterrupt(GicPtr->Config, InterruptID); +} + +/** + * @name: System_IrqHandler + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {u32} IccIar_num + */ +void System_IrqHandler(u32 IccIar_num) +{ + FGicv3_InterruptHandler(IccIar_num & 0x3FFUL, 0); +} diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.h b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.h new file mode 100644 index 0000000000..4eb9233ec4 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.h @@ -0,0 +1,85 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-27 16:01:32 + * @Description:  This files is for gic v3.0 + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef FT_GICV3_H +#define FT_GICV3_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_parameters.h" +#include "ft_types.h" +#include "ft_status.h" + +#include "ft_error_code.h" + +#define FGICV3_SUCCESS FST_SUCCESS /* SUCCESS */ +#define FGICV3_FAILURE FT_MAKE_ERRCODE(errGicV3, errBspGeneral, FST_FAILURE) /* Normal */ +#define FGICV3_TIMEOUT FT_MAKE_ERRCODE(errGicV3, errBspGeneral, FST_TIMEOUT) /* Timeout */ +#define FGICV3_EILSEQ FT_MAKE_ERRCODE(errGicV3, errBspGeneral, FST_EILSEQ) /* Illegal byte sequence. */ +#define FGICV3_INVALID_PARAM FT_MAKE_ERRCODE(errGicV3, errBspGeneral, FST_INVALID_PARAM) /* Invalid param. */ + + typedef void (*FGicv3_InterruptHandler_t)(void *data); + + /***************************Type Definitions*************************************/ + /* + Handler 是对应与中断向量表中的函数 + Args 是默认传入的参数 + */ + typedef struct + { + FGicv3_InterruptHandler_t Handler; + void *Args; + } FGicv3_VectorTableEntry_t; + + /***** Typedef contains configuration for the device *******/ + typedef struct + { + u32 InstanceId; /* Id of device */ + u32 Gicv3BaseAddress; /* gicv3 base Address */ + u32 Gicv3DistributorAddress; /* gicv3 Distributor Address */ + u32 Gicv3RdAddress; /* the overall behavior of the Redistributor, for controlling LPIs, and for generating */ + u32 Gicv3SgiAddress; /* controlling and generating PPIs and SGIs. */ + FGicv3_VectorTableEntry_t HanderTable[FT_GICV3_VECTORTABLE_NUM]; /* Vector table */ + } FGicv3_Config_t; + + typedef struct + { + FGicv3_Config_t *Config; + u32 IsReady; /* Device is initialised and ready */ + u32 UnhandledInterrupts; /**< Intc Statistics */ + } FGicv3; + + s32 FGicv3_Config_tInitalize(FGicv3 *GicPtr, FGicv3_Config_t *ConfigPtr); + s32 FGicV3_Connect(FGicv3 *GicPtr, + u32 InterruptID, + FGicv3_InterruptHandler_t InterruptHandler, + u32 Priority, + void *Args); + void FGicV3_Disconnect(FGicv3 *GicPtr, + u32 InterruptID); + void FGicV3_Enable(FGicv3 *GicPtr, + u32 InterruptID); + void FGicV3_Disable(FGicv3 *GicPtr, + u32 InterruptID); + FGicv3_Config_t *FGicv3_LookupConfig(u32 InstanceId); + void System_IrqHandler(u32 IccIar_num); + void FGicV3_SetPriority(FGicv3 *GicPtr, u32 InterruptID, u32 Priority); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_g.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_g.c new file mode 100644 index 0000000000..1de252deae --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_g.c @@ -0,0 +1,25 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-29 18:57:47 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_parameters.h" +#include "ft_gicv3.h" + +FGicv3_Config_t g_FGicv3_Config_tTable[FT_GICV3_INSTANCES_NUM] = { + { + .InstanceId = 0, /* Id of device */ + .Gicv3BaseAddress = FT_GICV3_BASEADDRESS, /* gicv3 base Address */ + .Gicv3DistributorAddress = FT_GICV3_DISTRIBUTOR_BASEADDRESS, /* gicv3 Distributor Address */ + .Gicv3RdAddress = FT_GICV3_RD_BASEADDRESS, /* the overall behavior of the Redistributor, for controlling LPIs, and for generating */ + .Gicv3SgiAddress = FT_GICV3_SGI_BASEADDRESS, /* controlling and generating PPIs and SGIs. */ + }}; \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c new file mode 100644 index 0000000000..dbf2c395a3 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c @@ -0,0 +1,745 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-26 11:14:54 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_io.h" +#include "ft_status.h" +#include "ft_printf.h" +#include "ft_cpu.h" +#include "ft_aarch32_asm.h" +#include "ft_parameters.h" +#include "asmArm.h" +#include "ft_gicv3.h" +#include "ft_gicv3_hw.h" + +/************************** Constant Definitions *****************************/ + +/**************************** Type Definitions *******************************/ + +/***************** Macros (Inline Functions) Definitions *********************/ + +#define GIC_IPI_PRIORITY 0 +#define GIC_PRIORITY_LEVEL_STEP 0x10 +#define GIC_SPI_PRIORITY_DEFAULT 0x10101010 +#define ARM_GICV3_MSI_START 125 +#define ARM_GICV3_MSI_CNT 34 + +#define ARM_GICV3_PHYS_REG(offset) (armGicBase + offset) +#define ARM_GICV3_VIRT_REG(offset) (armGicBase + offset) + +#define ARM_GICV3_MSI_CAP_ADDR_OFF 0x40 +#define ARM_GICV3_MSI_ANSWER_OFF 0x48 +#define ARM_GICV3_MSI_CAP_PHYS_ADDR ARM_GICV3_PHYS_REG(ARM_GICV3_MSI_CAP_ADDR_OFF) +#define ARM_GICV3_MSI_ANSWER_ADDR ARM_GICV3_VIRT_REG(ARM_GICV3_MSI_ANSWER_OFF) + +/********************************Variable Definitions************************************************/ +extern FGicv3_Config_t g_FGicv3_Config_tTable[FT_GICV3_INSTANCES_NUM]; +static u32 g_FGicVersion = 3; + +/************************** Function Prototypes ******************************/ + +/* assembly functions */ + +/** + * @name: FGiv3_ClrMsiIntr + * @msg: 清除msi 中断,暂时没有启用 + * @param {Ft_Gicv3_Config} *Config + * @param {u32} irq + * @return {*} + */ +void FGiv3_ClrMsiIntr(FGicv3_Config_t *Config, u32 irq) +{ + // *(volatile u32 *)(ARM_GICV3_MSI_ANSWER_ADDR) = irq; + return; +} + +static __inline__ void __dsb(void) +{ + __asm__ volatile( + "dsb" + : /* no output */ + : /* no input */ + : "memory"); +} + +/** + * @name: Gicv3_Init + * @msg: + * @param {*} + * @return {*} + */ +// static void +// Gicv3_Init(FGicv3_Config_t *Config, s32 CpuID) +// { +// u32 value = 0; +// u32 count = 1000; +// s32 regGicLineNum = 0; + +// u32 aGicCfg = 0; +// s32 i = 0; + +// Ft_out32((UINTPTR)(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET), 0xB7); +// while (1) +// { +// if ((Ft_in32((UINTPTR)(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET)) & 0x80000000) == 0) +// { +// break; +// } +// } +// //Gicv3RdAddress, CpuID))); +// value &= ~GICC_WAKER_PROCESSORSLEEP_IN; +// Ft_out32(Ft_in32(GICR_WAKER_CPU(Config->Gicv3RdAddress, CpuID)), value); +// while (--count) +// { +// value = Ft_in32(Ft_in32(GICR_WAKER_CPU(Config->Gicv3RdAddress, CpuID))); +// if (1 ^ (value & GICC_WAKER_CHILDRENASLEEP_QUIESCENT)) +// break; +// } + +// //!< GICR_WAKER_OFFSET is 0 +// value = Ft_in32(Config->Gicv3SgiAddress + GICR_ISENABLER0_OFFSET); +// Ft_out32(GICR_INTENCLR_CPU(Config->Gicv3SgiAddress, CpuID), Swap32(ALL_PPI_INT_MASK)); +// Ft_out32(GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuID), ALL_SGI_INT_MASK); + +// // val = *GICR_IntEnable; +// // *GICR_INTENCLR_CPU(Config->Gicv3SgiAddress, CpuID) = SWAP32(ALL_PPI_INT_MASK); +// // *GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuID) = ALL_SGI_INT_MASK; + +// if (CpuID == 0 && g_FGicVersion < 3) +// { +// Ft_out32(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET, 0); +// //Gicv3DistributorAddress + GICD_TYPER_OFFSET)) & 0x1f); +// regGicLineNum = (regGicLineNum + 1) * SPI_START_INT_NUM; +// //= 3) +// { +// /* +// The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode. +// Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled. +// If affinity routing is not enabled, GICv3 can be configured for legacy operation . +// Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state. +// Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1: +// GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts. +// GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts. +// GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts. +// */ +// Ft_out32(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET, 0x37); +// Ft_out32(Config->Gicv3SgiAddress + GICR_ICENABLER0_OFFSET, Swap32(ALL_PPI_INT_MASK)); +// Ft_out32(Config->Gicv3SgiAddress + GICR_ISENABLER0_OFFSET, ALL_SGI_INT_MASK); +// } +// else if (g_FGicVersion < 2) +// { +// Ft_out32(Config->Gicv3DistributorAddress + GICD_ICENABLE_OFFSET, 0); +// } + +// /* Get the max priority */ +// Ft_out32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, SPI_START_INT_NUM), Swap32(0xff)); +// g_FGicv3_PriorityLevelMax = Ft_in32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, SPI_START_INT_NUM)); +// Ft_out32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, SPI_START_INT_NUM), Swap32(0)); + +// /* clear all pending PPI and SGI interrupts in the distributor */ +// if (g_FGicVersion >= 3) +// { +// Ft_out32((Config->Gicv3SgiAddress + GICR_ICPENDR0_OFFSET), Swap32(ALL_PPI_INT_MASK | ALL_SGI_INT_MASK)); +// } +// else +// { +// Ft_out32((Config->Gicv3DistributorAddress + GICD_ICPENDR_OFFSET), Swap32(ALL_PPI_INT_MASK | ALL_SGI_INT_MASK)); +// } + +// /* set default priority for all PPI and SGI interrupts to level 0(highest) */ +// for (i = 0; i < SPI_START_INT_NUM; i += 4) +// { +// /* code */ +// Ft_out32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, i), GIC_IPI_PRIORITY); +// } + +// if (CpuID == 0) +// { +// /* +// * Disable all SPI interrupts +// * Clear all pending SPI interrupts in the distributor +// */ +// for (i = SPI_START_INT_NUM; i < GIC_INT_MAX_NUM; i += BITS_PER_WORD) +// { +// Ft_out32((UINTPTR)GIC_INTPENDCLR(Config->Gicv3DistributorAddress, i), 0xffffffff); +// //Gicv3DistributorAddress, i), 0xffffffff); +// //Gicv3DistributorAddress, i), GIC_SPI_PRIORITY_DEFAULT); +// if (g_FGicVersion < 3) +// { +// Ft_out32((UINTPTR)GIC_CPUTARG(Config->Gicv3DistributorAddress, i), Swap32(GIC_CPU_DIR_DEFAULT)); +// i += 4; +// } +// else +// { +// Ft_out32((UINTPTR)GIC_INTRPUTE_HIGH(Config->Gicv3DistributorAddress, i), GIC_V3_CPU_DIR_DEFAULT_HIGH); +// Ft_out32((UINTPTR)GIC_INTRPUTE_LOW(Config->Gicv3DistributorAddress, i), GIC_V3_CPU_DIR_DEFAULT_LOW); +// i++; +// } +// } +// } + +// /* setting whether 1-N/N-N and Level/Edge triggered */ + +// for (i = SPI_START_INT_NUM; i < regGicLineNum; i += CONFIGS_PER_WORD) +// { +// Ft_out32((UINTPTR)GIC_CONFIG(Config->Gicv3DistributorAddress, i), 0x55555555); /* 1-N, Level */ +// } + +// for (i = ARM_GICV3_MSI_START; i < ARM_GICV3_MSI_CNT; i++) +// { +// /* +// data = arm_gicv3_drv.rd32(ARM_GICV3_ICDICRn(i)); +// data |= EDGE_TRIGGER << ((i % 16) << 1); +// arm_gicv3_drv.wr32(ARM_GICV3_ICDICRn(i), data); +// */ + +// aGicCfg = Swap32(Ft_in32((UINTPTR)GIC_CONFIG(Config->Gicv3DistributorAddress, i))); +// aGicCfg |= BIT(((i % CONFIGS_PER_WORD) * GIC_INT_TRIGGER_SHIFT + 1)); +// Ft_out32((UINTPTR)GIC_CONFIG(Config->Gicv3DistributorAddress, i), aGicCfg); +// } + +// sys_icc_bpr_set(0); //Gicv3DistributorAddress + GICD_CTLR_OFFSET, Swap32(GIC_CONTROL_ENABLE)); +// } +// } + +static ft_error_t FGicv3_DistInit(u32 Gicv3DistributorAddress, s32 CpuID) +{ + s32 i = 0; + s32 regGicLineNum = 0; + + regGicLineNum = (s32)(Swap32(*(volatile u32 *)(Gicv3DistributorAddress + GICD_TYPER_OFFSET)) & 0x1f); + regGicLineNum = (regGicLineNum + 1) * SPI_START_INT_NUM; + + Ft_out32((UINTPTR)(Gicv3DistributorAddress + GICD_CTLR_OFFSET), 0xB7); + while (1) + { + if ((Ft_in32((UINTPTR)(Gicv3DistributorAddress + GICD_CTLR_OFFSET)) & 0x80000000) == 0) + { + break; + } + } + + if (CpuID == 0 && g_FGicVersion >= 3) + { + /* + The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode. + Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled. + If affinity routing is not enabled, GICv3 can be configured for legacy operation . + Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state. + Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1: + GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts. + GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts. + GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts. + */ + Ft_out32(Gicv3DistributorAddress + GICD_CTLR_OFFSET, 0x37); + } + + for (i = 0; i < SPI_START_INT_NUM; i += 4) + { + /* code */ + Ft_out32((UINTPTR)GIC_PRIO(Gicv3DistributorAddress, i), GIC_IPI_PRIORITY); + } + + if (CpuID == 0) + { + /* + * Disable all SPI interrupts + * Clear all pending SPI interrupts in the distributor + */ + for (i = SPI_START_INT_NUM; i < GIC_INT_MAX_NUM; i += BITS_PER_WORD) + { + Ft_out32((UINTPTR)GIC_INTPENDCLR(Gicv3DistributorAddress, i), 0xffffffff); + //= 3) + { + Ft_out32((Gicv3SgiAddress + GICR_ICPENDR0_OFFSET), Swap32(ALL_PPI_INT_MASK | ALL_SGI_INT_MASK)); + } + + if (CpuId == 0 && g_FGicVersion >= 3) + { + Ft_out32(Gicv3SgiAddress + GICR_ICENABLER0_OFFSET, Swap32(ALL_PPI_INT_MASK)); + Ft_out32(Gicv3SgiAddress + GICR_ISENABLER0_OFFSET, ALL_SGI_INT_MASK); + } + return FST_SUCCESS; +} + +static ft_error_t FGicv3_CpuInit(void) +{ + u32 value = 0; + + value = sys_icc_sre_get(); + value |= ICC_SRE_SRE; + sys_icc_sre_set(value); + + sys_icc_ctlr_set(0); + sys_icc_pmr_set(GICV3_INTR_PRIO_MASK); + + sys_icc_igrpen0_set(1); + + if (sys_icc_igrpen0_get() != 1) + { + Ft_assertNonvoid(0); + } + + sys_icc_igrpen1_set(1); + if ((sys_icc_igrpen1_get() & 1) != 1) + { + Ft_assertNonvoid(0); + } + + sys_icc_bpr_set(0); //Gicv3DistributorAddress, CpuID); + FGicv3_RdInit(Config->Gicv3RdAddress, CpuID); + FGicv3_SgiInit(Config->Gicv3SgiAddress, CpuID); + FGicv3_CpuInit(); +} + +/** + * @name: FGicV3_Stop + * @msg: 此方法用于移除当前CPU 所有的中断 并且停止GIC。 + * @return {*} + */ +void FGicV3_Stop(FGicv3_Config_t *Config, u8 CpuID) +{ + u32 RegValue = 0; + u32 LocalCpuID = ((u32)0x1 << CpuID); + u32 InterruptID = 0; + u32 DistDisable = 0; + + if (NULL == Config) + { + Ft_assertVoid(0); + } + + /* if distributor is already disable , no need to do anything */ + RegValue = Ft_in32(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET); + + if ((RegValue & GICD_CTLR_MASK) == 0U) + { + return; + } + + LocalCpuID |= (LocalCpuID << 8U); + LocalCpuID |= (LocalCpuID << 16U); + + for (InterruptID = 32U; InterruptID < GIC_INT_MAX_NUM; InterruptID += 4) + { + RegValue = Ft_in32((UINTPTR)GIC_CPUTARG(Config->Gicv3DistributorAddress, InterruptID)); + + if ((RegValue != LocalCpuID) && (RegValue != 0)) + { + /* 如果其他 */ + DistDisable = 0; + } + + /* Remove current CPU from interrupt target register */ + RegValue &= (~LocalCpuID); + Ft_out32((UINTPTR)GIC_CPUTARG(Config->Gicv3DistributorAddress, InterruptID), RegValue); + } + + /* + * If GIC distributor is safe to be disabled, disable all the interrupt + * and then disable distributor. + */ + if (DistDisable == 1) + { + for (InterruptID = 0; InterruptID < GIC_INT_MAX_NUM; InterruptID += 32U) + { + Ft_out32((UINTPTR)GIC_INTENCLR(Config->Gicv3DistributorAddress, InterruptID), 0xFFFFFFFFU); + } + Ft_out32(Config->Gicv3DistributorAddress + GICD_CTLR_OFFSET, 0U); + } +} + +/** + * @name: FGicv3_InstanceInitialize + * @msg: + * @param {u32} InstanceId 对应的gic 编号 + * @return {*} + */ +s32 FGicv3_InstanceInitialize(FGicv3_Config_t *Config) +{ + s32 CpuId = 0; + + CpuId = FCpu_IdGet(); + + Gicv3_Init(Config, CpuId); + return FST_SUCCESS; +} + +/** + * @name: FGicv3_InterruptHandler + * @msg: + * @param {void} InstanceId,选择对应的gic 控制器 + * @return {*} + */ +void FGicv3_InterruptHandler(u32 CurrentLevel, u32 InstanceId) +{ + FGicv3_Config_t *Config; + FGicv3_VectorTableEntry_t *TablePtr; + + Config = &g_FGicv3_Config_tTable[(u32)InstanceId]; + /* Read the icc_iar register to identify the hightest priority interrupt ID + and make sure it is valid. */ + // Ft_printf("is vaild InstanceId %d \r\n", CurrentLevel); + if (GIC_INT_SPURIOUS == CurrentLevel) + { + goto _exit; + } + + if (GIC_INT_MAX_NUM < CurrentLevel) + { + goto _exit; + } + + /* + * Jump into the Isr handler . + */ + TablePtr = &(Config->HanderTable[CurrentLevel]); + if (NULL != TablePtr) + { + TablePtr->Handler(TablePtr->Args); + } + + if (ARM_GICV3_MSI_START < CurrentLevel && (CurrentLevel <= ARM_GICV3_MSI_START + ARM_GICV3_MSI_CNT)) + { + // arm_gicv3_msi_intr_clear(CurrentLevel); + } +_exit: + return; +} + +/** + * @name: FGicv3_RegisterHandler + * @msg: + * @param {u32} InstanceId 选择对应的gic 控制器 + * @param {Ft_FGicv3_InterruptHandler} InterruptHandler 将会被添加进入向量表中的中断函数 + * @param {void} *Args ,参数将会被传入到 InterruptHandler 中 + * @return {*} + */ +s32 FGicv3_RegisterHandler(FGicv3_Config_t *Config, u32 InterruptID, FGicv3_InterruptHandler_t InterruptHandler, void *Args) +{ + Ft_assertNonvoid(Config != NULL); + + if (InterruptID > FT_GICV3_VECTORTABLE_NUM) + { + return FST_FAILURE; + } + + if (NULL != InterruptHandler) + { + Config->HanderTable[InterruptID].Handler = InterruptHandler; + } + + if (NULL != Args) + { + Config->HanderTable[InterruptID].Args = Args; + } + + return FST_SUCCESS; +} + +/** + * @name: GicV3_LookupConfigByBaseAddress + * @msg: + * @param {*} + * @return {*} + */ +static FGicv3_Config_t *Gicv3_LookupConfigByBaseAddress(u32 GicBaseAddress) +{ + FGicv3_Config_t *Config = NULL; + u32 instance = 0; + + for (instance = 0; instance < FT_GICV3_INSTANCES_NUM; instance++) + { + if (g_FGicv3_Config_tTable[instance].Gicv3BaseAddress == GicBaseAddress) + Config = &g_FGicv3_Config_tTable[instance]; + break; + } + + return Config; +} + +/** + * @name: FGicV3_SetPriTrigTypeByDistAddr + * @msg: + * @param {u32} GicBaseAddress + * @param {u32} InterruptID + * @param { u8} Trigger 0b00 Corresponding interrupt is level-sensitive. 0b01 Corresponding interrupt is edge-triggered. + * @param { u8} Priorty + * @return {*} + */ +s32 FGicV3_SetPriTrigTypeByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 Trigger, u8 Priorty) +{ + FGicv3_Config_t *Config = NULL; + u32 RegValue = 0; + Ft_assertNonvoid(InterruptID < FT_GICV3_VECTORTABLE_NUM); + Ft_assertNonvoid(Trigger <= GIC_TRIGGER_EDGE_SENSITIVE); + Ft_assertNonvoid(Priorty <= GICV3_MAX_INTR_PRIO_VAL); + Config = Gicv3_LookupConfigByBaseAddress(GicBaseAddress); + Ft_assertNonvoid(Config != NULL); + + if (InterruptID < SPI_START_INT_NUM) + { + /* 读出当前对应32 位 4组中断优先级配置参数 ,并设置对应的中断优先级*/ + RegValue = Ft_in32((UINTPTR)GIR_PRIO(Config->Gicv3SgiAddress, InterruptID)); + RegValue &= ~(GICD_IPRIORITYR_MASK << ((InterruptID % 4U) * 8U)); + RegValue |= (u32)Priorty << ((InterruptID % 4U) * 8U); + Ft_out32((UINTPTR)GIR_PRIO(Config->Gicv3SgiAddress, InterruptID), RegValue); + } + else + { + /* 读出当前对应32 位 4组中断优先级配置参数 ,并设置对应的中断优先级*/ + RegValue = Ft_in32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, InterruptID)); + RegValue &= ~(GICD_IPRIORITYR_MASK << ((InterruptID % 4U) * 8U)); + RegValue |= (u32)Priorty << ((InterruptID % 4U) * 8U); + Ft_out32((UINTPTR)GIC_PRIO(Config->Gicv3DistributorAddress, InterruptID), RegValue); + + /* 读取出32位 16 组 中断触发源配置,并且进行设置*/ + RegValue = Ft_in32((UINTPTR)GIC_CONFIG(Config->Gicv3DistributorAddress, InterruptID)); + RegValue &= ~(GICD_ICFGR_MASK << ((InterruptID % 16U) * 2U)); + RegValue |= (u32)Trigger << ((InterruptID % 16U) * 2U); + Ft_out32((UINTPTR)GIC_CONFIG(Config->Gicv3DistributorAddress, InterruptID), RegValue); + } + return FST_SUCCESS; +} + +/** + * @name: FGicV3_GetPriTrigTypeByDistAddr + * @msg: + * @param {u32} GicBaseAddress + * @param {u32} InterruptID + * @param { u8} Trigger + * @param { u8} Priorty + * @return {*} + */ +void FGicV3_GetPriTrigTypeByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 *Trigger, u8 *Priorty) +{ + u32 RegValue = 0; + Ft_assertVoid(InterruptID < FT_GICV3_VECTORTABLE_NUM); + Ft_assertVoid(*Trigger <= GIC_TRIGGER_EDGE_SENSITIVE); + Ft_assertVoid(*Priorty <= GICV3_MAX_INTR_PRIO_VAL); + + /* + * 获取中断优先级 + */ + RegValue = Ft_in32((UINTPTR)GIC_PRIO(GicBaseAddress, InterruptID)); + RegValue = RegValue >> ((InterruptID % 4U) * 8U); + *Priorty = (u8)(RegValue & GICD_IPRIORITYR_MASK); + + /* + * 获取当前中断触发信息 + */ + RegValue = Ft_in32((UINTPTR)GIC_CONFIG(GicBaseAddress, InterruptID)); + RegValue = RegValue >> ((InterruptID % 16U) * 2U); + *Trigger = (u8)RegValue & GICD_ICFGR_MASK; +} + +/** + * @name: FGicV3_InterruptUnmapByDistAddr + * @msg: 清除寄存器中的CPU中的 target PEs + * @param {u32} GicBaseAddress + * @param {u32} InterruptID + * @param { u8} cpu + * @return {*} + */ +void FGicV3_InterruptUnmapByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 CpuID) +{ + u32 RegValue = 0; + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + RegValue = Ft_in32((UINTPTR)GIC_CPUTARG(GicBaseAddress, InterruptID)); + RegValue &= ~(1U << (((InterruptID % 4) * 8U) + CpuID)); + Ft_out32((UINTPTR)GIC_CPUTARG(GicBaseAddress, InterruptID), RegValue); +} + +/** + * @name: + * @msg: + * @param {Ft_Gicv3_Config} *Config + * @param {u32} InterruptID + * @return {*} + */ +void FGicV3_DisableInterrupt(FGicv3_Config_t *Config, u32 InterruptID) +{ + u32 RegValue; + s32 CpuId = 0; + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + + // RegValue = 0x1U << (InterruptID % 32U); + + if (InterruptID < SPI_START_INT_NUM) + { + CpuId = FCpu_IdGet(); + RegValue = Ft_in32(GICR_INTENCLR_CPU(Config->Gicv3SgiAddress, CpuId)); + RegValue |= (0x1U << (InterruptID % 32U)); + Ft_out32(GICR_INTENCLR_CPU(Config->Gicv3SgiAddress, CpuId), RegValue); + } + else + { + RegValue = Ft_in32((UINTPTR)GIC_INTENCLR(Config->Gicv3SgiAddress, InterruptID)); + RegValue |= (0x1U << (InterruptID % 32U)); + Ft_out32((UINTPTR)GIC_INTENCLR(Config->Gicv3SgiAddress, InterruptID), RegValue); + } +} + +/** + * @name: FGicV3_EnableInterrupt + * @msg: + * @param {u32} GicBaseAddress + * @param {u32} InterruptID + * @return {*} + */ +void FGicV3_EnableInterrupt(FGicv3_Config_t *Config, u32 InterruptID) +{ + u32 RegValue; + s32 CpuId = 0; + Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); + + if (InterruptID < SPI_START_INT_NUM) + { + CpuId = FCpu_IdGet(); + // Ft_printf("cpuid is %x \r\n", CpuId); + // Ft_printf("before GICR_ISENABLER0_OFFSET %x \r\n", Ft_in32(GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuId))); + RegValue = Ft_in32(GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuId)); + RegValue |= (0x1U << (InterruptID % 32U)); + Ft_out32(GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuId), RegValue); + // Ft_printf("GICR_ISENABLER0_OFFSET %x \r\n", Ft_in32(GICR_INTENABLE_CPU(Config->Gicv3SgiAddress, CpuId))); + } + else + { + RegValue = Ft_in32((UINTPTR)GICD_ISENABLER(Config->Gicv3DistributorAddress, InterruptID)); + RegValue |= (0x1U << (InterruptID % 32U)); + Ft_out32((UINTPTR)GICD_ISENABLER(Config->Gicv3DistributorAddress, InterruptID), RegValue); + } +} + +/** + * @name: FGicv3_Ack + * @msg: Writes to this register to inform the CPU interface that it has completed the processing of the + specified Group 1 interrupt. + * @in param {u32} InterruptID + * @in param {s32} SrcCpu + * @return {*} + */ +ft_error_t FGicv3_Ack(u32 InterruptID, s32 CpuId) +{ + sys_icc_eoir1_set(InterruptID); + return FST_SUCCESS; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.h b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.h new file mode 100644 index 0000000000..f9c9f06acf --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.h @@ -0,0 +1,248 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-26 10:49:20 + * @Description:  This files is for gic v2.0 register implementation + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef GICV3_HW_H +#define GICV3_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_parameters.h" +#include "ft_gicv3.h" + /* interrupt priority */ + +#define SHIFT_PER_WORD 5 +#define BITS_PER_WORD 32 +#define CONFIGS_PER_WORD 16 +#define PRIOS_PER_WORD 4 +#define TARGETS_PER_WORD 4 +#define NWORD(bitnum) (bitnum / 32) +#define BIT(bitnum) (1 << (bitnum % BITS_PER_WORD)) + +#define GIC_PRIO(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_IPRIORITYR_OFFSET + \ + (0x4 * (Periph / PRIOS_PER_WORD)))) + +#define GIR_PRIO(gic_redist_addr, Periph) ((volatile u32 *)(gic_redist_addr + GICR_IPRIORITYR_OFFSET + \ + (0x4 * (Periph / PRIOS_PER_WORD)))) + + /* clear interrupt enable */ + +#define GIC_INTENCLR(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_ICENABLE_OFFSET + \ + (0x4 * NWORD(Periph)))) + + /* set interrupt enable */ +#define GICD_ISENABLER(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_ISENABLER_OFFSET + \ + (0x4 * NWORD(Periph)))) + + /* clear pending interrupt */ + +#define GIC_INTPENDCLR(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_ICPENDR_OFFSET + \ + (0x4 * NWORD(Periph)))) + + /* CPU target */ + +#define GIC_CPUTARG(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_ITARGETSR_OFFSET + \ + (0x4 * (Periph / TARGETS_PER_WORD)))) + + /* Interrupt Routing */ + +#define GIC_INTRPUTE_LOW(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_IROUTER_E_OFFSET + \ + (0x8 * (Periph)))) + +#define GIC_INTRPUTE_HIGH(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_IROUTER_E_OFFSET + \ + (0x8 * (Periph)) + 4)) /* interrupt configuration */ + +#define GIC_CONFIG(gic_dist_addr, Periph) ((volatile u32 *)(gic_dist_addr + GICD_ICFGR_OFFSET + \ + (0x4 * (Periph / CONFIGS_PER_WORD)))) + +#define GIC_TRIGGER_LEVEL_SENSITIVE 0 +#define GIC_TRIGGER_EDGE_SENSITIVE 1 + + /* ARM GIC interrupt distributor and CPU interface register access macros */ + +#define GIC_CPU_DIR_DEFAULT 0x01010101 /* all interrupts are directed to CPU0 */ +#define GIC_V3_CPU_DIR_DEFAULT_LOW 0x0llu /* all interrupts are directed to CPU0 */ +#define GIC_V3_CPU_DIR_DEFAULT_HIGH 0 +#define GIC_CONTROL_ENABLE 0x01 + +#define GIC_INT_ALL_ENABLED 0xFF /* priority 0-0xFF can run */ +#define GIC_INT_ALL_DISABLED 0x00 /* nothing higher than 0 hence disabled */ +#define GIC_INT_SPURIOUS 0x3FF /* no interrupt currently */ +#define GIC_INT_ONEMINUS_HIGH 0x55555555 /* interrupt config to 1-N, High */ +#define GIC_CPU_BINP_DEFAULT 0x07 /* split all priority to subpriority */ +#define GIC_CPU_BINP_PREEMPT_VAL 0x1 /* split all priority to group-priority */ +#define GIC_CPU_BINP_NONPREEMPT_VAL 0x07 /* split all priority to sub-priority */ +#define GIC_CPU_CONTROL_ENABLE 0x1 /* enable the processor interface */ +#define GIC_CPU_ALL_ENABLED 0xFF /* priority 0-E can run */ +#define GIC_CPU_ALL_DISABLED 0x00 /* nothing higher than 0 */ +#define GIC_SGI_SRC_CPU_ID_MASK 0x1C00 +#define GIC_INT_HIGHEST_PRIORITY 0x0 /* the highest priority for interrupts */ +#define GIC_INT_LOWEST_PRIORITY 0x1F /* the lowest priority for interrupts */ +#define GIC_INT_PRIORITY_SHIFT 0x8 +#define GIC_INT_PRIORITY_MASK 0xFF + +#define GIC_INT_TRIGGER_SHIFT 0x2 + /************ Distributor register map ****************/ + +#define GICD_CTLR_OFFSET 0x00000000U /* Distributor Control Register */ +#define GICD_TYPER_OFFSET 0x00000004U /* Interrupt Controller Type Register */ +#define GICD_IIDR_OFFSET 0x00000008U /* Distributor Implementer Identification Register */ +#define GICD_TYPER2_OFFSET 0x0000000CU /* Interrupt controller Type Register 2 */ +#define GICD_STATUSR_OFFSET 0x00000010U /* Error Reporting Status Register, optional */ +#define GICD_SETSPI_NSR_OFFSET 0x00000040U /* Set SPI Register */ +#define GICD_CLRSPI_NSR_OFFSET 0x00000048U /* Clear SPI Register */ +#define GICD_SETSPI_SR_OFFSET 0x00000050U /* Set SPI, Secure Register */ +#define GICD_CLRSPI_SR_OFFSET 0x00000058U /* Clear SPI, Secure Register */ +#define GICD_IGROUPR_OFFSET 0x00000080U /* Interrupt Group Registers */ +#define GICD_ISENABLER_OFFSET 0x00000100U /* Interrupt Set-Enable Registers */ +#define GICD_ICENABLE_OFFSET 0x00000180U /* Interrupt Clear-Enable Registers */ +#define GICD_ISPENDR_OFFSET 0x00000200U /* Interrupt Set-Pending Registers */ +#define GICD_ICPENDR_OFFSET 0x00000280U /* Interrupt Clear-Pending Registers */ +#define GICD_ISACTIVER_OFFSET 0x00000300U /* Interrupt Set-Active Registers */ +#define GICD_ICACTIVER_OFFSET 0x00000380U /* Interrupt Clear-Active Registers */ +#define GICD_IPRIORITYR_OFFSET 0x00000400U /* Interrupt Priority Registers */ +#define GICD_ITARGETSR_OFFSET 0x00000800U /* Interrupt Processor Targets Registers */ +#define GICD_ICFGR_OFFSET 0x00000C00U /* Interrupt Configuration Registers */ +#define GICD_IGRPMODR_OFFSET 0x00000D00U /* Interrupt Group Modifier Registers */ +#define GICD_NSACR_OFFSET 0x000000E00U /* Non-secure Access Control Registers */ +#define GICD_SGIR_OFFSET 0x000000F00U /* Software Generated Interrupt Register */ +#define GICD_CPENDSGIR_OFFSET 0x000000F10U /* SGI Clear-Pending Registers */ +#define GICD_SPENDSGIR_OFFSET 0x000000F20U /* SGI Set-Pending Registers */ +#define GICD_IGROUPR_E_OFFSET 0x000001000U /* Interrupt Group Registers for extended SPI range */ +#define GICD_ISENABLER_E_OFFSET 0x000001200U /* Interrupt Set-Enable for extended SPI range */ +#define GICD_ICENABLER_E_OFFSET 0x000001400U /* Interrupt Clear-Enable for extended SPI range */ +#define GICD_ISPENDR_E_OFFSET 0x000001600U /* Interrupt Set-Pend for extended SPI range */ +#define GICD_ICPENDR_E_OFFSET 0x000001800U /* Interrupt Clear-Pend for extended SPI range */ +#define GICD_ISACTIVER_E_OFFSET 0x000001A00U /* Interrupt Set-Active for extended SPI range */ +#define GICD_ICACTIVER_E_OFFSET 0x000001C00U /* Interrupt Clear-Active for extended SPI range */ +#define GICD_IPRIORITYR_E_OFFSET 0x000002000U /* Interrupt Priority for extended SPI range */ +#define GICD_ICFGR_E_OFFSET 0x000003000U /* Extended SPI Configuration Register */ +#define GICD_IGRPMODR_E_OFFSET 0x000003400U /* Interrupt Group Modifier for extended SPI range */ +#define GICD_NSACR_E_OFFSET 0x000003600U /* Non-secure Access Control Registers for extended SPI range */ +#define GICD_IROUTER_E_OFFSET 0x000006000U /* Interrupt Routing Registers */ +#define GICD_IROUTER_E_E_OFFSET 0x000008000U /* Interrupt Routing Registers for extended SPI range */ + + /************ physical LPI Redistributor register map ****************/ + +#define GICR_CTLR_OFFSET 0x0000 /*See the register description Redistributor Control Register*/ +#define GICR_IIDR_OFFSET 0x0004 /*IMPLEMENTATION DEFINED Implementer Identification Register*/ +#define GICR_TYPER_OFFSET 0x0008 /*IMPLEMENTATION DEFINED Redistributor Type Register*/ +#define GICR_STATUSR_OFFSET 0x0010 /*0x0000 0000 Error Reporting Status Register, optional*/ +#define GICR_WAKER_OFFSET 0x0014 /*See the register description Redistributor Wake Register*/ +#define GICR_MPAMIDR_OFFSET 0x0018 /*- Report maximum PARTID and PMG Register*/ +#define GICR_PARTIDR_OFFSET 0x001C /*0 Set PARTID and PMG Register*/ +#define GICR_SETLPIRa_OFFSET 0x0040 /*- Set LPI Pending Register*/ +#define GICR_CLRLPIRa_OFFSET 0x0048 /*- Clear LPI Pending Register*/ +#define GICR_PROPBASER_OFFSET 0x0070 /*- Redistributor Properties Base Address Register*/ +#define GICR_PENDBASER_OFFSET 0x0078 /*- Redistributor LPI Pending Table Base Address*/ +#define GICR_INVLPIRA_OFFSET 0x00A0 /*Redistributor Invalidate LPI Register*/ +#define GICR_INVALLRA_OFFSET 0x00B0 /*Redistributor Invalidate All Register */ +#define GICR_SYNCRA_OFFSET 0x00C0 /*Redistributor Synchronize Register*/ + +#define GICR_WAKER_CPU(gicr_addr, cpu) ((gicr_addr + cpu * (2 << 16) + GICR_WAKER_OFFSET)) + +/******************* SGI and PPI Redistributor register map ************************/ +#define GICR_IGROUPR0_OFFSET 0x0080 /* Interrupt Group Register 0*/ +#define GICR_ISENABLER0_OFFSET 0x0100 /* DEFINED Interrupt Set-Enable Register 0*/ +#define GICR_ICENABLER0_OFFSET 0x0180 /* DEFINED Interrupt Clear-Enable Register 0*/ +#define GICR_ISPENDR0_OFFSET 0x0200 /* Interrupt Set-Pend Register 0*/ +#define GICR_ICPENDR0_OFFSET 0x0280 /* Interrupt Clear-Pend Register 0*/ +#define GICR_ISACTIVER0_OFFSET 0x0300 /* Interrupt Set-Active Register 0*/ +#define GICR_ICACTIVER0_OFFSET 0x0380 /* Interrupt Clear-Active Register 0*/ +#define GICR_IPRIORITYR_OFFSET 0x0400 /* Interrupt Priority Registers*/ +#define GICR_IGROUPR_E_OFFSET 0x0084 /*Interrupt Group Registers for extended PPI range*/ +#define GICR_ISENABLER_E_OFFSET 0x0104 /* Interrupt Set-Enable for extended PPI range*/ +#define GICR_ICENABLER_E_OFFSET 0x0184 /* Interrupt Clear-Enable for extended PPI range*/ +#define GICR_ISPENDR_E_OFFSET 0x0204 /* Interrupt Set-Pend for extended PPI range*/ +#define GICR_ICPENDR_E_OFFSET 0x0284 /* Interrupt Clear-Pend for extended PPI range*/ +#define GICR_ISACTIVER_E_OFFSET 0x0304 /* Interrupt Set-Active for extended PPI range*/ +#define GICR_ICACTIVER_E_OFFSET 0x0384 /* Interrput Clear-Active for extended PPI range*/ +#define GICR_IPRIORITYR_E_OFFSET 0x0420 /* Interrupt Priority for extended PPI range*/ +#define GICR_ICFGR_E_OFFSET 0x0C08 /* Extended PPI Configuration Register */ +#define GICR_IGRPMODR_E_OFFSET 0x0D04 /* Interrupt Group Modifier for extended PPI range */ +#define GICR_ICFGR0_OFFSET 0x0C00 /*SGI Configuration Register*/ +#define GICR_ICFGR1_OFFSET 0x0C04 /*PPI Configuration Register*/ +#define GICR_IGRPMODR0_OFFSET 0x0D00 /*Interrupt Group Modifier Register 0*/ +#define GICR_NSACR_OFFSET 0x0E00 /* Secure Access Control Register*/ + +#define GICR_INTENABLE_CPU(gic_dist_addr, cpu) ((gic_dist_addr + cpu * (2 << 16) + GICR_ISENABLER0_OFFSET)) +#define GICR_INTENCLR_CPU(gic_dist_addr, cpu) ((gic_dist_addr + cpu * (2 << 16) + GICR_ICENABLER0_OFFSET)) + +/****************** CPU interface register map *************************/ +#ifdef CPU_GICC_SUPPORT + +#define GICC_CTLR_OFFSET 0x0000 /*See the register description CPU Interface Control Register*/ +#define GICC_PMR_OFFSET 0x0004 /* Interrupt Priority Mask Register*/ +#define GICC_BPR_OFFSET 0x0008 /* Binary Point Register*/ +#define GICC_IAR_OFFSET 0x000C /* Interrupt Acknowledge Register*/ +#define GICC_EOIR_OFFSET 0x0010 /* End of Interrupt Register*/ +#define GICC_RPR_OFFSET 0x0014 /* Running Priority Register*/ +#define GICC_HPPIR_OFFSET 0x0018 /* Highest Priority Pending Interrupt Register*/ +#define GICC_ABPR_OFFSET 0x001C /* Aliased Binary Point Register*/ +#define GICC_AIAR_OFFSET 0x0020 /* Aliased Interrupt Acknowledge Register*/ +#define GICC_AEOIR_OFFSET 0x0024 /* Aliased End of Interrupt Register*/ +#define GICC_AHPPIR_OFFSET 0x0028 /* Aliased Highest Priority Pending Interrupt Register*/ +#define GICC_STATUSR_OFFSET 0x002C /* Error Reporting Status Register, optional*/ +#define GICC_APR_OFFSET 0x00D0 /* Active Priorities Registers*/ +#define GICC_NSAPR_OFFSET 0x00E0 /* Non-secure Active Priorities Registers*/ +#define GICC_IIDR_OFFSET 0x00FC /* CPU Interface Identification Register */ +#define GICC_DIR_OFFSET 0x1000 /* Deactivate Interrupt Register */ + +#endif + + /** Redistributor Wake Register **/ + +#define GICC_WAKER_PROCESSORSLEEP_IN (1U << 1) +#define GICC_WAKER_PROCESSORSLEEP_NOT_IN (0U << 1) + +#define GICC_WAKER_CHILDRENASLEEP_ACTIVE (0U << 2) +#define GICC_WAKER_CHILDRENASLEEP_QUIESCENT (1U << 2) + + /** GICR_ISENABLER0 **/ + +#define ALL_PPI_INT_MASK 0xFFFF0000 /* bit field for all PPI interrupts */ +#define ALL_SGI_INT_MASK 0x0000FFFF /* bit field for all SGI interrupts */ + + /** ICC_SRE **/ + +#define ICC_SRE_SRE (1U << 0) + +/** GICD_IPRIORITYR **/ +#define GICD_IPRIORITYR_MASK 0xffU + +/** GICD_ICFGR **/ +#define GICD_ICFGR_MASK 0x03 + + /** GICD_CTLR **/ + +#define GICD_CTLR_MASK 1 + + /**************************** Function Prototypes ****************************************/ + s32 FGicv3_InstanceInitialize(FGicv3_Config_t *Config); + void FGicv3_InterruptHandler(u32 CurrentLevel, u32 InstanceId); + s32 FGicv3_RegisterHandler(FGicv3_Config_t *Config, u32 InterruptID, FGicv3_InterruptHandler_t InterruptHandler, void *Args); + s32 FGicV3_SetPriTrigTypeByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 Trigger, u8 Priorty); + void FGicV3_GetPriTrigTypeByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 *Trigger, u8 *Priorty); + void FGicV3_InterruptUnmapByDistAddr(u32 GicBaseAddress, u32 InterruptID, u8 CpuID); + void FGicV3_DisableInterrupt(FGicv3_Config_t *Config, u32 InterruptID); + void FGicV3_EnableInterrupt(FGicv3_Config_t *Config, u32 InterruptID); + void FGicV3_Stop(FGicv3_Config_t *Config, u8 CpuID); + +#ifdef __cplusplus +} +#endif + +#endif // \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_sinit.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_sinit.c new file mode 100644 index 0000000000..c8ee7015ff --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_sinit.c @@ -0,0 +1,47 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 16:28:25 + * @Description:  This files is for gic v2.0 static init + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_parameters.h" +#include "ft_gicv3.h" + +/************************** Variable Definitions *****************************/ +extern FGicv3_Config_t g_FGicv3_Config_tTable[FT_GICV3_INSTANCES_NUM]; + +/** + * @name: Ft_Gicv3_lookupConfig + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {u32} InstanceId + */ +FGicv3_Config_t *FGicv3_LookupConfig(u32 InstanceId) +{ + FGicv3_Config_t *Config = NULL; + u32 Index = 0; + + for (Index = 0; Index < FT_GICV3_INSTANCES_NUM; Index++) + { + if (g_FGicv3_Config_tTable[Index].InstanceId == InstanceId) + { + Config = &g_FGicv3_Config_tTable[Index]; + break; + } + } + + return Config; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_test.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_test.c new file mode 100644 index 0000000000..8b5a90e50e --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_test.c @@ -0,0 +1,13 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:35:03 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.c new file mode 100644 index 0000000000..fae9e4b7e1 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.c @@ -0,0 +1,171 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:23:19 + * @Description:  This files is for gmac ctrl + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac.h" +#include "ft_gmac_hw.h" +#include "ft_assert.h" +#include "ft_io.h" +#include "ft_status.h" +#include "ft_printf.h" +#include "ft_parameters.h" + +static void Ft_Gmac_StubHandler(void *args) +{ + Ft_assertVoidAlways(); +} + +static void Ft_Gmac_StubErrorHandler(void *CallBackRef, u32 ErrorWord) +{ + // Ft_assertVoidAlways(); +} + +s32 Ft_Gmac_HwInitialize(Ft_Gmac_t *Gmac) +{ + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + return FGmac_InitializeHw(&Gmac->Config); +} + +/** + * @name: Ft_GmacCfgInitialize + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @param {FGmac_Config_t} *Config + * @return {*} + */ +s32 Ft_GmacCfgInitialize(Ft_Gmac_t *Gmac, FGmac_Config_t *Config) +{ + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Config != NULL); + + Gmac->Config = *Config; + Gmac->IsReady = FT_COMPONENT_IS_READLY; + + FGmac_SetHandler(Gmac, FT_GMAC_TX_COMPLETE_CB_ID, Ft_Gmac_StubHandler, Gmac); + FGmac_SetHandler(Gmac, FT_GMAC_RX_COMPLETE_CB_ID, Ft_Gmac_StubHandler, Gmac); + FGmac_SetHandler(Gmac, FT_GMAC_DMA_ERROR_CB_ID, Ft_Gmac_StubErrorHandler, Gmac); + + return FST_SUCCESS; +} + +/** + * @name: Ft_Gmac_Start + * @msg: Enable GMAC MAC and DMA rx/tx + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +s32 Ft_Gmac_Start(Ft_Gmac_t *Gmac) +{ + FGmac_Config_t *Config = NULL; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + Config = &Gmac->Config; + + FGmac_DMAReceptionTransmissionEnable(Config); + FGmac_ReceptionTransmissionEnable(Config); + + /* Clear Tx and Rx process stopped flags */ + Ft_out32(Gmac->Config.BaseAddress + DMA_INTR_ENA_OFFSET, DMA_INTR_ENA_RIE | DMA_INTR_ENA_AIE | DMA_INTR_ENA_NIE); + + return FST_SUCCESS; +} + +/** + * @name: Ft_Gmac_Stop + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +s32 Ft_Gmac_Stop(Ft_Gmac_t *Gmac) +{ + FGmac_Config_t *Config = NULL; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + Config = &Gmac->Config; + + FGmac_TransmissionDisable(Config); + FGmac_ReceptionDisable(Config); + FGmac_DMATransmissionDisable(Config); + FGmac_DMAReceptionDisable(Config); + return FST_SUCCESS; +} + +void Ft_Gmac_Phy_Debug(Ft_Gmac_t *Gmac) +{ + // FGmac_Config_t *Config; + // Config = &Gmac->Config; + // Gmac_WritePHYRegister(Config, PHY_DEBUG_ADDR_OFFSET, 0x12); + // FGmac_ReadPHYRegister(Config, PHY_DEBUG_DATA_OFFSET, &RegValue); + + // Ft_printf("PHY_DEBUG_OFFSET 0x12 %x \r\n", RegValue); + // Ft_assertVoid(Gmac_WritePHYRegister(Config, PHY_DEBUG_ADDR_OFFSET, 0x10) == FST_SUCCESS); + // Ft_assertVoid(FGmac_ReadPHYRegister(Config, 0x1f, &RegValue) == FST_SUCCESS); + // Ft_printf("chip check 0x1f %x \r\n", RegValue); + // Ft_printf("chip check GMAC_CONTROL_OFFSET %x \r\n", Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET)); + + // Ft_printf("MAC 配置寄存器 %x \r\n", Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET)); + // Ft_printf("中断状态寄存器 %x \r\n", Ft_in32(Config->BaseAddress + GMAC_ISR_STATUS_OFFSET)); + // Ft_printf("中断屏蔽寄存器 %x \r\n", Ft_in32(Config->BaseAddress + GMAC_ISR_MASK_OFFSET)); + // Ft_printf("寄存器 54 %x \r\n", Ft_in32(Config->BaseAddress + GMAC_MAC_MAC_PHY_STATUS)); + + // Ft_printf("DMA 总线模式寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_BUS_MODE_OFFSET)); + // Ft_printf("DMA 状态寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_STATUS_OFFSET)); + // Ft_printf("DMA 操作模式寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_OP_OFFSET)); + // Ft_printf("DMA 中断使能寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_INTR_ENA_OFFSET)); + // Ft_printf("DMA 中断遮蔽寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_MISSED_FRAME_CTR_OFFSET)); + // Ft_printf("DMA AXI 总线模式寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_AXI_BUS_MOD_OFFSET)); + // Ft_printf("DMA AXI 状态寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_AXI_BUS_STATUS_OFFSET)); + // Ft_printf("接收描述符列表地址寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_RCV_BASE_ADDR_OFFSET)); + // Ft_printf("发送描述符列表地址寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_TX_BASE_ADDR_OFFSET)); + + // Ft_printf("当前主机接收描述符寄存器 %x \r\n", Ft_in32(Config->BaseAddress + 0x104c)); + // Ft_printf("当前接收缓冲区地址 %x \r\n", Ft_in32(Config->BaseAddress + 0x1054)); + + // Ft_printf("当前主机发送描述符寄存器 %x \r\n", Ft_in32(Config->BaseAddress + 0x1048)); + // Ft_printf("当前发送缓冲区地址 %x \r\n", Ft_in32(Config->BaseAddress + 0x1050)); + + // Ft_printf("接收描述符列表地址寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_RCV_BASE_ADDR_OFFSET)); + // Ft_printf("发送描述符列表地址寄存器 %x \r\n", Ft_in32(Config->BaseAddress + DMA_TX_BASE_ADDR_OFFSET)); + + Ft_printf("\r\n ****************************** \r\n"); +} + +/** + * @name: Ft_Gmac_UseDefaultMacAddr + * @msg: set default mac address + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {Ft_Gmac_t} *Gmac + * @param {u8} *MacAddr + */ +void Ft_Gmac_UseDefaultMacAddr(Ft_Gmac_t *Gmac, u8 *MacAddr) +{ + u32 MacAddrLo; + u32 MacAddrHi; + FGmac_Config_t *pConfig; + Ft_assertNoneReturn(Gmac != NULL); + pConfig = &Gmac->Config; + + MacAddrLo = Ft_in32(pConfig->BaseAddress + GMAC_MAC_ADDR0_LOWER16BIT_OFFSET); + MacAddrHi = Ft_in32(pConfig->BaseAddress + GMAC_MAC_ADDR0_UPPER16BIT_OFFSET); + + MacAddr[0] = MacAddrLo & 0xFF; + MacAddr[1] = (MacAddrLo >> 8) & 0xFF; + MacAddr[2] = (MacAddrLo >> 16) & 0xFF; + MacAddr[3] = (MacAddrLo >> 24) & 0xFF; + MacAddr[4] = MacAddrHi & 0xFF; + MacAddr[5] = (MacAddrHi >> 8) & 0xFF; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h new file mode 100644 index 0000000000..5d9d2f149c --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h @@ -0,0 +1,311 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-19 17:53:35 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 1.00a hh 2021-02-05 First release + */ + +#ifndef FT_GMAC_H +#define FT_GMAC_H + +#include "ft_types.h" +#define GMAC_INC_DESC(x, y) (x) = (((x) + 1) % y) +/** + * @name: Callback invoked when frame(s) have been sent or received in interrupt + * driven DMA mode .to set the send callback ,invoke FGmac_SetHandler() + * @msg: + * @param {void} *CallBackRef + * @return {*} + */ +typedef void (*FGmac_IntrCallback_t)(void *CallBackRef); + +/** + * @name: FGmac_ErrIntrCallback_t + * @msg: Callback whem asynchronous error occurs. To set this callback, invoke + * FGmac_ErrIntrCallback_t . + * @param {void} *CallBackRef + * @param {u32} ErrorWord definition varies with Error + * @return {*} + */ +typedef void (*FGmac_ErrIntrCallback_t)(void *CallBackRef, u32 ErrorWord); + +/** + * + * + */ +typedef void (*Ft_Gmac_MacPhyStatus_Callback)(void *CallBackRef, u32 MacPhyStatus); + +/** GMAC_SPEED */ +#define GMAC_SPEED_10M 0x00000001U +#define GMAC_SPEED_100M 0x00000002U +#define GMAC_SPEED_1000M 0x00000004U + +#define IS_RIGHT_SPEED() + +/* GMAC_AutoNegotiation */ +#define GMAC_AUTONEGOTIATION_ENABLE 0x00000001U +#define GMAC_AUTONEGOTIATION_DISABLE 0x00000000U + +/* GMAC_Duplex_Mode */ +#define GMAC_MODE_FULLDUPLEX 0x00000001U +#define GMAC_MODE_HALFDUPLEX 0x00000000U + +/* GMAC_Rx_Mode */ +#define GMAC_RXPOLLING_MODE 0x00000000U +#define GMAC_RXINTERRUPT_MODE 0x00000001U + +/* GMAC_Checksum_Mode */ +#define GMAC_CHECKSUM_BY_SOFTWARE 0x00000000U +#define GMAC_CHECKSUM_BY_HARDWARE 0x00000001U + +/* GMAC_Media_Interface */ +#define GMAC_MEDIA_INTERFACE_RGMII 0x00000000U +#define GMAC_MEDIA_INTERFACE_MII 0x00000001U + +/* Gmac Error value */ + +#define GMAC_ERROR_TRANSMIT_PROCESS_STOPPED 0x00000001U +#define GMAC_ERROR_TRANSMIT_UNAVAILABLE_STATUS 0x00000002U +#define GMAC_ERROR_TRANSMIT_JABBER_TIMEOUT 0x00000004U +#define GMAC_ERROR_RECEIVE_FIFO_OVERFLOW 0x00000008U +#define GMAC_ERROR_TRANSMIT_UNDERFLOW 0x00000010U +#define GMAC_ERROR_RECEIVE_BUFFER_UNAVAILABLE 0x00000020U +#define GMAC_ERROR_RECEIVE_PROCESS_STOPPED 0x00000040U +#define GMAC_ERROR_RECEIVE_WATCHDOG_TIMEOUT 0x00000080U +#define GMAC_ERROR_EARLY_TRANSMIT_INTERRUPT 0x000000100U +#define GMAC_ERROR_FATAL_BUS_ERROR 0x000000200U +#define GMAC_ERROR_UNDEFINED 0x000000400U + +typedef enum +{ + FT_GMAC_TX_COMPLETE_CB_ID = 0x01U, /*!< Gmac Tx Complete Callback ID */ + FT_GMAC_RX_COMPLETE_CB_ID = 0x02U, /*!< Gmac Rx Complete Callback ID */ + FT_GMAC_DMA_ERROR_CB_ID = 0x03U, /*!< Gmac DMA Error Callback ID */ + FT_GMAC_MAC_PHY_STATUS_CB_ID = 0x04U, /*!< */ +} FGmac_IsrCallbackSelect_t; + +/* Gmac DMA Descriptors data structure definition */ + +typedef struct +{ + volatile u32 Status; /*!< Status */ + u32 Control; /*!< Control and Buffer1, Buffer2 lengths */ + u32 Buffer1Addr; /*!< Buffer1 address pointer */ + u32 Buffer2NextDescAddr; /*!< Buffer2 or next descriptor address pointer */ +} FGmac_DmaDesc_t; + +/* Received Frame Informations structure definition */ +typedef struct +{ + FGmac_DmaDesc_t *FSRxDesc; /*!< First Segment Rx Desc */ + FGmac_DmaDesc_t *LSRxDesc; /*!< Last Segment Rx Desc */ + u32 SegCount; /*!< Segment count */ + u32 length; /*!< Frame length */ + u32 buffer; /*!< Frame buffer */ +} FGmac_DmaRxFrameInfos; + +typedef struct +{ + u32 InstanceId; /* Id of device */ + uintptr_t CommonAddress; /* Gmac Common Register */ + uintptr_t BaseAddress; /* Physical base address of Mac Private Address */ + u32 IRQ_NUM; + u32 IRQPriority; + s32 PhyAddr; /* Phy Ic Addre1 ,-1 is need to auto check*/ + u32 clkMDC; /* MDC clock access PHY. [1.0MHz ~2.5MHz] */ + u32 AutoNegotiation; /* Selects or not the AutoNegotiation mode for the external PHY + The AutoNegotiation allows an automatic setting of the Speed (10/100/1000Mbps) + and the mode (half/full-duplex). + This parameter can be a value of @ref GMAC_AutoNegotiation */ + u32 Speed; /* Sets the Ethernet speed: 10/100/1000 Mbps. + This parameter can be a value of @ref GMAC_SPEED */ + u32 DuplexMode; /* Selects the MAC duplex mode: Half-Duplex or Full-Duplex mode + This parameter can be a value of @ref GMAC_Duplex_Mode */ + u32 RxMode; /* Selects the Ethernet Rx mode: Polling mode, Interrupt mode. + This parameter can be a value of @ref GMAC_Rx_Mode */ + u32 ChecksumMode; /* Selects if the checksum is check by hardware or by software. + This parameter can be a value of @ref GMAC_Checksum_Mode */ + u32 MediaInterface; /* Selects the media-independent interface or the reduced media-independent interface. + This parameter can be a value of @ref GMAC_Media_Interface */ + u8 MacAddr[6]; /* 6 bytes Mac address */ + +} FGmac_Config_t; + +/* Only for Dma ring structure */ +struct DescRingData +{ + u32 DescIndex; /* For Current Desc position */ + u32 DescBufIndex; /* For Current Desc buffer buf position */ + u32 DescMaxNumber; /* Max Number for Desc and Desc buffer */ + u8 *DescBufBase; /* Desc buffer Base */ +}; + +typedef struct +{ + FGmac_Config_t Config; /* Hardware configuration */ + u32 IsReady; /* Device is initialised and ready */ + u32 Options; /* Current options word */ + + FGmac_DmaDesc_t *RxDesc; /*!< Rx descriptor to Get */ + struct DescRingData RxDescRingData; + FGmac_DmaDesc_t *TxDesc; /*!< Tx descriptor to Set */ + struct DescRingData TxDescRingData; + + FGmac_IntrCallback_t SendHandler; + FGmac_IntrCallback_t RecvHandler; + Ft_Gmac_MacPhyStatus_Callback StatusHandler; + void *SendArgs; + void *RecvArgs; + void *StatusArgs; + FGmac_ErrIntrCallback_t ErrorHandler; + void *ErrorArgs; + + FGmac_DmaRxFrameInfos DMARxFrameInfos; /* Only for chain structure */ + +} Ft_Gmac_t; + +/** + * @name: FGmac_DmaRxDescRingInit + * @msg: 化标准 DMA 接收描述符 在ring结构下的初始化。 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @param {FGmac_DmaDesc_t} *DMARxDescTab 用户定义的静态 DMA 接收描述符表格 + * @param {u8} *RxBuff 用户定义的用于匹配 DMA 接收描述符表格的缓冲区 + * @param {u32} DescBufPerLength 每个缓冲区的大小 + * @param {u32} RxBuffCount 缓冲区的总数量 + * @return {s32} Common_status 参数。 + */ +s32 FGmac_DmaRxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 DescBufPerLength, u32 TxBuffCount); + +/** + * @name: FGmac_DmaTxDescRingInit + * @msg: 标准 DMA 发送描述符 在ring结构下的初始化。 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @param {FGmac_DmaDesc_t} *DMATxDescTab 用户定义的静态 DMA 发送描述符表格 + * @param {u8} *TxBuff 用户定义的用于匹配 DMA 发送描述符表格的缓冲区 + * @param {u32} DescBufPerLength 每个缓冲区的大小 + * @param {u32} TxBuffCount 缓冲区的总数量 + * @return {s32} Common_status 参数 + */ +s32 FGmac_DmaTxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 DescBufPerLength, u32 TxBuffCount); + +/** + * @name: FGmac_TransmitframeRingPoll + * @msg: 轮询的方式发送DMA发送表述符 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @param {uint32_t} FrameLength 需要发送数据的有效长度 + * @return {s32} Common_status 参数 + */ +s32 FGmac_TransmitframeRingPoll(Ft_Gmac_t *Gmac, uint32_t FrameLength); + +/** + * @name: FGmac_RingGetReceivedFrame_IT + * @msg: 检查标准ring结构的DMA接收符中是否有完整的数据包。 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @return {s32} Common_status 参数 + */ +s32 FGmac_RingGetReceivedFrame_IT(Ft_Gmac_t *Gmac); + +/** + * @name: FGmac_ResumeTransmission + * @msg: 检查 DMA_STATUS_TU 符号是否存在,如果存在将其置位,恢复DMA 描述符的发送 。 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @return {void} + */ +void FGmac_ResumeTransmission(Ft_Gmac_t *Gmac); + +/** + * @name: FGmac_SetTransmitUnderflow + * @msg: 检查 DMA_STATUS_UNF 符号是否存在,如果存在将其置位,恢复DMA 描述符的发送 。 + * @param {Ft_Gmac_t} *Gmac Gmac对象 + * @return {void} + */ +void FGmac_SetTransmitUnderflow(Ft_Gmac_t *Gmac); + +/** + * @name: FGmac_ResumeTransmissionReception + * @msg: 检查 DMA_STATUS_RU 符号是否存在,如果存在将其置位,恢复DMA 描述符的接收 。 + * @param {Ft_Gmac_t} *Gmac + * @return {void} + */ +void FGmac_ResumeTransmissionReception(Ft_Gmac_t *Gmac); + +/** + * @name: Ft_Gmac_LookupConfig + * @msg: 获取 Gmac 静态预设配置参数 。 + * @param {u32} InstanceId Gmac 实例编号。 + * @return {FGmac_Config_t *} 返回Gmac的静态配置 + */ +FGmac_Config_t *Ft_Gmac_LookupConfig(u32 InstanceId); + +/** + * @name: Ft_Gmac_HwInitialize + * @msg: 对于Gmac Mac 层 与Phy 层的硬件部分进行预设初始化配置 + * @param {Ft_Gmac_t} *Gmac Gmac对象。 + * @return {s32} Common_status 参数 + */ +s32 Ft_Gmac_HwInitialize(Ft_Gmac_t *Gmac); + +/** + * @name: Ft_GmacCfgInitialize + * @msg: 初始化,硬件配置相关参数。 + * @param {Ft_Gmac_t} *Gmac Gmac对象。 + * @param {FGmac_Config_t} *Config + * @return {s32} Common_status 参数 + */ +s32 Ft_GmacCfgInitialize(Ft_Gmac_t *Gmac, FGmac_Config_t *Config); + +/** + * @name: Ft_Gmac_Start + * @msg: 开启Gmac 的 发送接收功能,并且开启中断功能。 + * @param {Ft_Gmac_t} *Gmac Gmac对象。 + * @return {s32} Common_status 参数 + */ +s32 Ft_Gmac_Start(Ft_Gmac_t *Gmac); + +/** + * @name: Ft_Gmac_Stop + * @msg: 关闭Gmac 的 发送接收功能。 + * @param {Ft_Gmac_t} *Gmac Gmac对象。 + * @return {s32} Common_status 参数 + */ +s32 Ft_Gmac_Stop(Ft_Gmac_t *Gmac); + +/** + * @name: FGmac_IntrHandler + * @msg: Gmac 中断函数,用于响应Gmac 相关所以函数 + * @param {void} *Args Ft_Gmac_t *Gmac 参数传入 + * @return {void} + */ +void FGmac_IntrHandler(void *Args); + +/** + * @name: FGmac_SetHandler + * @msg: 根据SelectIndex 配置 中断过程中响应函数 + * @param {Ft_Gmac_t} *Gmac Gmac对象。 + * @param {FGmac_IsrCallbackSelect_t} SelectIndex + * @param {void} *FuncPtr + * @param {void} *Args + * @return {s32} Common_status 参数 + */ +s32 FGmac_SetHandler(Ft_Gmac_t *Gmac, FGmac_IsrCallbackSelect_t SelectIndex, void *FuncPtr, void *Args); + +void Ft_Gmac_Phy_Debug(Ft_Gmac_t *Gmac); + +/** + * @name: Ft_Gmac_UseDefaultMacAddr + * @msg: 在有uboot的模式之下,使用默认的Mac0配置参数。 + * @in param{Ft_Gmac_t *}: Gmac对象 + * @out param{u8 *}: 输出的Mac参数 + * @return {None}: + */ +void Ft_Gmac_UseDefaultMacAddr(Ft_Gmac_t *Gmac, u8 *MacAddr); + +#endif // !GMAC_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c new file mode 100644 index 0000000000..b5d81c71fe --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c @@ -0,0 +1,529 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-05-18 13:28:05 + * @Description:  This files is for gmac description + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac_desc.h" +#include "ft_gmac_hw.h" +#include "ft_parameters.h" + +#include "ft_io.h" +#include "ft_status.h" +#include "ft_assert.h" +#include "ft_cache.h" +#include "ft_debug.h" +#include + +#define GMAC_DESC_DEBUG_TAG "GMAC_DESC" + +#define GMAC_DESC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(GMAC_DESC_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_DESC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(GMAC_DESC_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_DESC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(GMAC_DESC_DEBUG_TAG, format, ##__VA_ARGS__) + +void FGmac_ResumeTransmission(Ft_Gmac_t *Gmac) +{ + /* When Tx Buffer unavailable flag is set: clear it and resume transmission */ + if ((Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET) & DMA_STATUS_TU) == DMA_STATUS_TU) + { + /* Clear TBUS GMAC DMA flag */ + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_TU); + + /* Resume DMA transmission*/ + Ft_out32(Gmac->Config.BaseAddress + DMA_XMT_POLL_DEMAND_OFFSET, 0xff); + } +} + +void FGmac_SetTransmitUnderflow(Ft_Gmac_t *Gmac) +{ + /* When Tx Buffer unavailable flag is set: clear it and resume transmission */ + if ((Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET) & DMA_STATUS_UNF) == DMA_STATUS_UNF) + { + /* Clear TBUS GMAC DMA flag */ + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_UNF); + + /* Resume DMA transmission*/ + Ft_out32(Gmac->Config.BaseAddress + DMA_XMT_POLL_DEMAND_OFFSET, 0xff); + } +} + +/** + * @name: FGmac_DMATxDescChainInit + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @param {FGmac_DmaDesc_t} *DMATxDescTab + * @param {u8} *TxBuff + * @param {u32} TxBuffCount + * @return {*} + */ +s32 FGmac_DMATxDescChainInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 TxBuffCount) +{ + u32 i; + FGmac_DmaDesc_t *TxDesc; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + + /* Make Each DmaTx descriptor with initialized value*/ + for (i = 0; i < TxBuffCount; i++) + { + TxDesc = DMATxDescTab + i; + TxDesc->Control = DMA_TDES1_SECOND_ADDRESS_CHAINED; + TxDesc->Buffer1Addr = (u32)(&TxBuff[i * GMAC_MAX_PACKET_SIZE]); + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if (i < (TxBuffCount - 1)) + { + /* Set next descriptor address register with next descriptor base address */ + TxDesc->Buffer2NextDescAddr = (u32)(DMATxDescTab + i + 1); + Ft_printf(" Buffer2NextDescAddr %x \r\n", TxDesc->Buffer2NextDescAddr); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + TxDesc->Buffer2NextDescAddr = (u32)DMATxDescTab; + Ft_printf(" Buffer2NextDescAddr %x \r\n", TxDesc->Buffer2NextDescAddr); + } + } + Ft_printf("DMATxDescTab addr is %x", DMATxDescTab); + + return FST_SUCCESS; +} + +/** + * @name: Ft_Gmac_DMATxDescRingInit + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @param {FGmac_DmaDesc_t} *DMATxDescTab + * @param {u8} *TxBuff + * @param {u32} DescBufPerLength + * @param {u32} TxBuffCount + * @return {*} + */ +s32 FGmac_DmaTxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 DescBufPerLength, u32 TxBuffCount) +{ + u32 i; + FGmac_DmaDesc_t *TxDesc; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + + memset(&Gmac->TxDescRingData, 0, sizeof(Gmac->TxDescRingData)); + Gmac->TxDescRingData.DescMaxNumber = TxBuffCount; + memset(DMATxDescTab, 0, sizeof(DMATxDescTab) * TxBuffCount); + DMATxDescTab[TxBuffCount - 1].Control |= DMA_TDES1_END_RING; + for (i = 0; i < TxBuffCount; i++) + { + TxDesc = DMATxDescTab + i; + FCache_cpuDcacheInvalidate(&TxBuff[i * DescBufPerLength], DescBufPerLength); + TxDesc->Buffer1Addr = (u32)(&TxBuff[i * DescBufPerLength]); + } + Ft_out32(Gmac->Config.BaseAddress + DMA_TX_BASE_ADDR_OFFSET, (u32)DMATxDescTab); + return FST_SUCCESS; +} + +// static s32 FGmac_transmitframe(Ft_Gmac_t *Gmac, uint32_t FrameLength) +// { +// u32 Size = 0U; +// u32 i = 0U; +// u32 BufCount = 0U; + +// if (0U == FrameLength) +// { +// return FST_SUCCESS; +// } + +// if ((Gmac->TxDesc->Status & DMA_TDES0_OWN) == DMA_TDES0_OWN) +// { +// return FST_FAILURE; +// } + +// if (FrameLength > GMAC_MAX_PACKET_SIZE) +// { +// BufCount = FrameLength / GMAC_MAX_PACKET_SIZE; +// if (FrameLength % GMAC_MAX_PACKET_SIZE) +// { +// BufCount++; +// } +// } +// else +// { +// BufCount = 1U; +// } + +// if (BufCount == 1U) +// { +// /* Set LAST and FIRST segment */ +// Gmac->TxDesc->Control |= DMA_TDES1_FIRST_SEGMENT | DMA_TDES1_LAST_SEGMENT; +// /* Set frame size */ +// Gmac->TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); +// Gmac->TxDesc->Control |= (FrameLength & DMA_TDES1_BUFFER1_SIZE_MASK); +// /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ +// Gmac->TxDesc->Status |= (DMA_TDES0_OWN); +// /* Point to next descriptor */ +// Gmac->TxDesc = (FGmac_DmaDesc_t *)(Gmac->TxDesc->Buffer2NextDescAddr); +// } +// else +// { +// for (i = 0U; i < BufCount; i++) +// { +// /* Clear FIRST and LAST segment bits */ +// Gmac->TxDesc->Control &= ~(DMA_TDES1_FIRST_SEGMENT | DMA_TDES1_LAST_SEGMENT); + +// if (i == 0U) +// { +// /* Setting the first segment bit */ +// Gmac->TxDesc->Control |= DMA_TDES1_FIRST_SEGMENT; +// } + +// /* Program size */ +// Gmac->TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); +// Gmac->TxDesc->Control |= (GMAC_MAX_PACKET_SIZE & DMA_TDES1_BUFFER1_SIZE_MASK); + +// if (i == (BufCount - 1U)) +// { +// /* Setting the last segment bit */ +// Gmac->TxDesc->Control |= DMA_TDES1_LAST_SEGMENT; +// Size = FrameLength - (BufCount - 1U) * GMAC_MAX_PACKET_SIZE; +// Gmac->TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); +// Gmac->TxDesc->Control |= (Size & DMA_TDES1_BUFFER1_SIZE_MASK); +// } + +// /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ +// Gmac->TxDesc->Status |= (DMA_TDES0_OWN); +// /* point to next descriptor */ +// Gmac->TxDesc = (FGmac_DmaDesc_t *)(Gmac->TxDesc->Buffer2NextDescAddr); +// } +// } + +// FGmac_ResumeTransmission(Gmac); + +// return FST_SUCCESS; +// } + +s32 FGmac_TransmitframeRingPoll(Ft_Gmac_t *Gmac, uint32_t FrameLength) +{ + u32 Size = 0U; + u32 i = 0U; + u32 BufCount = 0U; + FGmac_DmaDesc_t *TxDesc; + + if (0U == FrameLength) + { + return FST_SUCCESS; + } + + if (FrameLength > GMAC_MAX_PACKET_SIZE) + { + BufCount = FrameLength / GMAC_MAX_PACKET_SIZE; + if (FrameLength % GMAC_MAX_PACKET_SIZE) + { + BufCount++; + } + } + else + { + BufCount = 1U; + } + + if (BufCount == 1U) + { + + TxDesc = &Gmac->TxDesc[Gmac->TxDescRingData.DescIndex]; + /* Set LAST and FIRST segment */ + TxDesc->Control |= (DMA_TDES1_FIRST_SEGMENT | DMA_TDES1_LAST_SEGMENT); + + /* Set frame size */ + TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); + TxDesc->Control |= (FrameLength & DMA_TDES1_BUFFER1_SIZE_MASK); + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + TxDesc->Status |= (DMA_TDES0_OWN); + GMAC_INC_DESC(Gmac->TxDescRingData.DescIndex, Gmac->TxDescRingData.DescMaxNumber); + } + else + { + for (i = 0U; i < BufCount; i++) + { + TxDesc = &Gmac->TxDesc[Gmac->TxDescRingData.DescIndex]; + /* Clear FIRST and LAST segment bits */ + TxDesc->Control &= ~(DMA_TDES1_FIRST_SEGMENT | DMA_TDES1_LAST_SEGMENT); + + if (i == 0U) + { + /* Setting the first segment bit */ + TxDesc->Control |= DMA_TDES1_FIRST_SEGMENT; + } + + /* Program size */ + TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); + TxDesc->Control |= (GMAC_MAX_PACKET_SIZE & DMA_TDES1_BUFFER1_SIZE_MASK); + + if (i == (BufCount - 1U)) + { + /* Setting the last segment bit */ + TxDesc->Control |= (DMA_TDES1_LAST_SEGMENT); + Size = FrameLength - (BufCount - 1U) * GMAC_MAX_PACKET_SIZE; + TxDesc->Control &= ~(DMA_TDES1_BUFFER1_SIZE_MASK); + TxDesc->Control |= (Size & DMA_TDES1_BUFFER1_SIZE_MASK); + } + + /* Set Own bit of the Tx descriptor Status: gives the buffer back to ETHERNET DMA */ + TxDesc->Status |= (DMA_TDES0_OWN); + + GMAC_INC_DESC(Gmac->TxDescRingData.DescIndex, Gmac->TxDescRingData.DescMaxNumber); + } + } + + FGmac_ResumeTransmission(Gmac); + + return FST_SUCCESS; +} + +/** + * @name: + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @param {FGmac_DmaDesc_t} *DMARxDescTab + * @param {u8} *RxBuff + * @param {u32} RxBuffCount + * @return {*} + */ +s32 FGmac_DMARxDescChainInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMARxDescTab, u8 *RxBuff, u32 RxBuffCount) +{ + u32 i; + FGmac_DmaDesc_t *RxDesc; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + + for (i = 0; i < RxBuffCount; i++) + { + RxDesc = DMARxDescTab + i; + /* Set Own bit of the Rx descriptor Status */ + RxDesc->Status = DMA_RDES0_OWN; + + /* Set Buffer1 size and Second Address Chained bit */ + RxDesc->Control = DMA_RDES1_BUFFER1_SIZE_MASK & (u32)(GMAC_MAX_PACKET_SIZE); + RxDesc->Control |= DMA_RDES1_SECOND_ADDRESS_CHAINED; + /* Set Buffer1 address pointer */ + RxDesc->Buffer1Addr = (u32)(&RxBuff[i * GMAC_MAX_PACKET_SIZE]); + + /* Initialize the next descriptor with the Next Descriptor Polling Enable */ + if (i < (RxBuffCount - 1)) + { + /* Set next descriptor address register with next descriptor base address */ + RxDesc->Buffer2NextDescAddr = (u32)(DMARxDescTab + i + 1); + } + else + { + /* For last descriptor, set next descriptor address register equal to the first descriptor base address */ + RxDesc->Buffer2NextDescAddr = (u32)(DMARxDescTab); + } + + // Ft_printf("rx Buffer2NextDescAddr %x \r\n", RxDesc->Buffer2NextDescAddr); + } + Ft_out32(Gmac->Config.BaseAddress + DMA_RCV_BASE_ADDR_OFFSET, (u32)DMARxDescTab); + return FST_SUCCESS; +} + +s32 FGmac_DmaRxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMARxDescTab, u8 *RxBuff, u32 DescBufPerLength, u32 RxBuffCount) +{ + u32 i; + FGmac_DmaDesc_t *RxDesc; + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + + memset(&Gmac->RxDescRingData, 0, sizeof(Gmac->RxDescRingData)); + Gmac->RxDescRingData.DescMaxNumber = RxBuffCount; + memset(DMARxDescTab, 0, sizeof(FGmac_DmaDesc_t) * RxBuffCount); + + for (i = 0; i < RxBuffCount; i++) + { + RxDesc = DMARxDescTab + i; + /* Set Own bit of the Rx descriptor Status */ + RxDesc->Status = DMA_RDES0_OWN; + /* Set Buffer1 size and Second Address Chained bit */ + RxDesc->Control = DMA_RDES1_BUFFER1_SIZE_MASK & (u32)(DescBufPerLength); + /* Set Buffer1 address pointer */ + FCache_cpuDcacheInvalidate(&RxBuff[i * DescBufPerLength], DescBufPerLength); + RxDesc->Buffer1Addr = (u32)(&RxBuff[i * DescBufPerLength]); + + if (i == (RxBuffCount - 1)) + { + RxDesc->Control |= DMA_RDES1_END_RING; + } + } + + Ft_out32(Gmac->Config.BaseAddress + DMA_RCV_BASE_ADDR_OFFSET, (u32)DMARxDescTab); + + return FST_SUCCESS; +} + +/** + * @name: FGmac_ResumeTransmissionReception + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +void FGmac_ResumeTransmissionReception(Ft_Gmac_t *Gmac) +{ + /* When Rx Buffer unavailable flag is set: clear it and resume transmission */ + if ((Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET) & DMA_STATUS_RU) == DMA_STATUS_RU) + { + /* Clear RBUS GMAC DMA flag */ + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_RU); + + /* Resume DMA transmission*/ + Ft_out32(Gmac->Config.BaseAddress + DMA_RCV_POLL_DEMAND_OFFSET, 0xff); + } +} + +/** + * @name: Gmac_GetReceivedFrame_IT + * @msg: Gets the received frame in interrupt mode. + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +// static s32 FGmac_chainGetReceivedFrame_IT(Ft_Gmac_t *Gmac) +// { +// u32 DescriptorsCounter = 0U; +// GMAC_DESC_DEBUG_I(" Gmac->RxDesc->Status %x \r\n", Gmac->RxDesc->Status); +// while (((Gmac->RxDesc->Status & DMA_RDES0_OWN) == 0) && DescriptorsCounter < RX_DESCNUM) +// { +// DescriptorsCounter++; +// /* Check if first segment in frame */ +// if ((Gmac->RxDesc->Status & (DMA_RDES0_FIRST_DESCRIPTOR | DMA_RDES0_LAST_DESCRIPTOR)) == (u32)DMA_RDES0_FIRST_DESCRIPTOR) +// { +// Gmac->DMARxFrameInfos.FSRxDesc = Gmac->RxDesc; +// Gmac->DMARxFrameInfos.SegCount = 1U; +// /* Point to next descriptor */ +// Gmac->RxDesc = (FGmac_DmaDesc_t *)(Gmac->RxDesc->Buffer2NextDescAddr); +// } /* Check if intermediate segment */ +// else if ((Gmac->RxDesc->Status & (DMA_RDES0_FIRST_DESCRIPTOR | DMA_RDES0_LAST_DESCRIPTOR)) == 0) +// { +// /* Increment segment count */ +// Gmac->DMARxFrameInfos.SegCount++; +// /* Point to next descriptor */ +// Gmac->RxDesc = (FGmac_DmaDesc_t *)(Gmac->RxDesc->Buffer2NextDescAddr); +// } /* Should be last segment */ +// else +// { +// Gmac->DMARxFrameInfos.LSRxDesc = Gmac->RxDesc; +// Gmac->DMARxFrameInfos.SegCount++; + +// /* Check if last segment is first segment: one segment contains the frame */ +// if (1U == Gmac->DMARxFrameInfos.SegCount) +// { +// Gmac->DMARxFrameInfos.FSRxDesc = Gmac->RxDesc; +// } + +// Gmac->DMARxFrameInfos.length = ((Gmac->RxDesc->Status & DMA_RDES0_FRAME_LEN_MASK) >> 16U); + +// /* Get address of the buffer start address */ +// Gmac->DMARxFrameInfos.buffer = Gmac->DMARxFrameInfos.FSRxDesc->Buffer1Addr; + +// /* Point to next descriptor */ +// Gmac->RxDesc = (FGmac_DmaDesc_t *)(Gmac->RxDesc->Buffer2NextDescAddr); + +// return FST_SUCCESS; +// } +// } +// return FST_FAILURE; +// } + +s32 FGmac_RingGetReceivedFrame_IT(Ft_Gmac_t *Gmac) +{ + u32 DescriptorsCounter = 0U; + FGmac_DmaDesc_t *RxDesc; + RxDesc = &Gmac->RxDesc[Gmac->RxDescRingData.DescIndex]; + + while (((RxDesc->Status & DMA_RDES0_OWN) == 0) && DescriptorsCounter < Gmac->RxDescRingData.DescMaxNumber) + { + DescriptorsCounter++; + + /* Check if first segment in frame */ + if ((RxDesc->Status & (DMA_RDES0_FIRST_DESCRIPTOR | DMA_RDES0_LAST_DESCRIPTOR)) == (u32)DMA_RDES0_FIRST_DESCRIPTOR) + { + // GMAC_DESC_DEBUG_I("find first frame"); + Gmac->RxDescRingData.DescBufIndex = Gmac->RxDescRingData.DescIndex; + /* Point to next descriptor */ + GMAC_INC_DESC(Gmac->RxDescRingData.DescIndex, Gmac->RxDescRingData.DescMaxNumber); + RxDesc = &Gmac->RxDesc[Gmac->RxDescRingData.DescIndex]; + } /* Check if intermediate segment */ + else if ((RxDesc->Status & (DMA_RDES0_FIRST_DESCRIPTOR | DMA_RDES0_LAST_DESCRIPTOR)) == 0) + { + // GMAC_DESC_DEBUG_I("find invaild frame"); + /* Point to next descriptor */ + GMAC_INC_DESC(Gmac->RxDescRingData.DescIndex, Gmac->RxDescRingData.DescMaxNumber); + RxDesc = &Gmac->RxDesc[Gmac->RxDescRingData.DescIndex]; + } /* Should be last segment */ + else + { + Gmac->RxDescRingData.DescBufIndex = Gmac->RxDescRingData.DescIndex; + GMAC_INC_DESC(Gmac->RxDescRingData.DescIndex, Gmac->RxDescRingData.DescMaxNumber); + return FST_SUCCESS; + } + } + return FST_FAILURE; +} + +/** + * @name: FGmac_GetReceivedFrame + * @msg: Check for received frames. + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +s32 FGmac_GetReceivedFrame(Ft_Gmac_t *Gmac) +{ + u32 FrameLength = 0U; + /* Check if segment is not owned by DMA */ + if ((Gmac->RxDesc->Status & DMA_RDES0_OWN) == 0) + { + if ((Gmac->RxDesc->Status & DMA_RDES0_LAST_DESCRIPTOR) != 0) + { + /* increment segment count */ + Gmac->DMARxFrameInfos.SegCount++; + + /* Check if last segment is first segment: one segment contains the frame */ + if (1U == Gmac->DMARxFrameInfos.SegCount) + { + (Gmac->DMARxFrameInfos).FSRxDesc = Gmac->RxDesc; + } + + Gmac->DMARxFrameInfos.LSRxDesc = Gmac->RxDesc; + + /* Get the Frame Length of the received packet: substruct 4 bytes of the CRC */ + FrameLength = (((Gmac->RxDesc)->Status & DMA_RDES0_FRAME_LEN_MASK) >> 16U) - 4U; + Gmac->DMARxFrameInfos.length = FrameLength; + + /* Get the address of the buffer start address */ + Gmac->DMARxFrameInfos.buffer = ((Gmac->DMARxFrameInfos).FSRxDesc)->Buffer1Addr; + /* point to next descriptor */ + Gmac->RxDesc = (FGmac_DmaDesc_t *)((Gmac->RxDesc)->Buffer2NextDescAddr); + + return FST_SUCCESS; + } + /* Check if first segment */ + else if ((Gmac->RxDesc->Status & DMA_RDES0_FIRST_DESCRIPTOR) == (u32)DMA_RDES0_FIRST_DESCRIPTOR) + { + (Gmac->DMARxFrameInfos).FSRxDesc = Gmac->RxDesc; + (Gmac->DMARxFrameInfos).LSRxDesc = NULL; + (Gmac->DMARxFrameInfos).SegCount = 1U; + /* Point to next descriptor */ + Gmac->RxDesc = (FGmac_DmaDesc_t *)(Gmac->RxDesc->Buffer2NextDescAddr); + } /* Check if intermediate segment */ + else + { + (Gmac->DMARxFrameInfos).SegCount++; + /* Point to next descriptor */ + Gmac->RxDesc = (FGmac_DmaDesc_t *)(Gmac->RxDesc->Buffer2NextDescAddr); + } + } + + return FST_FAILURE; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.h b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.h new file mode 100644 index 0000000000..f66a591f86 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.h @@ -0,0 +1,20 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:24:16 + * @Description:  This files is for gmac descrption + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef GMAC_DESC_H +#define GMAC_DESC_H + +#include "ft_gmac.h" + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_g.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_g.c new file mode 100644 index 0000000000..4e9cb78cdc --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_g.c @@ -0,0 +1,47 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:24:30 + * @Description:  This files is for gmac config + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac.h" +#include "ft_parameters.h" + +FGmac_Config_t Gmac_ConfigTable[FT_GMAC_INSTANCES_NUM] = { + {FT_GMAC0_ID, + FT_GMAC_COMMON_ADDR, + FT_GMAC0_BASEADDR, + GMAC0_ISRNUM, + GMAC0_ISRPRIORITY, + 0, + 5, + GMAC_AUTONEGOTIATION_ENABLE, + GMAC_SPEED_1000M, + GMAC_MODE_FULLDUPLEX, + GMAC_RXINTERRUPT_MODE, + GMAC_CHECKSUM_BY_SOFTWARE, + GMAC_MEDIA_INTERFACE_RGMII, + FT_GMAC0_DEFAULT_ADDR}, + {FT_GMAC1_ID, + FT_GMAC_COMMON_ADDR, + FT_GMAC1_BASEADDR, + GMAC1_ISRNUM, + GMAC1_ISRPRIORITY, + 0, + 5, + GMAC_AUTONEGOTIATION_ENABLE, + GMAC_SPEED_1000M, + GMAC_MODE_FULLDUPLEX, + GMAC_RXINTERRUPT_MODE, + GMAC_CHECKSUM_BY_SOFTWARE, + GMAC_MEDIA_INTERFACE_RGMII, + {0}}, +}; diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.c new file mode 100644 index 0000000000..5adf90ece0 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.c @@ -0,0 +1,582 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-19 17:50:52 + * @Description:  This files is for gmac register + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac_hw.h" +#include "ft_types.h" +#include "ft_generic_timer.h" +#include "ft_assert.h" +#include "ft_status.h" +#include "ft_io.h" +#include "ft_debug.h" +#define PHY_CONFIG_DELAY_US 100 + +#define GMAC_HW_DEBUG_TAG "GMAC_HW" + +#define GMAC_HW_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(GMAC_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_HW_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(GMAC_HW_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_HW_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(GMAC_HW_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: Gmac_WritePHYRegister + * @msg: + * @param {*} + * @return {*} + */ +s32 Gmac_WritePHYRegister(FGmac_Config_t *Config, u16 PHYReg, u32 RegValue) +{ + u32 TmpReg; + u32 Wait_Counter = 0; + + TmpReg = Ft_in32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET); + + TmpReg |= GMAC_MII_ADDR_CR(Config->clkMDC); + TmpReg |= GMAC_MII_ADDR_PA(Config->PhyAddr); /* Set the PHY device address */ + TmpReg |= GMAC_MII_ADDR_GR(PHYReg); /* Set the PHY register address */ + TmpReg |= GMAC_MII_ADDR_GW; /* Set the write mode */ + TmpReg |= GMAC_MII_ADDR_GB; /* Set the MII Busy bit */ + + Ft_out32(Config->BaseAddress + GMAC_MII_DATA_OFFSET, RegValue); + Ft_out32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET, TmpReg); + + /* Check for the Busy flag */ + while ((Ft_in32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET) & GMAC_MII_ADDR_GB) == GMAC_MII_ADDR_GB) + { + if (10000000 <= ++Wait_Counter) + { + GMAC_HW_DEBUG_E("Gmac_WritePHYRegister FST_ERROR_COUNT_MAX \r\n"); + return FST_ERROR_COUNT_MAX; + } + } + return FST_SUCCESS; +} + +s32 FGmac_ReadPHYRegister(FGmac_Config_t *Config, u16 PHYReg, u32 *RegValue) +{ + u32 TmpReg; + u32 Wait_Counter = 0; + + TmpReg = 0; + TmpReg |= GMAC_MII_ADDR_CR(Config->clkMDC); + TmpReg |= GMAC_MII_ADDR_PA(Config->PhyAddr); /* Set the PHY device address */ + TmpReg |= GMAC_MII_ADDR_GR(PHYReg); /* Set the PHY register address */ + TmpReg &= ~GMAC_MII_ADDR_GW; /* Set the read mode */ + TmpReg |= GMAC_MII_ADDR_GB; /* Set the MII Busy bit */ + + while ((Ft_in32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET) & GMAC_MII_ADDR_GB) == GMAC_MII_ADDR_GB) + { + if (10000000 <= ++Wait_Counter) + { + GMAC_HW_DEBUG_E("wait GMAC_MII_ADDR_GB is error \r\n"); + *RegValue = 0xffff; + return FST_ERROR_COUNT_MAX; + } + } + + Ft_out32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET, TmpReg); + + while ((Ft_in32(Config->BaseAddress + GMAC_MII_ADDR_OFFSET) & GMAC_MII_ADDR_GB) == GMAC_MII_ADDR_GB) + { + if (10000000 <= ++Wait_Counter) + { + GMAC_HW_DEBUG_E("wait GMAC_MII_ADDR_GB 2 is error \r\n"); + *RegValue = 0xffff; + return FST_ERROR_COUNT_MAX; + } + } + *RegValue = Ft_in32(Config->BaseAddress + GMAC_MII_DATA_OFFSET); + return FST_SUCCESS; +} + +static s32 FGmac_PhyAddrGet(FGmac_Config_t *Config) +{ + u32 i = 0; + u32 phyId1; + u32 phyId2; + u8 flag = 0; + + for (i = 0; i < 32; i++) + { + Config->PhyAddr = i; + FGmac_ReadPHYRegister(Config, PHY_ID1_REG_OFFSET, &phyId1); + FGmac_ReadPHYRegister(Config, PHY_ID2_REG_OFFSET, &phyId2); + + if ((phyId2 & 0xffff) != 0xffff) + { + if ((0 == i) && (0x1c == phyId1) && ((phyId2 >> 10) == 0x32)) + { + continue; + } + flag = 1; + break; + } + } + return (flag == 1) ? FST_SUCCESS : FST_FAILURE; +} + +/** + * @name: FGmac_MACAddressConfig + * @msg: + * @param {*} + * @return {*} + */ +static void FGmac_MACAddressConfig(FGmac_Config_t *Config, u32 MacAddr, u8 *Addr) +{ + u32 tmpreg; + /* Calculate the selected MAC address high register */ + tmpreg = ((u32)Addr[5] << 8) | (u32)Addr[4]; + /* Load the selected MAC address high register */ + Ft_out32(Config->BaseAddress + GMAC_MAC_ADDR0_UPPER16BIT_OFFSET + MacAddr, tmpreg); + /* Calculate the selected MAC address low register */ + tmpreg = ((u32)Addr[3] << 24) | ((u32)Addr[2] << 16) | ((u32)Addr[1] << 8) | Addr[0]; + /* Load the selected MAC address low register */ + Ft_out32(Config->BaseAddress + GMAC_MAC_ADDR0_LOWER16BIT_OFFSET + MacAddr, tmpreg); +} + +/** + * @name: FGmac_MACDMAInit + * @msg: + * @param {Config} + * @return {*} + */ +static void FGmac_MACDMAInit(FGmac_Config_t *Config) +{ + u32 RegValue = 0; + Ft_assertVoid(FGmac_PhyAddrGet(Config) == FST_SUCCESS); + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + /* MACCR Configuration **************************************/ + /* Set the WD bit according to ETH Watchdog value */ + /* Set the JD: bit according to ETH Jabber value */ + /* Set the IFG bit according to ETH InterFrameGap value */ + /* Set the DCRS bit according to ETH CarrierSense value */ + /* Set the FES bit according to ETH Speed value */ + /* Set the DO bit according to ETH ReceiveOwn value */ + /* Set the LM bit according to ETH LoopbackMode value */ + /* Set the DM bit according to ETH Mode value */ + /* Set the IPCO bit according to ETH ChecksumOffload value */ + /* Set the DR bit according to ETH RetryTransmission value */ + /* Set the ACS bit according to ETH AutomaticPadCRCStrip value */ + /* Set the BL bit according to ETH BackOffLimit value */ + /* Set the DC bit according to ETH DeferralCheck value */ + RegValue &= ~GMAC_CONTROL_WD; + RegValue &= ~GMAC_CONTROL_JD; + RegValue |= GMAC_CONTROL_IFG(0); + RegValue &= ~GMAC_CONTROL_DCRS; + RegValue &= ~GMAC_CONTROL_DO; + RegValue &= ~GMAC_CONTROL_LM; + if (Config->ChecksumMode) + { + RegValue |= GMAC_CONTROL_IPC; + } + else + { + RegValue &= ~GMAC_CONTROL_IPC; + } + + RegValue |= GMAC_CONTROL_DR; + RegValue &= ~GMAC_CONTROL_ACS; + RegValue |= GMAC_CONTROL_BL(0); + RegValue &= ~GMAC_CONTROL_DC; + if (Config->DuplexMode == GMAC_MODE_FULLDUPLEX) + { + RegValue |= GMAC_CONTROL_DM; + } + else + { + RegValue &= ~GMAC_CONTROL_DM; + } + + RegValue &= ~GMAC_CONTROL_FES; + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /*----------------------- GMAC MACFFR Configuration --------------------*/ + /* Set the RA bit according to ETH ReceiveAll value */ + /* Set the SAF and SAIF bits according to ETH SourceAddrFilter value */ + /* Set the PCF bit according to ETH PassControlFrames value */ + /* Set the DBF bit according to ETH BroadcastFramesReception value */ + /* Set the DAIF bit according to ETH DestinationAddrFilter value */ + /* Set the PR bit according to ETH PromiscuousMode value */ + /* Set the PM, HMC and HPF bits according to ETH MulticastFramesFilter value */ + /* Set the HUC and HPF bits according to ETH UnicastFramesFilter value */ + /* Write to MACFFR */ + RegValue = Ft_in32(Config->BaseAddress + GMAC_FRAME_FILTER_OFFSET); + RegValue |= GMAC_FRAME_FILTER_RA; + RegValue |= GMAC_FRAME_FILTER_PCF(2); + RegValue &= ~GMAC_FRAME_FILTER_PM; + RegValue &= ~GMAC_FRAME_FILTER_DBF; + RegValue &= ~GMAC_FRAME_FILTER_DAIF; + RegValue &= ~GMAC_FRAME_FILTER_PR; + Ft_out32(Config->BaseAddress + GMAC_FRAME_FILTER_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /*--------------- MACHTHR and MACHTLR Configuration --------------*/ + Ft_out32(Config->BaseAddress + GMAC_HASH_HIGH_OFFSET, 0); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + Ft_out32(Config->BaseAddress + GMAC_HASH_LOW_OFFSET, 0); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /*----------------------- MACFCR Configuration -------------------*/ + /* Set the PT bit according to ETH PauseTime value */ + /* Set the DZPQ bit according to ETH ZeroQuantaPause value */ + /* Set the PLT bit according to ETH PauseLowThreshold value */ + /* Set the UP bit according to ETH UnicastPauseFrameDetect value */ + /* Set the RFE bit according to ETH ReceiveFlowControl value */ + /* Set the TFE bit according to ETH TransmitFlowControl value */ + RegValue = Ft_in32(Config->BaseAddress + GMAC_FLOW_CTRL_OFFSET); + RegValue |= GMAC_FLOW_DZPQ; + RegValue |= GMAC_FLOW_PLT(0); + RegValue &= ~GMAC_FLOW_RFE; + RegValue &= ~GMAC_FLOW_TFE; + Ft_out32(Config->BaseAddress + GMAC_FLOW_CTRL_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /*----------------------- MACVLANTR Configuration ----------------*/ + /* Set the ETV bit according to ETH VLANTagComparison value */ + /* Set the VL bit according to ETH VLANTagIdentifier value */ + RegValue = GMAC_VLAN_TAG_VL(0); + RegValue &= ~GMAC_VLAN_TAG_ETV; + Ft_out32(Config->BaseAddress + GMAC_VLAN_TAG_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /* DMA default initialization ************************************/ + /* Set the DT bit according to ETH DropTCPIPChecksumErrorFrame value */ + /* Set the RSF bit according to ETH ReceiveStoreForward value */ + /* Set the DFF bit according to ETH FlushReceivedFrame value */ + /* Set the TSF bit according to ETH TransmitStoreForward value */ + /* Set the TTC bit according to ETH TransmitThresholdControl value */ + /* Set the FEF bit according to ETH ForwardErrorFrames value */ + /* Set the FUF bit according to ETH ForwardUndersizedGoodFrames value */ + /* Set the RTC bit according to ETH ReceiveThresholdControl value */ + /* Set the OSF bit according to ETH SecondFrameOperate value */ + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue &= DMA_OP_CLEAR_MASK; + RegValue &= ~DMA_OP_DT; + RegValue |= DMA_OP_RSF; + RegValue &= ~DMA_OP_DFF; + RegValue |= DMA_OP_TSF; + RegValue |= DMA_OP_TTC(7); + RegValue |= DMA_OP_OSF; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + /* DMABMR Configuration ------------------*/ + /* Set the AAL bit according to ETH AddressAlignedBeats value */ + /* Set the FB bit according to ETH FixedBurst value */ + /* Set the RPBL and 4*PBL bits according to ETH RxDMABurstLength value */ + /* Set the PBL and 4*PBL bits according to ETH TxDMABurstLength value */ + /* Set the Enhanced DMA descriptors bit according to ETH EnhancedDescriptorFormat value*/ + /* Set the DSL bit according to ETH DesciptorSkipLength value */ + /* Set the PR and DA bits according to ETH DMAArbitration value */ + RegValue = Ft_in32(Config->BaseAddress + DMA_BUS_MODE_OFFSET); + RegValue |= DMA_BUS_AAL; + RegValue |= DMA_BUS_FB; + RegValue |= DMA_BUS_RPBL(32); + RegValue |= DMA_BUS_PBL(32); + RegValue |= DMA_BUS_ATDS; + RegValue |= DMA_BUS_PR(0); + RegValue |= DMA_BUS_USP; + Ft_out32(Config->BaseAddress + DMA_BUS_MODE_OFFSET, RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + Ft_out32(Config->BaseAddress + DMA_AXI_BUS_MOD_OFFSET, 0x0000000e); + FGmac_MACAddressConfig(Config, 0, Config->MacAddr); +} + +s32 FGmac_InitializeHw(FGmac_Config_t *Config) +{ + u32 Wait_Counter = 0; + u32 RegValue; + s32 ret = FST_SUCCESS; + + /*Gmac Software reset */ + Ft_out32(Config->BaseAddress + DMA_BUS_MODE_OFFSET, DMA_BUS_SWR); + + while ((Ft_in32(Config->BaseAddress + DMA_BUS_MODE_OFFSET) & DMA_BUS_SWR) == DMA_BUS_SWR) + { + if (++Wait_Counter > 30) + { + GMAC_HW_DEBUG_E("DMA_BUS_MODE_OFFSET wait is too long ,Addr %x \r\n", Config->BaseAddress); + return FST_ERROR_COUNT_MAX; + } + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + } + Wait_Counter = 0; + Ft_out32(Config->BaseAddress + DMA_BUS_MODE_OFFSET, DMA_BUS_INIT); + + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, Ft_in32(Config->BaseAddress + DMA_OP_OFFSET) | DMA_OP_FTF); + + while ((Ft_in32(Config->BaseAddress + DMA_OP_OFFSET) & DMA_OP_FTF) == DMA_OP_FTF) + { + if (++Wait_Counter > 30) + { + GMAC_HW_DEBUG_E("DMA_OP_OFFSET wait is too long \r\n"); + return FST_ERROR_COUNT_MAX; + } + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + } + + Wait_Counter = 0; + + /* GMAC Init */ + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, GMAC_CONTROL_INIT); + /* disable flow control */ + Ft_out32(Config->BaseAddress + GMAC_FLOW_CTRL_OFFSET, 0); + + Ft_out32(Config->BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_INIT); + + Ft_out32(Config->BaseAddress + DMA_INTR_ENA_OFFSET, 0); + + /* get Phy addr */ + Ft_assertNonvoid(FGmac_PhyAddrGet(Config) == FST_SUCCESS); + + /*-------------------------------- MAC Initialization ----------------------*/ + /*-------------------- PHY initialization and configuration ----------------*/ + /* Put the PHY in reset mode */ + if (Gmac_WritePHYRegister(Config, PHY_BCR_OFFSET, PHY_RESET) != FST_SUCCESS) + { + GMAC_HW_DEBUG_E("PHY_BCR_OFFSET is error \r\n"); + return FST_FAILURE; + } + + do + { + FGmac_ReadPHYRegister(Config, PHY_BCR_OFFSET, &RegValue); + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + if (++Wait_Counter > 30) + { + GMAC_HW_DEBUG_E("PHY_BCR_OFFSET wait is too long \r\n"); + ret = FST_ERROR_COUNT_MAX; + goto gmac_init; + } + } while ((RegValue & PHY_RESET) == PHY_RESET); + + Wait_Counter = 0; + if (Config->AutoNegotiation == GMAC_AUTONEGOTIATION_ENABLE) + { + do + { + FGmac_ReadPHYRegister(Config, PHY_BSR_OFFSET, &RegValue); + + if (++Wait_Counter > 30) + { + GMAC_HW_DEBUG_E("PHY_BSR_OFFSET 1 wait is too long \r\n"); + ret = FST_ERROR_COUNT_MAX; + goto gmac_init; + } + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + + } while ((RegValue & PHY_LINKED_STATUS) != PHY_LINKED_STATUS); + + Wait_Counter = 0; + if (Gmac_WritePHYRegister(Config, PHY_BCR_OFFSET, PHY_AUTONEGOTIATION | PHY_RESTART_AUTONEGOTIATION) != FST_SUCCESS) + { + GMAC_HW_DEBUG_E("PHY_BCR_OFFSET 2 is error \r\n"); + ret = FST_FAILURE; + goto gmac_init; + } + + do + { + FGmac_ReadPHYRegister(Config, PHY_BSR_OFFSET, &RegValue); + if (++Wait_Counter > 30) + { + GMAC_HW_DEBUG_E("PHY_BSR_OFFSET 2 wait is too long \r\n"); + ret = FST_ERROR_COUNT_MAX; + goto gmac_init; + } + } while ((RegValue & PHY_AUTONEGO_COMPLETE) != PHY_AUTONEGO_COMPLETE); + + if (FGmac_ReadPHYRegister(Config, PHY_SPECIFIC_STATUS_OFFSET, &RegValue) != FST_SUCCESS) + { + GMAC_HW_DEBUG_E("PHY_SPECIFIC_STATUS_OFFSET is error \r\n"); + ret = FST_FAILURE; + goto gmac_init; + } + /* Configure the MAC with the Duplex Mode fixed by the auto-negotiation process */ + if ((RegValue & PHY_SPECIFIC_STATUS_DUPLEX) != PHY_SPECIFIC_STATUS_DUPLEX) + { + /* Set Ethernet duplex mode to Full-duplex following the auto-negotiation */ + Config->DuplexMode = GMAC_MODE_HALFDUPLEX; + } + else + { + /* Set Ethernet duplex mode to Half-duplex following the auto-negotiation */ + Config->DuplexMode = GMAC_MODE_FULLDUPLEX; + } + } + else + { + RegValue = 0; + if (Config->DuplexMode == GMAC_MODE_FULLDUPLEX) + { + if (Config->Speed == GMAC_SPEED_1000M) + { + RegValue = PHY_FULLDUPLEX_1000M; + } + else if (Config->Speed == GMAC_SPEED_100M) + { + RegValue = PHY_FULLDUPLEX_100M; + } + else if (Config->Speed == GMAC_SPEED_10M) + { + RegValue = PHY_FULLDUPLEX_10M; + } + } + else + { + if (Config->Speed == GMAC_SPEED_1000M) + { + RegValue = PHY_HALFDUPLEX_1000M; + } + else if (Config->Speed == GMAC_SPEED_100M) + { + RegValue = PHY_HALFDUPLEX_100M; + } + else if (Config->Speed == GMAC_SPEED_10M) + { + RegValue = PHY_HALFDUPLEX_10M; + } + } + + /* AutoNegotiation Disable */ + if (Gmac_WritePHYRegister(Config, PHY_BCR_OFFSET, RegValue) != FST_SUCCESS) + { + GMAC_HW_DEBUG_E("PHY_BCR_OFFSET 3 is error \r\n"); + ret = FST_FAILURE; + goto gmac_init; + } + } + Ft_GenericTimer_UsDelay(PHY_CONFIG_DELAY_US); + +gmac_init: + + FGmac_MACDMAInit(Config); + return ret; +} + +void FGmac_TransmissionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue |= GMAC_CONTROL_TE; + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} + +void FGmac_TransmissionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue &= ~GMAC_CONTROL_TE; + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} + +void FGmac_ReceptionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue |= GMAC_CONTROL_RE; + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} + +void FGmac_ReceptionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue &= ~GMAC_CONTROL_RE; + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} + +void FGmac_FlushTransmitFIFO(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue |= DMA_OP_FTF; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +void FGmac_DMATransmissionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue |= DMA_OP_ST; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +/** + * @name: FGmac_DMATransmissionDisable + * @msg: Disable the DMA transmission + * @param {FGmac_Config_t} *Config + * @return {*} + */ +void FGmac_DMATransmissionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue &= ~DMA_OP_ST; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +/** + * @name: FGmac_DMAReceptionEnable + * @msg: Enable the DMA reception + * @param {FGmac_Config_t} *Config + * @return {*} + */ +void FGmac_DMAReceptionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue |= DMA_OP_SR; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +void FGmac_DMAReceptionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue &= ~DMA_OP_SR; + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +void FGmac_DMAReceptionTransmissionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue |= (DMA_OP_SR | DMA_OP_ST); + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +void FGmac_DMAReceptionTransmissionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + DMA_OP_OFFSET); + RegValue &= ~(DMA_OP_SR | DMA_OP_ST); + Ft_out32(Config->BaseAddress + DMA_OP_OFFSET, RegValue); +} + +void FGmac_ReceptionTransmissionEnable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue |= (GMAC_CONTROL_RE | GMAC_CONTROL_TE); + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} + +void FGmac_ReceptionTransmissionDisable(FGmac_Config_t *Config) +{ + u32 RegValue; + RegValue = Ft_in32(Config->BaseAddress + GMAC_CONTROL_OFFSET); + RegValue &= ~(GMAC_CONTROL_RE | GMAC_CONTROL_TE); + Ft_out32(Config->BaseAddress + GMAC_CONTROL_OFFSET, RegValue); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.h b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.h new file mode 100644 index 0000000000..4842b72c69 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_hw.h @@ -0,0 +1,577 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:25:10 + * @Description:  This files is for gmac register + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef GMAC_HW_H +#define GMAC_HW_H +#include "ft_types.h" +#include "ft_gmac.h" +/* Register Offset */ + +#define GMAC_CONTROL_OFFSET 0x00000000U /* Configuration */ +#define GMAC_FRAME_FILTER_OFFSET 0x00000004U /* Frame Filter */ +#define GMAC_HASH_HIGH_OFFSET 0x00000008U /* Multicast Hash Table High */ +#define GMAC_HASH_LOW_OFFSET 0x0000000cU /* Multicast Hash Table Low */ +#define GMAC_MII_ADDR_OFFSET 0x00000010U /* MII Address */ +#define GMAC_MII_DATA_OFFSET 0x00000014U /* MII Data */ +#define GMAC_FLOW_CTRL_OFFSET 0x00000018U /* Flow Control */ +#define GMAC_VLAN_TAG_OFFSET 0x0000001cU /* VLAN Tag */ +#define GMAC_VERSION_OFFSET 0x00000020U /* GMAC CORE Version */ +#define GMAC_INTERNAL_MODULE_STATUS_OFFSET 0x00000024U /* 给出各种内部块的状态以进行调试。 */ +#define GMAC_LPI_CONTROL_STATUS_OFFSET 0x00000030U /* 控制低功耗空闲(LPI)操作并提供内核的 LPI 状态。 */ +#define GMAC_LPI_TIMER_CONTROL_OFFSET 0x00000034U /* 控制 LPI 状态中的超时值。 */ +#define GMAC_ISR_STATUS_OFFSET 0x00000038U /* 中断状态。 */ +#define GMAC_ISR_MASK_OFFSET 0x0000003CU /* 生成中断的掩码 */ +#define GMAC_MAC_ADDR0_UPPER16BIT_OFFSET 0x00000040U /* 第一个 MAC 地址的高 16 位。 */ +#define GMAC_MAC_ADDR0_LOWER16BIT_OFFSET 0x00000044U /* 第一个 MAC 地址的低 32 位。。 */ +#define GMAC_MAC_ADDR1_UPPER16BIT_OFFSET 0x00000048U /* 第二个 MAC 地址的高 16 位。 */ +#define GMAC_MAC_ADDR1_LOWER16BIT_OFFSET 0x0000004CU /* 第二个 MAC 地址的低 32 位。。 */ +#define GMAC_MAC_MAC_PHY_STATUS 0x000000D8U /* MAC PHY 状态 */ + +#define DMA_BUS_MODE_OFFSET 0x00001000U /* Bus Mode */ +#define DMA_XMT_POLL_DEMAND_OFFSET 0x00001004U /* Transmit Poll Demand */ +#define DMA_RCV_POLL_DEMAND_OFFSET 0x00001008U /* Received Poll Demand */ +#define DMA_RCV_BASE_ADDR_OFFSET 0x0000100cU /* Receive List Base */ +#define DMA_TX_BASE_ADDR_OFFSET 0x00001010U /* Transmit List Base */ +#define DMA_STATUS_OFFSET 0x00001014U /* Status Register */ +#define DMA_OP_OFFSET 0x00001018U /* Ctrl (Operational Mode) */ +#define DMA_INTR_ENA_OFFSET 0x0000101cU /* Interrupt Enable */ +#define DMA_MISSED_FRAME_CTR_OFFSET 0x00001020U /* Missed Frame Counter */ +#define DMA_RX_WATCHDOG_OFFSET 0x1024U /* Receive Interrupt Watchdog */ +#define DMA_AXI_BUS_MOD_OFFSET 0x1028U /* 控制 AXI 主行为 */ +#define DMA_AXI_BUS_STATUS_OFFSET 0x102CU /* 控制 AXI 状态 */ + +/* MMC control */ +#define MMC_CNTRL 0x0100U /* MMC Control */ +#define MMC_RX_INTR 0x0104U /* MMC RX Interrupt */ +#define MMC_TX_INTR 0x0108U /* MMC TX Interrupt */ +#define MMC_RX_INTR_MASK 0x010cU /* MMC Interrupt Mask */ +#define MMC_TX_INTR_MASK 0x0110U /* MMC Interrupt Mask */ +#define MMC_RX_IPC_INTR_MASK 0x0200U +#define MMC_RX_IPC_INTR 0x0208U + +/** MAC 配置寄存器 **/ +#define GMAC_CONTROL_2K 0x08000000U /* IEEE 802.3as 2K packets */ +#define GMAC_CONTROL_CST 0x2000000U +#define GMAC_CONTROL_TC 0x01000000U /* Transmit Conf. in RGMII/SGMII */ +#define GMAC_CONTROL_WD 0x00800000U /* Disable Watchdog on receive */ +#define GMAC_CONTROL_JD 0x00400000U /* Jabber disable */ +#define GMAC_CONTROL_BE 0x00200000U /* Frame Burst Enable */ +#define GMAC_CONTROL_JE 0x00100000U /* Jumbo frame */ +#define GMAC_CONTROL_IFG(x) ((x & 7) << 17) /* 帧内间隔 ,000 96bit times 001 88bit times 010 80bit times … 111 40bit times*/ +#define GMAC_CONTROL_DCRS 0x00010000U /* Disable carrier sense */ +#define GMAC_CONTROL_PS 0x00008000U /* Port Select 0:GMII 1:MII */ +#define GMAC_CONTROL_FES 0x00004000U /* Speed 0:10 1:100 */ +#define GMAC_CONTROL_DO 0x00002000U /* Disable Rx Own */ +#define GMAC_CONTROL_LM 0x00001000U /* Loop-back mode */ +#define GMAC_CONTROL_DM 0x00000800U /* Duplex Mode . 1 is Duplex */ +#define GMAC_CONTROL_IPC 0x00000400U /* Checksum Offload */ +#define GMAC_CONTROL_DR 0x00000200U /* Disable Retry */ +#define GMAC_CONTROL_LUD 0x00000100U /* Link up/down */ +#define GMAC_CONTROL_ACS 0x00000080U /* Auto Pad/FCS Stripping */ +#define GMAC_CONTROL_BL(x) ((x & 3) << 5) +#define GMAC_CONTROL_DC 0x00000010U /* Deferral Check */ +#define GMAC_CONTROL_TE 0x00000008U /* Transmitter Enable */ +#define GMAC_CONTROL_RE 0x00000004U /* Receiver Enable */ +#define GMAC_CONTROL_INIT (GMAC_CONTROL_DO | GMAC_CONTROL_JD | GMAC_CONTROL_ACS | GMAC_CONTROL_IPC | GMAC_CONTROL_BE | GMAC_CONTROL_DM | GMAC_CONTROL_WD | GMAC_CONTROL_CST) + +/** Frame Filter **/ +#define GMAC_FRAME_FILTER_PR 0x00000001U +#define GMAC_FRAME_FILTER_HUC 0x00000002U +#define GMAC_FRAME_FILTER_HMC 0x00000004U +#define GMAC_FRAME_FILTER_DAIF 0x00000008U +#define GMAC_FRAME_FILTER_PM 0x00000010U +#define GMAC_FRAME_FILTER_DBF 0x00000020U +#define GMAC_FRAME_FILTER_PCF(x) ((x & 3) << 6) +#define GMAC_FRAME_FILTER_SAIF 0x00000100U +#define GMAC_FRAME_FILTER_SAF 0x00000200U +#define GMAC_FRAME_FILTER_HPF 0x00000400U +#define GMAC_FRAME_FILTER_RA 0x80000000U + +/** 哈希表高位寄存器 **/ +#define GMAC_HASH_HIGH_HTH 0xffffffffUL /* 该字段包含 Hash 表的高 32 位。 */ + +/** 哈希表低位寄存器 **/ +#define GMAC_HASH_LOW_HTH 0xffffffffUL /* 该字段包含 Hash 表的低 32 位。 */ + +/** GMII 地址寄存器 **/ +#define GMAC_MII_ADDR_GB 0x00000001 +#define GMAC_MII_ADDR_GW 0x00000002 +#define GMAC_MII_ADDR_CR(x) ((x & 0xf) << 2) +#define GMAC_MII_ADDR_GR(x) ((x & 0x1f) << 6) +#define GMAC_MII_ADDR_PA(x) ((x & 0x1f) << 11) + +/** GMII 数据寄存器 **/ +#define GMAC_MII_DATA_GD 0x0000ffffU + +/** 流控寄存器 **/ +#define GMAC_FLOW_FCB 0x00000001U +#define GMAC_FLOW_BPA GMAC_FLOW_FCB +#define GMAC_FLOW_TFE 0x00000002U +#define GMAC_FLOW_RFE 0x00000004U +#define GMAC_FLOW_UP 0x00000008U +#define GMAC_FLOW_PLT(x) ((x & 3) << 3) +#define GMAC_FLOW_DZPQ 0x00000080U +#define GMAC_FLOW_PT 0xffff0000U + +/** VLAN 标记寄存器 **/ +// #define GMAC_VLAN_TAG_VL 0x0000ffffU +#define GMAC_VLAN_TAG_VL(x) (x & 0xffffU) +#define GMAC_VLAN_TAG_ETV 0x00010000U + +/** 版本寄存器 **/ +#define GMAC_VERSION_UDV 0x00FF0000U /* 用户定义版本号 */ +#define GMAC_VERSION_SDV 0x000000ffU /* 硬件定议版本号 */ + +/** LPI 控制和状态寄存器 **/ +#define GMAC_LPI_CONTROL_STATUS_TLPIEN 0x00000001U +#define GMAC_LPI_CONTROL_STATUS_TLPIEX 0x00000002U +#define GMAC_LPI_CONTROL_STATUS_RLPIEN 0x00000004U +#define GMAC_LPI_CONTROL_STATUS_RLPIEX 0x00000008U +#define GMAC_LPI_CONTROL_STATUS_TLPIST 0x00000100U +#define GMAC_LPI_CONTROL_STATUS_RLPIST 0x00000200U +#define GMAC_LPI_CONTROL_STATUS_LPIEN 0x00010000U +#define GMAC_LPI_CONTROL_STATUS_PLS 0x00020000U +#define GMAC_LPI_CONTROL_STATUS_PLSEN 0x00040000U +#define GMAC_LPI_CONTROL_STATUS_LPITXA 0x00080000U + +/** LPI 定时器控制寄存器 **/ +#define GMAC_LPI_TIMER_TWT 0x0000ffffU +#define GMAC_LPI_TIMER_LIT 0x3FF0000U + +/** 中断状态寄存器 **/ +#define GMAC_ISR_STATUS_RSIS 0x00000001U +#define GMAC_ISR_STATUS_PCSLSC 0x00000002U +#define GMAC_ISR_STATUS_PCSANC 0x00000004U +#define GMAC_ISR_STATUS_PMTIS 0x00000008U +#define GMAC_ISR_STATUS_MMCIS 0x00000010U +#define GMAC_ISR_STATUS_MMCRIS 0x00000020U +#define GMAC_ISR_STATUS_MMCTIS 0x00000040U +#define GMAC_ISR_STATUS_MMCRCOIS 0x00000080U +#define GMAC_ISR_STATUS_TIS 0x00000200U +#define GMAC_ISR_STATUS_LPIIS 0x00000400U + +/** 中断屏蔽寄存器 **/ +#define GMAC_ISR_MASK_RSIM 0x00000001U /* RGMII/SMII 中断屏蔽 */ +#define GMAC_ISR_MASK_PCSLSIM 0x00000002U +#define GMAC_ISR_MASK_PCSANCIM 0x00000004U +#define GMAC_ISR_MASK_PMTIM 0x00000008U +#define GMAC_ISR_MASK_TIM 0x00000020U +#define GMAC_ISR_MASK_LPIIM 0x00000040U + +/** MAC 地址 0 高寄存器 **/ +#define GMAC_MAC_ADDR0_UPPER16BIT_A 0x0000ffffU + +/** MAC 地址 0 低寄存器 **/ +#define GMAC_MAC_ADDR0_LOWERER16BIT_A 0xffffffffU + +/** MAC 地址 1 高寄存器 **/ +#define GMAC_MAC_ADDR1_UPPER16BIT_A 0x0000ffffU +#define GMAC_MAC_ADDR1_UPPER16BIT_MBC 0x3f000000U +#define GMAC_MAC_ADDR1_UPPER16BIT_SA 0x40000000U +#define GMAC_MAC_ADDR1_UPPER16BIT_AE 0x80000000U + +/** MAC 地址 1 低寄存器 **/ +#define GMAC_MAC_ADDR1_LOWER16BIT_A 0xffffffffU + +/* GMAC DMA 寄存器 */ +/** 总线模式寄存器 **/ +#define DMA_BUS_PRWG 0x030000000U +#define DMA_BUS_TXPR 0x08000000U +#define DMA_BUS_MB 0x04000000U +#define DMA_BUS_AAL 0x02000000U +#define DMA_BUS_8xPBL 0x01000000U +#define DMA_BUS_USP 0x00800000U +#define DMA_BUS_RPBL(x) ((x & 0x3f) << 17) +#define DMA_BUS_FB 0x00010000U /* Fixed Burst */ +#define DMA_BUS_PR(x) ((x & 0x3) << 14) /* 00: 1:1 ,01: 2:1 ,10: 3:1 ,11: 4:1 */ +#define DMA_BUS_PBL(x) ((x & 0x3f) << 8) +#define DMA_BUS_ATDS 0x00000080U +#define DMA_BUS_DSL 0x0000007CU /* Descriptor Skip Length */ +#define DMA_BUS_DA 0x00000002U /* DMA Arbitration Scheme,Rx High Pro */ +#define DMA_BUS_SWR 0x00000001U /* Software Reset */ + +#define DMA_BUS_INIT (DMA_BUS_FB | DMA_BUS_PBL(16) | DMA_BUS_RPBL(16)) + +/** 发送轮询请求寄存器 **/ +#define DMA_XMT_POLL_DEMAND_TPD 0xffffffffU + +/** 发送轮询请求寄存器 **/ +#define DMA_RCV_POLL_DEMAND_RPD 0xffffffffU + +/** 接收描述符列表地址寄存器 **/ +#define DMA_RCV_BASE_ADDR_START_REC_LIST 0xfffffff0U + +/** 发送描述符列表地址寄存器 **/ +#define DMA_TX_BASE_ADDR_START_TRA_LIST 0xfffffff0U + +/** + * @brief Bit definition of TDES0 register: DMA Tx descriptor status register + */ +#define BIT(bitnum) (1 << (bitnum % 32)) +#define GENMASK(h, l) \ + (((~0UL) - (1UL << (l)) + 1) & (~0UL >> (32 - 1 - (h)))) + +#define DMA_TDES0_DEFERRED BIT(0) +#define DMA_TDES0_UNDERFLOW_ERROR BIT(1) +#define DMA_TDES0_EXCESSIVE_DEFERRAL BIT(2) +#define DMA_TDES0_COLLISION_COUNT_MASK GENMASK(6, 3) +#define DMA_TDES0_VLAN_FRAME BIT(7) +#define DMA_TDES0_EXCESSIVE_COLLISIONS BIT(8) +#define DMA_TDES0_LATE_COLLISION BIT(9) +#define DMA_TDES0_NO_CARRIER BIT(10) +#define DMA_TDES0_LOSS_CARRIER BIT(11) +#define DMA_TDES0_PAYLOAD_ERROR BIT(12) +#define DMA_TDES0_FRAME_FLUSHED BIT(13) +#define DMA_TDES0_JABBER_TIMEOUT BIT(14) +#define DMA_TDES0_ERROR_SUMMARY BIT(15) +#define DMA_TDES0_IP_HEADER_ERROR BIT(16) +#define DMA_TDES0_TIME_STAMP_STATUS BIT(17) +#define DMA_TDES0_OWN ((u32)BIT(31)) /* silence sparse */ +/* TDES1 */ +#define DMA_TDES1_BUFFER1_SIZE_MASK GENMASK(10, 0) +#define DMA_TDES1_BUFFER2_SIZE_MASK GENMASK(21, 11) +#define DMA_TDES1_BUFFER2_SIZE_SHIFT 11 +#define DMA_TDES1_TIME_STAMP_ENABLE BIT(22) +#define DMA_TDES1_DISABLE_PADDING BIT(23) +#define DMA_TDES1_SECOND_ADDRESS_CHAINED BIT(24) +#define DMA_TDES1_END_RING BIT(25) +#define DMA_TDES1_CRC_DISABLE BIT(26) +#define DMA_TDES1_CHECKSUM_INSERTION_MASK GENMASK(28, 27) +#define DMA_TDES1_CHECKSUM_INSERTION_SHIFT 27 +#define DMA_TDES1_FIRST_SEGMENT BIT(29) +#define DMA_TDES1_LAST_SEGMENT BIT(30) +#define DMA_TDES1_INTERRUPT BIT(31) + +/* Bit definition of RDES0 register: DMA Rx descriptor status register */ +/* RDES0 */ +#define DMA_RDES0_PAYLOAD_CSUM_ERR BIT(0) +#define DMA_RDES0_CRC_ERROR BIT(1) +#define DMA_RDES0_DRIBBLING BIT(2) +#define DMA_RDES0_MII_ERROR BIT(3) +#define DMA_RDES0_RECEIVE_WATCHDOG BIT(4) +#define DMA_RDES0_FRAME_TYPE BIT(5) +#define DMA_RDES0_COLLISION BIT(6) +#define DMA_RDES0_IPC_CSUM_ERROR BIT(7) +#define DMA_RDES0_LAST_DESCRIPTOR BIT(8) +#define DMA_RDES0_FIRST_DESCRIPTOR BIT(9) +#define DMA_RDES0_VLAN_TAG BIT(10) +#define DMA_RDES0_OVERFLOW_ERROR BIT(11) +#define DMA_RDES0_LENGTH_ERROR BIT(12) +#define DMA_RDES0_SA_FILTER_FAIL BIT(13) +#define DMA_RDES0_DESCRIPTOR_ERROR BIT(14) +#define DMA_RDES0_ERROR_SUMMARY BIT(15) +#define DMA_RDES0_FRAME_LEN_MASK (0x3FFF << 16) /*GENMASK(29, 16)*/ +#define DMA_RDES0_FRAME_LEN_SHIFT 16 +#define DMA_RDES0_DA_FILTER_FAIL BIT(30) +#define DMA_RDES0_OWN BIT(31) +/* RDES1 */ +#define DMA_RDES1_BUFFER1_SIZE_MASK GENMASK(10, 0) +#define DMA_RDES1_BUFFER2_SIZE_MASK GENMASK(21, 11) +#define DMA_RDES1_BUFFER2_SIZE_SHIFT 11 +#define DMA_RDES1_SECOND_ADDRESS_CHAINED BIT(24) +#define DMA_RDES1_END_RING BIT(25) +#define DMA_RDES1_DISABLE_IC BIT(31) + +/** DMA Status register defines **/ +#define DMA_STATUS_GLPII 0x40000000 /* GMAC LPI interrupt */ +#define DMA_STATUS_GPI 0x10000000 /* PMT interrupt */ +#define DMA_STATUS_GMI 0x08000000 /* MMC interrupt */ +#define DMA_STATUS_GLI 0x04000000 /* GMAC Line interface int */ +#define DMA_STATUS_EB_MASK 0x00380000 /* Error Bits Mask */ +#define DMA_STATUS_EB_TX_ABORT 0x00080000 /* Error Bits - TX Abort */ +#define DMA_STATUS_EB_RX_ABORT 0x00100000 /* Error Bits - RX Abort */ +#define DMA_STATUS_TS_MASK 0x00700000 /* Transmit Process State */ +#define DMA_STATUS_RS_MASK 0x000e0000 /* Receive Process State */ +#define DMA_STATUS_NIS 0x00010000 /* Normal Interrupt Summary */ +#define DMA_STATUS_AIS 0x00008000 /* Abnormal Interrupt Summary */ +#define DMA_STATUS_ERI 0x00004000 /* Early Receive Interrupt */ +#define DMA_STATUS_FBI 0x00002000 /* Fatal Bus Error Interrupt */ +#define DMA_STATUS_ETI 0x00000400 /* Early Transmit Interrupt */ +#define DMA_STATUS_RWT 0x00000200 /* Receive Watchdog Timeout */ +#define DMA_STATUS_RPS 0x00000100 /* Receive Process Stopped */ +#define DMA_STATUS_RU 0x00000080 /* Receive Buffer Unavailable */ +#define DMA_STATUS_RI 0x00000040 /* Receive Interrupt */ +#define DMA_STATUS_UNF 0x00000020 /* Transmit Underflow */ +#define DMA_STATUS_OVF 0x00000010 /* Receive Overflow */ +#define DMA_STATUS_TJT 0x00000008 /* Transmit Jabber Timeout */ +#define DMA_STATUS_TU 0x00000004 /* Transmit Buffer Unavailable */ +#define DMA_STATUS_TPS 0x00000002 /* Transmit Process Stopped */ +#define DMA_STATUS_TI 0x00000001 /* Transmit Interrupt */ +#define DMA_STATUS_INIT 0xFFFFFFFF + +/** DMA 操作模式寄存器 **/ +#define DMA_OP_DT 0x04000000U /* No Dropping of TCP/IP csum Err Frame */ +#define DMA_OP_RSF 0x02000000U /* Rx Store and Forward */ +#define DMA_OP_DFF 0x01000000U /* */ +#define DMA_OP_TSF 0x000200000U /* Tx Store and Forward */ +#define DMA_OP_FTF 0x000100000U /* Flush Tx FIFO */ +#define DMA_OP_TTC(x) ((x & 7) << 14) /* Tx Threshold Control ,• 000 64 , 001 128 , 010 192 ,011 256 ,100 40 , 101 32 , 110 24 , 111 16*/ +#define DMA_OP_ST 0x000002000U /* Start/Stop Tx */ +#define DMA_OP_RFD(x) ((x & 0x3) << 11) /* Threshold for DeActive Flow Control */ +#define DMA_OP_RFA 0x000000600U /* Threshold for Active Flow Control */ +#define DMA_OP_EFC 0x000000100U /* Enable HW Flow control */ +#define DMA_OP_FEF 0x000000080U /* Forward Error Frame */ +#define DMA_OP_FUF 0x000000040U /* Forward Undersize Good Frame */ +#define DMA_OP_RTC 0x000000018U /* Rx Threshold Control */ +#define DMA_OP_OSF 0x000000004U /* Operate On Second Mode */ +#define DMA_OP_SR 0x00000002U /* Start/Stop Rx */ +#define DMA_OP_CLEAR_MASK ((u32)0xF8DE3F23U) +#define DMA_OP_INIT (DMA_OP_SR | DMA_OP_RSF) + +/** 中断使能寄存器 **/ +#define DMA_INTR_ENA_TIE 0x00000001U /* Transmit Interrupt */ +#define DMA_INTR_ENA_TSE 0x00000002U /* 传输停止启用 */ +#define DMA_INTR_ENA_TUE 0x00000004U /* Transmit Buffer Unavailable */ +#define DMA_INTR_ENA_THE 0x00000008U /* 发送 Jabber 超时启用 */ +#define DMA_INTR_ENA_OVE 0x00000010U /* 溢出中断使能 */ +#define DMA_INTR_ENA_UNE 0x00000020U /* 下溢中断使能 */ +#define DMA_INTR_ENA_RIE 0x00000040U /* Receive Interrupt */ +#define DMA_INTR_ENA_RUE 0x00000080U /* 接收缓冲区不可用启用 */ +#define DMA_INTR_ENA_RSE 0x00000100U /* 接收已停止启用 */ +#define DMA_INTR_ENA_RWE 0x00000200U /* 接收看门狗超时使能 */ +#define DMA_INTR_ENA_ETE 0x00000400U /* 早期发送中断使能 */ +#define DMA_INTR_ENA_FBE 0x00002000U /* Fatal Bus Error */ +#define DMA_INTR_ENA_ERE 0x00004000U /* Early Receive */ +#define DMA_INTR_ENA_AIE 0x00008000 /* Abnormal Summary */ +#define DMA_INTR_ENA_NIE 0x00010000 /* Normal Summary */ + +#define DMA_INTR_DEFAULT (DMA_INTR_ENA_TIE | DMA_INTR_ENA_TSE | DMA_INTR_ENA_TUE | DMA_INTR_ENA_THE | DMA_INTR_ENA_OVE | DMA_INTR_ENA_UNE | DMA_INTR_ENA_RIE | DMA_INTR_ENA_RUE | DMA_INTR_ENA_RSE | DMA_INTR_ENA_RWE | DMA_INTR_ENA_ETE | DMA_INTR_ENA_FBE | DMA_INTR_ENA_ERE | DMA_INTR_ENA_AIE | DMA_INTR_ENA_NIE) + +/** 丢帧和缓冲区溢出计数器寄存器 **/ +#define DMA_MISSED_FRAME_CTR_CMIS 0x0000FFFFUDMA_MISSED_FRAME +#define DMA_MISSED_FRAME_CTR_OVMIS 0x00010000U +#define DMA_MISSED_FRAME_CTR_CFIFO 0x0ffe0000U +#define DMA_MISSED_FRAME_CTR_OVFIFO 0x10000000U + +/** 接收中断看门狗定时器寄存器 **/ +#define DMA_RX_WATCHDOG_RIWT 0x0000000fU + +/** AXI_BUS_MOD **/ +#define DMA_AXI_BUS_MOD_UNDEF 0x00000001U /* AXI 未定义的突发长度 */ +#define DMA_AXI_BUS_MOD_BLEN4 0x00000002U /* AXI 突发长度 4 */ +#define DMA_AXI_BUS_MOD_BLEN8 0x00000004U /* AXI 突发长度 8 */ +#define DMA_AXI_BUS_MOD_BLEN16 0x00000008U /* AXI 突发长度 16 */ +#define DMA_AXI_BUS_MOD_BLEN32 0x00000010U /* AXI 突发长度 32 */ +#define DMA_AXI_BUS_MOD_BLEN64 0x00000020U /* AXI 突发长度 64 */ +#define DMA_AXI_BUS_MOD_BLEN128 0x00000040U /* AXI 突发长度 128 */ +#define DMA_AXI_BUS_MOD_BLEN256 0x00000080U /* AXI 突发长度 256 */ +#define DMA_AXI_BUS_MOD_AXI_AAL 0x00001000U /* 地址对齐的节拍 */ +#define DMA_AXI_BUS_MOD_RD_OSR_LMT(x) ((x & 0xf) << 16) /* XI 最大读取未决请求限制此值限 制 AXI 读取接口上的最大未完成请求。 */ +#define DMA_AXI_BUS_MOD_WR_OSR_LMT(x) ((x & 0xf) << 20) /* AXI 最大写入未决请求限制此值 限制 AXI 写入接口上的最大未完成请求。 */ +#define DMA_AXI_BUS_MOD_UNLCK_ON_MGK_RWK 0x40000000U +#define DMA_AXI_BUS_MOD_EN_LPI 0x80000000U + +/** MMC Control **/ +#define MMC_DEFAULT_MASK 0xffffffff + +/* Common PHY Registers (AR8035) */ + +#define PHY_BCR_OFFSET ((u16)0x00) /* Transceiver Basic Control Register */ +#define PHY_BSR_OFFSET ((u16)0x01) /* Transceiver Basic Status Register */ +#define PHY_ID1_REG_OFFSET ((u16)0x02) /* PHY ID1 Identifier */ +#define PHY_ID2_REG_OFFSET ((u16)0x03) /* PHY ID2 Identifier */ +#define PHY_AUTO_NEGOTIATION_ADVERTISEMENT_OFFSET ((u16)0x04) +#define PHY_EXTENDED_CONTROL_REGISTER_OFFSET ((u16)0x9) +#define PHY_SPECIFIC_STATUS_OFFSET ((u16)0x11) +#define PHY_INTERRUPT_ENABLE_OFFSET ((u16)0x12) +#define PHY_INTERRUPT_STATUS_OFFSET ((u16)0x13) +#define PHY_DEBUG_ADDR_OFFSET ((u16)0x1D) +#define PHY_DEBUG_DATA_OFFSET ((u16)0x1E) + +/* MII control register bit */ + +#define PHY_BCR_1000 0x0040 /* 1 = 1000mb when \ + PHY_BCR_100 is also 1 */ +#define PHY_BCR_COLL_TEST 0x0080 /* collision test */ +#define PHY_BCR_FDX 0x0100 /* FDX =1, half duplex =0 */ +#define PHY_BCR_RESTART 0x0200 /* restart auto negotiation */ +#define PHY_BCR_ISOLATE 0x0400 /* isolate PHY from MII */ +#define PHY_BCR_POWER_DOWN 0x0800 /* power down */ +#define PHY_BCR_AUTO_EN 0x1000 /* auto-negotiation enable */ +#define PHY_BCR_100 0x2000 /* 0 = 10mb, 1 = 100mb */ +#define PHY_BCR_LOOPBACK 0x4000 /* 0 = normal, 1 = loopback */ +#define PHY_BCR_RESET 0x8000 /* 0 = normal, 1 = PHY reset */ +#define PHY_BCR_NORM_EN 0x0000 /* just enable the PHY */ +#define PHY_BCR_DEF_0_MASK 0xca7f /* they must return zero */ +#define PHY_BCR_RES_MASK 0x003f /* reserved bits,return zero */ + +#define PHY_ANAR_10TX_HD ((u16)0x0020) +#define PHY_ANAR_10TX_FD ((u16)0x0040) +#define PHY_ANAR_100TX_HD ((u16)0x0080) +#define PHY_ANAR_100TX_FD ((u16)0x0100) +#define PHY_ANAR_100T_4 ((u16)0x0200) +#define PHY_ANAR_PAUSE ((u16)0x0400) +#define PHY_ANAR_ASM_PAUSE ((u16)0x0800) +#define PHY_ANAR_REMORT_FAULT ((u16)0x2000) +#define PHY_ANAR_NEXT_PAGE ((u16)0x8000) +#define PHY_ANAR_PAUSE_MASK ((u16)0x0c00) + +#define PHY_BSR_EXTENDED_STATUS ((u16)0x100) + +#define PHY_EXTENDED_CONTROL_1000T_FD ((u16)0x200) +#define PHY_EXTENDED_CONTROL_1000T_HD ((u16)0x100) + +#define PHY_RESET ((u16)0x8000U) /* PHY Reset */ +#define PHY_LOOPBACK ((u16)0x4000U) /* Select loop-back mode */ +#define PHY_FULLDUPLEX_1000M ((u16)0x2140U) /* Set the full-duplex mode at 1000 Mb/s */ +#define PHY_HALFDUPLEX_1000M ((u16)0x2040U) /* Set the half-duplex mode at 1000 Mb/s */ +#define PHY_FULLDUPLEX_100M ((u16)0x2100U) /* Set the full-duplex mode at 100 Mb/s */ +#define PHY_HALFDUPLEX_100M ((u16)0x2000U) /* Set the half-duplex mode at 100 Mb/s */ +#define PHY_FULLDUPLEX_10M ((u16)0x0100U) /* Set the full-duplex mode at 10 Mb/s */ +#define PHY_HALFDUPLEX_10M ((u16)0x0000U) /* Set the half-duplex mode at 10 Mb/s */ +#define PHY_AUTONEGOTIATION ((u16)0x1000U) /* Enable auto-negotiation function */ +#define PHY_RESTART_AUTONEGOTIATION ((u16)0x0200U) /* Restart auto-negotiation function */ +#define PHY_POWERDOWN ((u16)0x0800U) /* Select the power down mode */ +#define PHY_ISOLATE ((u16)0x0400U) /* Isolate PHY from MII */ + +#define PHY_AUTONEGO_COMPLETE ((u16)0x0020U) /* Auto-Negotiation process completed */ +#define PHY_LINKED_STATUS ((u16)0x0004U) /* Valid link established */ +#define PHY_JABBER_DETECTION ((u16)0x0002U) /* Jabber condition detected */ + +#define PHY_SPECIFIC_STATUS_DUPLEX ((u16)0x2000) /* 0 is Half-duplex ,1 is Full-duplex */ +#define PHY_SPECIFIC_STATUS_SPEED ((u16)0xc000) /* 0 is 10Mbps ,1 is 100Mbps , 2 is 1000Mbps */ + +#define PHY_INTERRUPT_ENABLE_WAKE_ON_LAN 0x00000001U /* Wake on LAN interrupt enable,0 Interrupt disable , 1 Interrupt enable */ +#define PHY_INTERRUPT_ENABLE_POLARITY_CHANGED 0x00000002U /* Polarity Changed interrupt enable,0 Interrupt disable , 1 Interrupt enable */ +#define PHY_INTERRUPT_ENABLE_WIRESPEED_DOWNGRADE 0x00000020U /* Wirespeed downgrade Interrupt ,0 Interrupt disable , 1 Interrupt enable*/ +#define PHY_INTERRUPT_ENABLE_LINK_SUCCESS 0x00000400U /* Link success interrupt ,0 Interrupt disable , 1 Interrupt enable*/ +#define PHY_INTERRUPT_ENABLE_LINK_FAIL 0x00000800U /* Link fail interrupt, 0 Interrupt disable , 1 Interrupt enable */ +#define PHY_INTERRUPT_ENABLE_PAGE_RECEIVED 0x00001000U /* Page Received, 0 Interrupt disable , 1 Interrupt enable */ +#define PHY_INTERRUPT_ENABLE_DUPLEX_CHANGED 0x00002000U /* Duplex Changed, 0 Interrupt disable , 1 Interrupt enable*/ +#define PHY_INTERRUPT_ENABLE_SPEED_CHANGED 0x00004000U /* Speed Changed , 0 Interrupt disable , 1 Interrupt enable */ +#define PHY_INTERRUPT_ENABLE_AUTO_NEGOTIATION_ERROR 0x00008000U /* Auto-Negotiation Error , 0 Interrupt disable , 1 Interrupt enable */ + +#define PHY_INTERRUPT_STATUS_WAKE_ON_LAN 0x00000001U /* Wake on LAN ,0 No Wake-on-LAN packet is received , 1 Wake-on-LAN packet is received */ +#define PHY_INTERRUPT_STATUS_POLARITY_CHANGED 0x00000002U /* Polarity Changed ,0 Polarity changed , 1 Polarity not changed */ +#define PHY_INTERRUPT_STATUS_WIRESPEED_DOWNGRADE 0x00000020U /* Wirespeed downgrade Interrupt ,0 No Smartspeed interrupt detected , 1 Smartspeed interrupt detected */ +#define PHY_INTERRUPT_STATUS_LINK_SUCCESS 0x00000400U /* Link success interrupt ,0 Link up not happened , 1 Link up happened.*/ +#define PHY_INTERRUPT_STATUS_LINK_FAIL 0x00000800U /* Link fail interrupt, 0 Link down not happened , 1 Link down happened. */ +#define PHY_INTERRUPT_STATUS_PAGE_RECEIVED 0x00001000U /* Page Received, 0 Page not received , 1 Page received */ +#define PHY_INTERRUPT_STATUS_DUPLEX_CHANGED 0x00002000U /* Duplex Changed, 0 Duplex not changed , 1 Duplex changed*/ +#define PHY_INTERRUPT_STATUS_SPEED_CHANGED 0x00004000U /* Speed Changed , 0 Speed not changed , 1 Speed changed */ +#define PHY_INTERRUPT_STATUS_AUTO_NEGOTIATION_ERROR 0x00008000U /* Auto-Negotiation Error , 0 No Auto-Negotiation Error , 1 Auto-Negotiation Error */ + +/** + * @name: FGmac_InitializeHw + * @msg: 初始化Mac层与Phy层参数 。 + * @param {FGmac_Config_t} *Config 包含Mac层 与Phy层,配置参数。 + * @return {s32} Common_status 参数。 + */ +s32 FGmac_InitializeHw(FGmac_Config_t *Config); + +/** + * @name: FGmac_ReadPHYRegister + * @msg: 读取Phy 中的寄存器 + * @param {FGmac_Config_t} *Config 提供读取的基地址,与Phy读取过程中需要的相关参数。 + * @param {u16} PHYReg 需要读取Phy 芯片的寄存器地址。 + * @param {u32} *RegValue 读取出来的寄存器参数 + * @return {s32} Common_status 参数。 + */ +s32 FGmac_ReadPHYRegister(FGmac_Config_t *Config, u16 PHYReg, u32 *RegValue); + +/** + * @name: Gmac_WritePHYRegister + * @msg: 向Phy 中的特定寄存器写入参数。 + * @param {FGmac_Config_t} *Config 提供读取的基地址,与Phy读取过程中需要的相关参数。 + * @param {u16} PHYReg 需要读取Phy 芯片的寄存器地址。 + * @param {u32} RegValue 需要写入的寄存器参数 + * @return {s32} Common_status 参数。 + */ +s32 Gmac_WritePHYRegister(FGmac_Config_t *Config, u16 PHYReg, u32 RegValue); + +/** + * @name: FGmac_TransmissionEnable + * @msg: 使能 Gmac 开始发送功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {None} + */ +void FGmac_TransmissionEnable(FGmac_Config_t *Config); + +/** + * @name: FGmac_TransmissionDisable + * @msg: 禁止 Gmac 开始发送功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {None} + */ +void FGmac_TransmissionDisable(FGmac_Config_t *Config); + +/** + * @name: FGmac_ReceptionEnable + * @msg: 使能 Gmac 开始接收功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {None} + */ +void FGmac_ReceptionEnable(FGmac_Config_t *Config); + +/** + * @name: FGmac_ReceptionDisable + * @msg: 禁止 Gmac 开始接收功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {None} + */ +void FGmac_ReceptionDisable(FGmac_Config_t *Config); + +/** + * @name: FGmac_DMAReceptionTransmissionEnable + * @msg: 使能 Gmac 开始DMA描述符接收与写入功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_DMAReceptionTransmissionEnable(FGmac_Config_t *Config); + +/** + * @name: FGmac_ReceptionTransmissionEnable + * @msg: 使能 Gmac 开始接收与发送功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_ReceptionTransmissionEnable(FGmac_Config_t *Config); + +/** + * @name: FGmac_FlushTransmitFIFO + * @msg: 刷新 Gmac 发送 FIFO + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_FlushTransmitFIFO(FGmac_Config_t *Config); + +/** + * @name: FGmac_DMATransmissionEnable + * @msg: 使能 DMA描述符发送功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_DMATransmissionEnable(FGmac_Config_t *Config); +/** + * @name: FGmac_DMATransmissionDisable + * @msg: 关闭 DMA描述符发送功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_DMATransmissionDisable(FGmac_Config_t *Config); +/** + * @name: FGmac_DMAReceptionEnable + * @msg: 使能 DMA描述符接收功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_DMAReceptionEnable(FGmac_Config_t *Config); +/** + * @name: FGmac_DMAReceptionEnable + * @msg: 使能 DMA描述符接收功能 + * @param {FGmac_Config_t} *Config 提供Mac的基地址。 + * @return {*} + */ +void FGmac_DMAReceptionDisable(FGmac_Config_t *Config); +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_intr.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_intr.c new file mode 100644 index 0000000000..e6ac067493 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_intr.c @@ -0,0 +1,194 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-05-18 13:48:57 + * @Description:  This files is for gmac irq + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac_hw.h" +#include "ft_gmac.h" +#include "ft_status.h" +#include "ft_assert.h" +#include "ft_io.h" + +#include "ft_debug.h" +#define GMAC_INTR_DEBUG_TAG "GMAC_INTR" + +#define GMAC_INTR_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(GMAC_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_INTR_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(GMAC_INTR_DEBUG_TAG, format, ##__VA_ARGS__) +#define GMAC_INTR_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(GMAC_INTR_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @name: FGmac_SetHandler + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @param {FGmac_IsrCallbackSelect_t} SelectIndex + * @param {void} *FuncPtr + * @param {void} *Args + * @return {*} + */ +s32 FGmac_SetHandler(Ft_Gmac_t *Gmac, FGmac_IsrCallbackSelect_t SelectIndex, void *FuncPtr, + void *Args) +{ + Ft_assertNonvoid(Gmac != NULL); + Ft_assertNonvoid(FuncPtr != NULL); + Ft_assertNonvoid(Gmac->IsReady == FT_COMPONENT_IS_READLY); + + switch (SelectIndex) + { + case FT_GMAC_TX_COMPLETE_CB_ID: + /* code */ + Gmac->SendHandler = FuncPtr; + Gmac->SendArgs = Args; + break; + case FT_GMAC_RX_COMPLETE_CB_ID: + Gmac->RecvHandler = FuncPtr; + Gmac->RecvArgs = Args; + break; + case FT_GMAC_DMA_ERROR_CB_ID: + Gmac->ErrorHandler = FuncPtr; + Gmac->ErrorArgs = Args; + break; + case FT_GMAC_MAC_PHY_STATUS_CB_ID: + Gmac->StatusHandler = FuncPtr; + Gmac->StatusArgs = Args; + break; + default: + return FST_FAILURE; + } + + return FST_SUCCESS; +} + +/** + * @name: FGmac_ErrorCheck + * @msg: + * @param {Ft_Gmac_t} *Gmac + * @return {*} + */ +__STATIC_INLINE u32 FGmac_ErrorCheck(Ft_Gmac_t *Gmac) +{ + u32 RegValue = 0; + u32 ErrIsr_RegValue = 0; + u32 RetValue = 0; + RegValue = Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET); + ErrIsr_RegValue = Ft_in32(Gmac->Config.BaseAddress + DMA_INTR_ENA_OFFSET); + + if (((RegValue & DMA_STATUS_TPS) == DMA_STATUS_TPS) && ((ErrIsr_RegValue & DMA_INTR_ENA_TSE) == DMA_INTR_ENA_TSE)) + { + RetValue |= GMAC_ERROR_TRANSMIT_PROCESS_STOPPED; + } + + if (((RegValue & DMA_STATUS_TU) == DMA_STATUS_TU) && ((ErrIsr_RegValue & DMA_INTR_ENA_TUE) == DMA_INTR_ENA_TUE)) + { + RetValue |= GMAC_ERROR_TRANSMIT_UNAVAILABLE_STATUS; + } + + if (((RegValue & DMA_STATUS_TJT) == DMA_STATUS_TJT) && ((ErrIsr_RegValue & DMA_INTR_ENA_THE) == DMA_INTR_ENA_THE)) + { + RetValue |= GMAC_ERROR_TRANSMIT_JABBER_TIMEOUT; + } + + if (((RegValue & DMA_STATUS_OVF) == DMA_STATUS_OVF) && ((ErrIsr_RegValue & DMA_INTR_ENA_OVE) == DMA_INTR_ENA_OVE)) + { + RetValue |= GMAC_ERROR_RECEIVE_FIFO_OVERFLOW; + } + + if (((RegValue & DMA_STATUS_UNF) == DMA_STATUS_UNF) && ((ErrIsr_RegValue & DMA_INTR_ENA_UNE) == DMA_INTR_ENA_UNE)) + { + RetValue |= GMAC_ERROR_TRANSMIT_UNDERFLOW; + } + + if (((RegValue & DMA_STATUS_RU) == DMA_STATUS_RU) && ((ErrIsr_RegValue & DMA_INTR_ENA_RUE) == DMA_INTR_ENA_RUE)) + { + RetValue |= GMAC_ERROR_RECEIVE_BUFFER_UNAVAILABLE; + } + + if (((RegValue & DMA_STATUS_RPS) == DMA_STATUS_RPS) && ((ErrIsr_RegValue & DMA_INTR_ENA_RSE) == DMA_INTR_ENA_RSE)) + { + RetValue |= GMAC_ERROR_RECEIVE_PROCESS_STOPPED; + } + + if (((RegValue & DMA_STATUS_RWT) == DMA_STATUS_RWT) && ((ErrIsr_RegValue & DMA_INTR_ENA_RWE) == DMA_INTR_ENA_RWE)) + { + RetValue |= GMAC_ERROR_RECEIVE_WATCHDOG_TIMEOUT; + } + + if (((RegValue & DMA_STATUS_ETI) == DMA_STATUS_ETI) && ((ErrIsr_RegValue & DMA_INTR_ENA_ETE) == DMA_INTR_ENA_ETE)) + { + RetValue |= GMAC_ERROR_EARLY_TRANSMIT_INTERRUPT; + } + + if (((RegValue & DMA_STATUS_FBI) == DMA_STATUS_FBI) && ((ErrIsr_RegValue & DMA_INTR_ENA_FBE) == DMA_INTR_ENA_FBE)) + { + RetValue |= GMAC_ERROR_FATAL_BUS_ERROR; + } + + if (0U == RetValue) + { + RetValue |= GMAC_ERROR_UNDEFINED; + } + Ft_printf("error RetValue %x \r\n", RetValue); + return RetValue; +} + +/** + * @name: FGmac_IntrHandler + * @msg: + * @param {void} *Args + * @return {*} + */ +void FGmac_IntrHandler(void *Args) +{ + Ft_Gmac_t *Gmac; + u32 RegValue; + u32 MACRegValue; + Ft_assertVoid(Args != NULL); + Gmac = (Ft_Gmac_t *)Args; + + RegValue = Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET); + if ((RegValue)&DMA_STATUS_GLI) + { + MACRegValue = Ft_in32(Gmac->Config.BaseAddress + GMAC_MAC_MAC_PHY_STATUS); + if (Gmac->StatusHandler) + { + Gmac->StatusHandler(Gmac->StatusArgs, MACRegValue); + } + } + + /* Frame received */ + if ((RegValue & (DMA_STATUS_RI)) != 0) + { + if (Gmac->RecvHandler) + { + Gmac->RecvHandler(Gmac->RecvArgs); + } + + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_RI); + } + else if ((RegValue & DMA_STATUS_TI) == DMA_STATUS_TI) + { + Ft_printf("DMA_STATUS_TI %x \r\n", RegValue); + Ft_printf("ti debug %x \r\n", Ft_in32(Gmac->Config.BaseAddress + GMAC_INTERNAL_MODULE_STATUS_OFFSET)); + // if (Gmac->SendHandler) + // Gmac->SendHandler(Gmac->SendArgs); + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_TI); + } + + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, DMA_STATUS_NIS); + + /* DMA Error */ + if ((Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET) & DMA_STATUS_AIS) == DMA_STATUS_AIS) + { + if (Gmac->ErrorHandler) + Gmac->ErrorHandler(Gmac->ErrorArgs, FGmac_ErrorCheck(Gmac)); + Ft_out32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET, Ft_in32(Gmac->Config.BaseAddress + DMA_STATUS_OFFSET)); + } +} diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_sinit.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_sinit.c new file mode 100644 index 0000000000..8528881435 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_sinit.c @@ -0,0 +1,43 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:26:04 + * @Description:  This files is for gmac static init + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gmac.h" +#include "ft_parameters.h" + +extern FGmac_Config_t Gmac_ConfigTable[FT_GMAC_INSTANCES_NUM]; + +/** + * @name: Ft_Gmac_LookupConfig + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {u32} InstanceId + */ +FGmac_Config_t *Ft_Gmac_LookupConfig(u32 InstanceId) +{ + FGmac_Config_t *CfgPtr = NULL; + u32 Index; + for (Index = 0; Index < (u32)FT_GMAC_INSTANCES_NUM; Index++) + { + if (Gmac_ConfigTable[Index].InstanceId == InstanceId) + { + CfgPtr = &Gmac_ConfigTable[Index]; + break; + } + } + + return (FGmac_Config_t *)CfgPtr; +} diff --git a/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.c b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.c new file mode 100644 index 0000000000..0162a936c9 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.c @@ -0,0 +1,108 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-27 17:55:31 + * @LastEditTime: 2021-04-28 15:47:10 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gpio.h" +#include "ft_gpio_hw.h" +#include "ft_assert.h" +#include "ft_debug.h" + +#define GPIO_MAX_PIN 7 +#define GPIO_MAX_CTRL_ID 1 + +void FGpio_SetGroupModeA(FT_IN u32 ctrlId, FT_IN u8 pin, FT_IN u32 mode) +{ + u32 RegVal; + Ft_assertNoneReturn(ctrlId <= GPIO_MAX_CTRL_ID); + Ft_assertNoneReturn(pin <= GPIO_MAX_PIN); + + RegVal = FGpioA_ReadReg(ctrlId, GPIO_INTEN); + switch(mode) + { + case GPIO_MODE_GPIO: + RegVal &= ~(1 << pin); + break; + case GPIO_MODE_INT: + RegVal |= (1 << pin); + break; + default: + Ft_assertNoneReturn(0); + break; + } + + FGpioA_WriteReg(ctrlId, GPIO_INTEN, RegVal); + return; +} + +static void FGpio_SetPinInOutA(FT_IN u32 ctrlId, FT_IN u8 pin, FT_IN u8 inOut) +{ + u32 RegVal; + Ft_assertNoneReturn(ctrlId <= GPIO_MAX_CTRL_ID); + Ft_assertNoneReturn(pin <= GPIO_MAX_PIN); + + RegVal = FGpioA_ReadReg(ctrlId, GPIO_SWPORTA_DDR); + if (inOut != (RegVal & (0x1 << pin))) + { + if (GPIO_INPUT == inOut) + { + RegVal &= ~(0x1 << pin); + } + else if (GPIO_OUTPUT == inOut) + { + RegVal |= (0x1 << pin); + } + else + { + Ft_assertNoneReturn(0); + } + + FGpioA_WriteReg(ctrlId, GPIO_SWPORTA_DDR, RegVal); + } + + return; +} + +u32 FGpio_ReadPinA(FT_IN u32 ctrlId, FT_IN u8 pin) +{ + u32 RegVal; + u32 OnOff; + Ft_assertNoneReturn(ctrlId <= GPIO_MAX_CTRL_ID); + Ft_assertNoneReturn(pin <= GPIO_MAX_PIN); + + FGpio_SetPinInOutA(ctrlId, pin, GPIO_INPUT); + RegVal = FGpioA_ReadReg(ctrlId, GPIO_EXT_PORTA); + OnOff = (RegVal & (0x1 << pin)) ? GPIO_ON : GPIO_OFF; + //FT_GPIO_DEBUG_I("ctrl %d, pin %d is %d", ctrlId, pin, OnOff); + return OnOff; +} + +void FGpio_WritePinA(FT_IN u32 ctrlId, FT_IN u8 pin, FT_IN u8 onOff) +{ + u32 RegVal; + Ft_assertNoneReturn(ctrlId <= GPIO_MAX_CTRL_ID); + Ft_assertNoneReturn(pin <= GPIO_MAX_PIN); + Ft_assertNoneReturn((onOff == GPIO_OFF) || (onOff == GPIO_ON)); + + FGpio_SetPinInOutA(ctrlId, pin, GPIO_OUTPUT); + RegVal = FGpioA_ReadReg(ctrlId, GPIO_SWPORTA_DR); + if (GPIO_OFF == onOff) + { + RegVal &= ~(1 << pin); + } + else + { + RegVal |= (1 << pin); + } + FGpioA_WriteReg(ctrlId, GPIO_SWPORTA_DR, RegVal); + return; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.h b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.h new file mode 100644 index 0000000000..bef9b4883c --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio.h @@ -0,0 +1,73 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-27 17:55:12 + * @LastEditTime: 2021-04-30 14:38:45 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_BSP_GPIO_H +#define FT_BSP_GPIO_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_gpio_hw.h" + +/* gpio mode: gpio/int */ +#define GPIO_MODE_GPIO 0 +#define GPIO_MODE_INT 1 + +/* define debug utilities */ +#define FT_GPIO_DEBUG_TAG "FT_GPIO" +#define FT_GPIO_ENABLE_DEBUG +#define FT_GPIO_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_GPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#ifdef FT_GPIO_ENABLE_DEBUG +#define FT_GPIO_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_GPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_GPIO_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_GPIO_DEBUG_TAG, format, ##__VA_ARGS__) +#else +#define FT_GPIO_DEBUG_I(format, ...) +#define FT_GPIO_DEBUG_W(format, ...) +#endif + +/** + * @name: FGpio_SetGroupModeA + * @msg: set gpio mode, polling or intr + * @return {*} + * @param {FT_IN u32} ctrlId + * @param {FT_IN u8} pin + * @param {FT_IN u32} mode + */ +void FGpio_SetGroupModeA(FT_IN u32 CtrlId, FT_IN u8 Pin, FT_IN u32 Mode); +/** + * @name: FGpio_ReadPinA + * @msg: get gpio pin status + * @return {*} + * @param {FT_IN u32} ctrlId + * @param {FT_IN u8} pin + */ +u32 FGpio_ReadPinA(FT_IN u32 CtrlId, FT_IN u8 Pin); +/** + * @name: FGpio_WritePinA + * @msg: set gpio pin status + * @return {*} + * @param {FT_IN u32} ctrlId + * @param {FT_IN u8} pin + * @param {FT_IN u8} onOff + */ +void FGpio_WritePinA(FT_IN u32 CtrlId, FT_IN u8 Pin, FT_IN u8 OnOff); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio_hw.h b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio_hw.h new file mode 100644 index 0000000000..d8f5215458 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_gpio/ft_gpio_hw.h @@ -0,0 +1,93 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-27 17:55:22 + * @LastEditTime: 2021-04-28 08:39:20 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_BSP_GPIO_HW_H +#define FT_BSP_GPIO_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_io.h" + +#define GPIO_CTRL_ID_0 0 +#define GPIO_CTRL_ID_1 1 + +/* base address of gpio register */ +#define GPIO_CTRL0_PA_BASE 0x28004000 +#define GPIO_CTRL1_PA_BASE 0x28005000 +#define GPIO_GROUPA_OFFSET 0x0 +#define GPIO_GROUPB_OFFSET 0xc + +/* offset of register map */ +#define GPIO_SWPORTA_DR 0x00 //A 组端口输出寄存器 +#define GPIO_SWPORTA_DDR 0x04 //A 组端口方向控制寄存器 +#define GPIO_EXT_PORTA 0x08 //A 组端口输入寄存器 + +#define GPIO_SWPORTB_DR 0x0c //B 组端口输出寄存器 +#define GPIO_SWPORTB_DDR 0x10 //B 组端口方向控制寄存器 +#define GPIO_EXT_PORTB 0x14 //B 组端口输入寄存器 + +#define GPIO_INTEN 0x18 //A 组端口中断使能寄存器 +#define GPIO_INTMASK 0x1c //A 组端口中断屏蔽寄存器 +#define GPIO_INTTYPE_LEVEL 0x20 //A 组端口中断等级寄存器 +#define GPIO_INT_POLARITY 0x24 //A 组端口中断极性寄存器 +#define GPIO_INTSTATUS 0x28 //A 组端口中断状态寄存器 +#define GPIO_RAW_INTSTATUS 0x2c //A 组端口原始中断状态寄存器 + +#define GPIO_LS_SYNC 0x30 //配置中断同步寄存器 +#define GPIO_DEBOUNCE 0x34 //防反跳配置寄存器 +#define GPIO_PORTA_EOI 0x38 //A 组端口中断清除寄存器 + +/* misc marco */ +#define GPIO_GROUP_A 0 +#define GPIO_OFF 0 +#define GPIO_ON 1 +#define GPIO_INPUT 0 +#define GPIO_OUTPUT 1 + +inline static u32 FGpio_GetBaseAddr(FT_IN u32 ctrlId, FT_IN u32 groupId) +{ + static const u32 CtrlAddr[2] = {GPIO_CTRL0_PA_BASE, GPIO_CTRL1_PA_BASE}; + static const u32 GroupOff[2] = {GPIO_GROUPA_OFFSET, GPIO_GROUPB_OFFSET}; + return CtrlAddr[ctrlId] + GroupOff[groupId]; +} + +/** + * @name: FGpio_WriteReg + * @msg: write gpio register + * @param {u32} BaseAddress base addr of i2c + * @param {u32} RegOffset addr offset of i2c register + * @param {u32} RegisterValue val to be write into register + * @return {void} + */ +#define FGpioA_WriteReg(ctrlId, RegOffset, RegisterValue) Ft_out32(FGpio_GetBaseAddr(ctrlId, GPIO_GROUP_A) + (u32)RegOffset, (u32)RegisterValue) + +/** + * @name: FGpio_ReadReg + * @msg: read gpio register + * @param {u32} BaseAddress base addr of i2c + * @param {u32} RegOffset addr offset of i2c register + * @return {u32} val read from register + */ +#define FGpioA_ReadReg(ctrlId, RegOffset) Ft_in32(FGpio_GetBaseAddr(ctrlId, GPIO_GROUP_A) + (u32)RegOffset) + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.c b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.c new file mode 100644 index 0000000000..2fe11534a2 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.c @@ -0,0 +1,516 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-27 15:46:38 + * @Description:  This files is for i2c user interface + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include "ft_mux.h" +#include "ft_i2c_hw.h" +#include "ft_i2c.h" +#include "ft_generic_timer.h" + +/* reset val of register */ +#define I2C_CON_DEFAULT 0x7F +#define I2C_TAR_DEFAULT 0x1055 +#define I2C_SAR_DEFAULT 0x55 +#define I2C_DATA_CMD_DEFAULT 0x0 +#define I2C_SS_SCL_LCNT_DEFAULT 0x1D6 +#define I2C_SS_SCL_HCNT_DEFAULT 0x190 +#define I2C_FS_SCL_LCNT_DEFAULT 0x82 +#define I2C_FS_SCL_HCNT_DEFAULT 0x3C +#define I2C_HS_SCL_LCNT_DEFAULT 0x10 +#define I2C_HS_SCL_HCNT_DEFAULT 0x6 +#define I2C_INTR_MASK_DEFAULT 0x8FF +//#define I2C_RX_TL_DEFAULT 0x0 +//#define I2C_TX_TL_DEFAULT 0x0 +#define I2C_SCL_HCNT_DEFAULT 0x2f +#define I2C_SCL_LCNT_DEFAULT 0x2f +//#define I2C_RX_TL_SET 0xff +//#define I2C_TX_TL_SET 0xff + +#define I2C_RX_TL_BY_BYTE 0x0 +#define I2C_TX_TL_BY_BYTE 0x0 +#define I2C_RX_TL_BY_FIFO 0x01 +#define I2C_TX_TL_BY_FIFO 0x01 + +LOCAL const u32 g_I2cSpeedMask[MAX_I2C_SPEED] = {I2C_STANDARD_SPEED_MASK, + I2C_FAST_SPEED_MASK, + I2C_HIGH_SPEED_MASK}; +LOCAL const u32 g_I2cSclLcntReg[MAX_I2C_SPEED] = {I2C_SS_SCL_LCNT, + I2C_FS_SCL_LCNT, I2C_HS_SCL_LCNT}; +LOCAL const u32 g_I2cSclHcntReg[MAX_I2C_SPEED] = {I2C_SS_SCL_HCNT, + I2C_FS_SCL_LCNT, I2C_HS_SCL_LCNT}; + +void FI2C_resetReg(u32 BaseAddr) +{ + /* set default value for register */ + FI2C_WriteReg(BaseAddr, I2C_CON, I2C_CON_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_TAR, I2C_TAR_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_SAR, I2C_SAR_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_DATA_CMD, I2C_DATA_CMD_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_SS_SCL_LCNT, I2C_SS_SCL_LCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_SS_SCL_HCNT, I2C_SS_SCL_HCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_FS_SCL_LCNT, I2C_FS_SCL_LCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_FS_SCL_HCNT, I2C_FS_SCL_HCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_HS_SCL_LCNT, I2C_HS_SCL_LCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_HS_SCL_HCNT, I2C_HS_SCL_HCNT_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_INTR_MASK, I2C_INTR_MASK_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_RX_TL, I2C_RX_TL_DEFAULT); + FI2C_WriteReg(BaseAddr, I2C_TX_TL, I2C_TX_TL_DEFAULT); +} + +inline LOCAL void FI2C_setSclClk(FT_IN u32 BaseAddr, FT_IN FI2C_SpeedType_t SpeedType, + FT_IN u32 SclLcnt, FT_IN u32 SclHcnt) +{ + u32 SclLcntAddr = g_I2cSclLcntReg[SpeedType]; + u32 SclHcntAddr = g_I2cSclHcntReg[SpeedType]; + + FI2C_WriteReg(BaseAddr, SclLcntAddr, SclLcnt); + FI2C_WriteReg(BaseAddr, SclHcntAddr, SclHcnt); + + return; +} + +inline LOCAL void FI2C_setCtrlParam(FT_IN u32 BaseAddr, FT_IN FI2C_SpeedType_t SpeedType, FT_IN bool_t Is7BitAddr) +{ + u32 RegVal; + + RegVal = I2C_CON_ME | g_I2cSpeedMask[SpeedType]; + RegVal |= ((TRUE == Is7BitAddr) ? I2C_CON_MASTER_ADR_7BIT : I2C_CON_MASTER_ADR_10BIT); + RegVal |= I2C_CON_RESTART_EN | I2C_CON_SLAVE_DISABLE; + + FI2C_WriteReg(BaseAddr, I2C_CON, RegVal); + return; +} + +void FI2C_initMasterCfg(FT_IN FI2C_Instance_t id, + FT_IN FI2C_WorkMode_t mode, + FT_IN bool_t UseWRFifo, + FT_IN u32 PageSize, + FT_INOUT FI2C_Config_t *pCfg) +{ + Ft_assertNoneReturn(NULL != pCfg); + + pCfg->InstanceId = id; + pCfg->BaseAddress = g_FI2cRegBaseAddr[id]; + pCfg->IrqNum = g_FI2cIrqNum[id]; + pCfg->IrqPriority = I2C_DEFAULT_IRQ_PRIORITY; + pCfg->BusSpeed = I2C_STANDARD_SPEED; + pCfg->SclLcnt = I2C_SCL_LCNT_DEFAULT; + pCfg->SclHcnt = I2C_SCL_HCNT_DEFAULT; + pCfg->WRByFifo = UseWRFifo; + if (TRUE == pCfg->WRByFifo) + { + pCfg->RxThres = I2C_RX_TL_BY_FIFO; + pCfg->TxThres = I2C_TX_TL_BY_FIFO; + } + else + { + pCfg->RxThres = I2C_RX_TL_BY_BYTE; + pCfg->TxThres = I2C_TX_TL_BY_BYTE; + } + pCfg->Is7BitAddr = TRUE; + pCfg->BlockSize = PageSize; + + if (I2C_POLLING_MODE == mode) + { + pCfg->IsPolling = TRUE; + } + else if (I2C_IRQ_MODE == mode) + { + pCfg->IsPolling = FALSE; + } + else + { + Ft_assertNoneReturn(0); + } + + return; +} + +void FI2C_initMaster(FT_IN FI2C_Instance_t id, + FT_IN FI2C_WorkMode_t mode, + FT_IN u32 SlaveAddr, + FT_IN bool_t UseWRFifo, + FT_IN u32 PageSize, + FT_INOUT FI2C_t *pDev) +{ + u32 RxDepth; + u32 TxDepth; + + Ft_assertNoneReturn(NULL != pDev); + + memset(pDev, 0, sizeof(FI2C_t)); + + /* setup i2c bus mux */ + Ft_setI2cMux(id); + + /* setup i2c config as master */ + FI2C_initMasterCfg(id, mode, UseWRFifo, PageSize, &pDev->Config); + pDev->SlaveAddr = SlaveAddr; + pDev->DelayHandle = Ft_GenericTimer_UsDelay; + + /* init irq handler */ + pDev->pRxEvent = NULL; + pDev->pTxEvent = NULL; + + pDev->pIrqCallBack = NULL; + pDev->pWaitCallBack = NULL; + + FI2C_DISABLE_I2C_BUS(pDev); + FI2C_CLR_ALL_IRQ_STATUS(pDev); + + /* reset reg val */ + FI2C_resetReg(pDev->Config.BaseAddress); + + /* set scl high && low level */ + FI2C_setSclClk(pDev->Config.BaseAddress, + pDev->Config.BusSpeed, + pDev->Config.SclLcnt, + pDev->Config.SclHcnt); + + /* set ctrl parameters */ + FI2C_setCtrlParam(pDev->Config.BaseAddress, + pDev->Config.BusSpeed, + pDev->Config.Is7BitAddr); + + /* set rx & tx trigger level */ + RxDepth = FI2C_GET_RX_BUFFER_DEPTH(pDev); + TxDepth = FI2C_GET_TX_BUFFER_DEPTH(pDev); + + /* threshold shall not greater than depth */ + // FT_I2C_DEBUG_I("set rx thres 0x%x, tx thres 0x%x", + // FT_MIN(pDev->Config.RxThres, RxDepth), + // FT_MIN(pDev->Config.TxThres, TxDepth)); + FI2C_SET_TX_TL(pDev, FT_MIN(pDev->Config.TxThres, TxDepth)); + FI2C_SET_RX_TL(pDev, FT_MIN(pDev->Config.RxThres, RxDepth)); + pDev->IsReady = TRUE; + return; +} + +void FI2C_deInitMaster(FT_INOUT FI2C_t *pDev) +{ + /* assert no memory need to release */ + pDev->IsReady = FALSE; +} + +inline LOCAL void FI2C_sendRestartCmd(FT_IN u32 BaseAddr) +{ + u32 RegVal = FI2C_ReadReg(BaseAddr, I2C_CON); + RegVal |= I2C_CON_RESTART_EN; + FI2C_WriteReg(BaseAddr, I2C_CON, RegVal); +} + +inline LOCAL void FI2C_setTarAddr(FT_IN u32 BaseAddr, FT_IN u32 SlaveAddr) +{ + u32 RegVal = (SlaveAddr & I2C_TAR_ADR_MASK); + FI2C_WriteReg(BaseAddr, I2C_TAR, RegVal); +} + +inline LOCAL void FI2C_sendWriteCmd(FT_IN u32 BaseAddr, FT_IN u32 PageAddr) +{ + u32 RegVal = I2C_DATA_CMD_RESTART | (PageAddr & I2C_DATA_CMD_DAT_MASK); + FI2C_WriteReg(BaseAddr, I2C_DATA_CMD, RegVal); +} + +inline LOCAL void FI2C_sendStartReadCmd(FT_IN u32 BaseAddr, FT_IN u32 PageAddr) +{ + /* send read cmd */ + u32 RegVal = I2C_DATA_CMD_STOP | I2C_DATA_CMD_RESTART | (PageAddr & 0xff); + FI2C_WriteReg(BaseAddr, I2C_DATA_CMD, RegVal); +} + +void FI2C_sendStopCmd(FT_IN u32 BaseAddr) +{ + /* send stop signal */ + u32 RegVal = I2C_DATA_CMD_STOP; + FI2C_WriteReg(BaseAddr, I2C_DATA_CMD, RegVal); +} + +LOCAL bool_t FI2C_blockWaitForStatus(FT_IN u32 stMask, FT_INOUT FI2C_t *pDev) +{ + u32 timeout = 0; + bool_t IsFree = FALSE; + + Ft_assertNoneReturn(NULL != pDev); + + /* Wait until Specific Status Bit in I2C_STATUS is 1 */ + while ((!FI2C_CHECK_STATUS(pDev, stMask)) && (timeout < I2C_TIMEOUT)) + { + pDev->DelayHandle(5000); + timeout++; + } + + /* check if status wait successful or timeout */ + if (I2C_TIMEOUT != timeout) + { + IsFree = TRUE; + } + else + { + FT_I2C_ERROR("wait status 0x%x failed!!! reg val is 0x%x", + stMask, FI2C_GET_STATUS(pDev)); + } + + return IsFree; +} + +u32 FI2C_writeByByte(FT_IN u32 len, FT_IN u8 *pI2cBuf, FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev) +{ + u32 loop; + bool_t isNotTimeout; + u32 ret = ERR_I2C_OK; + Ft_assertNoneReturn((NULL != pDev) && (NULL != pI2cBuf)); + + if (!pDev->IsReady) + { + return ERR_I2C_NOT_READY; + } + + if (pDev->Config.BlockSize <= len) + { + return ERR_I2C_SIZE_TOO_LARGE; + } + + FI2C_DISABLE_I2C_BUS(pDev); + FI2C_sendRestartCmd(pDev->Config.BaseAddress); + FI2C_setTarAddr(pDev->Config.BaseAddress, pDev->SlaveAddr); + FI2C_ENABLE_I2C_BUS(pDev); + + // FT_I2C_DEBUG_I("get rx thres 0x%x, tx thres 0x%x", + // FI2C_GET_TXFLR(pDev), + // FI2C_GET_RXFLR(pDev)); + + FI2C_sendWriteCmd(pDev->Config.BaseAddress, PageAddr); + + for (loop = 0; loop < len; loop++) + { + if (!pDev->Config.IsPolling) + { + FI2C_setIrq(pDev, I2C_IRQ_TX_EMPTY, TRUE); + } + pDev->DelayHandle(2); + FI2C_SET_TX_DATA(pDev, pI2cBuf[loop]); + + /* wait until TX fifo is empty */ + if (pDev->Config.IsPolling) + { + isNotTimeout = FI2C_blockWaitForStatus(I2C_STATUS_TFE, pDev); + if (!isNotTimeout) + { + ret = ERR_I2C_WRITE_TIMEOUT; + goto EXIT; + } + } + else + { + pDev->LastIrqErr = ERR_I2C_OK; + if (pDev->pWaitCallBack) + { + pDev->pWaitCallBack(I2C_IRQ_TYPE_TX_COMPLETE, pDev); + } + else + { + ret = ERR_I2C_INVALID_HANDLER; + goto EXIT; + } + + if (ERR_I2C_OK != pDev->LastIrqErr) + { + ret = pDev->LastIrqErr; + goto EXIT; + } + } + + pDev->DelayHandle(2); + } + +EXIT: + FI2C_SEND_TX_STOP_CMD(pDev); + pDev->DelayHandle(2); + + return ret; +} + +u32 FI2C_readByByte(FT_IN u32 len, FT_OUT u8 *pI2cBuf, FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev) +{ + u32 loop; + bool_t isNotTimeout; + u32 ret = ERR_I2C_OK; + Ft_assertNoneReturn((NULL != pDev) && (NULL != pI2cBuf)); + + if (!pDev->IsReady) + { + return ERR_I2C_NOT_READY; + } + + FI2C_DISABLE_I2C_BUS(pDev); + FI2C_sendRestartCmd(pDev->Config.BaseAddress); + FI2C_CLR_ALL_IRQ_STATUS(pDev); + FI2C_setTarAddr(pDev->Config.BaseAddress, pDev->SlaveAddr); + FI2C_ENABLE_I2C_BUS(pDev); + + /* assign page addr when start read */ + FI2C_sendStartReadCmd(pDev->Config.BaseAddress, PageAddr); + + /* read contents */ + for (loop = 0; loop < len; loop++) + { + if (!pDev->Config.IsPolling) + { + FI2C_setIrq(pDev, I2C_IRQ_RX_FULL, TRUE); + } + + FI2C_SEND_RX_NEXT_CMD(pDev); + pDev->DelayHandle(2); + + /* wait until data reach and start fetch data */ + if (pDev->Config.IsPolling) + { + isNotTimeout = FI2C_blockWaitForStatus(I2C_STATUS_RFNE, pDev); + if (!isNotTimeout) + { + ret = ERR_I2C_READ_TIMEOUT; + goto EXIT; + } + } + else + { + pDev->LastIrqErr = ERR_I2C_OK; + if (pDev->pWaitCallBack) + { + pDev->pWaitCallBack(I2C_IRQ_TYPE_RX_COMPLETE, pDev); + } + else + { + ret = ERR_I2C_INVALID_HANDLER; + goto EXIT; + } + + if (ERR_I2C_OK != pDev->LastIrqErr) + { + ret = pDev->LastIrqErr; + goto EXIT; + } + } + + pI2cBuf[loop] = FI2C_GET_RX_DATA(pDev); + pDev->DelayHandle(2); + } + +EXIT: + FI2C_SEND_RX_STOP_CMD(pDev); + pDev->DelayHandle(2); + return ret; +} + +u32 FI2C_writeByFifo(FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev) +{ + u32 loop; + u32 ret = ERR_I2C_OK; + u32 timeout = I2C_TIMEOUT / 10; + Ft_assertNoneReturn(NULL != pDev); + + if (!pDev->IsReady) + { + return ERR_I2C_NOT_READY; + } + + if (pDev->Config.IsPolling) + { + return ERR_I2C_NOT_SUPPORT; + } + + FI2C_DISABLE_I2C_BUS(pDev); + FI2C_sendRestartCmd(pDev->Config.BaseAddress); + FI2C_setTarAddr(pDev->Config.BaseAddress, pDev->SlaveAddr); + FI2C_ENABLE_I2C_BUS(pDev); + + FI2C_sendWriteCmd(pDev->Config.BaseAddress, PageAddr); + + /* enable TX Empty, disable Rx Full */ + FI2C_setIrq(pDev, I2C_IRQ_TX_EMPTY, TRUE); + FI2C_setIrq(pDev, I2C_IRQ_RX_FULL, FALSE); + + pDev->LastIrqErr = ERR_I2C_OK; + if (pDev->pWaitCallBack) + { + pDev->pWaitCallBack(I2C_IRQ_TYPE_TX_COMPLETE, pDev); + } + else + { + ret = ERR_I2C_INVALID_HANDLER; + } + + if (ERR_I2C_OK != pDev->LastIrqErr) + { + ret = pDev->LastIrqErr; + } + + return ret; +} + +u32 FI2C_readByFifo(FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev) +{ + u32 loop; + u32 ret = ERR_I2C_OK; + u32 timeout = I2C_TIMEOUT / 10; + Ft_assertNoneReturn((NULL != pDev)); + + if (!pDev->IsReady) + { + return ERR_I2C_NOT_READY; + } + + if (pDev->Config.IsPolling) + { + return ERR_I2C_NOT_SUPPORT; + } + + FI2C_DISABLE_I2C_BUS(pDev); + FI2C_sendRestartCmd(pDev->Config.BaseAddress); + FI2C_setTarAddr(pDev->Config.BaseAddress, pDev->SlaveAddr); + FI2C_ENABLE_I2C_BUS(pDev); + + /* assign page addr when start read */ + FI2C_sendStartReadCmd(pDev->Config.BaseAddress, PageAddr); + + FI2C_setIrq(pDev, I2C_IRQ_RX_FULL, TRUE); + FI2C_setIrq(pDev, I2C_IRQ_TX_EMPTY, FALSE); + FI2C_SET_RX_TL(pDev, 1); + //FT_MIN(pDev->RxBuf.DataLength, FI2C_GET_RX_BUFFER_DEPTH(pDev))); + pDev->DelayHandle(2); + FT_I2C_DEBUG_I("rx tl is 0x%x irq mask 0x%x", + FI2C_GET_RX_TL(pDev), + FI2C_getIrqMask(pDev)); + + FI2C_SEND_RX_NEXT_CMD(pDev); + pDev->DelayHandle(2); + + pDev->LastIrqErr = ERR_I2C_OK; + if (pDev->pWaitCallBack) + { + pDev->pWaitCallBack(I2C_IRQ_TYPE_RX_COMPLETE, pDev); + } + else + { + ret = ERR_I2C_INVALID_HANDLER; + } + + if (ERR_I2C_OK != pDev->LastIrqErr) + { + ret = pDev->LastIrqErr; + } + FI2C_SET_RX_TL(pDev, 0); + return ret; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.h b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.h new file mode 100644 index 0000000000..290e15aa24 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c.h @@ -0,0 +1,201 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:32:20 + * @Description:  This files is for i2c user interface + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_BSP_I2C_H +#define FT_BSP_I2C_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_debug.h" +#include "ft_assert.h" +#include "ft_error_code.h" +#include "ft_i2c_hw.h" + +/* I2C Ctrl instance */ +typedef enum +{ + I2C_CTRL_ID_0 = 0, + I2C_CTRL_ID_1, + I2C_CTRL_ID_2, + I2C_CTRL_ID_3, + + MAX_I2C_CTRL_ID, +} FI2C_Instance_t; + +static const u32 g_FI2cIrqNum[MAX_I2C_CTRL_ID] = { + 44, 45, 46, 47 +}; + +static const u32 g_FI2cRegBaseAddr[MAX_I2C_CTRL_ID] = { + I2C0_BASE_ADDRESS, I2C1_BASE_ADDRESS, + I2C2_BASE_ADDRESS, I2C3_BASE_ADDRESS +}; + +#define I2C_DEFAULT_IRQ_PRIORITY 0 + +/* Type of I2C device */ +typedef enum +{ + I2C_MASTER_DEV = 0, + I2C_SLAVE_DEV, + + MAX_I2C_DEV +} FI2C_DevType_t; + +/* I2C work mode type */ +typedef enum +{ + I2C_POLLING_MODE = 0, + I2C_IRQ_MODE, + + MAX_I2C_WORKMODE +} FI2C_WorkMode_t; + +/* Type of I2C bus speed */ +typedef enum +{ + I2C_STANDARD_SPEED = 0, + I2C_FAST_SPEED, + I2C_HIGH_SPEED, + + MAX_I2C_SPEED, +} FI2C_SpeedType_t; + +/* I2C error code Submodule */ +typedef enum +{ + I2C_ERR_MODE_DEF = 0, + + MAX_I2C_ERR_MODE, +} FI2C_Submodule_t; + +/* I2C irq type */ +typedef enum +{ + I2C_IRQ_TYPE_NONE = 0, + I2C_IRQ_TYPE_TX_COMPLETE, + I2C_IRQ_TYPE_RX_COMPLETE, + + MAX_I2C_IRQ_TYPE +} +FI2C_IrqType_t; + +/* I2C config info */ +typedef struct +{ + FI2C_Instance_t InstanceId;/* Id of I2C ctrl instance */ + u32 BaseAddress; /* base address of I2C register */ + FI2C_DevType_t WorkMode; /* work as master or slave */ + FI2C_SpeedType_t BusSpeed; /* bus speed setting */ + u32 SclLcnt; + u32 SclHcnt; + u32 RxThres; + u32 TxThres; + bool_t Is7BitAddr; /* TRUE: use 7 bit addr, FALSE: use 10 bit addr */ + bool_t IsPolling; /* is polling */ + u32 IrqNum; /* irq num of I2C in system */ + u32 IrqPriority; /* irq priority */ + u32 BlockSize; /* block size, for eeprom */ + bool_t WRByFifo; +} FI2C_Config_t; + +/* I2C RX/TX buffer */ +typedef struct +{ + u8 *BytePtr; + u32 TotalBytes; + u32 DataLength; + u32 CurIndex; +} FI2C_Buffer_t; + +typedef void (*FI2C_IrqCallBackHandler_t)(FT_IN u32 IrqType, + FT_INOUT void *pDev, + FT_INOUT void *pArg); +typedef void (*FI2C_IrqWaitHandler_t)(FT_IN u32 IrqType, + FT_INOUT void *pDev); +typedef int32_t (*FI2C_DelayHandler_t)(FT_IN u32); + +/* I2C device info */ +typedef struct +{ + FI2C_Config_t Config; /* Configuration data structure */ + + void *pRxEvent; + void *pTxEvent; + FI2C_IrqCallBackHandler_t pIrqCallBack; + FI2C_IrqWaitHandler_t pWaitCallBack; + u32 LastIrqErr; + FI2C_Buffer_t RxBuf; + FI2C_Buffer_t TxBuf; + + FI2C_DelayHandler_t DelayHandle; + bool_t IsReady; /* Device is ininitialized and ready*/ + u16 SlaveAddr; /* address of I2C slave device for master */ +} FI2C_t; + +void FI2C_initMaster(FT_IN FI2C_Instance_t id, + FT_IN FI2C_WorkMode_t mode, + FT_IN u32 SlaveAddr, + FT_IN bool_t UseWRFifo, + FT_IN u32 PageSize, + FT_INOUT FI2C_t *pDev); +u32 FI2C_writeByByte(FT_IN u32 len, FT_IN u8 *pI2cBuf, FT_IN u8 PageAddr, + FT_INOUT FI2C_t *pDev); +u32 FI2C_readByByte(FT_IN u32 len, FT_OUT u8 *pI2cBuf, FT_IN u8 PageAddr, + FT_INOUT FI2C_t *pDev); +u32 FI2C_setIrqHandler(FT_IN FI2C_IrqType_t IrqType, FT_IN FI2C_DelayHandler_t pHandler, FT_IN void *pArgs, + FT_INOUT FI2C_t *pDev); +void FI2C_irqHandler(void *pArgs); +void FI2C_irqHandler4Fifo(void *pArgs); +u32 FI2C_getIrqMask(FT_IN FI2C_t *pDev); +void FI2C_setIrqMask(FT_IN FI2C_t *pDev, FT_IN u32 mask); +void FI2C_setIrq(FT_IN FI2C_t *pDev, FT_IN u32 maskBit, FT_IN bool_t enable); +bool_t FI2C_checkIfIntr(FT_IN FI2C_t *pDev); +u32 FI2C_writeByFifo(FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev); +u32 FI2C_readByFifo(FT_IN u8 PageAddr, FT_INOUT FI2C_t *pDev); +void FI2C_deInitMaster(FT_INOUT FI2C_t *pDev); + +#define FT_I2C_DEBUG_TAG "FT_I2C" +//#define FT_I2C_ENABLE_DEBUG +#define FT_I2C_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_I2C_DEBUG_TAG, format, ##__VA_ARGS__) +#ifdef FT_I2C_ENABLE_DEBUG +#define FT_I2C_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_I2C_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_I2C_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_I2C_DEBUG_TAG, format, ##__VA_ARGS__) +#else +#define FT_I2C_DEBUG_I(format, ...) +#define FT_I2C_DEBUG_W(format, ...) +#endif + +#define ERR_I2C_OK ERR_SUCCESS +#define ERR_I2C_NOT_READY FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 1) +#define ERR_I2C_WRITE_TIMEOUT FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 2) +#define ERR_I2C_READ_TIMEOUT FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 3) +#define ERR_I2C_SIZE_TOO_LARGE FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 4) +#define ERR_I2C_NOT_SUPPORT FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 5) +#define ERR_I2C_INVALID_PARAM FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 6) +#define ERR_I2C_INVALID_HANDLER FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 7) +#define ERR_I2C_INVALID_NO_MEM FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 8) +#define ERR_I2C_BUS_NOT_ENABLED FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 9) +#define ERR_I2C_EVT_TIMEOUT FT_CODE_ERR(errModeI2c, I2C_ERR_MODE_DEF, 0xA) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_g.c b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_g.c new file mode 100644 index 0000000000..3d6c929641 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_g.c @@ -0,0 +1,13 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-20 15:35:23 + * @LastEditTime: 2021-04-20 15:35:24 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_hw.h b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_hw.h new file mode 100644 index 0000000000..99a767fd8a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_hw.h @@ -0,0 +1,322 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:26:58 + * @Description:  This files is for i2c register definition + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_BSP_I2C_HW_H +#define FT_BSP_I2C_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_io.h" + +/* i2c 1~4 reg base address */ +#define I2C0_BASE_ADDRESS 0x28006000U +#define I2C1_BASE_ADDRESS 0x28007000U +#define I2C2_BASE_ADDRESS 0x28008000U +#define I2C3_BASE_ADDRESS 0x28009000U + +/* defines */ +#define I2C_TIMEOUT 5000 + +#define I2C_MAX_READ_SIZE 1 +#define SEM_TIMEOUT 2 + + /* Register Definition */ + +#define I2C_CON 0x00 +#define I2C_TAR 0x04 +#define I2C_SAR 0x08 +#define I2C_HS_MADDR 0x0C +#define I2C_DATA_CMD 0x10 +#define I2C_SS_SCL_HCNT 0x14 +#define I2C_SS_SCL_LCNT 0x18 +#define I2C_FS_SCL_HCNT 0x1C +#define I2C_FS_SCL_LCNT 0x20 +#define I2C_HS_SCL_HCNT 0x24 +#define I2C_HS_SCL_LCNT 0x28 +#define I2C_INTR_STAT 0x2C +#define I2C_INTR_MASK 0x30 +#define I2C_RAW_INTR_STAT 0x34 +#define I2C_RX_TL 0x38 +#define I2C_TX_TL 0x3C +#define I2C_CLR_INTR 0x40 +#define I2C_CLR_RX_UNDER 0x44 +#define I2C_CLR_RX_OVER 0x48 +#define I2C_CLR_TX_OVER 0x4C +#define I2C_CLR_RD_REQ 0x50 +#define I2C_CLR_TX_ABRT 0x54 +#define I2C_CLR_RX_DONE 0x58 +#define I2C_CLR_ACTIVITY 0x5c +#define I2C_CLR_STOP_DET 0x60 +#define I2C_CLR_START_DET 0x64 +#define I2C_CLR_GEN_CALL 0x68 +#define I2C_ENABLE 0x6C +#define I2C_STATUS 0x70 +#define I2C_TXFLR 0x74 +#define I2C_RXFLR 0x78 +#define I2C_TX_ABRT_SOURCE 0x80 +#define I2C_SLV_DATA_NACK_ONLY 0x84 +#define I2C_DMA_CR 0x88 +#define I2C_DMA_TDLR 0x8c +#define I2C_DMA_RDLR 0x90 +#define I2C_SDA_SETUP 0x94 +#define I2C_ACK_GENERAL_CALL 0x98 +#define I2C_ENABLE_STATUS 0x9C +#define I2C_COMP_PARAM_1 0xf4 +#define I2C_COMP_VERSION 0xf8 +#define I2C_COMP_TYPE 0xfc + +#define I2C_RAW_INTR_STAT_RX_UNDER 0x1 +#define I2C_RAW_INTR_STAT_RX_OVER 0x2 +#define I2C_RAW_INTR_STAT_RX_FULL 0x4 +#define I2C_RAW_INTR_STAT_TX_OVER 0x8 +#define I2C_RAW_INTR_STAT_TX_EMPTY 0x10 + + /* Default parameters */ + +#define I2C_CON_ME (0x1 << 0) +#define I2C_CON_MS_SS (0x1 << 1) +#define I2C_CON_MS_FS (0x2 << 1) +#define I2C_CON_MS_HS (0x3 << 1) +#define I2C_CON_SLAVE_ADR_7BIT (0x0 << 3) +#define I2C_CON_SLAVE_ADR_10BIT (0x1 << 3) +#define I2C_CON_MASTER_ADR_7BIT (0x0 << 4) +#define I2C_CON_MASTER_ADR_10BIT (0x1 << 4) +#define I2C_CON_RESTART_EN (0x1 << 5) +#define I2C_CON_SLAVE_DISABLE (0x1 << 6) + +/* 0110 0011 0x63 */ +#define I2C_CON_DEFAULT_MASTER (I2C_CON_ME | I2C_CON_MS_FS /*I2C_CON_MS_SS*/ /* | I2C_CON_RESTART_EN*/ | \ + I2C_CON_SLAVE_ADR_7BIT | I2C_CON_SLAVE_DISABLE) +#define I2C_CTR_DEFAULT (I2C_CON_ME | I2C_CON_MASTER_ADR_7BIT | \ + I2C_CON_SLAVE_DISABLE) + +#define I2C_IRQ_NONE_MASK 0x0 +#define I2C_IRQ_ALL_MASK 0x8ff + +#define I2C_TAR_STARTBYTE (0x1 << 10) +#define I2C_TAR_SPECIAL_STARTBYTE (0x1 << 11) +#define I2C_TAR_ADR_7BIT (0x0 << 12) +#define I2C_TAR_ADR_10BIT (0x1 << 12) +#define I2C_TAR_ADR_MASK 0x3ff /* bit [9: 0] */ + +#define I2C_SLAVE_DISABLE_DEFAULT 0 +#define I2C_RESTART_EN_DEFAULT 1 +#define I2C_10BITADDR_MASTER_DEFAULT 0 +#define I2C_10BITADDR_SLAVE_DEFAULT 1 +#define I2C_MAX_SPEED_MODE_DEFAULT 3 +#define I2C_MASTER_MODE_DEFAULT 1 + +#define I2C_STANDARD_SPEED_MASK (0x01 << 1) +#define I2C_FAST_SPEED_MASK (0x02 << 1) +#define I2C_HIGH_SPEED_MASK (0x03 << 1) + +#define I2C_DEFAULT_TAR_ADDR_DEFAULT 0x055 +#define I2C_DEFAULT_SLAVE_ADDR_DEFAULT 0x055 +#define I2C_COMP_VERSION_DEFAULT 0x3131352A + +#define I2C_HS_MASTER_CODE_DEFAULT 1 +#define I2C_SS_SCL_HIGH_COUNT_DEFAULT 0x0190 +#define I2C_SS_SCL_LOW_COUNT_DEFAULT 0x01d6 +#define I2C_FS_SCL_HIGH_COUNT_DEFAULT 0x003c +#define I2C_FS_SCL_LOW_COUNT_DEFAULT 0x0082 +#define I2C_HS_SCL_HIGH_COUNT_DEFAULT 0x006 +#define I2C_HS_SCL_LOW_COUNT_DEFAULT 0x0010 +#define I2C_RX_TL_DEFAULT 0 +#define I2C_TX_TL_DEFAULT 0 +#define I2C_DEFAULT_SDA_SETUP_DEFAULT 0x64 +#define I2C_DEFAULT_ACK_GENERAL_CALL_DEFAULT 1 +#define I2C_DYNAMI2C_TAR_UPDATE_DEFAULT 1 +#define I2C_RX_BUFFER_DEPTH_DEFAULT 8 +#define I2C_TX_BUFFER_DEPTH_DEFAULT 8 +#define I2C_ADD_ENCODED_PARAMS_DEFAULT 1 +#define I2C_HAS_DMA_DEFAULT 0 +#define I2C_INTR_IO_DEFAULT 0 +#define I2C_HC_COUNT_VALUES_DEFAULT 0 +#define APB_DATA_WIDTH_DEFAULT 0 +#define I2C_SLV_DATA_NACK_ONLY_DEFAULT 0 +#define I2C_USE_COUNTS_DEFAULT 0 +#define I2C_CLK_TYPE_DEFAULT 1 +#define I2C_CLOCK_PERIOD_DEFAULT 10 + + /* Raw Interrupt Status */ + +#define I2C_IRQ_NONE (0x0) +#define I2C_IRQ_RX_UNDER (0x01 << 0) +#define I2C_IRQ_RX_OVER (0x01 << 1) +#define I2C_IRQ_RX_FULL (0x01 << 2) +#define I2C_IRQ_TX_OVER (0x01 << 3) +#define I2C_IRQ_TX_EMPTY (0x01 << 4) +#define I2C_IRQ_RD_REQ (0x01 << 5) +#define I2C_IRQ_TX_ABRT (0x01 << 6) +#define I2C_IRQ_RX_DONE (0x01 << 7) +#define I2C_IRQ_ACTIVITY (0x01 << 8) +#define I2C_IRQ_STOP_DET (0x01 << 9) +#define I2C_IRQ_START_DET (0x01 << 10) +#define I2C_IRQ_GEN_CALL (0x01 << 11) +#define I2C_IRQ_ALL 0xFFF + + /* Default IRQ Mask Bit Setting */ + +#define I2C_IRQ_DEFAULT_MASK (I2C_IRQ_RX_FULL | I2C_IRQ_TX_EMPTY | \ + I2C_IRQ_TX_ABRT | I2C_IRQ_STOP_DET | \ + I2C_IRQ_START_DET) + + /* Data command stop bit */ + +#define I2C_DATA_CMD_WR_STOP_BIT (0x02 << 8) /* bit 8=0:W, bit 9=1: stop */ +#define I2C_DATA_CMD_RD_STOP_BIT (0x03 << 8) /* bit 8=1:R, bit 9=1: stop */ +#define I2C_DATA_CMD_DAT_MASK 0xff /* bit [7:0] , TX and RX data */ +#define I2C_DATA_CMD_WR (0x00 << 8) +#define I2C_DATA_CMD_RD (0x01 << 8) +#define I2C_DATA_CMD_STOP (0x01 << 9) +#define I2C_DATA_CMD_RESTART (0X01 << 10) + + /* I2C TX Abort Source*/ + +#define I2C_ABRT_7B_ADDR_NOACK 0x001 +#define I2C_ABRT_10_ADDR1_NOACK 0x002 +#define I2C_ABRT_10_ADDR2_NOACK 0x004 +#define I2C_ABRT_TXDATA_NOACK 0x008 +#define I2C_ABRT_GCALL_NOACK 0x010 +#define I2C_ABRT_GCALL_READ 0x020 +#define I2C_ABRT_HS_ACKDET 0x040 +#define I2C_ABRT_SBYTE_ACKDET 0x080 +#define I2C_ABRT_HS_NORSTRT 0x100 +#define I2C_ABRT_SBYTE_NORSTRT 0x200 +#define I2C_ABRT_10B_RD_NORSTRT 0x400 +#define I2C_ABRT_MASTER_DIS 0x800 +#define I2C_ABRT_ARB_LOST 0x1000 +#define I2C_ABRT_SLVFLUSH_TXFIFO 0x2000 +#define I2C_ABRT_SLV_ARBLOST 0x5000 +#define I2C_ABRT_SLVRD_INTX 0x8000 + + /* Minimum High and Low period in nanosecends */ + +#define SS_MIN_SCL_HIGH 4000 +#define SS_MIN_SCL_LOW 4700 +#define FS_MIN_SCL_HIGH 600 +#define FS_MIN_SCL_LOW 1300 +#define HS_MIN_SCL_HIGH_100PF 60 +#define HS_MIN_SCL_LOW_100PF 120 + + /* I2C_STATUS (RO) */ + +#define I2C_STATUS_ACTIVITY (0x1 << 0) /* I2C ctrl is enabled */ +#define I2C_STATUS_TFNF (0x1 << 1) /* TX FIFO is not full */ +#define I2C_STATUS_TFE (0x1 << 2) /* TX FIFO is empty */ +#define I2C_STATUS_RFNE (0x1 << 3) /* RX FIFO is not empty */ +#define I2C_STATUS_RFF (0x1 << 4) /* RX FIFO is full */ +#define I2C_STATUS_MST_ACTIVITY (0x1 << 5) /* Master is not idle */ +#define I2C_STATUS_SLV_ACTIVITY (0x1 << 6) /* Slave is not idle */ + + /* Interrupts status */ + +#define I2C_INTR_RX_UNDER (0x1 << 0) +#define I2C_INTR_RX_OVER (0x1 << 1) +#define I2C_INTR_RX_FULL (0x1 << 2) +#define I2C_INTR_TX_OVER (0x1 << 3) +#define I2C_INTR_TX_EMPTY (0x1 << 4) +#define I2C_INTR_RD_REQ (0x1 << 5) +#define I2C_INTR_TX_ABRT (0x1 << 6) +#define I2C_INTR_RX_DONE (0x1 << 7) +#define I2C_INTR_ACTIVITY (0x1 << 8) +#define I2C_INTR_STOP_DET (0x1 << 9) +#define I2C_INTR_START_DET (0x1 << 10) +#define I2C_INTR_GEN_CALL (0x1 << 11) + +#define I2C_TX_BUFFER_DEPTH_MASK 0xFF0000 /* bit [23: 16] */ +#define I2C_RX_BUFFER_DEPTH_MASK 0x00FF00 /* bit [15: 8] */ + +#define I2C_INTR_MAX_BITS 12 + +#define I2C_ENABLE_CONTROLLER 0x01 +#define I2C_DISABLE_CONTROLLER 0x00 + +#define I2C_DATA_CMD_READ (0x01 << 8) +#define I2C_DATA_CMD_WRITE (0x00 << 8) + +#define I2C_TAR_DEFAULT_ADDR 0x55 +#define FT2000_I2C_CLK (48000000) /* I2C clock */ +/** + * @name: FI2c_WriteReg + * @msg: write i2c register + * @param {u32} BaseAddress base addr of i2c + * @param {u32} RegOffset addr offset of i2c register + * @param {u32} RegisterValue val to be write into register + * @return {void} + */ +#define FI2C_WriteReg(BaseAddress, RegOffset, RegisterValue) Ft_out32(BaseAddress + (u32)RegOffset, (u32)RegisterValue) + +/** + * @name: FI2c_ReadReg + * @msg: read i2c register + * @param {u32} BaseAddress base addr of i2c + * @param {u32} RegOffset addr offset of i2c register + * @return {u32} val read from register + */ +#define FI2C_ReadReg(BaseAddress, RegOffset) Ft_in32(BaseAddress + (u32)RegOffset) + +/* +* the following macros convert from BCD to binary and back. +* Be careful that the arguments are chars, only char width returned. +*/ + +#define BCD_TO_BIN(bcd) ((((((bcd)&0xf0) >> 4) * 10) + ((bcd)&0xf)) & 0xff) +#define BIN_TO_BCD(bin) (((((bin) / 10) << 4) + ((bin) % 10)) & 0xff) + +#define FI2C_GET_BASE_ADDR(pDev) ((pDev)->Config.BaseAddress) +#define FI2C_ENABLE_I2C_BUS(pDev) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_ENABLE, I2C_ENABLE_CONTROLLER)) +#define FI2C_DISABLE_I2C_BUS(pDev) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_ENABLE, I2C_DISABLE_CONTROLLER)) +#define FI2C_IS_I2C_BUS_ENABLED(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_ENABLE)) + +#define FI2C_GET_STATUS(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_STATUS)) +#define FI2C_CHECK_STATUS(pDev, stMask) (FI2C_GET_STATUS(pDev) & (stMask)) +#define FI2C_CLR_ALL_IRQ_STATUS(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_CLR_INTR)) +#define FI2C_GET_IRQ_STATUS(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_INTR_STAT)) +#define FI2C_GET_IRQ_MASK(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_INTR_MASK)) +#define FI2C_SET_IRQ_MASK(pDev, mask) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_INTR_MASK, (mask))) + +#define FI2C_GET_TX_TL(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_TX_TL)) +#define FI2C_GET_RX_TL(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_RX_TL)) +#define FI2C_SET_TX_TL(pDev, TxThres) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_TX_TL, (TxThres))) +#define FI2C_SET_RX_TL(pDev, RxThres) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_RX_TL, (RxThres))) + +#define FI2C_GET_TXFLR(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_TXFLR)) +#define FI2C_GET_RXFLR(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_RXFLR)) + +#define FI2C_CLR_IRQ_RX_OVER(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_CLR_RX_OVER)) +#define FI2C_CLR_IRQ_TX_OVER(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_CLR_TX_OVER)) + +#define FI2C_GET_COMP_PARAM_1(pDev) (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_COMP_PARAM_1)) +#define FI2C_GET_TX_BUFFER_DEPTH(pDev) ((I2C_TX_BUFFER_DEPTH_MASK & FI2C_GET_COMP_PARAM_1(pDev)) >> 16 ) +#define FI2C_GET_RX_BUFFER_DEPTH(pDev) ((I2C_RX_BUFFER_DEPTH_MASK & FI2C_GET_COMP_PARAM_1(pDev)) >> 8) + +#define FI2C_SEND_TX_STOP_CMD(pDev) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_DATA_CMD, I2C_DATA_CMD_STOP)) +#define FI2C_SET_TX_DATA(pDev, data) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_DATA_CMD, ((u32)(data) & I2C_DATA_CMD_DAT_MASK))) +#define FI2C_GET_RX_DATA(pDev) (u8)(I2C_DATA_CMD_DAT_MASK & (FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_DATA_CMD))) +#define FI2C_SEND_RX_NEXT_CMD(pDev) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_DATA_CMD, I2C_DATA_CMD_STOP | I2C_DATA_CMD_RESTART | I2C_DATA_CMD_RD)) +#define FI2C_SEND_RX_STOP_CMD(pDev) (FI2C_WriteReg(FI2C_GET_BASE_ADDR(pDev), I2C_DATA_CMD, I2C_DATA_CMD_STOP | I2C_DATA_CMD_RD)) + +void FI2C_resetReg(u32 BaseAddr); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_irq.c b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_irq.c new file mode 100644 index 0000000000..edab5525d2 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_irq.c @@ -0,0 +1,193 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-20 15:35:44 + * @LastEditTime: 2021-04-23 08:53:12 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_i2c.h" + +void FI2C_irqHandler(void *pArgs) +{ + FI2C_t *pDev; + u32 RegVal; + FI2C_IrqType_t IrqType = I2C_IRQ_TYPE_NONE; + + Ft_assertNoneReturn(NULL != pArgs); + pDev = (FI2C_t *)pArgs; + + RegVal = FI2C_GET_IRQ_STATUS(pDev); + if (I2C_IRQ_RX_FULL & RegVal) + { + IrqType = I2C_IRQ_TYPE_RX_COMPLETE; + } + else if (I2C_IRQ_TX_EMPTY & RegVal) + { + IrqType = I2C_IRQ_TYPE_TX_COMPLETE; + } + + if ((NULL != pDev->pIrqCallBack) && (I2C_IRQ_TYPE_NONE != IrqType)) + { + pDev->pIrqCallBack(IrqType, pDev, NULL); + } + + if (I2C_IRQ_TYPE_RX_COMPLETE == IrqType) + { + FI2C_setIrq(pDev, I2C_IRQ_RX_FULL, FALSE); + } + else if (I2C_IRQ_TYPE_TX_COMPLETE == IrqType) + { + FI2C_setIrq(pDev, I2C_IRQ_TX_EMPTY, FALSE); + } + + FI2C_CLR_ALL_IRQ_STATUS(pDev); + return; +} + +void FI2C_irqHandler4Fifo(void *pArgs) +{ + FI2C_t *pDev; + u32 RegVal; + u32 BytesToRx; + FI2C_IrqType_t IrqType = I2C_IRQ_TYPE_NONE; + + Ft_assertNoneReturn(NULL != pArgs); + pDev = (FI2C_t *)pArgs; + + /* check if i2c controller is enabled */ + if (!FI2C_IS_I2C_BUS_ENABLED(pDev)) + { + pDev->LastIrqErr = ERR_I2C_BUS_NOT_ENABLED; + return; + } + + /* check there is no interrupt */ + if (!FI2C_checkIfIntr(pDev)) + { + pDev->LastIrqErr = ERR_I2C_INVALID_PARAM; + return; + } + + /* read interrupt status */ + RegVal = FI2C_GET_IRQ_STATUS(pDev); + if (I2C_IRQ_RX_FULL & RegVal) + { + IrqType = I2C_IRQ_TYPE_RX_COMPLETE; + BytesToRx = FI2C_GET_RXFLR(pDev); + //FT_I2C_DEBUG_I("I2C_IRQ_RX_FULL bytes to rx %d", BytesToRx); + + while ((pDev->RxBuf.CurIndex < pDev->RxBuf.DataLength) && + (0 < BytesToRx)) + { + /* read one byte */ + pDev->RxBuf.BytePtr[pDev->RxBuf.CurIndex] = FI2C_GET_RX_DATA(pDev); + // FT_I2C_DEBUG_I("read 0x%x with idx %d, len %d remain %d", + // pDev->RxBuf.BytePtr[pDev->RxBuf.CurIndex], + // pDev->RxBuf.CurIndex, + // pDev->RxBuf.DataLength, + // BytesToRx); + pDev->RxBuf.CurIndex++; + BytesToRx--; + pDev->DelayHandle(10); + + /* read next byte */ + if (pDev->RxBuf.CurIndex != pDev->RxBuf.DataLength) + { + FI2C_SEND_RX_NEXT_CMD(pDev); + } + else + { + FI2C_SEND_RX_STOP_CMD(pDev); + pDev->DelayHandle(10); + FI2C_setIrq(pDev, I2C_IRQ_RX_FULL, FALSE); + //FT_I2C_DEBUG_I("stop and disable I2C_IRQ_RX_FULL"); + } + } + } + else if (I2C_IRQ_TX_EMPTY & RegVal) + { + IrqType = I2C_IRQ_TYPE_TX_COMPLETE; + // FT_I2C_DEBUG_I("I2C_IRQ_TX_EMPTY idx %d, len %d", + // pDev->TxBuf.CurIndex, + // pDev->TxBuf.DataLength); + if (pDev->TxBuf.CurIndex == pDev->TxBuf.DataLength) + { + FI2C_SEND_TX_STOP_CMD(pDev); + pDev->DelayHandle(10); + //FT_I2C_DEBUG_I("stop and disable I2C_IRQ_TX_EMPTY"); + FI2C_setIrq(pDev, I2C_IRQ_TX_EMPTY, FALSE); + } + else + { + //FT_I2C_DEBUG_I("TX 0x%x", pDev->TxBuf.BytePtr[pDev->TxBuf.CurIndex]); + FI2C_SET_TX_DATA(pDev, pDev->TxBuf.BytePtr[pDev->TxBuf.CurIndex]); + pDev->TxBuf.CurIndex++; + } + } + + if ((NULL != pDev->pIrqCallBack) && (I2C_IRQ_TYPE_NONE != IrqType)) + { + pDev->pIrqCallBack(IrqType, pDev, NULL); + } + + pDev->LastIrqErr = ERR_I2C_OK; + return; +} + +u32 FI2C_getIrqMask(FT_IN FI2C_t *pDev) +{ + u32 RegVal; + Ft_assertNoneReturn(NULL != pDev); + + RegVal = FI2C_GET_IRQ_MASK(pDev); + return RegVal; +} + + +void FI2C_setIrqMask(FT_IN FI2C_t *pDev, FT_IN u32 mask) +{ + u32 RegVal; + Ft_assertNoneReturn(NULL != pDev); + + RegVal = mask & I2C_IRQ_ALL_MASK; + FI2C_SET_IRQ_MASK(pDev, RegVal); + return; +} + +void FI2C_setIrq(FT_IN FI2C_t *pDev, FT_IN u32 maskBit, FT_IN bool_t enable) +{ + Ft_assertNoneReturn(0x0 != maskBit); + + if (TRUE == enable) + { + FI2C_setIrqMask(pDev, FI2C_getIrqMask(pDev) | maskBit); + } + else + { + FI2C_setIrqMask(pDev, FI2C_getIrqMask(pDev) & (~maskBit)); + } + return; +} + +bool_t FI2C_checkIfIntr(FT_IN FI2C_t *pDev) +{ + u32 RegVal; + Ft_assertNoneReturn(NULL != pDev); + + RegVal = FI2C_ReadReg(FI2C_GET_BASE_ADDR(pDev), I2C_RAW_INTR_STAT); + if (0 == (RegVal & (~I2C_IRQ_ACTIVITY))) + { + return FALSE; + } + else + { + return TRUE; + } +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_selftest.c b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_selftest.c new file mode 100644 index 0000000000..f94b6d6d1a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_i2c/ft_i2c_selftest.c @@ -0,0 +1,21 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:27:24 + * @Description:  This files is for i2c test cases + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include +#include +#include "ft_i2c_hw.h" +#include "ft_i2c.h" +#include "ft_status.h" +#include "ft_printf.h" diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c new file mode 100644 index 0000000000..0f2bf9abab --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c @@ -0,0 +1,626 @@ +/* + * @[H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-05 22:15:53 + * @LastEditTime: 2021-04-06 09:10:20 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + * * 1.00 hh 2021.04-06 init + */ + +#include "ft_qspi.h" +#include "qspi_hw.h" +#include "ft_io.h" +#include "ft_assert.h" +#include "ft_types.h" +#include "string.h" + +#include "ft_debug.h" + +#define FTQSPI_DEBUG_TAG "FTQSPI" + +#define FTQSPI_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FTQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTQSPI_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FTQSPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FTQSPI_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FTQSPI_DEBUG_TAG, format, ##__VA_ARGS__) + +ft_error_t FQSpi_CfgInitialize(FQSpi_t *pQspi, FQSpi_Config_t *pConfig) +{ + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pConfig != NULL); + + pQspi->config = *pConfig; + pQspi->isReady = FT_COMPONENT_IS_READLY; + + FQSpi_Reset(pQspi); + + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_MemcpyToReg + * @msg: Memory copy To Register + * @in param {FQSpi_t} *pQspi + * @in param {u8} *buf + * @in param {u32} length + * @return {ft_error_t} + */ +static ft_error_t FQSpi_MemcpyToReg(FQSpi_t *pQspi, FT_IN u8 *buf, u32 length) +{ + u32 val = 0; + FQSpi_Config_t *pConfig; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + if (!buf || (length > 4)) + { + return FQSPI_FAILURE; + } + + if (1 == length) + { + val = buf[0]; + } + else if (2 == length) + { + val = buf[1]; + val = (val << 8) + buf[0]; + } + else if (3 == length) + { + val = buf[2]; + val = (val << 8) + buf[1]; + val = (val << 8) + buf[0]; + } + else if (4 == length) + { + val = buf[3]; + val = (val << 8) + buf[2]; + val = (val << 8) + buf[1]; + val = (val << 8) + buf[0]; + } + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET, val); + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_MemcpyFromReg + * @msg: Memory copy from Register + * @in param {FT_INFQSpi_t} *pQspi + * @out param {u8} *buf + * @in param {u32} length + * @return {*} + */ +static ft_error_t FQSpi_MemcpyFromReg(FQSpi_t *pQspi, u8 *buf, u32 length) +{ + s32 i; + u32 val = 0; + FQSpi_Config_t *pConfig; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + for (i = 0; i < length; i++) + { + /* code */ + if (0 == i % 4) + { + val = Ft_in32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET); + } + buf[i] = (u8)(val >> (i % 4) * 8) & 0xff; + } + + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_FlashRead + * @msg: Reads bytes data from flash addr to buf + * @in param pQspi: + * @in param cmd: Read the instruction byte of the command + * @in param addr: Read the data start character + * @out param rxBuf: Read buffer + * @in param length: need read length + * @return {*} + */ +ft_error_t FQSpi_FlashRead(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u32 addr, + FT_OUT u8 *rxBuf, + u32 length) +{ + FQSpi_Config_t *pConfig; + FQSpi_RdCfgReg_t rdCfgReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + if ((NULL == rxBuf) || (0 == length)) + { + return FQSPI_FAILURE; + } + + rdCfgReg.data = 0; + + rdCfgReg.val.rdCmd = cmd; + rdCfgReg.val.dBuffer = 1; + rdCfgReg.val.rdAddrSel = pConfig->addrMode; + rdCfgReg.val.rdSckSel = pConfig->clkDiv; + rdCfgReg.val.rdTransfer = pConfig->transMode; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_RD_CFG_OFFSET, rdCfgReg.data); + + memcpy(rxBuf, (char *)(addr), length); + + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_FlashWrite + * @msg: Writes one page into flash,changing bits from 1 to 0 + * @in param pQspi: + * @in param cmd: write the instruction byte of the command + * @in param addr: write the data start character + * @in param txBuf: write buffer + * @in param length: need write length + * @return {*} + */ +ft_error_t FQSpi_FlashWrite(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u32 addr, + FT_IN u8 *txBuf, + u32 length) +{ + + FQSpi_Config_t *pConfig; + FQSpi_WrCfgReg_t wrCfgReg; + u32 index = 0; + u32 val = 0; + u32 *pu32Buf = NULL; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + if ((NULL == txBuf) || (0 == length)) + { + return FQSPI_FAILURE; + } + + pu32Buf = (u32 *)txBuf; + + wrCfgReg.data = 0; + wrCfgReg.val.wrCmd = cmd; + wrCfgReg.val.wrWait = 1; + wrCfgReg.val.wrSckSel = pConfig->clkDiv; + wrCfgReg.val.wrAddrsel = pConfig->addrMode; + wrCfgReg.val.wrTransfer = pConfig->transMode; + wrCfgReg.val.wrMode = 1; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_WR_CFG_OFFSET, wrCfgReg.data); + + while (length) + { + if (length >= 4) + { + Ft_out32(addr + index, pu32Buf[index / 4]); + length -= 4; + index += 4; + } + else + { + if (1 == length) + { + val = txBuf[index] | 0xFFFFFF00; + } + else if (2 == length) + { + val = txBuf[index] | (txBuf[index + 1] << 8) | 0xFFFF0000; + } + else + { + val = txBuf[index] | (txBuf[index + 1] << 8) | (txBuf[index + 2] << 8) | 0xFF000000; + } + + Ft_out32(addr + index, val); + length = 0; + } + } + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_FLUSH_OFFSET, 1); + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_FlashRegSet + * @msg: Set registers of flash + * @in param cmd: Command byte + * @in param writebuf: write buffer + * @in param length: need write length + * @return {*} + */ +ft_error_t FQSpi_FlashRegSet(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u8 *writebuf, + u32 length) +{ + FQSpi_Config_t *pConfig; + FQSpi_CmdPortReg_t cmdPortReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + cmdPortReg.data = 0; + cmdPortReg.val.cmd = cmd; + cmdPortReg.val.wait = 1; + cmdPortReg.val.sckSel = pConfig->clkDiv; + cmdPortReg.val.transfer = pConfig->transMode; + cmdPortReg.val.cs = pConfig->channel; + + if (length == 0) + { + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET, 1); + } + else + { + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.rwMum = length; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyToReg(pQspi, writebuf, length); + } + + return FQSPI_SUCCESS; +} + +/** + * @name: FQSpi_FlashRegSetWithaddr + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +ft_error_t FQSpi_FlashRegSetWithaddr(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u32 addr, + FT_IN u8 *writebuf, + u32 length) +{ + FQSpi_Config_t *pConfig; + FQSpi_CmdPortReg_t cmdPortReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + cmdPortReg.data = 0; + cmdPortReg.val.cmd = cmd; + cmdPortReg.val.wait = 1; + cmdPortReg.val.sckSel = pConfig->clkDiv; + cmdPortReg.val.transfer = pConfig->transMode; + cmdPortReg.val.cs = pConfig->channel; + cmdPortReg.val.cmdAddr = 1; + cmdPortReg.val.addrSel = pConfig->addrMode; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_ADDR_PORT_OFFSET, addr); + + if (length == 0) + { + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET, 0); + } + else + { + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.rwMum = length; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyToReg(pQspi, writebuf, length); + } + + return FQSPI_SUCCESS; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +ft_error_t FQSpi_FlashRegGet(FQSpi_t *pQspi, + FT_IN u8 cmd, + u8 *readbuf, + u32 length) +{ + FQSpi_Config_t *pConfig; + FQSpi_CmdPortReg_t cmdPortReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + cmdPortReg.data = 0; + cmdPortReg.val.cmd = cmd; + cmdPortReg.val.wait = 1; + cmdPortReg.val.sckSel = pConfig->clkDiv; + cmdPortReg.val.transfer = pConfig->transMode; + cmdPortReg.val.cs = pConfig->channel; + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.pBuffer = 1; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyFromReg(pQspi, readbuf, length); + return FQSPI_SUCCESS; +} + +ft_error_t FQSpi_FlashRegGetWithAddr(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u32 addr, + FT_IN u32 dummyCycle, + u8 *readbuf, + u32 length) +{ + FQSpi_Config_t *pConfig; + FQSpi_CmdPortReg_t cmdPortReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + cmdPortReg.data = 0; + cmdPortReg.val.cmd = cmd; + cmdPortReg.val.wait = 1; + cmdPortReg.val.sckSel = pConfig->clkDiv; + cmdPortReg.val.transfer = pConfig->transMode; + cmdPortReg.val.cs = pConfig->channel; + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.pBuffer = 1; + cmdPortReg.val.cmdAddr = 1; + + cmdPortReg.val.addrSel = pConfig->addrMode; + cmdPortReg.val.latency = 1; + cmdPortReg.val.dummy = dummyCycle - 1; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_ADDR_PORT_OFFSET, addr); + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyFromReg(pQspi, readbuf, length); + return FQSPI_SUCCESS; +} + +ft_error_t FQSpi_Write(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) +{ + FQSpi_Config_t *pConfig; + FQSpi_WrCfgReg_t wrCfgReg; + u32 length; + u32 index = 0; + u32 val = 0; + const u32 *pu32Buf = NULL; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + if ((FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK) == (pDataPack->flags & (FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK))) + { + FTQSPI_DEBUG_E(" Two addresses are not allowed at the same time "); + return FQSPI_FAILURE; + } + + if (0 == (pDataPack->flags & (FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK))) + { + FTQSPI_DEBUG_E(" There is no address configuration "); + return FQSPI_FAILURE; + } + + if (NULL == pDataPack->txBuf) + { + FTQSPI_DEBUG_E("pDataPack->txBuf is null"); + return FQSPI_FAILURE; + } + + pu32Buf = (const u32 *)pDataPack->txBuf; + wrCfgReg.data = 0; + + if (FQSPI_DATA_ADDRESS_3BYTE_MASK == (pDataPack->flags & FQSPI_DATA_ADDRESS_3BYTE_MASK)) + { + wrCfgReg.val.wrAddrsel = FT_QSPI_ADDR_SEL_3; + } + else if (FQSPI_DATA_ADDRESS_4BYTE_MASK == (pDataPack->flags & FQSPI_DATA_ADDRESS_4BYTE_MASK)) + { + wrCfgReg.val.wrAddrsel = FT_QSPI_ADDR_SEL_4; + } + + wrCfgReg.val.wrCmd = pDataPack->cmd; + wrCfgReg.val.wrWait = 1; + wrCfgReg.val.wrSckSel = pConfig->clkDiv; + wrCfgReg.val.wrTransfer = pConfig->transMode; + wrCfgReg.val.wrMode = 1; + length = pDataPack->length; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_WR_CFG_OFFSET, wrCfgReg.data); + + while (length) + { + if (length >= 4) + { + Ft_out32(pDataPack->addr + index, pu32Buf[index / 4]); + length -= 4; + index += 4; + } + else + { + if (1 == length) + { + val = pDataPack->txBuf[index] | 0xFFFFFF00; + } + else if (2 == length) + { + val = pDataPack->txBuf[index] | (pDataPack->txBuf[index + 1] << 8) | 0xFFFF0000; + } + else + { + val = pDataPack->txBuf[index] | (pDataPack->txBuf[index + 1] << 8) | (pDataPack->txBuf[index + 2] << 8) | 0xFF000000; + } + FTQSPI_DEBUG_I("val is 0x%x", val); + Ft_out32(pDataPack->addr + index, val); + length = 0; + } + } + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_FLUSH_OFFSET, 1); + return FQSPI_SUCCESS; +} + +ft_error_t FQSpi_Read(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) +{ + FQSpi_Config_t *pConfig; + FQSpi_RdCfgReg_t rdCfgReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + + if ((FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK) == (pDataPack->flags & (FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK))) + { + FTQSPI_DEBUG_E(" Two addresses are not allowed at the same time "); + return FQSPI_FAILURE; + } + + if (0 == (pDataPack->flags & (FQSPI_DATA_ADDRESS_3BYTE_MASK | FQSPI_DATA_ADDRESS_4BYTE_MASK))) + { + FTQSPI_DEBUG_E(" There is no address configuration "); + return FQSPI_FAILURE; + } + + if (NULL == pDataPack->rxBuf) + { + FTQSPI_DEBUG_E("pDataPack->rxBuf is null"); + return FQSPI_FAILURE; + } + + rdCfgReg.data = 0; + + if (FQSPI_DATA_NEED_DUMMY_MASK == (pDataPack->flags & FQSPI_DATA_NEED_DUMMY_MASK)) + { + rdCfgReg.val.rdLatency = 1; + rdCfgReg.val.dummy = pDataPack->dummyCycle - 1; + } + + if (FQSPI_DATA_ADDRESS_3BYTE_MASK == (pDataPack->flags & FQSPI_DATA_ADDRESS_3BYTE_MASK)) + { + rdCfgReg.val.rdAddrSel = FT_QSPI_ADDR_SEL_3; + } + else if (FQSPI_DATA_ADDRESS_4BYTE_MASK == (pDataPack->flags & FQSPI_DATA_ADDRESS_4BYTE_MASK)) + { + rdCfgReg.val.rdAddrSel = FT_QSPI_ADDR_SEL_4; + } + + rdCfgReg.val.rdCmd = pDataPack->cmd; + rdCfgReg.val.dBuffer = 1; + rdCfgReg.val.rdSckSel = pConfig->clkDiv; + rdCfgReg.val.rdTransfer = pConfig->transMode; + + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_RD_CFG_OFFSET, rdCfgReg.data); + + memcpy(pDataPack->rxBuf, (char *)(pDataPack->addr), pDataPack->length); + + return FQSPI_SUCCESS; +} + +ft_error_t +FQSpi_CmdOperation(FQSpi_t *pQspi, struct FQSpi_CmdPack *pCmdPack) +{ + FQSpi_Config_t *pConfig; + FQSpi_CmdPortReg_t cmdPortReg; + Ft_assertNonvoid(pQspi != NULL); + Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); + pConfig = &pQspi->config; + if ((FQSPI_CMD_ADDRESS_3BYTE_MASK | FQSPI_CMD_ADDRESS_4BYTE_MASK) == (pCmdPack->flags & (FQSPI_CMD_ADDRESS_3BYTE_MASK | FQSPI_CMD_ADDRESS_4BYTE_MASK))) + { + FTQSPI_DEBUG_E(" Two addresses are not allowed at the same time "); + return FQSPI_FAILURE; + } + + cmdPortReg.data = 0; + cmdPortReg.val.cmd = pCmdPack->cmd; + cmdPortReg.val.wait = 1; + cmdPortReg.val.sckSel = pConfig->clkDiv; + cmdPortReg.val.transfer = pConfig->transMode; + cmdPortReg.val.cs = pConfig->channel; + + if (FQSPI_CMD_NEED_ADDR_MASK == (pCmdPack->flags & FQSPI_CMD_NEED_ADDR_MASK)) + { + // FTQSPI_DEBUG_I(" send addr is 0x%x ", pCmdPack->addr); + cmdPortReg.val.cmdAddr = 1; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_ADDR_PORT_OFFSET, pCmdPack->addr); + } + + if (FQSPI_CMD_NEED_DUMMY_MASK == (pCmdPack->flags & FQSPI_CMD_NEED_DUMMY_MASK)) + { + cmdPortReg.val.latency = 1; + cmdPortReg.val.dummy = pCmdPack->dummyCycle - 1; + } + + if (FQSPI_CMD_ADDRESS_3BYTE_MASK == (pCmdPack->flags & FQSPI_CMD_ADDRESS_3BYTE_MASK)) + { + cmdPortReg.val.addrSel = FT_QSPI_ADDR_SEL_3; + } + else if (FQSPI_CMD_ADDRESS_4BYTE_MASK == (pCmdPack->flags & FQSPI_CMD_ADDRESS_4BYTE_MASK)) + { + cmdPortReg.val.addrSel = FT_QSPI_ADDR_SEL_4; + } + + if (FQSPI_CMD_NEED_SET_MASK == (pCmdPack->flags & (FQSPI_CMD_NEED_SET_MASK))) + { + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.rwMum = pCmdPack->length; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyToReg(pQspi, pCmdPack->txBuf, pCmdPack->length); + } + else if (FQSPI_CMD_NEED_GET_MASK == (pCmdPack->flags & (FQSPI_CMD_NEED_GET_MASK))) + { + cmdPortReg.val.dataTransfer = 1; + cmdPortReg.val.pBuffer = 1; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + FQSpi_MemcpyFromReg(pQspi, pCmdPack->rxBuf, pCmdPack->length); + } + else + { + // FTQSPI_DEBUG_I(" Use no data way 0x%x", cmdPortReg.data); + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CMD_PORT_OFFSET, cmdPortReg.data); + + if (FQSPI_CMD_NEED_ADDR_MASK == (pCmdPack->flags & FQSPI_CMD_NEED_ADDR_MASK)) + { + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET, 0); + } + else + { + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_LD_PORT_OFFSET, 1); + } + } + + return FQSPI_SUCCESS; +} diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.h b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.h new file mode 100644 index 0000000000..41f594e412 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.h @@ -0,0 +1,208 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-05 21:31:10 + * @LastEditTime: 2021-04-05 21:31:10 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + * * v1.0 hh 2021-04-05 init + */ + +#ifndef FT_QSPI_H +#define FT_QSPI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_error_code.h" + +#define FQSPI_SUCCESS FST_SUCCESS /* SUCCESS */ +#define FQSPI_FAILURE FT_MAKE_ERRCODE(errQspi, errBspGeneral, FST_FAILURE) /* Normal */ +#define FQSPI_TIMEOUT FT_MAKE_ERRCODE(errQspi, errBspGeneral, FST_TIMEOUT) /* Timeout */ +#define FQSPI_EILSEQ FT_MAKE_ERRCODE(errQspi, errBspGeneral, FST_EILSEQ) /* Illegal byte sequence. */ +#define FQSPI_INVALID_PARAM FT_MAKE_ERRCODE(errQspi, errBspGeneral, FST_INVALID_PARAM) /* Invalid param. */ + +/* qspi bsp command instruction operation */ +#define FQSPI_CMD_NEED_ADDR_MASK 0x1U +#define FQSPI_CMD_NEED_DUMMY_MASK 0x2U +#define FQSPI_CMD_NEED_GET_MASK 0x4U +#define FQSPI_CMD_NEED_SET_MASK 0x08U +#define FQSPI_CMD_ADDRESS_3BYTE_MASK 0x10U +#define FQSPI_CMD_ADDRESS_4BYTE_MASK 0x20U + +/* qspi cmd of transfer operation */ +#define FQSPI_DATA_NEED_DUMMY_MASK 0x1U +#define FQSPI_DATA_ADDRESS_3BYTE_MASK 0x2U +#define FQSPI_DATA_ADDRESS_4BYTE_MASK 0x4U + +#define FQSPI_FLASH_CMD_WRR 0x01 /* Write status register */ +#define FQSPI_FLASH_CMD_PP 0x02 /* Page program */ +#define FQSPI_FLASH_CMD_READ 0x03 /* Normal read data bytes */ +#define FQSPI_FLASH_CMD_WRDI 0x04 /* Write disable */ +#define FQSPI_FLASH_CMD_RDSR1 0x05 /* Read status register */ +#define FQSPI_FLASH_CMD_WREN 0x06 /* Write enable */ +#define FQSPI_FLASH_CMD_RDSR2 0x07 /* Read status register */ +#define FQSPI_FLASH_CMD_FAST_READ 0x0B /* Fast read data bytes */ +#define FQSPI_FLASH_CMD_4FAST_READ 0x0C /* Fast read data bytes */ +#define FQSPI_FLASH_CMD_4PP 0x12 /* Page program */ +#define FQSPI_FLASH_CMD_4READ 0x13 /* Normal read data bytes */ +#define FQSPI_FLASH_CMD_P4E 0x20 /* Erase 4kb sector */ +#define FQSPI_FLASH_CMD_4P4E 0x21 /* Erase 4kb sector */ +#define FQSPI_FLASH_CMD_QPP 0x32 /* Quad Page program */ +#define FQSPI_FLASH_CMD_4QPP 0x34 /* Quad Page program */ +#define FQSPI_FLASH_CMD_RDCR 0x35 /* Read config register */ +#define FQSPI_FLASH_CMD_BE 0x60 /* Bulk erase */ +#define FQSPI_FLASH_CMD_RDAR 0x65 /* Read Any Register */ +#define FQSPI_FLASH_CMD_QOR 0x6B /* Quad read data bytes */ +#define FQSPI_FLASH_CMD_4QOR 0x6C /* Quad read data bytes */ +#define FQSPI_FLASH_CMD_WRAR 0x71 /* Write Any Register */ +#define FQSPI_FLASH_CMD_RDID 0x9F /* Read JEDEC ID */ +#define FQSPI_FLASH_CMD_4BAM 0xB7 /* Enter 4 Bytes Mode */ +#define FQSPI_FLASH_CMD_4BE 0xC7 /* Bulk erase */ +#define FQSPI_FLASH_CMD_SE 0xD8 /* Sector erase */ +#define FQSPI_FLASH_CMD_4SE 0xDC /* Sector erase */ +#define FQSPI_FLASH_CMD_4BEX 0xE9 /* Exit 4 Bytes Mode */ +#define FQSPI_FLASH_CMD_QIOR 0xEB /* Quad read data bytes */ +#define FQSPI_FLASH_CMD_4QIOR 0xEC /* Quad read data bytes */ +#define FQSPI_FLASH_DISCOVERABLE_PARAMETER 0x5a +#define FQSPI_CMD_ENABLE_RESET 0x66 +#define FQSPI_CMD_RESET 0x99 + + struct FQSpi_DataPack + { + u32 flags; /* Follow qspi cmd of transfer operation */ + u32 cmd; /* Command instruction */ + u32 addr; /* Flash address */ + u32 dummyCycle; /* dummy Cycle */ + const u8 *txBuf; + u8 *rxBuf; /* Need send or read buffer */ + u32 length; /* Buffer length */ + }; + + struct FQSpi_CmdPack + { + u32 flags; /* Follow qspi bsp command instruction operation */ + u32 cmd; /* Command instruction */ + u32 addr; /* Command address */ + u32 dummyCycle; /* dummy Cycle */ + const u8 *txBuf; + u8 *rxBuf; /* Need send or read buffer */ + u32 length; /* Buffer length */ + }; + + typedef struct + { + u32 instanceId; /* Id of device */ + uintptr_t baseAddress; /* Base address of qspi */ + u32 transMode; /* Transfer mode */ + u32 capacity; /* Flash capacity */ + u32 addrMode; /**/ + u32 clkDiv; + u32 qspiDevNum; /*Qspi device number */ + u32 channel; /* Cs number */ + u32 bitWidth; /* Transfer unit width */ + } FQSpi_Config_t; + + typedef struct + { + FQSpi_Config_t config; + u32 isReady; /**< Device is initialized and ready */ + + } FQSpi_t; + + /** + * @name: FQSpi_LookupConfig + * @msg: FQSpi_LookupConfig returns a reference FQSpi_Config_t structure based on the + * unique device id. + * @in param {u32} instanceId : unique device + * @return {FQSpi_Config_t} FQSpi_Config_t is a reference to a config record in the configuration + * table (in qspi_g.c) corresponding to instanceId, or NULL if no match is found. + */ + FQSpi_Config_t *FQSpi_LookupConfig(u32 instanceId); + + /** + * @name: FQSpi_CfgInitialize + * @msg: This function intializes the configuration for the qspi instance + * @in param {FQSpi_t *} pQspi: A pointer to the qspi instance + * @in param {FQSpi_Config_t *} pConfig: A pointer to the qspi instance config record + * @return {ft_error_t} + */ + ft_error_t FQSpi_CfgInitialize(FQSpi_t *pQspi, FQSpi_Config_t *pConfig); + + /** + * @name: FQSpi_CmdOperation + * @msg: This function send command instruction by the struct FQSpi_CmdPack + * @in param {FQSpi_t *} pQspi: A pointer to the qspi instance + * @in param {struct FQSpi_CmdPack *} pCmdPack: Need to send command instruction package + * @return {ft_error_t} + */ + ft_error_t FQSpi_CmdOperation(FQSpi_t *pQspi, struct FQSpi_CmdPack *pCmdPack); + + /** + * @name: FQSpi_Read + * @msg: This function reads flash data from a specific address by {struct FQSpi_DataPack} + * @in param {FQSpi_t *} pQspi: A pointer to the qspi instance + * @in param {struct FQSpi_DataPack *} pDataPack: Need to read data package + * @return {ft_error_t} + */ + ft_error_t FQSpi_Read(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack); + + /** + * @name: FQSpi_Write + * @msg: This function writes data from a specific address by {struct FQSpi_DataPack} + * @in param {FQSpi_t *} pQspi: A pointer to the qspi instance + * @in param {struct FQSpi_DataPack *} pDataPack: Need to read data package + * @return {ft_error_t} + */ + ft_error_t FQSpi_Write(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack); + + /** + * @name: FQSpi_FlashRegSet + * @msg: This function sends command instruction with specific parameters + * @in param {FQSpi_t *} pQspi: A pointer to the qspi instance + * @in param {FT_IN u8} cmd: Command instruction + * @in param {FT_IN u8 *} writebuf: Data that needs to be sent through command instruction registers + * @in param {u32} length: Data length + * @return {ft_error_t} + */ + ft_error_t FQSpi_FlashRegSet(FQSpi_t *pQspi, + FT_IN u8 cmd, + FT_IN u8 *writebuf, + u32 length); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_g.c b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_g.c new file mode 100644 index 0000000000..044c0e427c --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_g.c @@ -0,0 +1,50 @@ +/* + * @[H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-07 11:31:12 + * @LastEditTime: 2021-04-07 11:31:13 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + * * v1.0 hh 2021-04-07 init + */ +#include "ft_qspi.h" +#include "ft_parameters.h" + +FQSpi_Config_t FqSpi_ConfigTable[FT_QSPI_NUM] = { + {.instanceId = FT_QSPI_INSTANCE, /* Id of device */ + .baseAddress = FT_QSPI_BASEADDR, /* Base address of qspi */ + .transMode = FT_QSPI_TRANSFER_1_1_1, /* Transfer mode */ + .capacity = FT_QSPI_FLASH_CAP_32MB, /* Flash capacity */ + .addrMode = FT_QSPI_ADDR_SEL_3, /**/ + .clkDiv = FT_QSPI_SCK_DIV_128, + .qspiDevNum = 1, + .channel = 0, + .bitWidth = 8}}; \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c new file mode 100644 index 0000000000..3d5691d2e3 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c @@ -0,0 +1,49 @@ +/* + * @[H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-02 18:32:42 + * @LastEditTime: 2021-04-02 18:32:43 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ +#include "qspi_hw.h" +#include "ft_qspi.h" +#include "ft_assert.h" +#include "ft_io.h" + +void FQSpi_Reset(FQSpi_t *pQspi) +{ + FQSpi_Config_t *pConfig; + Ft_assertVoid(pQspi != NULL); + pConfig = &pQspi->config; + Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CAP_OFFSET, + FT_REG_QSPI_CAP_FLASH_NUM(pConfig->qspiDevNum) | FT_REG_QSPI_CAP_FLASH_CAP(pConfig->capacity)); +} diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.h b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.h new file mode 100644 index 0000000000..cabe5da888 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.h @@ -0,0 +1,223 @@ +/* + * @[H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-02 18:32:54 + * @LastEditTime: 2021-04-02 21:30:00 + * @Description:  Description of file + * + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef QSPI_HW_H +#define QSPI_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_qspi.h" +#include "ft_types.h" + +/* register definition */ +#define FT_REG_QSPI_CAP_OFFSET (0x00) /* Flash capacity setting register */ +#define FT_REG_QSPI_RD_CFG_OFFSET (0x04) /* Address access reads configuration registers */ +#define FT_REG_QSPI_WR_CFG_OFFSET (0x08) /* Write buffer flush register */ +#define FT_REG_QSPI_FLUSH_OFFSET (0x0C) /* Write buffer flush register */ +#define FT_REG_QSPI_CMD_PORT_OFFSET (0x10) /* Command port register */ +#define FT_REG_QSPI_ADDR_PORT_OFFSET (0x14) /* Address port register */ +#define FT_REG_QSPI_HD_PORT_OFFSET (0x18) /* Upper bit port register */ +#define FT_REG_QSPI_LD_PORT_OFFSET (0x1C) /* low bit port register */ +#define FT_REG_QSPI_FUN_SET_OFFSET (0x20) /* CS setting register */ +#define FT_REG_QSPI_WIP_RD_OFFSET (0x24) /* WIP reads the Settings register */ +#define FT_REG_QSPI_WP_OFFSET (0x28) /* WP register */ +#define FT_REG_QSPI_MODE_OFFSET (0x2C) /* Mode setting register */ + +/*QSPI_CAP*/ +#define FT_REG_QSPI_CAP_FLASH_NUM(data) ((data) << 3) /* Flash number */ +#define FT_REG_QSPI_CAP_FLASH_CAP(data) ((data) << 0) /* The flash capacity */ + +/* RD_CFG */ +#define FT_RD_CFG_CMD(data) ((data) << 24) /* Read Command */ +#define FT_RD_CFG_THROUGH(data) ((data) << 23) /* The programming flag in the status register */ +#define FT_RD_CFG_TRANSFER(data) ((data) << 20) /* */ +#define FT_RD_CFG_ADDR_SEL(data) ((data) << 19) /* */ +#define FT_RD_CFG_LATENCY(data) ((data) << 18) /* */ +#define FT_RD_CFG_MODE_BYTE(data) ((data) << 17) /* */ +#define FT_RD_CFG_CMD_SIGN(data) ((data) << 9) /* */ +#define FT_RD_CFG_DUMMY(data) ((data) << 4) /* */ +#define FT_RD_CFG_D_BUFFER(data) ((data) << 3) /* */ +#define FT_RD_CFG_SCK_SEL(data) ((data) << 0) /* */ + +/*QSPI_WR_CFG*/ +#define FT_WR_CFG_CMD(data) ((data) << 24) +#define FT_WR_CFG_WAIT(data) ((data) << 9) +#define FT_WR_CFG_THROUGH(data) ((data) << 8) +#define FT_WR_CFG_TRANSFER(data) ((data) << 5) +#define FT_WR_CFG_ADDRSEL(data) ((data) << 4) +#define FT_WR_CFG_MODE(data) ((data) << 3) +#define FT_WR_CFG_SCK_SEL(data) ((data) << 0) + +/*QSPI_CMD_PORT*/ +#define FT_CMD_PORT_CMD(data) ((data) << 24) +#define FT_CMD_PORT_WAIT(data) ((data) << 22) +#define FT_CMD_PORT_THROUGH(data) ((data) << 21) +#define FT_CMD_PORT_CS(data) ((data) << 19) +#define FT_CMD_PORT_TRANSFER(data) ((data) << 16) +#define FT_CMD_PORT_ADDR(data) ((data) << 15) +#define FT_CMD_PORT_LATENCY(data) ((data) << 14) +#define FT_CMD_PORT_DATA_TRANS(data) ((data) << 13) +#define FT_CMD_PORT_ADDR_SEL(data) ((data) << 12) +#define FT_CMD_PORT_DUMMY(data) ((data) << 7) +#define FT_CMD_PORT_P_BUFFER(data) ((data) << 6) +#define FT_CMD_PORT_RW_NUM(data) ((data) << 3) +#define FT_CMD_PORT_CLK_SEL(data) ((data) << 0) + +/*QSPI_FUN_SET*/ +#define FT_FUN_SET_CS_HOLD(data) ((data) << 24) +#define FT_FUN_SET_CS_SETUP(data) ((data) << 16) +#define FT_FUN_SET_CS_DELAY(data) ((data) << 0) + +/*QSPI_WIP_RD*/ +#define FT_WIP_RD_CMD(data) ((data) << 24) +#define FT_WIP_RD_TRANSFER(data) ((data) << 3) +#define FT_WIP_RD_SCK_SEL(data) ((data) << 0) + +/*QSPI_WP*/ +#define FT_WP_EN(data) ((data) << 17) +#define FT_WP_WP(data) ((data) << 16) +#define FT_WP_HOLD(data) ((data) << 8) +#define FT_WP_SETUP(data) ((data) << 0) + +/*QSPI_MODE*/ +#define FT_MODE_VALID(data) ((data) << 8) +#define FT_MODE_MODE(data) ((data) << 0) + +#define FT_QSPI_FLASH_CAP_4MB 0 +#define FT_QSPI_FLASH_CAP_8MB 1 +#define FT_QSPI_FLASH_CAP_16MB 2 +#define FT_QSPI_FLASH_CAP_32MB 3 +#define FT_QSPI_FLASH_CAP_64MB 4 +#define FT_QSPI_FLASH_CAP_128MB 5 +#define FT_QSPI_FLASH_CAP_256MB 6 + +#define FT_QSPI_ADDR_SEL_3 0 +#define FT_QSPI_ADDR_SEL_4 1 + +#define FT_QSPI_SCK_DIV_128 0 +#define FT_QSPI_SCK_DIV_2 1 +#define FT_QSPI_SCK_DIV_4 2 +#define FT_QSPI_SCK_DIV_8 3 +#define FT_QSPI_SCK_DIV_16 4 +#define FT_QSPI_SCK_DIV_32 5 +#define FT_QSPI_SCK_DIV_64 6 + +#define FT_QSPI_TRANSFER_1_1_1 0 +#define FT_QSPI_TRANSFER_1_1_2 1 +#define FT_QSPI_TRANSFER_1_1_4 2 +#define FT_QSPI_TRANSFER_1_2_2 3 +#define FT_QSPI_TRANSFER_1_4_4 4 +#define FT_QSPI_TRANSFER_2_2_2 5 +#define FT_QSPI_TRANSFER_4_4_4 6 + + /* typedefs */ + /*QSPI_RD_CFG*/ + typedef union + { + u32 data; + struct + { + u32 rdSckSel : 3; /* 2:0 */ + u32 dBuffer : 1; /* 3 */ + u32 dummy : 5; /* 8:4 */ + u32 cmdSign : 8; /* 16:9 */ + u32 modeByte : 1; /* 17 */ + u32 rdLatency : 1; /* 18 */ + u32 rdAddrSel : 1; /* 19 */ + u32 rdTransfer : 3; /* 22:20 */ + u32 rdThrough : 1; /* 23 */ + u32 rdCmd : 8; /* 31:24 */ + } val; + } FQSpi_RdCfgReg_t; + + /*QSPI_WR_CFG*/ + typedef union + { + u32 data; + struct + { + u32 wrSckSel : 3; /* 2:0 */ + u32 wrMode : 1; /* 3 */ + u32 wrAddrsel : 1; /* 4 */ + u32 wrTransfer : 3; /* 7:5 */ + u32 wrThrough : 1; /* 8 */ + u32 wrWait : 1; /* 9 */ + u32 wrRes : 14; /* 23:10 */ + u32 wrCmd : 8; /* 31:24 */ + } val; + } FQSpi_WrCfgReg_t; + + /*QSPI_CMD_PORT*/ + typedef union + { + u32 data; + struct + { + u32 sckSel : 3; /* 2:0 */ + u32 rwMum : 3; /* 5:3 */ + u32 pBuffer : 1; /* 6 */ + u32 dummy : 5; /* 11:7 */ + u32 addrSel : 1; /* 12 */ + u32 dataTransfer : 1; /* 13 */ + u32 latency : 1; /* 14 */ + u32 cmdAddr : 1; /* 15 */ + u32 transfer : 3; /* 18:16 */ + u32 cs : 2; /* 20:19 */ + u32 through : 1; /* 21 */ + u32 wait : 1; /* 22 */ + u32 res : 1; /* 23 */ + u32 cmd : 8; /* 31:24 */ + } val; + } FQSpi_CmdPortReg_t; + + /** + * @name: FQSpi_Reset + * @msg: This routine performs the QSPI controller initialization. + * @in param: pQspi + * @return {*} + */ + void FQSpi_Reset(FQSpi_t *pQspi); + +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_sinit.c b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_sinit.c new file mode 100644 index 0000000000..79a30bae59 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_sinit.c @@ -0,0 +1,58 @@ +/* + * @[H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-07 13:22:01 + * @LastEditTime: 2021-04-07 13:22:01 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_qspi.h" +#include "ft_parameters.h" + +extern FQSpi_Config_t FqSpi_ConfigTable[FT_QSPI_NUM]; + +FQSpi_Config_t *FQSpi_LookupConfig(u32 instanceId) +{ + FQSpi_Config_t *pFQSpi_Config_t = NULL; + + u32 Index; + for (Index = 0; Index < (u32)FT_GMAC_INSTANCES_NUM; Index++) + { + if (FqSpi_ConfigTable[Index].instanceId == instanceId) + { + pFQSpi_Config_t = &FqSpi_ConfigTable[Index]; + break; + } + } + + return pFQSpi_Config_t; +} diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.c new file mode 100644 index 0000000000..d1f8b5c43d --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.c @@ -0,0 +1,718 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-15 13:57:52 + * @Description:  This files is for implemenation of sd ctrl + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_parameters.h" +#include "ft_assert.h" +#include "ft_io.h" +#include "ft_sdctrl.h" +#include "ft_sdctrl_hw.h" +#include "ft_debug.h" +#include "ft_printf.h" +#include "ft_trace.h" +#include "ft_cache.h" +#define FT_SD_CTRL_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SD_CTRL_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SD_CTRL_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * @Description: setup sd ctrl clk freq + * @Date: 2021-04-01 09:28:28 + * @Param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN u32} sdClk, clk from SD + */ +void FSdCtrl_ClkFreqSetup(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN u32 sdClk) +{ + FSdCtrl_Config_t *pConfig; + u32 clkDiv; + u32 inputClk; + u32 tmpSdFreq; + u32 workSpeed; + + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + inputClk = pConfig->inputClockHz; + + /* sd work speed is limit to 25MHz */ + workSpeed = (sdClk < SD_CLK_FREQ_25MHZ) ? sdClk : SD_CLK_FREQ_25MHZ; + + /* if sd clk freq is valid and is two times greater than io input clk */ + if ((SD_CLK_FREQ_400KHZ < workSpeed) && (inputClk > (2 * workSpeed))) + { + clkDiv = (u32)(inputClk / (2 * workSpeed)) - 1; + + /* calculte sd freq one more time base on divsor */ + tmpSdFreq = (u32)inputClk / (2 * (clkDiv + 1)); + + /* if calculated sd freq is greater than the real val */ + if (tmpSdFreq > workSpeed) + { + clkDiv += 1; + } + } + else + { + clkDiv = SD_FRRQ_DIV_DEFAULT; + } + + Ft_out32(pConfig->baseAddress + CLOCK_DIV_REG_OFFSET, clkDiv); + + return; +} + +/** + * @name: FsdCtrl_Init + * @msg: init sd ctrl + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +ft_error_t FsdCtrl_Init(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* Disable card detection */ + Ft_out32(pConfig->baseAddress + SD_SEN_REG_OFFSET, 0); + + /* Disable all interrupts */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_EN_REG_OFFSET, 0); + Ft_out32(pConfig->baseAddress + ERROR_INT_EN_REG_OFFSET, 0); + Ft_out32(pConfig->baseAddress + BD_ISR_EN_REG_OFFSET, 0); + // Ft_out32(pConfig->baseAddress + NORMAL_INT_EN_REG_OFFSET, NORMAL_INT_EN_ECCRCE); + + /* Clear status register */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, 0); + Ft_out32(pConfig->baseAddress + ERROR_INT_STATUS_REG, 0); + Ft_out32(pConfig->baseAddress + BD_ISR_REG, 0); + + /* Set default ctrl register */ + Ft_out32(pConfig->baseAddress + CONTROLL_SETTING_REG_OFFSET, 0x0f00); + + /* Set default drive and sampling register */ + Ft_out32(pConfig->baseAddress + SD_DRV_REG_OFFSET, 0); + Ft_out32(pConfig->baseAddress + SD_SAMP_REG_OFFSET, 0); + + /* Configure to default cmd data timeout */ + Ft_out32(pConfig->baseAddress + TIMEOUT_CMD_REG_OFFSET, 0xFFFFFFFF); + //FSdCtrl_ClkFreqSetup(pFtsdCtrl, 1); + + Ft_out32(pConfig->baseAddress + CLOCK_DIV_REG_OFFSET, SD_FRRQ_DIV_DEFAULT); + Ft_out32(pConfig->baseAddress + SD_SAMP_REG_OFFSET, SD_SAMP_DEFAULT); + + pFtsdCtrl->isReady = FT_COMPONENT_IS_READLY; + + return FTSDC_SUCCESS; +} + +/** + * @name: FSdCtrl_CardDetect + * @msg: + * @in param {FtsdCtrl_t} *pFtsdCtrl + * @inout param: + * @out param: + * @return {*} + */ +bool_t FSdCtrl_CardDetect(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + u32 status; + + Ft_assertBool(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + status = Ft_in32(pConfig->baseAddress + STATUS_REG); + + /* check card-detection signal */ + if (((status)&STATUS_REG_CDSL) == STATUS_REG_CDSL) + { + pConfig->cardDetect = 0; + } + else + { + pConfig->cardDetect = 1; + } + + return pConfig->cardDetect; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +void FSdCtrl_ResetDma(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + Ft_setBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); + Ft_clearBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INFtsdCtrl_t} *pFtsdCtrl + * @param {FT_INu32} cmdIndex + * @param {FT_INu32} rspType + */ +u32 FSdCtrl_PrepareCmdRaw(FT_IN u32 cmdIndex, FT_IN u32 rspType) +{ + u32 rawCmd = 0; + + rawCmd |= CMD_SETTING_CMDI(cmdIndex); + + switch (rspType) + { + case FTSDCTRL_CMD_RES_NONE: + rawCmd |= CMD_SETTING_RTS(0); + break; + case FTSDCTRL_CMD_RES_LONG: + rawCmd |= CMD_SETTING_RTS(1); + break; + case FTSDCTRL_CMD_RES_SHORT: + rawCmd |= CMD_SETTING_RTS(2); + break; + default: + rawCmd |= CMD_SETTING_RTS(0); + break; + } + + return rawCmd; +} + +/** + * Description: sd ctrl write data + * Date: 2021-03-31 11:12:09 + * Param: + * return {*} + * param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * param {FT_IN UINTPTR} dataAddr, src addr + * param {FT_IN UINTPTR} cardAddr, dst addr + * param {FT_IN u32} blkNum, num of blks to write + */ +void FSdCtrl_WriteData(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN UINTPTR dataAddr, + FT_IN UINTPTR cardAddr, FT_IN u32 blkNum) +{ + + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* write 1 to clear data status register and command status register */ + Ft_out32(pConfig->baseAddress + BD_ISR_REG, BD_ISR_REG_TRS); + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_STATUS_CC); + + /* set DMA BD */ + Ft_setBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); + Ft_clearBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); + + /* set transfer lenth */ + Ft_out32(pConfig->baseAddress + BLK_CNT_REG, blkNum); + + /* set DMA discriptor data low address,data high address,card low address,card high address*/ + Ft_out32(pConfig->baseAddress + DAT_IN_M_TX_BD, dataAddr); + Ft_out32(pConfig->baseAddress + DAT_IN_M_TX_BD, 0); + Ft_out32(pConfig->baseAddress + DAT_IN_M_TX_BD, cardAddr); + Ft_out32(pConfig->baseAddress + DAT_IN_M_TX_BD, 0); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +void FSdCtrl_ReadData(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN UINTPTR dataAddr, + FT_IN UINTPTR cardAddr, + FT_IN u32 blkNum) +{ + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* clear data status register and command status register */ + Ft_out32(pConfig->baseAddress + BD_ISR_REG, BD_ISR_EN_ETRS); + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_EN_ECC); + + /* set DMA BD */ + Ft_setBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); + Ft_clearBit32((UINTPTR)(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET), SOFTWARE_RESET_BDRST); + + /* set transfer lenth */ + Ft_out32(pConfig->baseAddress + BLK_CNT_REG, blkNum); + + /* set DMA discriptor data low address,data high address,card low address,card high address*/ + + Ft_out32(pConfig->baseAddress + DAT_IN_M_RX_BD, dataAddr); + Ft_out32(pConfig->baseAddress + DAT_IN_M_RX_BD, 0); + Ft_out32(pConfig->baseAddress + DAT_IN_M_RX_BD, cardAddr); + Ft_out32(pConfig->baseAddress + DAT_IN_M_RX_BD, 0); +} + +/** + * Description: send ctrl cmd + * Date: 2021-03-31 10:51:33 + * Param: + * return {*} + * param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * param {FT_IN pFtsdCtrl_delayTimer_t} pDelayTimer_fun + * param {FT_IN u32} cmd + * param {FT_IN u32} respType + * param {FT_IN u32} arg + */ +static ft_error_t FsdCtrl_privateSendCmd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, FT_IN u32 cmd, FT_IN u32 respType, FT_IN u32 arg) +{ + u32 temp; + u32 sd_cmd; + u32 sd_arg; + FSdCtrl_Config_t *pConfig; + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* enable cmd finished irq */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_EN_ECC); + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, 0); + + Ft_out32(pConfig->baseAddress + ERROR_INT_STATUS_REG, BD_ISR_EN_ETRS); + Ft_out32(pConfig->baseAddress + ERROR_INT_STATUS_REG, 0); + + /* prepare cmd msg, along with arg */ + sd_cmd = FSdCtrl_PrepareCmdRaw(cmd, respType); + sd_arg = arg; + + /* send cmd and arg */ + Ft_out32(pConfig->baseAddress + CMD_SETTING_REG_OFFSET, sd_cmd); + Ft_out32(pConfig->baseAddress + ARGUMENT_REG_OFFSET, sd_arg); + + if (pConfig->workMode & FTSDCTRL_CMD_IRQ_MASK) + { + if (pFtsdCtrl->cmdWaitCallback) + { + /* if irq is enabled and call back registered, enter call back procedure */ + return pFtsdCtrl->cmdWaitCallback(pFtsdCtrl); + } + else + { + return FTSDC_INVALID_PARAM; + } + } + else + { + temp = Ft_in32(pConfig->baseAddress + NORMAL_INT_STATUS_REG); + /* polling wait for cmd-finished response */ + while (NORMAL_INT_STATUS_CC != (temp & NORMAL_INT_STATUS_CC)) + { + temp = Ft_in32(pConfig->baseAddress + NORMAL_INT_STATUS_REG); + pDelayTimer_fun(1); + } + } + + return FTSDC_SUCCESS; +} + +/** + * Description: wait data write procedure to end + * Date: 2021-03-31 11:28:41 + * Param: + * return {*} + */ +ft_error_t FSdCtrl_WaitWriteDataEnd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 blkNum) +{ + u32 status; + u32 statusMask; + ft_error_t ret; + s32 timeout = 1000; + FSdCtrl_Config_t *pConfig; + + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + if (pConfig->workMode & FTSDCTRL_DATA_WRITE_IRQ_MASK) + { + /* enter irq mode */ + if (NULL == pFtsdCtrl->writeWaitCallback) + { + return FTSDC_INVALID_PARAM; + } + + ret = pFtsdCtrl->writeWaitCallback(pFtsdCtrl); + if (FTSDC_SUCCESS != ret) + { + return FTSDC_EILSEQ; + } + } + else + { + /* wait for DMA-error or DMA-finished status */ + statusMask = BD_ISR_REG_DAIS | BD_ISR_REG_TRS; + do + { + status = (Ft_in32(pConfig->baseAddress + BD_ISR_REG) & statusMask); + pDelayTimer_fun(1); + timeout--; + } while ((!status) && timeout); + + if (status & BD_ISR_REG_DAIS) + { + if (status & (BD_ISR_REG_TRE | BD_ISR_REG_NRCRC | BD_ISR_REG_CMDE)) + { + return FTSDC_EILSEQ; + } + else if (!timeout) + { + return FTSDC_TIMEOUT; + } + } + } + + /* multi block needs MMC_STOP_TRANSMISSION to stop process*/ + if (blkNum > 1) + { + return FsdCtrl_privateSendCmd(pFtsdCtrl, pDelayTimer_fun, 12, CMD_SETTING_RTS(2), 0); + } + + return FTSDC_SUCCESS; +} + +/** + * Description: wait for read data procedure end + * Date: 2021-03-31 11:25:25 + * Param: + * return {*} + */ +ft_error_t FSdCtrl_WaitReadDataEnd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 blkNum) +{ + u32 status; + u32 statusMask; + ft_error_t ret; + s32 timeout = 1000; + FSdCtrl_Config_t *pConfig; + + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + if (pConfig->workMode & FTSDCTRL_DATA_READ_IRQ_MASK) + { + if (pFtsdCtrl->readWaitCallback) + { + ret = pFtsdCtrl->readWaitCallback(pFtsdCtrl); + if (FTSDC_SUCCESS != ret) + { + return FTSDC_EILSEQ; + } + } + else + { + return FTSDC_INVALID_PARAM; + } + } + else + { + /* wait for DMA-error or Read-finish status */ + statusMask = BD_ISR_REG_DAIS | BD_ISR_REG_RESPE; + do + { + status = (Ft_in32(pConfig->baseAddress + BD_ISR_REG) & statusMask); + pDelayTimer_fun(1); + timeout--; + } while ((!status) && timeout); + + if (status & BD_ISR_REG_DAIS) + { + /* error handle */ + if (status & (BD_ISR_REG_TRE | BD_ISR_REG_NRCRC | BD_ISR_REG_CMDE)) + { + return FTSDC_EILSEQ; + } + else if (!timeout) + { + return FTSDC_TIMEOUT; + } + } + } + + // /* multi block needs MMC_STOP_TRANSMISSION to stop process*/ + if (blkNum > 1) + { + return FsdCtrl_privateSendCmd(pFtsdCtrl, pDelayTimer_fun, 12, CMD_SETTING_RTS(2), 0); + } + + return FTSDC_SUCCESS; +} + +/** + * Description: wait for cmd response + * Date: 2021-03-31 11:18:57 + * Param: + * return {*} + */ +ft_error_t FSdCtrl_WaitCmdEnd(FT_OUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 rspType, + FT_OUT u32 *cmdRsp) +{ + u32 status; + u32 statusMask; + s32 timeout = 1000; + const FSdCtrl_Config_t *pConfig; + ft_error_t result = FST_SUCCESS; + + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + if (pConfig->workMode & FTSDCTRL_CMD_IRQ_MASK) + { + if (pFtsdCtrl->cmdWaitCallback) + { + result = pFtsdCtrl->cmdWaitCallback(pFtsdCtrl); + } + else + { + return FTSDC_INVALID_PARAM; + } + status = Ft_in32(pConfig->baseAddress + NORMAL_INT_STATUS_REG); + } + else + { + /* wait for cmd-error or cmd-finish respones */ + statusMask = NORMAL_INT_STATUS_EI | NORMAL_INT_STATUS_CC; + + do + { + status = (Ft_in32(pConfig->baseAddress + NORMAL_INT_STATUS_REG) & statusMask); + pDelayTimer_fun(1); + timeout--; + + } while ((!status) && timeout); + } + + if (status & NORMAL_INT_STATUS_EI) + { + /* error handle */ + status = Ft_in32(pConfig->baseAddress + ERROR_INT_STATUS_REG); + + if (!timeout) + { + return FTSDC_TIMEOUT; + } + else if ((status & NORMAL_INT_EN_ECCRCE)) + { + return FTSDC_EILSEQ; + } + } + + if (rspType != FTSDCTRL_CMD_RES_NONE) + { + /* get cmd respones */ + if (rspType == FTSDCTRL_CMD_RES_LONG) + { + cmdRsp[0] = Ft_in32(pConfig->baseAddress + CMD_RESP_1); + cmdRsp[1] = Ft_in32(pConfig->baseAddress + CMD_RESP_2); + cmdRsp[2] = Ft_in32(pConfig->baseAddress + CMD_RESP_3); + cmdRsp[3] = Ft_in32(pConfig->baseAddress + CMD_RESP_4); + } + else + { + cmdRsp[0] = Ft_in32(pConfig->baseAddress + CMD_RESP_1); + cmdRsp[1] = 0; + cmdRsp[2] = 0; + cmdRsp[3] = 0; + } + } + + return result; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +void FSdCtrl_DoCmd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN u32 cmdIndex, + FT_IN u32 rspType, + u32 arg) +{ + u32 cmd; + + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* clear normal interrupt status */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_STATUS_EI); + + /* set command*/ + cmd = FSdCtrl_PrepareCmdRaw(cmdIndex, rspType); + Ft_out32(pConfig->baseAddress + CMD_SETTING_REG_OFFSET, cmd); + + if (cmdIndex == 41) + { + arg = 0x40ff8000; + } + + Ft_out32(pConfig->baseAddress + ARGUMENT_REG_OFFSET, arg); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +void FSdCtrl_DoACmd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN u32 cmdIndex, + FT_IN u32 rspType, + u32 arg) +{ + u32 cmd; + + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* clear normal interrupt status */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_EN_ECC); + /* set command*/ + cmd = FSdCtrl_PrepareCmdRaw(cmdIndex, rspType); + cmd |= CMD_SETTING_TRTY(2); + Ft_out32(pConfig->baseAddress + CMD_SETTING_REG_OFFSET, cmd); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN pFtsdCtrl_irqWaitCallback_t} callBack + */ +void FSdCtrl_WriteWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack) +{ + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pFtsdCtrl->writeWaitCallback = callBack; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN pFtsdCtrl_irqWaitCallback_t} callBack + */ +void FSdCtrl_ReadWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack) +{ + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pFtsdCtrl->readWaitCallback = callBack; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN pFtsdCtrl_irqWaitCallback_t} callBack + */ +void FSdCtrl_CmdWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack) +{ + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pFtsdCtrl->cmdWaitCallback = callBack; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +u32 FSdCtrl_GetNormalIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + FSdCtrl_Config_t *pConfig; + pConfig = &pFtsdCtrl->config; + // rt_kprintf("FSdCtrl_GetNormalIrqStatus is in \r\n"); + return Ft_in32(pConfig->baseAddress + NORMAL_INT_STATUS_REG); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +u32 FSdCtrl_GetDataIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + FSdCtrl_Config_t *pConfig; + pConfig = &pFtsdCtrl->config; + + return Ft_in32(pConfig->baseAddress + BD_ISR_REG); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +u32 FSdCtrl_GetErrorIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + Ft_assertNonvoid(FT_NULL != pFtsdCtrl); + FSdCtrl_Config_t *pConfig; + pConfig = &pFtsdCtrl->config; + + return Ft_in32(pConfig->baseAddress + ERROR_INT_STATUS_REG); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.h b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.h new file mode 100644 index 0000000000..1951645547 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl.h @@ -0,0 +1,235 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-19 17:04:44 + * @Description:  This files is for implementation of sd ctrl + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FTSDCTRL_H +#define FTSDCTRL_H +#include "ft_types.h" +#include "ft_error_code.h" + +/* sd ctrl module debug tag */ +#define FT_SD_CTRL_DEBUG_TAG "FT_SD_CTRL" + +/* definition of errcode for sd module */ +#define FTSDC_SUCCESS FST_SUCCESS /* 成功 */ +#define FTSDC_FAILURE FT_MAKE_ERRCODE(errModeSdCtrl, errBspGeneral, FST_FAILURE) /* Normal */ +#define FTSDC_TIMEOUT FT_MAKE_ERRCODE(errModeSdCtrl, errBspGeneral, FST_TIMEOUT) /* Timeout */ +#define FTSDC_EILSEQ FT_MAKE_ERRCODE(errModeSdCtrl, errBspGeneral, FST_EILSEQ) /* Illegal byte sequence. */ +#define FTSDC_INVALID_PARAM FT_MAKE_ERRCODE(errModeSdCtrl, errBspGeneral, FST_INVALID_PARAM) /* Illegal byte sequence. */ + +/* type of response to sd cmds */ +#define FTSDCTRL_CMD_RES_NONE 0 /* No response */ +#define FTSDCTRL_CMD_RES_LONG 1 /* The response length is long, code length is 128 */ +#define FTSDCTRL_CMD_RES_SHORT 2 /* The response length is short, code length is 32 */ + +/* irq enable bits */ +#define FTSDCTRL_DATA_WRITE_IRQ_MASK 0x1 +#define FTSDCTRL_DATA_READ_IRQ_MASK 0x2 +#define FTSDCTRL_CMD_IRQ_MASK 0x4 + +/* type of irq callback */ +typedef enum +{ + FTSDCTRL_DMADATAIRQID = 0x1U, /* Select dma interrupt */ + FTSDCTRL_CMDIRQID = 0x2U, /* Select cmd interrupt */ + FTSDCTRL_ERRORIRQID = 0x3U, /* Select error interrupt */ +} FSdCtrl_IrqCallbackSelect_t; + +/* normal irq enable bits for NORMAL_INT_EN_REG_OFFSET */ +typedef enum +{ + NORMAL_IRQ_CC = 1, /* Command completion interrupt */ + NORMAL_IRQ_CR = 2, /* Card removal interrupt */ + NORMAL_IRQ_EI = 4 /* Command error interrupt */ +} FSdCtrl_NormalIrqSelect_t; + +/* error irq enable bits for ERROR_INT_EN_REG_OFFSET */ +typedef enum +{ + ERROR_IRQ_CTE = 1, /* Command timeout error interrupted */ + ERROR_IRQ_CCRCE = 2, /* Command CRC error interrupt */ + ERROR_IRQ_CIR = 4, /* Command index error interrupt */ + ERROR_IRQ_CNR = 8 /* Command response error interrupted */ +} FSdCtrl_ErrorIrqSelect_t; + +/* data trans irq bits for BD_ISR_EN_REG_OFFSET */ +typedef enum +{ + BD_IRQ_TRS = 1, /* DMA transmission has been interrupted */ + BD_IRQ_DTE = 2, /* Timeout interrupt */ + BD_IRQ_CMDE = 4, /* Command response error interrupted */ + BD_IRQ_TRE = 8, /* Command response error interrupt CRC response error interrupt */ + BD_IRQ_NRCRC = 0x10, /* No CRC response interruption */ + BD_IRQ_DATFRAX = 0x20, /* AXI bus forces to release interrupts */ + BD_IRQ_RESPE = 0x40, /* Read SD card operation, AXI BR channel complete interrupt */ + BD_IRQ_DAIS = 0x80, /* DMA error interrupt */ +} FSdCtrl_BdIrqSelect; + +/* types of irq */ +typedef enum +{ + FTSDC_NORMAL_ISR = 0U, + FTSDC_BD_ISR, + FTSDC_ERROR_ISR +} FSdCtrl_IsrCallbackSelect_t; + +/* voltage supply range type of SD Card follow POWER_CONTROLL_REG +*/ +typedef enum +{ + FSDC_HIGH_V = 0, /* SD card operate within the voltage range of 2.7-3.6 V */ + FSDC_DUAL_V, /* SD card operate within the Low Voltage Range (T.B.D) and 2.7-3.6 V */ + + MAX_FSDC_VOLTAGE_TYPE +} FSdCtrl_VRangeType_t; + +/* read-write property of SD Card */ +typedef enum +{ + FSDC_RW_CARD = 0, + FSDC_RO_CARD, + + MAX_FSDC_WR_CARD_TYPE +} FSdCtrl_WRType_t; + +/* capacity type of SD Card */ +typedef enum +{ + FSDC_SD_CARD = 0, + FSDC_SDHC_CARD, + FSDC_SDXC_CARD, + + MAX_FSDC_CARD_CAPACITY_TYPE +} FSdCtrl_CapacityType_t; + +/* speed class of SD Card */ +typedef enum +{ + FSDC_CLASS0 = 0, + FSDC_CLASS2, + FSDC_CLASS4, + FSDC_CLASS6, + + MAX_FSDC_CLASS_TYPE +} FSdCtrl_ClassType_t; + +/** + * This typedef contains configuration information for the sd device. + */ +typedef struct +{ + u32 instanceId; /* Unique ID of device */ + u32 baseAddress; /* Base address of the device */ + u32 inputClockHz; /* Input clock frequency */ + u32 cardDetect; /* Card Detect */ + u32 writeProtect; /* Write Protect */ + u32 busWidth; /* Bus Width */ + u32 dmaIrqNum; /* dma irq number */ + u32 normalIrqNum; /* normal irq number */ + u32 errIrqNum; /* error irq number */ + u8 workMode; /* Work mode for data transfers , + If the mask bit is 0, polling is used , + follow irq enable bits*/ +} FSdCtrl_Config_t; + +typedef void (*FtsdCtrl_irqCallback_t)(void *args); + +/* irq callback and iput args */ +typedef struct +{ + FtsdCtrl_irqCallback_t pDmaDataCallback; /* DMA data interrupt function pointer */ + void *pDmaDataArgs; + + FtsdCtrl_irqCallback_t pCmdCallback; /* Commond interrupt function pointer */ + void *pCmdArgs; + + FtsdCtrl_irqCallback_t pErrorCallback; /* Error interrupt function pointer */ + void *pErrorArgs; +} FSdCtrl_IrqConfig_t; + +typedef struct FtsdCtrl FtsdCtrl_t; +typedef void (*pFtsdCtrl_delayTimer_t)(ft_base_t delayUs); +typedef ft_error_t (*pFtsdCtrl_irqWaitCallback_t)(FtsdCtrl_t *FtsdCtrl); + +/* ctrl instance of sd */ +struct FtsdCtrl +{ + FSdCtrl_Config_t config; + u32 isReady; /* Device is initialized and ready */ + /*************reserved**************/ + FSdCtrl_VRangeType_t voltageType; + FSdCtrl_WRType_t writeReadType; + FSdCtrl_CapacityType_t capacityType; + FSdCtrl_ClassType_t speedClassType; + /*************reserved**************/ + FSdCtrl_IrqConfig_t irqConfig; + pFtsdCtrl_irqWaitCallback_t writeWaitCallback; /* function pointer .Used to determine whether the data transmission is complete*/ + pFtsdCtrl_irqWaitCallback_t readWaitCallback; /* function pointer .Used to determine whether the data received is complete*/ + pFtsdCtrl_irqWaitCallback_t cmdWaitCallback; /* function pointer . Used to determine whether the command is complete */ +}; + +u32 FSdCtrl_PrepareCmdRaw(FT_IN u32 cmdIndex, FT_IN u32 rspType); +void FSdCtrl_WriteData(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN UINTPTR dataAddr, FT_IN UINTPTR cmdArg, FT_IN u32 blkNum); +void FSdCtrl_ReadData(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN UINTPTR dataAddr, + FT_IN UINTPTR cardAddr, + FT_IN u32 blkNum); +ft_error_t FSdCtrl_WaitCmdEnd(FT_OUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 rspType, + FT_OUT u32 *cmdRsp); +ft_error_t FSdCtrl_WaitReadDataEnd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 blkNum); +ft_error_t FSdCtrl_WaitWriteDataEnd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN pFtsdCtrl_delayTimer_t pDelayTimer_fun, + FT_IN u32 blkNum); + +void FSdCtrl_DoCmd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN u32 cmdIndex, FT_IN u32 rspType, u32 arg); +void FSdCtrl_DoACmd(FT_INOUT FtsdCtrl_t *pFtsdCtrl, + FT_IN u32 cmdIndex, + FT_IN u32 rspType, + u32 arg); +void FSdCtrl_NormalIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +void FSdCtrl_DmaIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +void FSdCtrl_ErrIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +bool_t FSdCtrl_CardDetect(FT_INOUT FtsdCtrl_t *pFtsdCtrl); + +FSdCtrl_Config_t *FSdCtrl_LookupConfig(u32 instanceId); +/* This routine performs per device specific initialization of Phytium SDHC.*/ +ft_error_t FsdCtrl_Init(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +void FSdCtrl_ClkFreqSetup(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN u32 sdClk); +void FSdCtrl_ResetDma(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +/* reset sd ctrl during init */ +void FSdCtrl_Reset(FT_INOUT FtsdCtrl_t *pFtsdCtrl, pFtsdCtrl_delayTimer_t fDelayTimer); + +/* set irq call backs */ +ft_error_t FSdCtrl_SetHandler(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_IrqCallbackSelect_t selectIndex, + void *FuncPtr, + void *Args); + +/* register call-backs to determinate wheather write、read and cmd is complete */ +void FSdCtrl_WriteWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack); +void FSdCtrl_ReadWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack); +void FSdCtrl_CmdWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack); +void FSdCtrl_ErrWaitRegister(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN pFtsdCtrl_irqWaitCallback_t callBack); + +/* get irq status */ +u32 FSdCtrl_GetNormalIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +u32 FSdCtrl_GetDataIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl); +u32 FSdCtrl_GetErrorIrqStatus(FT_INOUT FtsdCtrl_t *pFtsdCtrl); + +/* enable selected normal irq */ +void FSdCtrl_NormalIrqSet(FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_NormalIrqSelect_t flgs); +void FSdCtrl_BdIrqSet(FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_BdIrqSelect flgs); + +#endif // \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_g.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_g.c new file mode 100644 index 0000000000..a8a388e227 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_g.c @@ -0,0 +1,31 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-12 10:03:52 + * @Description:  This files is for sd ctrl config definition + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_sdctrl.h" +#include "ft_parameters.h" + +/* configs of sd ctrl */ +FSdCtrl_Config_t FSdCtrl_Config[FT_SDC_NUM] = + { + { + .instanceId = FT_SDC_INSTANCE, + .baseAddress = FT_SDC_BASEADDR, + .inputClockHz = FT_SDC_FREQ, + .cardDetect = 1, + .writeProtect = 0, + .busWidth = 1, + .dmaIrqNum = 52, + .normalIrqNum = 53, + .errIrqNum = 54, + .workMode = 0 /*FTSDCTRL_CMD_IRQ_MASK | FTSDCTRL_DATA_WRITE_IRQ_MASK | FTSDCTRL_DATA_READ_IRQ_MASK*/ + }}; diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.c new file mode 100644 index 0000000000..c06dc28e22 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.c @@ -0,0 +1,51 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-07 13:40:04 + * @Description:  This files is for sd ctrl register-related implementations + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_sdctrl_hw.h" +#include "ft_sdctrl.h" + +#include "ft_io.h" +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_generic_timer.h" + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + * @param {pFtsdCtrl_delayTimer_t} fDelayTimer + */ +void FSdCtrl_Reset(FT_INOUT FtsdCtrl_t *pFtsdCtrl, pFtsdCtrl_delayTimer_t fDelayTimer) +{ + FSdCtrl_Config_t *pConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + + pConfig = &pFtsdCtrl->config; + /* trigger software reset for 1us */ + Ft_setBit32(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET, SOFTWARE_RESET_SRST); + /* Wait for reset is ok */ + Ft_GenericTimer_UsDelay(1); + Ft_clearBit32(pConfig->baseAddress + SOFTWARE_RESET_REG_OFFSET, SOFTWARE_RESET_SRST); + + /* wait dat[0] to be high-lev */ + while ((!(Ft_in32(pConfig->baseAddress + STATUS_REG) & STATUS_REG_DLSL(1)))) + { + fDelayTimer(1); + } + + return; +} diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.h b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.h new file mode 100644 index 0000000000..f5e9014e52 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_hw.h @@ -0,0 +1,210 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-19 16:17:39 + * @Description:  This files is for sd ctrl register + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _SDCTRL_HW_H +#define _SDCTRL_HW_H + +#include "ft_sdctrl.h" + +/* Register Offset */ + +#define CONTROLL_SETTING_REG_OFFSET 0x00 /* Controller configuration register */ +#define ARGUMENT_REG_OFFSET 0x04 /* Parameter register */ +#define CMD_SETTING_REG_OFFSET 0x08 /* Command register */ +#define CLOCK_DIV_REG_OFFSET 0x0C /* Clock division register */ +#define SOFTWARE_RESET_REG_OFFSET 0x10 /* Reset control register */ +#define POWER_CONTROLL_REG_OFFSET 0x14 /* Power control register */ +#define TIMEOUT_CMD_REG_OFFSET 0x18 /* cmd Timeout setting register */ +#define TIMEOUT_DATA_REG_OFFSET 0x1C /* Data timeout setting register */ +#define NORMAL_INT_EN_REG_OFFSET 0x20 /* Interrupt Enable Register */ +#define ERROR_INT_EN_REG_OFFSET 0x24 /* error Interrupt Enable Register */ +#define BD_ISR_EN_REG_OFFSET 0x28 /* Data Transfer Interrupt Enable Register */ +#define CAPABILIES_REG_OFFSET 0x2c /* capabilies register */ +#define SD_DRV_REG_OFFSET 0x30 /* SD card driver phase register */ +#define SD_SAMP_REG_OFFSET 0x34 /* SD card sampling phase register */ +#define SD_SEN_REG_OFFSET 0x38 /* Card detection controller */ +#define HDS_AXI_REG_CONF1_OFFSET 0x3c /* AXI boundary configuration register 1 */ +#define DAT_IN_M_RX_BD 0x40 /* SD BD RX address register */ +#define DAT_IN_M_TX_BD 0x60 /* SD BD TX address register */ +#define BLK_CNT_REG 0x80 /* Block reads and writes configuration registers */ +#define HDS_AXI_REG_CONF2 0xa8 /* AXI boundary configuration register 2 */ +#define NORMAL_INT_STATUS_REG 0xc0 /* Interrupt status register */ +#define ERROR_INT_STATUS_REG 0xc4 /* ERROR interrupt register */ +#define BD_ISR_REG 0xc8 /* Data Transfer Interrupt Status Register */ +#define BD_STATUS 0xcc /* BD descriptor register */ +#define STATUS_REG 0xd0 /* state register */ +#define BLOCK 0xd4 /* Block length register */ +#define CMD_RESP_1 0xe0 /* Command response register 1 */ +#define CMD_RESP_2 0xe4 /* Command response register 2 */ +#define CMD_RESP_3 0xe8 /* Command response register 3 */ +#define CMD_RESP_4 0xec /* Command response register 4 */ + +/* Controller configuration register */ +#define CONTROLL_SETTING_PERMDR(x) ((x & 3) << 8) /* Read operation corresponding to the size of the end selection: 00: small end alignment,01: large end alignment,10: SD protocol mode */ +#define CONTROLL_SETTING_PERMDW(x) ((x & 3) << 10) /* Write operation corresponding to the size of the endian selection: 00: small endian alignment 01: large endian alignment 10: SD protocol mode*/ + +/* Parameter register */ +#define ARGUMENT_REG(x) (x & 0xffffffff) + +/* Command register */ +#define CMD_SETTING_RTS(x) ((x & 3) << 0) /* 0: No response 01: Response byte length 136 10: Response byte length 48 11: Response byte length 48 */ +#define CMD_SETTING_CRCE (1U << 3) /* 0: CRC check is not performed on CMD response 1: CRC check is performed on CMD response */ +#define CMD_SETTING_CICE (1U << 4) /* 0:CMD 响应时,不执行索引检查 1:CMD 响应时,执行索引检查 */ +#define CMD_SETTING_CMDW(x) ((x & 3) << 6) +#define CMD_SETTING_CMDI(x) ((x & 0x3f) << 8) /* 命令索引 */ +#define CMD_SETTING_TRTY(x) ((x & 3) << 14) /* 10: adtc 指令 ,其它: 读写操作 */ + +/* 时钟分频寄存器 */ +#define CLOCK_DIV_RE(x) (x & 0xffffffff) /* CLKD-时钟分频系数 SD_frequency= 600M/ (2*(clock_d+1)) */ + +/* 复位控制寄存器 */ +#define SOFTWARE_RESET_SRST (1U) /* 控制器软复位 */ +#define SOFTWARE_RESET_BDRST (4U) /* DMA BD 清 0 */ +#define SOFTWARE_RESET_CFCLF (8U) /* 卡插入拔出状态触发标志清 0 */ + +/* cmd 超时设置寄存器 */ +#define TIMEOUT_CMD_REG(x) (x & 0xffffffff) /* command 超时参数 */ + +/* 数据超时设置寄存器 */ +#define TIMEOUT_DATA_REG(x) (x & 0xffffffff) /* data 超时参数 */ + +/* 中断使能寄存器 */ +#define NORMAL_INT_EN_ECC 1U /* 命令完成中断使能 */ +#define NORMAL_INT_EN_ECCRCE 2U /* 卡拔出中断使能 */ +#define NORMAL_INT_EN_ECIE (1U << 15) /* 错误中断使能 */ + +/* error 中断使能寄存器 */ +#define ERROR_INT_EN_CNR (1U << 4) /* Command response error interrupted */ +#define ERROR_INT_EN_CIR (1U << 3) /* 命令索引错误中断使能 */ +#define ERROR_INT_EN_CCRCE (1U << 1) /* 命令 CRC 错误中断使能 */ +#define ERROR_INT_EN_CTE (1U << 0) /* 命令超时中断使能 */ + +/* 数据传输中断使能寄存器 */ +#define BD_ISR_EN_ETRS (1U << 0) /* DMA 传输完成中断使能 */ +#define BD_ISR_EN_EDTE (1U << 3) /* 数据超时中断使能 */ +#define BD_ISR_EN_ECMDE (1U << 4) /* 命令响应错误中断使能 */ +#define BD_ISR_EN_ETRE (1U << 5) /* 传输错误中断使能 */ +#define BD_ISR_EN_ENRCRCE (1U << 6) /* CRC 校验错误中断使能 */ +#define BD_ISR_EN_EDATFRAXE (1U << 7) /* AXI 总线错误中断使能 */ +#define BD_ISR_EN_RESPE (1U << 8) /* 读 SD 卡操作,AXI BR 通道完成中断 */ +#define BD_ISR_EN_EDAISE (1U << 15) /* DMA 错误中断使能 */ +#define BD_ISR_ALL_MASK (BD_ISR_EN_ETRS | BD_ISR_EN_EDTE | \ + BD_ISR_EN_ECMDE | BD_ISR_EN_ETRE | \ + BD_ISR_EN_ENRCRCE | BD_ISR_EN_EDATFRAXE | \ + BD_ISR_EN_RESPE | BD_ISR_EN_EDAISE) + +/* 状态寄存器 */ +#define CAPABILIES_REG(x) (x & 0xffffffff) + +/* SD 卡驱动相位寄存器 */ +#define SD_DRV_REG(x) (x & 0xffffffff) /* 卡驱动相位配置参数 */ + +/* SD 卡采样相位寄存器 */ +#define SD_SAMP_REG(x) (x & 0xffffffff) /* 卡采样相位配置参数 */ +#define SD_SAMP_DEFAULT 11 /* when SD card work in high-speed mode, \ + the best sampling pharse is needed to \ + get the correct data */ +#define SD_FRRQ_DIV_DEFAULT 5 /* default freq div */ +#define SD_CLK_FREQ_400KHZ 400000 +#define SD_CLK_FREQ_25MHZ 25000000 + +/* 卡检测控制器 */ +#define SD_SEN_REG_CREFR (1U << 1) /* 卡拔出时自动释放 AXI 总线选择 */ +#define SD_SEN_REG_CRES (1U << 2) /* CARD 在位状态标志选择 0: 卡在位-0,不在位-1 1: 卡在位-1,不在位-0 */ +#define SD_SEN_REG_DEBNCE(x) ((x & 0xffffff) << 8) /* 去抖时钟分频参数 */ + +/* AXI 边界配置寄存器 1 */ +#define HDS_AXI_REG_CONF1_AWDOMAIN_HDS_M(x) ((x & 0x3) << 0) +#define HDS_AXI_REG_CONF1_AWBAR_HDS_M(x) ((x & 0x3) << 2) +#define HDS_AXI_REG_CONF1_ARSNOOP_HDS_M(x) ((x & 0xf) << 4) +#define HDS_AXI_REG_CONF1_ARREGION_HDS_M(x) ((x & 0xf) << 8) +#define HDS_AXI_REG_CONF1_ARDOMAIN_HDS_M(x) ((x & 0x3) << 12) +#define HDS_AXI_REG_CONF1_ARBAR_HDS_M(x) ((x & 0x3) << 14) +#define HDS_AXI_REG_CONF1_AWSNOOP_HDS_M(x) ((x & 0x7) << 16) +#define HDS_AXI_REG_CONF1_AWREGION_HDS_M(x) ((x & 0xF) << 19) + +/* SD BD RX 地址寄存器 */ +#define DAT_IN_M_RX_BD_MASK(x) (x & 0xffffffff) /* dma 读卡地址配置:4 个 cycle ,系统低 4B-系统高 4B-SD 低 4B- SD 高 4B */ + +/* SD BD TX 地址寄存器 */ +#define DAT_IN_M_TX_BD_MASK(x) (x & 0xffffffff) /* dma 写卡地址配置:4 个 cycle ,系统低 4B-系统高 4B-SD 低 4B- SD 高 4B */ + +/* 块读写配置寄存器 */ +#define BLK_CNT(x) (x & 0xffffffff) /* dma block num setting */ + +/* AXI 边界配置寄存器 2 */ +#define HDS_AXI_REG_CONF2_D_ARPROT(x) ((x & 0x7) << 27) +#define HDS_AXI_REG_CONF2_SD_AWPROT(x) ((x & 0x7) << 24) +#define HDS_AXI_REG_CONF2_SD_ARCACHE_M(x) ((x & 0xf) << 20) +#define HDS_AXI_REG_CONF2_SD_AWCACHE_M(x) ((x & 0xf) << 16) +#define HDS_AXI_REG_CONF2_RESERVED(x) ((x & 0x3) << 14) +#define HDS_AXI_REG_CONF2_HDA_ARPRO(x) ((x & 0x7) << 11) +#define HDS_AXI_REG_CONF2_HDA_AWPROT(x) ((x & 0x7) << 8) +#define HDS_AXI_REG_CONF2_HDA_ARCACHE_M(x) ((x & 0xf) << 4) +#define HDS_AXI_REG_CONF2_HDA_AWCACHE_M(x) ((x & 0xf) << 0) + +/* 中断状态寄存器 */ +#define NORMAL_INT_STATUS_EI (1U << 15) /* 命令错误中断 */ +#define NORMAL_INT_STATUS_CR (1U << 1) /* 卡移除中断 */ +#define NORMAL_INT_STATUS_CC 1U /* 命令完成中断 */ +#define NORMAL_INT_STATUS_ALL_MASK (NORMAL_INT_STATUS_EI | NORMAL_INT_STATUS_CR | NORMAL_INT_STATUS_CC) + +/* error 中断寄存器 */ +#define ERROR_INT_STATUS_CNR (1U << 4) /* 命令响应错误中断 */ +#define ERROR_INT_STATUS_CIR (1U << 3) /* 命令索引错误中断 */ +#define ERROR_INT_STATUS_CCRCE (1U << 1) /* 命令 CRC 错误中断 */ +#define ERROR_INT_STATUS_CTE 1U /* 命令超时错误中断 */ +#define ERROR_INT_STATUS_ALL_MASK (ERROR_INT_STATUS_CNR | ERROR_INT_STATUS_CIR | ERROR_INT_STATUS_CCRCE | ERROR_INT_STATUS_CTE) + +/* 数据传输中断状态寄存器 */ +#define BD_ISR_REG_DAIS (1U << 15) /* DMA 错误中断*/ +#define BD_ISR_REG_RESPE (1U << 8) /* 读 SD 卡操作,AXI BR 通道完成中断*/ +#define BD_ISR_REG_DATFRAX (1U << 7) /* axi 总线强制释放中断*/ +#define BD_ISR_REG_NRCRC (1U << 6) /* 无 CRC 响应中断*/ +#define BD_ISR_REG_TRE (1U << 5) /* CRC 响应错误中断*/ +#define BD_ISR_REG_CMDE (1U << 4) /* 命令响应错误中断*/ +#define BD_ISR_REG_DTE (1U << 3) /* 超时中断*/ +#define BD_ISR_REG_TRS (1U << 0) /* DMA 传输完成中断*/ + +/* bd 描述符寄存器 */ +#define BD_STATUS_REG(x) (x & 0xffffffff) /* bd 描述符寄存器 */ + +/* 状态寄存器 */ +#define STATUS_REG_DATMAST(x) ((x & 0xf) << 27) /* data_master 状态机 */ +#define STATUS_REG_CDIF (1U << 26) /* 卡在位标志 */ +#define STATUS_REG_CDRF (1U << 25) /* 卡不在位标志 */ +#define STATUS_REG_CLSL (1U << 24) /* 命令闲信号 */ +#define STATUS_REG_DLSL(x) ((x & 0xf) << 20) /* 线信号 */ +#define STATUS_REG_CDSL (1U << 19) /* 卡检测管脚信号 */ +#define STATUS_REG_CST(x) ((x & 0xf) << 12) /* cmd_host state 状态机 */ +#define STATUS_REG_CSM(x) ((x & 0X1F) << 7) +#define STATUS_REG_DAT_AVA (1 << 6) /* DAT_AVA 当前命令状态流程运转完 */ +#define STATUS_REG_CRC_VALID (1 << 5) +#define STATUS_REG_CICMD 1U /* RO 0x0 CMD 总线状态 */ + +/* 块长度寄存器 */ +#define BLOCK_RGE(x) (x & 0xffffffff) /* 块长度寄存器 */ + +/* 命令响应寄存器 1 */ +#define CMD_RESP_1_REG(x) (x & 0xffffffff) + +/* 命令响应寄存器 2 */ +#define CMD_RESP_2_REG(x) (x & 0xffffffff) + +/* 命令响应寄存器 3 */ +#define CMD_RESP_3_REG(x) (x & 0xffffffff) + +/* 命令响应寄存器 4 */ +#define CMD_RESP_4_REG(x) (x & 0xffffffff) + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_intr.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_intr.c new file mode 100644 index 0000000000..09a35d3d8f --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_intr.c @@ -0,0 +1,143 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-07 13:40:20 + * @Description:  This files is for sd ctrl irq handling + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_sdctrl.h" +#include "ft_sdctrl_hw.h" +#include "ft_assert.h" +#include "ft_types.h" +#include "ft_io.h" +#include "ft_printf.h" +#include "ft_debug.h" + +#ifndef LOG_LOCAL_LEVEL +#define LOG_LOCAL_LEVEL FT_LOG_NONE +#endif +#define FT_SD_CTRL_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SD_CTRL_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SD_CTRL_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_SD_CTRL_DEBUG_TAG, format, ##__VA_ARGS__) + +/** + * Description: normal irq for sd ctrl + * Date: 2021-03-31 10:46:55 + * Param: + * return {*} + * param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +void FSdCtrl_NormalIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + FSdCtrl_IrqConfig_t *irqConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + irqConfig = &pFtsdCtrl->irqConfig; + + //FT_SD_CTRL_DEBUG_I("enter cmd irq procedure\r\n"); + if (irqConfig->pCmdCallback) + { + irqConfig->pCmdCallback(irqConfig->pCmdArgs); + } + + /* clear interrupts */ + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, NORMAL_INT_STATUS_ALL_MASK); + Ft_out32(pConfig->baseAddress + NORMAL_INT_STATUS_REG, 0); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +void FSdCtrl_DmaIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + FSdCtrl_IrqConfig_t *irqConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + irqConfig = &pFtsdCtrl->irqConfig; + + if (irqConfig->pDmaDataCallback) + { + irqConfig->pDmaDataCallback(irqConfig->pDmaDataArgs); + } + + /* clear interrupts */ + Ft_out32(pConfig->baseAddress + BD_ISR_REG, BD_ISR_ALL_MASK); + Ft_out32(pConfig->baseAddress + BD_ISR_REG, 0); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FT_INOUT FtsdCtrl_t} *pFtsdCtrl + */ +void FSdCtrl_ErrIrq(FT_INOUT FtsdCtrl_t *pFtsdCtrl) +{ + FSdCtrl_Config_t *pConfig; + FSdCtrl_IrqConfig_t *irqConfig; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + irqConfig = &pFtsdCtrl->irqConfig; + + if (irqConfig->pErrorCallback) + { + irqConfig->pErrorCallback(irqConfig->pErrorArgs); + } + + /* clear interrupts */ + Ft_out32(pConfig->baseAddress + ERROR_INT_STATUS_REG, ERROR_INT_STATUS_ALL_MASK); + Ft_out32(pConfig->baseAddress + ERROR_INT_STATUS_REG, 0); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +ft_error_t FSdCtrl_SetHandler(FT_INOUT FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_IrqCallbackSelect_t selectIndex, + void *FuncPtr, void *Args) +{ + Ft_assertNonvoid(pFtsdCtrl != NULL); + Ft_assertNonvoid(FuncPtr != NULL); + Ft_assertNonvoid(pFtsdCtrl->isReady == FT_COMPONENT_IS_READLY); + + switch (selectIndex) + { + case FTSDCTRL_DMADATAIRQID: + pFtsdCtrl->irqConfig.pDmaDataCallback = FuncPtr; + pFtsdCtrl->irqConfig.pDmaDataArgs = Args; + break; + case FTSDCTRL_CMDIRQID: + pFtsdCtrl->irqConfig.pCmdCallback = FuncPtr; + pFtsdCtrl->irqConfig.pCmdArgs = Args; + break; + case FTSDCTRL_ERRORIRQID: + pFtsdCtrl->irqConfig.pErrorCallback = FuncPtr; + pFtsdCtrl->irqConfig.pErrorArgs = Args; + break; + default: + return FTSDC_FAILURE; + } + + return FTSDC_SUCCESS; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_option.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_option.c new file mode 100644 index 0000000000..fdb520753a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_option.c @@ -0,0 +1,91 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-07 13:40:38 + * @Description:  This files is for sd ctrl option setting + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_sdctrl.h" +#include "ft_sdctrl_hw.h" +#include "ft_types.h" +#include "ft_io.h" +#include "ft_printf.h" +#include "ft_assert.h" + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN FSdCtrl_NormalIrqSelect_t} flgs + */ +void FSdCtrl_NormalIrqSet(FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_NormalIrqSelect_t flgs) +{ + FSdCtrl_Config_t *pConfig; + u32 regValue; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + /* */ + regValue = ((flgs & NORMAL_IRQ_CC) ? NORMAL_INT_EN_ECC : 0) | ((flgs & NORMAL_IRQ_CR) ? NORMAL_INT_EN_ECCRCE : 0) | + ((flgs & NORMAL_IRQ_EI) ? NORMAL_INT_EN_ECIE : 0); + + Ft_out32(pConfig->baseAddress + NORMAL_INT_EN_REG_OFFSET, regValue); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN FSdCtrl_ErrorIrqSelect_t} flgs + */ +void FsdCtrl_errorIrqSet(FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_ErrorIrqSelect_t flgs) +{ + FSdCtrl_Config_t *pConfig; + u32 regValue; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + regValue = ((flgs & ERROR_IRQ_CTE) ? ERROR_INT_EN_CTE : 0) | ((flgs & ERROR_IRQ_CCRCE) ? ERROR_INT_EN_CCRCE : 0) | + ((flgs & ERROR_IRQ_CIR) ? ERROR_INT_EN_CIR : 0) | ((flgs & ERROR_IRQ_CNR) ? ERROR_INT_EN_CNR : 0); + + Ft_out32(pConfig->baseAddress + ERROR_INT_EN_REG_OFFSET, regValue); +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {FtsdCtrl_t} *pFtsdCtrl + * @param {FT_IN FSdCtrl_BdIrqSelect} flgs + */ +void FSdCtrl_BdIrqSet(FtsdCtrl_t *pFtsdCtrl, FT_IN FSdCtrl_BdIrqSelect flgs) +{ + FSdCtrl_Config_t *pConfig; + u32 regValue; + Ft_assertVoid(FT_NULL != pFtsdCtrl); + pConfig = &pFtsdCtrl->config; + + regValue = ((flgs & BD_IRQ_TRS) ? BD_ISR_EN_ETRS : 0) | ((flgs & BD_IRQ_DTE) ? BD_ISR_EN_EDTE : 0) | + ((flgs & BD_IRQ_CMDE) ? BD_ISR_EN_ECMDE : 0) | ((flgs & BD_IRQ_TRE) ? BD_ISR_EN_ETRE : 0) | + ((flgs & BD_IRQ_NRCRC) ? BD_ISR_EN_ENRCRCE : 0) | ((flgs & BD_IRQ_DATFRAX) ? BD_ISR_EN_EDATFRAXE : 0) | + ((flgs & BD_IRQ_RESPE) ? BD_ISR_EN_RESPE : 0) | ((flgs & BD_IRQ_DAIS) ? BD_ISR_EN_EDAISE : 0); + + Ft_out32(pConfig->baseAddress + BD_ISR_EN_REG_OFFSET, regValue); +} diff --git a/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_sinit.c b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_sinit.c new file mode 100644 index 0000000000..ad31af76ff --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_sd/ft_sdctrl_sinit.c @@ -0,0 +1,42 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-19 17:05:14 + * @Description:  This files is for sd ctrl static initialization + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_sdctrl.h" +#include "ft_parameters.h" + +extern FSdCtrl_Config_t FSdCtrl_Config[FT_SDC_NUM]; + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {u32} instanceId + */ +FSdCtrl_Config_t *FSdCtrl_LookupConfig(u32 instanceId) +{ + FSdCtrl_Config_t *CfgPtr = NULL; + u32 Index; + for (Index = 0; Index < (u32)FT_GMAC_INSTANCES_NUM; Index++) + { + if (FSdCtrl_Config[Index].instanceId == instanceId) + { + CfgPtr = &FSdCtrl_Config[Index]; + break; + } + } + + return (FSdCtrl_Config_t *)CfgPtr; +} diff --git a/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.c b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.c new file mode 100644 index 0000000000..bd0e3349ed --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.c @@ -0,0 +1,192 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-25 14:00:25 + * @LastEditTime: 2021-04-30 16:11:38 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_assert.h" +#include "ft_spi.h" +#include "ft_spi_hw.h" +#include "ft_generic_timer.h" +#include "ft_gpio.h" + +void FSpi_DumpAllStatus(FT_IN FSpi_Ctrl_t *pCtrl, FT_IN char *tag) +{ + FT_SPI_DEBUG_I("***%s status******\r\n", tag); + FT_SPI_DEBUG_I("busy: %d", SPI_STATUS_REG(pCtrl)->val.Busy); + FT_SPI_DEBUG_I("tx fifo not empty: %d", SPI_STATUS_REG(pCtrl)->val.Tfnf); + FT_SPI_DEBUG_I("tx fifo empty: %d", SPI_STATUS_REG(pCtrl)->val.Tfe); + FT_SPI_DEBUG_I("rx fifo not empty: %d", SPI_STATUS_REG(pCtrl)->val.Rfne); + FT_SPI_DEBUG_I("rx fifo full: %d", SPI_STATUS_REG(pCtrl)->val.Rff); + FT_SPI_DEBUG_I("trans error: %d", SPI_STATUS_REG(pCtrl)->val.Txe); + FT_SPI_DEBUG_I("trans conflict error: %d", SPI_STATUS_REG(pCtrl)->val.Dcol); +} + +u32 FSpi_ReadWriteByte(FT_INOUT FSpi_Ctrl_t *pCtrl, FT_IN u8 TxData, + FT_OUT u8 *pRxData) +{ + u32 Retry = 0; + u32 Ret = ERR_SPI_OK; + u16 RxData; + + if (!pCtrl->IsReady) + { + return ERR_SPI_NOT_READY; + } + + while (FSPI_TX_FIFO_NOT_EMPTY(pCtrl)) + { + //Ft_GenericTimer_UsDelay(2); + if ((Retry++) > SPI_TIMEOUT) + { + Ret = ERR_SPI_TX_TIMEOUT; + goto __EXIT; + } + } + FSPI_WRITE_DATA(pCtrl, (u16)TxData); + + Retry = 0; + + while (FSPI_RX_FIFO_EMPTY(pCtrl)) + { + //Ft_GenericTimer_UsDelay(2); + if ((Retry++) > SPI_TIMEOUT) + { + Ret = ERR_SPI_RX_TIMEOUT; + goto __EXIT; + } + } + RxData = FSPI_READ_DATA(pCtrl); + + if (pRxData) + { + *pRxData = (u8)RxData; + } + +__EXIT: + return Ret; +} + +u32 FSpi_Init(FT_INOUT FSpi_Ctrl_t *pCtrl) +{ + u32 Ret = ERR_SPI_OK; + + FSPI_DISABLE(pCtrl); + + /* config spi ctrl register */ + SPI_CTRL0_REG(pCtrl)->val.Dfs = SPI_DFS_DEFAULT; + SPI_CTRL0_REG(pCtrl)->val.Frf = SPI_FRF_DEFAULT; + + if (SPI_CTRL_CPHA_1EDGE == pCtrl->Config.Cpha) + { + SPI_CTRL0_REG(pCtrl)->val.Scph = SPI_SCPH_SW_CLK_AT_DATA_BEG; + } + else if (SPI_CTRL_CPHA_2EDGE == pCtrl->Config.Cpha) + { + SPI_CTRL0_REG(pCtrl)->val.Scph = SPI_SCPH_SW_CLK_AT_DATA_MID; + } + else + { + Ft_assertNoneReturn(0); + } + + if (SPI_CTRL_CPOL_LOW == pCtrl->Config.Cpol) + { + SPI_CTRL0_REG(pCtrl)->val.Scpol = SPI_SCPOL_NOT_ACT_LOW; + } + else if (SPI_CTRL_CPOL_HIGH == pCtrl->Config.Cpol) + { + SPI_CTRL0_REG(pCtrl)->val.Scpol = SPI_SCPOL_NOT_ACT_HIGH; + } + else + { + Ft_assertNoneReturn(0); + } + + SPI_CTRL0_REG(pCtrl)->val.Tmod = SPI_TMOD_TX_RX_MODE; + SPI_CTRL0_REG(pCtrl)->val.SlvOE = SPI_SLV_OE_DISABLE; + SPI_CTRL0_REG(pCtrl)->val.Srl = SPI_SRL_NORMAL_MODE; + SPI_CTRL0_REG(pCtrl)->val.Cfs = SPI_CFS_DEFAULT; + + /* config spi clock */ + FSPI_SET_BAUDR(pCtrl, pCtrl->Config.BaudRDiv); + + /* config rx and tx fifo, fifo depth to trigger intr */ + SPI_TXFTL_REG(pCtrl)->val.Tft = 0; + SPI_RXFTL_REG(pCtrl)->val.Rft = 0; + SPI_TXFL_REG(pCtrl)->val.Txtfl = 0; + SPI_RXFL_REG(pCtrl)->val.Rxtfl = 0; + + SPI_RXSAMPLE_DLY_REG(pCtrl)->val.Rsd = SPI_DEFAULT_RSD; + + FSPI_ENABLE(pCtrl); + + /* set spi ready flag */ + if (ERR_SPI_OK == Ret) + { + pCtrl->IsReady = TRUE; + } + + return Ret; +} + +static void FSpi_ToggleCSPin(FT_INOUT FSpi_Ctrl_t *pCtrl, FT_IN FSpi_DevId_t DevId, + FT_IN bool_t select) +{ + u32 setVal = ((TRUE == select) ? GPIO_OFF : GPIO_ON); + + Ft_assertNoneReturn(NULL != pCtrl); + + if (FGpio_ReadPinA(GPIO_CTRL_ID_1, pCtrl->CsPin) != setVal) + { + FGpio_WritePinA(GPIO_CTRL_ID_1, pCtrl->CsPin, setVal); + } + + Ft_GenericTimer_UsDelay(10); + return; +} + +void FSpi_SelectSlave(FT_INOUT FSpi_Ctrl_t *pCtrl, FT_IN FSpi_DevId_t DevId, + FT_IN bool_t select) +{ + FSpi_SeReg_t *pSelReg; + u32 setVal = ((TRUE == select) ? SPI_SE_SELECTED : SPI_SE_UNSELECTED); + + FSPI_DISABLE(pCtrl); + /* enable or disable specific spi slave device */ + pSelReg = SPI_SE_REG(pCtrl); + switch(DevId) + { + case SPI_DEV_ID_0: + pSelReg->val.SelSlave_0 = setVal; + break; + case SPI_DEV_ID_1: + pSelReg->val.SelSlave_1 = setVal; + Ft_assertNoneReturn(0); + break; + case SPI_DEV_ID_2: + pSelReg->val.SelSlave_2 = setVal; + Ft_assertNoneReturn(0); + break; + case SPI_DEV_ID_3: + pSelReg->val.SelSlave_3 = setVal; + Ft_assertNoneReturn(0); + break; + default: + Ft_assertNoneReturn(0); + break; + } + + FSpi_ToggleCSPin(pCtrl, DevId, select); + FSPI_ENABLE(pCtrl); + + return; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.h b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.h new file mode 100644 index 0000000000..3c5190f562 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi.h @@ -0,0 +1,95 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-25 13:59:05 + * @LastEditTime: 2021-04-30 16:11:46 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_BSP_SPI_H +#define FT_BSP_SPI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_spi_hw.h" +#include "ft_error_code.h" +#include "ft_debug.h" + +typedef struct +{ + u8 WorkMode; +#define SPI_CTRL_MASTER_MODE (u8)0x0 +#define SPI_CTRL_SLAVE_MODE (u8)0x1 + u8 DevAddrLen; +#define SPI_3_BYTE_ADDR (u8)0x3 +#define SPI_4_BYTE_ADDR (u8)0x4 + u8 Cpol; +#define SPI_CTRL_CPOL_LOW (u8)0x0 +#define SPI_CTRL_CPOL_HIGH (u8)0x1 + u8 Cpha; +#define SPI_CTRL_CPHA_1EDGE (u8)0x0 +#define SPI_CTRL_CPHA_2EDGE (u8)0x1 + u8 DevAddr[4]; + u32 BaudRDiv; +} FSpi_Conf_t; + +typedef struct +{ + FSpi_Conf_t Config; + FSpi_CtrlId_t CtrlId; + FSpi_DevId_t DevId; /* support only one slave at the moment */ + bool_t IsReady; + u16 CsPin; /* cs pin in gpio group A */ +}FSpi_Ctrl_t; + +/* misc marco */ +#define SPI_TIMEOUT 5000 +#define SPI_DUMMY_TX_DATA 0xFF + +/* ctrl member shortcut */ +#define FSPI_DEV_ADDR_LEN(pCtrl) (pCtrl->Config.DevAddrLen) +#define FSPI_IS_3_BYTE_ADDR(pCtrl) (SPI_3_BYTE_ADDR == FSPI_DEV_ADDR_LEN(pCtrl)) +#define FSPI_DEV_ADDR(pCtrl) (pCtrl->Config.DevAddr) + +/* define error code */ +#define ERR_SPI_OK ERR_SUCCESS +#define ERR_SPI_GENERAL FT_CODE_ERR(ERR_MODE_SPI, 0, 1) +#define ERR_SPI_NOT_READY FT_CODE_ERR(ERR_MODE_SPI, 0, 2) +#define ERR_SPI_TX_TIMEOUT FT_CODE_ERR(ERR_MODE_SPI, 0, 3) +#define ERR_SPI_RX_TIMEOUT FT_CODE_ERR(ERR_MODE_SPI, 0, 4) + +/* spi flash error code */ +#define ERR_SPI_WAIT_TIMEOUT FT_CODE_ERR(ERR_MODE_SPI, 1, 1) + +/* define debug utilities */ +#define FT_SPI_DEBUG_TAG "FT_SPI" +#define FT_SPI_ENABLE_DEBUG +#define FT_SPI_ERROR(format, ...) FT_DEBUG_PRINT_E(FT_SPI_DEBUG_TAG, format, ##__VA_ARGS__) +#ifdef FT_SPI_ENABLE_DEBUG +#define FT_SPI_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_SPI_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SPI_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_SPI_DEBUG_TAG, format, ##__VA_ARGS__) +#else +#define FT_SPI_DEBUG_I(format, ...) +#define FT_SPI_DEBUG_W(format, ...) +#endif + +u32 FSpi_Init(FT_INOUT FSpi_Ctrl_t *pCtrl); +u32 FSpi_ReadWriteByte(FT_INOUT FSpi_Ctrl_t *pCtrl, FT_IN u8 TxData, + FT_OUT u8 *pRxData); +void FSpi_SelectSlave(FT_INOUT FSpi_Ctrl_t *pCtrl, FT_IN FSpi_DevId_t DevId, + FT_IN bool_t select); +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_hw.h b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_hw.h new file mode 100644 index 0000000000..53c502f87e --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_hw.h @@ -0,0 +1,331 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-25 13:59:44 + * @LastEditTime: 2021-04-30 15:42:30 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#ifndef FT_BSP_SPI_HW_H +#define FT_BSP_SPI_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" + +/* offset map of SPI register */ +#define SPI_CTRL_R0 0x00 //Ctrl register 0 +#define SPI_CTRL_R1 0x04 //Ctrl register 1 +#define SPI_SSI_EN_R 0x08 //SPI enable register +#define SPI_MW_CR 0x0c //Microwire ctrl register +#define SPI_SE_R 0x10 //Slave enable register +#define SPI_BAUD_R 0x14 //Baudrate set register +#define SPI_TXFTL_R 0x18 //Tx threshold register +#define SPI_RXFTL_R 0x1c //Rx threshold register +#define SPI_TXFL_R 0x20 //Tx level register +#define SPI_RXFL_R 0x24 //Rx level register +#define SPI_S_R 0x28 //Status register +#define SPI_IM_R 0x2c //Intr mask register +#define SPI_RIS_R 0x34 //Intr status register +#define SPI_TXOI_CR 0x38 //TX FIFO overflow intr clear register +#define SPI_RXOI_CR 0x3c //RX FIFO overflow intr clear register +#define SPI_RXUI_CR 0x40 //TX FIFO underflow intr clear register +#define SPI_MSTI_CR 0x44 //Multi slave intr clear register +#define SPI_IC_R 0x48 //Intr clear register +#define SPI_DMA_CR 0x4c //DMA ctrl register +#define SPI_DMA_TDL_R 0x50 //DMA TX Data level register +#define SPI_DMA_RDL_R 0x54 //DMA RX Data level register +#define SPI_ID_R 0x58 //Identification register +#define SPI_D_R 0xec //Data register +#define SPI_RX_SAMPLE_DLY 0xfc //RX Data delay register + +typedef enum +{ + SPI_CTRL_ID_0 = 0, + SPI_CTRL_ID_1, + + NUM_OF_SPI_CTRL, +} FSpi_CtrlId_t; + +typedef enum +{ + SPI_DEV_ID_0 = 0, + SPI_DEV_ID_1, + SPI_DEV_ID_2, + SPI_DEV_ID_3, + + NUM_OF_SPI_DEV, +}FSpi_DevId_t; + +/* base address of SPI register */ +const static u32 g_SpiBaseAddr[NUM_OF_SPI_CTRL] = {0x2800c000, 0x28013000}; + +typedef union +{ + u32 data; + struct + { + u32 Dfs : 4; /* 3:0, select data length */ +#define SPI_DFS_DEFAULT 0x7 + u32 Frf : 2; /* 5:4, selcet trans mode */ +#define SPI_FRF_DEFAULT 0x0 + u32 Scph : 1; /* 6, serial clock phase */ +#define SPI_SCPH_SW_CLK_AT_DATA_MID 0x0 /* second edge */ +#define SPI_SCPH_SW_CLK_AT_DATA_BEG 0x1 /* first edge */ + u32 Scpol : 1; /* 7, serial clock Polarity */ +#define SPI_SCPOL_NOT_ACT_LOW 0x0 +#define SPI_SCPOL_NOT_ACT_HIGH 0x1 + u32 Tmod : 2; /* 9:8, ctrl trans mode, indicate if tx rx data is valid */ +#define SPI_TMOD_TX_RX_MODE 0x0 +#define SPI_TMOD_TX_MODE 0x1 +#define SPI_TMOD_RX_MODE 0x2 +#define SPI_TMOD_EEPROM_MODE 0x3 + u32 SlvOE : 1; /* 10, enable slave tx logic */ +#define SPI_SLV_OE_ENABLE 0x0 +#define SPI_SLV_OE_DISABLE 0x1 + u32 Srl : 1; /* 11, shift register loopback */ +#define SPI_SRL_NORMAL_MODE 0x0 +#define SPI_SRL_TEST_MODE 0x1 + u32 Cfs : 4; /* 15:12, ctrl data size, applied in Microwire mode */ +#define SPI_CFS_DEFAULT 0x0 + u32 Reserve : 16; + } val; +}FSpi_CtrlReg0_t; + +typedef union +{ + u32 data; + struct + { + u32 ndf : 16; /* 15:0 valid when TMOD = 10, TMOD = 11 */ +#define SPI_NDF_DEFAULT 16 + u32 Reserve : 16; + } val; +}FSpi_CtrlReg1_t; + +typedef struct +{ + u32 CPOL; + u32 CPHA; +} FSpi_ClockMode_t; + +static const FSpi_ClockMode_t g_FSpi_ClockMode[4] = +{ + {.CPOL = SPI_SCPOL_NOT_ACT_LOW, .CPHA = SPI_SCPH_SW_CLK_AT_DATA_BEG}, /* low level logic, sample at rising edge, shift at falling edge */ + {.CPOL = SPI_SCPOL_NOT_ACT_LOW, .CPHA = SPI_SCPH_SW_CLK_AT_DATA_MID}, /* low level logic, sample at falling edge, shift at rising edge */ + {.CPOL = SPI_SCPOL_NOT_ACT_HIGH, .CPHA = SPI_SCPH_SW_CLK_AT_DATA_MID}, /* high level logic, sample at falling edge, shift at rising edge */ + {.CPOL = SPI_SCPOL_NOT_ACT_HIGH, .CPHA = SPI_SCPH_SW_CLK_AT_DATA_BEG}, /* high level logic, sample at rising edge, shift at falling edge */ +}; + +typedef union +{ + u32 data; + struct + { + u32 SsiEn : 1; /* 0, enable or disable all SPI op */ + u32 Reserve : 31; + }val; +}FSpi_SsiEnReg_t; + + +typedef union +{ + u32 data; + struct + { + u32 MwMod : 1; /* 0 microwire trans mode */ +#define SPI_MWMODE_NO_CONTINUOUES 0 +#define SPI_MWMODE_CONTINUOUES 1 + u32 Mdd : 1; /* 1 microwire ctrl bit */ +#define SPI_MWMDD_RXFROM_EXT_DEV 0 +#define SPI_MWMDD_TXTO_EXT_DEV 1 + u32 Mhs : 1; /* 2 microwire handshake */ +#define SPI_MWMHS_DISABLE 0 +#define SPI_MWMHS_ENABLE 1 + u32 Reserve : 29; + }val; +}FSpi_MwcrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 SelSlave_0 : 1; /* 3:0, select specifc slave device */ + u32 SelSlave_1 : 1; + u32 SelSlave_2 : 1; + u32 SelSlave_3 : 1; +#define SPI_SE_SELECTED 0x1 +#define SPI_SE_UNSELECTED 0x0 + u32 Reserve : 28; + } val; +}FSpi_SeReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Sckdv : 16; /* 15:0, SSI clk divider, must be times of 2 */ +#define SPI_SCKDV_MIN (2) +#define SPI_SCKDV_4 (4) +#define SPI_SCKDV_8 (8) +#define SPI_SCKDV_16 (16) +#define SPI_SCKDV_32 (20) +#define SPI_SCKDV_64 (28) +#define SPI_SCKDV_128 (128) +#define SPI_SCKDV_256 (256) +#define SPI_SCKDV_1024 (1024) +#define SPI_SCKDV_4096 (4096) +#define SPI_SCKDV_12800 (12800) +#define SPI_SCKDV_56800 (56800) +#define SPI_SCKDV_MAX (65534) + u32 Reserve : 16; + }val; +}FSpi_BaudrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Tft : 8; /* 7:0, TX FIFO threshold */ + u32 Reserve : 24; + }val; + +}FSpi_TxFtlrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Rft : 8; /* 7:0, RX FIFO threshold */ + u32 Reserve : 24; + }val; + +}FSpi_RxFtlrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Txtfl : 8; /* 7:0, TX FIFO level, num of valid num */ + u32 Reserve : 24; + }val; + +}FSpi_TxFlrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Rxtfl : 8; /* 7:0, RX FIFO level, num of valid num */ + u32 Reserve : 24; + }val; + +}FSpi_RxFlrReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Busy : 1; /* 0, SPI bus busy bit */ + u32 Tfnf : 1; /* 1, tx FIFO not empty */ +#define SPI_TX_FIFO_FULL 0x0 +#define SPI_TX_FIFO_NOT_FULL 0x1 + u32 Tfe : 1; /* 2, tx FIFO empty */ +#define SPI_TX_FIFO_NOT_EMPTY 0x0 +#define SPI_TX_FIFO_EMPTY 0x1 + u32 Rfne : 1; /* 3, rx FIFO not emptu */ +#define SPI_RX_FIFO_EMPTY 0x0 +#define SPI_RX_FIFO_NOT_EMPTY 0x1 + u32 Rff : 1; /* 4, rx FIFO full */ +#define SPI_RX_FIFO_NOT_FULL 0x0 +#define SPI_RX_FIFO_FULL 0x1 + u32 Txe : 1; /* 5, trans error */ +#define SPI_TX_NO_ERR 0x0 +#define SPI_TX_ERR 0x1 + u32 Dcol : 1; /* 6, trans conflict error */ +#define SPI_TX_NO_COLERR 0x0 +#define SPI_TX_COLERR 0x1 + u32 Reserve : 25; + }val; +}FSpi_StatusReg_t; /* Read-Only */ + +typedef union +{ + u32 IdCode : 32; +}FSpi_IDReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Dr : 16; /* 15:0, RX and TX fifo */ +#define SPI_8BIT_MASK 0xFF +#define SPI_16BIT_MASK 0xFFFF + u32 Reserve : 16; + } val; +}FSpi_DataReg_t; + +typedef union +{ + u32 data; + struct + { + u32 Rsd : 8; /* 7:0, RX data delay */ +#define SPI_DEFAULT_RSD 0x6 + u32 Reserve : 24; + } val; +}FSpi_RxSampleDlyReg_t; + +#define SPI_CTL_ID(pCtrl) ((pCtrl)->CtrlId) +#define SPI_BASE_ADDR(pCtrl) (g_SpiBaseAddr[SPI_CTL_ID(pCtrl)]) + +/* select slave device */ +#define SPI_SE_REG(pCtrl) ((FSpi_SeReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_SE_R)) +/* set speed */ +#define SPI_BAUDR_REG(pCtrl) ((FSpi_BaudrReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_BAUD_R)) +#define FSPI_SET_BAUDR(pCtrl, div) (SPI_BAUDR_REG(pCtrl)->val.Sckdv = (div)) +#define FSPI_GET_BAUDR(pCtrl) (SPI_BAUDR_REG(pCtrl)->val.Sckdv) +/* check status */ +#define SPI_STATUS_REG(pCtrl) ((FSpi_StatusReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_S_R)) +#define FSPI_TX_FIFO_NOT_EMPTY(pCtrl) (SPI_TX_FIFO_NOT_EMPTY == (SPI_STATUS_REG(pCtrl)->val.Tfe)) +#define FSPI_RX_FIFO_EMPTY(pCtrl) (SPI_RX_FIFO_EMPTY == (SPI_STATUS_REG(pCtrl)->val.Rfne)) +/* enable/disable spi bus */ +#define SPI_SSIEN_REG(pCtrl) ((FSpi_SsiEnReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_SSI_EN_R)) +#define FSPI_ENABLE(pCtrl) (SPI_SSIEN_REG(pCtrl)->val.SsiEn = 1) +#define FSPI_DISABLE(pCtrl) (SPI_SSIEN_REG(pCtrl)->val.SsiEn = 0) +/* shortcut to access register */ +#define SPI_CTRL0_REG(pCtrl) ((FSpi_CtrlReg0_t *)(SPI_BASE_ADDR(pCtrl) + SPI_CTRL_R0)) +#define SPI_CTRL1_REG(pCtrl) ((FSpi_CtrlReg1_t *)(SPI_BASE_ADDR(pCtrl) + SPI_CTRL_R1)) +#define SPI_TXFTL_REG(pCtrl) ((FSpi_TxFtlrReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_TXFTL_R)) +#define SPI_RXFTL_REG(pCtrl) ((FSpi_RxFtlrReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_RXFTL_R)) +#define SPI_TXFL_REG(pCtrl) ((FSpi_TxFlrReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_TXFL_R)) +#define SPI_RXFL_REG(pCtrl) ((FSpi_RxFlrReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_RXFL_R)) +#define SPI_ID_REG(pCtrl) ((FSpi_IDReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_ID_R)) +#define FSPI_GET_ID(pCtrl) (SPI_ID_REG(pCtrl)->IdCode) +#define SPI_DATA_REG(pCtrl) ((FSpi_DataReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_D_R)) +#define FSPI_READ_DATA(pCtrl) (u16)(SPI_DATA_REG(pCtrl)->val.Dr) +#define FSPI_WRITE_DATA(pCtrl, dat) (SPI_DATA_REG(pCtrl)->val.Dr = (u16)(dat)) +#define SPI_RXSAMPLE_DLY_REG(pCtrl) ((FSpi_RxSampleDlyReg_t *)(SPI_BASE_ADDR(pCtrl) + SPI_RX_SAMPLE_DLY)) +#define SPI_MWCTRL_REG(pCtrl) ((FSpi_MwcrReg_t *)SPI_BASE_ADDR(pCtrl) + SPI_MW_CR) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_irq.c b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_irq.c new file mode 100644 index 0000000000..7a53a33589 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_spi/ft_spi_irq.c @@ -0,0 +1,13 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-25 14:00:39 + * @LastEditTime: 2021-04-25 14:00:39 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.c new file mode 100644 index 0000000000..750b0c83a9 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.c @@ -0,0 +1,287 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 17:49:58 + * @Description:  This files is for uart functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +/***************************** Include Files ********************************/ + +#include "ft_status.h" +#include "ft_uart.h" +#include "ft_io.h" + +u32 FUart_SendBuffer(Ft_Uart *UartPtr); +u32 FUart_ReceiveBuffer(Ft_Uart *UartPtr); + +static void FUart_StubHandler(void *Args, u32 Event, + u32 ByteCount); + +/** + * @name: FUart_CfgInitialize + * @msg: initalize uart configure + * @return {*} + * @param {Ft_Uart} *UartPtr + * @param {FUart_Config_t} *Config + */ +s32 FUart_CfgInitialize(Ft_Uart *UartPtr, FUart_Config_t *Config) +{ + u32 RegValue = 0; + + Ft_assertNonvoid(UartPtr != NULL); + Ft_assertNonvoid(Config != NULL); + + UartPtr->Config.InstanceId = Config->InstanceId; + UartPtr->Config.BaseAddress = Config->BaseAddress; + UartPtr->Config.RefClockHz = Config->RefClockHz; + UartPtr->Config.IsrNum = Config->IsrNum; + + UartPtr->Handler = FUart_StubHandler; + + UartPtr->SendBuffer.BytePtr = NULL; + UartPtr->SendBuffer.RequestedBytes = 0; + UartPtr->SendBuffer.RemainingBytes = 0; + + UartPtr->ReceiveBuffer.BytePtr = NULL; + UartPtr->ReceiveBuffer.RequestedBytes = 0; + UartPtr->ReceiveBuffer.RemainingBytes = 0; + UartPtr->rxbs_error = 0; + UartPtr->IsReady = FT_COMPONENT_IS_READLY; + //Config.BaseAddress, UARTLCR_H_OFFSET, RegValue); + + /* Set the RX FIFO trigger at 8 data bytes.Tx FIFO trigger is 8 data bytes*/ + RegValue = (1 << 3) | (1 << 0); + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIFLS_OFFSET, RegValue); + + /* Disable all interrupts, polled mode is the default */ + RegValue = 0; + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, RegValue); + return FST_SUCCESS; +} + +/** + * @name: FUart_Send + * @msg: + * @param {Ft_Uart} *UartPtr + * @param {u8} *Buffer + * @param {u32} Length + * @return {u32} The Number of bytes actully sent. + */ +u32 FUart_Send(Ft_Uart *UartPtr, u8 *BytePtr, u32 Length) +{ + u32 RegValue = 0; + u32 SentCount = 0; + Ft_assertNonvoid(UartPtr != NULL); + Ft_assertNonvoid(BytePtr != NULL); + Ft_assertNonvoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + /* + * Disable the UART transmit interrupts to allow this call to stop a + * previous operation that may be interrupt driven. + */ + RegValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET); + RegValue &= ~(UARTIMSC_TXIM); + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, RegValue); + + UartPtr->SendBuffer.BytePtr = BytePtr; + UartPtr->SendBuffer.RequestedBytes = Length; + UartPtr->SendBuffer.RemainingBytes = Length; + + SentCount = FUart_SendBuffer(UartPtr); + + return SentCount; +} + +/** + * @name: FUart_PutChar + * @msg: send a char through uart + * @return {*} + * @param {Ft_Uart} *UartPtr + * @param {s8} Data + */ +void FUart_PutChar(Ft_Uart *UartPtr, s8 Data) +{ + Ft_assertVoid(UartPtr != NULL); + Ft_assertVoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + while (!FT_UART_IsTransmitFull(UartPtr->Config.BaseAddress)) + { + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTDR_OFFSET, Data); + break; + } +} + +static void FUart_StubHandler(void *Args, u32 Event, + u32 ByteCount) +{ + (void)Args; + (void)Event; + (void)ByteCount; + + Ft_assertVoidAlways(); +} + +/** + * @name: FUart_SendBuffer + * @msg: send data buffer through uart + * @return {*} + * @param {Ft_Uart} *UartPtr + */ +u32 FUart_SendBuffer(Ft_Uart *UartPtr) +{ + u32 SentCount = 0U; + u32 RegValue; + + /* + * If the TX FIFO is full, send nothing. + * Otherwise put bytes into the TX FIFO unil it is full, or all of the + * data has been put into the FIFO. + */ + while ((!FT_UART_IsTransmitFull(UartPtr->Config.BaseAddress)) && (UartPtr->SendBuffer.RemainingBytes > SentCount)) + { + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTDR_OFFSET, (u32)UartPtr->SendBuffer.BytePtr[SentCount]); + SentCount++; + } + + /* Update the buffer to reflect the bytes that were sent from it */ + UartPtr->SendBuffer.BytePtr += SentCount; + UartPtr->SendBuffer.RemainingBytes -= SentCount; + + RegValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET); + RegValue |= (UARTIMSC_TXIM); + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, RegValue); + + return SentCount; +} + +/** + * @name: FUart_Receive + * @msg: receive data through uart + * @return {*} + * @param {Ft_Uart} *UartPtr + * @param {u8} *BytePtr + * @param {u32} Length + */ +u32 FUart_Receive(Ft_Uart *UartPtr, u8 *BytePtr, u32 Length) +{ + u32 Received; + u32 BackRegValue; + + Ft_assertNonvoid(UartPtr != NULL); + Ft_assertNonvoid(BytePtr != NULL); + Ft_assertNonvoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + /* + * Disable all the interrupts. + * This stops a previous operation that may be interrupt driven + */ + BackRegValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET); + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, 0); + + UartPtr->ReceiveBuffer.BytePtr = BytePtr; + UartPtr->ReceiveBuffer.RequestedBytes = Length; + UartPtr->ReceiveBuffer.RemainingBytes = Length; + + Received = FUart_ReceiveBuffer(UartPtr); + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, BackRegValue); + + return Received; +} + +/** + * @name: Ft_Uart_ReceiveBuffer + * @msg: handling uart receive buffer + * @return {*} + * @param {Ft_Uart} *UartPtr + */ +u32 FUart_ReceiveBuffer(Ft_Uart *UartPtr) +{ + + u32 ReceivedCount = 0U; + u32 Event; + u32 EventData; + u32 ByteValue; + + while ((ReceivedCount < UartPtr->ReceiveBuffer.RemainingBytes) && !FT_UART_IsReceiveData(UartPtr->Config.BaseAddress)) + { + ByteValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTDR_OFFSET); + + if (UartPtr->rxbs_error) + { + if ((ByteValue & UARTDR_ALLE) != 0) + { + EventData = ByteValue; + Event = FUART_EVENT_PARE_FRAME_BRKE; + + if (UartPtr->Handler) + { + UartPtr->Handler(UartPtr->Args, Event, EventData); + } + } + } + UartPtr->ReceiveBuffer.BytePtr[ReceivedCount] = (u8)(ByteValue & 0xff); + ReceivedCount++; + } + + UartPtr->rxbs_error = 0; + + if (UartPtr->ReceiveBuffer.BytePtr != NULL) + { + UartPtr->ReceiveBuffer.BytePtr += ReceivedCount; + } + UartPtr->ReceiveBuffer.RemainingBytes -= ReceivedCount; + + return ReceivedCount; +} + +/** + * @name: FUart_BlockSend + * @msg: initiate uart block send + * @return {*} + * @param {Ft_Uart} *UartPtr + * @param {u8} *BytePtr + * @param {u32} Length + */ +void FUart_BlockSend(Ft_Uart *UartPtr, u8 *BytePtr, u32 Length) +{ + u32 index; + + Ft_assertVoid(UartPtr != NULL); + Ft_assertVoid(BytePtr != NULL); + Ft_assertVoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + for (index = 0; index < Length; index++) + { + FUart_SendByte(UartPtr->Config.BaseAddress, BytePtr[index]); + } +} + +/** + * @name: FUart_BlockReceive + * @msg: initiate uart block receive + * @return {*} + * @param {Ft_Uart} *UartPtr + */ +u8 FUart_BlockReceive(Ft_Uart *UartPtr) +{ + Ft_assertNonvoid(UartPtr != NULL); + Ft_assertNonvoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + return FUart_RecvByte(UartPtr->Config.BaseAddress); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.h b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.h new file mode 100644 index 0000000000..e072fdab7d --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart.h @@ -0,0 +1,111 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-19 16:30:05 + * @Description:  This files is for uart functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_UART_H +#define FT_UART_H + +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_status.h" +#include "ft_uart_hw.h" + +#define FUART_BAUDRATE 115200U + +/* Config options */ +#define FUART_OPTION_UARTEN 0x1U +#define FUART_OPTION_RXEN 0x2U +#define FUART_OPTION_TXEN 0x4U +#define FUART_OPTION_FIFOEN 0x8U + +/* Data format values */ +#define FUART_FORMAT_WORDLENGTH_8BIT 0x3 +#define FUART_FORMAT_WORDLENGTH_7BIT 0x2 +#define FUART_FORMAT_WORDLENGTH_6BIT 0x1 +#define FUART_FORMAT_WORDLENGTH_5BIT 0x0 + +#define FUART_FORMAT_NO_PARITY 0U +#define FUART_FORMAT_MARK_PARITY 1U +#define FUART_FORMAT_SPACE_PARITY 2U +#define FUART_FORMAT_ODD_PARTY 3U +#define FUART_FORMAT_EVEN_PARITY 4U + +#define FUART_FORMAT_2_STOP_BIT 0U +#define FUART_FORMAT_1_STOP_BIT 1U + +/* Callback events */ + +#define FUART_EVENT_RECV_DATA 1U /**< Data receiving done */ +#define FUART_EVENT_RECV_TOUT 2U /**< A receive timeout occurred */ +#define FUART_EVENT_SENT_DATA 3U /**< Data transmission done */ +#define FUART_EVENT_RECV_ERROR 4U /**< A receive error detected */ +#define FUART_EVENT_MODEM 5U /**< Modem status changed */ +#define FUART_EVENT_PARE_FRAME_BRKE 6U /**< A receive parity, frame, break \ + * error detected */ +#define FUART_EVENT_RECV_ORERR 7U /**< A receive overrun error detected */ + +/**************************** Type Definitions ******************************/ +typedef struct +{ + u32 InstanceId; /* Id of device*/ + u32 BaseAddress; + u32 RefClockHz; + u32 IsrNum; +} FUart_Config_t; + +typedef struct +{ + u8 *BytePtr; + u32 RequestedBytes; + u32 RemainingBytes; +} FUart_Buffer_t; + +typedef struct +{ + u32 BaudRate; /**< In bps, ie 1200 */ + u32 DataBits; /**< Number of data bits */ + u32 Parity; /**< Parity */ + u8 StopBits; /**< Number of stop bits */ +} FUart_Format_t; + +typedef void (*FUart_Handler_t)(void *Args, u32 Event, u32 EventData); + +typedef struct +{ + FUart_Config_t Config; /* Configuration data structure */ + u32 InputClockHz; + u32 IsReady; /* Device is ininitialized and ready*/ + u32 BaudRate; + + FUart_Buffer_t SendBuffer; + FUart_Buffer_t ReceiveBuffer; + + FUart_Handler_t Handler; + void *Args; + uint8_t rxbs_error; /* 接收过程中出现错误 ,0 无错误,1 存在错误*/ + +} Ft_Uart; + +void FUart_PutChar(Ft_Uart *UartPtr, s8 Data); +u32 FUart_Send(Ft_Uart *UartPtr, u8 *Buffer, u32 Length); +u32 FUart_Receive(Ft_Uart *UartPtr, u8 *BytePtr, u32 Length); +s32 FUart_CfgInitialize(Ft_Uart *UartPtr, FUart_Config_t *Config); +FUart_Config_t *FUart_LookupConfig(u32 InstanceId); +void FUart_SetOptions(Ft_Uart *UartPtr, u32 Options); +void FUart_SetSpecificOptions(Ft_Uart *UartPtr, u32 Options); +void FUart_ClearSpecificOptions(Ft_Uart *UartPtr, u32 Options); +void FUart_InterruptHandler(Ft_Uart *UartPtr); +void FUart_SetHandler(Ft_Uart *UartPtr, FUart_Handler_t FuncPtr, + void *Args); +void FUart_SetInterruptMask(Ft_Uart *UartPtr, u32 Mask); +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_g.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_g.c new file mode 100644 index 0000000000..1da9225045 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_g.c @@ -0,0 +1,34 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:42:30 + * @Description:  This files is for uart config + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_uart.h" +#include "ft_parameters.h" + +FUart_Config_t FUart_Config_tTable[FT_UART_NUM] = { + {FT_UART0_ID, + FT_UART0_BASE_ADDR, + FT_UART0_CLK_FREQ_HZ, + 38}, + {FT_UART1_ID, + FT_UART1_BASE_ADDR, + FT_UART1_CLK_FREQ_HZ, + 39}, + {FT_UART2_ID, + FT_UART2_BASE_ADDR, + FT_UART2_CLK_FREQ_HZ, + 40}, + {FT_UART3_ID, + FT_UART3_BASE_ADDR, + FT_UART3_CLK_FREQ_HZ, + 41}}; diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.c new file mode 100644 index 0000000000..0f52367ddb --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.c @@ -0,0 +1,66 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 17:54:21 + * @Description:  This files is for uart register function + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "ft_uart_hw.h" + +/** + * @name: FUart_SendByte + * @msg: + * @return {*} + * @param {u32} BaseAddress + * @param {u8} Byte + */ +void FUart_SendByte(u32 BaseAddress, u8 Byte) +{ + while (FT_UART_IsTransmitFull(BaseAddress)) + { + ; + } + FT_UART_WriteReg(BaseAddress, UARTDR_OFFSET, (u32)Byte); +} + +/** + * @name: FUart_RecvByte + * @msg: + * @return {*} + * @param {u32} BaseAddress + */ +u8 FUart_RecvByte(u32 BaseAddress) +{ + u32 RecievedByte; + while (FT_UART_IsReceiveData(BaseAddress)) + { + ; + } + RecievedByte = FT_UART_ReadReg(BaseAddress, UARTDR_OFFSET); + return RecievedByte; +} + +/** + * @name: FUart_GetChar + * @msg: + * @return {*} + * @param {u32} BaseAddress + */ +u8 FUart_GetChar(u32 BaseAddress) +{ + u32 RecievedByte; + if (FT_UART_IsReceiveData(BaseAddress)) + { + return 0xff; + } + RecievedByte = FT_UART_ReadReg(BaseAddress, UARTDR_OFFSET); + return RecievedByte; +} diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.h b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.h new file mode 100644 index 0000000000..2a3ac1c749 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_hw.h @@ -0,0 +1,222 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology(TianJin), Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-03-31 14:59:20 + * @LastEditTime: 2021-04-02 14:14:34 + * @Description:  This files is for definition of uart register + * + * @Modify History: * * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_UART_HW_H +#define FT_UART_HW_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_io.h" + +/************************** Constant Definitions *****************************/ + +/** @name Register Map + * + * Register offsets for the UART. + * @{ + */ + +#define UARTDR_OFFSET 0U /* 数据寄存器 */ +#define UARTRSR_OFFSET 4U /* 接收状态寄存器/错误清除寄存器 */ +#define UARTECR_OFFSET UARTRSR_OFFSET +#define UARTFTR_OFFSET 0x18U /* 标志寄存器 */ +#define UARTILPR_OFFSET 0x020U /* 低功耗计数寄存器 */ +#define UARTIBRD_OFFSET 0x024U /* 波特率整数值配置寄存器 */ +#define UARTFBRD_OFFSET 0x028U /* 波特率小数值配置寄存器 */ +#define UARTLCR_H_OFFSET 0x02cU /* 线控寄存器 */ +#define UARTCR_OFFSET 0x030U /* 控制寄存器 */ +#define UARTIFLS_OFFSET 0x034U /* FIFO阈值选择寄存器 */ +#define UARTIMSC_OFFSET 0x038U /* 中断屏蔽选择清除寄存器 */ +#define UARTRIS_OFFSET 0x03cU /* 中断状态寄存器 */ +#define UARTMIS_OFFSET 0x040U /* 中断屏蔽状态寄存器 */ +#define UARTICR_OFFSET 0x044U /* 中断清除寄存器 */ +#define UARTDMACR_OFFSET 0x048U /* DMA控制寄存器 */ + + /* 数据寄存器 RW */ + +#define UARTDR_OE 0x800U /* 如果接收到数据并且接收的 FIFO 已满,该位设置为 1 */ +#define UARTDR_BE 0x400U /* 突发错误 */ +#define UARTDR_PE 0x200U /* 奇偶校验错误。 */ +#define UARTDR_FE 0x100U /* 帧错误。 */ +#define UARTDR_ALLE (UARTDR_OE | UARTDR_BE | UARTDR_PE | UARTDR_FE) +#define UARTDR_DATA 0xffU /* R 接收数据 ,W 传输数据 */ + + /* 接收状态寄存器 RW */ + +#define UARTRSR_OE 0x8U /* 溢出错误。 */ +#define UARTRSR_BE 0x4U /* 突发错误 */ +#define UARTRSR_PE 0x2U /* 奇偶校验错误。 */ +#define UARTRSR_FE 0x1U /* 帧错误 */ + +#define UARTECR_CLE 0xffU /* 清除 */ + +/* 标志寄存器 RO */ +#define UARTFTR_RI 0x100U /* Ring indicator */ +#define UARTFTR_TXFE 0x80U /* Transmit FIFO empty */ +#define UARTFTR_RXFF 0x40U /* Receive FIFO full */ +#define UARTFTR_TXFF 0x20U /* Transmit FIFO full. */ +#define UARTFTR_RXFE 0x10U /* Receive FIFO empty */ +#define UARTFTR_BUSY 0x08U /* UART busy */ +#define UARTFTR_DCD 0x04U /* Data carrier detect. */ +#define UARTFTR_DSR 0x02U /* Data set ready. */ +#define UARTFTR_CTS 0x1U /* Clear to send */ + +/* IrDA 低功耗计数寄存器 RW */ +#define UARTILPR_ILPDVSR 0xffU /* 8-bit low-power divisor value. These bits are cleared to 0 at reset */ + +/* 波特率整数值配置寄存器 RW */ +#define UARTIBRD_BAUD_DIVFRAC 0xffffU /* The fractional baud rate divisor. */ + +/* 波特率小数值配置寄存器 RW */ +#define UARTFBRD_BAUD_DIVFRAC 0x3fU /* The fractional baud rate divisor. */ + +/* 线控寄存器 RW */ +#define UARTLCR_H_SPS 0x80U /* Stick parity select. */ +#define UARTLCR_H_WLEN 0x60U /* Word length. */ +#define UARTLCR_H_FEN 0x10U /* Enable FIFOs. */ +#define UARTLCR_H_STP2 0x08U /* Two stop bits select. */ +#define UARTLCR_H_EPS 0x04U /* Even parity select. */ +#define UARTLCR_H_PEN 0x02U /* Parity enable. */ +#define UARTLCR_H_BRK 0x01U /* send break */ + +/* 控制寄存器 RW */ +#define UARTCR_CTSEN 0x8000U /* CTS hardware flow control enable. */ +#define UARTCR_RTSEN 0x4000U /* RTS hardware flow control enable. */ +#define UARTCR_OUT2 0x2000U /* This bit is the complement of the UART Out2 (nUARTOut2) modem status output. */ +#define UARTCR_Out1 0x1000U /* This bit is the complement of the UART Out1 (nUARTOut1) modem status output. */ +#define UARTCR_RTS 0x0800U /* Request to send. */ +#define UARTCR_DTR 0x0400U /* Data transmit ready */ +#define UARTCR_RXE 0x0200U /* Receive enable. */ +#define UARTCR_TXE 0x0100U /* Transmit enable. */ +#define UARTCR_LBE 0x0080U /* Loop back enable.*/ +#define UARTCR_SIRLP 0x4U /* IrDA SIR low power mode. */ +#define UARTCR_SIREN 0x2U /* SIR enable. */ +#define UARTCR_UARTEN 0x1U /* UART enable. */ + +/* FIFO阈值选择寄存器 RW */ +#define UARTIFLS_RXIFLSEL 0x38U /* Receive interrupt FIFO level select. */ +#define UARTIFLS_TXIFLSEL 0x7U /* Transmit interrupt FIFO level select. */ + +/* 中断屏蔽选择清除寄存器 RW */ +#define UARTIMSC_OEIM 0x400U /* Overrun error interrupt mask. */ +#define UARTIMSC_BEIM 0x200U /* Break error interrupt mask */ +#define UARTIMSC_PEIM 0x100U /* Parity error interrupt mask. */ +#define UARTIMSC_FEIM 0x80U /* Framing error interrupt mask. */ +#define UARTIMSC_RTIM 0x40U /* Receive timeout interrupt mask. */ +#define UARTIMSC_TXIM 0x20U /* Transmit interrupt mask. */ +#define UARTIMSC_RXIM 0x10U /* Receive interrupt mask. */ +#define UARTIMSC_DSRMIM 0x8U /* nUARTDSR modem interrupt mask. */ +#define UARTIMSC_DCDMIM 0x4U /* nUARTDCD modem interrupt mask. */ +#define UARTIMSC_CTSMIM 0x2U /* nUARTCTS modem interrupt mask. */ +#define UARTIMSC_RIMIM 0x1U /* nUARTRI modem interrupt mask. */ +#define UARTIMSC_ALLM 0x3ffU /* all interrupt mask */ + + /* 中断状态寄存器 RO */ + +#define UARTRIS_OEIS 0x400U /* Overrun error interrupt mask. */ +#define UARTRIS_BEIS 0x200U /* Break error interrupt mask */ +#define UARTRIS_PEIS 0x100U /* Parity error interrupt mask. */ +#define UARTRIS_FEIS 0x80U /* Framing error interrupt mask. */ +#define UARTRIS_RTIS 0x40U /* Receive timeout interrupt mask. */ +#define UARTRIS_TXIS 0x20U /* Transmit interrupt mask. */ +#define UARTRIS_RXIS 0x10U /* Receive interrupt mask. */ +#define UARTRIS_DSRMIS 0x8U /* nUARTDSR modem interrupt mask. */ +#define UARTRIS_DCDMIS 0x4U /* nUARTDCD modem interrupt mask. */ +#define UARTRIS_CTSMIS 0x2U /* nUARTCTS modem interrupt mask. */ +#define UARTRIS_RIMIS 0x1U /* nUARTRI modem interrupt mask. */ + + /* 中断屏蔽状态寄存器 R0 */ + +#define UARTMIS_OEMIS 0x400U /* Overrun error interrupt mask. */ +#define UARTMIS_BEMIS 0x200U /* Break error interrupt mask */ +#define UARTMIS_PEMIS 0x100U /* Parity error interrupt mask. */ +#define UARTMIS_FEMIS 0x80U /* Framing error interrupt mask. */ +#define UARTMIS_RTMIS 0x40U /* Receive timeout interrupt mask. */ +#define UARTMIS_TXMIS 0x20U /* Transmit interrupt mask. */ +#define UARTMIS_RXMIS 0x10U /* Receive interrupt mask. */ +#define UARTMIS_DSRMMIS 0x8U /* nUARTDSR modem interrupt mask. */ +#define UARTMIS_DCDMMIS 0x4U /* nUARTDCD modem interrupt mask. */ +#define UARTMIS_CTSMMIS 0x2U /* nUARTCTS modem interrupt mask. */ +#define UARTMIS_RIMMIS 0x1U /* nUARTRI modem interrupt mask. */ + +/* 中断清除寄存器 WO */ +#define UARTICR_OEIC 0x400U /* Overrun error interrupt mask. */ +#define UARTICR_BEIC 0x200U /* Break error interrupt mask */ +#define UARTICR_PEIC 0x100U /* Parity error interrupt mask. */ +#define UARTICR_FEIC 0x80U /* Framing error interrupt mask. */ +#define UARTICR_RTIC 0x40U /* Receive timeout interrupt mask. */ +#define UARTICR_TXIC 0x20U /* Transmit interrupt mask. */ +#define UARTICR_RXIC 0x10U /* Receive interrupt mask. */ +#define UARTICR_DSRMIC 0x8U /* nUARTDSR modem interrupt mask. */ +#define UARTICR_DCDMIC 0x4U /* nUARTDCD modem interrupt mask. */ +#define UARTICR_CTSMIC 0x2U /* nUARTCTS modem interrupt mask. */ +#define UARTICR_RIMIC 0x1U /* nUARTRI modem interrupt mask. */ + +/* DMA控制寄存器 RW */ +#define UARTDMACR_DMAONERR 0x4U /* DMA on error. */ +#define UARTDMACR_TXDMAE 0x2U /* Transmit DMA enable. */ +#define UARTDMACR_RXDMAE 0x1U /* Receive DMA enable. */ + +/***************** Macros (Inline Functions) Definitions *********************/ + +/** + * @name: FT_UART_ReadReg + * @msg: 读取串口寄存器 + * @param {u32} BaseAddress 串口的基地址 + * @param {u32} RegOffset 串口的寄存器的偏移 + * @return {u32} 寄存器参数 + */ +#define FT_UART_ReadReg(BaseAddress, RegOffset) Ft_in32(BaseAddress + (u32)RegOffset) + +/** + * @name: FT_UART_WriteReg + * @msg: 写入串口寄存器 + * @param {u32} BaseAddress 串口的基地址 + * @param {u32} RegOffset 串口的寄存器的偏移 + * @param {u32} RegisterValue 写入寄存器参数 + * @return {void} + */ +#define FT_UART_WriteReg(BaseAddress, RegOffset, RegisterValue) Ft_out32(BaseAddress + (u32)RegOffset, (u32)RegisterValue) + +/** + * @name: FT_UART_ISRECEIVEDATA + * @msg: 用于确认是否接收到数据 + * @param {u32} BaseAddress 串口的基地址 + * @return {bool} true 是存在数据 , false 是不存在数据 + * + */ +#define FT_UART_IsReceiveData(BaseAddress) (Ft_in32(BaseAddress + UARTFTR_OFFSET) & UARTFTR_RXFE) + +/** + * @name: FT_UART_ISTRANSMITFULL + * @msg: 用于确认是否能够发送数据 + * @param {u32} BaseAddress 串口的基地址 + * @return {bool} true 是数据已满 , false 可以发送数据 + */ +#define FT_UART_IsTransmitFull(BaseAddress) ((Ft_in32(BaseAddress + UARTFTR_OFFSET) & (u32)UARTFTR_TXFF) == UARTFTR_TXFF) + + void FUart_SendByte(u32 BaseAddress, u8 Byte); + u8 FUart_RecvByte(u32 BaseAddress); + u8 FUart_GetChar(u32 BaseAddress); + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_intr.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_intr.c new file mode 100644 index 0000000000..fc6dd5402a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_intr.c @@ -0,0 +1,209 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 17:52:02 + * @Description:  This files is for uart irq functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_uart.h" + +extern u32 FUart_SendBuffer(Ft_Uart *UartPtr); +extern u32 FUart_ReceiveBuffer(Ft_Uart *UartPtr); + +static void FUart_receiveErrorHandler(Ft_Uart *UartPtr, u32 InterruptStatus); +static void FUart_receiveDataHandler(Ft_Uart *UartPtr); +static void FUart_receiveTimeoutHandler(Ft_Uart *UartPtr); +static void FUart_sendDataHandler(Ft_Uart *UartPtr, u32 InterruptStatus); + +/** + * @name: FUart_GetInterruptMask + * @msg: 此函数获取所有串口中断的mask。 + * @param {Ft_Uart} *UartPtr + * @return {u32} mask + */ +u32 FUart_GetInterruptMask(Ft_Uart *UartPtr) +{ + Ft_assertNonvoid(UartPtr != NULL); + + return FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET); +} + +/** + * @name: + * @msg: + * @param {Ft_Uart} *UartPtr + * @param {u32} Mask + * @return {*} + */ +void FUart_SetInterruptMask(Ft_Uart *UartPtr, u32 Mask) +{ + u32 TempMask = Mask; + Ft_assertVoid(UartPtr != NULL); + + TempMask &= UARTIMSC_ALLM; + + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, TempMask); +} + +/** + * @name: FUart_SetHandler + * @msg: 设置中断回调函数 + * @param {*} + * @return {*} + */ +void FUart_SetHandler(Ft_Uart *UartPtr, FUart_Handler_t FuncPtr, + void *Args) +{ + Ft_assertVoid(UartPtr != NULL); + Ft_assertVoid(FuncPtr != NULL); + Ft_assertVoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + UartPtr->Handler = FuncPtr; + UartPtr->Args = Args; +} + +/** + * @name: FUart_InterruptHandler + * @msg: 串口中断函数入口 + * @param {Ft_Uart} *UartPtr + * @return {*} + */ +void FUart_InterruptHandler(Ft_Uart *UartPtr) +{ + u32 RegValue = 0; + Ft_assertVoid(UartPtr != NULL); + Ft_assertVoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + //Ft_printf("FUart_InterruptHandler %x\r\n", UartPtr); + RegValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET); + + RegValue &= FT_UART_ReadReg(UartPtr->Config.BaseAddress, UARTMIS_OFFSET); + + if ((RegValue & ((u32)UARTMIS_RXMIS)) != (u32)0) + { + /* Received data interrupt */ + FUart_receiveDataHandler(UartPtr); + } + + if ((RegValue & ((u32)UARTMIS_TXMIS)) != (u32)0) + { + /* Transmit data interrupt */ + FUart_sendDataHandler(UartPtr, RegValue); + } + + if (((RegValue) & ((u32)UARTMIS_OEMIS | (u32)UARTMIS_BEMIS | (u32)UARTMIS_PEMIS | (u32)UARTMIS_FEMIS)) != (u32)0) + { + /* Received Error Status interrupt */ + FUart_receiveErrorHandler(UartPtr, RegValue); + } + + if ((RegValue & ((u32)UARTMIS_RTMIS)) != (u32)0) + { + /* Received Timeout interrupt */ + FUart_receiveTimeoutHandler(UartPtr); + } + + if (((RegValue) & ((u32)UARTMIS_DSRMMIS | (u32)UARTMIS_DCDMMIS | (u32)UARTMIS_CTSMMIS | (u32)UARTMIS_RIMMIS)) != (u32)0) + { + /* Modem status interrupt */ + } + + /* Clear the interrupt status. */ + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTICR_OFFSET, + RegValue); +} + +static void FUart_receiveErrorHandler(Ft_Uart *UartPtr, u32 InterruptStatus) +{ + UartPtr->rxbs_error = 0; + + if (((InterruptStatus) & ((u32)UARTMIS_OEMIS | (u32)UARTMIS_BEMIS | (u32)UARTMIS_PEMIS | (u32)UARTMIS_FEMIS)) != 0) + { + UartPtr->rxbs_error = 1; + } + + (void)FUart_ReceiveBuffer(UartPtr); + + if (0 == UartPtr->rxbs_error) + { + if (UartPtr->Handler) + { + UartPtr->Handler(UartPtr->Args, FUART_EVENT_RECV_ERROR, UartPtr->ReceiveBuffer.RequestedBytes - UartPtr->ReceiveBuffer.RemainingBytes); + } + } +} + +/** + * @name: FUart_receiveDataHandler + * @msg: + * @param {*} + * @return {*} + */ +static void FUart_receiveDataHandler(Ft_Uart *UartPtr) +{ + if ((u32)0 != UartPtr->ReceiveBuffer.RemainingBytes) + { + (void)FUart_ReceiveBuffer(UartPtr); + } + + if ((u32)0 == UartPtr->ReceiveBuffer.RemainingBytes) + { + if (UartPtr->Handler) + { + UartPtr->Handler(UartPtr->Args, FUART_EVENT_RECV_DATA, UartPtr->ReceiveBuffer.RequestedBytes - UartPtr->ReceiveBuffer.RemainingBytes); + } + } +} + +static void FUart_receiveTimeoutHandler(Ft_Uart *UartPtr) +{ + u32 Event; + + if ((u32)0 != UartPtr->ReceiveBuffer.RemainingBytes) + { + (void)FUart_ReceiveBuffer(UartPtr); + } + + if ((u32)0 == UartPtr->ReceiveBuffer.RemainingBytes) + { + Event = FUART_EVENT_RECV_TOUT; + } + else + { + Event = FUART_EVENT_RECV_DATA; + } + + if (UartPtr->Handler) + { + UartPtr->Handler(UartPtr->Args, Event, UartPtr->ReceiveBuffer.RequestedBytes - UartPtr->ReceiveBuffer.RemainingBytes); + } +} + +static void FUart_sendDataHandler(Ft_Uart *UartPtr, u32 InterruptStatus) +{ + u32 RegValue; + if (UartPtr->SendBuffer.RemainingBytes == (u32)0) + { + //Config.BaseAddress, UARTIMSC_OFFSET); + RegValue &= ~UARTIMSC_TXIM; + FT_UART_WriteReg(UartPtr->Config.BaseAddress, UARTIMSC_OFFSET, RegValue); + if (UartPtr->Handler) + { + UartPtr->Handler(UartPtr->Args, FUART_EVENT_RECV_DATA, UartPtr->ReceiveBuffer.RequestedBytes - UartPtr->ReceiveBuffer.RemainingBytes); + } + } + else if (InterruptStatus & UARTMIS_TXMIS) + { + FUart_SendBuffer(UartPtr); + } + else + { + } +} diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_options.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_options.c new file mode 100644 index 0000000000..58b5bea59a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_options.c @@ -0,0 +1,60 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-19 16:28:05 + * @Description:  This files is for uart option setting + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_uart.h" +#include "ft_uart_hw.h" +#include "ft_types.h" +/************************** Variable Definitions ****************************/ +/* + * The following data type is a map from an option to the offset in the + * register to which it belongs as well as its bit mask in that register. + */ +typedef struct +{ + u32 Option; + u32 RegisterOffset; + u32 Mask; +} Mapping; + +static Mapping OptionTable[] = { + {FUART_OPTION_UARTEN, UARTCR_OFFSET, UARTCR_UARTEN}, + {FUART_OPTION_RXEN, UARTCR_OFFSET, UARTCR_RXE}, + {FUART_OPTION_TXEN, UARTCR_OFFSET, UARTCR_TXE}, + {FUART_OPTION_FIFOEN, UARTLCR_H_OFFSET, UARTLCR_H_FEN}}; + +#define FT_UART_NUM_OPITIONS (sizeof(OptionTable) / sizeof(Mapping)) + +void FUart_SetOptions(Ft_Uart *UartPtr, u32 Options) +{ + u32 Index; + u32 RegValue; + Ft_assertVoid(UartPtr != NULL); + Ft_assertVoid(UartPtr->IsReady == FT_COMPONENT_IS_READLY); + + for (Index = 0; Index < FT_UART_NUM_OPITIONS; Index++) + { + RegValue = FT_UART_ReadReg(UartPtr->Config.BaseAddress, OptionTable[Index].RegisterOffset); + + if ((Options & OptionTable[Index].Option) != (u32)(0)) + { + RegValue |= OptionTable[Index].Mask; + } + else + { + RegValue &= ~OptionTable[Index].Mask; + } + + FT_UART_WriteReg(UartPtr->Config.BaseAddress, OptionTable[Index].RegisterOffset, RegValue); + } +} diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_selftest.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_selftest.c new file mode 100644 index 0000000000..0feffa0990 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_selftest.c @@ -0,0 +1,13 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:44:41 + * @Description:  This files is for uart test cases + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ diff --git a/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_sinit.c b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_sinit.c new file mode 100644 index 0000000000..da4f856b58 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/ft_uart/ft_uart_sinit.c @@ -0,0 +1,41 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 13:44:56 + * @Description:  This files is for uart static init + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_uart.h" +#include "ft_parameters.h" + +extern FUart_Config_t FUart_Config_tTable[FT_UART_NUM]; + +/** + * @name: Ft_Uart_LookupConfig + * @msg: 获取串口的基本配置 + * @param {u16} InstanceId FT_UARTX_ID + * @return {*} + */ +FUart_Config_t *FUart_LookupConfig(u32 InstanceId) +{ + FUart_Config_t *CfgPtr = NULL; + u32 Index; + + for (Index = 0; Index < (u32)FT_UART_NUM; Index++) + { + if (FUart_Config_tTable[Index].InstanceId == InstanceId) + { + CfgPtr = &FUart_Config_tTable[Index]; + break; + } + } + + return (FUart_Config_t *)CfgPtr; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/include/ft_parameters.h b/bsp/ft2004/libraries/bsp/include/ft_parameters.h new file mode 100644 index 0000000000..60612d98a3 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/include/ft_parameters.h @@ -0,0 +1,204 @@ +/* + * [H]: Copyright (C) 2014-2021 Tianjin Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * Date: 2021-03-30 14:57:03 + * LastEditTime: 2021-03-30 15:05:12 + * Description:  definitions of BSP parameters + * Modify History: + * * * Ver Who Date Changes + * * ----- ------ -------- ---------------------------------------------- + * 1.00 Huanghe 2021/3/1 init + */ + +#ifndef FT_PARAMETERS_H +#define FT_PARAMETERS_H + +/* Device register address */ +#define FT_DEV_BASE_ADDR 0x28000000 +#define FT_DEV_END_ADDR 0x2FFFFFFF + +/******** UART ************/ + +#define FT_UART_NUM 4 +#define FT_UART_REG_LENGTH 0x18000 + +#define FT_UART0_ID 0 +#define FT_UART0_BASE_ADDR 0x28000000 +#define FT_UART0_CLK_FREQ_HZ 48000000 + +#define FT_UART1_ID 1 +#define FT_UART1_BASE_ADDR 0x28001000 +#define FT_UART1_CLK_FREQ_HZ 48000000 + +#define FT_UART2_ID 2 +#define FT_UART2_BASE_ADDR 0x28002000 +#define FT_UART2_CLK_FREQ_HZ 48000000 + +#define FT_UART3_BASE_ADDR 0x28003000 +#define FT_UART3_ID 3 +#define FT_UART3_CLK_FREQ_HZ 48000000 + +#define FT_STDOUT_BASEADDRESS FT_UART1_BASE_ADDR +#define FT_STDIN_BASEADDRESS FT_UART1_BASE_ADDR + +/****** GIC v3 *****/ +#define FT_GICV3_INSTANCES_NUM 1U +#define GICV3_REG_LENGTH 0x00009000 + +/* + * The maximum priority value that can be used in the GIC. + */ +#define GICV3_MAX_INTR_PRIO_VAL 240U +#define GICV3_INTR_PRIO_MASK 0x000000f0U + +#define ARM_GIC_IPI_COUNT 16 /* MPCore IPI count */ +#define SGI_INT_MAX 16 +#define SPI_START_INT_NUM 32 /* SPI start at ID32 */ +#define PPI_START_INT_NUM 16 /* PPI start at ID16 */ +#define GIC_INT_MAX_NUM 1020 /* GIC max interrupts count */ + +#define FT_GICV3_BASEADDRESS 0x29900000U +#define FT_GICV3_DISTRIBUTOR_BASEADDRESS (FT_GICV3_BASEADDRESS + 0) +#define FT_GICV3_RD_BASEADDRESS (FT_GICV3_BASEADDRESS + 0x80000U) +#define FT_GICV3_SGI_BASEADDRESS (FT_GICV3_RD_BASEADDRESS + (1U << 16)) + +#define FT_GICV3_VECTORTABLE_NUM GIC_INT_MAX_NUM + +/** Gmac **/ +#define FT_GMAC_INSTANCES_NUM 2U +#define FT_GMAC_REG_LENGTH 0x00009000 + +#define FT_GMAC_COMMON_ADDR 0x2820B000U + +#define FT_GMAC0_ID 0 +#define FT_GMAC0_BASEADDR 0x2820C000U +#define FT_GMAC0_DEFAULT_ADDR \ + { \ + 0x11, 0x1c, 0x2c, 0x5c, 0x66, 0x88 \ + } + +#define FT_GMAC1_ID 1 +#define FT_GMAC1_BASEADDR 0x28210000U + +/** @defgroup ENET_Buffers_setting + * @{ + */ +#define GMAC_MAX_PACKET_SIZE 1600 /* GMAC_HEADER + GMAC_EXTRA + VLAN_TAG + MAX_GMAC_PAYLOAD + GMAC_CRC */ +#define GMAC_HEADER 14 /* 6 byte Dest addr, 6 byte Src addr, 2 byte length/type */ +#define GMAC_CRC 4 /* Gmac CRC */ +#define GMAC_EXTRA 2 /* Extra bytes in some cases */ +#define VLAN_TAG 4 /* optional 802.1q VLAN Tag */ +#define MIN_GMAC_PAYLOAD 46 /* Minimum Gmac payload size */ +#define MAX_GMAC_PAYLOAD 1500 /* Maximum Gmac payload size */ +#define JUMBO_FRAME_PAYLOAD 9000 /* Jumbo frame payload size */ +#define RX_DESCNUM 1024U /* Rx buffers of size GMAC_MAX_PACKET_SIZE */ +#define TX_DESCNUM 1024U /* Tx buffers of size GMAC_MAX_PACKET_SIZE */ + +#define PHY_USING_AR8035 + +#define GMAC0_ISRNUM 81 +#define GMAC0_ISRPRIORITY 0 + +#define GMAC1_ISRNUM 82 +#define GMAC1_ISRPRIORITY 0 + +/* SDC */ +#define FT_SDC_NUM 1 +#define FT_SDC_INSTANCE 0 +#define FT_SDC_BASEADDR 0x28207C00U +#define FT_SDC_REG_LENGTH 0x4000 +#define FT_SDC_FREQ 600000000 + +/* pin MUX/DEMUX */ + +#define FT_PIN_MUX_BASEADDR 0x28180000 +#define FT_PIN_MUX_REG_LENGTH 0x10000 + +/* CAN */ + +#define FT_CAN_NUM 3 +#define FT_CAN_REG_LENGTH 0x1000 +#define FT_CAN0_BASEADDR 0x28207000 +#define FT_CAN1_BASEADDR 0x28207400 +#define FT_CAN2_BASEADDR 0x28207800 +#define FT_CAN0_IRQNUM 119 +#define FT_CAN1_IRQNUM 123 +#define FT_CAN2_IRQNUM 124 +#define FT_CAN_BAUDRATE 1000000 /* 1M */ +#define FT_CAN_CLK 600000000 + +/* pci */ + +#define FT_PCI_CONFIG_BASEADDR 0x40000000 +#define FT_PCI_CONFIG_REG_LENGTH 0x10000000 + +#define FT_PCI_IO_CONFIG_BASEADDR 0x50000000 +#define FT_PCI_IO_CONFIG_REG_LENGTH 0x08000000 + +#define FT_PCI_MEM32_BASEADDR 0x58000000 +#define FT_PCI_MEM32_REG_LENGTH 0x27000000 + +/* qspi */ +#define FT_QSPI_NUM 1U +#define FT_QSPI_INSTANCE 0 +#define FT_QSPI_MAX_CS_NUM 4 +#define FT_QSPI_BASEADDR 0x28014000 + +#define FT_QSPI_FLASH_CAP_4MB 0 +#define FT_QSPI_FLASH_CAP_8MB 1 +#define FT_QSPI_FLASH_CAP_16MB 2 +#define FT_QSPI_FLASH_CAP_32MB 3 +#define FT_QSPI_FLASH_CAP_64MB 4 +#define FT_QSPI_FLASH_CAP_128MB 5 +#define FT_QSPI_FLASH_CAP_256MB 6 + +#define FT_QSPI_ADDR_SEL_3 0 +#define FT_QSPI_ADDR_SEL_4 1 + +#define FT_QSPI_SCK_DIV_128 0 +#define FT_QSPI_SCK_DIV_2 1 +#define FT_QSPI_SCK_DIV_4 2 +#define FT_QSPI_SCK_DIV_8 3 +#define FT_QSPI_SCK_DIV_16 4 +#define FT_QSPI_SCK_DIV_32 5 +#define FT_QSPI_SCK_DIV_64 6 + +#define FT_QSPI_TRANSFER_1_1_1 0 +#define FT_QSPI_TRANSFER_1_1_2 1 +#define FT_QSPI_TRANSFER_1_1_4 2 +#define FT_QSPI_TRANSFER_1_2_2 3 +#define FT_QSPI_TRANSFER_1_4_4 4 +#define FT_QSPI_TRANSFER_2_2_2 5 +#define FT_QSPI_TRANSFER_4_4_4 6 + +/* smp */ + +#define FT_SMP_EN + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_assert.c b/bsp/ft2004/libraries/bsp/standlone/ft_assert.c new file mode 100644 index 0000000000..a37c9ab5fd --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_assert.c @@ -0,0 +1,43 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-05-18 13:43:09 + * @Description:  This files is for type definition + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_types.h" +#include "ft_assert.h" + +/************* 全局变量 用于判断驱动是否出现断言 *****************/ +u32 Ft_assertStatus; + +/************** 断言是否需要无限等待,1 等待,0不等待 ******************/ +s32 Ft_assertWait = 1; + +/* 当断言发生时,将会调用此函数 */ +static Ft_assertCallback Ft_assertCallbackRoutine = NULL; + +/************************** Function Prototypes ******************************/ +void Ft_assert(FT_IN char *File, s32 Line) +{ + if (Ft_assertCallbackRoutine != NULL) + { + Ft_assertCallbackRoutine(File, Line); + } + + while (Ft_assertWait != 0) + { + } +} + +void Ft_assertSetCallBack(Ft_assertCallback Routine) +{ + Ft_assertCallbackRoutine = Routine; +} diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_assert.h b/bsp/ft2004/libraries/bsp/standlone/ft_assert.h new file mode 100644 index 0000000000..03dd543e15 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_assert.h @@ -0,0 +1,154 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-05-18 13:43:19 + * @Description:  This files is for assert function + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef Ft_assert_H +#define Ft_assert_H + +#include "ft_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define Fassert_NONE 0U +#define Fassert_OCCURRED 1U + + extern u32 Ft_assertStatus; + extern s32 Ft_assertWait; + extern void Ft_assert(FT_IN char *File, s32 Line); + + typedef void (*Ft_assertCallback)(FT_IN char *File, s32 Line); + +/** + * @name: Ft_assertVoid + * @msg: 断言函数不带返回值 + * @param {*} + * @return {*} + */ +#define Ft_assertVoid(Expression) \ + { \ + if (Expression) \ + { \ + Ft_assertStatus = Fassert_NONE; \ + } \ + else \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return; \ + } \ + } + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + */ +#define Ft_assertBool(Expression) \ + { \ + if (Expression) \ + { \ + Ft_assertStatus = Fassert_NONE; \ + } \ + else \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return FALSE; \ + } \ + } + +/** + * @name: Ft_assertZeroNum + * @msg: 断言函数带返回值0 + * @param {*} + * @return {*} + */ +#define Ft_assertZeroNum(Expression) \ + { \ + if (Expression) \ + { \ + Ft_assertStatus = Fassert_NONE; \ + } \ + else \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return FST_ASSERT_RETURN; \ + } \ + } + +/** + * @name: Ft_assertNonvoid + * @msg: 断言函数带返回值FST_ASSERT_RETURN + * @param {*} + * @return {*} + */ +#define Ft_assertNonvoid(Expression) \ + { \ + if (Expression) \ + { \ + Ft_assertStatus = Fassert_NONE; \ + } \ + else \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return FST_ASSERT_RETURN; \ + } \ + } + +/** + * @name: Ft_assertNoneReturn + * @msg: 断言函数不返回 + * @param {*} + * @return {*} + */ +#define Ft_assertNoneReturn(Expression) \ + { \ + if (Expression) \ + { \ + Ft_assertStatus = Fassert_NONE; \ + } \ + else \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + } \ + } + +#define Ft_assertVoidAlways() \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return; \ + } + +#define Ft_assertNonvoidAlways() \ + { \ + Ft_assert(__FILE__, __LINE__); \ + Ft_assertStatus = Fassert_OCCURRED; \ + return FST_ASSERT_RETURN; \ + } + + void Ft_assertSetCallBack(Ft_assertCallback routine); +#ifdef __cplusplus +} +#endif + +#endif // ! diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_cache.c b/bsp/ft2004/libraries/bsp/standlone/ft_cache.c new file mode 100644 index 0000000000..f21a20cbcb --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_cache.c @@ -0,0 +1,87 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-16 14:00:59 + * @LastEditTime: 2021-04-16 16:07:27 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "ft_cache.h" + +__STATIC_INLINE u32 FCache_cacheLineSize(void) +{ + u32 ctr; + asm volatile("mrc p15, 0, %0, c0, c0, 1" + : "=r"(ctr)); + return 4 << ((ctr >> 16) & 0xF); +} + +void FCache_cpuDcacheInvalidate(void *addr, ft_base_t size) +{ + u32 lineSize = FCache_cacheLineSize(); + u32 startAddr = (u32)addr; + u32 endAddr = (u32)addr + size + lineSize - 1; + + asm volatile("dmb" :: + : "memory"); + + startAddr &= ~(lineSize - 1); + endAddr &= ~(lineSize - 1); + + while (startAddr < endAddr) + { + asm volatile("mcr p15, 0, %0, c7, c6, 1" ::"r"(startAddr)); /* dcimvac */ + startAddr += lineSize; + } + + asm volatile("dsb" :: + : "memory"); +} + +void FCache_cpuDcacheClean(void *addr, ft_base_t size) +{ + u32 lineSize = FCache_cacheLineSize(); + u32 startAddr = (u32)addr; + u32 endAddr = (u32)addr + size + lineSize - 1; + + asm volatile("dmb" :: + : "memory"); + + startAddr &= ~(lineSize - 1); + endAddr &= ~(lineSize - 1); + + while (startAddr < endAddr) + { + asm volatile("mcr p15, 0, %0, c7, c10, 1" ::"r"(startAddr)); /* dccmvac */ + startAddr += lineSize; + } + + asm volatile("dsb" :: + : "memory"); +} + +void FCache_cpuIcacheInvalidate(void *addr, ft_base_t size) +{ + u32 lineSize = FCache_cacheLineSize(); + u32 startAddr = (u32)addr; + u32 endAddr = (u32)addr + size + lineSize - 1; + + asm volatile("dmb" :: + : "memory"); + startAddr &= ~(lineSize - 1); + endAddr &= ~(lineSize - 1); + while (startAddr < endAddr) + { + asm volatile("mcr p15, 0, %0, c7, c5, 1" ::"r"(startAddr)); /* icimvau */ + startAddr += lineSize; + } + asm volatile("dsb\n\tisb" :: + : "memory"); +} diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_cache.h b/bsp/ft2004/libraries/bsp/standlone/ft_cache.h new file mode 100644 index 0000000000..5132138636 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_cache.h @@ -0,0 +1,56 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-13 21:52:20 + * @LastEditTime: 2021-04-13 21:52:20 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_CACHE_H +#define FT_CACHE_H + +#include "ft_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + void FCache_cpuDcacheInvalidate(void *addr, ft_base_t size); + void FCache_cpuDcacheClean(void *addr, ft_base_t size); + void FCache_cpuIcacheInvalidate(void *addr, ft_base_t size); + +#ifdef __cplusplus +} +#endif + +#endif // !FT_CACHE_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_cpu.c b/bsp/ft2004/libraries/bsp/standlone/ft_cpu.c new file mode 100644 index 0000000000..affa0c196a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_cpu.c @@ -0,0 +1,170 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-20 11:32:32 + * @LastEditTime: 2021-04-20 11:32:32 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_parameters.h" +#include "ft_cpu.h" +#include "ft_assert.h" +#include "ft_printf.h" + +#ifdef FT_SMP_EN + +typedef union +{ + u32 Slock; + struct ArchTicket + { + u16 owner; + u16 next; + } Tickets; +} FCpu_Lock_t; + +struct FCpu +{ + u32 IsReady; + FCpu_Lock_t Clock; +}; + +struct FCpu FCpu_Lock = {0}; + +const u32 SoftAffiTable[4] = {0, 1, 0x100, 0x101}; + +/** + * @name: FCpu_IdGet + * @msg: In a multiprocessor system, provides an additional PE identification mechanism for scheduling + purposes. + * @return {Aff0} Affinity level 0. The most significant affinity level field, for this PE in the system. + */ +s32 FCpu_IdGet(void) +{ + s32 cpu_id; + __asm__ volatile( + "mrc p15, 0, %0, c0, c0, 5" + : "=r"(cpu_id)); + // Ft_printf("error cpu_id %x \r\n", cpu_id); + // cpu_id &= 0xf; + + switch ((cpu_id & 0xfff)) + { + case 1: + return 1; + case 0x100: + return 2; + case 0x101: + return 3; + default: + return (cpu_id & 0xf); + } +} + +s32 FCpu_AffinityGet(void) +{ + s32 AffinityId; + __asm__ volatile( + "mrc p15, 0, %0, c0, c0, 5" + : "=r"(AffinityId)); + // Ft_printf("error cpu_id %x \r\n", cpu_id); + // cpu_id &= 0xf; + return AffinityId & 0xfff; +} + +void FCpu_SpinLockInit(void) +{ + FCpu_Lock.Clock.Slock = 0; + FCpu_Lock.IsReady = FT_COMPONENT_IS_READLY; +} + +void FCpu_SpinLock(void) +{ + u32 Tmp; + u32 Newval; + FCpu_Lock_t LockVal; + Ft_assertVoid(FCpu_Lock.IsReady == FT_COMPONENT_IS_READLY); + + __asm__ __volatile__( + "pld [%0]" ::"r"(&FCpu_Lock.Clock.Slock)); + + __asm__ __volatile__( + "1: ldrex %0, [%3]\n" + " add %1, %0, %4\n" + " strex %2, %1, [%3]\n" + " teq %2, #0\n" + " bne 1b" + : "=&r"(LockVal), "=&r"(Newval), "=&r"(Tmp) + : "r"(&FCpu_Lock.Clock.Slock), "I"(1 << 16) + : "cc"); + + while (LockVal.Tickets.next != LockVal.Tickets.owner) + { + __asm__ __volatile__("wfe" :: + : "memory"); + LockVal.Tickets.owner = *(volatile unsigned short *)(&FCpu_Lock.Clock.Tickets.owner); + } + + __asm__ volatile("dmb" :: + : "memory"); +} + +void FCpu_SpinUnlock(void) +{ + Ft_assertVoid(FCpu_Lock.IsReady == FT_COMPONENT_IS_READLY); + __asm__ volatile("dmb" :: + : "memory"); + FCpu_Lock.Clock.Tickets.owner++; + __asm__ volatile("dsb ishst\nsev" :: + : "memory"); +} + +#else /*RT_USING_SMP*/ + +s32 FCpu_IdGet(void) +{ + return 0; +} +void FCpu_SpinLockInit(void) +{ + return; +} +void FCpu_SpinLock(void) +{ + return; +} +void FCpu_SpinUnlock(void) +{ + return; +} + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_cpu.h b/bsp/ft2004/libraries/bsp/standlone/ft_cpu.h new file mode 100644 index 0000000000..4dd32dbbbf --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_cpu.h @@ -0,0 +1,50 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-20 11:33:54 + * @LastEditTime: 2021-04-20 11:33:55 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_CPU_H +#define FT_CPU_H + +#include "ft_types.h" +#include "ft_error_code.h" +extern const u32 SoftAffiTable[4]; +s32 FCpu_IdGet(void); +void FCpu_SpinLockInit(void); +void FCpu_SpinLock(void); +void FCpu_SpinUnlock(void); +s32 FCpu_AffinityGet(void); + +#endif // !FT_SPIN_H diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_debug.c b/bsp/ft2004/libraries/bsp/standlone/ft_debug.c new file mode 100644 index 0000000000..fcfd9fcd73 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_debug.c @@ -0,0 +1,91 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-25 16:44:23 + * @LastEditTime: 2021-04-25 16:44:23 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ +#include "ft_debug.h" + +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') +void Ft_DumpHexByte(const u8 *ptr, ft_base_t buflen) +{ + unsigned char *buf = (unsigned char *)ptr; + int i, j; + + for (i = 0; i < buflen; i += 16) + { + Ft_printf("0x%08X: ", ptr + i); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + Ft_printf("%02X ", buf[i + j]); + else + Ft_printf(" "); + Ft_printf(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + Ft_printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + Ft_printf("\r\n"); + } +} + +void Ft_DumpHexWord(const u32 *ptr, ft_base_t buflen) +{ + u32 *buf = (u32 *)ptr; + int i, j; + buflen = buflen / 4; + for (i = 0; i < buflen; i += 4) + { + Ft_printf("0x%08X: ", ptr + i); + + for (j = 0; j < 4; j++) + { + if (i + j < buflen) + { + Ft_printf("%08X ", buf[i + j]); + } + else + { + Ft_printf(" "); + } + } + + Ft_printf(" "); + + for (j = 0; j < 16; j++) + if (i + j < buflen) + Ft_printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + Ft_printf("\r\n"); + } +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_debug.h b/bsp/ft2004/libraries/bsp/standlone/ft_debug.h new file mode 100644 index 0000000000..6ea77713f1 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_debug.h @@ -0,0 +1,78 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-25 17:19:03 + * @Description:  This files is for debug functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_DEBUG_H +#define FT_DEBUG_H + +#include "ft_printf.h" + +typedef enum +{ + FT_LOG_NONE, /*!< No log output */ + FT_LOG_ERROR, /*!< Critical errors, software module can not recover on its own */ + FT_LOG_WARN, /*!< Error conditions from which recovery measures have been taken */ + FT_LOG_INFO, /*!< Information messages which describe normal flow of events */ + FT_LOG_DEBUG, /*!< Extra information which is not necessary for normal use (values, pointers, sizes, etc). */ + FT_LOG_VERBOSE /*!< Bigger chunks of debugging information, or frequent messages which can potentially flood the output. */ +} ft_log_level_t; + +#define LOG_COLOR_BLACK "30" +#define LOG_COLOR_RED "31" +#define LOG_COLOR_GREEN "32" +#define LOG_COLOR_BROWN "33" +#define LOG_COLOR_BLUE "34" +#define LOG_COLOR_PURPLE "35" +#define LOG_COLOR_CYAN "36" +#define LOG_COLOR(COLOR) "\033[0;" COLOR "m" +#define LOG_BOLD(COLOR) "\033[1;" COLOR "m" +#define LOG_RESET_COLOR "\033[0m" +#define LOG_COLOR_E LOG_COLOR(LOG_COLOR_RED) +#define LOG_COLOR_W LOG_COLOR(LOG_COLOR_BROWN) +#define LOG_COLOR_I LOG_COLOR(LOG_COLOR_GREEN) +#define LOG_COLOR_D +#define LOG_COLOR_V + +#ifndef LOG_LOCAL_LEVEL +#define LOG_LOCAL_LEVEL FT_LOG_VERBOSE +#endif + +#define LOG_FORMAT(letter, format) LOG_COLOR_##letter " %s: " format LOG_RESET_COLOR "\r\n" + +#define PORT_KPRINTF Ft_printf + +#define LOG_EARLY_IMPL(tag, format, log_level, log_tag_letter, ...) \ + do \ + { \ + if (LOG_LOCAL_LEVEL < log_level) \ + break; \ + PORT_KPRINTF(LOG_FORMAT(log_tag_letter, format), tag, ##__VA_ARGS__); \ + } while (0) + +#define EARLY_LOGE(tag, format, ...) LOG_EARLY_IMPL(tag, format, FT_LOG_ERROR, E, ##__VA_ARGS__) +#define EARLY_LOGI(tag, format, ...) LOG_EARLY_IMPL(tag, format, FT_LOG_INFO, I, ##__VA_ARGS__) +#define EARLY_LOGD(tag, format, ...) LOG_EARLY_IMPL(tag, format, FT_LOG_DEBUG, D, ##__VA_ARGS__) +#define EARLY_LOGW(tag, format, ...) LOG_EARLY_IMPL(tag, format, FT_LOG_WARN, W, ##__VA_ARGS__) +#define EARLY_LOGV(tag, format, ...) LOG_EARLY_IMPL(tag, format, FT_LOG_VERBOSE, W, ##__VA_ARGS__) + +#define FT_DEBUG_PRINT_I(TAG, format, ...) EARLY_LOGI(TAG, format, ##__VA_ARGS__) +#define FT_DEBUG_PRINT_E(TAG, format, ...) EARLY_LOGE(TAG, format, ##__VA_ARGS__) +#define FT_DEBUG_PRINT_D(TAG, format, ...) EARLY_LOGD(TAG, format, ##__VA_ARGS__) +#define FT_DEBUG_PRINT_W(TAG, format, ...) EARLY_LOGW(TAG, format, ##__VA_ARGS__) +#define FT_DEBUG_PRINT_V(TAG, format, ...) EARLY_LOGV(TAG, format, ##__VA_ARGS__) + +#define FT_RAW_PRINTF(format, ...) PORT_KPRINTF(format, ##__VA_ARGS__) + +void Ft_DumpHexWord(const u32 *ptr, ft_base_t buflen); +void Ft_DumpHexByte(const u8 *ptr, ft_base_t buflen); +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_error_code.h b/bsp/ft2004/libraries/bsp/standlone/ft_error_code.h new file mode 100644 index 0000000000..4ac3606735 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_error_code.h @@ -0,0 +1,72 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:30 + * @LastEditTime: 2021-04-25 17:07:09 + * @Description:  This files is for error code functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_ERROR_CODE_H +#define _FT_ERROR_CODE_H + +#include "ft_status.h" +#include "ft_types.h" + +typedef ft_base_t ft_error_t; + +/* 系统错误码模块定义 */ +typedef enum _ft_errcode_module_mask +{ + errModGeneral = 0, + errModBsp, + errModeUart, + errModeI2c, + errModeGmac, + errModeSdCtrl, + errCan, + errGicV3, + errQspi, + ERR_MODE_SD_MMC, + ERR_MODE_SPI, + + errModMaxMask = 255, +} ft_errcode_module_mask_t; + +/* BSP模块的错误子模块定义 */ +typedef enum _ft_errcode_bsp_mask +{ + errBspGeneral = 0, + errBspClk, + errBspRtc, + errPort, + errBspModMaxMask = 255 +} ft_errcode_bsp_mask_t; + +#define FT_ERRCODE_SYS_MODULE_OFFSET 24 +#define FT_ERRCODE_SUB_MODULE_OFFSET 16 + +#define FT_ERRCODE_SYS_MODULE_MASK (0xff << FT_ERRCODE_SYS_MODULE_OFFSET) /* bit 24 .. 31 */ +#define FT_ERRCODE_SUB_MODULE_MASK (0xff << FT_ERRCODE_SUB_MODULE_OFFSET) /* bit 16 .. 23 */ +#define FT_ERRCODE_TAIL_VALUE_MASK (0xffff) /* bit 1 .. 15 */ + +/* Offset error code */ +#define FT_ERRCODE_OFFSET(code, offset, mask) \ + (((code) << offset) & mask) + +/* Assembly error code */ +#define FT_MAKE_ERRCODE(sys_mode, sub_mode, tail) \ + ((FT_ERRCODE_OFFSET(sys_mode, FT_ERRCODE_SYS_MODULE_OFFSET, FT_ERRCODE_SYS_MODULE_MASK)) | \ + (FT_ERRCODE_OFFSET(sub_mode, FT_ERRCODE_SUB_MODULE_OFFSET, FT_ERRCODE_SUB_MODULE_MASK)) | \ + (tail & FT_ERRCODE_TAIL_VALUE_MASK)) +#define FT_CODE_ERR FT_MAKE_ERRCODE + +#define ERR_SUCCESS FT_MAKE_ERRCODE(errModGeneral, errBspGeneral, FST_SUCCESS) /* 成功 */ +#define ERR_GENERAL FT_MAKE_ERRCODE(errModGeneral, errBspGeneral, FST_FAILURE) /* 一般错误 */ + +#endif diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_generic_timer.c b/bsp/ft2004/libraries/bsp/standlone/ft_generic_timer.c new file mode 100644 index 0000000000..4116f1c694 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_generic_timer.c @@ -0,0 +1,328 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-26 11:25:56 + * @Description:  This files is for generic timer functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_status.h" +#include "ft_types.h" +#include "ft_generic_timer.h" +#include "ft_printf.h" +#include "ft_assert.h" +#include "ft_aarch32_asm.h" +#include "ft_cpu.h" + +#define GENERICTIMER_ASM(x) __asm__ volatile(x) +#define USEVIRTUAL 0 +#define USE_ISRNUM GEN_TIMER_PHYSICAL_NOSECURE_IRQN + +static volatile u32 _TickCnt; +static volatile u32 _Tickms; + +typedef struct ft_Generictimer +{ + u64 Frequency; + u64 MaxCount; + u64 TicksPerUs; + u32 Isr_PeriodMsec; + u32 Isr_PeriodCnt; //member))) + +#endif // ! FT_LIST_H diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_math.c b/bsp/ft2004/libraries/bsp/standlone/ft_math.c new file mode 100644 index 0000000000..0195649874 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_math.c @@ -0,0 +1,43 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-28 22:15:48 + * @LastEditTime: 2021-04-28 22:15:48 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_math.h" + +u32 Ft_Abs(s32 num) +{ + return (num >= 0 ? num : -num); +} diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_math.h b/bsp/ft2004/libraries/bsp/standlone/ft_math.h new file mode 100644 index 0000000000..792d9a073c --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_math.h @@ -0,0 +1,48 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-28 22:15:19 + * @LastEditTime: 2021-04-28 22:15:19 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_MATH_H +#define FT_MATH_H + +#include "ft_types.h" + +#define FT_INT_MAX 2147483647 +#define FT_UINT_MAX (FT_INT_MAX * 2U + 1) + +u32 Ft_Abs(s32 num); + +#endif // !FT_MATH_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_mux.c b/bsp/ft2004/libraries/bsp/standlone/ft_mux.c new file mode 100644 index 0000000000..07a4e10d2f --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_mux.c @@ -0,0 +1,96 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-27 17:28:42 + * @Description:  This files is for pin mux + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "ft_mux.h" +#include "ft_io.h" +#include "ft_assert.h" +#include "ft_printf.h" + +/** + * Description: enable i2c 1 ~ 3 pin pad mux + * Date: 2021-03-24 13:34:06 + * Param: + * return {*} + * param {FT_IN u32} I2cId, i2c 0 ~ 3 + */ +void Ft_setI2cMux(FT_IN u32 I2cId) +{ + u32 RegValue; + + switch (I2cId) + { + case I2C0_ID: + /* i2c0 is by default enabled */ + break; + case I2C1_ID: + /* select i2c1 SCL, SDA mux */ + RegValue = Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG200_OFFSET); + RegValue |= (I2C1_SCL_PIN_REG200_BIT | I2C1_SDA_PIN_REG200_BIT); + Ft_out32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG200_OFFSET, RegValue); + break; + case I2C2_ID: + Ft_assertNoneReturn(0); + break; + case I2C3_ID: + Ft_assertNoneReturn(0); + break; + default: + Ft_assertNoneReturn(0); + break; + } + + return; +} + +void Ft_setSpiMux(FT_IN u32 SpiId) +{ + u32 RegValue; + + switch (SpiId) + { + case SPI0_ID: + /* spi0 is by default enabled */ + RegValue = Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG208_OFFSET); + /* clear specific bits to choose Func 0 */ + RegValue |= SPI1_PORTA5_PIN_REG208_BIT; + Ft_out32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG208_OFFSET, RegValue); + Ft_printf("bef reg 208 0x%x\r\n", Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG208_OFFSET)); + + // RegValue &= SPI0_CSN0_PIN_REG208_BIT; + // RegValue &= SPI0_SCK_PIN_REG208_BIT; + // RegValue &= SPI0_SO_PIN_REG208_BIT; + // RegValue &= SPI0_SI_PIN_REG208_BIT; + // Ft_out32(FT_PIN_DEMUX_BASE + SPI0_SI_PIN_REG208_BIT, RegValue); + break; + case SPI1_ID: + /* select spi cs, sck, so, si pin mux */ + Ft_printf("bef reg 210 0x%x\r\n", Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG210_OFFSET)); + RegValue = Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG210_OFFSET); + RegValue |= SPI1_CSN0_PIN_REG210_BIT; + Ft_out32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG210_OFFSET, RegValue); + Ft_printf("aft reg 210 0x%x\r\n", Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG210_OFFSET)); + + Ft_printf("bef reg 214 0x%x\r\n", Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG214_OFFSET)); + RegValue = Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG214_OFFSET); + RegValue |= SPI1_SCK_PIN_REG214_BIT | SPI1_SO_PIN_REG214_BIT | SPI1_SI_PIN_REG214_BIT; + Ft_out32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG214_OFFSET, RegValue); + Ft_printf("aft reg 214 0x%x\r\n", Ft_in32(FT_PIN_DEMUX_BASE + FT_PIN_DEMUX_REG214_OFFSET)); + break; + default: + break; + } + + return; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_mux.h b/bsp/ft2004/libraries/bsp/standlone/ft_mux.h new file mode 100644 index 0000000000..7d820b1b98 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_mux.h @@ -0,0 +1,87 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-27 17:27:41 + * @Description:  This files is for pin mux + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_BSP_MUX_H +#define FT_BSP_MUX_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" + +/* pad pin multi-function demu */ +#define FT_PIN_DEMUX_BASE 0x28180000 +#define FT_PIN_DEMUX_REG200_OFFSET 0x200 +#define FT_PIN_DEMUX_REG204_OFFSET 0x204 +#define FT_PIN_DEMUX_REG208_OFFSET 0x208 +#define FT_PIN_DEMUX_REG210_OFFSET 0x210 +#define FT_PIN_DEMUX_REG214_OFFSET 0x214 + +/* i2c mux function option */ +#define I2C1_SCL_PIN_REG200_MASK ((u32)0x3 << 28) /* all_pll_lock_pad [29:28] */ +#define I2C1_SCL_PIN_REG200_BIT ((u32)0x2 << 28) +#define I2C1_SDA_PIN_REG200_MASK ((u32)0x3 << 24) /* cru_clk_obv_pad [25:24] */ +#define I2C1_SDA_PIN_REG200_BIT ((u32)0x2 << 24) +#define I2C2_SCL_PIN_REG204_MASK ((u32)0x3 << 8) /* swdo_swj_pad [9: 8] */ +#define I2C2_SCL_PIN_REG204_BIT ((u32)0x2 << 8) +#define I2C2_SDA_PIN_REG204_MASK ((u32)0x3 << 6) /* tdo_swj_pad [7: 6] */ +#define I2C2_SDA_PIN_REG204_BIT ((u32)0x2 << 6) +#define I2C3_SCL_PIN_REG204_MASK ((u32)0x3 << 0) /* hdt_mb_done_state_pad [1 : 0] */ +#define I2C3_SCL_PIN_REG204_BIT ((u32)0x2 << 0) +#define I2C3_SDA_PIN_REG208_MASK ((u32)0x3 << 30) /* hdt_mb_fail_state_pad [31 : 30] */ +#define I2C3_SDA_PIN_REG208_BIT ((u32)0x2 << 30) + +//#define SPI0_CSN0_PIN_REG208_BIT ((u32)~(0x3 << 16)) +#define SPI1_PORTA5_PIN_REG208_BIT ((u32)0x1 << 16) +// #define SPI0_SCK_PIN_REG208_BIT ((u32)~(0x3 << 12)) +// #define SPI0_SO_PIN_REG208_BIT ((u32)~(0x3 << 8)) +// #define SPI0_SI_PIN_REG208_BIT ((u32)~(0x3 << 4)) + +#define SPI1_CSN0_PIN_REG210_MASK ((u32)0x3 << 0) /* uart_2_rxd_pad [1 : 0] */ +#define SPI1_CSN0_PIN_REG210_BIT ((u32)0x1 << 0) +#define SPI1_SCK_PIN_REG214_MASK ((u32)0x3 << 28) /* uart_2_txd_pad [29 : 28] */ +#define SPI1_SCK_PIN_REG214_BIT ((u32)0x1 << 28) +#define SPI1_SO_PIN_REG214_MASK ((u32)0x3 << 24) /* uart_3_rxd_pad [25 : 24] */ +#define SPI1_SO_PIN_REG214_BIT ((u32)0x1 << 24) +#define SPI1_SI_PIN_REG214_MASK ((u32)0x3 << 20) /* uart_3_txd_pad [21 : 20] */ +#define SPI1_SI_PIN_REG214_BIT ((u32)0x1 << 20) + +/* i2c ctrl instance */ +#define I2C0_ID 0 +#define I2C1_ID 1 +#define I2C2_ID 2 +#define I2C3_ID 3 + +/* spi ctrl instance */ +#define SPI0_ID 0 +#define SPI1_ID 1 + +/** + * Description: enable mux of certain i2c bus + * Date: 2021-03-30 13:58:45 + * Param: + * return {*} + * param {FT_IN u32} I2cId + */ +void Ft_setI2cMux(FT_IN u32 I2cId); +void Ft_setSpiMux(FT_IN u32 SpiId); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_printf.c b/bsp/ft2004/libraries/bsp/standlone/ft_printf.c new file mode 100644 index 0000000000..0b3de4bdf5 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_printf.c @@ -0,0 +1,97 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:29:54 + * @Description:  This files is for printf function + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "ft_printf.h" +#include "stdio.h" +#include "ft_aarch32_asm.h" + +static char print_buffer[PRINTF_BUFFER_LENGTH]; + +extern void outbyte(char byte); + +vsprintf_p Vsprintf_pFun = (vsprintf_p)vsprintf; + +void Ft_vsprintfRegister(vsprintf_p f) +{ + Vsprintf_pFun = f; +} + +void Ft_printf(const char *fmt, ...) +{ + va_list args; + size_t i; + size_t length; +#ifdef NEED_CLOSE_ISR + IRQ_DISABLE(); +#endif + va_start(args, fmt); + + length = Vsprintf_pFun(print_buffer, fmt, args); + va_end(args); + for (i = 0; i < length; i++) + { + outbyte(print_buffer[i]); + } + +#ifdef NEED_CLOSE_ISR + IRQ_ENABLE(); +#endif +} + +char *Ft_itoa(int num, char *str, int radix) +{ + char index[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; + unsigned unum; + int i = 0; + int j; + int k; + char temp; + + if (radix == 10 && num < 0) + { + unum = (unsigned)-num; + str[i++] = '-'; + } + else + { + unum = (unsigned)num; + } + + do + { + str[i++] = index[unum % (unsigned)radix]; + unum /= radix; + } while (unum); + + str[i] = '\0'; + + if (str[0] == '-') + { + k = 1; + } + else + { + k = 0; + } + + for (j = k; j <= (i - 1) / 2; j++) + { + temp = str[j]; + str[j] = str[i - 1 + k - j]; + str[i - 1 + k - j] = temp; + } + + return str; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_printf.h b/bsp/ft2004/libraries/bsp/standlone/ft_printf.h new file mode 100644 index 0000000000..96d3a45fd7 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_printf.h @@ -0,0 +1,31 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:30:52 + * @Description:  This files is for printf functions + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef Ft_printf_H +#define Ft_printf_H + +#include +#include "ft_types.h" + +// #define NEED_CLOSE_ISR +#define PRINTF_BUFFER_LENGTH 4096 + +typedef s32 (*vsprintf_p)(char *buf, const char *format, va_list arg_ptr); + +void Ft_vsprintfRegister(vsprintf_p f); +void Ft_printf(const char *fmt, ...); +char *Ft_itoa(int num, char *str, int radix); + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_psci.c b/bsp/ft2004/libraries/bsp/standlone/ft_psci.c new file mode 100644 index 0000000000..003385fc6e --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_psci.c @@ -0,0 +1,102 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-21 10:43:52 + * @LastEditTime: 2021-04-21 10:43:53 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#include "ft_psci.h" +#include "ft_smc.h" +#include "ft_cpu.h" +#include "ft_printf.h" + +#define PSCI_CPUON_NUM 0x84000003 +#define PSCI_RESET_NUM 0x84000009 + +/** + * @name: FPsci_CpuOn + * @msg: Power up a core + * @in param CpuList: Bits[24:31]: Must be zero. + * Bits[16:23] Aff2: Match Aff2 of target core MPIDR + * Bits[8:15] Aff1: Match Aff1 of target core MPIDR + * Bits[0:7] Aff0: Match Aff0 of target core MPIDR + * @in param BootAddr: a 32-bit entry point physical address (or IPA). + * @return {None} + */ +void FPsci_CpuOn(s32 CpuIdMask, u32 BootAddr) +{ + + FSmc_Data_t Input = {0}; + FSmc_Data_t Output = {0}; + Input.FunctionIdentifier = PSCI_CPUON_NUM; + + if ((1 << 0) == CpuIdMask) + { + Input.a1 = SoftAffiTable[0]; + } + else if ((1 << 1) == CpuIdMask) + { + Input.a1 = SoftAffiTable[1]; + } + else if ((1 << 2) == CpuIdMask) + { + Input.a1 = SoftAffiTable[2]; + } + else if ((1 << 3) == CpuIdMask) + { + Input.a1 = SoftAffiTable[3]; + } + else + { + return; + } + + /*input.a2 = (u32)(BootAddr >> 32);*/ + Input.a2 = (u32)(BootAddr & 0xFFFFFFFF); + FSmc_Call(&Input, &Output); + __asm__ volatile("NOP"); +} + +void FPsci_Reset(void) +{ + + FSmc_Data_t Input = {0}; + FSmc_Data_t Output = {0}; + + Input.FunctionIdentifier = PSCI_RESET_NUM; + FSmc_Call(&Input, &Output); + + __asm__ volatile("NOP"); + while (1) + ; +} diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_psci.h b/bsp/ft2004/libraries/bsp/standlone/ft_psci.h new file mode 100644 index 0000000000..ebb8ccbff2 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_psci.h @@ -0,0 +1,55 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-21 10:43:59 + * @LastEditTime: 2021-04-21 10:44:00 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_PSCI_H +#define FT_PSCI_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" + + void FPsci_CpuOn(s32 CpuIdMask, u32 BootAddr); + void FPsci_Reset(void); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_smc.S b/bsp/ft2004/libraries/bsp/standlone/ft_smc.S new file mode 100644 index 0000000000..c610350eb5 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_smc.S @@ -0,0 +1,36 @@ + + +/******************************************************************************* +* +* FSmc_Call - initiate SMC call +* +* This routine initiates SMC call which traps the processor into Monitor Mode. +* The ARM SMC Call Convetion defines that up to eight registers can be exchanged +* during an SMC call. The input parameter contains eight INT32 valeus which are +* to be passed in the SMC call; similarily the output parameter also contains +* eight INT32 values which are returned from the SMC call. +* +* \NOMANUAL +* +* RETURNS: OK +* +* void FSmc_Call +* ( +* FSmc_Data_t * input, /@ r0 - input register values @/ +* FSmc_Data_t * output /@ r1 - output register values @/ +* ) +*/ + +.arm +.align 4 +.globl FSmc_Call +FSmc_Call: + STMDB sp!, {r0-r7} /* save clobbered registers to stack */ + ldr r12, [sp, #(4 * 0)] /* get 1st argument (ptr to input struct) */ + ldmia r12, {r0-r7} /* save input argument to r0-r7 */ + smc #0 + ldr r12, [sp, #(4 * 1)] /* get 2th argument (ptr to output result) */ + stmia r12, {r0-r7} /* get output argument from r0-r7 */ + ldmfd sp!, {r0-r7} /* restore clobbered registers from stack */ + bx lr +.size FSmc_Call, .- FSmc_Call diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_smc.h b/bsp/ft2004/libraries/bsp/standlone/ft_smc.h new file mode 100644 index 0000000000..aa83d5124a --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_smc.h @@ -0,0 +1,67 @@ +/* + * @[H]: Copyright (C) 2014-2021 Phytium Technology.  All rights reserved. + * + *  Permission is hereby granted, free of charge, to any person obtaining a copy + *  of this software and associated documentation files (the "Software"), to deal + *  in the Software without restriction, including without limitation the rights + *  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + *  copies of the Software, and to permit persons to whom the Software is + *  furnished to do so, subject to the following conditions: + * + *  The above copyright notice and this permission notice shall be included in + *  all copies or substantial portions of the Software. + * + *  Use of the Software is limited solely to applications: + *  (a) running on a Phytium device, or + *  (b) that interact with a Phytium device through a bus or interconnect. + * + *  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + *  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + *  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL + *  PHYTIUM  BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, + *  WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF + *  OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + *  SOFTWARE. + * + *  Except as contained in this notice, the name of the Phytium shall not be used + *  in advertising or otherwise to promote the sale, use or other dealings in + *  this Software without prior written authorization from Phytium.  + * + * @Date: 2021-04-21 11:53:38 + * @LastEditTime: 2021-04-21 11:53:38 + * @Description:  Description of file + * @Modify History: + * * * Ver   Who        Date         Changes + * * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_SMC_H +#define FT_SMC_H + +#include "ft_types.h" + +#ifdef __cplusplus +extern "C" +{ +#endif + + typedef struct + { + /* data */ + u32 FunctionIdentifier; + u32 a1; + u32 a2; + u32 a3; + u32 a4; + u32 a5; + u32 a6; + + } FSmc_Data_t; + + void FSmc_Call(FSmc_Data_t *Input, FSmc_Data_t *Output); + +#ifdef __cplusplus +} +#endif + +#endif // !FT_SMC_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_status.h b/bsp/ft2004/libraries/bsp/standlone/ft_status.h new file mode 100644 index 0000000000..c56c6a03db --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_status.h @@ -0,0 +1,82 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:31:10 + * @Description:  This files is for definition of status + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_STATUS_H +#define FT_STATUS_H + +/** +@name Common Status Codes +*/ + +typedef enum +{ + FST_SUCCESS = 0L, + FST_FAILURE = 1L, + FST_INSTANCE_NOT_FOUND = 2L, + FST_DEVICE_BLOCK_NOT_FOUND = 3L, + FST_INVALID_VERSION = 4L, + FST_DEVICE_IS_STARTED = 5L, + FST_DEVICE_IS_STOPPED = 6L, + FST_FIFO_ERROR = 7L, /* An error occurred during an \ + operation with a FIFO such as \ + an underrun or overrun, this \ + error requires the device to \ + be reset */ + FST_RESET_ERROR = 8L, /* An error occurred which requires \ + the device to be reset */ + FST_DMA_ERROR = 9L, /* A DMA error occurred, this error \ + typically requires the device \ + using the DMA to be reset */ + FST_NOT_POLLED = 10L, /* The device is not configured for \ + polled mode operation */ + FST_FIFO_NO_ROOM = 11L, /* A FIFO did not have room to put \ + the specified data into */ + FST_BUFFER_TOO_SMALL = 12L, /* The buffer is not large enough \ + to hold the expected data */ + FST_NO_DATA = 13L, /* There was no data available */ + FST_REGISTER_ERROR = 14L, /* A register did not contain the \ + expected value */ + FST_INVALID_PARAM = 15L, /* An invalid parameter was passed \ + into the function */ + FST_NOT_SGDMA = 16L, /* The device is not configured for \ + scatter-gather DMA operation */ + FST_LOOPBACK_ERROR = 17L, /* A loopback test failed */ + FST_NO_CALLBACK = 18L, /* A callback has not yet been \ + registered */ + FST_NO_FEATURE = 19L, /* Device is not configured with \ + the requested feature */ + FST_NOT_INTERRUPT = 20L, /* Device is not configured for \ + interrupt mode operation */ + FST_DEVICE_BUSY = 21L, /* Device is busy */ + FST_ERROR_COUNT_MAX = 22L, /* The error counters of a device \ + have maxed out */ + FST_IS_STARTED = 23L, /* Used when part of device is \ + already started i.e. \ + sub channel */ + FST_IS_STOPPED = 24L, /* Used when part of device is \ + already stopped i.e. \ + sub channel */ + FST_DATA_LOST = 26L, /* Driver defined error */ + FST_RECV_ERROR = 27L, /* Generic receive error */ + FST_SEND_ERROR = 28L, /* Generic transmit error */ + FST_NOT_ENABLED = 29L, /* A requested service is not \ + available because it has not \ + been enabled */ + FST_ASSERT_RETURN = 30L, /* Assert occurs defined error */ + FST_TIMEOUT = 31L, + FST_EILSEQ = 32L, /* Illegal byte sequence. */ + FST_STATUS_MAX_VALUE = 0xffff /* Status max value */ +} Common_status; + +#endif // !FT_STATUS_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_trace.c b/bsp/ft2004/libraries/bsp/standlone/ft_trace.c new file mode 100644 index 0000000000..365fc32db0 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_trace.c @@ -0,0 +1,68 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-15 11:27:24 + * @LastEditTime: 2021-04-26 17:24:53 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +//#include "FreeRTOS.h" +//#include "task.h" +#include "ft_trace.h" +#include "ft_printf.h" + +// typedef struct MEMBLOCK_LINK +// { +// struct MEMBLOCK_LINK *pxNextFreeBlock; /*<< The next free block in the list. */ +// size_t xBlockSize; /*<< The size of the free block. */ +// } MEMBlockLink_t; +void Sdmmc_TraceHeapAlloc(const char *tag) +{ +// #ifdef FT_MEM_TRACE_ENABLED +// MEMBlockLink_t *pNextBlk = pvHeapAllocAddr(); +// FT_MEM_TRACE("<%s> next blk to alloc 0x%p, next next blk to alloc 0x%p", +// tag, +// pNextBlk, +// pNextBlk->pxNextFreeBlock); +// #endif +} + +void dump_hex(const u8 *ptr, u32 buflen, const char *tag) +{ + unsigned char *buf = (unsigned char *)ptr; + u32 i, j; + + Ft_printf("dump hex for %s\r\n", tag); + for (i = 0; i < buflen; i += 16) + { + Ft_printf("%08X: ", ptr + i); + + for (j = 0; j < 16; j++) + { + if (i + j < buflen) + { + Ft_printf("%02X ", buf[i + j]); + } + else + { + Ft_printf(" "); + } + } + Ft_printf(" "); + + for (j = 0; j < 16; j++) + { + if (i + j < buflen) + { + Ft_printf("%c", __is_print(buf[i + j]) ? buf[i + j] : '.'); + } + } + Ft_printf("\r\n"); + } +} diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_trace.h b/bsp/ft2004/libraries/bsp/standlone/ft_trace.h new file mode 100644 index 0000000000..e562d3801b --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_trace.h @@ -0,0 +1,53 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-15 11:27:14 + * @LastEditTime: 2021-04-16 13:40:55 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_TRACE_H_ +#define _FT_TRACE_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_debug.h" + +/* define debug log function */ +#define FT_MEM_TRACE_ENABLED +#define FT_MEM_TRACE_TAG "FT_MEM" +#ifdef FT_MEM_TRACE_ENABLED +#define FT_MEM_TRACE(format, ...) FT_DEBUG_PRINT_I(FT_MEM_TRACE_TAG, format, ##__VA_ARGS__) +#else +#define FT_MEM_TRACE(format, ...) +#endif + +#define FT_LOGIC_TRACE_ENABLED +#define FT_LOGIC_TRACE_TAG "FT_LOGIC" +#ifdef FT_LOGIC_TRACE_ENABLED +#define FT_LOGIC_TRACE(format, ...) FT_DEBUG_PRINT_I(FT_LOGIC_TRACE_TAG, format, ##__VA_ARGS__) +#define FT_MEM_TRACE_DUMP(buf, buflen, tag) dump_hex((buf), (buflen), (tag)) +#else +#define FT_LOGIC_TRACE(format, ...) +#define FT_MEM_TRACE_DUMP(buf, buflen, tag) +#endif + +#define __is_print(ch) ((unsigned int)((ch) - ' ') < 127u - ' ') +void dump_hex(const u8 *ptr, u32 buflen, const char *tag); +void Sdmmc_TraceHeapAlloc(const char *tag); + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/ft_types.h b/bsp/ft2004/libraries/bsp/standlone/ft_types.h new file mode 100644 index 0000000000..bd5a699b87 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/ft_types.h @@ -0,0 +1,75 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-05-11 10:18:14 + * @Description:  This files is for definition of system-level types + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_TYPES_H +#define FT_TYPES_H +#include +#include + +/* Constant Definitions */ + +#ifndef TRUE +#define TRUE 1U +#endif + +#ifndef FALSE +#define FALSE 0U +#endif + +#ifndef NULL +#define NULL 0U +#endif + +#define FT_NULL NULL + +#define FT_COMPONENT_IS_READLY 0x11111111U +#define FT_COMPONENT_IS_STARTED 0x22222222U + +#define __STATIC_INLINE static inline + +#define FT_OUT /* 表示输出参数,指针指向的值会修改,且不会读 */ +#define FT_IN /* 表示输入参数,指针指向的值不会修改 */ +#define FT_INOUT /* 表示输入输出参数,指针指向的值会修改,且会读取 */ +#define FT_IO volatile + +typedef char s8; +typedef unsigned char u8; +typedef short s16; +typedef unsigned short u16; +typedef int s32; +typedef unsigned int u32; +typedef unsigned long long u64; +typedef long long s64; +typedef int ft_base_t; +typedef ft_base_t bool_t; + +typedef intptr_t INTPTR; +typedef uintptr_t UINTPTR; +typedef ptrdiff_t PTRDIFF; + +#define __STATIC_INLINE static inline +#define LOCAL static + +#define LLSB(x) ((x)&0xff) /* 32bit word byte/word swap macros */ +#define LNLSB(x) (((x) >> 8) & 0xff) +#define LNMSB(x) (((x) >> 16) & 0xff) +#define LMSB(x) (((x) >> 24) & 0xff) +#define U32SWAP(x) ((LLSB(x) << 24) | \ + (LNLSB(x) << 16) | \ + (LNMSB(x) << 8) | \ + (LMSB(x))) + +#define FT_SWAP32(x) U32SWAP((u32)x) + +#endif // diff --git a/bsp/ft2004/libraries/bsp/standlone/inbyte.c b/bsp/ft2004/libraries/bsp/standlone/inbyte.c new file mode 100644 index 0000000000..4f49daf6cc --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/inbyte.c @@ -0,0 +1,22 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:31:34 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#include "ft_parameters.h" +#include "ft_uart_hw.h" + +u8 inbyte(void) +{ + return FUart_RecvByte(FT_STDIN_BASEADDRESS); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/bsp/standlone/outbyte.c b/bsp/ft2004/libraries/bsp/standlone/outbyte.c new file mode 100644 index 0000000000..e532ec9ed9 --- /dev/null +++ b/bsp/ft2004/libraries/bsp/standlone/outbyte.c @@ -0,0 +1,21 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 17:54:32 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_parameters.h" +#include "ft_uart_hw.h" + +void outbyte(char byte) +{ + FUart_SendByte(FT_STDOUT_BASEADDRESS, byte); +} diff --git a/bsp/ft2004/libraries/component/component.mk b/bsp/ft2004/libraries/component/component.mk new file mode 100644 index 0000000000..dcd981c6ec --- /dev/null +++ b/bsp/ft2004/libraries/component/component.mk @@ -0,0 +1,4 @@ +INCDIRS += $(DEVICE_PATH)/component/sdmmc + + +SRCDIRS += $(DEVICE_PATH)/component/sdmmc diff --git a/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.c b/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.c new file mode 100644 index 0000000000..b0f02116ee --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.c @@ -0,0 +1,426 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 16:57:05 + * @LastEditTime: 2021-04-16 13:38:21 + * @Description:  This files is for free-rtos sdmmc porting + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include "ft_assert.h" +#include "ft_parameters.h" +#include "ft_sdctrl.h" +#include "ft_sdmmc.h" +#include "ft_sdmmc_common.h" +#include "ft_sdmmc_cmd.h" +#include "diskio_sdmmc.h" + +#define SDCTR_BUFF_SIZE (512 * 2) +#define SDCTR_ALIGN_MASK 0x1f +static u8 g_DMABuf[SDCTR_BUFF_SIZE] __attribute__((aligned(SDCTR_ALIGN_MASK + 1))); + +typedef struct +{ + FtsdCtrl_t *pSdmmcReg; + Sdmmc_Host_t *pSdmmcHost; + Sdmmc_Card_t *pSdmmcCard; + bool_t isReady; +} FSdmmc_Ctrl; + +FSdmmc_Ctrl *FSdmmc_GetSdmmcCtrl(); +ft_error_t FSdmmc_InitHost(); +ft_error_t FSdmmc_SetBusWidth(int slot, size_t width); +size_t FSdmmc_GetSlotWidth(int slot); +ft_error_t FSdmmc_SetBusDdrMode(int slot, bool_t ddr_enabled); +ft_error_t FSdmmc_SetCardClk(int slot, u32 freq_khz); +ft_error_t FSdmmc_DoTransaction(int slot, Sdmmc_Cmd_t *cmdinfo); +ft_error_t FSdmmc_DeInit(void); +ft_error_t FSdmmc_IoIntEnable(int slot); +ft_error_t FSdmmc_IoIntWait(int slot, u32 timeout_ticks); + +#define SDMMC_SLOT_NO_CD GPIO_NUM_NC ///< indicates that card detect line is not used +#define SDMMC_SLOT_NO_WP GPIO_NUM_NC ///< indicates that write protect line is not used +#define SDMMC_SLOT_WIDTH_DEFAULT 0 ///< use the default width for the slot (8 for slot 0, 4 for slot 1) + +static FSdmmc_Ctrl g_SdmmcCtrl = { + .pSdmmcReg = NULL, + .pSdmmcHost = NULL, + .pSdmmcCard = NULL, + .isReady = FALSE}; + +static FtsdCtrl_t *FSdmmc_GetHWCtrl() +{ + if (NULL == g_SdmmcCtrl.pSdmmcReg) + { + g_SdmmcCtrl.pSdmmcReg = Sdmmc_HeapMalloc(sizeof(FtsdCtrl_t)); + Ft_assertNoneReturn(g_SdmmcCtrl.pSdmmcReg); + } + + return g_SdmmcCtrl.pSdmmcReg; +} + +static Sdmmc_Host_t *FSdmmc_GetHostCtrl() +{ + if (NULL == g_SdmmcCtrl.pSdmmcHost) + { + g_SdmmcCtrl.pSdmmcHost = Sdmmc_HeapMalloc(sizeof(Sdmmc_Host_t)); + Ft_assertNoneReturn(g_SdmmcCtrl.pSdmmcHost); + + g_SdmmcCtrl.pSdmmcHost->flags = SDMMC_HOST_FLAG_1BIT; + g_SdmmcCtrl.pSdmmcHost->flags &= ~SDMMC_HOST_FLAG_DDR; + g_SdmmcCtrl.pSdmmcHost->slot = FATFS_HOST_SLOT_0; + g_SdmmcCtrl.pSdmmcHost->max_freq_khz = SDMMC_FREQ_PROBING; + g_SdmmcCtrl.pSdmmcHost->io_voltage = 3.3f; + g_SdmmcCtrl.pSdmmcHost->init = &FSdmmc_InitHost; + g_SdmmcCtrl.pSdmmcHost->set_bus_width = &FSdmmc_SetBusWidth; + g_SdmmcCtrl.pSdmmcHost->get_bus_width = &FSdmmc_GetSlotWidth; + g_SdmmcCtrl.pSdmmcHost->set_bus_ddr_mode = &FSdmmc_SetBusDdrMode; + g_SdmmcCtrl.pSdmmcHost->set_card_clk = &FSdmmc_SetCardClk; + g_SdmmcCtrl.pSdmmcHost->do_transaction = &FSdmmc_DoTransaction; + g_SdmmcCtrl.pSdmmcHost->deinit = &FSdmmc_DeInit; + g_SdmmcCtrl.pSdmmcHost->io_int_enable = FSdmmc_IoIntEnable; + g_SdmmcCtrl.pSdmmcHost->io_int_wait = FSdmmc_IoIntWait; + g_SdmmcCtrl.pSdmmcHost->command_timeout_ms = 0; + } + + return g_SdmmcCtrl.pSdmmcHost; +} + +static Sdmmc_Card_t *FSdmmc_GetCardCtrl() +{ + if (NULL == g_SdmmcCtrl.pSdmmcCard) + { + g_SdmmcCtrl.pSdmmcCard = Sdmmc_HeapMalloc(sizeof(Sdmmc_Card_t)); + Ft_assertNoneReturn(g_SdmmcCtrl.pSdmmcCard); + } + + return g_SdmmcCtrl.pSdmmcCard; +} + +bool_t FSdmmc_IsSdmmcReady() +{ + return g_SdmmcCtrl.isReady; +} + +static void FSdmmc_SetSdmmcReady(bool_t status) +{ + g_SdmmcCtrl.isReady = status; +} + +/******************SD MMC for FreeRTOS*************************/ +ft_error_t FSdmmc_InitHost() +{ + FtsdCtrl_t *pRegCtrl = FSdmmc_GetHWCtrl(); + ft_error_t ret = ERR_SDMMC_OK; + + pRegCtrl->config = *(FSdCtrl_Config_t *)FSdCtrl_LookupConfig(0); + FSdCtrl_Reset(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay); + FATFS_ERROR_HANDLE_RET(ret, FsdCtrl_Init(pRegCtrl), "init host"); + + FSdCtrl_ClkFreqSetup(pRegCtrl, SDMMC_FREQ_PROBING); + + return ret; +} + +ft_error_t FSdmmc_InitCard() +{ + return Sdmmc_CardInit(FSdmmc_GetHostCtrl(), FSdmmc_GetCardCtrl()); +} + +bool_t FSdmmc_CardExist() +{ + return FSdCtrl_CardDetect(FSdmmc_GetHWCtrl()); +} + +ft_error_t FSdmmc_SetBusWidth(int slot, size_t width) +{ + ft_error_t ret = ERR_SDMMC_OK; + + return ret; +} + +size_t FSdmmc_GetSlotWidth(int slot) +{ + return 1; +} + +ft_error_t FSdmmc_SetBusDdrMode(int slot, bool_t ddr_enabled) +{ + ft_error_t ret = ERR_SDMMC_OK; + + return ret; +} + +ft_error_t FSdmmc_SetCardClk(int slot, u32 freq_khz) +{ + ft_error_t ret = ERR_SDMMC_OK; + FSdCtrl_ClkFreqSetup(FSdmmc_GetHWCtrl(), SDMMC_FREQ_DEFAULT); + return ret; +} + +static u32 FSdmmc_resp2CmdType(u32 cmdFlag) +{ + u32 cmdType = FTSDCTRL_CMD_RES_SHORT; + + switch (cmdFlag) + { + case SCT_RESP_NONE: + cmdType = FTSDCTRL_CMD_RES_NONE; + break; + case SCT_RESP_LONG: + cmdType = FTSDCTRL_CMD_RES_LONG; + break; + case SCT_RESP_SHORT: + cmdType = FTSDCTRL_CMD_RES_SHORT; + break; + default: + Ft_assertNoneReturn(0); + break; + } + + return cmdType; +} + +static ft_error_t FSdmmc_transferByDMA(FtsdCtrl_t *pRegCtrl, Sdmmc_Cmd_t *cmdInfo) +{ + u32 cmdType; + u32 *pBuf; + u32 ret = ERR_SDMMC_OK; + + Ft_assertNoneReturn((NULL != pRegCtrl) && (NULL != cmdInfo)); + + /* get respones type from command info */ + Sdmmc_TraceHeapAlloc("BEF-CMD-TYPE"); + cmdType = FSdmmc_resp2CmdType(cmdInfo->cmd_resp_type); + pBuf = cmdInfo->data; + + if (cmdInfo->flags & SCF_CMD_READ) + { + //FT_FATFS_DEBUG_I("read cmd"); + + /* read mode */ + if ((cmdInfo->flags & SCF_CMD_ADTC) && (512 > cmdInfo->blklen)) + { + Sdmmc_TraceHeapAlloc("BEF-DO-ACMD"); + FSdCtrl_DoACmd(pRegCtrl, cmdInfo->opcode, cmdType, cmdInfo->arg); + Sdmmc_Delay(10); + } + + Sdmmc_TraceHeapAlloc("BEF-READ-DATA"); + FSdCtrl_ReadData(pRegCtrl, (UINTPTR)cmdInfo->data, cmdInfo->arg, 1); + Sdmmc_TraceHeapAlloc("AFT-READ-DATA"); + //Ft_printf("0x%p %x\r\n", &cmdInfo->error, cmdInfo->error); + cmdInfo->error = FSdCtrl_WaitCmdEnd(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay, cmdType, (u32 *)cmdInfo->response); + Sdmmc_TraceHeapAlloc("AFT-READ-WAIT"); + + ret = FSdCtrl_WaitReadDataEnd(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay, 1); + Sdmmc_TraceHeapAlloc("AFT-READ-WAIT-END"); + } + else + { + /* write mode */ + //FT_FATFS_DEBUG_I("write cmd"); + + FSdCtrl_WriteData(pRegCtrl, (UINTPTR)cmdInfo->data, cmdInfo->arg, 1); + cmdInfo->error = FSdCtrl_WaitCmdEnd(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay, cmdType, (u32 *)cmdInfo->response); + + ret = FSdCtrl_WaitWriteDataEnd(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay, 1); + } + + return ret; +} + +static ft_error_t FSdmmc_doCommand(FtsdCtrl_t *pRegCtrl, Sdmmc_Cmd_t *cmdInfo) +{ + u32 cmdType; + u32 *pBuf; + u32 ret = ERR_SDMMC_OK; + + Ft_assertNoneReturn((NULL != pRegCtrl) && (NULL != cmdInfo)); + + cmdType = FSdmmc_resp2CmdType(cmdInfo->cmd_resp_type); + Sdmmc_Delay(10); + + //FT_FATFS_DEBUG_I("do cmd: type: %d, resp: 0x%x", cmdType, cmdInfo->response); + FSdCtrl_DoCmd(pRegCtrl, cmdInfo->opcode, cmdType, cmdInfo->arg); + cmdInfo->error = FSdCtrl_WaitCmdEnd(pRegCtrl, (pFtsdCtrl_delayTimer_t)Sdmmc_Delay, cmdType, (u32 *)cmdInfo->response); + ret = cmdInfo->error; + + return ret; +} + +static ft_error_t FSdmmc_sendCommand(FtsdCtrl_t *pRegCtrl, Sdmmc_Cmd_t *cmdInfo) +{ + ft_error_t ret = ERR_SDMMC_OK; + + Ft_assertNoneReturn((NULL != pRegCtrl) && (NULL != cmdInfo)); + + /* read one block */ + if (NULL != cmdInfo->data) + { + //FT_FATFS_DEBUG_I("CMD %d ,data transfer", cmdInfo->opcode); + Sdmmc_TraceHeapAlloc("BEF-DMA"); + ret = FSdmmc_transferByDMA(pRegCtrl, cmdInfo); + } + else + { + //FT_FATFS_DEBUG_I("CMD %d ,cmd transfer", cmdInfo->opcode); + Sdmmc_TraceHeapAlloc("BEF-DO-CMD"); + ret = FSdmmc_doCommand(pRegCtrl, cmdInfo); + } + + Sdmmc_TraceHeapAlloc("AFT-DO-CMD"); + return ret; +} + +static bool_t FSdmmc_cmdNeedsAutoStop(const Sdmmc_Cmd_t *cmd) +{ + /* SDMMC host needs an "auto stop" flag for the following commands: */ + return cmd->datalen > 0 && + (cmd->opcode == MMC_WRITE_BLOCK_MULTIPLE || + cmd->opcode == MMC_READ_BLOCK_MULTIPLE || + cmd->opcode == MMC_WRITE_DAT_UNTIL_STOP || + cmd->opcode == MMC_READ_DAT_UNTIL_STOP); +} + +static void FSdmmc_dumpCmdInfo(Sdmmc_Cmd_t *cmdInfo) +{ + Ft_assertNoneReturn(NULL != cmdInfo); + return; +} + +static void FSdmmc_dumpCardInfo(Sdmmc_Card_t *pCard) +{ + Ft_assertNoneReturn(NULL != pCard); + + // FT_FATFS_DEBUG_I("HOST:0x%x 0x%x, RCA:0x%x, Freq: %d, MEM: %d, SPI: %d, SDIO: %d, MMC: %d, BUS Width: %d. DDR: %d", + // pCard->host, FSdmmc_GetSdmmcCtrl(), pCard->rca, pCard->max_freq_khz, + // pCard->is_mem, Sdmmc_IsHostSpi(pCard), pCard->is_sdio, pCard->is_mmc, + // pCard->log_bus_width, pCard->is_ddr); + + return; +} + +ft_error_t FSdmmc_DoTransaction(int slot, Sdmmc_Cmd_t *cmdInfo) +{ + ft_error_t ret = ERR_SDMMC_OK; + FtsdCtrl_t *pRegCtrl = FSdmmc_GetHWCtrl(); + size_t inputBufSize; + u8 *pDataBuf = cmdInfo->data; + + Sdmmc_TraceHeapAlloc("BEF-DUMP"); + FSdmmc_dumpCmdInfo(cmdInfo); + + /* use DMA buf to transfer data */ + if ((NULL != cmdInfo->data)) + { + Ft_assertNoneReturn(cmdInfo->datalen <= SDCTR_BUFF_SIZE); + memset(g_DMABuf, 0, sizeof(g_DMABuf)); + memcpy(g_DMABuf, cmdInfo->data, cmdInfo->datalen); + cmdInfo->data = g_DMABuf; /* use dma buffer to read and write data */ + } + + ret = FSdmmc_sendCommand(pRegCtrl, cmdInfo); + + if ((NULL != cmdInfo->data) && (ERR_SDMMC_OK == ret)) + { + /* copy back data from DMA buf to the original data buf */ + //dump_hex(g_DMABuf, 512, "DMA buf"); + memcpy(pDataBuf, g_DMABuf, cmdInfo->datalen); + } + + if (FSdmmc_cmdNeedsAutoStop(cmdInfo)) + { + Ft_assertNoneReturn(0); + } + + return ret; +} + +ft_error_t FSdmmc_DeInit(void) +{ + ft_error_t ret = ERR_SDMMC_OK; + + FSdmmc_SetSdmmcReady(FALSE); + + return ret; +} + +ft_error_t FSdmmc_IoIntEnable(int slot) +{ + ft_error_t ret = ERR_SDMMC_OK; + + return ret; +} + +ft_error_t FSdmmc_IoIntWait(int slot, u32 timeout_ticks) +{ + ft_error_t ret = ERR_SDMMC_OK; + + return ret; +} + +ft_error_t FSdmmc_InitSd() +{ + ft_error_t ret = ERR_SDMMC_OK; + + if (FSdmmc_IsSdmmcReady()) + { + FT_SDMMC_DEBUG_E("try to re-init"); + return ret; + } + + FATFS_ERROR_HANDLE_RET(ret, FSdmmc_InitHost(), "init host"); + FATFS_ERROR_HANDLE_RET(ret, FSdmmc_InitCard(), "init card"); + + if (FSdmmc_CardExist()) + { + FSdmmc_SetSdmmcReady(TRUE); + } + else + { + ret = ERR_SDMMC_CARD_NOFOUND; + } + + return ret; +} + +ft_error_t FSdmmc_ReadSector(u32 startSector, u32 numSector, void *pBuf) +{ + ft_error_t ret = ERR_SDMMC_OK; + //FT_FATFS_DEBUG_I("before get ctrl"); + Sdmmc_Card_t *pCard = FSdmmc_GetCardCtrl(); + + //FT_FATFS_DEBUG_I("before check ready"); + if (!FSdmmc_IsSdmmcReady()) + { + return ERR_SDMMC_NOTYET_INIT; + } + + FATFS_ERROR_HANDLE_RET(ret, Sdmmc_ReadSectors(pCard, pBuf, startSector, numSector), "read sector"); + return ret; +} + +ft_error_t FSdmmc_WriteSector(u32 startSector, u32 numSector, void *pBuf) +{ + ft_error_t ret = ERR_SDMMC_OK; + //FT_FATFS_DEBUG_I("before get ctrl"); + Sdmmc_Card_t *pCard = FSdmmc_GetCardCtrl(); + + //FT_FATFS_DEBUG_I("before check ready"); + if (!FSdmmc_IsSdmmcReady()) + { + return ERR_SDMMC_NOTYET_INIT; + } + + FATFS_ERROR_HANDLE_RET(ret, Sdmmc_WriteSectors(pCard, pBuf, startSector, numSector), "write sector"); + //FT_FATFS_DEBUG_I("sector write finished! ret %x", ret); + return ret; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.h b/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.h new file mode 100644 index 0000000000..d00d7df32e --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/diskio_sdmmc.h @@ -0,0 +1,102 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-14 15:03:12 + * @LastEditTime: 2021-04-16 10:39:54 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_DISKIO_FATFS_H_ +#define _FT_DISKIO_FATFS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_sdctrl.h" +#include "ft_types.h" +#include "ft_error_code.h" +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc_common.h" +#include "ft_sdmmc.h" +#include "ft_debug.h" + +#define FATFS_HOST_SLOT_0 0 ///< FATFS slot 0 + +#define FATFS_ERROR_HANDLE_RET(result, expression, msg) \ + { \ + if (ERR_SDMMC_OK == (result)) { \ + result = expression; \ + if (result != ERR_SDMMC_OK) { \ + FT_FATFS_DEBUG_E("sd mmc process failed when %s err 0x%x at %s", msg, result, __func__); \ + return result; \ + } \ + } \ + } + +#define FATFS_ERROR_HANDLE_GOTO(result, expression, label, msg) \ + { \ + if (ERR_SDMMC_OK == (result)) { \ + result = expression; \ + if (result != ERR_SDMMC_OK) { \ + FT_FATFS_DEBUG_E("sd mmc process failed when %s err 0x%x at %s", msg, result, __func__); \ + goto label; \ + } \ + } \ + } + +#define FATFS_ALLOC_HANDLE_RET(result, ptr) \ + { \ + if (NULL == (ptr)) \ + { \ + result = ERR_FATFS_NO_MEM; \ + FT_FATFS_DEBUG_E("sd mmc memory allocate failed err %d at %s", result, __func__); \ + return result; \ + }\ + } + +#define FATFS_ALLOC_HANDLE_GOTO(result, ptr, label) \ + { \ + if (NULL == (ptr)) \ + { \ + result = ERR_FATFS_NO_MEM; \ + FT_FATFS_DEBUG_E("sd mmc memory allocate failed err %d at %s", result, __func__); \ + goto label; \ + }\ + } + +ft_error_t FSdmmc_ProbeSd(int slot, int width, int freq_khz, bool_t ddr); +ft_error_t FSdmmc_InitSd(); +ft_error_t FSdmmc_ReadSector(u32 startSector, u32 numSector, void *pBuf); +ft_error_t FSdmmc_WriteSector(u32 startSector, u32 numSector, void *pBuf); +bool_t FSdmmc_CardExist(); +bool_t FSdmmc_IsSdmmcReady(); + +#define FT_FATFS_DEBUG_ENABLED +#define FT_FATFS_DEBUG_TAG "FT_FATFS" +#ifdef FT_FATFS_DEBUG_ENABLED +#define FT_FATFS_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_FATFS_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_FATFS_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_FATFS_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_FATFS_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FT_FATFS_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_FATFS_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_FATFS_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_FATFS_DEBUG_V(format, ...) FT_DEBUG_PRINT_V(FT_FATFS_DEBUG_TAG, format, ##__VA_ARGS__) +#else +#define FT_FATFS_DEBUG_I(format, ...) +#define FT_FATFS_DEBUG_E(format, ...) +#define FT_FATFS_DEBUG_D(format, ...) +#define FT_FATFS_DEBUG_W(format, ...) +#define FT_FATFS_DEBUG_V(format, ...) +#endif + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc.h b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc.h new file mode 100644 index 0000000000..b46756de8f --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc.h @@ -0,0 +1,239 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 11:50:53 + * @LastEditTime: 2021-04-16 13:29:24 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_SDMMC_H_ +#define _FT_SDMMC_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_trace.h" +#include "ft_error_code.h" +#include "ft_sdmmc_defs.h" + +/** + * Decoded values from SD card Card Specific Data register + */ +typedef struct { + int csdVer; /*!< CSD structure format */ + int mmcVer; /*!< MMC version (for CID format) */ + int capacity; /*!< total number of sectors */ + int sectorSize; /*!< sector size in bytes */ + int readBlkLen; /*!< block length for reads */ + int cardCmdClass; /*!< Card Command Class for SD */ + int transSpeed; /*!< Max transfer speed */ +} Sdmmc_CSD_t; + +/** + * Decoded values from SD card Card IDentification register + */ +typedef struct { + int mfgId; /*!< manufacturer identification number */ + int oemId; /*!< OEM/product identification number */ + char name[8]; /*!< product name (MMC v1 has the longest) */ + int revision; /*!< product revision */ + int serial; /*!< product serial number */ + int date; /*!< manufacturing date */ +} Sdmmc_CID_t; + +/** + * Decoded values from SD Configuration Register + */ +typedef struct { + int sdSpec; /*!< SD Physical layer specification version, reported by card */ + int bus_width; /*!< bus widths supported by card: BIT(0) — 1-bit bus, BIT(2) — 4-bit bus */ +} Sdmmc_SCR_t; + +/** + * Decoded values of Extended Card Specific Data + */ +typedef struct { + u8 power_class; /*!< Power class used by the card */ +} Sdmmc_ExtCSD_t; + +/** + * SD/MMC command response buffer + */ +typedef u32 Sdmmc_Resp_t[4]; + +/** + * SD SWITCH_FUNC response buffer + */ +typedef struct { + u32 data[512 / 8 / sizeof(u32)]; /*!< response data */ +} Sdmmc_SwitchFuncRsp_t; + +/** + * SD/MMC command information + */ +typedef struct { + u32 opcode; /*!< SD or MMC command index */ + u32 arg; /*!< SD/MMC command argument */ + Sdmmc_Resp_t response; /*!< response buffer */ + //u32 response[4]; + void* data; /*!< buffer to send or read into */ + size_t datalen; /*!< length of data buffer */ + size_t blklen; /*!< block length */ + int flags; /*!< see below */ +/** @cond */ +#define SCF_ITSDONE 0x0001 /*!< command is complete */ +#define SCF_CMD(flags) ((flags) & 0x00f0) +#define SCF_CMD_AC 0x0000 +#define SCF_CMD_ADTC 0x0010 +#define SCF_CMD_BC 0x0020 +#define SCF_CMD_BCR 0x0030 +#define SCF_CMD_READ 0x0040 /*!< read command (data expected) */ +#define SCF_RSP_BSY 0x0100 +#define SCF_RSP_136 0x0200 +#define SCF_RSP_CRC 0x0400 +#define SCF_RSP_IDX 0x0800 +#define SCF_RSP_PRESENT 0x1000 +/* response types */ +#define SCF_RSP_R0 0 /*!< none */ +#define SCF_RSP_R1 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R1B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R2 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_136) +#define SCF_RSP_R3 (SCF_RSP_PRESENT) +#define SCF_RSP_R4 (SCF_RSP_PRESENT) +#define SCF_RSP_R5 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R5B (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX|SCF_RSP_BSY) +#define SCF_RSP_R6 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +#define SCF_RSP_R7 (SCF_RSP_PRESENT|SCF_RSP_CRC|SCF_RSP_IDX) +/* special flags */ +#define SCF_WAIT_BUSY 0x2000 /*!< Wait for completion of card busy signal before returning */ +/** @endcond */ + u32 cmd_resp_type; /*!< command respones type */ +#define SCT_RESP_NONE 0x01 +#define SCT_RESP_LONG 0x02 +#define SCT_RESP_SHORT 0x04 + ft_error_t error; /*!< error returned from transfer */ + int timeout_ms; /*!< response timeout, in milliseconds */ +} Sdmmc_Cmd_t; + +/** + * SD/MMC Host description + * + * This structure defines properties of SD/MMC host and functions + * of SD/MMC host which can be used by upper layers. + */ +typedef struct { + u32 flags; /*!< flags defining host properties */ +#define SDMMC_HOST_FLAG_1BIT BIT(0) /*!< host supports 1-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_4BIT BIT(1) /*!< host supports 4-line SD and MMC protocol */ +#define SDMMC_HOST_FLAG_8BIT BIT(2) /*!< host supports 8-line MMC protocol */ +#define SDMMC_HOST_FLAG_SPI BIT(3) /*!< host supports SPI protocol */ +#define SDMMC_HOST_FLAG_DDR BIT(4) /*!< host supports DDR mode for SD/MMC */ + int slot; /*!< slot number, to be passed to host functions */ + int max_freq_khz; /*!< max frequency supported by the host */ +#define SDMMC_FREQ_DEFAULT 20000 /*!< SD/MMC Default speed (limited by clock divider) */ +#define SDMMC_FREQ_HIGHSPEED 40000 /*!< SD High speed (limited by clock divider) */ +#define SDMMC_FREQ_PROBING 400 /*!< SD/MMC probing speed */ +#define SDMMC_FREQ_52M 52000 /*!< MMC 52MHz speed */ +#define SDMMC_FREQ_26M 26000 /*!< MMC 26MHz speed */ + float io_voltage; /*!< I/O voltage used by the controller (voltage switching is not supported) */ + ft_error_t (*init)(void); /*!< Host function to initialize the driver */ + ft_error_t (*set_bus_width)(int slot, size_t width); /*!< host function to set bus width */ + size_t (*get_bus_width)(int slot); /*!< host function to get bus width */ + ft_error_t (*set_bus_ddr_mode)(int slot, bool_t ddr_enable); /*!< host function to set DDR mode */ + ft_error_t (*set_card_clk)(int slot, u32 freq_khz); /*!< host function to set card clock frequency */ + ft_error_t (*do_transaction)(int slot, Sdmmc_Cmd_t* cmdinfo); /*!< host function to do a transaction */ + ft_error_t (*deinit)(void); /*!< host function to deinitialize the driver */ + ft_error_t (*io_int_enable)(int slot); /*!< Host function to enable SDIO interrupt line */ + ft_error_t (*io_int_wait)(int slot, u32 timeout_ticks); /*!< Host function to wait for SDIO interrupt line to be active */ + int command_timeout_ms; /*!< timeout, in milliseconds, of a single command. Set to 0 to use the default value. */ +} Sdmmc_Host_t; + +/** + * SD/MMC card information structure + */ +typedef struct { + Sdmmc_Host_t host; /*!< Host with which the card is associated */ + u32 ocr; /*!< OCR (Operation Conditions Register) value */ + union { + Sdmmc_CID_t cid; /*!< decoded CID (Card IDentification) register value */ + Sdmmc_Resp_t raw_cid; /*!< raw CID of MMC card to be decoded + after the CSD is fetched in the data transfer mode*/ + }; + Sdmmc_CSD_t csd; /*!< decoded CSD (Card-Specific Data) register value */ + Sdmmc_SCR_t scr; /*!< decoded SCR (SD card Configuration Register) value */ + Sdmmc_ExtCSD_t ext_csd; /*!< decoded EXT_CSD (Extended Card Specific Data) register value */ + u16 rca; /*!< RCA (Relative Card Address) */ + u16 max_freq_khz; /*!< Maximum frequency, in kHz, supported by the card */ + u32 is_mem : 1; /*!< Bit indicates if the card is a memory card */ + u32 is_sdio : 1; /*!< Bit indicates if the card is an IO card */ + u32 is_mmc : 1; /*!< Bit indicates if the card is MMC */ + u32 num_io_functions : 3; /*!< If is_sdio is 1, contains the number of IO functions on the card */ + u32 log_bus_width : 2; /*!< log2(bus width supported by card) */ + u32 is_ddr : 1; /*!< Card supports DDR mode */ + u32 reserved : 23; /*!< Reserved for future expansion */ +} Sdmmc_Card_t; + +/* +* Platform configuration for SDMMC +*/ +typedef struct +{ + void (*task_delay)(u32 ticks_to_delay); /* os-depends tick time delay method */ + void *(*heap_alloc)(size_t size); /* os-depends heap allocate method */ + void (*heap_free)(void *ptr); /* os-depends heap free method */ + bool_t (*check_dma_capable)(const void *ptr); /* os-depends memory allign check before dma */ + void (*printf)(const char *fmt, ...); /* os-depends printf method */ + u32 tick_period; /* os-depends tick time period */ + void *event_queue; /* os-depends event queue */ + void *io_intr_event; /* os-depends event semphere */ +} Sdmmc_OSImpl_t; +const Sdmmc_OSImpl_t *get_sdmmc_os_impl(); +static inline void Sdmmc_Printf(const char *fmt, ...) +{ + get_sdmmc_os_impl()->printf(fmt); +} + +static inline void *Sdmmc_HeapMalloc(size_t size) +{ + //FT_MEM_TRACE("size %d", size); + Sdmmc_TraceHeapAlloc("BEF-MALLOC"); + void *pMen = get_sdmmc_os_impl()->heap_alloc(size); + //FT_MEM_TRACE("malloc 0x%p size %d", pMen); + return pMen; +} + +static inline void Sdmmc_HeapFree(void *ptr) +{ + if (NULL != ptr) + { + //FT_MEM_TRACE("free 0x%p", ptr); + Sdmmc_TraceHeapAlloc("BEF-FREE"); + get_sdmmc_os_impl()->heap_free(ptr); + } + return; +} + +static inline void Sdmmc_Delay(u32 tick) +{ + get_sdmmc_os_impl()->task_delay(tick); +} + +static inline bool_t Sdmmc_DMACapable(const void *ptr) +{ + get_sdmmc_os_impl()->check_dma_capable(ptr); +} + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.c new file mode 100644 index 0000000000..7236ae5f9f --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.c @@ -0,0 +1,610 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 11:51:03 + * @LastEditTime: 2021-04-15 13:39:44 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc_cmd.h" +#include "ft_sdmmc_common.h" + +static const char* TAG = "sdmmc_cmd"; + +ft_error_t Sdmmc_SendCmd(Sdmmc_Card_t* card, Sdmmc_Cmd_t* cmd) +{ + if (card->host.command_timeout_ms != 0) { + cmd->timeout_ms = card->host.command_timeout_ms; + } else if (cmd->timeout_ms == 0) { + cmd->timeout_ms = SDMMC_DEFAULT_CMD_TIMEOUT_MS; + } + + int slot = card->host.slot; + Sdmmc_TraceHeapAlloc("BEF-TRANS"); + FT_SDMMC_DEBUG_V( "sending cmd slot=%d op=%d arg=%x flags=%x data=%p blklen=%d datalen=%d timeout=%d", + slot, cmd->opcode, cmd->arg, cmd->flags, cmd->data, cmd->blklen, cmd->datalen, cmd->timeout_ms); + //FT_SDMMC_DEBUG_I("transaction handler %x", (*card->host.do_transaction)); + ft_error_t err = (*card->host.do_transaction)(slot, cmd); + if (err != 0) { + FT_SDMMC_DEBUG_E("cmd=%d, sdmmc_req_run returned 0x%x", cmd->opcode, err); + return err; + } + Sdmmc_TraceHeapAlloc("AFT-TRANS"); + int state = MMC_R1_CURRENT_STATE(cmd->response); + FT_SDMMC_DEBUG_V( "CMD %d type %d arg 0x%x \r\n response--------- \r\n %08x \r\n %08x \r\n %08x \r\n %08x \r\n err=0x%x state=%d \r\n ---------", + cmd->opcode, cmd->cmd_resp_type, cmd->arg, + cmd->response[0], + cmd->response[1], + cmd->response[2], + cmd->response[3], + cmd->error, + state); + return cmd->error; +} + +ft_error_t Sdmmc_sendAppCmd(Sdmmc_Card_t* card, Sdmmc_Cmd_t* cmd) +{ + Sdmmc_Cmd_t app_cmd = { + .opcode = MMC_APP_CMD, + .flags = SCF_CMD_AC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + .arg = MMC_ARG_RCA(card->rca) + }; + Sdmmc_TraceHeapAlloc("BEF-SEND"); + ft_error_t err = Sdmmc_SendCmd(card, &app_cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + // Check APP_CMD status bit (only in SD mode) + Sdmmc_TraceHeapAlloc("AFT-SEND"); + if (!Sdmmc_IsHostSpi(card) && !(MMC_R1(app_cmd.response) & MMC_R1_APP_CMD)) { + FT_SDMMC_DEBUG_E("card doesn't support APP_CMD"); + return ERR_SDMMC_NOT_SUPPORTED; + } + return Sdmmc_SendCmd(card, cmd); +} + + +ft_error_t Sdmmc_SendCmdGoIdleState(Sdmmc_Card_t* card) +{ + Sdmmc_Cmd_t cmd = { + .opcode = MMC_GO_IDLE_STATE, + .flags = SCF_CMD_BC | SCF_RSP_R0, + .cmd_resp_type = SCT_RESP_NONE + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (Sdmmc_IsHostSpi(card)) { + /* To enter SPI mode, CMD0 needs to be sent twice (see figure 4-1 in + * SD Simplified spec v4.10). Some cards enter SD mode on first CMD0, + * so don't expect the above command to succeed. + * SCF_RSP_R1 flag below tells the lower layer to expect correct R1 + * response (in SPI mode). + */ + (void) err; + Sdmmc_Delay(SDMMC_GO_IDLE_DELAY_MS / get_sdmmc_os_impl()->tick_period); + + cmd.flags |= SCF_RSP_R1; + err = Sdmmc_SendCmd(card, &cmd); + } + if (err == ERR_SDMMC_OK) { + Sdmmc_Delay(SDMMC_GO_IDLE_DELAY_MS / get_sdmmc_os_impl()->tick_period); + } + return err; +} + + +ft_error_t Sdmmc_SendCmdSendIfCond(Sdmmc_Card_t* card, u32 ocr) +{ + const u8 pattern = 0xaa; /* any pattern will do here */ + Sdmmc_Cmd_t cmd = { + .opcode = SD_SEND_IF_COND, + .arg = (((ocr & SD_OCR_VOL_MASK) != 0) << 8) | pattern, + .flags = SCF_CMD_BCR | SCF_RSP_R7, + .cmd_resp_type = SCT_RESP_SHORT, + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + u8 response = cmd.response[0] & 0xff; + if (response != pattern) { + FT_SDMMC_DEBUG_E("%s: received=0x%x expected=0x%x", __func__, response, pattern); + return ERR_SDMMC_INVALID_RESPONSE; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdSendOpCond(Sdmmc_Card_t* card, u32 ocr, u32 *ocrp) +{ + ft_error_t err; + + Sdmmc_Cmd_t cmd = { + .arg = ocr, + .flags = SCF_CMD_BCR | SCF_RSP_R3, + .cmd_resp_type = SCT_RESP_SHORT, + .opcode = SD_APP_OP_COND + }; + int nretries = SDMMC_SEND_OP_COND_MAX_RETRIES; + int err_cnt = SDMMC_SEND_OP_COND_MAX_ERRORS; + for (; nretries != 0; --nretries) { + bzero(&cmd, sizeof cmd); + cmd.arg = ocr; + cmd.flags = SCF_CMD_BCR | SCF_RSP_R3; + if (!card->is_mmc) { /* SD mode */ + cmd.opcode = SD_APP_OP_COND; + cmd.cmd_resp_type = SCT_RESP_SHORT; + err = Sdmmc_sendAppCmd(card, &cmd); + } else { /* MMC mode */ + cmd.arg &= ~MMC_OCR_ACCESS_MODE_MASK; + cmd.arg |= MMC_OCR_SECTOR_MODE; + cmd.opcode = MMC_SEND_OP_COND; + cmd.cmd_resp_type = SCT_RESP_SHORT; + err = Sdmmc_SendCmd(card, &cmd); + } + + if (err != ERR_SDMMC_OK) { + if (--err_cnt == 0) { + FT_SDMMC_DEBUG_E("%s: Sdmmc_sendAppCmd err=0x%x", __func__, err); + return err; + } else { + FT_SDMMC_DEBUG_V( "%s: ignoring err=0x%x", __func__, err); + continue; + } + } + // In SD protocol, card sets MEM_READY bit in OCR when it is ready. + // In SPI protocol, card clears IDLE_STATE bit in R1 response. + if (!Sdmmc_IsHostSpi(card)) { + if ((MMC_R3(cmd.response) & MMC_OCR_MEM_READY) || + ocr == 0) { + break; + } + } else { + if ((SD_SPI_R1(cmd.response) & SD_SPI_R1_IDLE_STATE) == 0) { + break; + } + } + Sdmmc_Delay(10 / get_sdmmc_os_impl()->tick_period); + } + if (nretries == 0) { + return ERR_SDMMC_TIMEOUT; + } + if (ocrp) { + *ocrp = MMC_R3(cmd.response); + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdReadOCR(Sdmmc_Card_t *card, u32 *ocrp) +{ + assert(ocrp); + Sdmmc_Cmd_t cmd = { + .opcode = SD_READ_OCR, + .flags = SCF_CMD_BCR | SCF_RSP_R2, + .cmd_resp_type = SCT_RESP_LONG + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + *ocrp = SD_SPI_R3(cmd.response); + return ERR_SDMMC_OK; +} + + +ft_error_t Sdmmc_SendCmdAllSendCID(Sdmmc_Card_t* card, Sdmmc_Resp_t* out_raw_cid) +{ + assert(out_raw_cid); + Sdmmc_Cmd_t cmd = { + .opcode = MMC_ALL_SEND_CID, + .flags = SCF_CMD_BCR | SCF_RSP_R2, + .cmd_resp_type = SCT_RESP_LONG + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + memcpy(out_raw_cid, &cmd.response, sizeof(Sdmmc_Resp_t)); + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdSendCID(Sdmmc_Card_t *card, Sdmmc_CID_t *out_cid) +{ + assert(out_cid); + assert(Sdmmc_IsHostSpi(card) && "SEND_CID should only be used in SPI mode"); + assert(!card->is_mmc && "MMC cards are not supported in SPI mode"); + Sdmmc_Resp_t buf; + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SEND_CID, + .flags = SCF_CMD_READ | SCF_CMD_ADTC, + .cmd_resp_type = SCT_RESP_LONG, + .arg = 0, + .data = &buf[0], + .datalen = sizeof(buf) + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + Sdmmc_FilpByteOrder(buf, sizeof(buf)); + return Sdmmc_DecodeCID(buf, out_cid); +} + + +ft_error_t Sdmmc_SendCmdSetRelativeAddr(Sdmmc_Card_t* card, uint16_t* out_rca) +{ + assert(out_rca); + Sdmmc_Cmd_t cmd = { + .opcode = SD_SEND_RELATIVE_ADDR, + .flags = SCF_CMD_BCR | SCF_RSP_R6, + .cmd_resp_type = SCT_RESP_SHORT + }; + + /* MMC cards expect us to set the RCA. + * Set RCA to 1 since we don't support multiple cards on the same bus, for now. + */ + uint16_t mmc_rca = 1; + if (card->is_mmc) { + cmd.arg = MMC_ARG_RCA(mmc_rca); + } + + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("send CMD RCA failed!!"); + return err; + } + + *out_rca = (card->is_mmc) ? mmc_rca : SD_R6_RCA(cmd.response); + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdSetBlkLen(Sdmmc_Card_t* card, Sdmmc_CSD_t* csd) +{ + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SET_BLOCKLEN, + .arg = csd->sectorSize, + .flags = SCF_CMD_AC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT + }; + return Sdmmc_SendCmd(card, &cmd); +} + +ft_error_t Sdmmc_SendCmdSendCSD(Sdmmc_Card_t* card, Sdmmc_CSD_t* out_csd) +{ + /* The trick with SEND_CSD is that in SPI mode, it acts as a data read + * command, while in SD mode it is an AC command with R2 response. + */ + Sdmmc_Resp_t spi_buf; + const bool_t is_spi = Sdmmc_IsHostSpi(card); + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SEND_CSD, + .arg = is_spi ? 0 : MMC_ARG_RCA(card->rca), + .flags = is_spi ? (SCF_CMD_READ | SCF_CMD_ADTC | SCF_RSP_R1) : + (SCF_CMD_AC | SCF_RSP_R2), + .cmd_resp_type = SCT_RESP_LONG, + .data = is_spi ? &spi_buf[0] : 0, + .datalen = is_spi ? sizeof(spi_buf) : 0, + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + u32* ptr = cmd.response; + if (is_spi) { + Sdmmc_FilpByteOrder(spi_buf, sizeof(spi_buf)); + ptr = spi_buf; + } + if (card->is_mmc) { + err = Sdmmc_MMCDecodeCSD(cmd.response, out_csd); + } else { + err = Sdmmc_DecodeCSD(ptr, out_csd); + } + return err; +} + +ft_error_t Sdmmc_SendCmdSelectCard(Sdmmc_Card_t* card, u32 rca) +{ + /* Don't expect to see a response when de-selecting a card */ + u32 response = (rca == 0) ? 0 : SCF_RSP_R1; + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SELECT_CARD, + .arg = MMC_ARG_RCA(rca), + .flags = SCF_CMD_AC | response, + .cmd_resp_type = SCT_RESP_SHORT + }; + return Sdmmc_SendCmd(card, &cmd); +} + +ft_error_t Sdmmc_SendCmdSendSCR(Sdmmc_Card_t* card, Sdmmc_SCR_t *out_scr) +{ + size_t datalen = 8; + u32* buf = (u32*) Sdmmc_HeapMalloc(datalen); + if (buf == NULL) { + return ERR_SDMMC_NO_MEM; + } + Sdmmc_Cmd_t cmd = { + .data = buf, + .datalen = datalen, + .blklen = datalen, + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + .opcode = SD_APP_SEND_SCR + }; + Sdmmc_TraceHeapAlloc("BEF-APP-CMD"); + ft_error_t err = Sdmmc_sendAppCmd(card, &cmd); + Sdmmc_TraceHeapAlloc("AFT-APP-CMD"); + if (err == ERR_SDMMC_OK) { + err = Sdmmc_DecodeSCR(buf, out_scr); + + // if (ERR_SDMMC_OK != err){ + // //dump_hex((u8*)buf, datalen * sizeof(u32)); + // } + + Sdmmc_TraceHeapAlloc("AFT-DECODE-SCR"); + FT_SDMMC_DEBUG_E("result: %x, bus width: %d, sd spec: %d", err, + out_scr->bus_width, out_scr->sdSpec); + } + Sdmmc_HeapFree(buf); + return err; +} + +ft_error_t Sdmmc_SendCmdSetBusWidth(Sdmmc_Card_t* card, int width) +{ + Sdmmc_Cmd_t cmd = { + .opcode = SD_APP_SET_BUS_WIDTH, + .flags = SCF_RSP_R1 | SCF_CMD_AC, + .cmd_resp_type = SCT_RESP_SHORT, + .arg = (width == 4) ? SD_ARG_BUS_WIDTH_4 : SD_ARG_BUS_WIDTH_1, + }; + + return Sdmmc_sendAppCmd(card, &cmd); +} + +ft_error_t Sdmmc_SendCmdCrcOnOff(Sdmmc_Card_t* card, bool_t crc_enable) +{ + assert(Sdmmc_IsHostSpi(card) && "CRC_ON_OFF can only be used in SPI mode"); + Sdmmc_Cmd_t cmd = { + .opcode = SD_CRC_ON_OFF, + .arg = crc_enable ? 1 : 0, + .flags = SCF_CMD_AC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT + }; + return Sdmmc_SendCmd(card, &cmd); +} + +ft_error_t Sdmmc_SendCmdSendStatus(Sdmmc_Card_t* card, u32* out_status) +{ + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SEND_STATUS, + .arg = MMC_ARG_RCA(card->rca), + .flags = SCF_CMD_AC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + return err; + } + if (out_status) { + *out_status = MMC_R1(cmd.response); + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdSwitch(Sdmmc_Card_t* card) +{ + size_t datalen = 64; + ft_error_t err; + u32* buf = (u32*) Sdmmc_HeapMalloc(datalen); + if (buf == NULL) { + return ERR_SDMMC_NO_MEM; + } + //memset(buf, 0, sizeof(u32) * datalen); + // Sdmmc_Cmd_t cmd = { + // .opcode = MMC_SWITCH, + // .arg = 0x00FFFFF1, + // .flags = SCF_CMD_ADTC | SCF_RSP_R1, + // .cmd_resp_type = SCT_RESP_SHORT, + // .data = buf, + // .datalen = datalen, + // .blklen = datalen + // }; + // ft_error_t err = Sdmmc_SendCmd(card, &cmd); + // if (ERR_SDMMC_OK != err){ + // goto _EXIT; + // } + + // memset(buf, 0, sizeof(u32) * datalen); + // Sdmmc_Cmd_t cmd2 = { + // .opcode = MMC_SWITCH, + // .arg = 0x80FFFFF1, + // .flags = SCF_CMD_ADTC | SCF_RSP_R1, + // .cmd_resp_type = SCT_RESP_SHORT, + // .data = buf, + // .datalen = datalen, + // .blklen = datalen + // }; + // err = Sdmmc_SendCmd(card, &cmd2); + // if (ERR_SDMMC_OK != err){ + // goto _EXIT; + // } + + memset(buf, 0, datalen); + Sdmmc_Cmd_t cmd3 = { + .opcode = MMC_SWITCH, + .arg = 0x00000002, + .flags = SCF_CMD_ADTC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + //.data = buf, + //.datalen = datalen, + //.blklen = datalen + }; + err = Sdmmc_sendAppCmd(card, &cmd3); + +_EXIT: + Sdmmc_HeapFree(buf); + return err; +} + +ft_error_t Sdmmc_WriteSectors(Sdmmc_Card_t* card, const void* src, + size_t start_block, size_t block_count) +{ + ft_error_t err = ERR_SDMMC_OK; + size_t block_size = card->csd.sectorSize; + if ((Sdmmc_DMACapable(src)) && ((intptr_t)src % 4 == 0)) { + err = Sdmmc_WriteSectorsDMA(card, src, start_block, block_count); + } else { + // SDMMC peripheral needs DMA-capable buffers. Split the write into + // separate single block writes, if needed, and allocate a temporary + // DMA-capable buffer. + void* tmp_buf = Sdmmc_HeapMalloc(block_size); + if (tmp_buf == NULL) { + return ERR_SDMMC_NO_MEM; + } + const u8* cur_src = (const u8*) src; + for (size_t i = 0; i < block_count; ++i) { + memcpy(tmp_buf, cur_src, block_size); + cur_src += block_size; + err = Sdmmc_WriteSectorsDMA(card, tmp_buf, start_block + i, 1); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_D("%s: error 0x%x writing block %d+%d", + __func__, err, start_block, i); + break; + } + } + FT_SDMMC_DEBUG_D("before free"); + Sdmmc_HeapFree(tmp_buf); + } + + return err; +} + +ft_error_t Sdmmc_WriteSectorsDMA(Sdmmc_Card_t* card, const void* src, + size_t start_block, size_t block_count) +{ + if (start_block + block_count > (size_t)card->csd.capacity) { + return ERR_SDMMC_INVALID_SIZE; + } + size_t block_size = card->csd.sectorSize; + Sdmmc_Cmd_t cmd = { + .flags = SCF_CMD_ADTC | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + .blklen = block_size, + .data = (void*) src, + .datalen = block_count * block_size, + .timeout_ms = SDMMC_WRITE_CMD_TIMEOUT_MS + }; + if (block_count == 1) { + cmd.opcode = MMC_WRITE_BLOCK_SINGLE; + } else { + cmd.opcode = MMC_WRITE_BLOCK_MULTIPLE; + } + if (card->ocr & SD_OCR_SDHC_CAP) { + cmd.arg = start_block; + } else { + cmd.arg = start_block * block_size; + } + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: Sdmmc_SendCmd returned 0x%x", __func__, err); + return err; + } + u32 status = 0; + size_t count = 0; + while (!Sdmmc_IsHostSpi(card) && !(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + err = Sdmmc_SendCmdSendStatus(card, &status); + if (err != ERR_SDMMC_OK) { + return err; + } + if (++count % 10 == 0) { + FT_SDMMC_DEBUG_V( "waiting for card to become ready (%d)", count); + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_ReadSectors(Sdmmc_Card_t* card, void* dst, + size_t start_block, size_t block_count) +{ + ft_error_t err = ERR_SDMMC_OK; + size_t block_size = card->csd.sectorSize; + FT_SDMMC_DEBUG_I("Sdmmc_ReadSectors dst 0x%x", dst); + if (Sdmmc_DMACapable(dst) && (intptr_t)dst % 4 == 0) { + FT_SDMMC_DEBUG_I("1"); + err = Sdmmc_ReadSectorsDMA(card, dst, start_block, block_count); + } else { + // SDMMC peripheral needs DMA-capable buffers. Split the read into + // separate single block reads, if needed, and allocate a temporary + // DMA-capable buffer. + FT_SDMMC_DEBUG_I("2"); + void* tmp_buf = Sdmmc_HeapMalloc(block_size); + if (tmp_buf == NULL) { + return ERR_SDMMC_NO_MEM; + } + u8* cur_dst = (u8*) dst; + for (size_t i = 0; i < block_count; ++i) { + err = Sdmmc_ReadSectorsDMA(card, tmp_buf, start_block + i, 1); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_D("%s: error 0x%x writing block %d+%d", + __func__, err, start_block, i); + break; + } + memcpy(cur_dst, tmp_buf, block_size); + cur_dst += block_size; + } + Sdmmc_HeapFree(tmp_buf); + } + return err; +} + +ft_error_t Sdmmc_ReadSectorsDMA(Sdmmc_Card_t* card, void* dst, + size_t start_block, size_t block_count) +{ + if (start_block + block_count > (size_t)card->csd.capacity) { + return ERR_SDMMC_INVALID_SIZE; + } + size_t block_size = card->csd.sectorSize; + Sdmmc_Cmd_t cmd = { + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + .blklen = block_size, + .data = (void*) dst, + .datalen = block_count * block_size + }; + if (block_count == 1) { + cmd.opcode = MMC_READ_BLOCK_SINGLE; + } else { + cmd.opcode = MMC_READ_BLOCK_MULTIPLE; + } + if (card->ocr & SD_OCR_SDHC_CAP) { + cmd.arg = start_block; + } else { + cmd.arg = start_block * block_size; + } + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: Sdmmc_SendCmd returned 0x%x", __func__, err); + return err; + } + u32 status = 0; + size_t count = 0; + while (!Sdmmc_IsHostSpi(card) && !(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + err = Sdmmc_SendCmdSendStatus(card, &status); + if (err != ERR_SDMMC_OK) { + return err; + } + if (++count % 10 == 0) { + FT_SDMMC_DEBUG_V( "waiting for card to become ready (%d)", count); + } + } + return ERR_SDMMC_OK; +} + diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.h b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.h new file mode 100644 index 0000000000..d725b02b6f --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_cmd.h @@ -0,0 +1,88 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 13:27:51 + * @LastEditTime: 2021-04-14 10:44:46 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_SDMMC_CMD_H_ +#define _FT_SDMMC_CMD_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_error_code.h" +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc.h" + +#define FILE void + +/** + * Probe and initialize SD/MMC card using given host + * + * @note Only SD cards (SDSC and SDHC/SDXC) are supported now. + * Support for MMC/eMMC cards will be added later. + * + * @param host pointer to structure defining host controller + * @param out_card pointer to structure which will receive information + * about the card when the function completes + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +ft_error_t Sdmmc_CardInit(const Sdmmc_Host_t* host, + Sdmmc_Card_t* out_card); + +/** + * @brief Print information about the card to a stream + * @param stream stream obtained using fopen or fdopen + * @param card card information structure initialized using Sdmmc_CardInit + */ +void Sdmmc_CardPrintInfo(FILE* stream, const Sdmmc_Card_t* card); + +/** + * Write given number of sectors to SD/MMC card + * + * @param card pointer to card information structure previously initialized + * using Sdmmc_CardInit + * @param src pointer to data buffer to read data from; data size must be + * equal to sector_count * card->csd.sectorSize + * @param start_sector sector where to start writing + * @param sector_count number of sectors to write + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +ft_error_t Sdmmc_WriteSectors(Sdmmc_Card_t* card, const void* src, + size_t start_sector, size_t sector_count); + +/** + * Write given number of sectors to SD/MMC card + * + * @param card pointer to card information structure previously initialized + * using Sdmmc_CardInit + * @param dst pointer to data buffer to write into; buffer size must be + * at least sector_count * card->csd.sectorSize + * @param start_sector sector where to start reading + * @param sector_count number of sectors to read + * @return + * - ESP_OK on success + * - One of the error codes from SDMMC host controller + */ +ft_error_t Sdmmc_ReadSectors(Sdmmc_Card_t* card, void* dst, + size_t start_sector, size_t sector_count); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.c new file mode 100644 index 0000000000..983102efde --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.c @@ -0,0 +1,318 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 13:45:15 + * @LastEditTime: 2021-04-14 15:49:41 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include +#include "ft_sdmmc_common.h" +#include "ft_sdmmc_cmd.h" +#include "ft_sdmmc.h" + +static const char* TAG = "sdmmc_common"; + +ft_error_t Sdmmc_InitOCR(Sdmmc_Card_t* card) +{ + ft_error_t err; + /* In SPI mode, READ_OCR (CMD58) command is used to figure out which voltage + * ranges the card can support. This step is skipped since 1.8V isn't + * supported on the ESP32. + */ + + u32 host_ocr = Sdmmc_GetHostOCR(card->host.io_voltage); + if ((card->ocr & SD_OCR_SDHC_CAP) != 0) { + host_ocr |= SD_OCR_SDHC_CAP; + } + /* Send SEND_OP_COND (ACMD41) command to the card until it becomes ready. */ + err = Sdmmc_SendCmdSendOpCond(card, host_ocr, &card->ocr); + + /* If time-out, re-try send_op_cond as MMC */ + if (err == ERR_SDMMC_TIMEOUT && !Sdmmc_IsHostSpi(card)) { + FT_SDMMC_DEBUG_D("send_op_cond timeout, trying MMC"); + card->is_mmc = 1; + err = Sdmmc_SendCmdSendOpCond(card, host_ocr, &card->ocr); + } + + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: send_op_cond (1) returned 0x%x", __func__, err); + return err; + } + if (Sdmmc_IsHostSpi(card)) { + err = Sdmmc_SendCmdReadOCR(card, &card->ocr); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: read_ocr returned 0x%x", __func__, err); + return err; + } + } + FT_SDMMC_DEBUG_D("host_ocr=0x%x card_ocr=0x%x", host_ocr, card->ocr); + + /* Clear all voltage bits in host's OCR which the card doesn't support. + * Don't touch CCS bit because in SPI mode cards don't report CCS in ACMD41 + * response. + */ + host_ocr &= (card->ocr | (~SD_OCR_VOL_MASK)); + FT_SDMMC_DEBUG_D("Sdmmc_CardInit: host_ocr=%08x, card_ocr=%08x", host_ocr, card->ocr); + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitCID(Sdmmc_Card_t* card) +{ + ft_error_t err; + Sdmmc_Resp_t raw_cid; + if (!Sdmmc_IsHostSpi(card)) { + err = Sdmmc_SendCmdAllSendCID(card, &raw_cid); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: all_send_cid returned 0x%x", __func__, err); + return err; + } + if (!card->is_mmc) { + err = Sdmmc_DecodeCID(raw_cid, &card->cid); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: decoding CID failed (0x%x)", __func__, err); + return err; + } + } else { + /* For MMC, need to know CSD to decode CID. But CSD can only be read + * in data transfer mode, and it is not possible to read CID in data + * transfer mode. We temporiliy store the raw cid and do the + * decoding after the RCA is set and the card is in data transfer + * mode. + */ + memcpy(card->raw_cid, raw_cid, sizeof(Sdmmc_Resp_t)); + } + } else { + err = Sdmmc_SendCmdSendCID(card, &card->cid); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: send_cid returned 0x%x", __func__, err); + return err; + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitRCA(Sdmmc_Card_t* card) +{ + ft_error_t err; + err = Sdmmc_SendCmdSetRelativeAddr(card, &card->rca); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: set_relative_addr returned 0x%x", __func__, err); + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitMMCDecodeCID(Sdmmc_Card_t* card) +{ + ft_error_t err; + Sdmmc_Resp_t raw_cid; + memcpy(raw_cid, card->raw_cid, sizeof(raw_cid)); + err = Sdmmc_MMCDecodeCID(card->csd.mmcVer, raw_cid, &card->cid); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: decoding CID failed (0x%x)", __func__, err); + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitCSD(Sdmmc_Card_t* card) +{ + //FT_SDMMC_DEBUG_I("is mem: %d", card->is_mem); + assert(card->is_mem == 1); + /* Get and decode the contents of CSD register. Determine card capacity. */ + ft_error_t err = Sdmmc_SendCmdSendCSD(card, &card->csd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: send_csd returned 0x%x", __func__, err); + return err; + } + const size_t max_sdsc_capacity = UINT32_MAX / card->csd.sectorSize + 1; + if (!(card->ocr & SD_OCR_SDHC_CAP) && + card->csd.capacity > (int)max_sdsc_capacity) { + FT_SDMMC_DEBUG_W("%s: SDSC card reports capacity=%u. Limiting to %u.", + __func__, card->csd.capacity, max_sdsc_capacity); + card->csd.capacity = max_sdsc_capacity; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSelectCard(Sdmmc_Card_t* card) +{ + assert(!Sdmmc_IsHostSpi(card)); + ft_error_t err = Sdmmc_SendCmdSelectCard(card, card->rca); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: select_card returned 0x%x", __func__, err); + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitCardHsMode(Sdmmc_Card_t* card) +{ + ft_error_t err = ERR_SDMMC_NOT_SUPPORTED; + if (card->is_mem && !card->is_mmc) { + err = Sdmmc_EnableHsModeAndCheck(card); + } /*else if (card->is_sdio) { + err = sdmmc_io_enable_hs_mode(card); + } */else if (card->is_mmc){ + err = Sdmmc_MMCEnableHsMode(card); + } + if (err == ERR_SDMMC_NOT_SUPPORTED) { + FT_SDMMC_DEBUG_D("%s: host supports HS mode, but card doesn't", __func__); + card->max_freq_khz = SDMMC_FREQ_DEFAULT; + } else if (err != ERR_SDMMC_OK) { + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitHostBusWidth(Sdmmc_Card_t* card) +{ + int bus_width = 1; + + if ((card->host.flags & SDMMC_HOST_FLAG_4BIT) && + (card->log_bus_width == 2)) { + bus_width = 4; + } else if ((card->host.flags & SDMMC_HOST_FLAG_8BIT) && + (card->log_bus_width == 3)) { + bus_width = 8; + } + FT_SDMMC_DEBUG_D("%s: using %d-bit bus", __func__, bus_width); + if (bus_width > 1) { + ft_error_t err = (*card->host.set_bus_width)(card->host.slot, bus_width); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("host.set_bus_width failed (0x%x)", err); + return err; + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitHostFreq(Sdmmc_Card_t* card) +{ + assert(card->max_freq_khz <= card->host.max_freq_khz); + + /* Find highest frequency in the following list, + * which is below card->max_freq_khz. + */ + const u32 freq_values[] = { + SDMMC_FREQ_52M, + SDMMC_FREQ_HIGHSPEED, + SDMMC_FREQ_26M, + SDMMC_FREQ_DEFAULT + //NOTE: in sdspi mode, 20MHz may not work. in that case, add 10MHz here. + }; + const int n_freq_values = sizeof(freq_values) / sizeof(freq_values[0]); + + u32 selected_freq = SDMMC_FREQ_PROBING; + for (int i = 0; i < n_freq_values; ++i) { + u32 freq = freq_values[i]; + if (card->max_freq_khz >= freq) { + selected_freq = freq; + break; + } + } + + FT_SDMMC_DEBUG_D("%s: using %d kHz bus frequency", __func__, selected_freq); + if (selected_freq > SDMMC_FREQ_PROBING) { + ft_error_t err = (*card->host.set_card_clk)(card->host.slot, selected_freq); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("failed to switch bus frequency (0x%x)", err); + return err; + } + } + + if (card->is_ddr) { + if (card->host.set_bus_ddr_mode == NULL) { + FT_SDMMC_DEBUG_E("host doesn't support DDR mode or voltage switching"); + return ERR_SDMMC_NOT_SUPPORTED; + } + ft_error_t err = (*card->host.set_bus_ddr_mode)(card->host.slot, TRUE); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("failed to switch bus to DDR mode (0x%x)", err); + return err; + } + } + return ERR_SDMMC_OK; +} + +void Sdmmc_FilpByteOrder(u32* response, size_t size) +{ + assert(size % (2 * sizeof(u32)) == 0); + const size_t n_words = size / sizeof(u32); + for (int i = 0; i < (int)n_words / 2; ++i) { + u32 left = __builtin_bswap32(response[i]); + u32 right = __builtin_bswap32(response[n_words - i - 1]); + response[i] = right; + response[n_words - i - 1] = left; + } +} + +void Sdmmc_CardPrintInfo(FILE* stream, const Sdmmc_Card_t* card) +{ + bool_t print_scr = FALSE; + bool_t print_csd = FALSE; + const char* type; + Sdmmc_Printf("Name: %s\n", card->cid.name); + if (card->is_sdio) { + type = "SDIO"; + print_scr = TRUE; + print_csd = TRUE; + } else if (card->is_mmc) { + type = "MMC"; + print_csd = TRUE; + } else { + type = (card->ocr & SD_OCR_SDHC_CAP) ? "SDHC/SDXC" : "SDSC"; + } + Sdmmc_Printf("Type: %s\n", type); + if (card->max_freq_khz < 1000) { + Sdmmc_Printf("Speed: %d kHz\n", card->max_freq_khz); + } else { + Sdmmc_Printf("Speed: %d MHz%s\n", card->max_freq_khz / 1000, + card->is_ddr ? ", DDR" : ""); + } + Sdmmc_Printf("Size: %lluMB\n", ((uint64_t) card->csd.capacity) * card->csd.sectorSize / (1024 * 1024)); + + if (print_csd) { + Sdmmc_Printf("CSD: ver=%d, sectorSize=%d, capacity=%d read_bl_len=%d\n", + card->csd.csdVer, + card->csd.sectorSize, card->csd.capacity, card->csd.readBlkLen); + } + if (print_scr) { + Sdmmc_Printf("SCR: sdSpec=%d, bus_width=%d\n", card->scr.sdSpec, card->scr.bus_width); + } +} + +ft_error_t Sdmmc_FixHostFlags(Sdmmc_Card_t* card) +{ + const u32 width_1bit = SDMMC_HOST_FLAG_1BIT; + const u32 width_4bit = SDMMC_HOST_FLAG_4BIT; + const u32 width_8bit = SDMMC_HOST_FLAG_8BIT; + const u32 width_mask = width_1bit | width_4bit | width_8bit; + + int slot_bit_width = card->host.get_bus_width(card->host.slot); + if (slot_bit_width == 1 && + (card->host.flags & (width_4bit | width_8bit))) { + card->host.flags &= ~width_mask; + card->host.flags |= width_1bit; + } else if (slot_bit_width == 4 && (card->host.flags & width_8bit)) { + if ((card->host.flags & width_4bit) == 0) { + FT_SDMMC_DEBUG_W("slot width set to 4, but host flags don't have 4 line mode enabled; using 1 line mode"); + card->host.flags &= ~width_mask; + card->host.flags |= width_1bit; + } else { + card->host.flags &= ~width_mask; + card->host.flags |= width_4bit; + } + } + + return ERR_SDMMC_OK; +} diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.h b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.h new file mode 100644 index 0000000000..d81bcb9565 --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_common.h @@ -0,0 +1,178 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 13:45:05 + * @LastEditTime: 2021-04-15 15:12:03 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef _FT_SDMMC_COMMON_H_ +#define _FT_SDMMC_COMMON_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_debug.h" +#include "ft_error_code.h" +#include "ft_sdmmc.h" + +#define SDMMC_GO_IDLE_DELAY_MS 20 +#define SDMMC_IO_SEND_OP_COND_DELAY_MS 10 + +/* These delay values are mostly useful for cases when CD pin is not used, and + * the card is removed. In this case, SDMMC peripheral may not always return + * CMD_DONE / DATA_DONE interrupts after signaling the error. These timeouts work + * as a safety net in such cases. + */ +#define SDMMC_DEFAULT_CMD_TIMEOUT_MS 1000 // Max timeout of ordinary commands +#define SDMMC_WRITE_CMD_TIMEOUT_MS 5000 // Max timeout of write commands + +/* Maximum retry/error count for SEND_OP_COND (CMD1). + * These are somewhat arbitrary, values originate from OpenBSD driver. + */ +#define SDMMC_SEND_OP_COND_MAX_RETRIES 100 +#define SDMMC_SEND_OP_COND_MAX_ERRORS 3 + +/* Functions to send individual commands */ +ft_error_t Sdmmc_SendCmd(Sdmmc_Card_t* card, Sdmmc_Cmd_t* cmd); +ft_error_t Sdmmc_SendAppCmd(Sdmmc_Card_t* card, Sdmmc_Cmd_t* cmd); +ft_error_t Sdmmc_SendCmdGoIdleState(Sdmmc_Card_t* card); +ft_error_t Sdmmc_SendCmdSendIfCond(Sdmmc_Card_t* card, u32 ocr); +ft_error_t Sdmmc_SendCmdSendOpCond(Sdmmc_Card_t* card, u32 ocr, u32 *ocrp); +ft_error_t Sdmmc_SendCmdReadOCR(Sdmmc_Card_t *card, u32 *ocrp); +ft_error_t Sdmmc_SendCmdSendCID(Sdmmc_Card_t *card, Sdmmc_CID_t *out_cid); +ft_error_t Sdmmc_SendCmdAllSendCID(Sdmmc_Card_t* card, Sdmmc_Resp_t* out_raw_cid); +ft_error_t Sdmmc_SendCmdSetRelativeAddr(Sdmmc_Card_t* card, u16* out_rca); +ft_error_t Sdmmc_SendCmdSetBlkLen(Sdmmc_Card_t* card, Sdmmc_CSD_t* csd); +ft_error_t Sdmmc_SendCmdSwitchFunc(Sdmmc_Card_t* card, + u32 mode, u32 group, u32 function, + Sdmmc_SwitchFuncRsp_t* resp); +ft_error_t Sdmmc_SendCmdSendCSD(Sdmmc_Card_t* card, Sdmmc_CSD_t* out_csd); +ft_error_t Sdmmc_SendCmdSelectCard(Sdmmc_Card_t* card, u32 rca); +ft_error_t Sdmmc_SendCmdSendSCR(Sdmmc_Card_t* card, Sdmmc_SCR_t *out_scr); +ft_error_t Sdmmc_SendCmdSwitch(Sdmmc_Card_t *card); +ft_error_t Sdmmc_SendCmdSetBusWidth(Sdmmc_Card_t* card, int width); +ft_error_t Sdmmc_SendCmdSendStatus(Sdmmc_Card_t* card, u32* out_status); +ft_error_t Sdmmc_SendCmdCrcOnOff(Sdmmc_Card_t* card, bool_t crc_enable); + +/* Higher level functions */ +ft_error_t Sdmmc_EnableHsMode(Sdmmc_Card_t* card); +ft_error_t Sdmmc_EnableHsModeAndCheck(Sdmmc_Card_t* card); +ft_error_t Sdmmc_WriteSectorsDMA(Sdmmc_Card_t* card, const void* src, + size_t start_block, size_t block_count); +ft_error_t Sdmmc_ReadSectorsDMA(Sdmmc_Card_t* card, void* dst, + size_t start_block, size_t block_count); + +/* SD specific */ +ft_error_t Sdmmc_CheckSCR(Sdmmc_Card_t* card); +ft_error_t Sdmmc_DecodeCID(Sdmmc_Resp_t resp, Sdmmc_CID_t* out_cid); +ft_error_t Sdmmc_DecodeCSD(Sdmmc_Resp_t response, Sdmmc_CSD_t* out_csd); +ft_error_t Sdmmc_DecodeSCR(u32 *raw_scr, Sdmmc_SCR_t* out_scr); + +/* MMC specific */ +ft_error_t Sdmmc_MMCSendExtCSDData(Sdmmc_Card_t* card, void *out_data, size_t datalen); +ft_error_t Sdmmc_MMCSwitch(Sdmmc_Card_t* card, u8 set, u8 index, u8 value); +ft_error_t Sdmmc_MMCDecodeCID(int mmcVer, Sdmmc_Resp_t resp, Sdmmc_CID_t* out_cid); +ft_error_t Sdmmc_MMCDecodeCSD(Sdmmc_Resp_t response, Sdmmc_CSD_t* out_csd); +ft_error_t Sdmmc_MMCEnableHsMode(Sdmmc_Card_t* card); + +/* Parts of card initialization flow */ +ft_error_t Sdmmc_InitSdIfCond(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSelectCard(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitCSD(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitCID(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitRCA(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitMMCDecodeCID(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitOCR(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSpiCrc(Sdmmc_Card_t* card); +//ft_error_t sdmmc_init_io(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSdBlkLen(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSdSCR(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSwitchHs(Sdmmc_Card_t *card); +ft_error_t Sdmmc_InitSdWaitDataReady(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitMMCReadExtCSD(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitMMCReadCID(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitHostBusWidth(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitSdBusWidth(Sdmmc_Card_t* card); +//ft_error_t sdmmc_init_io_bus_width(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitMMCBusWidth(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitCardHsMode(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitHostFreq(Sdmmc_Card_t* card); +ft_error_t Sdmmc_InitMMCCheckCSD(Sdmmc_Card_t* card); + +/* Various helper functions */ +static inline bool_t Sdmmc_IsHostSpi(const Sdmmc_Card_t* card) +{ + return (card->host.flags & SDMMC_HOST_FLAG_SPI) != 0; +} + +static inline u32 Sdmmc_GetHostOCR(float voltage) +{ + // TODO: report exact voltage to the card + // For now tell that the host has 2.8-3.6V voltage range + (void) voltage; + return SD_OCR_VOL_MASK; +} + +void Sdmmc_FilpByteOrder(u32* response, size_t size); + +ft_error_t Sdmmc_FixHostFlags(Sdmmc_Card_t* card); + +/* define debug log function */ +//#define FT_SDMMC_DEBUG_ENABLED +#define FT_SDMMC_DEBUG_TAG "FT_SDMMC" +#define FT_SDMMC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(FT_SDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#ifdef FT_SDMMC_DEBUG_ENABLED +#define FT_SDMMC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(FT_SDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SDMMC_DEBUG_D(format, ...) FT_DEBUG_PRINT_D(FT_SDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SDMMC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(FT_SDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#define FT_SDMMC_DEBUG_V(format, ...) FT_DEBUG_PRINT_V(FT_SDMMC_DEBUG_TAG, format, ##__VA_ARGS__) +#else +#define FT_SDMMC_DEBUG_I(format, ...) +#define FT_SDMMC_DEBUG_D(format, ...) +#define FT_SDMMC_DEBUG_W(format, ...) +#define FT_SDMMC_DEBUG_V(format, ...) +#endif + +/* define SD MMC error code */ +typedef enum +{ + ERR_SUB_MODE_SDMMC_GENERAL = 0 +}FT_SDMMC_ERR_SUB_MODE; + + +#define ERR_SDMMC_NO_MEM FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x1) /*!< Out of memory */ +#define ERR_SDMMC_INVALID_ARG FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x2) +#define ERR_SDMMC_INVALID_STATE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x3) /*!< Invalid state */ +#define ERR_SDMMC_INVALID_SIZE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x4) /*!< Invalid size */ +#define ERR_SDMMC_NOT_FOUND FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x5) /*!< Requested resource not found */ +#define ERR_SDMMC_NOT_SUPPORTED FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x6) /*!< Operation or feature not supported */ +#define ERR_SDMMC_TIMEOUT FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x7) /*!< Operation timed out */ +#define ERR_SDMMC_INVALID_RESPONSE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x8) /*!< Received response was invalid */ +#define ERR_SDMMC_INVALID_CRC FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x9) /*!< CRC or checksum was invalid */ +#define ERR_SDMMC_INVALID_VERSION FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xa) /*!< Version was invalid */ +#define ERR_SDMMC_INVALID_MAC FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xb) /*!< MAC address was invalid */ +#define ERR_SDMMC_CARD_NOFOUND FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0x10) /*!< SDMMC card not found */ + +#define ERR_SDMMC_WIFI_BASE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xc) /*!< Starting number of WiFi error codes */ +#define ERR_SDMMC_MESH_BASE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xd) /*!< Starting number of MESH error codes */ +#define ERR_SDMMC_FLASH_BASE FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xe) /*!< Starting number of flash error codes */ +#define ERR_SDMMC_NOTYET_INIT FT_CODE_ERR(ERR_MODE_SD_MMC, ERR_SUB_MODE_SDMMC_GENERAL, 0xf) /*!< Starting number of flash error codes */ + +#define ERR_SDMMC_OK ERR_SUCCESS + + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_defs.h b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_defs.h new file mode 100644 index 0000000000..c247fe46fa --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_defs.h @@ -0,0 +1,501 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 11:50:06 + * @LastEditTime: 2021-04-13 10:50:52 + * @Description:  This files is for sdmmc cmd definition + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + * 0.2.0 Zhu Gengyu 2021/4/8 init + */ + +#ifndef _FT_SDMMC_DEFS_H_ +#define _FT_SDMMC_DEFS_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include +#include "ft_types.h" + +#define BIT(nr) (1UL << (nr)) +#define BIT64(nr) (1ULL << (nr)) + +/* MMC commands */ /* response type */ +#define MMC_GO_IDLE_STATE 0 /* R0 */ +#define MMC_SEND_OP_COND 1 /* R3 */ +#define MMC_ALL_SEND_CID 2 /* R2 */ +#define MMC_SET_RELATIVE_ADDR 3 /* R1 */ +#define MMC_SWITCH 6 /* R1B */ +#define MMC_SELECT_CARD 7 /* R1 */ +#define MMC_SEND_EXT_CSD 8 /* R1 */ +#define MMC_SEND_CSD 9 /* R2 */ +#define MMC_SEND_CID 10 /* R1 */ +#define MMC_READ_DAT_UNTIL_STOP 11 /* R1 */ +#define MMC_STOP_TRANSMISSION 12 /* R1B */ +#define MMC_SEND_STATUS 13 /* R1 */ +#define MMC_SET_BLOCKLEN 16 /* R1 */ +#define MMC_READ_BLOCK_SINGLE 17 /* R1 */ +#define MMC_READ_BLOCK_MULTIPLE 18 /* R1 */ +#define MMC_WRITE_DAT_UNTIL_STOP 20 /* R1 */ +#define MMC_SET_BLOCK_COUNT 23 /* R1 */ +#define MMC_WRITE_BLOCK_SINGLE 24 /* R1 */ +#define MMC_WRITE_BLOCK_MULTIPLE 25 /* R1 */ +#define MMC_APP_CMD 55 /* R1 */ + +/* SD commands */ /* response type */ +#define SD_SEND_RELATIVE_ADDR 3 /* R6 */ +#define SD_SEND_SWITCH_FUNC 6 /* R1 */ +#define SD_SEND_IF_COND 8 /* R7 */ +#define SD_READ_OCR 58 /* R3 */ +#define SD_CRC_ON_OFF 59 /* R1 */ + +/* SD application commands */ /* response type */ +#define SD_APP_SET_BUS_WIDTH 6 /* R1 */ +#define SD_APP_SD_STATUS 13 /* R2 */ +#define SD_APP_OP_COND 41 /* R3 */ +#define SD_APP_SEND_SCR 51 /* R1 */ + +/* SD IO commands */ +#define SD_IO_SEND_OP_COND 5 /* R4 */ +#define SD_IO_RW_DIRECT 52 /* R5 */ +#define SD_IO_RW_EXTENDED 53 /* R5 */ + + +/* OCR bits */ +#define MMC_OCR_MEM_READY (1<<31) /* memory power-up status bit */ +#define MMC_OCR_ACCESS_MODE_MASK 0x60000000 /* bits 30:29 */ +#define MMC_OCR_SECTOR_MODE (1<<30) +#define MMC_OCR_BYTE_MODE (1<<29) +#define MMC_OCR_3_5V_3_6V (1<<23) +#define MMC_OCR_3_4V_3_5V (1<<22) +#define MMC_OCR_3_3V_3_4V (1<<21) +#define MMC_OCR_3_2V_3_3V (1<<20) +#define MMC_OCR_3_1V_3_2V (1<<19) +#define MMC_OCR_3_0V_3_1V (1<<18) +#define MMC_OCR_2_9V_3_0V (1<<17) +#define MMC_OCR_2_8V_2_9V (1<<16) +#define MMC_OCR_2_7V_2_8V (1<<15) +#define MMC_OCR_2_6V_2_7V (1<<14) +#define MMC_OCR_2_5V_2_6V (1<<13) +#define MMC_OCR_2_4V_2_5V (1<<12) +#define MMC_OCR_2_3V_2_4V (1<<11) +#define MMC_OCR_2_2V_2_3V (1<<10) +#define MMC_OCR_2_1V_2_2V (1<<9) +#define MMC_OCR_2_0V_2_1V (1<<8) +#define MMC_OCR_1_65V_1_95V (1<<7) + +#define SD_OCR_SDHC_CAP (1<<30) +#define SD_OCR_VOL_MASK 0xFF8000 /* bits 23:15 */ + +/* SD mode R1 response type bits */ +#define MMC_R1_READY_FOR_DATA (1<<8) /* ready for next transfer */ +#define MMC_R1_APP_CMD (1<<5) /* app. commands supported */ +#define MMC_R1_SWITCH_ERROR (1<<7) /* switch command did not succeed */ + +/* SPI mode R1 response type bits */ +#define SD_SPI_R1_IDLE_STATE (1<<0) +#define SD_SPI_R1_ERASE_RST (1<<1) +#define SD_SPI_R1_ILLEGAL_CMD (1<<2) +#define SD_SPI_R1_CMD_CRC_ERR (1<<3) +#define SD_SPI_R1_ERASE_SEQ_ERR (1<<4) +#define SD_SPI_R1_ADDR_ERR (1<<5) +#define SD_SPI_R1_PARAM_ERR (1<<6) +#define SD_SPI_R1_NO_RESPONSE (1<<7) + +#define SDIO_R1_FUNC_NUM_ERR (1<<4) + +/* 48-bit response decoding (32 bits w/o CRC) */ +#define MMC_R1(resp) ((resp)[0]) +#define MMC_R3(resp) ((resp)[0]) +#define MMC_R4(resp) ((resp)[0]) +#define MMC_R5(resp) ((resp)[0]) +#define SD_R6(resp) ((resp)[0]) +#define MMC_R1_CURRENT_STATE(resp) (((resp)[0] >> 9) & 0xf) + +/* SPI mode response decoding */ +#define SD_SPI_R1(resp) ((resp)[0] & 0xff) +#define SD_SPI_R2(resp) ((resp)[0] & 0xffff) +#define SD_SPI_R3(resp) ((resp)[0]) +#define SD_SPI_R7(resp) ((resp)[0]) + +/* SPI mode data response decoding */ +#define SD_SPI_DATA_RSP_VALID(resp_byte) (((resp_byte)&0x11)==0x1) +#define SD_SPI_DATA_RSP(resp_byte) (((resp_byte)>>1)&0x7) +#define SD_SPI_DATA_ACCEPTED 0x2 +#define SD_SPI_DATA_CRC_ERROR 0x5 +#define SD_SPI_DATA_WR_ERROR 0x6 + +/* RCA argument and response */ +#define MMC_ARG_RCA(rca) ((rca) << 16) +#define SD_R6_RCA(resp) (SD_R6((resp)) >> 16) + +/* bus width argument */ +#define SD_ARG_BUS_WIDTH_1 0 +#define SD_ARG_BUS_WIDTH_4 2 + +/* EXT_CSD fields */ +#define EXT_CSD_BUS_WIDTH 183 /* WO */ +#define EXT_CSD_HS_TIMING 185 /* R/W */ +#define EXT_CSD_REV 192 /* RO */ +#define EXT_CSD_STRUCTURE 194 /* RO */ +#define EXT_CSD_CARD_TYPE 196 /* RO */ +#define EXT_CSD_SEC_COUNT 212 /* RO */ +#define EXT_CSD_PWR_CL_26_360 203 /* RO */ +#define EXT_CSD_PWR_CL_52_360 202 /* RO */ +#define EXT_CSD_PWR_CL_26_195 201 /* RO */ +#define EXT_CSD_PWR_CL_52_195 200 /* RO */ +#define EXT_CSD_POWER_CLASS 187 /* R/W */ +#define EXT_CSD_CMD_SET 191 /* R/W */ +#define EXT_CSD_S_CMD_SET 504 /* RO */ + +/* EXT_CSD field definitions */ +#define EXT_CSD_CMD_SET_NORMAL (1U << 0) +#define EXT_CSD_CMD_SET_SECURE (1U << 1) +#define EXT_CSD_CMD_SET_CPSECURE (1U << 2) + +/* EXT_CSD_HS_TIMING */ +#define EXT_CSD_HS_TIMING_BC 0 +#define EXT_CSD_HS_TIMING_HS 1 +#define EXT_CSD_HS_TIMING_HS200 2 +#define EXT_CSD_HS_TIMING_HS400 3 + +/* EXT_CSD_BUS_WIDTH */ +#define EXT_CSD_BUS_WIDTH_1 0 +#define EXT_CSD_BUS_WIDTH_4 1 +#define EXT_CSD_BUS_WIDTH_8 2 +#define EXT_CSD_BUS_WIDTH_4_DDR 5 +#define EXT_CSD_BUS_WIDTH_8_DDR 6 + +/* EXT_CSD_CARD_TYPE */ +/* The only currently valid values for this field are 0x01, 0x03, 0x07, + * 0x0B and 0x0F. */ +#define EXT_CSD_CARD_TYPE_F_26M (1 << 0) /* SDR at "rated voltages */ +#define EXT_CSD_CARD_TYPE_F_52M (1 << 1) /* SDR at "rated voltages */ +#define EXT_CSD_CARD_TYPE_F_52M_1_8V (1 << 2) /* DDR, 1.8V or 3.3V I/O */ +#define EXT_CSD_CARD_TYPE_F_52M_1_2V (1 << 3) /* DDR, 1.2V I/O */ +#define EXT_CSD_CARD_TYPE_26M 0x01 +#define EXT_CSD_CARD_TYPE_52M 0x03 +#define EXT_CSD_CARD_TYPE_52M_V18 0x07 +#define EXT_CSD_CARD_TYPE_52M_V12 0x0b +#define EXT_CSD_CARD_TYPE_52M_V12_18 0x0f + +/* EXT_CSD MMC */ +#define EXT_CSD_MMC_SIZE 512 + +/* MMC_SWITCH access mode */ +#define MMC_SWITCH_MODE_CMD_SET 0x00 /* Change the command set */ +#define MMC_SWITCH_MODE_SET_BITS 0x01 /* Set bits in value */ +#define MMC_SWITCH_MODE_CLEAR_BITS 0x02 /* Clear bits in value */ +#define MMC_SWITCH_MODE_WRITE_BYTE 0x03 /* Set target to value */ + +/* MMC R2 response (CSD) */ +#define MMC_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define MMC_CSD_CSDVER_1_0 1 +#define MMC_CSD_CSDVER_2_0 2 +#define MMC_CSD_CSDVER_EXT_CSD 3 +#define MMC_CSD_MMCVER(resp) MMC_RSP_BITS((resp), 122, 4) +#define MMC_CSD_MMCVER_1_0 0 /* MMC 1.0 - 1.2 */ +#define MMC_CSD_MMCVER_1_4 1 /* MMC 1.4 */ +#define MMC_CSD_MMCVER_2_0 2 /* MMC 2.0 - 2.2 */ +#define MMC_CSD_MMCVER_3_1 3 /* MMC 3.1 - 3.3 */ +#define MMC_CSD_MMCVER_4_0 4 /* MMC 4 */ +#define MMC_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define MMC_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define MMC_CSD_CAPACITY(resp) ((MMC_CSD_C_SIZE((resp))+1) << \ + (MMC_CSD_C_SIZE_MULT((resp))+2)) +#define MMC_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) + +/* MMC v1 R2 response (CID) */ +#define MMC_CID_MID_V1(resp) MMC_RSP_BITS((resp), 104, 24) +#define MMC_CID_PNM_V1_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = MMC_RSP_BITS((resp), 48, 8); \ + (pnm)[7] = '\0'; \ + } while (0) +#define MMC_CID_REV_V1(resp) MMC_RSP_BITS((resp), 40, 8) +#define MMC_CID_PSN_V1(resp) MMC_RSP_BITS((resp), 16, 24) +#define MMC_CID_MDT_V1(resp) MMC_RSP_BITS((resp), 8, 8) + +/* MMC v2 R2 response (CID) */ +#define MMC_CID_MID_V2(resp) MMC_RSP_BITS((resp), 120, 8) +#define MMC_CID_OID_V2(resp) MMC_RSP_BITS((resp), 104, 16) +#define MMC_CID_PNM_V2_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = MMC_RSP_BITS((resp), 56, 8); \ + (pnm)[6] = '\0'; \ + } while (0) +#define MMC_CID_PSN_V2(resp) MMC_RSP_BITS((resp), 16, 32) + +/* SD R2 response (CSD) */ +#define SD_CSD_CSDVER(resp) MMC_RSP_BITS((resp), 126, 2) +#define SD_CSD_CSDVER_1_0 0 +#define SD_CSD_CSDVER_2_0 1 +#define SD_CSD_TAAC(resp) MMC_RSP_BITS((resp), 112, 8) +#define SD_CSD_TAAC_1_5_MSEC 0x26 +#define SD_CSD_NSAC(resp) MMC_RSP_BITS((resp), 104, 8) +#define SD_CSD_SPEED(resp) MMC_RSP_BITS((resp), 96, 8) +#define SD_CSD_SPEED_25_MHZ 0x32 +#define SD_CSD_SPEED_50_MHZ 0x5a +#define SD_CSD_CCC(resp) MMC_RSP_BITS((resp), 84, 12) +#define SD_CSD_CCC_BASIC (1 << 0) /* basic */ +#define SD_CSD_CCC_BR (1 << 2) /* block read */ +#define SD_CSD_CCC_BW (1 << 4) /* block write */ +#define SD_CSD_CCC_ERASE (1 << 5) /* erase */ +#define SD_CSD_CCC_WP (1 << 6) /* write protection */ +#define SD_CSD_CCC_LC (1 << 7) /* lock card */ +#define SD_CSD_CCC_AS (1 << 8) /*application specific*/ +#define SD_CSD_CCC_IOM (1 << 9) /* I/O mode */ +#define SD_CSD_CCC_SWITCH (1 << 10) /* switch */ +#define SD_CSD_READ_BL_LEN(resp) MMC_RSP_BITS((resp), 80, 4) +#define SD_CSD_READ_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 79, 1) +#define SD_CSD_WRITE_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 78, 1) +#define SD_CSD_READ_BLK_MISALIGN(resp) MMC_RSP_BITS((resp), 77, 1) +#define SD_CSD_DSR_IMP(resp) MMC_RSP_BITS((resp), 76, 1) +#define SD_CSD_C_SIZE(resp) MMC_RSP_BITS((resp), 62, 12) +#define SD_CSD_CAPACITY(resp) ((SD_CSD_C_SIZE((resp))+1) << \ + (SD_CSD_C_SIZE_MULT((resp))+2)) +#define SD_CSD_V2_C_SIZE(resp) MMC_RSP_BITS((resp), 48, 22) +#define SD_CSD_V2_CAPACITY(resp) ((SD_CSD_V2_C_SIZE((resp))+1) << 10) +#define SD_CSD_V2_BL_LEN 0x9 /* 512 */ +#define SD_CSD_VDD_R_CURR_MIN(resp) MMC_RSP_BITS((resp), 59, 3) +#define SD_CSD_VDD_R_CURR_MAX(resp) MMC_RSP_BITS((resp), 56, 3) +#define SD_CSD_VDD_W_CURR_MIN(resp) MMC_RSP_BITS((resp), 53, 3) +#define SD_CSD_VDD_W_CURR_MAX(resp) MMC_RSP_BITS((resp), 50, 3) +#define SD_CSD_VDD_RW_CURR_100mA 0x7 +#define SD_CSD_VDD_RW_CURR_80mA 0x6 +#define SD_CSD_C_SIZE_MULT(resp) MMC_RSP_BITS((resp), 47, 3) +#define SD_CSD_ERASE_BLK_EN(resp) MMC_RSP_BITS((resp), 46, 1) +#define SD_CSD_SECTOR_SIZE(resp) MMC_RSP_BITS((resp), 39, 7) /* +1 */ +#define SD_CSD_WP_GRP_SIZE(resp) MMC_RSP_BITS((resp), 32, 7) /* +1 */ +#define SD_CSD_WP_GRP_ENABLE(resp) MMC_RSP_BITS((resp), 31, 1) +#define SD_CSD_R2W_FACTOR(resp) MMC_RSP_BITS((resp), 26, 3) +#define SD_CSD_WRITE_BL_LEN(resp) MMC_RSP_BITS((resp), 22, 4) +#define SD_CSD_RW_BL_LEN_2G 0xa +#define SD_CSD_RW_BL_LEN_1G 0x9 +#define SD_CSD_WRITE_BL_PARTIAL(resp) MMC_RSP_BITS((resp), 21, 1) +#define SD_CSD_FILE_FORMAT_GRP(resp) MMC_RSP_BITS((resp), 15, 1) +#define SD_CSD_COPY(resp) MMC_RSP_BITS((resp), 14, 1) +#define SD_CSD_PERM_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 13, 1) +#define SD_CSD_TMP_WRITE_PROTECT(resp) MMC_RSP_BITS((resp), 12, 1) +#define SD_CSD_FILE_FORMAT(resp) MMC_RSP_BITS((resp), 10, 2) + +/* SD R2 response (CID) */ +#define SD_CID_MID(resp) MMC_RSP_BITS((resp), 120, 8) +#define SD_CID_OID(resp) MMC_RSP_BITS((resp), 104, 16) +#define SD_CID_PNM_CPY(resp, pnm) \ + do { \ + (pnm)[0] = MMC_RSP_BITS((resp), 96, 8); \ + (pnm)[1] = MMC_RSP_BITS((resp), 88, 8); \ + (pnm)[2] = MMC_RSP_BITS((resp), 80, 8); \ + (pnm)[3] = MMC_RSP_BITS((resp), 72, 8); \ + (pnm)[4] = MMC_RSP_BITS((resp), 64, 8); \ + (pnm)[5] = '\0'; \ + } while (0) +#define SD_CID_REV(resp) MMC_RSP_BITS((resp), 56, 8) +#define SD_CID_PSN(resp) MMC_RSP_BITS((resp), 24, 32) +#define SD_CID_MDT(resp) MMC_RSP_BITS((resp), 8, 12) + +/* SCR (SD Configuration Register) */ +#define SCR_STRUCTURE(scr) MMC_RSP_BITS((scr), 60, 4) +#define SCR_STRUCTURE_VER_1_0 0 /* Version 1.0 */ +#define SCR_SD_SPEC(scr) MMC_RSP_BITS((scr), 56, 4) +#define SCR_SD_SPEC_VER_1_0 0 /* Version 1.0 and 1.01 */ +#define SCR_SD_SPEC_VER_1_10 1 /* Version 1.10 */ +#define SCR_SD_SPEC_VER_2 2 /* Version 2.00 or Version 3.0X */ +#define SCR_DATA_STAT_AFTER_ERASE(scr) MMC_RSP_BITS((scr), 55, 1) +#define SCR_SD_SECURITY(scr) MMC_RSP_BITS((scr), 52, 3) +#define SCR_SD_SECURITY_NONE 0 /* no security */ +#define SCR_SD_SECURITY_1_0 1 /* security protocol 1.0 */ +#define SCR_SD_SECURITY_1_0_2 2 /* security protocol 1.0 */ +#define SCR_SD_BUS_WIDTHS(scr) MMC_RSP_BITS((scr), 48, 4) +#define SCR_SD_BUS_WIDTHS_1BIT (1 << 0) /* 1bit (DAT0) */ +#define SCR_SD_BUS_WIDTHS_4BIT (1 << 2) /* 4bit (DAT0-3) */ +#define SCR_SD_SPEC3(scr) MMC_RSP_BITS((scr), 47, 1) +#define SCR_EX_SECURITY(scr) MMC_RSP_BITS((scr), 43, 4) +#define SCR_SD_SPEC4(scr) MMC_RSP_BITS((scr), 42, 1) +#define SCR_RESERVED(scr) MMC_RSP_BITS((scr), 34, 8) +#define SCR_CMD_SUPPORT_CMD23(scr) MMC_RSP_BITS((scr), 33, 1) +#define SCR_CMD_SUPPORT_CMD20(scr) MMC_RSP_BITS((scr), 32, 1) +#define SCR_RESERVED2(scr) MMC_RSP_BITS((scr), 0, 32) + +/* Max supply current in SWITCH_FUNC response (in mA) */ +#define SD_SFUNC_I_MAX(status) (MMC_RSP_BITS((u32 *)(status), 496, 16)) + +/* Supported flags in SWITCH_FUNC response */ +#define SD_SFUNC_SUPPORTED(status, group) \ + (MMC_RSP_BITS((u32 *)(status), 400 + (group - 1) * 16, 16)) + +/* Selected function in SWITCH_FUNC response */ +#define SD_SFUNC_SELECTED(status, group) \ + (MMC_RSP_BITS((u32 *)(status), 376 + (group - 1) * 4, 4)) + +/* Busy flags in SWITCH_FUNC response */ +#define SD_SFUNC_BUSY(status, group) \ + (MMC_RSP_BITS((u32 *)(status), 272 + (group - 1) * 16, 16)) + +/* Version of SWITCH_FUNC response */ +#define SD_SFUNC_VER(status) (MMC_RSP_BITS((u32 *)(status), 368, 8)) + +#define SD_SFUNC_GROUP_MAX 6 +#define SD_SFUNC_FUNC_MAX 15 + +#define SD_ACCESS_MODE 1 /* Function group 1, Access Mode */ + +#define SD_ACCESS_MODE_SDR12 0 /* 25 MHz clock */ +#define SD_ACCESS_MODE_SDR25 1 /* 50 MHz clock */ +#define SD_ACCESS_MODE_SDR50 2 /* UHS-I, 100 MHz clock */ +#define SD_ACCESS_MODE_SDR104 3 /* UHS-I, 208 MHz clock */ +#define SD_ACCESS_MODE_DDR50 4 /* UHS-I, 50 MHz clock, DDR */ + +/** + * @brief Extract up to 32 sequential bits from an array of 32-bit words + * + * Bits within the word are numbered in the increasing order from LSB to MSB. + * + * As an example, consider 2 32-bit words: + * + * 0x01234567 0x89abcdef + * + * On a little-endian system, the bytes are stored in memory as follows: + * + * 67 45 23 01 ef cd ab 89 + * + * MMC_RSP_BITS will extact bits as follows: + * + * start=0 len=4 -> result=0x00000007 + * start=0 len=12 -> result=0x00000567 + * start=28 len=8 -> result=0x000000f0 + * start=59 len=5 -> result=0x00000011 + * + * @param src array of words to extract bits from + * @param start index of the first bit to extract + * @param len number of bits to extract, 1 to 32 + * @return 32-bit word where requested bits start from LSB + */ +static inline u32 MMC_RSP_BITS(u32 *src, int start, int len) +{ + u32 mask = (len % 32 == 0) ? UINT_MAX : UINT_MAX >> (32 - (len % 32)); + size_t word = start / 32; + size_t shift = start % 32; + u32 right = src[word] >> shift; + u32 left = (len + shift <= 32) ? 0 : src[word + 1] << ((32 - shift) % 32); + return (left | right) & mask; +} + +/* SD R4 response (IO OCR) */ +#define SD_IO_OCR_MEM_READY (1<<31) +#define SD_IO_OCR_NUM_FUNCTIONS(ocr) (((ocr) >> 28) & 0x7) +#define SD_IO_OCR_MEM_PRESENT (1<<27) +#define SD_IO_OCR_MASK 0x00fffff0 + +/* CMD52 arguments */ +#define SD_ARG_CMD52_READ (0<<31) +#define SD_ARG_CMD52_WRITE (1<<31) +#define SD_ARG_CMD52_FUNC_SHIFT 28 +#define SD_ARG_CMD52_FUNC_MASK 0x7 +#define SD_ARG_CMD52_EXCHANGE (1<<27) +#define SD_ARG_CMD52_REG_SHIFT 9 +#define SD_ARG_CMD52_REG_MASK 0x1ffff +#define SD_ARG_CMD52_DATA_SHIFT 0 +#define SD_ARG_CMD52_DATA_MASK 0xff +#define SD_R5_DATA(resp) ((resp)[0] & 0xff) + +/* CMD53 arguments */ +#define SD_ARG_CMD53_READ (0<<31) +#define SD_ARG_CMD53_WRITE (1<<31) +#define SD_ARG_CMD53_FUNC_SHIFT 28 +#define SD_ARG_CMD53_FUNC_MASK 0x7 +#define SD_ARG_CMD53_BLOCK_MODE (1<<27) +#define SD_ARG_CMD53_INCREMENT (1<<26) +#define SD_ARG_CMD53_REG_SHIFT 9 +#define SD_ARG_CMD53_REG_MASK 0x1ffff +#define SD_ARG_CMD53_LENGTH_SHIFT 0 +#define SD_ARG_CMD53_LENGTH_MASK 0x1ff +#define SD_ARG_CMD53_LENGTH_MAX 512 + +/* Card Common Control Registers (CCCR) */ +#define SD_IO_CCCR_START 0x00000 +#define SD_IO_CCCR_SIZE 0x100 +#define SD_IO_CCCR_FN_ENABLE 0x02 +#define SD_IO_CCCR_FN_READY 0x03 +#define SD_IO_CCCR_INT_ENABLE 0x04 +#define SD_IO_CCCR_INT_PENDING 0x05 +#define SD_IO_CCCR_CTL 0x06 +#define CCCR_CTL_RES (1<<3) +#define SD_IO_CCCR_BUS_WIDTH 0x07 +#define CCCR_BUS_WIDTH_1 (0<<0) +#define CCCR_BUS_WIDTH_4 (2<<0) +#define CCCR_BUS_WIDTH_8 (3<<0) +#define CCCR_BUS_WIDTH_ECSI (1<<5) +#define SD_IO_CCCR_CARD_CAP 0x08 +#define CCCR_CARD_CAP_LSC BIT(6) +#define CCCR_CARD_CAP_4BLS BIT(7) +#define SD_IO_CCCR_CISPTR 0x09 +#define SD_IO_CCCR_BLKSIZEL 0x10 +#define SD_IO_CCCR_BLKSIZEH 0x11 +#define SD_IO_CCCR_HIGHSPEED 0x13 +#define CCCR_HIGHSPEED_SUPPORT BIT(0) +#define CCCR_HIGHSPEED_ENABLE BIT(1) + +/* Function Basic Registers (FBR) */ +#define SD_IO_FBR_START 0x00100 +#define SD_IO_FBR_SIZE 0x00700 + +/* Card Information Structure (CIS) */ +#define SD_IO_CIS_START 0x01000 +#define SD_IO_CIS_SIZE 0x17000 + +/* CIS tuple codes (based on PC Card 16) */ +#define CISTPL_CODE_NULL 0x00 +#define CISTPL_CODE_DEVICE 0x01 +#define CISTPL_CODE_CHKSUM 0x10 +#define CISTPL_CODE_VERS1 0x15 +#define CISTPL_CODE_ALTSTR 0x16 +#define CISTPL_CODE_CONFIG 0x1A +#define CISTPL_CODE_CFTABLE_ENTRY 0x1B +#define CISTPL_CODE_MANFID 0x20 +#define CISTPL_CODE_FUNCID 0x21 +#define TPLFID_FUNCTION_SDIO 0x0c +#define CISTPL_CODE_FUNCE 0x22 +#define CISTPL_CODE_VENDER_BEGIN 0x80 +#define CISTPL_CODE_VENDER_END 0x8F +#define CISTPL_CODE_SDIO_STD 0x91 +#define CISTPL_CODE_SDIO_EXT 0x92 +#define CISTPL_CODE_END 0xFF + + +/* Timing */ +#define SDMMC_TIMING_LEGACY 0 +#define SDMMC_TIMING_HIGHSPEED 1 +#define SDMMC_TIMING_MMC_DDR52 2 + +/* Architecture specifics. */ +//#define CONFIG_FREERTOS_HZ 100 /* NOT SURE ABOUT THE VALUE */ +//#define configTICK_RATE_HZ ( CONFIG_FREERTOS_HZ ) +//#define portTICK_PERIOD_MS ( ( u32 ) 1000 / configTICK_RATE_HZ ) +//#define MALLOC_CAP_DMA (1<<3) ///< Memory must be able to accessed by DMA +#define MIN(A,B) ((A) < (B) ? (A):(B)) + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_init.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_init.c new file mode 100644 index 0000000000..b5e5841349 --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_init.c @@ -0,0 +1,147 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 11:51:14 + * @LastEditTime: 2021-04-14 15:49:12 + * @Description:  This files is for sdmmc init process + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include "ft_assert.h" +#include "ft_sdmmc.h" +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc_common.h" + +#define SDMMC_INIT_STEP(condition, function) \ + do { \ + if ((condition)) { \ + ft_error_t err = (function)(card); \ + if (err != ERR_SDMMC_OK) { \ + FT_SDMMC_DEBUG_E("init failed at %s %d: returned 0x%x", #function, __LINE__, err); \ + return err; \ + } \ + } \ + } while(0); + +void Sdmmc_cardSetSDMem(Sdmmc_Card_t* card) +{ + card->is_ddr = FALSE; + card->is_mem = TRUE; + card->is_mmc = FALSE; + card->is_sdio = FALSE; + card->log_bus_width = 2; +} + +ft_error_t Sdmmc_CardInit(const Sdmmc_Host_t* config, Sdmmc_Card_t* card) +{ + memset(card, 0, sizeof(*card)); + memcpy(&card->host, config, sizeof(*config)); + const bool_t is_spi = FALSE/*Sdmmc_IsHostSpi(card)*/; + const bool_t always = TRUE; + const bool_t io_supported = FALSE; + const bool_t is_mmc = FALSE; + const bool_t is_sdmem = TRUE; + const bool_t is_mem = TRUE; + const bool_t is_sdio = FALSE; + + Sdmmc_cardSetSDMem(card); + + /* Check if host flags are compatible with slot configuration. */ + //SDMMC_INIT_STEP(!is_spi, Sdmmc_FixHostFlags); + SDMMC_INIT_STEP(always, Sdmmc_FixHostFlags); + + /* Reset SDIO (CMD52, RES) before re-initializing IO (CMD5). */ + //SDMMC_INIT_STEP(io_supported, sdmmc_io_reset); + + /* GO_IDLE_STATE (CMD0) command resets the card */ + SDMMC_INIT_STEP(always, Sdmmc_SendCmdGoIdleState); + + /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. */ + SDMMC_INIT_STEP(always, Sdmmc_InitSdIfCond); + + /* IO_SEND_OP_COND(CMD5), Determine if the card is an IO card. */ + //SDMMC_INIT_STEP(io_supported, sdmmc_init_io); + + // const bool_t is_mem = card->is_mem; + // const bool_t is_sdio = !is_mem; + + /* Enable CRC16 checks for data transfers in SPI mode */ + SDMMC_INIT_STEP(is_spi, Sdmmc_InitSpiCrc); + + /* Use SEND_OP_COND to set up card OCR */ + SDMMC_INIT_STEP(is_mem, Sdmmc_InitOCR); + + // const bool_t is_mmc = is_mem && card->is_mmc; + // const bool_t is_sdmem = is_mem && !is_mmc; + + // FT_SDMMC_DEBUG_D("%s: card type is %s", __func__, + // is_sdio ? "SDIO" : is_mmc ? "MMC" : "SD"); + + /* Read the contents of CID register*/ + SDMMC_INIT_STEP(is_mem, Sdmmc_InitCID); + + /* Assign RCA */ + SDMMC_INIT_STEP(!is_spi, Sdmmc_InitRCA); + FT_SDMMC_DEBUG_D("finish assign rca!!"); + + /* Read and decode the contents of CSD register */ + SDMMC_INIT_STEP(is_mem, Sdmmc_InitCSD); + + /* Decode the contents of mmc CID register */ + SDMMC_INIT_STEP(is_mmc && !is_spi, Sdmmc_InitMMCDecodeCID); + + /* Switch the card from stand-by mode to data transfer mode (not needed if + * SPI interface is used). This is needed to issue SET_BLOCKLEN and + * SEND_SCR commands. + */ + SDMMC_INIT_STEP(!is_spi, Sdmmc_InitSelectCard); + + /* SD memory cards: + * Set block len for SDSC cards to 512 bytes (same as SDHC) + * Read SCR + * Wait to enter data transfer state + */ + SDMMC_INIT_STEP(is_sdmem, Sdmmc_InitSdBlkLen); + + /* Switch to high speed before one need to fetch data from SD Card */ + SDMMC_INIT_STEP(is_sdmem, Sdmmc_InitSwitchHs); + + SDMMC_INIT_STEP(is_sdmem, Sdmmc_InitSdSCR); + //SDMMC_INIT_STEP(is_sdmem, Sdmmc_InitSdWaitDataReady); + + // /* MMC cards: read CXD */ + // SDMMC_INIT_STEP(is_mmc, Sdmmc_InitMMCReadExtCSD); + + // /* Try to switch card to HS mode if the card supports it. + // * Set card->max_freq_khz value accordingly. + // */ + SDMMC_INIT_STEP(always, Sdmmc_InitCardHsMode); + + // /* Set bus width. One call for every kind of card, then one for the host */ + if (!is_spi) { + //SDMMC_INIT_STEP(is_sdmem, Sdmmc_InitSdBusWidth); + //SDMMC_INIT_STEP(is_sdio, sdmmc_init_io_bus_width); + //SDMMC_INIT_STEP(is_mmc, Sdmmc_InitMMCBusWidth); + //SDMMC_INIT_STEP(always, Sdmmc_InitHostBusWidth); + } + + // /* Switch to the host to use card->max_freq_khz frequency. */ + //SDMMC_INIT_STEP(always, Sdmmc_InitHostFreq); + + // /* Sanity check after switching the bus mode and frequency */ + // FT_SDMMC_DEBUG_D("before check sd scr"); + // SDMMC_INIT_STEP(is_sdmem, Sdmmc_CheckSCR); + // FT_SDMMC_DEBUG_D("after check sd scr"); + // /* TODO: this is CMD line only, add data checks for eMMC */ + // //SDMMC_INIT_STEP(is_mmc, Sdmmc_InitMMCCheckCSD); + // /* TODO: add similar checks for SDIO */ + + return ERR_SDMMC_OK; +} + diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_mmc.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_mmc.c new file mode 100644 index 0000000000..e80a9045fc --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_mmc.c @@ -0,0 +1,261 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 16:03:05 + * @LastEditTime: 2021-04-14 11:55:03 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc_common.h" + +ft_error_t Sdmmc_InitMMCReadExtCSD(Sdmmc_Card_t* card) +{ + int card_type; + ft_error_t err = ERR_SDMMC_OK; + + u8* ext_csd = Sdmmc_HeapMalloc(EXT_CSD_MMC_SIZE); + if (!ext_csd) { + FT_SDMMC_DEBUG_E("%s: could not allocate ext_csd", __func__); + return ERR_SDMMC_NO_MEM; + } + + uint32_t sectors = 0; + + FT_SDMMC_DEBUG_D("MMC version: %d", card->csd.mmcVer); + if (card->csd.mmcVer < MMC_CSD_MMCVER_4_0) { + err = ERR_SDMMC_NOT_SUPPORTED; + goto out; + } + + /* read EXT_CSD */ + err = Sdmmc_MMCSendExtCSDData(card, ext_csd, EXT_CSD_MMC_SIZE); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: send_ext_csd_data error 0x%x", __func__, err); + goto out; + } + card_type = ext_csd[EXT_CSD_CARD_TYPE]; + + card->is_ddr = 0; + if (card_type & EXT_CSD_CARD_TYPE_F_52M_1_8V) { + card->max_freq_khz = SDMMC_FREQ_52M; + if ((card->host.flags & SDMMC_HOST_FLAG_DDR) && + card->host.max_freq_khz >= SDMMC_FREQ_26M && + card->host.get_bus_width(card->host.slot) == 4) { + FT_SDMMC_DEBUG_D("card and host support DDR mode"); + card->is_ddr = 1; + } + } else if (card_type & EXT_CSD_CARD_TYPE_F_52M) { + card->max_freq_khz = SDMMC_FREQ_52M; + } else if (card_type & EXT_CSD_CARD_TYPE_F_26M) { + card->max_freq_khz = SDMMC_FREQ_26M; + } else { + FT_SDMMC_DEBUG_W("%s: unknown CARD_TYPE 0x%x", __func__, card_type); + } + /* For MMC cards, use speed value from EXT_CSD */ + card->csd.transSpeed = card->max_freq_khz * 1000; + FT_SDMMC_DEBUG_D("MMC card type %d, max_freq_khz=%d, is_ddr=%d", card_type, card->max_freq_khz, card->is_ddr); + card->max_freq_khz = MIN(card->max_freq_khz, card->host.max_freq_khz); + + if (card->host.flags & SDMMC_HOST_FLAG_8BIT) { + card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ? + EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] >> 4; + card->log_bus_width = 3; + } else if (card->host.flags & SDMMC_HOST_FLAG_4BIT) { + card->ext_csd.power_class = ext_csd[(card->max_freq_khz > SDMMC_FREQ_26M) ? + EXT_CSD_PWR_CL_52_360 : EXT_CSD_PWR_CL_26_360] & 0x0f; + card->log_bus_width = 2; + } else { + card->ext_csd.power_class = 0; //card must be able to do full rate at powerclass 0 in 1-bit mode + card->log_bus_width = 0; + } + + sectors = ( ext_csd[EXT_CSD_SEC_COUNT + 0] << 0 ) + | ( ext_csd[EXT_CSD_SEC_COUNT + 1] << 8 ) + | ( ext_csd[EXT_CSD_SEC_COUNT + 2] << 16 ) + | ( ext_csd[EXT_CSD_SEC_COUNT + 3] << 24 ); + + if (sectors > (2u * 1024 * 1024 * 1024) / 512) { + card->csd.capacity = sectors; + } + +out: + Sdmmc_HeapFree(ext_csd); + return err; +} + +ft_error_t Sdmmc_InitMMCBusWidth(Sdmmc_Card_t* card) +{ + ft_error_t err; + if (card->ext_csd.power_class != 0) { + err = Sdmmc_MMCSwitch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_POWER_CLASS, card->ext_csd.power_class); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: can't change power class (%d bit), 0x%x" + , __func__, card->ext_csd.power_class, err); + return err; + } + } + + if (card->log_bus_width > 0) { + int csd_bus_width_value = EXT_CSD_BUS_WIDTH_1; + int bus_width = 1; + if (card->log_bus_width == 2) { + if (card->is_ddr) { + csd_bus_width_value = EXT_CSD_BUS_WIDTH_4_DDR; + } else { + csd_bus_width_value = EXT_CSD_BUS_WIDTH_4; + } + bus_width = 4; + } else if (card->log_bus_width == 3) { + if (card->is_ddr) { + csd_bus_width_value = EXT_CSD_BUS_WIDTH_8_DDR; + } else { + csd_bus_width_value = EXT_CSD_BUS_WIDTH_8; + } + bus_width = 8; + } + err = Sdmmc_MMCSwitch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_BUS_WIDTH, csd_bus_width_value); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: can't change bus width (%d bit), 0x%x", + __func__, bus_width, err); + return err; + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_MMCEnableHsMode(Sdmmc_Card_t* card) +{ + ft_error_t err; + if (card->max_freq_khz > SDMMC_FREQ_26M) { + /* switch to high speed timing */ + err = Sdmmc_MMCSwitch(card, EXT_CSD_CMD_SET_NORMAL, + EXT_CSD_HS_TIMING, EXT_CSD_HS_TIMING_HS); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: mmc_switch EXT_CSD_HS_TIMING_HS error 0x%x", + __func__, err); + return err; + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_MMCDecodeCID(int mmcVer, Sdmmc_Resp_t resp, Sdmmc_CID_t* out_cid) +{ + if (mmcVer == MMC_CSD_MMCVER_1_0 || + mmcVer == MMC_CSD_MMCVER_1_4) { + out_cid->mfgId = MMC_CID_MID_V1(resp); + out_cid->oemId = 0; + MMC_CID_PNM_V1_CPY(resp, out_cid->name); + out_cid->revision = MMC_CID_REV_V1(resp); + out_cid->serial = MMC_CID_PSN_V1(resp); + out_cid->date = MMC_CID_MDT_V1(resp); + } else if (mmcVer == MMC_CSD_MMCVER_2_0 || + mmcVer == MMC_CSD_MMCVER_3_1 || + mmcVer == MMC_CSD_MMCVER_4_0) { + out_cid->mfgId = MMC_CID_MID_V2(resp); + out_cid->oemId = MMC_CID_OID_V2(resp); + MMC_CID_PNM_V1_CPY(resp, out_cid->name); + out_cid->revision = 0; + out_cid->serial = MMC_CID_PSN_V1(resp); + out_cid->date = 0; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_MMCDecodeCSD(Sdmmc_Resp_t response, Sdmmc_CSD_t* out_csd) +{ + out_csd->csdVer = MMC_CSD_CSDVER(response); + if (out_csd->csdVer == MMC_CSD_CSDVER_1_0 || + out_csd->csdVer == MMC_CSD_CSDVER_2_0 || + out_csd->csdVer == MMC_CSD_CSDVER_EXT_CSD) { + out_csd->mmcVer = MMC_CSD_MMCVER(response); + out_csd->capacity = MMC_CSD_CAPACITY(response); + out_csd->readBlkLen = MMC_CSD_READ_BL_LEN(response); + } else { + FT_SDMMC_DEBUG_E("unknown MMC CSD structure version 0x%x\n", out_csd->csdVer); + return 1; + } + int read_bl_size = 1 << out_csd->readBlkLen; + out_csd->sectorSize = MIN(read_bl_size, 512); + if (out_csd->sectorSize < read_bl_size) { + out_csd->capacity *= read_bl_size / out_csd->sectorSize; + } + /* transSpeed will be determined when reading CXD */ + out_csd->transSpeed = 0; + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_MMCSendExtCSDData(Sdmmc_Card_t* card, void *out_data, size_t datalen) +{ + assert(Sdmmc_DMACapable(out_data)); + Sdmmc_Cmd_t cmd = { + .data = out_data, + .datalen = datalen, + .blklen = datalen, + .opcode = MMC_SEND_EXT_CSD, + .arg = 0, + .flags = SCF_CMD_ADTC | SCF_RSP_R1 | SCF_CMD_READ, + .cmd_resp_type = SCT_RESP_SHORT + }; + return Sdmmc_SendCmd(card, &cmd); +} + +ft_error_t Sdmmc_MMCSwitch(Sdmmc_Card_t* card, u8 set, u8 index, u8 value) +{ + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SWITCH, + .arg = (MMC_SWITCH_MODE_WRITE_BYTE << 24) | (index << 16) | (value << 8) | set, + .flags = SCF_RSP_R1B | SCF_CMD_AC | SCF_WAIT_BUSY, + .cmd_resp_type = SCT_RESP_SHORT + }; + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err == ERR_SDMMC_OK) { + //check response bit to see that switch was accepted + if (MMC_R1(cmd.response) & MMC_R1_SWITCH_ERROR) + err = ERR_SDMMC_INVALID_RESPONSE; + } + + return err; +} + +ft_error_t Sdmmc_InitMMCCheckCSD(Sdmmc_Card_t* card) +{ + ft_error_t err; + assert(card->is_mem == 1); + assert(card->rca != 0); + //The card will not respond to send_csd command in the transfer state. + //Deselect it first. + err = Sdmmc_SendCmdSelectCard(card, 0); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: select_card returned 0x%x", __func__, err); + return err; + } + + Sdmmc_CSD_t csd; + /* Get the contents of CSD register to verify the communication over CMD line + is OK. */ + err = Sdmmc_SendCmdSendCSD(card, &csd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: send_csd returned 0x%x", __func__, err); + return err; + } + + //Select the card again + err = Sdmmc_SendCmdSelectCard(card, card->rca); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("%s: select_card returned 0x%x", __func__, err); + return err; + } + return ERR_SDMMC_OK; +} diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_port.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_port.c new file mode 100644 index 0000000000..c0f1645bf3 --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_port.c @@ -0,0 +1,64 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 16:52:05 + * @LastEditTime: 2021-04-15 13:51:10 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_printf.h" +#include "ft_generic_timer.h" +#include "ft_assert.h" +#include "FreeRTOS.h" +#include "task.h" +#include "semphr.h" +#include "ft_sdmmc.h" +#include "ft_sdmmc_common.h" + +static void freertos_task_delay(u32 delay_tick) +{ + (void)vTaskDelay(delay_tick); +} + +static void *freertos_heap_alloc(size_t size) +{ + void *ptr = pvPortMalloc(size); + FT_SDMMC_DEBUG_D("allocate %x size %d", ptr, size); + return ptr; +} + +static void freertos_heap_free(void *ptr) +{ + Ft_assertNoneReturn(NULL != ptr); + FT_SDMMC_DEBUG_D("do free %x", ptr); + vPortFree(ptr); +} + +static bool_t freertos_dma_capable(const void *ptr) +{ + /* no check implemented */ + return TRUE; +} + +/* specify cpu or os depended implementation */ +static const Sdmmc_OSImpl_t sdmmc_os_impl = { + .task_delay = freertos_task_delay, /* freeRTOS delay */ + .heap_alloc = freertos_heap_alloc, /* freeRTOS heap memory allocator */ + .heap_free = freertos_heap_free, /* freeRTOS heap free */ + .check_dma_capable = freertos_dma_capable, /* freeRTOS mem allign check */ + .printf = Ft_printf, /* freeRTOS printf */ + .tick_period = portTICK_PERIOD_MS, /* freeRTOS tick period */ + .event_queue = NULL, /* freeRTOS follow type QueueHandle_t */ + .io_intr_event = NULL, /* freeRTOS follow type SemaphoreHandle_t */ +}; + +const Sdmmc_OSImpl_t *get_sdmmc_os_impl() +{ + return &sdmmc_os_impl; +} diff --git a/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_sd.c b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_sd.c new file mode 100644 index 0000000000..47ca2c068c --- /dev/null +++ b/bsp/ft2004/libraries/component/sdmmc/ft_sdmmc_sd.c @@ -0,0 +1,376 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-08 11:51:42 + * @LastEditTime: 2021-04-14 11:55:34 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ +#include +#include +#include "ft_sdmmc_defs.h" +#include "ft_sdmmc.h" +#include "ft_sdmmc_common.h" + +ft_error_t Sdmmc_InitSdIfCond(Sdmmc_Card_t* card) +{ + /* SEND_IF_COND (CMD8) command is used to identify SDHC/SDXC cards. + * SD v1 and non-SD cards will not respond to this command. + */ + u32 host_ocr = Sdmmc_GetHostOCR(card->host.io_voltage); + ft_error_t err = Sdmmc_SendCmdSendIfCond(card, host_ocr); + if (err == ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_D( "SDHC/SDXC card"); + host_ocr |= SD_OCR_SDHC_CAP; + } else if (err == ERR_SDMMC_TIMEOUT) { + FT_SDMMC_DEBUG_D( "CMD8 timeout; not an SD v2.00 card"); + } else if (Sdmmc_IsHostSpi(card) && err == ERR_SDMMC_NOT_SUPPORTED) { + FT_SDMMC_DEBUG_D( "CMD8 rejected; not an SD v2.00 card"); + } else { + FT_SDMMC_DEBUG_E( "%s: send_if_cond (1) returned 0x%x", __func__, err); + return err; + } + card->ocr = host_ocr; + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSdBlkLen(Sdmmc_Card_t* card) +{ + /* SDSC cards support configurable data block lengths. + * We don't use this feature and set the block length to 512 bytes, + * same as the block length for SDHC cards. + */ + if ((card->ocr & SD_OCR_SDHC_CAP) == 0) { + ft_error_t err = Sdmmc_SendCmdSetBlkLen(card, &card->csd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: set_blocklen returned 0x%x", __func__, err); + return err; + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSdSCR(Sdmmc_Card_t* card) +{ + ft_error_t err; + /* Get the contents of SCR register: bus width and the version of SD spec + * supported by the card. + * In SD mode, this is the first command which uses D0 line. Errors at + * this step usually indicate connection issue or lack of pull-up resistor. + */ + err = Sdmmc_SendCmdSendSCR(card, &card->scr); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: send_scr (1) returned 0x%x", __func__, err); + return err; + } + + if ((card->scr.bus_width & SCR_SD_BUS_WIDTHS_4BIT) + && (card->host.flags & SDMMC_HOST_FLAG_4BIT)) { + card->log_bus_width = 2; + } else { + card->log_bus_width = 0; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSwitchHs(Sdmmc_Card_t* card) +{ + ft_error_t err; + + err = Sdmmc_SendCmdSwitch(card); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E("switch card to high speed failed !!"); + return err; + } + + return err; +} + +ft_error_t Sdmmc_InitSdBusWidth(Sdmmc_Card_t* card) +{ + int width = 1; + if (card->log_bus_width == 2) { + width = 4; + } else if (card->log_bus_width == 3) { + width = 8; + } + ft_error_t err = Sdmmc_SendCmdSetBusWidth(card, width); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "set_bus_width failed (0x%x)", err); + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSdWaitDataReady(Sdmmc_Card_t* card) +{ + /* Wait for the card to be ready for data transfers */ + u32 status = 0; + u32 count = 0; + while (!Sdmmc_IsHostSpi(card) && !(status & MMC_R1_READY_FOR_DATA)) { + // TODO: add some timeout here + ft_error_t err = Sdmmc_SendCmdSendStatus(card, &status); + if (err != ERR_SDMMC_OK) { + return err; + } + if (++count % 16 == 0) { + FT_SDMMC_DEBUG_V("waiting for card to become ready (%d)", count); + } + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_SendCmdSwitchFunc(Sdmmc_Card_t* card, + u32 mode, u32 group, u32 function, + Sdmmc_SwitchFuncRsp_t* resp) +{ + if (card->scr.sdSpec < SCR_SD_SPEC_VER_1_10 || + ((card->csd.cardCmdClass & SD_CSD_CCC_SWITCH) == 0)) { + return ERR_SDMMC_NOT_SUPPORTED; + } + + if (group == 0 || + group > SD_SFUNC_GROUP_MAX || + function > SD_SFUNC_FUNC_MAX) { + return ERR_SDMMC_INVALID_ARG; + } + + if (mode > 1) { + return ERR_SDMMC_INVALID_ARG; + } + + u32 group_shift = (group - 1) << 2; + /* all functions which should not be affected are set to 0xf (no change) */ + u32 other_func_mask = (0x00ffffff & ~(0xf << group_shift)); + u32 func_val = (function << group_shift) | other_func_mask; + + Sdmmc_Cmd_t cmd = { + .opcode = MMC_SWITCH, + .flags = SCF_CMD_ADTC | SCF_CMD_READ | SCF_RSP_R1, + .cmd_resp_type = SCT_RESP_SHORT, + .blklen = sizeof(Sdmmc_SwitchFuncRsp_t), + .data = resp->data, + .datalen = sizeof(Sdmmc_SwitchFuncRsp_t), + .arg = (!!mode << 31) | func_val + }; + + ft_error_t err = Sdmmc_SendCmd(card, &cmd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: Sdmmc_SendCmd returned 0x%x", __func__, err); + return err; + } + Sdmmc_FilpByteOrder(resp->data, sizeof(Sdmmc_SwitchFuncRsp_t)); + u32 resp_ver = SD_SFUNC_VER(resp->data); + if (resp_ver == 0) { + /* busy response is never sent */ + } else if (resp_ver == 1) { + if (SD_SFUNC_BUSY(resp->data, group) & (1 << function)) { + FT_SDMMC_DEBUG_D( "%s: response indicates function %d:%d is busy", + __func__, group, function); + return ERR_SDMMC_INVALID_STATE; + } + } else { + FT_SDMMC_DEBUG_D( "%s: got an invalid version of SWITCH_FUNC response: 0x%02x", + __func__, resp_ver); + return ERR_SDMMC_INVALID_RESPONSE; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_EnableHsMode(Sdmmc_Card_t* card) +{ + /* This will determine if the card supports SWITCH_FUNC command, + * and high speed mode. If the cards supports both, this will enable + * high speed mode at the card side. + */ + if (card->scr.sdSpec < SCR_SD_SPEC_VER_1_10 || + ((card->csd.cardCmdClass & SD_CSD_CCC_SWITCH) == 0)) { + return ERR_SDMMC_NOT_SUPPORTED; + } + Sdmmc_SwitchFuncRsp_t* response = (Sdmmc_SwitchFuncRsp_t*) + Sdmmc_HeapMalloc(sizeof(*response)); + if (response == NULL) { + return ERR_SDMMC_NO_MEM; + } + + ft_error_t err = Sdmmc_SendCmdSwitchFunc(card, 0, SD_ACCESS_MODE, 0, response); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_D( "%s: Sdmmc_SendCmdSwitchFunc (1) returned 0x%x", __func__, err); + goto out; + } + u32 supported_mask = SD_SFUNC_SUPPORTED(response->data, 1); + if ((supported_mask & BIT(SD_ACCESS_MODE_SDR25)) == 0) { + err = ERR_SDMMC_NOT_SUPPORTED; + goto out; + } + err = Sdmmc_SendCmdSwitchFunc(card, 1, SD_ACCESS_MODE, SD_ACCESS_MODE_SDR25, response); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_D( "%s: Sdmmc_SendCmdSwitchFunc (2) returned 0x%x", __func__, err); + goto out; + } + +out: + Sdmmc_HeapFree(response); + return err; +} + +ft_error_t Sdmmc_EnableHsModeAndCheck(Sdmmc_Card_t* card) +{ + /* All cards should support at least default speed */ + card->max_freq_khz = SDMMC_FREQ_DEFAULT; + if (card->host.max_freq_khz <= card->max_freq_khz) { + /* Host is configured to use low frequency, don't attempt to switch */ + card->max_freq_khz = card->host.max_freq_khz; + return ERR_SDMMC_OK; + } + + /* Try to enabled HS mode */ + ft_error_t err = Sdmmc_EnableHsMode(card); + if (err != ERR_SDMMC_OK) { + return err; + } + /* HS mode has been enabled on the card. + * Read CSD again, it should now indicate that the card supports + * 50MHz clock. + * Since SEND_CSD is allowed only in standby mode, and the card is currently in data transfer + * mode, deselect the card first, then get the CSD, then select the card again. This step is + * not required in SPI mode, since CMD7 (select_card) is not supported. + */ + const bool_t is_spi = Sdmmc_IsHostSpi(card); + if (!is_spi) { + err = Sdmmc_SendCmdSelectCard(card, 0); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: select_card (1) returned 0x%x", __func__, err); + return err; + } + } + err = Sdmmc_SendCmdSendCSD(card, &card->csd); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: send_csd returned 0x%x", __func__, err); + return err; + } + if (!is_spi) { + err = Sdmmc_SendCmdSelectCard(card, card->rca); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: select_card (2) returned 0x%x", __func__, err); + return err; + } + } + + if (card->csd.transSpeed != 50000000) { + FT_SDMMC_DEBUG_W("unexpected: after enabling HS mode, transSpeed=%d", card->csd.transSpeed); + return ERR_SDMMC_NOT_SUPPORTED; + } + + card->max_freq_khz = SDMMC_FREQ_HIGHSPEED; + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_CheckSCR(Sdmmc_Card_t* card) +{ + /* If frequency switch has been performed, read SCR register one more time + * and compare the result with the previous one. Use this simple check as + * an indicator of potential signal integrity issues. + */ + Sdmmc_SCR_t scr_tmp; + ft_error_t err = Sdmmc_SendCmdSendSCR(card, &scr_tmp); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: send_scr returned 0x%x", __func__, err); + return err; + } + if (memcmp(&card->scr, &scr_tmp, sizeof(scr_tmp)) != 0) { + FT_SDMMC_DEBUG_E( "got corrupted data after increasing clock frequency"); + return ERR_SDMMC_INVALID_RESPONSE; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_InitSpiCrc(Sdmmc_Card_t* card) +{ + /* In SD mode, CRC checks of data transfers are mandatory and performed + * by the hardware. In SPI mode, CRC16 of data transfers is optional and + * needs to be enabled. + */ + assert(Sdmmc_IsHostSpi(card)); + ft_error_t err = Sdmmc_SendCmdCrcOnOff(card, TRUE); + if (err != ERR_SDMMC_OK) { + FT_SDMMC_DEBUG_E( "%s: Sdmmc_SendCmdCrcOnOff returned 0x%x", __func__, err); + return err; + } + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_DecodeCID(Sdmmc_Resp_t resp, Sdmmc_CID_t* out_cid) +{ + out_cid->mfgId = SD_CID_MID(resp); + out_cid->oemId = SD_CID_OID(resp); + SD_CID_PNM_CPY(resp, out_cid->name); + out_cid->revision = SD_CID_REV(resp); + out_cid->serial = SD_CID_PSN(resp); + out_cid->date = SD_CID_MDT(resp); + return ERR_SDMMC_OK; +} + +ft_error_t Sdmmc_DecodeCSD(Sdmmc_Resp_t response, Sdmmc_CSD_t* out_csd) +{ + out_csd->csdVer = SD_CSD_CSDVER(response); + FT_SDMMC_DEBUG_I("is sd v%d.0", out_csd->csdVer + 1); + switch (out_csd->csdVer) { + case SD_CSD_CSDVER_2_0: + out_csd->capacity = SD_CSD_V2_CAPACITY(response); + out_csd->readBlkLen = SD_CSD_V2_BL_LEN; + break; + case SD_CSD_CSDVER_1_0: + out_csd->capacity = SD_CSD_CAPACITY(response); + out_csd->readBlkLen = SD_CSD_READ_BL_LEN(response); + break; + default: + FT_SDMMC_DEBUG_E( "unknown SD CSD structure version 0x%x", out_csd->csdVer); + return ERR_SDMMC_NOT_SUPPORTED; + } + + out_csd->cardCmdClass = SD_CSD_CCC(response); + int read_bl_size = 1 << out_csd->readBlkLen; + out_csd->sectorSize = MIN(read_bl_size, 512); + if (out_csd->sectorSize < read_bl_size) { + out_csd->capacity *= read_bl_size / out_csd->sectorSize; + } + int speed = SD_CSD_SPEED(response); + if (speed == SD_CSD_SPEED_50_MHZ) { + out_csd->transSpeed = 50000000; + } else { + out_csd->transSpeed = 25000000; + } + + FT_SDMMC_DEBUG_I("sd capacity is %dMB", ((out_csd->capacity / 1000) * out_csd->sectorSize)/1000); + return ERR_SDMMC_OK; +} + +#define be32_to_cpu(x) ((u32)( \ + (((u32)(x) & (u32)0x000000ffUL) << 24) | \ + (((u32)(x) & (u32)0x0000ff00UL) << 8) | \ + (((u32)(x) & (u32)0x00ff0000UL) >> 8) | \ + (((u32)(x) & (u32)0xff000000UL) >> 24))) + +ft_error_t Sdmmc_DecodeSCR(u32 *raw_scr, Sdmmc_SCR_t* out_scr) +{ + Sdmmc_Resp_t resp = { 0 }; + FT_SDMMC_DEBUG_D("raw scr 0x%x 0x%x", raw_scr[0], raw_scr[1]); + resp[1] = be32_to_cpu(raw_scr[0]); + resp[0] = be32_to_cpu(raw_scr[1]); + FT_SDMMC_DEBUG_D("resp 0x%x 0x%x", resp[0], resp[1]); + int ver = SCR_STRUCTURE(resp); + if (ver != 0) { + FT_SDMMC_DEBUG_E("not support!! scr version is %d", ver); + return ERR_SDMMC_NOT_SUPPORTED; + } + out_scr->sdSpec = SCR_SD_SPEC(resp); + out_scr->bus_width = SCR_SD_BUS_WIDTHS(resp); + return ERR_SDMMC_OK; +} + diff --git a/bsp/ft2004/libraries/cpu/ft_aarch32_asm.h b/bsp/ft2004/libraries/cpu/ft_aarch32_asm.h new file mode 100644 index 0000000000..82a30ce06a --- /dev/null +++ b/bsp/ft2004/libraries/cpu/ft_aarch32_asm.h @@ -0,0 +1,316 @@ +/* + * @Author: your name + * @Date: 2021-01-22 16:30:56 + * @LastEditTime: 2021-04-23 15:16:10 + * @LastEditors: Please set LastEditors + * @Description: In User Settings Edit + * @FilePath: \project_freertos\devices\ft2004\bsp\core\ft_asm.h + */ + +#ifndef FT_AARCH32_ASM_H +#define FT_AARCH32_ASM_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" + +#define __ASM __asm +#define __STRINGIFY(x) #x +/* C语言实现MCR指令 */ +#define __MCR(coproc, opcode_1, src, CRn, CRm, opcode_2) \ + __ASM volatile("MCR " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \ + "%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " __STRINGIFY(opcode_2) \ + : \ + : "r"(src) \ + : "memory"); + +/* C语言实现MRC指令 */ +#define __MRC(coproc, opcode_1, CRn, CRm, opcode_2) \ + ({ \ + u32 __dst; \ + __ASM volatile("MRC " __STRINGIFY(p##coproc) ", " __STRINGIFY(opcode_1) ", " \ + "%0, " __STRINGIFY(c##CRn) ", " __STRINGIFY(c##CRm) ", " __STRINGIFY(opcode_2) \ + : "=r"(__dst)::"memory"); \ + __dst; \ + }) + + __attribute__((always_inline)) __STATIC_INLINE u32 __get_VBAR(void) + { + return __MRC(15, 0, 12, 0, 0); + } + + __attribute__((always_inline)) __STATIC_INLINE void __set_VBAR(u32 vbar) + { + __MCR(15, 0, vbar, 12, 0, 0); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_igrpen0_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 6); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_igrpen0_get(void) + { + return __MRC(15, 0, 12, 12, 6); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_igrpen1_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 7); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_igrpen1_get(void) + { + return __MRC(15, 0, 12, 12, 7); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_ctlr_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 4); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_ctlr_get(void) + { + return __MRC(15, 0, 12, 12, 4); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_hppir0_get(void) + { + return __MRC(15, 0, 12, 8, 2); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_bpr_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 3); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_bpr_get(void) + { + return __MRC(15, 0, 12, 12, 3); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_hppir1_get(void) + { + return __MRC(15, 0, 12, 12, 2); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_eoir0_set(u32 value) + { + __MCR(15, 0, value, 12, 8, 1); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_eoir1_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 1); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_pmr_set(u32 value) + { + __MCR(15, 0, value, 4, 6, 0); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_pmr_get(void) + { + return __MRC(15, 0, 4, 6, 0); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_iar1_get(void) + { + return __MRC(15, 0, 12, 12, 0); + } + + __attribute__((always_inline)) __STATIC_INLINE void sys_icc_sre_set(u32 value) + { + __MCR(15, 0, value, 12, 12, 5); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_sre_get(void) + { + return __MRC(15, 0, 12, 12, 5); + } + + __attribute__((always_inline)) __STATIC_INLINE u32 sys_icc_rpr_get(void) + { + return __MRC(15, 0, 12, 11, 3); + } + + /* Generic Timer registers */ + /** + * @name: arm_aarch32_cntfrq_get + * @msg: This register is provided so that software can discover the frequency of the system counter. + * @return {__STATIC_INLINEu32}: frequency of the system counter + */ + __attribute__((always_inline)) __STATIC_INLINE u32 arm_aarch32_cntfrq_get(void) + { + return __MRC(15, 0, 14, 0, 0); + } + + /** + * @name: arm_aarch32_cnthv_tval_get + * @msg: Provides AArch32 access to the timer value for the EL2 virtual timer. + * @return {__STATIC_INLINEu32}: EL2 virtual timer Cnt. + */ + __attribute__((always_inline)) __STATIC_INLINE u32 arm_aarch32_cnthv_tval_get(void) + { + return __MRC(15, 0, 14, 3, 0); + } + + /** + * @name: arm_aarch32_cnthv_ctl_set + * @msg: Provides AArch32 access to the control register for the EL2 virtual timer. + * @in param {u32}: RegValue;ENABLE: bit [0] 0 Timer disabled,1 Timer enabled. + * IMASK,bit [1]: 0 Timer interrupt is not masked by the IMASK bit. 1 Timer interrupt is masked by the IMASK bit. + * ISTATUS, bit [2]: 0 Timer condition is not met. 1 Timer condition is met. rea-only + */ + __attribute__((always_inline)) __STATIC_INLINE void arm_aarch32_cnthv_ctl_set(u32 RegValue) + { + __MCR(15, 0, RegValue, 14, 3, 1); + } + + /** + * @name: arm_aarch32_cnthv_ctl_get + * @msg: Provides AArch32 access to the control register for the EL2 virtual timer. + * @return {__STATIC_INLINEu32}: RegValue;ENABLE: bit [0] 0 Timer disabled,1 Timer enabled. + * IMASK,bit [1]: 0 Timer interrupt is not masked by the IMASK bit. 1 Timer interrupt is masked by the IMASK bit. + * ISTATUS, bit [2]: 0 Timer condition is not met. 1 Timer condition is met. read-only + */ + __attribute__((always_inline)) __STATIC_INLINE u32 arm_aarch32_cnthv_ctl_get(void) + { + return __MRC(15, 0, 14, 3, 1); + } + + /** + * @name: arm_aarch32_cnthv_tval_set + * @msg: Provides AArch32 access to the timer value for the EL2 virtual timer. + * @in param {u32}: TimerValue, bits [31:0] The TimerValue view of the EL2 virtual timer. + */ + __attribute__((always_inline)) __STATIC_INLINE void arm_aarch32_cnthv_tval_set(u32 RegValue) + { + __MCR(15, 0, RegValue, 14, 3, 0); + } + + /** + * @name: arm_aarch32_cntvct_get + * @msg: Read the register that holds the 64-bit virtual count value. The virtual count value is equal to the physical count value visible in CNTPCT minus the virtual offset visible in CNTVOFF. + * @return {__STATIC_INLINEu64}Bits [63:0] Virtual count value. + */ + __attribute__((always_inline)) __STATIC_INLINE u64 arm_aarch32_cntvct_get(void) + { + /* "r0" --- low, + "r1" --- hi + */ + u32 low; + u32 hi; + __asm__ volatile( + ".word 0xec510f1e \n" /* mrrc p15, 1, r0, r1, c14 */ + "mov %0, r0 \n" + "mov %1, r1 \n" + : "=&r"(low), "=&r"(hi)); + return (((u64)hi) << 32) | low; + } + + /* physical */ + + /** + * @name: arm_aarch32_cntp_tval_get + * @msg: Read the register that holds the timer value for the EL1 physical timer. + * @return {__STATIC_INLINEu32}: TimerValue, bits [31:0] The TimerValue view of the EL1 physical timer. + */ + __attribute__((always_inline)) __STATIC_INLINE u32 arm_aarch32_cntp_tval_get(void) + { + return __MRC(15, 0, 14, 2, 0); + } + + /** + * @name: arm_aarch32_cntp_tval_set + * @msg: write the register that control register for the EL1 physical timer. + * @in param {u32}: TimerValue, bits [31:0] The TimerValue view of the EL1 physical timer. + */ + __attribute__((always_inline)) __STATIC_INLINE void arm_aarch32_cntp_tval_set(u32 RegValue) + { + __MCR(15, 0, RegValue, 14, 2, 0); + } + + /** + * @name: arm_aarch32_cntp_ctl_set + * @msg: write the register that control register for the EL1 physical timer. + * @in param {u32}: ENABLE, bit[0] Enables the timer ; IMASK, bit [1] Timer interrupt mask bit; ISTATUS, bit [2] The status of the timer. + */ + __attribute__((always_inline)) __STATIC_INLINE void arm_aarch32_cntp_ctl_set(u32 RegValue) + { + __MCR(15, 0, RegValue, 14, 2, 1); + } + + /** + * @name: arm_aarch32_cntp_ctl_get + * @msg: Read the register that control register for the EL1 physical timer. + * @return {__STATIC_INLINEu32}: ENABLE, bit[0] Enables the timer ; IMASK, bit [1] Timer interrupt mask bit; ISTATUS, bit [2] The status of the timer. + */ + __attribute__((always_inline)) __STATIC_INLINE u32 arm_aarch32_cntp_ctl_get(void) + { + return __MRC(15, 0, 14, 2, 1); + } + + /** + * @name: arm_aarch32_cntpct_get + * @msg: Read the register that holds the 64-bit physical count value. + * @return {__STATIC_INLINEu64} CompareValue, bits [63:0] Physical count value. + */ + __attribute__((always_inline)) __STATIC_INLINE u64 arm_aarch32_cntpct_get(void) + { + /* "r0" --- low, + "r1" --- hi + */ + u32 low; + u32 hi; + __asm__ volatile( + + ".word 0xec510f0e \n" /* mrrc p15, 0, r0, r1, c14 */ + "mov %0, r0 \n" + "mov %1, r1 \n" + : "=&r"(low), "=&r"(hi)); + return (((u64)hi) << 32) | low; + } + +#define IRQ_DISABLE() \ + __asm volatile("CPSID i" :: \ + : "memory"); \ + __asm volatile("DSB"); \ + __asm volatile("ISB"); + +#define IRQ_ENABLE() \ + __asm volatile("CPSIE i" :: \ + : "memory"); \ + __asm volatile("DSB"); \ + __asm volatile("ISB"); + + /* the exception stack without VFP registers */ + struct ft_hw_exp_stack + { + u32 r0; + u32 r1; + u32 r2; + u32 r3; + u32 r4; + u32 r5; + u32 r6; + u32 r7; + u32 r8; + u32 r9; + u32 r10; + u32 fp; + u32 ip; + u32 sp; + u32 lr; + u32 pc; + u32 cpsr; + }; + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/device.mk b/bsp/ft2004/libraries/device.mk new file mode 100644 index 0000000000..7370439282 --- /dev/null +++ b/bsp/ft2004/libraries/device.mk @@ -0,0 +1,12 @@ + + +include $(DEVICE_PATH)/bsp/bsp.mk +include $(DEVICE_PATH)/drivers/drivers.mk +include $(DEVICE_PATH)/component/component.mk + +INCDIRS += $(DEVICE_PATH)/include \ + $(DEVICE_PATH)/cpu +SRCDIRS += $(DEVICE_PATH)/gcc + +# EXCLOUDLE_CFILES += $(DEVICE_PATH)/utilities/test_print.c + diff --git a/bsp/ft2004/libraries/doc/ChangeLog.md b/bsp/ft2004/libraries/doc/ChangeLog.md new file mode 100644 index 0000000000..b0d6bd9d11 --- /dev/null +++ b/bsp/ft2004/libraries/doc/ChangeLog.md @@ -0,0 +1,90 @@ +# FT2004-driver ChangeLog + +Change log since v0.1.0 + +# FT2004-driver V0.4.2 Change Log + +## bsp/ft_can + +1. support can controller + +## drivers/rtthread + +1. adapt can drivers to rt-thread + +# FT2004-driver V0.4.2 Change Log + +## bsp/ft_spi + +1. support spi ctrl to read and write spi flash in rt-thread +2. notes that spi chip select pin is ctrl in the way of gpio + +## bsp/ft_gpio + +1. support group A gpio ctrl, include w/r status, change direction + +## component/s25fsxx + +1. support s24fs serial spi flash operations + +## drivers/rtthread + +1. adapt spi drivers to rt-thread SFUD flash support component + +# FT2004-driver V0.4.1 Change Log + +## bsp/ft_i2c + +1. support irq read and write for i2c +2. delete unused rtc and eeprom component + +# FT2004-driver V0.4.0 Change Log + +## component/sdmmc + +1. add sd2.0 comand support +2. modify function and variable name by code convention + +# FT2004-driver V0.3.2 Change Log + +## Docs + +1. add ChangeLog.md + +## bsp/standlone + +1. Added ft_cache.c ft_cache.h + +## bsp/ft_gmac + +1. Added descriptor cache handling + +## drivers/rtthread + +1. Modify the bug for drv_qspi + +1. Added cache to drv_sdctrl + +# FT2004-driver V0.3.1 Change Log + +## Docs + +1. add ChangeLog.md + +## bsp/ft_qspi + +1. Complete the writing of QSPI driver + +2. Test bsp api in rtthread + +## drivers/rtthread + +1. add drv_qspi.c + +2. Access to the SUFD framework + +3. Test functions through the rtthread device management framework + +## exhibition + +![](./figures/v0.3.0_add.png) diff --git a/bsp/ft2004/libraries/doc/figures/v0.3.0_add.png b/bsp/ft2004/libraries/doc/figures/v0.3.0_add.png new file mode 100644 index 0000000000000000000000000000000000000000..d7e6b398490234df5adbf4ceffc91586da1621f5 GIT binary patch literal 297513 zcmZUa1yEb-_V(LCOYycqDNaiX?(W51io3hJLt3;HcZVXu-95MlcMb0D5CR|P+;i^# z&U`b;WOiou?EU&$zvo#Y3UcD;DEKHZUc5k;ln_yT@d6R+#S4VDZ{IxslVV>Q`uy|K zNl{$rMcD}9-tz^bnV_uTix-uVs1F9OpRbYaB{ZB~y!hb!*Z1X$DT(`w7f&Eb5kX~l z-NWTTH|5!tQ-@x%SeZ1$m_HrM{>@&Y*t0@^dQh+_6)3T9))Cz>Q@;HQ4rpjL5M6+A zv_Nkl#)gdFJ_s@R9Q)4Pm+-rd@WayRVtXEk6D*%jp5Ow%A5X3hW;uCylu9*gQJgH< z;%VMX24rZ6A@G0(SaD2kg_}XR11Q|X{nrtBlgd$pS^O+Fw->C#i5jg3SqZ6f6mR`V z|M_$xDXTCFW;w4d4$n%40=pSO{(mUNv`-Q%&YqIrsg5m!>?de?D2-MchdWvIerj zX9RDD2DcvM`Ro}JljYQiRd8e!IGIoLjk@#5E6gmhXuEa)w|yQe#4=I+RxU;xpuSv0 zPnO&^j7EralYCLOzF*@de`>jC^rx!K&uk?{+qua9eVpK}-W9nY@^q1f#kk!E0eDN8 z0Uh1rK?kCEq$}jXf=`!h(J-sbPYLjUZ@31WPecYNM^i8T`2O!(3%(1WK6gRFa%O$< zB@s+?WvKwQ?y>$~?~^`Mx9#iuWoU+mkKnV!t7rUAN9hHY^C$h!b?59>CNiL3p@=Oh zH5J^27A~b>>MsUbfQU4?C0Qs83T*TbJiT{R;o4WUM2J_EyK?QibD6|Q@YxBGIy*^y zoozmrn#o~pGMpyJNrMBJ^iCs;pSppBT^Rzdn0lVZ-e;fhA0@#K05c4*h@hguDQF_O z8s+!Y_)9Y%*e9jMaPp)QmCN z4;VZkyc){&8ePWnhBi}u1Z0x$4s1$k$~}aq-jF1hb$!7st8_3W;SSRkbr*W$yV#qh z`Uvww-Y;`H4VC-+aA~-&=FV8s*;_~sEngY}1-}gPJZtp8FRwgusC&GjFOSw(P^vaX zKP(-NPHoUlDS9-Q3#NE?R3Bbu`Ml$@mCKIvPNWU`c8;Y%Wk56LH# zN+oG1<>b+fT&q&b>fruo00W-bo@5w1qQ{FzVy@5asy6%^U53Q*(QfSzzEg%Yj!Izi z57qErRn*jhMjw{7!48x5%R4i4#QE5#H>o>cMG+mvpORPZ_+<7cUO2^$ez-J7!J+GY zfHV$qpd84YQ?g52Zm4a36}1N!(3hv__qgd;+z1fLFxFcPS9M|b_Ybc+jPcptEs?EC ze!&~SoZFhl_v!)Le+<(OncTe+5qaaNzovgE3!)O;T1d~lA>Lk1m}f1Wf*PDI^9BZx+66UB!Y8I$^;mou4VKs^SUzwzTe3#vO~ic`Hr>UX0Ltn< zomAM{^j4uFx5#1|!Q(onn3Zy(Rwg^fqp$gpm@7hyvz}6#>ZyHJAh`dm+FOBL5=;Q? zv0u?`q6pUrI?SH;oNXWxn3bGbH^n!tU~z;8rm0{|Vyxm@P@E0;JwB?uTXpMfz}V5f zT1A<3YTt*JQcKXgFzm(Yz54>pkStQ2vVZd?smJOtQT~s{72qud!%rWVOHu`bdXMW&&Xo$RCJKhkKb9`GXLTuZ5B!93Fj|?b zX-^l$>_{VsgVG5ypzf76H;{@=8%%qcIWVR&Sy#OLs0!FVK^nV|@6_teU;eHKX-iWJ|bF)zw%M9vW{WX~8DZE|z z^yd+NIg44*(Vc-jxs+bODGv@+N$2*S)9e|HOr9gg{2h-y5?nC=0~uY$dU(L z*Vtd0_dWhm+gzF#IauJ1wLmj=tT}2k*%+~*&0N4tWIoBw>5^cmJ6sqze2I=?#@w&6 zi|?}5EP62KTA&Ohm=G;G>AX=&)?zYVF~yLYja+_9TD;2jdY~hX$1#@?zCz@^W1(ef zhmLd2w!mUAf`2V>l>hW-4fFV@lQK=ag4+w}NRJkf@+eE~8q*k>Z!-#37CQhqFVD}lvhc*0?w$^qHK>F^`-9r%-71a*Up*brd2gMc^Kz&~Y z`xr^c$$b%Q%1BC+5%Z*O6HlD1OQ_?N7~4 zwOa>uGG!2lo3r(3QT1nIQ>fA8L;4b0I6zg3goH%Aztkwqs#J)c57?favxFHzzLJ#L z#y<&qh$V(qS-86if9oi7b(+x&W7n34_>SlSTem!P0g}C5LMIsphF(UD9opg-^RQnL z;S`_AQw5$pCDTIqum~3;H?1Y;an01i94Q&oS)Yu;%-7VHKBDxoKX^0$ zIt+V}a8(u}gkO+4P0~Lz^fPzURN6*eo#PDoOk_!5G>!i_2nKP9fUNAum$d1bZJu41 zgFWE9iP%OuVuAd|TZ~9?L3MF!3i%A+3||pLDU^^P)-z*c)dv&h9_k&LZ?t5|74(XUFVE# zpd@JKhTj1`28^GRaT#Jx`HqHfqiyURDnVlD+i1kx0b*>RmoCGqC164_^g0A}j(pVG z%UOatlYyUJ##?X{s5HS3IwZHsyQDSBLwvLicj@xcN0&htgI}KbuCT9WPbY8XTBYf zT)U2#cgLQFUd-}_dbKYU-k)%RSRqEUEWP=luF9kqLCeVnN)k}q~W$Hh5e zv%oE+c>Y$PIVGZOKgJBt4>^k$m@2w^Wks7bPU7aKMW|yrZgeWr#HvcFRkOQAwhn3SjC;)wOw`JiQa~X+4h<+$k~-@ zM5(lvswA^R`t2|2*ah3yHfgz+y-ib=C}rRZ`*lu4W<=IE|Ew zBQlkscfCvr8wqQr&AnS5t7*b)-}Zwp_%m!p)zjHxICuH6MXssu&^KJ+6ZGF0w@vD4 z_e~h^G5F~LjthjFTwbHg8$HZ%x6;U|ib|76{rYQvQ)B$h7Fm#bU~WVxld_j~%1u0! z8o(p7xAC7@7rtX*P=Yu42SWVu{z(s@w#sPF7Oi2iw0t0%qj+>McSbN0wEe-wnLT0# ze}m64jiAP0>=s!johPyJLwTU|9bD=w5G1e~EI6!CSZdC8QNj90e)Er-Kv61AYh=Q) zE=;1Is=%4?^VVQW)sXdNM*w{&GDLE1q>PJ|wg~KJeH>2}nGyuwc3?R3ko3@Qu2^&Y z%5|2QG&ePKD&4?estHEwZ&Sz+(>Ap>oBex68Snz5hwe<42kHICWxDJKUD2U10vJ6_1^*lwxe-3h7~B%*Deto6^B>#?4lAs zc8#1gW9PwN9p&V^*tHkDCg5`8@H}N6 zHsxLqF4<31i9~5Fb?M5y`@TLkrH0R^K z!BRxY*Z_L7ZS+MLK$bGOE*XCw#_2yM3H-nZy~zn-|buC|O7{v{AgD-d)vnxUJ(gi6L`F%odDi_^4)LRaP+8H;2o6 zIWu>O@LacTwI@`leC44Bc;1C}WM^a1w;F!q5xX8d+n#3Gx2M6&Lo&586ja#CFjQQv`PWBJ69T@Z>EI z%@1;6t%Oa3wB)E%SW>(X(QkiD&_Hg7SA8LB_eP#=c{9f9@skpS$wf!x54t8LVdrX)y%hYgACRAejNi&ykVmoE{7UHQls@uZ?JS< zALXZuqc5d|????2_+86)bPWf&N;91A+q{D1U)~v~{*(H40;hxhkx>p8hF66YM9%P! z=6-#19kx{4(-+#~!!pLFv8=`KA$g?Q6xTJdyz%Twjl(Hi&=*ok*n#GIdjaD3hSw%L z0z?dnkMqn5*)BvcN3EVV9I7%OB?aZ0vf74zMUtI%;xEi=W;bSu9QhtR@_>`3zJs)P!mve&lZ)YN@fOLJzOXiQX?Bx1^~=+BLFcV&nK|6k80Sp% z8V3e#fIcaDuT{jnkYXO|Cp|d&bm%?gNQ+RLd5zv0HwaS&#-0X$rO;jvoTltthc z=Q8W0U&T$7n6JyyDICd}BLi4a?Z=Zp7ca&haTrO&?&lfls2#4*)rVt9S1VE|fAUAh z+r!1%F@EMYYWO8VUrpaTdNUDEux$3;)D$j9%epbQ{nq#M749pt&BTnRagkZP>9=3# zEgHWX(yq4RirIL%x7oeCc{_~w2yyS8$fB!cW!lI|Q4CKG#NpOLY8n-g+MkrMwM&&; zcAM~_PWrRQ8O!q?nU}li1!i9iACA<*5=wZL@hIYZnSa5>)#Et&CnyiT;?FW$lDa$4 z1@}NOmCo`wHs-gsZ;WfeIe}+bG*D?Vm8le|6w>DMqp~#BFmD1WbSi1lbF5`i!IPt& z#rjQw4O3FiO%}Te2B0H1(o-{oRswm@BNN%jvZF| zRN~ZJlbg|S;fr%U{_f$R?$k*nZP0WmnVkU4#Yk|tB~Qr zZnr09d;0l0yR%sZn4e_+Ad-|V$elw zLGGhHA9LWX7i@3KRBbyYW1^7Uia|W;gNwgtE02LuSvdulB`OE+!cD$>u%{xFl51G- zzpDk<`ee%}m|dAao96Qso0r`F2D?ZL$F@J8!Vg(x=Q;0}h{LDT0mIURk>^E_csHlc zHVF9-hXi9>n|Ua zw1IIFrlyD#s#o0_VA}kGN9K!xU%n8y_$=t0Fn-W6F{$L0A{>*YMfPePj*&)yZrpc8 zIq|`;!Sy&z`IwCGstpnz{CiyT99wL-Ig^ z1gX{arM3jxZYY}GYmzQ7PzEcWfduV2-&5;+5U-R7K6N%7ikXL+U$=DKcSgzQ9 zdgtZ#@GHMGVSfu9;9cQk>jW443QWjwY>73|rnRngXK13Ar=*+j6B#6Zj`|HEGQfiNmi+S} zKOeZ;-m98CzbrJ5u9zVpT1)w9!i#XO+5d#s2Ax7`mxIt|&IN!gq$nAIw(%h8<3rK| z>qb9D?6Y^daOWiyW&AJ0*?fkab4>ax^xpn5N8d^NvK<82GrYc?meM)9RnQa56xO(+ zY5Dw2ilQ-@O+v-VBYCJztxW%;FL3?9_zvb)HcQMHIpp@hRqgnxXkFj0^6`XdXR*v> zC|7Noxcz#_t--~V{75CwANgKyG#=kf^IEsRjO02CgL)G*ZDkrm*)vH+h4jCtQ}G&XqHzlAf)e(jXb`#G+!O|K6cyT zF@a>(bvV1O%CIBAa++1yjwcBOqhHgyd>|$9&6tjD!U*)KH9jRAsvHhY(!9UxV`nUp zM(C0W%;q&=X0c@b1S!ds$NKb%A)Y6r!Cb(WSZB26v(0z{@zO%A{z{qjt-w-Wg(4ix z>f>uG8(j5pOZRGHBP23%<8fXGk~S4dML_?te1HK1Ab~AKRO&UnIX3)_m|*rec!jU4 z`}$tL6Xx|R4oAG;%!g3((rgw)N_?E^?_aC22E)F}N2*unsq{Hxk*y#7AO_JE)VHS2 zqWGwg|EdEK?ilnYJ-_<6pR3SLKVDWhL-jJ))>`j((;{AQt&SSa~gn0g=xXoTo;N_K`bkWnWA{raZ=RPXF zi%Uvt*%wAtGPmBf(HH3QiO4thqB()NLD*($&{DmDbu}0P^H)>NPt~h52Mfk9 z1Wdz*nl-Kc#(i^sFOu=foB30l*_*bUfB5>T%AJ`X^1UGFHFqlJ;dfGWYY1&=Wo@m{ z#YmGWsPXcy*$NUU^+r5IMFuT$KRCYc*At!WBvR!*Kws1&S9$MK-w%}heX@c&Q`m;RpJ1Op>KNEfDG z9_ij<)CGaagRB78@2HATeJ;UGTI!M}@q-%oJnB=KC6J+G7sDc-lt{gyjqc|Y6F)q8 zpy{45Stc;K&Bhw#qPn&=`aZa>0+=LGZU%LGiMhez;b_BGN+$z~y-&S-oSC@?^8{{9 zOZv#^WuD~IWw*B|!w)RjK(LC854Fit2|ZVq;qf4iwmO`K0$EbhwjbmLoisS`oM zVWX&}7%vY6tDpwpm+}>@^>T9k6*b;`W18Mf?O_%5ziDhy&|ARU&irQ@$E7i2JSN}w z77f@QuGGinxygxJ_9i+(HsFlB08;vi8Hh?T0{z0pmkFt039~Zwod5N& z%91JheBP{0qTIZlGo2&Tm)+xg6;QP)wS@0%y4FOt^fvShJAX!BB#DFj!57}thCc#a z9h*KX?bQWe(qy#(j=`b85*F z+R4XheFc-B`%}y6WfmF6AZ%l?l;5#{629K$Mg6bNcA6~m@5cC71QURA$J~L4Csm8L zy}ez4omZ9^`!|KHHmb!vI?za!Q^IfzL}A&}ks?{X0ZV~dz`YP{O9B?Yse9`Q|;-`a!npbp5XZQzMno@!RXX3g#Xk=W_Mb~^3T$Yg_6+n#egs9+_c z5o~J;KR+?QJl;bakxg!JW)Y7dWLoIFJRYu~PT$7DM)#~X>|&o8!3%Q=jBKLKn!ujP zZ}Ov{d{8n*d2Lxpf>YP{$Op}AC5@Hz5vIFBH^S#*3E!{27K)OFBB&HTYRy68NI03q zf`p(Pp%W<j?G_stn4?suNrv}M5wqcS<5$Z+^^ZnM&fDa@g05tiqnKKS;1c*d#btrf>5N@&#Yf`D)U2PsPou{@Zm$P>ubfgy zol5d#u8G}88_rI0qbGEf^R+`_hI`h65R>GM&s*lSR^wLpnh~EePrTrSV92f{QFF|< zpECDa)1bajGlx8p^7>Ji5rT-fdiqcM4mD-A{!N$AZ+mwUbPJ4i%0O@(+>1AjPHN)c zIXzpp|D0Ub(1|&28hl__9H8B2 zvfL1@a>_V=kVR?kQBh-WeCBX50S&G@JAA=h6(;Pm&~z?RC4Ezi z@X}a6@!GYf`m!R!_dH=t^E>poqCP z^C+q#XKRD>yuM!!(nh^51wVJ#j~`k~P2^YM&21t8P%QDyA4hnpuRMDiP(((A6ccWT zb5oxSu{I>hFibH+pX#ka3Fm{Go_yi0{*l|jc)Zjc3{+-LZge2jnZw1s==>J5;KZFs z9CYy&LZT-?V!P535(%WlYfMJW)RE8hoS~HB15VzN#uyS~5#meiPl6>HtAb2z7h>G7 z|J^Wk;*qdu)9@-*-wwzt&GXg0USI~Ft0+A8c#+EFgyZ1HFENT-)g?5pdV6sv43YqJ zdo4^@cdFH>2BgQ!Pd~ zAM(X@bn!X3sF2OdzjP~-2Xh6xD@KWrg=AV78SS@W!X`Qp`QVpcjjT~{{R$ZnX#Q3w z_W{=rnK>}#8dWYv=aiycw`JAC_C=`IOxJ@`0+9-N<13vZLD~$qSl{#mX7fGUY$aJl zEZQRrcH<8l!LGT2L{4#vT7#u&yI-#AD+aGQ#4$Z1)B8_#W$jA~SuZ)_r=V>b4heBB zXx??BxfzZ8rLT6xggHNCNPF%|=Aqe!h!uDFta^_GnZ*$KM0~%pi5e=(DwquUj@36& z-^zo5F~LKPZGPaQzto8xnNVgJygCsa(Eq=5ccXs#Ph`Zz8r#&+juwhT<)OyVGDCz(Y!)n$qZ znZ$gj_dJO-WP|9rq_>OJ*)eSW0v+z_gw#@BuXN=0+@1-a;GaO%3>-^{?wvLz%cc?| zm}@}sA*>{-*fQ=7K>A@Mx%F8(FHXnZ7mI3;Jge%IpqGF6)06#q$Z)OKR#Wz-9OylQ z2KaEpyxb23+~*o_J72P=EU;urcs!2*o)nK8FZj%G@fc1x4-R%=c{6Uh@MS*GI&!4JSOn}A)c2>+8u~ym`Np-k`e`a=XOu)5%WbjBV{lX5 z6U}hzdYCqSB9pr;Lq@YD93C7|;dX!$R9o^h&-or@pW6&~oC@HFO!B8@Z>a4D;zYI` zbFB?dTG?kMeJwY9emoBm=$y>W!h{)$bPC#C`rIsDng(wiKE9DAi$Qw+ z#JTVZP5mE(lUk7$-wtJ}NpT=+2+YJsIYzo=INfr_kPX3uc{_qfu7<%jf{wNLobqh z%gc_#2uW^J$erF{2Y1cZ$hM)(uADwpK9-E{X26HUyOS7;09Rbm@y+~%6Fh=G--ga+ zpaj*T!Uk%>^pk6%HdK7B5zgGfq?iuqmOdfuyVDc@zSynb*g}mj!&tr7*A{%1+qYtr zV5UMiP1RTUh-Pby{SIgqJO#}ig*ZEFduQM_W|iPgzqBd54DT!3axetkumeNykj&bW zCTsdMJ3-*$$ZNU0wl0{(e}j|T*`h&xKheL0rVNq&93wQVtE)r@JJ^~kZ zhTI^$_0sNKplUvOFm1*t!HHoka*HfAOND!4hvhj+@3<+FmTW=cm*@+ccCea28O5M0 zJTAzIpZ#7{70X#|`1Kla498B^mTpxg+FK|k;OiSPfR&oZGgKgx_LX&G8sg&HkVX?w>Qdm{9H$wUJb z6fQfwnZ-iwN8=ua{0w(+w9nEgPbLI@>dP$8P+@-dT{X|@8ctp`)cL+maA~UEv&3$s zs{eh1Muh?31rD>oR}hq%6o%%b7lwl?&%8-JMin_3s}W{I2?#$TVVzU<|K6A&N!^<* z=EuOOo3fn=jrLzqZ1t1=E>*0+C|IO6n!NZ6;x6^j%6DmBzRHY*Wt=b)20vn+SA?D} z3uKDb-M0!J-nsUXt$bZF(1ZqYPFo4}MI04wScV>qnFKs@HDAY${GC><+hCS9A@}&p%3TwZaZ*&;$Bx z$oR;9Ru_w@nFD_aG1pwD&*Me)q}Bt^X7zw)+oSnQ3oNk-I4k}gpU@=eNRL7|HiK|_2qf&6Xnm$KRZbo=l-_s6oB@<>s7Z}x_6JBg%C%bao@RHxp@iqQ+YF}&j zjtEhDcusBGs6-|%5>|%NJKqF5Ztl-Cwb6w#*L(AbBubC^ey@*-@*JsI<8kH5=J-0W zOaeI`yc6k4!8%=CaoUbXw#?;PK3!pG&pwrhvTrNhP=kdZfUsrH-!(N9B@b;w6gM?d z1IwSP9w9D%@{`-OG+2rM|i)$T9)t={!T7RNV z7x?&AhJ0_3TeDHkn&=T6E|il4edr1Z(l(VVUzo!mX>&6g3lsj@BDob=c;rwsA>l7O zd6R0mw-7e17K6@$#hcnV-k>EAgt`CI3SCPR!Y5ScYuZ`#Jst&607)}nv_ph?U!X%r zl7~*zyhB3V+kfIf739?_U*>%N6qVGIBammLME&Vc$;vy1lefBk87Ahj_;U}q&(WPU z&l|gz(Nt`KmN)F88v7}-(AS9FAhj<^q{6e$q6=KHKb(K4A#b`eZlU$KARbZr>b-}> zYgyL87=0m6TBMJcbk)$G$<9wNq1aX49OFwP4O3ZDkbqy(2WKVUW7??W7?S>Uf_w$o z^+EtoCipwEk)SLOQ8Sx3@>s2jKaZyO03^Ml-Yx3}+W`xD9J4Kb_j|1Tb4IY{{V~IZy_0gy7;WL$1L1 zGGmyk;Q3>Q)*(LAOB?k(?~i;f?(cM-tr44=3t^g~_9I#=ZT$f1+=^vkLq(6Xd-TSM zRaZ}*i|1aqMDuEGO4NwSHJ^&)bHf@qgNh-+-29OXmlio9#Z+2$`t$vfsnhuGD<8Gr z?cGUg-}-QuYD1`}C|1a-{}|+l5PHQ)h>3AFx7>ekb`J`g$_If3bQ1KoOvs zcr9|6tlr8)y50KR)D!keq-qf|auS?)_(O9kNmRpZ19irG!vVkk8%UL_kar+nZDZ?${@* z8IezJYYV6w`|<2Ys5m=IgT?EF_erzHx1+fI_ZP?qFpka%_o+YH2c0clFO9qJs@;FU zU^gm$zKk5ku_WbALboO&TC}pcckhH5Nx6b4aDiEYDw22BmTtzA>1Y{uCs{r|UJxsG zgS|#h-Cz=sSJ!^8VC`?!Sl=B<#~Cvutdy`hp{0Yb5xxRH_D_h1%=2=(va6q!edCL6 z`rY5<#pX;7WYHFlI2Zs*YfBb~Njj8A2>E)4k1b9;O!mR3$~}R2ASi@x4Er_K`9x!t z!I!fAY+mdJ*W~{Ae(w(>wC$qzzMuG?x2{}!vspr3DZXlI@4sxmo~``WP1sVJnv`Cu zk-c5>hb&JcNs|Rb$}R8>$#C%S=p>J{iBCS!)e(m!0dlz#7ZdQ^i0Du8y^GI75$`iI zI36TeXSN2Wf9kNJF!`t!`o|J|Bqc95{9IKAYyA2w(^BZjl1 zUG9KekGTxkG6i`qMuQ2(UqOhKEmp16>Q3;hn1Rh8r%P$haku@KA69ZJV9nb>=+{!^ zJ{&dq;uO8Z=Y}&484a4|E68=~6Ndexyo2s?{dbvrOEqsCrFrnfHA3wSKQg(wO|;%O z`&*&%a$}g@-#}ShzY$o6Mrg8D*}LYOL3L{ikAnP#4BRth@^j`-2hxze1~Vny&h{58 zgv2rNpKUOs9x3;~*q}9?Gurs(#g-Fh_kb_wy6?Q(L*K~Z7tK!@Jk-H>M?>|G0wUBc zAr&qsy$h2sHw9>JTl9t4SoUkq*L@#ZzC_UF=ESnTp!d=FJS`2z3D_;qT>J(Ga9fa; z(tA7(?K}_Z+>}MnHV@Y(5=pLJ*S;c@-r6ltQFh~OF=SM3XC@+fYWAZ|`z?jV&-~<> zVIr1zLe%Cw8699koY|eW<_vCna^>Mut-fEIz_rOnpu+kWyDAS;JwoW70FIep7rhea zF+(l13kyOr_JJnWi@(6Zf;WMs^7U7NrO~;Jt1)%;wIv&BaRkNw5O8+@fIa9%oQ$b- zVkOv~g*!e`Ixu=}NFaX>9}{N4nC>XG(%=V#ulgA;*0>DO3a=@D=* z80r1nu~5;h`ei|P;5{V*MoS9;M|6WbU^G@kD81<9lJxi-hv~|QR_@2mcZXi_IcD^Z z0^H#xVfL;v2S2X7eA@qGj)h#V&SedgUh83g-MyBjC*tWH&k^TxU3=B$K!v^e)zNuF z*%F^Uk~V#(4`)Qiy?1i=EpY(Mac$~*L}ruVFN4@t9@ECijcXA@e(d%we9dw7N{>aI9#v%gSk2wTs z{AF($B&c4|rPAi$Z$nfYb2h zF{c(zt3OLC1)WLc83iSWKN@io z#f$f^d9X69uz`PjKbt}nXTwDM{3RlqdTuhBn5}xB?5cAv3a*wyu8oF&^{Thw%2fRE z0FyKjevodzW2>pIWvxUL39%WDkb!#t=K0Rl-P4EVetZ#45Kz7&10y|(Mw(Jwu@h(2iuI+ z=ThDeT_KU-Urr}TAcQOLtuC^E))AuWAOPs+B)`pJWoN(QYVyK|`hvr(+=v}Lw-l6v z^!;8nyRtOAyPZ4rJv#{@-|Wy0;#N;Ql19O)UBoorF(Ap{rII=D@@1Fu=tm%_M*CBa`wz2`=b9B)UxCIo~Wr z4Ib|X8P7yCExV;P;8-&Yw1$Fxq;+7~yjB;>)OLQdxuL-GsAU1PlBw~cf7wOkecJQV zu@S{&u#34MzJRIATNgX3`mNwT7i+{8I}ly6tc6HwKiYh$l}&s31dJE5h^P(67{Y|~ zN6}2F?^#!jYxo+IiE>f1NK=terHh!^1UB9a`|ILKg^LRrb!QaW z!W)(zl<<(kqk9lw`=e^l-lYqOz7^;PQ5WdlIrvK~p{}`iW7p6l$*PMxAIoA>OGS&K z8ZJdG*U}uDekb$GbT-ZQgctfMz7T}id#7(52PoCwdI({4dwO-?12$$9yhmzHR?!g~(d&sx?mDw) zjTd|OXh+RI-0@Ck*OK|a1uvaQKF1lzdQ-UQ&mzSW{eMGI#IpY<2;$1~^3`Dnd1_7~@>V@}=%E(5E5iUORJ z0CdZcj9Ml0-0IsKU+6zf6j(os3g{o>@?vydpvKU?=@H;eTGa=7+a0b3uMrhkJdU(0 znhGx0L95QEWW7N(KOZqUuVLQL9u8q0fWn{l!K~Ltsz)0wz!!pw3tDD3EOyE?gmgpn zZc#MTQ$^4SaQ z63K)$p>~%IuJ$*Y-ykeDyUZ@r*+P1haw6dMj4;_E*=fYV{u(doIXi?q&$&Z@|Ey)K zTudujSg`xzQrWG~3xF{Hs+#aOf<>pH4}6BNE2ddPWb-^CliApGmhBxX9{U~AGV@%l zdQ)hqT2L0`c_M)wd7z1#uECYpHz^TGsoW7&%(*AQ{!X$>+J|HP&?f?g!t1*f_e+D> zba%&BZsR?=T8$_6I9DA1IYORIGS5IU_oYcH2?iDcmZ1GAk?nF*N4S>T+c2rG6g&wz zsIWD|GSP$L~d=|zxnnyI65g+csKCsvd~P&+qHGDh@OQ*kDN&$GwIhi z@8kRTF>#Wf>%uc(mcQ%Fe~`wm&AC#-?D}Fd)8(A||Ei5y9r4i<8Y#{9NH5o)6cfkn zwQH60cBYJcWxGb!3JvG^ht%78OtBt-qZS<|zEQ9RdAZp+F|Y<*`pMYZWXfUjY}{R3 zc~z}FkQovqowh@RQ#tJHCvV<|B+qx>Xa0xf@6fPbU#7N(oKAz4c4`^1gW zWY4>7ANH}1V|Yj1TY$0-@E^z6W3Z1Zqo9r8RhdyTcNqg6fu?~eK-@MD)X{#YjXwI7 zXPY-?fAN#=_e7fIsh-36`f$wRtyOL?Jqwwhr{$~ES`9NJ=7F5^>%pN z4sT;y#LpwEtP0oc=_xuy(3S*3K)7=i1K|sr+N-3~J~G`MatFsDp(eIU4qD_2N5_1% zNP5nB%b~Utpg)LjI7r6$VJyOZie315w_h3zp;z?i9E=kWYLwMd)eR-**!x^;=GwP1 zgP&SQHfK*3vC|l-K2c4)jY1`lgy|Xn%=t5(Ie(T2ePWJt(daIYndHDwyIG&ie)2(P zd3)pdyNH0D7-CO9Du1m%LQ7W8yx-!(esc|%6cH63K z9Rc!ab)t+}`}e8;QG!ne4y&J`cDBW!AA+y(c2Bi!L5@K=g30h_LUcJ&{!>bSoiK3F zkV=Cq;ZS_TjtHTaMAtrkQS!9$uDscRH_lM6plk&rJy2oSvbvoospiwOk6g$9xtNNb zF5iKVD)?55XHVw|TFb9(F`3vJ`9UJ|+Q3pPUYYo{YiY~r@ZjorC8(x`IZq{J(sl{O zZ!&Ye+Vrh!0!fl2>9XDb&(-He4iC^qXo;oJ!@O*iFb)moF^w$NZMD+!jL~7{DeNc1*=&oP zo||uB9P}n4^}RtaqCb}orZpk#ocDFmL5`QRAsKHRL8!tzrAB?*Y+>elt)~|gUZj`S z{$8;L54Q*W7$jVS5qjXh>O7S-Y`ZS}OFF{%mGZF!%;RJQpb1_^)X8qq-#QwbS+LT@ZV?n$IdGt)zx6_0@Y zfqeMHOY-E12(q0@mmB&vlDP^YcvpK9J=u1t$^ImaXNH>+nif)z# zo2OwF)d!T6#Xh4b zT%I~xNYBN>*8o252P2 z&zRk3!}u*g-T5xV>pN9ICLB3Z5gee=$YBN#N7F@o6wJTJ=qKV$YGQ~|D^$aSF16k! zO2Hd%|4%9*OQb2}a;1^P3(;2R`043--O$vyMqjwL`9rnzq3G$mrRid%59WqsWNRO- zyu}YS?F__`vd1_1iWccSzFmP8yfF+9BEy)EG1PqnMgR_GlwaUlf88t4C?r>KE#{)7 zAKhfQ;M!aoWXJ1D7m3BvSbwJl34L?0rB8*fs-`2iZgTtHla&=?K|sVpc=7DuG+dgT zjxX>w!2Lt)q~vJan}|N&Je|_cp9*WM^oh6~ZE07itCBKkXJ-S(#%AwH+~Y}t+31&) zgv&aXoj&pp$cOM($9$wu=ow|c`SjCG;ln^y{#{+A=-N(?7wK0=&mrQ7^b0a(e!;1*-|OO~=nRqpxM?!NaoU*n`_%iev;Q08r* zK?7`_vz~rwreCJu{Svf!Nvq2{+I=(S$Zd&=AhgXbZhGP)A*0QEw%6DKKd{pk7$w*6 z#Dq8VB6~c?C>SJ5?o)Bu`ki{;KWn2R16a8=2WdiTAhZ)ke)HV}K!li#7_ zn=?$kwW{OC%_>VM3H;JhpLgc19xtw6<4J}DIUeXd9BJ(@r1rxQF1_;6;CF~w^0_c2 zX7|T^Iw06XO4HE5tFKq2(oK%01awWmZWkav?+BUE+82L|QVA-|(JC7w8sYEHj$QB@LZ z@kPD{r+;DR@?W}i&RA+{PhsM7xYZmTRnu7Gaw3RMmJb2f2NT;$bqUm=4q*)+H!fEe zeheEu-*2T?`we67(Wow_Z7 z0#mtDnputJlg)K=p{A~Vn&xN-Bu)#m%R^IjkS*qiC2|XPFud3F++#12ZEYv#m z&ddafrMDTl%X2`c6uFaMu}E846E5$v!$>Wj3#=aOJ$!i%;hmR1t!%BYg8fX# zN61F(49$a6=H_SehHmv5GNXj0PN{n9DARg!Cb*gJZ<=|BB8?{z<(==zS*Mk!_SyXp z#yz@q?~+}ww{b8B@!mlHEh>B9%po~GX6Jk^&Vv0xcD60~mc`tqNfAGu!*|izE>hclev z&VBEFo=-hf^`^K=8qeQSQU@uL5sGf}jlqYWMm-pdLW(}$#MQwR9bLtIP@oHZY>BmL zUG_V+1R!wxEMqp7WcNegs-FY#UTRQ{99`2t`(C+V?iUWC=BWZ<^})3sZ>TqU&z9V- zeYF$$+h{-P@ZH3!+ZfAcg>rAVPN&a1hIPDFc)Z&8e7r_eFhT3jx7_zQWH+o`rhqZ~ z47N~45H3H;FJ*uH+2v>dkD|cYTGJ=o=Nj*M?y1XSiJ2ibZwAnA7(FEE3MO@F@;d-h z;*_Zgw(BRyKzr5gB*xOT;gGv~Q4}O5zBw^fQ9y8^Kb__miAro?3O1s@FKk%rTrkj@ zb_*CjYx6s5dhCh?2{e6@%;>aBJvN?Retg3auLUl_pxDE36Yh&=WwF2c%=oeNyY8}C zx&E_&z zW;qD+IVE>c_Su8B4FP$FLV7cvbpXdUYKqYggp;KCoXgdeX)k66#-W}4Z3(<$>BZQ8 z%|^GNFLRymOi!YJDx=!JPd}%1EKg#<24^)Bq-Gcrk-x z(w~YdCl%1G7VHCTK#9{AUTMawy4SGLVOq8g)^Y)cnSV}27uH1Cy9Ifqus%1QNs1yIGhaC$Wb$p0u zZ?qSuMMX|0?yNW5J z?&^PBW#edpxpn~hUPMtSJ^cppJ9U1r?qorXPJ5My59mSa;hB?3oZZAmDeAo!RwP&p z%hrTIYE0+s&iM|hgJ4^m(?mnOxXyFbWMMCSJd#$@A8?@6YJ~qyABJaUs;=;_y)dRo zC8cCdUbJt-H+pqS$n)p0l9fswL5TB*XPl3?k35VI-zJ)yZgKkBui&q(^B7Gp5G$~! zqi13OtS-Jz5|pt;o6M|cr?WisiHY2>H-RsNl;d4&xqN7WvUgjN;9cz=h6g#?(6kpz z(p!0|7xT3A2~S>JXHh#!j{oIF>p5N&vTuqpd<12mmVtNG3wT!~#j z5~n7VaLMOEJPQpupqHC$^P7IPbZVi}QeUPrLhi4Q4MxHGRy0R)O zaJD3U>Q1d;M~Q^ir!+m8Sb`sL_;aTFn2f)DaD1&2S*dME!e{3$CG%}7S%|ghj^ea2 z{51byZ{r3wo#GESaoIcp2zj|yT@0TW-4WOiiyUs(KDyFD(-p?}G?V#q6!}J>QnyY& zpD}H38^BzKPB#6un%(*#_Y&Y|;{9-Vs}Rsr^~=|KVSl@(<9j0|=1{03pz+MsYkun+ z&Of>yKZyS~6bh9NOL0>thHeiims+GeWpw`Mfpy3CW~ z{^kL6qsbm8mDh`urYuHiE5Xo9s|WP{=LgX84K*5&0Qy)-YX|bK5?m|bTr^y_OgWO- zS57$FYp)^YbHoE1*K3Sc`_Hh(tiAM?V>ixfw!#Ek&nol)5;K%wrSW3`1w0oL?^t-d&$WHB?6IZ8;hcldJfXB~Tg}i%%n+?NGus>2e3^${x4#)h&-n){)J1zIpr3WI-i0#5sZ_9uC`s{A>w z6!h^8+sV$?tk>L<|Lk!4=cbad{4wrV?GKF!YA6D-UOtZRtHi^?gb} z&BvR}4}PtlpRC?9fuXd76hFVgQ@-0sdYdPTY6K5ZuADyJP8$pxAF}U7NnD_;ThLkRWKfAqLh8;5I{>N4lE&OI1+@c|p7T6Oo->vyN=^#xql=y}K`ao5OehZ$n2ukcjYx%E4+Avt%aqr`nJWj5;fgZAre(i}{`;)8jX7e(ilnej( z7MVK6jz&`wrPT0cs4kkvlL1s;!50Kh(01Dgq%7i89j}K3m0pRbNIAyr#2wI?$1!&o z;96r(mINgZ=ZwD5FR$%F&Z%4wt&y^;Q^I$9di*;@(_uNZncDz%SOJBD)HPv=7A}3^QrTM(vb9-Mu+ZWwyVZv$f zsxIdF=fxh~A|1#XmX$Z4uAPZ^3T>&RAWcc~sU%(9|_B zzal91k)f@1n!{-W^CEk^r|(bWn`(3wLWrfJS@AZf7k_l{b|;ed>Ok;nDxZ5s{aQXx zcypIudJRo8nMZt;lm1$=_!{g!v^h?uqv0ZV*JK#MKVB!hqF~vekWmuw(B8pfdPqVz z&|q)O;SP#3C?qB(R{OBKgX8}Md9${d2fICVx~wPYoW3bfwbJnlptfK0Y5(SlFMjdF zMEs5c=4}-Q8~JulKNvM~+dQBb^cY1us4De|J#iXxdI(kRxwIV+PH8i*^a;K?)P4J%c3!>87oR;{f^0L%PvO44T^J%M_+d#dhc z(D7Ruf1hf~y{k-Ir+^RgC{O{BCX$Gl`#n~O@zW|j3w64sMY?jHHY70eT)0|~mgnLd z{vYHp4z2sU)cU}&8-g8s3_vjVV?FJ-lb~;Pu&`>n9v@FkKwtx46yn${RXq8^0N3MV zD-FhK{cXf>(>8w&kjvz3_qc5|bD`NTRlj>^84FKRO6u7(K-<@zHCdKso(IJv<@0VL zjHy#KSdUx&-g|Lu4$x}fq6KwUQM6P+FoRNl!>n= z9`o1*`FHH-k;m@>o6*4TDO%&evd8D=z0tZFJf5Fu0@*307T2^s(EFx3I$i`v~JKsBsj6%B_T}(PvTMs(&TS#z@w^4epkW_V9Ee+@&Ar8)ixyizX(4X zimA26xt^E&N^xA2+NBetIh&|#a2KI^2VM#qAMqO{=3?I$8-~w(#4OAm4CeCW7hEv~ zQ{&i1Hd*>9ezEJ)f82o#X==C^frPP@z!jcR{)=9$+sFeWfeVVHqM~KxpOFTMBn^ngTLaGI;DTJDYN z77t#d4l=G_2HsR^;0;`_zde(Mp~YD6si}pg36lXuiqzseMz)B{dJfZNr^FWV{L!(g zdf8mBh9G;a0J@oh*{ow8l+}gNGt~V(?@x*x1@r_9hKmb*Q6y7#$3|aPP#=Ptj8!=8 z|757m%d4U-x8%cxhk0Nk4l=EuV3{hY5un~Zg>N<8o?A{c-^jeKvdM&F+@R1aLJa+F zJO+_=w#E}^A|n;jFO#SqOIaJv#T61A(a%073U$VL00QRKQJ!csIy6(!@7n1WEOh;2>ctmm^IyDTB8aDHR#EBtA z2y42MQth=Y7siIk2TZ6f;CnXGXO?WhKPPJF5rnm%I#Q|D>Z$2Iq*9#??{?i6f^L4m zTXRcW3QyNFJEg8~Dpam-Y~hy|WNgJiz zif)CmbJXdhJei;VWT{el=8ahWndy+;oE&)XJj)Bw(bZ<1h1W9eH`xXtG{0X>R#%mD zGBb#}UaKo6h(O}3x33io%)t8dlznaC82pE_gRcOGp_^I% z#q@Z>@jd62*g$b{=mptG24xxZL8wduQ9P7^}Pya?kNvcYp!oTV%vft(nnd|AABPeM&Ek)$SiVxS)DH79`_)lz*@5^r_>bbv35H?>uv$Wz- zJLR<_56?$XvVy{kVA4Mp4M<|B26HdQMG?98%Wq}govn=B=r{Mz^)*XCPuEAq)|oWw zrsRrF*fstH%{~!$C@O7GYGhbccKezyqmFk_!OUJ^`qkSZBUe$>Q*7zs-XmEJ&Yp82 zM;#Y%i|V>vToJePUBQhvu=Ry2UPf=M`L3yEGf0FS8^NjPpQqs|5m4#y5V!Tf;IULp zy8TA%?rwzk+r{%bY5%_LxO8)WDdkfQ^J2PzzS9c~FOo?k|KYlcxa}guIs2W~(*Ojl z#&DKbfToz|u4JbVXn}IMYN+;70$`9aIA%%wy;mU|XOs5tn%85Jj~fVj%;=q{K9R0) zwC<=CkAD3=uwrBWnqpjmvjLMEL;Cu3_rnu)d(#mz&{8%TMUw%>pRpHSKjb=5^IGQ}=`2>v4(@Oxkaw7R!-itm$j_T?f?i!khZDT5Xkct}evGOJGbt5bz@ zb4RzrFL_aZF8PnAa=4|+8zWxDYB>G%r~MT+lu++Sq6u+#JBjaJ_=Y@v z9TT`}@8=~-bRS+e%?+wT-i6f9s93#$w;dYQj3j-&5bjyqN=;$h$8!I0tDdw=1Z}x^ z$5e5Iw7@fXN?gKcL-Vievi+Be?b7;Ygy0;5sg6biW@&lWVBSS5l6!qg{PKaqtiPT4 z^+DAUwuQ=XXJk9f_jkwe*BZ*a`;}taTXBll`7KRd#fT#jE<_+-thvQz$r@M(%Bo87 z`~fjd*mlNL*0-{X@b~v$ff3YTLVIg=udzr6m=edf$CFJJ5c_zb*kgIID=7X+Re97{ zN}+5Yzx{QchPRr&(SZCzI)Q?iJj|#+idNAiq+dTkt)yZw%c|Uhk4t2;v@B_w z5$3AN@65`j(;U%O|2CHT$cO?F1wA8MsW#4zAB{`zFJC(VeZ;?MMlMNW`XSlK^5;Ox z;`;N|lm^8^6-InBV+=P5^&hjlt$u!f%Lqt@Aa~+eQ9O4D^P@korOw>I&KIg4K0YYu z&wu{(J`3B|dSQ7q8piA(=e3%1i!OF`C4S}ace(1WPsxvp_#k<5fqb@}#oV2#NU~wc z6OsY>y25J>=jsQYe3I|)t&~s$6bwqf8x@O}9uGHsdm3B`zZ489H=&~iu zAQdrOB8O0W_s!A#OxlFLnA=m4QdK|`Q72w&f07PcnQd)lpJ{cYDA}32D`G?a$GryV zHCi!A&?u7TN36Wrx0&ixnG3KJa;-fQCA=*|t!G`kQsAv55|Z+!5?5h5J_A`4caSCl z4Jn-7(fft-hPM5i%=a6=Y%1J$$#a=3Lp%eFNlthb-f~ z=3l2aspIPgF#?0bgWpmevhm4zv3poy9lrNY-BXER*b5A8IBvPPJXg zSO{t1XID7)rO5l36ll*jYj$BK>yS)Tq3IOlRo$xnXX)^jgEaL8wD5N&Z)4Tx^T#KK zT<$&Y(k(b1f|9x)@wm9i_er}A-`u}9GQ!%0e9Pg%lniI{fRF}--8$$AAb#492i`oA z22wtMJj{?LH^f7W4F9gsKPb?x@r0{ zr3UxDBilpOda_W#ET83Q($TCr-RqLxKRQcaF|{>5H@4Y**J-qAzuALP{cZD0dj;{G z7oVV<*0VaN0l+X9CuQj^Kg}e8Kc9$$R?G;F?aLbd$)rQ9+|Jw+!8u%T&4zu$s=fcA zOw~OYqV?UkWqeCU#8<;gxMfJ56D#49Stm9{t&FItVbXlH;baZRaSZWcf0)(Gtcw1) z5eH#u_Yn{K7xI=J-O{LIhXUy`CM>+EY$jH7s+?N`s@&tSn7&xKMjkt~Gk3Z**#mpM zvJztxLT-iUXc5FuW<1El_KK;zwh<~sHvh%_`q?pIOW0UVg&V3z+U~94mj>jXTm|zCY4h&dKufH39)UZQF~wwi(lSh4C6asPi9C=ta_ed+p)0 zClrzTuG9g)w+X0rY;oPClRO?(%q|WN z)bur7#tm%n*l;8oyh(sPQ51D+85i=RQEp_Hr$a>&D3$inIC zpR1=CKWyRdteQJ`;PRru0!LibjPCr6qw6w_ad(Oymimk2Zs>PBQ<)4YIRT=ISJ9XJ zhdJic@ju{iT%(`-??|of# zsF}u*-c?rTWHf}}LAu~ZYKUu+)Pu4S7Q+=Ue&!c3v!~&)fHPOQ0I&QKueGfL(2WxI zl_e&`3D|W$VT;`IZt)s^*1*e~xA$&C7!%pZp{rMp-$q%-bUdL3w59ZNLj%*Z10&!O3hJKQa=@e>2NXHEF+?WrK~0{pz5=r_hB3 zj_6NLc7OlZ;-L8T0OdVWeH!fuywjH z7>el6;qz21M!(5HgYe)xe|EJx-!B>~7cF zWj1mm9vHUM3#PHDU11Xm}bff5yrrKN*&5K;!g#|VpSU% ztvayn(l_lO4;}<_I9^r<6G08!Wmq?t&*l@u^RyA(5=kxC+HjrH2R3Y6^D}qX|7wPI z5yjbblnkIQowOsJCY1|nXo@|U49(l2wK?4yZi~7V1H!mqUEKAqmHTq9K@aySFf^?b z<`uqOk=h?!E@J;9b4#u@AHM%?5cA|>yx8?lI!@YKv!wOb={M{z^exg9U-zVMA=H8n!4Nl`!lwTPI2{Z?fJf!sUyf( zjdXmLe^TrvkH?q7%SdT>)8y5=v&xectqIt~R=pl$8lDBX zxQZx{CVl0vt;E+#0Uf^|%jhU-!Q(f3y_!F;{(W6X;PDV+ne-dFIDaq}YMWnwzipXn zno|ohQ@JwXUaBVE?TzQsmN7+`#f8+>n#ydQ-hG6-K}%o9Lnkv=)8{9URY$0tVB~ga z{T|6jvya4Ap!Ad5hP5B!TClsO93EzS9xkc1%SB2*IB#bq^(sDn%jSi+^G`zJwjZ7^ z4vP!fh9o$IrOa5GHXS+&z2vXa2!!$zO8=%+0(6ahHo4o78Z^WOhw)Qi48I3$GBs_S ze=ofJEb%nL(Y{SZmMCP#@xZ*4~#|L8W+0<`_q&5r#5l2#Hc51I%?^ zczO4i%8w*YaM8E4Hn?y0OG1!WX-arw&;5^SR?xxMY*gs4w~$w+B1$fGryVhms}8wq z5?YC;3dGXfbPkw;IxtpyhFu0ocDqL!?B$c$EXAr01c9))Z}mI`H;ocAPLamF&X=vu z;Nq|+&fwi0R4x1HfgDCQNvlAC(oV|-HaTWYjuyN3=RXi~;X7|c?D^;-<3h!4K`q8U z12Lb{C|BM@0p=<^?#=Vu1jWL^*Q`_37hD2TMo8*L=edn)x|bS-qhn5U6r@ zP(0z-N|dYY{$gpKkb(N1Y#$v=kZ_A>EnCG3gNst$t-N~1UdOsr1}IC&`S*_Aae@P0 zAbWlF^ekfbMrvsUBI^KYLC?{Ti8?kxtLA$vR|_E3c1I4gjD#dxxnqa?(Tj$d>+$nw zD6|LP^YDW<#ByUbBYw#DNvMgDu0%cnhM)cehU)@g_%ozaaY_Qml&&KYl$_2*y(<=J z4F^MMw-IFVu05B|6a(!Ibc>M630BPv64%!$Qh=smLKb_S`tQ!@*X#P>sE*Gkk)np~ zUPwa7MHUkWw6JeUV-D~!iRD{aG!cqB+5KVOYUy9%%KG|S@@ZOYaL_G`=9PrZn8^a$ z&#EdEo~nd{gW3T1NgOAKjB~Ca z0IJ|tG_@Dt;*@j4AG3d^uOnAgTLakf#}~h_2wMv+Q24misko4|8OY3Rt@_ISha<)xwMx0*z1_R`fd#_ z`xyQ1hziH6*U3$vr?CkZxTTHeGs%cf_=Z$7WS%eGiyBG1P0VrVj(DRtK8$6OBlp=C z^2UdD<=rb@UZ%fJhpJk)kcQu<)0!MHh` zw!UyD6Z68()n!XWO}UN=)qh zk1@j%1qEwhSb3F%B|^>2HSpi2?rmI$h%d^d0oCN>Hh9B$hzFI7nIVFs*Ap@6P)Me! zY--AQitCZBhnfKc#mwg`jY#EYc!Ab)?Q>7o3{#t5ebBI=dsP>4a#Ahp0o#8p+j@h) zRBr?=yv!g~rm{7kg_5w7tmy()FmMJ>+d~fn&Z(JqRh|Kbzl2eMdF(qnZl~Bkwr49U zm=<_jV(|qD^s|{ZOiQz`_F8y?BRuno{7HT9RG20n<=z6Ni_UdttHgbYg)58NoAi$S z%FT91YJ`cPR>h-7sakx#@6a@!Kf!13?*rs-no z2AkT7gt|l^H&3+g-L2atYG!n_ziE5N(QKtpuq6PY($;kUTZj6*jqC3AMe$#=V@*)v za$sYd`Rp*IHX$tXoMxbc&C4p;aCY_?fHbM?wwq5WCDGrePBpu1_5l4bYX3 zZHHtOj;rbAMWv{ufBj%-s(9NvrIauRhKpv`(hYUL?$7$(A#w~>B=%hH(4h6yic36g zRWdS*ZNq{pz;SYS)Xg)Q1_G$0$YQgj)74X_x%;Xk`S?BEUWhvgJHEf0o@*y^JSxQa z?-R>BT1WYxN66>xYpmbl6Mx-NG(x}A2>2^-U{ zwe|VK^K-Vjt^wk~3by!<4ZX4{>N>i*A)GV~3rSK&977`uLIn2V-}VZhVZTkJl`+rZ z5nDITlO<)3t|c{iPEJ4#&$>{c4egWVe4hI}+*Q;IXM;c?WVnC)14Z|kJ|k>zuIrfE z)0QbdFY7gdnIm+dwnP2fR|p=IbHwYE^qlxAUe_^A{7s4~Q%HcPN{y?wEeW4m`faO_ zD^2g=Jq)3fHvLj#iE~{jPewQIfkE`H?sW6tMR~xB1#(P(p~S?twzfG2yzBdrn3=y^ z2h3EI4P)V=gi6Vc3Mv>s?(4+e%U4(7hNK5kY3?t2pQdr zYb8>`juSRX{wJoX^_vA_Tdk?J7r!lTM#;F%1T?VGcEz3XB1+jt0Em_c4> zb(L)N3U|No5$zqIWZGgG=brv>Cv{Jv2%{t%;wPoJedUE=Wpj7US_*gntY^e#8%YzL zk^a|I5I^BAO#Jhx<(c>C*lRX!wuyaDvBIvN@f zJZDbBA1jdez4nTIkHa(U433eNSIB~$Tb&~X0!3(Ys(ibXh}waih6N3LQ{L zCf$&7NNY}r#jQKC6r2vJ6MyCZT%6Y2!i>%Mn1aNvA4`JD+V%bspr_}kCFz}wA{qlR z{OIj!9$@`l)%ig>MJ^-?cC^h5nGG=W&$GjPGYu8$Y8X9~n7`AZQuwVk|NL3$1e6L( ztIIP!NBy9fn8db{H{e2JEgHDAZGrT#HB8?mvTMRY7VlV|3kbeF2FLeHOP79c{P6*2 zC`k$~<%oAV&X6U~N162X!+aLMJVoSJd9Zu6THyjPRHG!YYS)c!*#3#iSP@`5z`9WS zl37ZgSE{Hqdk${P#%Ma|jQ!<+&3I0M_qug8e>`50+=xqIwu=9MAjU(*A`3uuW@GSm z+O77kHRfKDh5X|6XxXSWhu>tAQ-#yg=N;8VjhjoI0tH50Q*MipXIFAwjdOyWXYW(C zc2l;snofD+zdq!)9w>M)9|yG_U0pe*oj}|CFl3bycV^YieDhON;RI<=APPu-U*KZf zTx=gwk(eR52bIw@i#=0!t}?TOB-fC%U0XtyJx>|c6C`sRit_fB8;RV_de)wENvix< z5*$10cMj@z8_M8hTHtVPw;8U;7E$3SW>89QKV4s3{XkLoJyVIjYe2iJRWR`RI08Ow z%`cHFFWG%@Aw(t3n3?MCN5zuh0KI(?7)eYUUpq2N#+@`bi}u1$)~)bip2cXZGh?RV zgleff!q#S-`IDoy4#`P=qQ4LG)VV=FS!j`ne5`SSCn`k%Mow;kr0GJW$PBB5!5_0HgA$O4R z^ZVf2be>=eaDo!Hn_4CAkE#R$UoT|uDiW!{4(L^(&5P@ z8@+eciaphJ3@{!~eu3MrJYfY}9D4ftXs+a4%WJUQcBhdt+HS8$8+&`A%5d?J zB;7GQfanK!P7apc;U)LN=Ajq*vrz#_ih5Arc8SpN65g|Cr7=6KXB};R)rqfjuB!Cx z_!su~j)?fMGdexSi1&)3V)=l6!FQRP%%> z()z;chfTFWlot>~GniMsVi}Ost5iZUFz2d7Fq3a*rVE_R;&;c|={f>W&;OK765S3% zSnp1VxlNnPEdgr$rU0dy{paI_#dytYFPUa%7kC@KM4;`L*EoOek7Gq5-#ofpAmD2F zmRoJMyq2f6 zF;2EbiLa<1-CkSLRvR7^QmM~H++jaVXxQml{hX{x4`#uZKLY|9Y5EfeW*k#)VaF&- zbx{FQYFOKS+gSOWcs?kFt~?D#w3ZY8z8>H-GOccXS3uoO?a9-Z!%l@rC%~QsXA=2B zUU1{`AV(VbB;uarv}PhmLY>md_~?DV-+{|Po^g7@=f95K_soTS>KJe#n|pia4k>V< zuX-=$i#i62dQ4HNTVV>83Ti%oLCgK`&Nl+C1g`!Sni_?9wRw}pWNSRqYu^`IIq(xb zwqQ>dHx9`8@|UyQ{F3zTZOYjyIzRvJJXgevfQOb~&ul-rvX|XA>^oMNiF%q>5hyk# zzURnL(AMJmO(I!LuzP*r5}V<02S}Rn9o++ev*%gr=I{$%_k>S-*;U1Nt?X8Fa=wm$ zC?Ha+lO%Vl?4jZwnQNkoA>J4T01Y~Zz#Js$qFPQIO&d<#+w$Gm34)W)unYIiaBX{#Ol4VPZi1nE3tdYxBtEA zgw}!cOGaJPcks=l=AP#UXF{NJjTi53SKgGml^HZotF>c-kLJO-E;ed8&0wqpD?(r6 zc?uTOtm!Y;HK`SB?#5rORs>)q_isDv12jQv#|+P^t3fGJ(ng}$L$Msh0D#~I00^f5 zfS@=)S^z|DdG&c(egZ8GuNGU7dSZYJ_0a~z5vnryKq33yV7bncXr~OA#8v%uedbIE zmxp``!B~~=kZ0K6en0Be=O%z9iP~Dklk~PmI{ciDV@+I3>-jX>;MYdS*?Y@rqkmHN z(NT{4-G$;_*+EH=t2;k^gIm|$oKPGQgKt>rrY?{uo z`_1aDl42WDxFP)~5a$OWHJS_1eb?9Xp(IbMw;vG?9~eJA@afm`ttl!o(!#cbY8nf| z6{}NExqf$6$|=lMpYtZgLJMjefPNVg_f&< zqkH9@5vnPmUscIBvW3y*Rdoqu3mD~P-?am|Fp+=f!oajis3X&DBazL;D?0xzgC1gl zSwG)Q3&zbDE>1FDZMOtTNo`Px!8LZStMc)M(lmvdjP_OS`O=L++Hz5}Xn!`%WHXlH zgMO)frK`TmP8X)S#C@3XT^-nkKi=m?oH9TM0-OXXj_)qU@fVJ;U?$>ySQ0X38e6PJ z&rXaWfySQ33J6VS{Z0&b6e+RgjdYT2nel_P>wDCPK^-4=WiJ?xu+_pFin>y=v%p9s zbv{f0zMf;U>Cn{L?5ylC$K$fco}9p$Fyr=llh{t}VyXufWF>5jfPb0*MiUgj`S?cM z^rbe1`RaM^wyp&UKa#RKVaKMOKh^=g z+gOu3^1>OR7Y*Iv)&=<8HDdZI{M7ne>~#deyxQW`nZq)2pFG+NV~hX~_7v5Qx$Md5 zkA>#{H6%fi02Y`uZ81D{65Tu$vEG(jMRaj)G(RkFavp{8kMHgRfwMV02F$6e4)(4@wJ(yoEw==T=q6Yn;LrNSZvuWzK& zyFtvp%GUNMO;OAvj0Md+(Yy!rR~Ar6Z`qR4f2eV@jPfN%umon zZslE~iMr4PAtxKtP+ptbgwn(&9%hzCqgxlYkUGdiAg*a-gWF2nJ5Wouh!bN?6(LTS z>ibDTRGs zzjZz22-tj@*LPB=llt$a&A+?~3=0(yE^&G?W4lEG;0@-j2-mVa@wE0`yBn^sc^7Cw zU^gCo4o#8uDe)qu-57gFKVkQ(p6X?Pp@3q_wmRq)@!in1t*?9WL*9$?0Asb0W;lk% zpFsx07bWFvv}X+QihzqRViA0_AduTHpL6NiL0oQr|Ho=mJcY<6haWG7KoC>(X6(&h z*bLzQ(B$^wB>CWAvy$xEKX%m{&w;KwSjBRJ@MZ#_WfT0+9Unt5i%c;O@Ts^?&e4?#aq3F)kHR%H*HAZy}$ua+SM` ziyjDn!lKQkl_&nCTdx=Dba%z3-XUR*+u3pPYDyKvXb*}j6%OG0+0$6)aBqZ@UkYFC zi@={?@J#ki|0JIBF_zP{EyG0rp{e|yaA!_@x=%A*ZXfr?t8t6+LY23fDWMC<2{8%~ zzw;hYal(8MVUkN$5lr3g-SQYrA)xEX5ZLau#Z@^AUUldHJL(`Z4{E+zlnwK=sU4wMm$-3{=BA zH-3=Yym7tY%(c16YvDs+E|j2wsYE05lewkuRCCBV3u7r0*koXP@qia5qKbvCE{gWdk)dUtd-o9A zNUlW`VV=i;KhoMH@}sG^2EhoZ&mr>Q#)i+Uf<|$@IE56-$L~Je&e%(d>d>s^7aVL?()SSJscrN?dRb7w%Y`vXj zN<}p}+toPlBP$d&_mdm4=biX=aW2cZ$T*RQJLYMz>6{CiyN3TZbRHm&{Cz3>f{FD0 zD^YCfo~aK$gJ<$ldxN?E}Zjos#J8_<6_U-mRMK1EVagQ$W@_VVD0 z7}*^BZSve;eYB~wt1CM7Dz5H9RPP~stD)9obX+M9rG4+XS&irBaQ47mVdi^RobcGUcvfxuL z@_f9ZX3^8#(%d7u9Ts@4`Ii={z@fS4U`Onlk3jA+L8PjAIIRPEsY9CMmumJS(j*zt|GwdN{;aro8RSL3KjYG4-4r1u}xXvGQFTd}-0u%wO&e6d3hzH_#f8UO` zWPErMG6fZ;04~RGX)s;UUb81sYh^TaXf?s|?8Prh*NrW=NbWAB_0!ms)2Gt%f(;J0 zF1puGnxHd3(ET+Y81V^;J~0MIBvU@i~ts=M8JEqQ*n>`{kr`Xx~=N)6__%CFki}UA?o544q91g zeBS$EyQ}hC9b=o*81yPCYFHaI z=HhZDuu?@18`|@(stIo9|5)ny)%HP@4&Iv4aovngV$?nS0&ipnyXvO{!zr{ml|N_K z7^F$|?!j6sN7&}9y|CSl^#J;HL}kqI*sO4|?;ul0Q~KMyXsyJM}HqDw}LY7&RqiZNECFP#U{ zu-+Q(cDn1TTQt|rq>I<)#GXW?3&Y2n4M`Gi&5DNy`|&4RPzVYi;u`Xa?gpa{(vFE@ zuP+<5H%3bQ36Guou{@HNIz*T>ukQ)^s&3eg!Vf#UCu^&nf|&px@4 zyLnTv6S?8w^4K;yarX3tiTKlxmt?t-u?H>A4&<7v|rd`Kld@KcTZ1GqQFUGLK-Elq-l|2MBo>Ls(!uV_WsnQz^G8#2Eetk{KPV%UPzd$1o`Tkcxg1JP5{3e2vES{2=JI$q#=eqj( z9?LSXb zlRqyMAJ)n~SNa<94{yLVOKKUEMxy-7XT(?~(4{m)nUmo!UfdYERfHOqI6I`s9 zDS(4J!*pLqyPiTzuaH54QkiC(d0PZWB3XbN1EYf#IdJEcm^V@UU!BU2E1YLoF|J3X zC~$cZd~h@NWYMlo>15m^U*yqpBi1;h1pi@E_84C;(KV!pj}MKdGXxnWtjai>%I^Z& zG^vK+2Cv-iXioCaehf;w!FLWE-dw&(4}VRiKu}zqyE$Rq&4XZE%30EJ%ctPL%dj*w zHA;<^^`fD5r`Si?5dMgif^+&^lBP_`ERy1h2-x`gtwvhdlLs?q1p@KE-an0uC|Ax| zx(t_+GQ0%KZ zPbKaxD4a`LUalD8ljsz{%RTPBpy9(n|2Og-o%^N>RlPj0VDB+;hPF86v>X_zZye4y z#pnjAQDL)}U5rJ_P}l zF@JL#M(S8*2-^hNanzlXnctp$vl&JTABt{Rs^Ag#99VIDfj8MiDY+~9WFs+28zHaJTH$Mp`ck`%KlI zzOqeNn-EK{f|xTa!gag8@cm(+J0Fx$)_FpA{9M1OK4C#V;{Lqwytk#vAFykagXxcb zRp;^_8FhV05ZLalw{-56iZJNtOi+jwzd+aMh%IJ0I7ky0wCt@Snv2|!Fku)Kfpw2I zPB6dzU5fdU0*~XC*_pw3eD@lUr>K`0L8R|(z1I$2e||)#VzwJ7TN)V3A%@1A2ioY% z{w(+6xCe5dy6FbeItB(}AkdV6q)X}?U_z?L_(g_t7cL5gU(8<)VImn1i*L@@&#=~L z#MRK$gRPI{wQzXISh)xwaP!oKhG^M+f_Px^fDjOHZHB5PW~0Fsc`n(JH?gd$+Val6 zg{(DM$0yvJcLTvOw3_ppIUW13xPavDD*2(=Q)}VDfca^uW$UWMXPV)R*0;2L-Z~HS z8a(AGv%XC_`lutoB5RAdj92NN{cM$ooYJ&oF(1yemD;uWMQJcwmu7m6GGhiWv?pkp zd%C0@1pz}oCSW?>F{Y06 z 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�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�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 Y>7MZ&AurM=yClG)t7)K7tY#DRFK=p8xc~qF literal 0 HcmV?d00001 diff --git a/bsp/ft2004/libraries/drivers/drivers.mk b/bsp/ft2004/libraries/drivers/drivers.mk new file mode 100644 index 0000000000..cf1b66f990 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/drivers.mk @@ -0,0 +1,13 @@ + + + + +INCDIRS += $(DEVICE_PATH)/drivers\ + $(DEVICE_PATH)/drivers/freertos \ + $(DEVICE_PATH)/drivers/freertos/lwip \ + $(DEVICE_PATH)/drivers/freertos/lwip/system/arch + + +SRCDIRS +=$(DEVICE_PATH)/drivers \ + $(DEVICE_PATH)/drivers/freertos \ + $(DEVICE_PATH)/drivers/freertos/lwip \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/FreeRTOSConfig.h b/bsp/ft2004/libraries/drivers/freertos/FreeRTOSConfig.h new file mode 100644 index 0000000000..ae47a6bdd4 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/FreeRTOSConfig.h @@ -0,0 +1,235 @@ +/* + * FreeRTOS V202011.00 + * Copyright (C) 2020 Amazon.com, Inc. or its affiliates. All Rights Reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to + * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of + * the Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER + * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN + * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * http://www.FreeRTOS.org + * http://aws.amazon.com/freertos + * + * 1 tab == 4 spaces! + */ + +#ifndef FREERTOS_CONFIG_H +#define FREERTOS_CONFIG_H + +/*----------------------------------------------------------- + * Application specific definitions. + * + * These definitions should be adjusted for your particular hardware and + * application requirements. + * + * THESE PARAMETERS ARE DESCRIBED WITHIN THE 'CONFIGURATION' SECTION OF THE + * FreeRTOS API DOCUMENTATION AVAILABLE ON THE FreeRTOS.org WEB SITE. + * + * See http://www.freertos.org/a00110.html + *----------------------------------------------------------*/ + +/* + * The FreeRTOS Cortex-A port implements a full interrupt nesting model. + * + * Interrupts that are assigned a priority at or below + * configMAX_API_CALL_INTERRUPT_PRIORITY (which counter-intuitively in the ARM + * generic interrupt controller [GIC] means a priority that has a numerical + * value above configMAX_API_CALL_INTERRUPT_PRIORITY) can call FreeRTOS safe API + * functions and will nest. + * + * Interrupts that are assigned a priority above + * configMAX_API_CALL_INTERRUPT_PRIORITY (which in the GIC means a numerical + * value below configMAX_API_CALL_INTERRUPT_PRIORITY) cannot call any FreeRTOS + * API functions, will nest, and will not be masked by FreeRTOS critical + * sections (although it is necessary for interrupts to be globally disabled + * extremely briefly as the interrupt mask is updated in the GIC). + * + * FreeRTOS functions that can be called from an interrupt are those that end in + * "FromISR". FreeRTOS maintains a separate interrupt safe API to enable + * interrupt entry to be shorter, faster, simpler and smaller. + * + */ +#define configMAX_API_CALL_INTERRUPT_PRIORITY (16 - 1) + +#define configCPU_CLOCK_HZ 100000000UL +#define configUSE_PORT_OPTIMISED_TASK_SELECTION 1 +#define configUSE_TICKLESS_IDLE 0 +#define configTICK_RATE_HZ ((TickType_t)100) +#define configUSE_PREEMPTION 1 +#define configUSE_IDLE_HOOK 1 +#define configUSE_TICK_HOOK 1 +#define configMAX_PRIORITIES (7) +#define configMINIMAL_STACK_SIZE ((unsigned short)256) +#define configTOTAL_HEAP_SIZE (4096 * 1024) +#define configMAX_TASK_NAME_LEN (20) +#define configUSE_TRACE_FACILITY 1 +#define configUSE_16_BIT_TICKS 0 +#define configIDLE_SHOULD_YIELD 1 +#define configUSE_MUTEXES 1 +#define configQUEUE_REGISTRY_SIZE 8 +#define configCHECK_FOR_STACK_OVERFLOW 0 +#define configUSE_RECURSIVE_MUTEXES 1 +#define configUSE_MALLOC_FAILED_HOOK 1 +#define configUSE_APPLICATION_TASK_TAG 0 +#define configUSE_COUNTING_SEMAPHORES 1 +#define configUSE_QUEUE_SETS 1 +#define configSUPPORT_STATIC_ALLOCATION 0 +#define configSUPPORT_DYNAMIC_ALLOCATION 1 + +/* Include the query-heap CLI command to query the free heap space. */ +#define configINCLUDE_QUERY_HEAP_COMMAND 1 + +/* Co-routine definitions. */ +#define configUSE_CO_ROUTINES 0 +#define configMAX_CO_ROUTINE_PRIORITIES (2) + +/* Software timer definitions. */ +#define configUSE_TIMERS 1 +#define configTIMER_TASK_PRIORITY (configMAX_PRIORITIES - 1) +#define configTIMER_QUEUE_LENGTH 5 +#define configTIMER_TASK_STACK_DEPTH (configMINIMAL_STACK_SIZE * 2) + +/* If configUSE_TASK_FPU_SUPPORT is set to 1 (or undefined) then each task will +be created without an FPU context, and a task must call vTaskUsesFPU() before +making use of any FPU registers. If configUSE_TASK_FPU_SUPPORT is set to 2 then +tasks are created with an FPU context by default, and calling vTaskUsesFPU() has +no effect. */ +#define configUSE_TASK_FPU_SUPPORT 1 + +/* Set the following definitions to 1 to include the API function, or zero +to exclude the API function. */ +#define INCLUDE_vTaskPrioritySet 1 +#define INCLUDE_uxTaskPriorityGet 1 +#define INCLUDE_vTaskDelete 1 +#define INCLUDE_vTaskCleanUpResources 1 +#define INCLUDE_vTaskSuspend 1 +#define INCLUDE_vTaskDelayUntil 1 +#define INCLUDE_vTaskDelay 1 +#define INCLUDE_xTimerPendFunctionCall 1 +#define INCLUDE_eTaskGetState 1 +#define INCLUDE_xTaskAbortDelay 1 +#define INCLUDE_xTaskGetTaskHandle 1 +#define INCLUDE_xTaskGetHandle 1 +#define INCLUDE_xSemaphoreGetMutexHolder 1 + +/* This demo makes use of one or more example stats formatting functions. These +format the raw data provided by the uxTaskGetSystemState() function in to human +readable ASCII form. See the notes in the implementation of vTaskList() within +FreeRTOS/Source/tasks.c for limitations. */ +#define configUSE_STATS_FORMATTING_FUNCTIONS 1 + +/* Run time stats are not generated. portCONFIGURE_TIMER_FOR_RUN_TIME_STATS and +portGET_RUN_TIME_COUNTER_VALUE must be defined if configGENERATE_RUN_TIME_STATS +is set to 1. */ +#define configGENERATE_RUN_TIME_STATS 0 +#define portCONFIGURE_TIMER_FOR_RUN_TIME_STATS() +#define portGET_RUN_TIME_COUNTER_VALUE() + +/* The size of the global output buffer that is available for use when there +are multiple command interpreters running at once (for example, one on a UART +and one on TCP/IP). This is done to prevent an output buffer being defined by +each implementation - which would waste RAM. In this case, there is only one +command interpreter running. */ +#define configCOMMAND_INT_MAX_OUTPUT_SIZE 2096 + +/* Normal assert() semantics without relying on the provision of an assert.h +header file. */ +// void vAssertCalled(const char *pcFile, unsigned long ulLine); +// #define configASSERT(x) +// if ((x) == 0) +// vAssertCalled(__FILE__, __LINE__); + +#define configASSERT(x) \ + if ((x) == 0) \ + { \ + extern void Ft_printf(const char *fmt, ...); \ + Ft_printf("configASSERT is %s : %d \r\n", __FILE__, __LINE__); \ + taskDISABLE_INTERRUPTS(); \ + for (;;) \ + ; \ + } + +/* If configTASK_RETURN_ADDRESS is not defined then a task that attempts to +return from its implementing function will end up in a "task exit error" +function - which contains a call to configASSERT(). However this can give GCC +some problems when it tries to unwind the stack, as the exit error function has +nothing to return to. To avoid this define configTASK_RETURN_ADDRESS to 0. */ +#define configTASK_RETURN_ADDRESS NULL + +/****** Hardware specific settings. *******************************************/ + +/* + * The application must provide a function that configures a peripheral to + * create the FreeRTOS tick interrupt, then define configSETUP_TICK_INTERRUPT() + * in FreeRTOSConfig.h to call the function. FreeRTOS_Tick_Handler() must + * be installed as the peripheral's interrupt handler. + */ + +#define configSETUP_TICK_INTERRUPT() \ + do \ + { \ + void SystemSetupSystick(u32 tickRateHz, void *tickHandler, u32 intPriority); \ + /* Setup systick with lowest priority */ \ + SystemSetupSystick(configTICK_RATE_HZ, (void *)FreeRTOS_Tick_Handler, (configUNIQUE_INTERRUPT_PRIORITIES - 2) << portPRIORITY_SHIFT); \ + } while (0) + +#define configCLEAR_TICK_INTERRUPT() \ + do \ + { \ + } while (0) + +#define FreeRTOS_IRQ_Handler IRQ_Handler +#define FreeRTOS_SWI_Handler SVC_Handler + +#define configINTERRUPT_CONTROLLER_BASE_ADDRESS (0x71800000) +#define configINTERRUPT_CONTROLLER_CPU_INTERFACE_OFFSET (0x2000UL) +#define configUNIQUE_INTERRUPT_PRIORITIES 16 + +/****** Network configuration settings - only used when the lwIP example is +built. See the page that documents this demo on the http://www.FreeRTOS.org +website for more information. ***********************************************/ + +/* The priority for the task that unblocked by the MAC interrupt to process +received packets. */ +#define configMAC_INPUT_TASK_PRIORITY (configMAX_PRIORITIES - 1) + +/* The priority of the task that runs the lwIP stack. */ +#define configLWIP_TASK_PRIORITY (configMAX_PRIORITIES - 2) + +/* The priority of the task that uses lwIP sockets to provide a simple command +line interface. */ +#define configCLI_TASK_PRIORITY (tskIDLE_PRIORITY) + +/* MAC address configuration. */ +#define configMAC_ADDR0 0x00 +#define configMAC_ADDR1 0x13 +#define configMAC_ADDR2 0x14 +#define configMAC_ADDR3 0x15 +#define configMAC_ADDR4 0x15 +#define configMAC_ADDR5 0x16 + +/* IP address configuration. */ +#define configIP_ADDR0 172 +#define configIP_ADDR1 25 +#define configIP_ADDR2 218 +#define configIP_ADDR3 200 + +/* Netmask configuration. */ +#define configNET_MASK0 255 +#define configNET_MASK1 255 +#define configNET_MASK2 255 +#define configNET_MASK3 0 + +#endif /* FREERTOS_CONFIG_H */ diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.c b/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.c new file mode 100644 index 0000000000..c0e2504cbf --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.c @@ -0,0 +1,186 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:00:19 + * @Description:  This files is for freertos gmac ports + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_os_gmac.h" +#include "ft_gmac.h" +#include "ft_assert.h" +#include "ft_io.h" +#include "system_gic.h" +#include "ft_list.h" +#include "ft_debug.h" + +#include +#include +#include + +#include + +#define OS_MAC_DEBUG_TAG "OS_MAC" + +#define OS_MAC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(OS_MAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define OS_MAC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(OS_MAC_DEBUG_TAG, format, ##__VA_ARGS__) +#define OS_MAC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(OS_MAC_DEBUG_TAG, format, ##__VA_ARGS__) + +#define FT_OS_GMACOBJECT_READLY 0x58 + +/** + * @name: Ft_Os_GmacMem_Create + * @msg: Initialize the Gmac TX/Rx Describe Memory 。 + * @param {*} + * @return {*} + */ +static void Ft_Os_GmacMem_Create(Ft_Os_Gmac *Os_Gmac) +{ + Os_Gmac->Rxbuffer = pvPortMalloc(RX_DESCNUM * GMAC_MAX_PACKET_SIZE); + if (Os_Gmac->Rxbuffer == NULL) + { + OS_MAC_DEBUG_E("Rxbuffer Malloc is error "); + Ft_assertVoidAlways(); + } + + Os_Gmac->Txbuffer = pvPortMalloc(TX_DESCNUM * GMAC_MAX_PACKET_SIZE); + if (Os_Gmac->Txbuffer == NULL) + { + OS_MAC_DEBUG_E("Txbuffer Malloc is error "); + Ft_assertVoidAlways(); + } + + Os_Gmac->Gmac.TxDesc = pvPortMalloc(TX_DESCNUM * sizeof(FGmac_DmaDesc_t)); + if (Os_Gmac->Gmac.TxDesc == NULL) + { + OS_MAC_DEBUG_E("TxDesc Malloc is error "); + Ft_assertVoidAlways(); + } + + Os_Gmac->Gmac.RxDesc = pvPortMalloc(RX_DESCNUM * sizeof(FGmac_DmaDesc_t) + 128); + if (Os_Gmac->Gmac.RxDesc == NULL) + { + OS_MAC_DEBUG_E("RxDesc Malloc is error "); + Ft_assertVoidAlways(); + } + + OS_MAC_DEBUG_E("RxDesc fit before addr %x ", Os_Gmac->Gmac.RxDesc); + +#define ROUND_UP(x, align) (((long)(x) + ((long)align - 1)) & \ + ~((long)align - 1)) + + Os_Gmac->Gmac.RxDesc = (FGmac_DmaDesc_t *)ROUND_UP(Os_Gmac->Gmac.RxDesc, 128); + OS_MAC_DEBUG_E("RxDesc fit after addr %x ", Os_Gmac->Gmac.RxDesc); +} + +static void Ft_Os_GmacMem_Free(Ft_Os_Gmac *Os_Gmac) +{ + if (Os_Gmac->Rxbuffer) + { + vPortFree(Os_Gmac->Rxbuffer); + } + + if (Os_Gmac->Txbuffer) + { + vPortFree(Os_Gmac->Txbuffer); + } + + if (Os_Gmac->Gmac.RxDesc) + { + vPortFree(Os_Gmac->Gmac.RxDesc); + } + + if (Os_Gmac->Gmac.TxDesc) + { + vPortFree(Os_Gmac->Gmac.TxDesc); + } +} + +/** + * @name: Ft_Os_GmacObject_Init + * @msg: + * @param {Ft_Os_Gmac} *Os_Gmac + * @param {Ft_Os_Gmac_Config} *Config + * @return {*} + */ +void Ft_Os_GmacObject_Init(Ft_Os_Gmac *Os_Gmac, Ft_Os_Gmac_Config *Config) +{ + Ft_assertVoid(Os_Gmac != NULL); + Ft_assertVoid(Os_Gmac->IsReady != FT_OS_GMACOBJECT_READLY); + memset(Os_Gmac, 0, sizeof(Ft_Os_Gmac)); + Os_Gmac->Config = *Config; + Os_Gmac->IsReady = FT_OS_GMACOBJECT_READLY; +} + +/** + * @name: Ft_Os_Gmac_Init + * @msg: + * @param {Ft_Os_Gmac} *Os_Gmac + * @param {u32} InstanceId + * @return {*} + */ +void Ft_Os_Gmac_Init(Ft_Os_Gmac *Os_Gmac) +{ + Ft_Gmac_t *Gmac; + Ft_assertVoid(Os_Gmac != NULL); + Ft_assertVoid((Os_Gmac->IsReady == FT_OS_GMACOBJECT_READLY)); + Gmac = &Os_Gmac->Gmac; + Ft_Os_GmacMem_Free(Os_Gmac); + Ft_assertVoid(Ft_GmacCfgInitialize(Gmac, Ft_Gmac_LookupConfig(Os_Gmac->Config.Gmac_Instance)) == FST_SUCCESS); + Ft_Os_GmacMem_Create(Os_Gmac); + + /* Create a binary semaphore used for informing ethernetif of frame reception */ + Ft_assertVoid((Os_Gmac->s_xSemaphore = xSemaphoreCreateBinary()) != NULL); + /* Create a event group used for ethernetif of status change */ + Ft_assertVoid((Os_Gmac->s_xStatusEvent = xEventGroupCreate()) != NULL); +} + +/** + * @name: Ft_Os_Gmac_Start + * @msg: + * @param {Ft_Os_Gmac} *Os_Gmac + * @return {*} + */ +void Ft_Os_Gmac_Start(Ft_Os_Gmac *Os_Gmac) +{ + Ft_Gmac_t *Gmac; + Ft_assertVoid(Os_Gmac != NULL); + Ft_assertVoid((Os_Gmac->IsReady == FT_OS_GMACOBJECT_READLY)); + Gmac = &Os_Gmac->Gmac; + u32 ret; + + ret = Ft_Gmac_HwInitialize(Gmac); + if (FST_SUCCESS != ret) + { + OS_MAC_DEBUG_E("Gmac return err code %d\r\n", ret); + Ft_assertVoid(FST_SUCCESS == ret); + } + + /* Initialize Rx Description list : ring Mode */ + FGmac_DmaRxDescRingInit(Gmac, Gmac->RxDesc, Os_Gmac->Rxbuffer, GMAC_MAX_PACKET_SIZE, RX_DESCNUM); + + /* Initialize Tx Description list : ring Mode */ + FGmac_DmaTxDescRingInit(Gmac, Gmac->TxDesc, Os_Gmac->Txbuffer, GMAC_MAX_PACKET_SIZE, TX_DESCNUM); + + Ft_Gmac_Start(Gmac); + /* Gmac interrupt init */ + System_Gic_Enable(Gmac->Config.IRQ_NUM, (FGicv3_InterruptHandler_t)FGmac_IntrHandler, Gmac->Config.IRQPriority, Gmac); + + return; +} + +void Ft_Os_Gmac_Stop(Ft_Os_Gmac *Os_Gmac) +{ + Ft_Gmac_t *Gmac; + + Ft_assertVoid(Os_Gmac != NULL); + Ft_assertVoid((Os_Gmac->IsReady == FT_OS_GMACOBJECT_READLY)); + Gmac = &Os_Gmac->Gmac; + Ft_Gmac_Stop(Gmac); +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.h b/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.h new file mode 100644 index 0000000000..3f91bb6d7e --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_gmac.h @@ -0,0 +1,84 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-20 09:51:46 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_OS_GMAC_H +#define FT_OS_GMAC_H +#include "ft_gmac.h" +#include "ft_parameters.h" + +#include +#include +#include + +#include "lwip/netif.h" + +#define FT_OS_GMAC0_ID FT_GMAC0_ID +#define FT_OS_GMAC1_ID FT_GMAC1_ID + +#ifdef PHY_USING_AR8035 +#define USE_LWIP +#endif + +#define FT_NETIF_LINKUP 0x1U +#define FT_NETIF_DOWN 0x2U + +struct Ipv4Address +{ + u8 Ip_Address[4]; + u8 Netmask_Address[4]; + u8 GateWay_Address[4]; +}; + +struct GmacThread +{ + const char *Thread_Name; + u16 StackDepth; /* The number of words the stack */ + u32 Priority; /* Defines the priority at which the task will execute. */ + TaskHandle_t Thread_Handle; +}; + +typedef struct +{ + u32 Gmac_Instance; /* select Gmac global object */ + u32 Isr_Priority; /* irq Priority */ + struct Ipv4Address Address; + + /* Gmac input thread */ + struct GmacThread MacInputThread; + struct GmacThread MacStatusThread; + +} Ft_Os_Gmac_Config; + +typedef struct +{ + Ft_Gmac_t Gmac; + struct netif Netif_Object; + Ft_Os_Gmac_Config Config; + u8 *Rxbuffer; /* Buffer for RxDesc */ + u8 *Txbuffer; /* Buffer for TxDesc */ + u8 IsReady; /* Ft_Os_Gmac Object first need Init use Ft_Os_GmacObjec_Init */ + + SemaphoreHandle_t s_xSemaphore; /* Semaphore to signal incoming packets */ + EventGroupHandle_t s_xStatusEvent; /* Event Group to show netif's status ,follow FT_NETIF_XX*/ +} Ft_Os_Gmac; +void Ft_Os_GmacObject_Init(Ft_Os_Gmac *Os_Gmac, Ft_Os_Gmac_Config *Config); +void Ft_Os_Gmac_Init(Ft_Os_Gmac *Ft_Os_Gmac); +void Ft_Os_Gmac_Start(Ft_Os_Gmac *Os_Gmac); +void Ft_Os_Gmac_Stop(Ft_Os_Gmac *Os_Gmac); + +#ifdef USE_LWIP + +#endif + +#endif // ! FT_OS_GMAC_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.c b/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.c new file mode 100644 index 0000000000..f6a2efc389 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.c @@ -0,0 +1,232 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-21 09:29:56 + * @LastEditTime: 2021-04-23 10:11:00 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include +#include +#include +#include +#include "ft_os_i2c.h" +#include "ft_i2c.h" +#include "system_gic.h" + +static FI2C_t g_I2cCtrl[MAX_I2C_CTRL_ID]; + +inline static FI2C_t *FOS_getI2cCtrl(FI2C_Instance_t id) +{ + return &g_I2cCtrl[id]; +} + +static void FOS_waitI2cCallback(const u32 IrqType, + void *pArg) +{ + FI2C_t *pDev = (FI2C_t *)pArg; + u32 irqMask; + + /* wait for TX over signal */ + EventBits_t ev = 0; + + Ft_assertNoneReturn(NULL != pDev); + + irqMask = FI2C_getIrqMask(pDev); + FI2C_setIrqMask(pDev, irqMask); + + FT_I2C_DEBUG_I("%d wait for event mask 0x%x", IrqType, FI2C_getIrqMask(pDev)); + switch (IrqType) + { + case I2C_IRQ_TYPE_TX_COMPLETE: + ev = xEventGroupWaitBits(pDev->pTxEvent, + FOS_I2C_TX_OVER_EVT, + pdTRUE, pdFALSE, I2C_TIMEOUT); + break; + case I2C_IRQ_TYPE_RX_COMPLETE: + ev = xEventGroupWaitBits(pDev->pRxEvent, + FOS_I2C_RX_OVER_EVT, + pdTRUE, pdFALSE, I2C_TIMEOUT); + break; + default: + Ft_assertNoneReturn(0); + break; + } + + if (ev & FOS_I2C_TX_OVER_EVT) + { + FT_I2C_DEBUG_I("recev tx over event"); + } + else if (ev & FOS_I2C_RX_OVER_EVT) + { + FT_I2C_DEBUG_I("recev rx over event"); + } + else + { + FT_I2C_ERROR("wait evt timeout"); + pDev->LastIrqErr = ERR_I2C_EVT_TIMEOUT; + } + + return; +} + +static void FOS_irqI2cCallback(const u32 IrqType, + void *pArg, + void *pData) +{ + FI2C_t *pDev = (FI2C_t *)pArg; + + /* send RX over signal */ + BaseType_t xResult = pdFALSE; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + + Ft_assertNoneReturn((NULL != pDev)); + + switch (IrqType) + { + case I2C_IRQ_TYPE_TX_COMPLETE: + xResult = xEventGroupSetBitsFromISR(pDev->pTxEvent, + FOS_I2C_TX_OVER_EVT, + &xHigherPriorityTaskWoken); + break; + case I2C_IRQ_TYPE_RX_COMPLETE: + xResult = xEventGroupSetBitsFromISR(pDev->pRxEvent, + FOS_I2C_RX_OVER_EVT, + &xHigherPriorityTaskWoken); + break; + default: + Ft_assertNoneReturn(0); + break; + } + FT_I2C_DEBUG_I("send signal %d, result %d", IrqType, xResult); + + if (pdFAIL != xResult) + { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } + + return; +} + +void FOS_deInitI2cCtrl(FI2C_Instance_t Id) +{ + FI2C_deInitMaster(FOS_getI2cCtrl(Id)); +} + +u32 FOS_initI2cCtrl(FOS_I2cConfig_t *pConf) +{ + u32 ret = ERR_I2C_OK; + u32 irqMask; + u32 txBufSize; + u32 rxBufSize; + FI2C_t *pCtrl = FOS_getI2cCtrl(pConf->Id); + if (I2C_MASTER_DEV == pConf->DevType) + { + FI2C_initMaster(pConf->Id, pConf->WorkMode,pConf->SlaveAddr, + pConf->UseWRFIFO, + pConf->PageSize, pCtrl); + } + else + { + return ERR_I2C_NOT_SUPPORT; + } + + if (ERR_I2C_OK != ret) + { + return ret; + } + + if (I2C_IRQ_MODE == pConf->WorkMode) + { + /* enable some irq */ + irqMask = I2C_IRQ_TX_ABRT | I2C_IRQ_STOP_DET | + I2C_IRQ_START_DET | I2C_IRQ_RX_OVER | I2C_IRQ_RX_FULL | + I2C_IRQ_TX_OVER | I2C_IRQ_TX_EMPTY; + + //I2C_IRQ_DEFAULT_MASK; //I2C_IRQ_RX_OVER | I2C_IRQ_TX_OVER; + FI2C_setIrqMask(pCtrl, irqMask); + //FT_I2C_DEBUG_I("set irq mask 0x%x", FI2C_getIrqMask(pCtrl)); + + /* register irq handler */ + pCtrl->pIrqCallBack = FOS_irqI2cCallback; + pCtrl->pWaitCallBack = FOS_waitI2cCallback; + + /* create sync event */ + pCtrl->pRxEvent = xEventGroupCreate(); + pCtrl->pTxEvent = xEventGroupCreate(); + if (!pCtrl->pRxEvent || !pCtrl->pTxEvent) + { + FT_I2C_ERROR("alloc event failed!!!"); + ret = ERR_I2C_INVALID_NO_MEM; + goto EXIT; + } + + /* register i2c irq to gic */ + if (pConf->UseWRFIFO) + { + System_Gic_Enable(g_FI2cIrqNum[pConf->Id], + (FGicv3_interruptHandler)FI2C_irqHandler4Fifo, + I2C_DEFAULT_IRQ_PRIORITY, + pCtrl); + } + else + { + System_Gic_Enable(g_FI2cIrqNum[pConf->Id], + (FGicv3_interruptHandler)FI2C_irqHandler, + I2C_DEFAULT_IRQ_PRIORITY, + pCtrl); + } + } + +EXIT: + return ret; +} + +u32 FOS_i2cRead(FOS_I2cConfig_t *pConf) +{ + FI2C_t *pDev = FOS_getI2cCtrl(pConf->Id); + FI2C_Buffer_t *pBuf = &pDev->RxBuf; + u32 ret; + if (pConf->UseWRFIFO) + { + pBuf->BytePtr = pConf->pRxBuf; + pBuf->DataLength = pConf->RxBufSize ; + pBuf->CurIndex = 0; + ret = FI2C_readByFifo(pConf->PageAddr, pDev); + } + else + { + ret = FI2C_readByByte(pConf->RxBufSize, pConf->pRxBuf, + pConf->PageAddr, pDev); + } + + return ret; +} + +u32 FOS_i2cWrite(FOS_I2cConfig_t *pConf) +{ + FI2C_t *pDev = FOS_getI2cCtrl(pConf->Id); + FI2C_Buffer_t *pBuf = &pDev->TxBuf; + u32 ret = ERR_I2C_OK; + if (pConf->UseWRFIFO) + { + pBuf->BytePtr = pConf->pTxBuf; + pBuf->DataLength = pConf->TxBufSize; + pBuf->CurIndex = 0; + + ret = FI2C_writeByFifo(pConf->PageAddr, pDev); + } + else + { + ret = FI2C_writeByByte(pConf->TxBufSize, pConf->pTxBuf, + pConf->PageAddr, pDev); + } + + return ret; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.h b/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.h new file mode 100644 index 0000000000..e0b99adfb7 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_i2c.h @@ -0,0 +1,55 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-21 09:29:48 + * @LastEditTime: 2021-04-23 09:24:59 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef FT_FREERTOS_I2C_H +#define FT_FREERTOS_I2C_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_i2c.h" + +/* RX event */ +#define FOS_I2C_RX_OVER_EVT (0x01 << 0) + +/* TX event */ +#define FOS_I2C_TX_OVER_EVT (0x01 << 0) + +typedef struct +{ + FI2C_Instance_t Id; + FI2C_DevType_t DevType; + FI2C_WorkMode_t WorkMode; + u32 SlaveAddr; + u32 PageAddr; + u32 PageSize; + u8 *pTxBuf; + u32 TxBufSize; + u8 *pRxBuf; + u32 RxBufSize; + bool_t UseWRFIFO; +}FOS_I2cConfig_t; + +u32 FOS_initI2cCtrl(FOS_I2cConfig_t *config); +void FOS_deInitI2cCtrl(FI2C_Instance_t Id); +u32 FOS_i2cRead(FOS_I2cConfig_t *config); +u32 FOS_i2cWrite(FOS_I2cConfig_t *config); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.c b/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.c new file mode 100644 index 0000000000..fb08cba368 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.c @@ -0,0 +1,229 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:40:07 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_os_uart.h" +#include "ft_uart.h" +#include "ft_printf.h" +#include "system_gic.h" +#include "ft_types.h" +#include "ft_status.h" + +extern u32 FUart_GetInterruptMask(Ft_Uart *UartPtr); +extern void FUart_SetInterruptMask(Ft_Uart *UartPtr, u32 Mask); + +/* Callback events */ + +static void Ft_Os_Uart_Callback(void *Args, u32 Event, u32 EventData) +{ + Ft_Os_Uart *OsUartPtr = (Ft_Os_Uart *)Args; + BaseType_t xHigherPriorityTaskWoken = pdFALSE; + BaseType_t xResult = pdFALSE; + u32 RegTemp; + if (FUART_EVENT_RECV_DATA == Event || FUART_EVENT_RECV_TOUT == Event) + { + xResult = xEventGroupSetBitsFromISR(OsUartPtr->rxEvent, RTOS_UART_COMPLETE, &xHigherPriorityTaskWoken); + } + else if (FUART_EVENT_RECV_ERROR == Event) + { + xResult = xEventGroupSetBitsFromISR(OsUartPtr->rxEvent, RTOS_UART_RECV_ERROR, &xHigherPriorityTaskWoken); + } + else if (FUART_EVENT_SENT_DATA == Event) + { + + xResult = xEventGroupSetBitsFromISR(OsUartPtr->txEvent, RTOS_UART_RECV_ERROR, &xHigherPriorityTaskWoken); + } + else if (FUART_EVENT_PARE_FRAME_BRKE == Event) + { + xResult = xEventGroupSetBitsFromISR(OsUartPtr->rxEvent, RTOS_UART_RECV_ERROR, &xHigherPriorityTaskWoken); + } + else if (FUART_EVENT_RECV_ORERR == Event) + { + } + + if (FUART_EVENT_SENT_DATA == Event) + { + } + else + { + //UartObj); + RegTemp &= ~UARTMIS_RTMIS; + FUart_SetInterruptMask(&OsUartPtr->UartObj, RegTemp); + } + + if (xResult != pdFAIL) + { + portYIELD_FROM_ISR(xHigherPriorityTaskWoken); + } +} + +void Ft_Os_UartInit(Ft_Os_Uart *OsUartPtr, Ft_Os_Uart_Config *Config) +{ + Ft_Uart *UartPtr = NULL; + Ft_assertVoid(OsUartPtr != NULL); + Ft_assertVoid(Config != NULL); + UartPtr = &OsUartPtr->UartObj; + Ft_printf("OsUartPtr %x", OsUartPtr); + Ft_assertVoid(FUart_CfgInitialize(UartPtr, FUart_LookupConfig(Config->Uart_Instance)) == FST_SUCCESS); + FUart_SetHandler(UartPtr, Ft_Os_Uart_Callback, OsUartPtr); + Ft_assertVoid((OsUartPtr->rxSemaphore = xSemaphoreCreateMutex()) != NULL); + Ft_assertVoid((OsUartPtr->txSemaphore = xSemaphoreCreateMutex()) != NULL); + Ft_assertVoid((OsUartPtr->txEvent = xEventGroupCreate()) != NULL); + Ft_assertVoid((OsUartPtr->rxEvent = xEventGroupCreate()) != NULL); + System_Gic_Enable(OsUartPtr->UartObj.Config.IsrNum, (FGicv3_InterruptHandler_t)FUart_InterruptHandler, Config->Isr_Priority, UartPtr); + FUart_SetOptions(UartPtr, FUART_OPTION_UARTEN | FUART_OPTION_RXEN | FUART_OPTION_TXEN | FUART_OPTION_FIFOEN); +} + +/** + * @name: Ft_Os_ReceiveBuffer + * @msg: + * @param {Ft_Os_Uart} *OsUartPtr + * @param {u8} *buffer + * @param {u32} length + * @param {u32} *Received + * @return {*} + */ +s32 Ft_Os_UartReceiveBuffer(Ft_Os_Uart *OsUartPtr, u8 *buffer, u32 length, u32 *Received) +{ + u32 GetLength; + Ft_Uart *UartPtr; + EventBits_t ev; + s32 ret; + if (NULL == OsUartPtr) + { + return FST_INSTANCE_NOT_FOUND; + } + + if (NULL == buffer) + { + return FST_FAILURE; + } + + if (NULL == Received) + { + return FST_FAILURE; + } + + if (0 == length) + { + *Received = 0; + return FST_SUCCESS; + } + + UartPtr = &OsUartPtr->UartObj; + + /* New transfer can be performed only after current one is finished */ + if (pdFALSE == xSemaphoreTake(OsUartPtr->rxSemaphore, portMAX_DELAY)) + { + /* We could not take the semaphore, exit with 0 data received */ + ret = FST_FAILURE; + } + + GetLength = FUart_Receive(UartPtr, buffer, length); + if (GetLength > 0) + { + *Received = GetLength; + ret = FST_SUCCESS; + } + else + { + u32 RegTemp; + + RegTemp = FUart_GetInterruptMask(UartPtr); + RegTemp |= UARTMIS_RTMIS; + FUart_SetInterruptMask(UartPtr, RegTemp); + + ev = xEventGroupWaitBits(OsUartPtr->rxEvent, + RTOS_UART_COMPLETE | RTOS_UART_HARDWARE_BUFFER_OVERRUN | RTOS_UART_RECV_ERROR, + pdTRUE, pdFALSE, portMAX_DELAY); + + if (ev & RTOS_UART_HARDWARE_BUFFER_OVERRUN) + { + ret = FST_FIFO_ERROR; + *Received = 0; + } + else if (ev & RTOS_UART_COMPLETE) + { + ret = FST_SUCCESS; + *Received = UartPtr->ReceiveBuffer.RequestedBytes - UartPtr->ReceiveBuffer.RemainingBytes; + } + else if (ev & RTOS_UART_RECV_ERROR) + { + ret = FST_RECV_ERROR; + *Received = 0; + } + else + { + ret = FST_INVALID_PARAM; + *Received = 0; + } + } + + /* Enable next transfer. Current one is finished */ + if (pdFALSE == xSemaphoreGive(OsUartPtr->rxSemaphore)) + { + /* We could not post the semaphore, exit with error */ + Ft_printf("FST_FAILURE xSemaphoreGive \r\n"); + ret = FST_FAILURE; + } + + return ret; +} + +/** + * @name: + * @msg: + * @param {Ft_Os_Uart} *OsUartPtr + * @param {u8} *buffer + * @param {u32} length + * @return {*} + */ +s32 Ft_Os_UartBlockingSend(Ft_Os_Uart *OsUartPtr, u8 *buffer, u32 length) +{ + s32 ret = FST_SUCCESS; + Ft_Uart *UartPtr; + EventBits_t ev; + if (NULL == OsUartPtr) + { + return FST_INSTANCE_NOT_FOUND; + } + + if (NULL == buffer) + { + return FST_FAILURE; + } + + UartPtr = &OsUartPtr->UartObj; + + if (pdFALSE == xSemaphoreTake(OsUartPtr->txSemaphore, portMAX_DELAY)) + { + return FST_FAILURE; + } + + FUart_Send(UartPtr, buffer, length); + + ev = xEventGroupWaitBits(OsUartPtr->txEvent, RTOS_UART_COMPLETE, pdTRUE, pdFALSE, portMAX_DELAY); + if (!(ev & RTOS_UART_COMPLETE)) + { + ret = FST_FAILURE; + } + + if (pdFALSE == xSemaphoreGive(OsUartPtr->txSemaphore)) + { + /* We could not post the semaphore, exit with error */ + ret = FST_FAILURE; + } + + return ret; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.h b/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.h new file mode 100644 index 0000000000..1b0ae3c13f --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/ft_os_uart.h @@ -0,0 +1,67 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:40:12 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + + +#ifndef FT_OS_UART_H +#define FT_OS_UART_H + +#include "ft_uart.h" +#include "ft_parameters.h" +#include +#include +#include + +#include "ft_types.h" + +#define UART0_ID FT_UART0_ID +#define UART1_ID FT_UART1_ID +#define UART2_ID FT_UART2_ID +#define UART3_ID FT_UART3_ID + +/*! +* @cond RTOS_PRIVATE +* @name UART FreeRTOS handler +* +* These are the only valid states for txEvent and rxEvent +*/ +/*@{*/ +/*! @brief Event flag - transfer complete. */ +#define RTOS_UART_COMPLETE 0x1 +/*! @brief Event flag - hardware buffer overrun. */ +#define RTOS_UART_HARDWARE_BUFFER_OVERRUN 0x2 +/*! @brief Event flag Receive is error */ +#define RTOS_UART_RECV_ERROR 0x4 + +/*@}*/ + +typedef struct +{ + u32 Uart_Instance; /* select uart global object */ + u32 Isr_Priority; /* irq Priority */ +} Ft_Os_Uart_Config; + +typedef struct +{ + Ft_Uart UartObj; /* Uart Object */ + SemaphoreHandle_t rxSemaphore; /*!< RX semaphore for resource sharing */ + SemaphoreHandle_t txSemaphore; /*!< TX semaphore for resource sharing */ + EventGroupHandle_t rxEvent; /*!< RX completion event */ + EventGroupHandle_t txEvent; /*!< TX completion event */ +} Ft_Os_Uart; + +void Ft_Os_UartInit(Ft_Os_Uart *OsUartPtr, Ft_Os_Uart_Config *Config); +s32 Ft_Os_UartBlockingSend(Ft_Os_Uart *OsUartPtr, u8 *buffer, u32 length); +s32 Ft_Os_UartReceiveBuffer(Ft_Os_Uart *OsUartPtr, u8 *buffer, u32 length, u32 *Received); + +#endif // ! diff --git a/bsp/ft2004/libraries/drivers/freertos/system_ft2004.c b/bsp/ft2004/libraries/drivers/freertos/system_ft2004.c new file mode 100644 index 0000000000..459ae1ae9e --- /dev/null +++ b/bsp/ft2004/libraries/drivers/freertos/system_ft2004.c @@ -0,0 +1,88 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-07 15:40:19 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_assert.h" +#include "ft_printf.h" +#include "system_gic.h" +#include "ft_generic_timer.h" +#include "ft_aarch32_asm.h" + +static void AssertCallback(FT_IN s8 *File, s32 Line) +{ + Ft_printf("Assert Error is %s : %d \r\n", File, Line); +} + +void SystemInit(void) +{ + // + +#ifdef BSP_USE_CAN + +#define LOG_TAG "drv_can" +#include + +#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/libraries/drivers/rtthread/drv_can.h b/bsp/ft2004/libraries/drivers/rtthread/drv_can.h new file mode 100644 index 0000000000..1c91f09ca1 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_can.h @@ -0,0 +1,38 @@ +/* + * Copyright (c) 2006-2020, 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 +#include +#include +#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/libraries/drivers/rtthread/drv_eth.c b/bsp/ft2004/libraries/drivers/rtthread/drv_eth.c new file mode 100644 index 0000000000..97cf46c1cf --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_eth.c @@ -0,0 +1,709 @@ +/* + * 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 +#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" +#include "system_gic.h" + +#ifdef BSP_USING_GMAC + +#define LOG_TAG "drv.gmac" +#include + +#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); + } +} + +/** + * @name: Ft_Os_Gmac_Start + * @msg: + * @param {Ft_Os_Gmac} *Os_Gmac + * @return {*} + */ +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; +} + +/** + * @name: rt_ft2004_gmac_tx + * @msg: + * @in param{rt_device_t}: + * @return {*} + * @param {rt_device_t} dev + * @param {structpbuf} *p + */ +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) + { + LOG_I("netif_set_link_up %x \r\n", rt_event_send(&pOsMac->link_event, FT_NETIF_LINKUP)); + } + else + { + LOG_I("netif_set_link_down %x\r\n", 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; + } + + // pOsMac->_debug_tid = rt_thread_create(name, rt_hw_gmac_test, pOsMac, 512, 1, 2); + // rt_thread_startup(pOsMac->_debug_tid); + 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/libraries/drivers/rtthread/drv_eth.h b/bsp/ft2004/libraries/drivers/rtthread/drv_eth.h new file mode 100644 index 0000000000..8eaebdff2d --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_eth.h @@ -0,0 +1,25 @@ +/* + * Copyright (c) 2006-2020, 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 // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_log.h b/bsp/ft2004/libraries/drivers/rtthread/drv_log.h new file mode 100644 index 0000000000..7e0bfee5b4 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_log.h @@ -0,0 +1,27 @@ +/* + * Copyright (c) 2006-2018, 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 diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_qspi.c b/bsp/ft2004/libraries/drivers/rtthread/drv_qspi.c new file mode 100644 index 0000000000..dafed45baa --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/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 +#include "rtdevice.h" +#include "ft_qspi.h" +#include "ft_parameters.h" + +#ifdef BSP_USE_QSPI + +#define DRV_DEBUG +#define LOG_TAG "drv.qspi" +#include + +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 \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_qspi.h b/bsp/ft2004/libraries/drivers/rtthread/drv_qspi.h new file mode 100644 index 0000000000..54b0724b9e --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/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 + +#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 \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_sdcard.c b/bsp/ft2004/libraries/drivers/rtthread/drv_sdcard.c new file mode 100644 index 0000000000..e8b0ae2b6c --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_sdcard.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-18 Carl the first version + */ + +#include + +#define BSP_USING_SDC +#ifdef BSP_USING_SDC + +#include +#include +#include +#include "drv_sdctrl.h" + +#define DBG_TAG "app.card" +#define DBG_LVL DBG_INFO +#include + +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/libraries/drivers/rtthread/drv_sdctrl.c b/bsp/ft2004/libraries/drivers/rtthread/drv_sdctrl.c new file mode 100644 index 0000000000..85b1a34277 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_sdctrl.c @@ -0,0 +1,693 @@ +/* + * 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 +#include "interrupt.h" +#include "rtconfig.h" +#include "ft_cache.h" + +#ifdef BSP_USING_SDC + +#define LOG_TAG "drv.sdmmc" +#include + +#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, +}; + +/** + * @name: rthw_sdctrl_nomarl_callback + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {void} *args + */ +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; +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {void} *args + */ +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); + } +} + +/** + * @name: + * @msg: + * @in param: + * @inout param: + * @out param: + * @return {*} + * @param {void} *args + */ +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; + } +} + +/** + * Description: create sd ctrl instance + * Date: 2021-03-31 10:10:40 + * Param: + * return {*} + * param {ft_sdctrl_class_t} *class_p + */ +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/libraries/drivers/rtthread/drv_sdctrl.h b/bsp/ft2004/libraries/drivers/rtthread/drv_sdctrl.h new file mode 100644 index 0000000000..dba34f2525 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/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 + +#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/libraries/drivers/rtthread/drv_spi.c b/bsp/ft2004/libraries/drivers/rtthread/drv_spi.c new file mode 100644 index 0000000000..1ed51420b5 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_spi.c @@ -0,0 +1,447 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-25 14:01:29 + * @LastEditTime: 2021-04-30 16:11:25 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "drv_spi.h" +#include +#include +#include +#include "ft_spi.h" +#include "ft_mux.h" +#include "ft_trace.h" +#include "ft_generic_timer.h" + +#define DRV_DEBUG +#define LOG_TAG "drv.spi" +#include + +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 + { + //LOG_D("transfer done"); + } + + 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"); \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_spi.h b/bsp/ft2004/libraries/drivers/rtthread/drv_spi.h new file mode 100644 index 0000000000..0d9ae1d9e6 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_spi.h @@ -0,0 +1,29 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @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 + +#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 \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/drv_usart.c b/bsp/ft2004/libraries/drivers/rtthread/drv_usart.c new file mode 100644 index 0000000000..ada3178e19 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_usart.c @@ -0,0 +1,193 @@ +/* + * 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; + Ft_Uart *uart_ptr; + u32 RegTemp; + 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); + + //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 + { + // u32 RegTemp; + // //UartObj); + // RegTemp &= ~UARTMIS_RTMIS; + // FUart_SetInterruptMask(&Osuart_ptr->UartObj, RegTemp); + } +} + +static int uart_putc(struct rt_serial_device *serial, char c) +{ + struct drv_usart *uart; + Ft_Uart *uart_ptr; + 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; + Ft_Uart *uart_ptr; + 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; + +#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; + // Ft_printf("RT_USING_UART1 serial is %x \r\n", &_RtUart1.serial); + _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/libraries/drivers/rtthread/drv_usart.h b/bsp/ft2004/libraries/drivers/rtthread/drv_usart.h new file mode 100644 index 0000000000..abd265a259 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/drv_usart.h @@ -0,0 +1,26 @@ +/* + * 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 +#include "rtdevice.h" +#include "ft_uart.h" + +struct drv_usart +{ + Ft_Uart *handle; + + /* data */ + struct rt_serial_device serial; +}; + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/rtthread/ports/drv_qspi_flash.c b/bsp/ft2004/libraries/drivers/rtthread/ports/drv_qspi_flash.c new file mode 100644 index 0000000000..102cd5fc5b --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/ports/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 +#include +#include +#include +#include + +#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/libraries/drivers/rtthread/ports/drv_spi_flash.c b/bsp/ft2004/libraries/drivers/rtthread/ports/drv_spi_flash.c new file mode 100644 index 0000000000..6189a81da4 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/rtthread/ports/drv_spi_flash.c @@ -0,0 +1,43 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @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 +#include +#include +#include +#include +#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 \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/system_gic.c b/bsp/ft2004/libraries/drivers/system_gic.c new file mode 100644 index 0000000000..427b1a5a32 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/system_gic.c @@ -0,0 +1,120 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-28 15:05:59 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#include "ft_gicv3.h" +#include "ft_gicv3_hw.h" +#include "ft_io.h" +#include "ft_printf.h" +#include "ft_cpu.h" +#include "ft_types.h" +#include "ft_assert.h" +#include "ft_aarch32_asm.h" +#include "system_gic.h" + +#include "ft_debug.h" +#define SYSTEM_GIC_DEBUG_TAG "SYSTEM_GIC" + +#define SYSTEM_GIC_DEBUG_I(format, ...) FT_DEBUG_PRINT_I(SYSTEM_GIC_DEBUG_TAG, format, ##__VA_ARGS__) +#define SYSTEM_GIC_DEBUG_E(format, ...) FT_DEBUG_PRINT_E(SYSTEM_GIC_DEBUG_TAG, format, ##__VA_ARGS__) +#define SYSTEM_GIC_DEBUG_W(format, ...) FT_DEBUG_PRINT_W(SYSTEM_GIC_DEBUG_TAG, format, ##__VA_ARGS__) + +FGicv3 gicv3_object = {0}; + +void System_Gic_Init(void) +{ + Ft_assertVoid((FGicv3_Config_tInitalize(&gicv3_object, FGicv3_LookupConfig(0)) == FST_SUCCESS)); +} + +void System_Gic_Enable(u32 InterruptID, FGicv3_InterruptHandler_t InterruptHandler, u32 Priority, void *Args) +{ + Ft_assertVoid((FGicV3_Connect(&gicv3_object, InterruptID, InterruptHandler, Priority, Args) == FST_SUCCESS)); + FGicV3_Enable(&gicv3_object, InterruptID); +} + +void System_Gic_Disable(u32 InterruptID) +{ + FGicV3_Disconnect(&gicv3_object, InterruptID); + FGicV3_Disable(&gicv3_object, InterruptID); +} + +void System_GicMask(u32 InterruptID) +{ + FGicV3_Enable(&gicv3_object, InterruptID); +} + +void System_GicUnMask(u32 InterruptID) +{ + FGicV3_Enable(&gicv3_object, InterruptID); +} + +void System_GicSetPriority(u32 InterruptID, u32 Priority) +{ + FGicV3_SetPriority(&gicv3_object, InterruptID, Priority); +} + +s32 System_GicGetActiveIrq(void) +{ + return __MRC(15, 0, 12, 12, 0); +} + +void System_GicAck(s32 InterruptID) +{ + sys_icc_eoir1_set((u32)InterruptID); +} + +__STATIC_INLINE void System_SoftIrqSend(u32 ValLow, u32 ValHigh) +{ + __asm__ __volatile__("MCRR p15,0,%0,%1,c12" + : + : "r"(ValLow), "r"(ValHigh)); +} + +ft_error_t System_IpiGen(s32 InterruptID, s32 CpuIdMask) +{ + u32 cnt = 0; + u8 aff1 = 0, + aff2 = 0, CpuList = 0; + volatile u32 LowWord = 0, HighWord = 0; + + for (cnt = 0; cnt < 32; cnt++) + { + /* code */ + if ((CpuIdMask >> cnt) & 0x1) + { + if (cnt < sizeof(SoftAffiTable) / sizeof(u32)) + { + CpuList = (u8)(SoftAffiTable[cnt] & 0xff); + aff1 = (u8)((SoftAffiTable[cnt] >> 8) & 0xff); + aff2 = (u8)((SoftAffiTable[cnt] >> 16) & 0xff); + CpuList = 1 << CpuList; + LowWord |= CpuList; + LowWord |= (InterruptID & 0xffff) << 24; + LowWord |= aff1 << 16; + HighWord |= aff2; + rt_kprintf("HighWord %x \r\n", HighWord); + rt_kprintf("LowWord %x \r\n", LowWord); + System_SoftIrqSend(LowWord, HighWord); + + // SYSTEM_GIC_DEBUG_E("System_IpiGen IS OK cnt is %x low %x, High %x \r\n", cnt, LowWord, HighWord); + } + else + { + SYSTEM_GIC_DEBUG_E("CpuId is over max size %x \r\n", CpuIdMask); + return SYSGIC_FAILURE; + } + } + } + + return SYSGIC_SUCCESS; +} \ No newline at end of file diff --git a/bsp/ft2004/libraries/drivers/system_gic.h b/bsp/ft2004/libraries/drivers/system_gic.h new file mode 100644 index 0000000000..974a667c97 --- /dev/null +++ b/bsp/ft2004/libraries/drivers/system_gic.h @@ -0,0 +1,47 @@ +/* + * @ : Copyright (c) 2020 Phytium Information Technology, Inc. + * The right to copy, distribute, modify, or otherwise make use + * of this software may be licensed only pursuant to the terms + * of an applicable Phytium license agreement. + * @Date: 2021-04-07 09:53:07 + * @LastEditTime: 2021-04-27 16:02:36 + * @Description:  This files is for + * + * @Modify History: + * Ver   Who        Date         Changes + * ----- ------     --------    -------------------------------------- + */ + +#ifndef SYSTEM_GIC_H +#define SYSTEM_GIC_H + +#ifdef __cplusplus +extern "C" +{ +#endif + +#include "ft_types.h" +#include "ft_gicv3.h" +#include "ft_error_code.h" + +#define SYSGIC_SUCCESS FST_SUCCESS /* SUCCESS */ +#define SYSGIC_FAILURE FT_MAKE_ERRCODE(errGicV3, errPort, FST_FAILURE) /* Normal */ +#define SYSGIC_TIMEOUT FT_MAKE_ERRCODE(errGicV3, errPort, FST_TIMEOUT) /* Timeout */ +#define SYSGIC_EILSEQ FT_MAKE_ERRCODE(errGicV3, errPort, FST_EILSEQ) /* Illegal byte sequence. */ +#define SYSGIC_INVALID_PARAM FT_MAKE_ERRCODE(errGicV3, errPort, FST_INVALID_PARAM) /* Invalid param. */ + + void System_Gic_Init(void); + void System_Gic_Enable(u32 InterruptID, FGicv3_InterruptHandler_t InterruptHandler, u32 Priority, void *Args); + void System_Gic_Disable(u32 InterruptID); + void System_GicMask(u32 InterruptID); + void System_GicUnMask(u32 InterruptID); + void System_GicAck(s32 InterruptID); + void System_GicSetPriority(u32 InterruptID, u32 Priority); + s32 System_GicGetActiveIrq(void); + ft_error_t System_IpiGen(s32 InterruptID, s32 CpuId); + +#ifdef __cplusplus +} +#endif + +#endif // ! \ No newline at end of file diff --git a/bsp/ft2004/libraries/gcc/assembly_functions.olsd b/bsp/ft2004/libraries/gcc/assembly_functions.olsd new file mode 100644 index 0000000000..68e58e48a3 --- /dev/null +++ b/bsp/ft2004/libraries/gcc/assembly_functions.olsd @@ -0,0 +1,178 @@ + +#include "asmArm.h" + + + .globl FUNC(sys_icc_igrpen0_set) + .globl FUNC(sys_icc_igrpen0_get) + .globl FUNC(sys_icc_igrpen1_set) + .globl FUNC(sys_icc_igrpen1_get) + .globl FUNC(sys_icc_ctlr_set) + .globl FUNC(sys_icc_bpr_set) + .globl FUNC(sys_icc_hppir0_get) + .globl FUNC(sys_icc_hppir1_get) + .globl FUNC(sys_icc_eoir0_set) + .globl FUNC(sys_icc_eoir1_set) + .globl FUNC(sys_icc_pmr_set) + .globl FUNC(sys_icc_iar1_get) + .globl FUNC(sys_icc_sgi1r_set) + .globl FUNC(sys_icc_sgi0r_set) + .globl FUNC(sys_icc_sre_set) + .globl FUNC(sys_icc_sre_get) + .globl FUNC(vxMpidrGet) + .globl FUNC(armSmcCall) + .globl FUNC(sys_cntkctl_get) + .globl FUNC(sys_dfar_get) + .globl FUNC(sys_dfsr_get) + .globl FUNC(sys_ifar_get) + .globl FUNC(sys_ifsr_get) + .text + .balign 4 + +/* void sys_icc_igrpen0_set(unsigned int) */ +FUNC_BEGIN(sys_icc_igrpen0_set) + mcr p15, 0, r0, c12, c12, 6 + isb + mov pc, lr + +/* unsigned int sys_icc_igrpen0_get(void) */ +FUNC_BEGIN(sys_icc_igrpen0_get) + mrc p15, 0, r0, c12, c12, 6 + mov pc, lr + +/* void sys_icc_igrpen1_set(unsigned int) */ +FUNC_BEGIN(sys_icc_igrpen1_set) + mcr p15, 0, r0, c12, c12, 7 + isb + mov pc, lr + +/* void sys_icc_igrpen1_get(unsigned int) */ +FUNC_BEGIN(sys_icc_igrpen1_get) + mrc p15, 0, r0, c12, c12, 7 + isb + mov pc, lr + +/* void sys_icc_ctlr_set(unsigned int) */ +FUNC_BEGIN(sys_icc_ctlr_set) + mcr p15, 0, r0, c12, c12, 4 + mov pc, lr + +/* unsigned int sys_icc_hppir0_get(void) */ +FUNC_BEGIN(sys_icc_hppir0_get) + mrc p15, 0, r0, c12, c8, 2 + isb + mov pc, lr + +/* unsigned int sys_icc_hppir1_get(void) */ +FUNC_BEGIN(sys_icc_hppir1_get) + mrc p15, 0, r0, c12, c12, 2 + isb + mov pc, lr + +/* void sys_icc_bpr_set(unsigned int) */ +FUNC_BEGIN(sys_icc_bpr_set) + mcr p15, 0, r0, c12, c8, 3 + isb + mov pc, lr + + + +/* void sys_icc_eoir0_set(unsigned int) */ +FUNC_BEGIN(sys_icc_eoir0_set) + mcr p15, 0, r0, c12, c8, 1 + isb + mov pc, lr + + +/* void sys_icc_eoir1_set(unsigned int) */ +FUNC_BEGIN(sys_icc_eoir1_set) + mcr p15, 0, r0, c12, c12, 1 + isb + mov pc, lr + +/* void sys_icc_pmr_set(unsigned int) */ +FUNC_BEGIN(sys_icc_pmr_set) + mcr p15, 0, r0, c4, c6, 0 + isb + mov pc, lr + +/* unsigned int sys_icc_iar1_get(void) */ +FUNC_BEGIN(sys_icc_iar1_get) + mrc p15, 0, r0, c12, c12, 0 + isb + mov pc, lr +#if 0 +/* void sys_icc_igrpen1_set(unsigned int) */ +FUNC_BEGIN(sys_icc_sgi1r_set) + mcrr p15, 0, r0, r1, c12 + isb + mov pc, lr + +FUNC_BEGIN(sys_icc_sgi0r_set) + mcrr p15, 2, r0, r1, c12 + isb + mov pc, lr +#endif +/* void sys_icc_sre_set(unsigned int) */ +FUNC_BEGIN(sys_icc_sre_set) + mcr p15, 0, r0, c12, c12, 5 + isb + mov pc, lr + +/* unsigned int sys_icc_sre_get(void) */ +FUNC_BEGIN(sys_icc_sre_get) + mrc p15, 0, r0, c12, c12, 5 + isb + mov pc, lr + + /* unsigned int sys_icc_sre_get(void) */ +FUNC_BEGIN(sys_cntkctl_get) + mcr p15, 0, r0, c14, c1, 0 + isb + mov pc, lr + +FUNC_BEGIN(sys_dfar_get) + MRC p15,0,r0,c6,c0,0 + isb + mov pc, lr + +FUNC_BEGIN(sys_dfsr_get) + MRC p15,0,r0,c5,c0,0 + isb + mov pc, lr + +FUNC_BEGIN(sys_ifar_get) + MRC p15,0,r0,c6,c0,2 + isb + mov pc, lr + +FUNC_BEGIN(sys_ifsr_get) + MRC p15,0,r0,c5,c0,1 + isb + mov pc, lr + +#if 0 +FUNC_BEGIN(vxCpsrGet) + MRS r0, cpsr + isb + mov pc, lr + +#endif +FUNC_BEGIN(vxMpidrGet) + MRC p15, 0, r0, c0, c0, 5 + MOV pc, lr + + + + + + + + + + + + + + + + diff --git a/bsp/ft2004/libraries/gcc/lib_test.c b/bsp/ft2004/libraries/gcc/lib_test.c new file mode 100644 index 0000000000..20f037ab03 --- /dev/null +++ b/bsp/ft2004/libraries/gcc/lib_test.c @@ -0,0 +1,134 @@ +/* + * @Author: hh + * @Date: 2020-12-29 17:07:06 + * @LastEditTime: 2021-03-01 11:11:24 + * @LastEditors: Please set LastEditors + * @Description: In User Settings Edit + * @FilePath: \ft\project_freertos\devices\ft2000ahk\gcc\main.c + */ + +// #include +// #include + +// int main(void) +// { +// volatile char *data = 0; +// data = malloc(1); +// memcpy(data, data, 1); +// free((void *)data); +// return data; +// } + +#include +#include +#include +#include +#include "ft_types.h" +#include "ft_printf.h" + +// int main(void) +// { +// volatile char *data = 0; +// Ft_printf("233333"); +// data = malloc(1024); +// return (int)data; +// } + +void user_fun_def(void) +{ + Ft_printf("user_fun_def \r\n"); + while (1) + { + } +} + +void frq_debug(u32 r0, u32 r1) +{ + Ft_printf("test frq current %x ,last %x\r\n", r0, r1); + while (1) + { + } +} + +void irq_debug(u32 r0, u32 r1) +{ + Ft_printf("test irq current %x ,last %x\r\n", r0, r1); + while (1) + { + } +} + +void PrefAbort_debug(void) +{ + Ft_printf("PrefAbort_debug\r\n"); + while (1) + { + } +} + +void Svc_debug(void) +{ + Ft_printf("Svc_debug\r\n"); + while (1) + { + } +} + +void Undefine_debug(void) +{ + Ft_printf("Undefine_debug\r\n"); + while (1) + { + } +} + +void data_abort_debug(void) +{ + Ft_printf("data abort\r\n"); + while (1) + { + } +} + +int _close(int file) { return -1; } + +int _fstat(int file, struct stat *st) +{ + // st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) { return 1; } + +int _lseek(int file, int ptr, int dir) { return 0; } + +int _open(const char *name, int flags, int mode) { return -1; } + +int _read(int file, char *ptr, int len) +{ + int todo = 0; + // if (len == 0) + // return 0; + // while (UART_FR(UART0_ADDR) & UART_FR_RXFE) + // ; + // *ptr++ = UART_DR(UART0_ADDR); + // for (todo = 1; todo < len; todo++) + // { + // if (UART_FR(UART0_ADDR) & UART_FR_RXFE) + // { + // break; + // } + // *ptr++ = UART_DR(UART0_ADDR); + // } + return todo; +} +int _write(int file, char *ptr, int len) +{ + // int todo; + + // for (todo = 0; todo < len; todo++) + // { + // UART_DR(UART0_ADDR) = *ptr++; + // } + return 0; +} diff --git a/bsp/ft2004/libraries/gcc/ram_ft_aarch32.lds b/bsp/ft2004/libraries/gcc/ram_ft_aarch32.lds new file mode 100644 index 0000000000..50bbd06b06 --- /dev/null +++ b/bsp/ft2004/libraries/gcc/ram_ft_aarch32.lds @@ -0,0 +1,86 @@ + + +ENTRY(Startup_Aarch32) + +HEAP_SIZE = DEFINED(__heap_size__) ? __heap_size__ : 0x0400; +ISTACK_SIZE = DEFINED(__irq_stack_size__) ? __irq_stack_size__ : 0x0800; +CSTACK_SIZE = DEFINED(__stack_size__) ? __stack_size__ : 0x0800; +USTACK_SIZE = DEFINED(__user_stack_size__) ? __user_stack_size__ : 0x0800; + + + +/* Specify the memory areas */ +MEMORY +{ + m_mode_transform (RX) : ORIGIN = 0x80100000, LENGTH = 0x00000200 + m_interrupts (RX) : ORIGIN = 0x80100200, LENGTH = 0x00000040 + m_text (RX) : ORIGIN = 0x80100240, LENGTH = 0x003ffdc0 + m_data (RW) : ORIGIN = 0x80400000, LENGTH = 0x00800000 + m_stack (RW) : ORIGIN = 0x80c00000, LENGTH = 0x00400000 +} + + +/* Define output sections */ +SECTIONS +{ + . = 0x80100000 ; + .mode_transform : + { + . = ALIGN(4); + KEEP(*(.Startup_Aarch32)) /* Startup code */ + . = ALIGN(4); + } > m_mode_transform + + .interrupts : + { + __VECTOR_TABLE = .; + . = ALIGN(4); + KEEP(*(.Startup_Ft_Aarch32)) /* Startup code */ + . = ALIGN(4); + } > m_interrupts + + /* The program code and other data goes into DDR RAM */ + .text : + { + . = ALIGN(4); + *(.text) /* .text sections (code) */ + *(.text*) /* .text* sections (code) */ + *(.rodata) /* .rodata sections (constants, strings, etc.) */ + *(.rodata*) /* .rodata* sections (constants, strings, etc.) */ + *(.glue_7) /* glue arm to thumb code */ + *(.glue_7t) /* glue thumb to arm code */ + *(.eh_frame) + KEEP (*(.init)) + KEEP (*(.fini)) + _exit = .; + . = ALIGN(4); + } > m_text + + .heap : + { + . = ALIGN(8); + __end__ = .; + PROVIDE(end = .); + __HeapBase = .; + . += HEAP_SIZE; + __HeapLimit = .; + __heap_limit = .; /* Add for _sbrk */ + } > m_data + + /* Initializes stack on the end of block */ + .stack : + { + __IStackTop = .; + __IStackLimit = __IStackTop - ISTACK_SIZE; + __CStackTop = __IStackLimit; + __CStackLimit = __CStackTop - CSTACK_SIZE; + __UStackTop = __CStackLimit; + __UStackLimit = __UStackTop - USTACK_SIZE; + PROVIDE(__stack = __CStackTop); + } > m_stack + + + .rodata ALIGN(4) : {*(.rodata*)} >m_data + .data ALIGN(4) : { *(.data) } >m_data +} + diff --git a/bsp/ft2004/libraries/gcc/startup_aarch32.S b/bsp/ft2004/libraries/gcc/startup_aarch32.S new file mode 100644 index 0000000000..b08dcbb6ad --- /dev/null +++ b/bsp/ft2004/libraries/gcc/startup_aarch32.S @@ -0,0 +1,244 @@ + + +.equ Mode_USR, 0x10 +.equ Mode_FIQ, 0x11 +.equ Mode_IRQ, 0x12 +.equ Mode_SVC, 0x13 +.equ Mode_ABT, 0x17 +.equ Mode_UND, 0x1B +.equ Mode_SYS, 0x1F + +.section .Startup_Aarch32, "a" +.global Startup_Aarch32 /* 全局标号 */ + +Startup_Aarch32: + + + + MRS r1, cpsr + BIC r1, r1, #0x0000001F + ORR r1, r1, #0x13 | (1<<7) | (1<<6) + MSR cpsr, r1 + + /* + * SPSR does not have pre-defined reset value. + * Here correct endianess (BE bit) in SPSR + */ + + MRS r0, spsr + BIC r0, r0, #(0x1 << 9) /* Little Endian */ + MSR spsr_x, r0 + + + b Startup_Ft_Aarch32 + + .size Startup_Aarch32, . - Startup_Aarch32 + .text + .arm + + +.syntax unified +.arch armv8-a + +.section .Startup_Ft_Aarch32, "a" +.align 2 +.global Startup_Ft_Aarch32 /* 全局标号 */ + +Startup_Ft_Aarch32: + + ldr pc, =Reset_Handler /* Reset */ + ldr pc, =Undefined_Handler /* Undefined instructions */ + ldr pc, =SVC_Handler /* Supervisor Call */ + ldr pc, =PrefAbort_Handler /* Prefetch abort */ + /* ldr pc, =DataAbort_Handler Data abort */ + ldr pc, =DataAbort_Handler + .word 0 /* RESERVED */ + ldr pc, =IRQ_Handler /* IRQ interrupt */ + ldr pc, =FIQ_Handler /* FIQ interrupt */ + + .size Startup_Ft_Aarch32, . - Startup_Ft_Aarch32 + .text + .arm + + + + .arm + .align 2 + .globl Reset_Handler + .weak Reset_Handler + .type Reset_Handler, %function +Reset_Handler: + cpsid i /* Mask interrupts */ + + + /* 关闭I,DCache和MMU + * 采取读-改-写的方式。 + */ + mrc p15, 0, r0, c1, c0, 0 /* 读取CP15的C1寄存器到R0中 */ + bic r0, r0, #(0x1 << 12) /* 清除C1寄存器的bit12位(I位),关闭I Cache */ + bic r0, r0, #(0x1 << 2) /* 清除C1寄存器的bit2(C位),关闭D Cache */ + bic r0, r0, #0x2 /* 清除C1寄存器的bit1(A位),关闭对齐 */ + bic r0, r0, #(0x1 << 11) /* 清除C1寄存器的bit11(Z位),关闭分支预测 */ + bic r0, r0, #0x1 /* 清除C1寄存器的bit0(M位),关闭MMU */ + mcr p15, 0, r0, c1, c0, 0 /* 将r0寄存器中的值写入到CP15的C1寄存器中 */ + + + /* 设置用户堆栈 */ + /* Set up stack for IRQ, System/User and Supervisor Modes */ + /* 进入IRQ模式 */ + mrs r0, cpsr + bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */ + orr r0, r0, #0x12 /* r0或上0x13,表示使用IRQ模式 */ + msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ + ldr sp, =__IStackTop + + /* 进入SYS模式 */ + mrs r0, cpsr + bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */ + orr r0, r0, #0x1f /* r0或上0x13,表示使用SYS模式 */ + msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ + ldr sp, =__CStackTop + + /* 进入SVC模式 */ + mrs r0, cpsr + bic r0, r0, #0x1f /* 将r0寄存器中的低5位清零,也就是cpsr的M0~M4 */ + orr r0, r0, #0x13 /* r0或上0x13,表示使用SVC模式 */ + msr cpsr, r0 /* 将r0 的数据写入到cpsr_c中 */ + ldr sp, =__CStackTop + + + + +#ifndef NO_RTOS + ldr r0,=SystemInit + blx r0 + + MOV R0, #(0xF << 20) // Enable CP10 & CP11 function + MCR P15, 0, R0, C1, C0, 2 // Write the Coprocessor Access Control + ISB // Register (CPACR). + // Switch on the FP and NEON hardware. + MOV R1, #(0x1 << 30) + VMSR FPEXC, R1 + +#endif + + cpsie i /* 打开全局中断 */ + + /* b main 跳转到main函数 */ + b main + + + +.macro push_svc_reg + sub sp, sp, #17 * 4 @/* Sizeof(struct ft_hw_exp_stack) */ + stmia sp, {r0 - r12} @/* Calling r0-r12 */ + mov r0, sp + mrs r6, spsr @/* Save CPSR */ + str lr, [r0, #15*4] @/* Push PC */ + str r6, [r0, #16*4] @/* Push CPSR */ + cps #Mode_SVC + str sp, [r0, #13*4] @/* Save calling SP */ + str lr, [r0, #14*4] @/* Save calling PC */ +.endm + + + +/* Undefined_Handler */ + .align 2 + .arm + .weak Undefined_Handler + .type Undefined_Handler, %function +Undefined_Handler: + b Undefine_debug + b Undefined_Handler + .size Undefined_Handler, . - Undefined_Handler + + + .align 2 + .arm + .weak SVC_Handler + .type SVC_Handler, %function +SVC_Handler: + b Svc_debug + ldr r0,=SVC_Handler + bx r0 + .size SVC_Handler, . - SVC_Handler + + .align 2 + .arm + .weak PrefAbort_Handler + .type PrefAbort_Handler, %function +PrefAbort_Handler: + b PrefAbort_debug + ldr r0,=PrefAbort_Handler + bx r0 + .size PrefAbort_Handler, . - PrefAbort_Handler + + + .align 5 + .arm + .weak DataAbort_Handler + .type DataAbort_Handler, %function +DataAbort_Handler: + push_svc_reg + bl Ft_Arrch32_trap_dabt + b . + + .align 4 + .arm + .weak IRQ_Handler + .type IRQ_Handler, %function +IRQ_Handler: + push {lr} /* Save return address+4 */ + push {r0-r3, r12} /* Push caller save registers */ + + MRS r0, spsr /* Save SPRS to allow interrupt reentry */ + push {r0} + + /* Get GIC base address MRC P15, 4, r1, C15, C0, 0 */ + + //r0, Regs->r1, Regs->r2, Regs->r3); + TRAPS_DEBUG_E("r04:0x%08x r05:0x%08x r06:0x%08x r07:0x%08x\n", Regs->r4, Regs->r5, Regs->r6, Regs->r7); + TRAPS_DEBUG_E("r08:0x%08x r09:0x%08x r10:0x%08x\n", Regs->r8, Regs->r9, Regs->r10); + TRAPS_DEBUG_E("fp :0x%08x ip :0x%08x\n", Regs->fp, Regs->ip); + TRAPS_DEBUG_E("sp :0x%08x lr :0x%08x pc :0x%08x\n", Regs->sp, Regs->lr, Regs->pc); + TRAPS_DEBUG_E("cpsr:0x%08x\n", Regs->cpsr); +} + +void Ft_Arrch32_trap_dabt(struct ft_hw_exp_stack *Regs) +{ + TRAPS_DEBUG_E("data abort:"); + Ft_Arrch32_Registers(Regs); + while (1) + ; +} diff --git a/bsp/ft2004/libraries/include/asmArm.h b/bsp/ft2004/libraries/include/asmArm.h new file mode 100644 index 0000000000..8a27663530 --- /dev/null +++ b/bsp/ft2004/libraries/include/asmArm.h @@ -0,0 +1,34 @@ +/* + * @Author: your name + * @Date: 2021-01-20 10:07:04 + * @LastEditTime: 2021-04-22 16:32:31 + * @LastEditors: Please set LastEditors + * @Description: In User Settings Edit + * @FilePath: \project_freertos\devices\ft2004\gcc\asmArm.h + */ +#ifndef ASMARM_H +#define ASMARM_H + +#define FUNC_LABEL(func) \ + func: + +#if (defined __GNUC__) +#define FUNC_BEGIN(func) \ + .thumb; \ + .thumb_func; \ + .balign 4; \ + FUNC_LABEL(func) +#else /* !__GNUC__ */ +#define FUNC_BEGIN(func) \ + .thumb; \ + .balign 4; \ + FUNC_LABEL(func) +#endif /* __GNUC__ */ + +#define FUNC(sym) sym +#define FUNC_END(func) .size FUNC(func), .- FUNC(func) + +#define Swap64(var64) var64 +#define Swap32(var32) var32 + +#endif // !ASMARM_H \ No newline at end of file diff --git a/bsp/ft2004/libraries/readme.md b/bsp/ft2004/libraries/readme.md new file mode 100644 index 0000000000..c8d372b5f2 --- /dev/null +++ b/bsp/ft2004/libraries/readme.md @@ -0,0 +1,69 @@ +# README + +- FT2004 rtos 驱动介绍 + +## 1. 当前版本 + +- V0.4.2 + +## 2. 关于此文档 + +- 本文档提供此项目框架性和基本功能介绍 + +- 详细的硬件 bsp 介绍请阅读 doc 文件中模块子文档。 + +## 3. 编译器版本 + +- gcc-arm-none-eabi-10-2020-q4-major + +## 4. 文件结构介绍 + +| 目录名 | 内容 | +| --------- | ---------------------------------------- | +| bsp | 存放芯片上的外设,例如网卡,i2c 和串口等 | +| component | 板载外设驱动 ,rtc、eeprom、sdmmc 等 | +| cpu | cpu 架构方面指令 | +| doc | 具体外设文档 | +| drivers | 与不同 rtos 进行耦合的驱动 | +| gcc | gcc 编译环境下的启动、链接文件 | +| include | 平台相关的包含 | + +## 5. 支持外设 + +| 外设名 | 备注 | +| -------- | ---------------------- | +| ft_gicv3 | gicv3 中断控制器 | +| ft_gmac | ft gmac 千兆网卡控制器 | +| ft_i2c | FT I2C | +| ft_qspi | FT qspi 控制器 | +| ft_sd | FT mmcsd 控制器 | +| ft_uart | PrimeCell PL011 | +| ft_spi | FT spi 控制器 | +| ft_gpio | FT gpio 控制器 | +| ft_can | FT can 控制器 | + +## 6. 使用实例 + +[freertos](https://gitee.com/phytium_embedded/ft2004-freertos) + +## 7. 参考资源 + +armv8 Architecture Reference Manual + +FT-2000/4 软件编程手册-V1.4 + +FT-2000/4 硬件设计指导手册 + +FT-2000 四核处理器数据手册 V1.6 + +## 8. 贡献方法 + +请联系飞腾嵌入式软件部 + +huanghe@phytium.com.cn + +zhugengyu@phytium.com.cn + +## 9. 许可协议 + +Apache License. diff --git a/bsp/ft2004/make.sh b/bsp/ft2004/make.sh new file mode 100644 index 0000000000..4fd5c685e0 --- /dev/null +++ b/bsp/ft2004/make.sh @@ -0,0 +1,16 @@ +### + # @Author: hh + # @Date: 2020-12-23 13:53:50 + # @LastEditTime: 2021-05-18 16:48:25 + # @LastEditors: Please set LastEditors + # @Description: In User Settings Edit + # @FilePath: \ft\bare\2_base\factory.sh +### +#/bin/sh +# scons --clean +# ./update.sh +scons -j8 +rm /mnt/d/project/tftp/rtthread.bin +cp ./rtthread.bin /mnt/d/project/tftp/ + +# arm-none-eabi-objdump -D -m arm ft2004.elf > rtt.dis \ No newline at end of file diff --git a/bsp/ft2004/rtconfig.h b/bsp/ft2004/rtconfig.h new file mode 100644 index 0000000000..7f6381ad0b --- /dev/null +++ b/bsp/ft2004/rtconfig.h @@ -0,0 +1,268 @@ +#ifndef RT_CONFIG_H__ +#define RT_CONFIG_H__ + +/* Automatically generated file; DO NOT EDIT. */ +/* RT-Thread Project Configuration */ + +/* RT-Thread Kernel */ + +#define RT_NAME_MAX 32 +#define RT_ALIGN_SIZE 128 +#define RT_THREAD_PRIORITY_32 +#define RT_THREAD_PRIORITY_MAX 32 +#define RT_TICK_PER_SECOND 100 +#define RT_USING_OVERFLOW_CHECK +#define RT_USING_HOOK +#define RT_USING_IDLE_HOOK +#define RT_IDLE_HOOK_LIST_SIZE 4 +#define IDLE_THREAD_STACK_SIZE 4096 +#define RT_USING_TIMER_SOFT +#define RT_TIMER_THREAD_PRIO 4 +#define RT_TIMER_THREAD_STACK_SIZE 512 + +/* kservice optimization */ + +#define RT_DEBUG +#define RT_DEBUG_COLOR + +/* Inter-Thread communication */ + +#define RT_USING_SEMAPHORE +#define RT_USING_MUTEX +#define RT_USING_EVENT +#define RT_USING_MAILBOX +#define RT_USING_MESSAGEQUEUE + +/* Memory Management */ + +#define RT_USING_MEMHEAP +#define RT_USING_SLAB +#define RT_USING_HEAP + +/* Kernel Device Object */ + +#define RT_USING_DEVICE +#define RT_USING_INTERRUPT_INFO +#define RT_USING_CONSOLE +#define RT_CONSOLEBUF_SIZE 4096 +#define RT_CONSOLE_DEVICE_NAME "uart1" +#define RT_VER_NUM 0x40004 +#define ARCH_ARM +#define ARCH_ARM_CORTEX_A +#define RT_USING_GIC_V3 + +/* RT-Thread Components */ + +#define RT_USING_COMPONENTS_INIT +#define RT_USING_USER_MAIN +#define RT_MAIN_THREAD_STACK_SIZE 2048 +#define RT_MAIN_THREAD_PRIORITY 10 + +/* C++ features */ + + +/* Command shell */ + +#define RT_USING_FINSH +#define FINSH_THREAD_NAME "tshell" +#define FINSH_USING_HISTORY +#define FINSH_HISTORY_LINES 5 +#define FINSH_USING_SYMTAB +#define FINSH_USING_DESCRIPTION +#define FINSH_THREAD_PRIORITY 20 +#define FINSH_THREAD_STACK_SIZE 4096 +#define FINSH_CMD_SIZE 128 +#define FINSH_USING_MSH +#define FINSH_USING_MSH_DEFAULT +#define FINSH_ARG_MAX 10 + +/* Device virtual file system */ + +#define RT_USING_DFS +#define DFS_USING_WORKDIR +#define DFS_FILESYSTEMS_MAX 2 +#define DFS_FILESYSTEM_TYPES_MAX 2 +#define DFS_FD_MAX 16 +#define RT_USING_DFS_ELMFAT + +/* elm-chan's FatFs, Generic FAT Filesystem Module */ + +#define RT_DFS_ELM_CODE_PAGE 437 +#define RT_DFS_ELM_WORD_ACCESS +#define RT_DFS_ELM_USE_LFN_3 +#define RT_DFS_ELM_USE_LFN 3 +#define RT_DFS_ELM_LFN_UNICODE_0 +#define RT_DFS_ELM_LFN_UNICODE 0 +#define RT_DFS_ELM_MAX_LFN 255 +#define RT_DFS_ELM_DRIVES 2 +#define RT_DFS_ELM_MAX_SECTOR_SIZE 512 +#define RT_DFS_ELM_REENTRANT +#define RT_USING_DFS_DEVFS + +/* Device Drivers */ + +#define RT_USING_DEVICE_IPC +#define RT_PIPE_BUFSZ 512 +#define RT_USING_SERIAL +#define RT_SERIAL_USING_DMA +#define RT_SERIAL_RB_BUFSZ 256 +#define RT_USING_CAN +#define RT_USING_SDIO +#define RT_SDIO_STACK_SIZE 512 +#define RT_SDIO_THREAD_PRIORITY 15 +#define RT_MMCSD_STACK_SIZE 1024 +#define RT_MMCSD_THREAD_PREORITY 22 +#define RT_MMCSD_MAX_PARTITION 16 +#define RT_USING_SPI +#define RT_USING_QSPI +#define RT_USING_SFUD +#define RT_SFUD_USING_SFDP +#define RT_SFUD_USING_FLASH_INFO_TABLE +#define RT_SFUD_USING_QSPI +#define RT_SFUD_SPI_MAX_HZ 50000000 + +/* Using USB */ + + +/* POSIX layer and C standard library */ + +#define RT_LIBC_USING_TIME +#define RT_LIBC_FIXED_TIMEZONE 8 + +/* Network */ + +/* Socket abstraction layer */ + + +/* Network interface device */ + +#define RT_USING_NETDEV +#define NETDEV_USING_IFCONFIG +#define NETDEV_USING_PING +#define NETDEV_USING_NETSTAT +#define NETDEV_USING_AUTO_DEFAULT +#define NETDEV_IPV4 1 +#define NETDEV_IPV6 0 + +/* light weight TCP/IP stack */ + +#define RT_USING_LWIP +#define RT_USING_LWIP212 +#define RT_LWIP_MEM_ALIGNMENT 4 +#define RT_LWIP_IGMP +#define RT_LWIP_ICMP +#define RT_LWIP_DNS + +/* Static IPv4 Address */ + +#define RT_LWIP_IPADDR "192.168.3.20" +#define RT_LWIP_GWADDR "192.168.3.1" +#define RT_LWIP_MSKADDR "255.255.255.0" +#define RT_LWIP_UDP +#define RT_LWIP_TCP +#define RT_LWIP_RAW +#define RT_MEMP_NUM_NETCONN 8 +#define RT_LWIP_PBUF_NUM 16 +#define RT_LWIP_RAW_PCB_NUM 4 +#define RT_LWIP_UDP_PCB_NUM 4 +#define RT_LWIP_TCP_PCB_NUM 4 +#define RT_LWIP_TCP_SEG_NUM 40 +#define RT_LWIP_TCP_SND_BUF 8196 +#define RT_LWIP_TCP_WND 8196 +#define RT_LWIP_TCPTHREAD_PRIORITY 10 +#define RT_LWIP_TCPTHREAD_MBOX_SIZE 8 +#define RT_LWIP_TCPTHREAD_STACKSIZE 1024 +#define RT_LWIP_ETHTHREAD_PRIORITY 12 +#define RT_LWIP_ETHTHREAD_STACKSIZE 1024 +#define RT_LWIP_ETHTHREAD_MBOX_SIZE 8 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define SO_REUSE 1 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_RCVBUF 1 +#define LWIP_SO_LINGER 0 +#define LWIP_NETIF_LOOPBACK 0 +#define RT_LWIP_USING_PING + +/* AT commands */ + + +/* VBUS(Virtual Software BUS) */ + + +/* Utilities */ + + +/* RT-Thread Utestcases */ + + +/* RT-Thread online packages */ + +/* IoT - internet of things */ + + +/* Wi-Fi */ + +/* Marvell WiFi */ + + +/* Wiced WiFi */ + + +/* IoT Cloud */ + + +/* security packages */ + + +/* language packages */ + + +/* multimedia packages */ + + +/* tools packages */ + + +/* system packages */ + + +/* Micrium: Micrium software products porting for RT-Thread */ + + +/* peripheral libraries and drivers */ + + +/* AI packages */ + + +/* miscellaneous packages */ + + +/* samples: kernel and components samples */ + + +/* entertainment: terminal games and other interesting software packages */ + +#define FT2004 + +/* Hardware Drivers Config */ + +/* On-chip Peripheral Drivers */ + +#define BSP_USING_UART +#define RT_USING_UART1 +#define BSP_USING_SDC +#define BSP_USING_GMAC +#define BSP_USING_GMAC1 +#define RT_LWIP_ETH_PAD_SIZE 2 +#define BSP_USE_SPI +#define BSP_USE_GPIO +#define BSP_USE_CAN +#define BSP_USING_CAN0 + +/* Board extended module Drivers */ + + +#endif diff --git a/bsp/ft2004/rtconfig.py b/bsp/ft2004/rtconfig.py new file mode 100644 index 0000000000..c6b54f85f1 --- /dev/null +++ b/bsp/ft2004/rtconfig.py @@ -0,0 +1,84 @@ +''' +Author: your name +Date: 2021-03-01 18:57:53 +LastEditTime: 2021-05-06 13:49:19 +LastEditors: Please set LastEditors +Description: In User Settings Edit +FilePath: \rtthread\bsp\ft2004\rtconfig.py +''' +import os + +# header = ''' +# #ifndef __MAC_AUTO_GENERATE_H__ +# #define __MAC_AUTO_GENERATE_H__ + +# /* Automatically generated file; DO NOT EDIT. */ +# /* mac configure file for RT-Thread qemu */ + +# #define AUTOMAC0 0x52 +# #define AUTOMAC1 0x54 +# #define AUTOMAC2 0x00 +# #define AUTOMAC''' + +# end = ''' +# #endif +# ''' + +# toolchains options +ARCH='arm' +CPU='cortex-a' +CROSS_TOOL='gcc' + +if os.getenv('RTT_CC'): + CROSS_TOOL = os.getenv('RTT_CC') + +# only support GNU GCC compiler. +PLATFORM = 'gcc' +EXEC_PATH = '/home/carl/ft/gcc-arm-none-eabi-10-2020-q4-major/bin' + +if os.getenv('RTT_EXEC_PATH'): + EXEC_PATH = os.getenv('RTT_EXEC_PATH') + +BUILD = 'debug' + +LIBPATH = "/home/carl/ft/gcc-arm-none-eabi-10-2020-q4-major/lib/gcc/arm-none-eabi/10.2.1" + + + +if PLATFORM == 'gcc': + # toolchains + PREFIX = 'arm-none-eabi-' + CC = PREFIX + 'gcc' + CXX = PREFIX + 'g++' + AS = PREFIX + 'gcc' + AR = PREFIX + 'ar' + LINK = PREFIX + 'gcc' + TARGET_EXT = 'elf' + SIZE = PREFIX + 'size' + OBJDUMP = PREFIX + 'objdump' + OBJCPY = PREFIX + 'objcopy' + STRIP = PREFIX + 'strip' + + + DEVICE = ' -march=armv8-a -mfpu=vfpv4-d16 -ftree-vectorize -ffast-math -mfloat-abi=soft --specs=nano.specs --specs=nosys.specs -fno-builtin ' + # DEVICE = ' -march=armv7-a -mfpu=vfpv3-d16 -ftree-vectorize -ffast-math -mfloat-abi=hard' + CFLAGS = DEVICE + ' -Wall' + AFLAGS = ' -c'+ DEVICE + ' -fsingle-precision-constant -fno-builtin -x assembler-with-cpp -D__ASSEMBLY__' + LINK_SCRIPT = 'ft_aarch32.lds' + LFLAGS = DEVICE + ' -Wl,--gc-sections,-Map=rtthread.map,-cref,-u,system_vectors'+\ + ' -T %s' % LINK_SCRIPT + + CPATH = '' + LPATH = LIBPATH + + # generate debug info in all cases + AFLAGS += ' -gdwarf-2' + CFLAGS += ' -g -gdwarf-2' + + if BUILD == 'debug': + CFLAGS += ' -O0' + else: + CFLAGS += ' -O2' + + POST_ACTION = OBJCPY + ' -O binary $TARGET rtthread.bin\n' +\ + SIZE + ' $TARGET \n' -- Gitee From 8b701757666247d5339f18f321c3b8b03d85fca8 Mon Sep 17 00:00:00 2001 From: huanghe Date: Tue, 18 May 2021 18:32:19 +0800 Subject: [PATCH 2/4] Modify some code styles --- bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c | 12 +++++++--- .../libraries/bsp/ft_gicv3/ft_gicv3_hw.c | 6 ++--- bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h | 14 +++++++++--- .../libraries/bsp/ft_gmac/ft_gmac_desc.c | 4 ++-- bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c | 22 +++++++++---------- bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c | 2 +- 6 files changed, 37 insertions(+), 23 deletions(-) diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c index fdaf53fd91..70b20aad0e 100644 --- a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3.c @@ -4,7 +4,7 @@ * of this software may be licensed only pursuant to the terms * of an applicable Phytium license agreement. * @Date: 2021-04-07 09:53:07 - * @LastEditTime: 2021-04-22 13:43:03 + * @LastEditTime: 2021-05-18 18:07:24 * @Description:  This files is for gic v3.0 * * @Modify History: @@ -93,7 +93,10 @@ s32 FGicV3_Connect(FGicv3 *GicPtr, if (FST_SUCCESS == FGicv3_RegisterHandler(GicPtr->Config, InterruptID, InterruptHandler, Args)) { - return FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, InterruptID, GIC_TRIGGER_LEVEL_SENSITIVE, Priority); + return FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, + InterruptID, + GIC_TRIGGER_LEVEL_SENSITIVE, + Priority); } else { @@ -152,7 +155,10 @@ void FGicV3_SetPriority(FGicv3 *GicPtr, Ft_assertVoid(GicPtr != NULL); Ft_assertVoid(InterruptID < GIC_INT_MAX_NUM); Ft_assertVoid(GicPtr->IsReady == FT_COMPONENT_IS_READLY); - FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, InterruptID, GIC_TRIGGER_LEVEL_SENSITIVE, Priority); + FGicV3_SetPriTrigTypeByDistAddr(GicPtr->Config->Gicv3DistributorAddress, + InterruptID, + GIC_TRIGGER_LEVEL_SENSITIVE, + Priority); } /** diff --git a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c index dbf2c395a3..8be3e8dd5f 100644 --- a/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c +++ b/bsp/ft2004/libraries/bsp/ft_gicv3/ft_gicv3_hw.c @@ -4,7 +4,7 @@ * of this software may be licensed only pursuant to the terms * of an applicable Phytium license agreement. * @Date: 2021-04-07 09:53:07 - * @LastEditTime: 2021-04-26 11:14:54 + * @LastEditTime: 2021-05-18 18:08:20 * @Description:  This files is for * * @Modify History: @@ -492,8 +492,8 @@ s32 FGicv3_InstanceInitialize(FGicv3_Config_t *Config) */ void FGicv3_InterruptHandler(u32 CurrentLevel, u32 InstanceId) { - FGicv3_Config_t *Config; - FGicv3_VectorTableEntry_t *TablePtr; + FGicv3_Config_t *Config = NULL; + FGicv3_VectorTableEntry_t *TablePtr = NULL; Config = &g_FGicv3_Config_tTable[(u32)InstanceId]; /* Read the icc_iar register to identify the hightest priority interrupt ID diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h index 5d9d2f149c..f149d3603b 100644 --- a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac.h @@ -4,7 +4,7 @@ * of this software may be licensed only pursuant to the terms * of an applicable Phytium license agreement. * @Date: 2021-04-07 09:53:07 - * @LastEditTime: 2021-04-19 17:53:35 + * @LastEditTime: 2021-05-18 18:10:36 * @Description:  This files is for * * @Modify History: @@ -182,7 +182,11 @@ typedef struct * @param {u32} RxBuffCount 缓冲区的总数量 * @return {s32} Common_status 参数。 */ -s32 FGmac_DmaRxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 DescBufPerLength, u32 TxBuffCount); +s32 FGmac_DmaRxDescRingInit(Ft_Gmac_t *Gmac, + FGmac_DmaDesc_t *DMATxDescTab, + u8 *TxBuff, + u32 DescBufPerLength, + u32 TxBuffCount); /** * @name: FGmac_DmaTxDescRingInit @@ -194,7 +198,11 @@ s32 FGmac_DmaRxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 * * @param {u32} TxBuffCount 缓冲区的总数量 * @return {s32} Common_status 参数 */ -s32 FGmac_DmaTxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 *TxBuff, u32 DescBufPerLength, u32 TxBuffCount); +s32 FGmac_DmaTxDescRingInit(Ft_Gmac_t *Gmac, + FGmac_DmaDesc_t *DMATxDescTab, + u8 *TxBuff, + u32 DescBufPerLength, + u32 TxBuffCount); /** * @name: FGmac_TransmitframeRingPoll diff --git a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c index b5d81c71fe..4971974efb 100644 --- a/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c +++ b/bsp/ft2004/libraries/bsp/ft_gmac/ft_gmac_desc.c @@ -4,7 +4,7 @@ * of this software may be licensed only pursuant to the terms * of an applicable Phytium license agreement. * @Date: 2021-04-07 09:53:07 - * @LastEditTime: 2021-05-18 13:28:05 + * @LastEditTime: 2021-05-18 17:58:33 * @Description:  This files is for gmac description * * @Modify History: @@ -116,7 +116,7 @@ s32 FGmac_DmaTxDescRingInit(Ft_Gmac_t *Gmac, FGmac_DmaDesc_t *DMATxDescTab, u8 * memset(&Gmac->TxDescRingData, 0, sizeof(Gmac->TxDescRingData)); Gmac->TxDescRingData.DescMaxNumber = TxBuffCount; - memset(DMATxDescTab, 0, sizeof(DMATxDescTab) * TxBuffCount); + memset(DMATxDescTab, 0, sizeof(FGmac_DmaDesc_t) * TxBuffCount); DMATxDescTab[TxBuffCount - 1].Control |= DMA_TDES1_END_RING; for (i = 0; i < TxBuffCount; i++) { diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c index 0f2bf9abab..5827123707 100644 --- a/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c +++ b/bsp/ft2004/libraries/bsp/ft_qspi/ft_qspi.c @@ -75,7 +75,7 @@ ft_error_t FQSpi_CfgInitialize(FQSpi_t *pQspi, FQSpi_Config_t *pConfig) static ft_error_t FQSpi_MemcpyToReg(FQSpi_t *pQspi, FT_IN u8 *buf, u32 length) { u32 val = 0; - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); pConfig = &pQspi->config; @@ -123,7 +123,7 @@ static ft_error_t FQSpi_MemcpyFromReg(FQSpi_t *pQspi, u8 *buf, u32 length) { s32 i; u32 val = 0; - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); pConfig = &pQspi->config; @@ -157,7 +157,7 @@ ft_error_t FQSpi_FlashRead(FQSpi_t *pQspi, FT_OUT u8 *rxBuf, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_RdCfgReg_t rdCfgReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -199,7 +199,7 @@ ft_error_t FQSpi_FlashWrite(FQSpi_t *pQspi, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_WrCfgReg_t wrCfgReg; u32 index = 0; u32 val = 0; @@ -270,7 +270,7 @@ ft_error_t FQSpi_FlashRegSet(FQSpi_t *pQspi, FT_IN u8 *writebuf, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_CmdPortReg_t cmdPortReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -313,7 +313,7 @@ ft_error_t FQSpi_FlashRegSetWithaddr(FQSpi_t *pQspi, FT_IN u8 *writebuf, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_CmdPortReg_t cmdPortReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -359,7 +359,7 @@ ft_error_t FQSpi_FlashRegGet(FQSpi_t *pQspi, u8 *readbuf, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_CmdPortReg_t cmdPortReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -386,7 +386,7 @@ ft_error_t FQSpi_FlashRegGetWithAddr(FQSpi_t *pQspi, u8 *readbuf, u32 length) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_CmdPortReg_t cmdPortReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -414,7 +414,7 @@ ft_error_t FQSpi_FlashRegGetWithAddr(FQSpi_t *pQspi, ft_error_t FQSpi_Write(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_WrCfgReg_t wrCfgReg; u32 length; u32 index = 0; @@ -497,7 +497,7 @@ ft_error_t FQSpi_Write(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) ft_error_t FQSpi_Read(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_RdCfgReg_t rdCfgReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); @@ -553,7 +553,7 @@ ft_error_t FQSpi_Read(FQSpi_t *pQspi, struct FQSpi_DataPack *pDataPack) ft_error_t FQSpi_CmdOperation(FQSpi_t *pQspi, struct FQSpi_CmdPack *pCmdPack) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig =NULL; FQSpi_CmdPortReg_t cmdPortReg; Ft_assertNonvoid(pQspi != NULL); Ft_assertNonvoid(pQspi->isReady == FT_COMPONENT_IS_READLY); diff --git a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c index 3d5691d2e3..fe3bb24cc8 100644 --- a/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c +++ b/bsp/ft2004/libraries/bsp/ft_qspi/qspi_hw.c @@ -41,7 +41,7 @@ void FQSpi_Reset(FQSpi_t *pQspi) { - FQSpi_Config_t *pConfig; + FQSpi_Config_t *pConfig = NULL; Ft_assertVoid(pQspi != NULL); pConfig = &pQspi->config; Ft_out32(pConfig->baseAddress + FT_REG_QSPI_CAP_OFFSET, -- Gitee From fb00ffa10d4203099b072567a4fd7584fcb65369 Mon Sep 17 00:00:00 2001 From: huanghe Date: Tue, 18 May 2021 18:39:42 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=88=A0=E9=99=A4=E4=B8=8D=E5=BF=85?= =?UTF-8?q?=E8=A6=81=E7=9A=84cpu=E6=96=87=E4=BB=B6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bsp/ft2004/.config | 8 +- bsp/ft2004/cpu/armv7.h | 72 ---- bsp/ft2004/cpu/cache.c | 100 ----- bsp/ft2004/cpu/context_gcc.S | 182 --------- bsp/ft2004/cpu/cp15.h | 41 -- bsp/ft2004/cpu/cp15_gcc.S | 138 ------- bsp/ft2004/cpu/cpu.c | 89 ----- bsp/ft2004/cpu/gic.c | 495 ------------------------ bsp/ft2004/cpu/gic.h | 62 --- bsp/ft2004/cpu/gicv3.c | 718 ----------------------------------- bsp/ft2004/cpu/gicv3.h | 188 --------- bsp/ft2004/cpu/gtimer.c | 171 --------- bsp/ft2004/cpu/gtimer.h | 26 -- bsp/ft2004/cpu/interrupt.c | 316 --------------- bsp/ft2004/cpu/interrupt.h | 60 --- bsp/ft2004/cpu/mmu.c | 183 --------- bsp/ft2004/cpu/mmu.h | 53 --- bsp/ft2004/cpu/pmu.c | 20 - bsp/ft2004/cpu/pmu.h | 174 --------- bsp/ft2004/cpu/stack.c | 71 ---- bsp/ft2004/cpu/start_gcc.S | 586 ---------------------------- bsp/ft2004/cpu/trap.c | 213 ----------- bsp/ft2004/cpu/vector_gcc.S | 51 --- bsp/ft2004/rtconfig.h | 2 + 24 files changed, 4 insertions(+), 4015 deletions(-) delete mode 100644 bsp/ft2004/cpu/armv7.h delete mode 100644 bsp/ft2004/cpu/cache.c delete mode 100644 bsp/ft2004/cpu/context_gcc.S delete mode 100644 bsp/ft2004/cpu/cp15.h delete mode 100644 bsp/ft2004/cpu/cp15_gcc.S delete mode 100644 bsp/ft2004/cpu/cpu.c delete mode 100644 bsp/ft2004/cpu/gic.c delete mode 100644 bsp/ft2004/cpu/gic.h delete mode 100644 bsp/ft2004/cpu/gicv3.c delete mode 100644 bsp/ft2004/cpu/gicv3.h delete mode 100644 bsp/ft2004/cpu/gtimer.c delete mode 100644 bsp/ft2004/cpu/gtimer.h delete mode 100644 bsp/ft2004/cpu/interrupt.c delete mode 100644 bsp/ft2004/cpu/interrupt.h delete mode 100644 bsp/ft2004/cpu/mmu.c delete mode 100644 bsp/ft2004/cpu/mmu.h delete mode 100644 bsp/ft2004/cpu/pmu.c delete mode 100644 bsp/ft2004/cpu/pmu.h delete mode 100644 bsp/ft2004/cpu/stack.c delete mode 100644 bsp/ft2004/cpu/start_gcc.S delete mode 100644 bsp/ft2004/cpu/trap.c delete mode 100644 bsp/ft2004/cpu/vector_gcc.S diff --git a/bsp/ft2004/.config b/bsp/ft2004/.config index 9a7018d70c..6bcf2ddb8d 100644 --- a/bsp/ft2004/.config +++ b/bsp/ft2004/.config @@ -8,7 +8,8 @@ # CONFIG_RT_NAME_MAX=32 # CONFIG_RT_USING_ARCH_DATA_TYPE is not set -# CONFIG_RT_USING_SMP is not set +CONFIG_RT_USING_SMP=y +CONFIG_RT_CPUS_NR=4 CONFIG_RT_ALIGN_SIZE=128 # CONFIG_RT_THREAD_PRIORITY_8 is not set CONFIG_RT_THREAD_PRIORITY_32=y @@ -310,11 +311,6 @@ CONFIG_RT_LWIP_USING_PING=y # # CONFIG_RT_USING_RYM is not set # CONFIG_RT_USING_ULOG is not set -# CONFIG_ULOG_OUTPUT_LVL_A is not set -# CONFIG_ULOG_OUTPUT_LVL_E is not set -# CONFIG_ULOG_OUTPUT_LVL_W is not set -# CONFIG_ULOG_OUTPUT_LVL_I is not set -# CONFIG_ULOG_OUTPUT_LVL_D is not set # CONFIG_RT_USING_UTEST is not set # CONFIG_RT_USING_RT_LINK is not set # CONFIG_RT_USING_LWP is not set diff --git a/bsp/ft2004/cpu/armv7.h b/bsp/ft2004/cpu/armv7.h deleted file mode 100644 index 2a945f3312..0000000000 --- a/bsp/ft2004/cpu/armv7.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - */ -#ifndef __ARMV7_H__ -#define __ARMV7_H__ - -/* the exception stack without VFP registers */ -struct rt_hw_exp_stack -{ - unsigned long r0; - unsigned long r1; - unsigned long r2; - unsigned long r3; - unsigned long r4; - unsigned long r5; - unsigned long r6; - unsigned long r7; - unsigned long r8; - unsigned long r9; - unsigned long r10; - unsigned long fp; - unsigned long ip; - unsigned long sp; - unsigned long lr; - unsigned long pc; - unsigned long cpsr; -}; - -struct rt_hw_stack -{ - unsigned long cpsr; - unsigned long r0; - unsigned long r1; - unsigned long r2; - unsigned long r3; - unsigned long r4; - unsigned long r5; - unsigned long r6; - unsigned long r7; - unsigned long r8; - unsigned long r9; - unsigned long r10; - unsigned long fp; - unsigned long ip; - unsigned long lr; - unsigned long pc; -}; - -#define USERMODE 0x10 -#define FIQMODE 0x11 -#define IRQMODE 0x12 -#define SVCMODE 0x13 -#define MONITORMODE 0x16 -#define ABORTMODE 0x17 -#define HYPMODE 0x1b -#define UNDEFMODE 0x1b -#define MODEMASK 0x1f -#define NOINT 0xc0 - -#define T_Bit (1 << 5) -#define F_Bit (1 << 6) -#define I_Bit (1 << 7) -#define A_Bit (1 << 8) -#define E_Bit (1 << 9) -#define J_Bit (1 << 24) - -#endif diff --git a/bsp/ft2004/cpu/cache.c b/bsp/ft2004/cpu/cache.c deleted file mode 100644 index 0a7e702dbb..0000000000 --- a/bsp/ft2004/cpu/cache.c +++ /dev/null @@ -1,100 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2019-03-29 quanzhao the first version - */ -#include -#include - -rt_inline rt_uint32_t rt_cpu_icache_line_size(void) -{ - rt_uint32_t ctr; - asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr)); - return 4 << (ctr & 0xF); -} - -rt_inline rt_uint32_t rt_cpu_dcache_line_size(void) -{ - rt_uint32_t ctr; - asm volatile ("mrc p15, 0, %0, c0, c0, 1" : "=r"(ctr)); - return 4 << ((ctr >> 16) & 0xF); -} - -void rt_hw_cpu_icache_invalidate(void *addr, int size) -{ - rt_uint32_t line_size = rt_cpu_icache_line_size(); - rt_uint32_t start_addr = (rt_uint32_t)addr; - rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; - - asm volatile ("dmb":::"memory"); - start_addr &= ~(line_size-1); - end_addr &= ~(line_size-1); - while (start_addr < end_addr) - { - asm volatile ("mcr p15, 0, %0, c7, c5, 1" :: "r"(start_addr)); /* icimvau */ - start_addr += line_size; - } - asm volatile ("dsb\n\tisb":::"memory"); -} - -void rt_hw_cpu_dcache_invalidate(void *addr, int size) -{ - rt_uint32_t line_size = rt_cpu_dcache_line_size(); - rt_uint32_t start_addr = (rt_uint32_t)addr; - rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; - - asm volatile ("dmb":::"memory"); - start_addr &= ~(line_size-1); - end_addr &= ~(line_size-1); - while (start_addr < end_addr) - { - asm volatile ("mcr p15, 0, %0, c7, c6, 1" :: "r"(start_addr)); /* dcimvac */ - start_addr += line_size; - } - asm volatile ("dsb":::"memory"); -} - -void rt_hw_cpu_dcache_clean(void *addr, int size) -{ - rt_uint32_t line_size = rt_cpu_dcache_line_size(); - rt_uint32_t start_addr = (rt_uint32_t)addr; - rt_uint32_t end_addr = (rt_uint32_t) addr + size + line_size - 1; - - asm volatile ("dmb":::"memory"); - start_addr &= ~(line_size-1); - end_addr &= ~(line_size-1); - while (start_addr < end_addr) - { - asm volatile ("mcr p15, 0, %0, c7, c10, 1" :: "r"(start_addr)); /* dccmvac */ - start_addr += line_size; - } - asm volatile ("dsb":::"memory"); -} - -void rt_hw_cpu_icache_ops(int ops, void *addr, int size) -{ - if (ops == RT_HW_CACHE_INVALIDATE) - rt_hw_cpu_icache_invalidate(addr, size); -} - -void rt_hw_cpu_dcache_ops(int ops, void *addr, int size) -{ - if (ops == RT_HW_CACHE_FLUSH) - rt_hw_cpu_dcache_clean(addr, size); - else if (ops == RT_HW_CACHE_INVALIDATE) - rt_hw_cpu_dcache_invalidate(addr, size); -} - -rt_base_t rt_hw_cpu_icache_status(void) -{ - return 0; -} - -rt_base_t rt_hw_cpu_dcache_status(void) -{ - return 0; -} diff --git a/bsp/ft2004/cpu/context_gcc.S b/bsp/ft2004/cpu/context_gcc.S deleted file mode 100644 index 768c5138e9..0000000000 --- a/bsp/ft2004/cpu/context_gcc.S +++ /dev/null @@ -1,182 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-05 Bernard the first version - */ - -#include "rtconfig.h" -.section .text, "ax" - -#ifdef RT_USING_SMP -#define rt_hw_interrupt_disable rt_hw_local_irq_disable -#define rt_hw_interrupt_enable rt_hw_local_irq_enable -#endif - -/* - * rt_base_t rt_hw_interrupt_disable(); - */ -.globl rt_hw_interrupt_disable -rt_hw_interrupt_disable: - mrs r0, cpsr - cpsid i - bx lr - -/* - * void rt_hw_interrupt_enable(rt_base_t level); - */ -.globl rt_hw_interrupt_enable -rt_hw_interrupt_enable: - msr cpsr, r0 - bx lr - -/* - * void rt_hw_context_switch_to(rt_uint32 to, struct rt_thread *to_thread); - * r0 --> to (thread stack) - * r1 --> to_thread - */ -.globl rt_hw_context_switch_to -rt_hw_context_switch_to: - ldr sp, [r0] @ get new task stack pointer - -#ifdef RT_USING_SMP - mov r0, r1 - bl rt_cpus_lock_status_restore -#endif /*RT_USING_SMP*/ - b rt_hw_context_switch_exit - -.section .bss.share.isr -_guest_switch_lvl: - .word 0 - -.globl vmm_virq_update - -.section .text.isr, "ax" -/* - * void rt_hw_context_switch(rt_uint32 from, rt_uint32 to, struct rt_thread *to_thread); - * r0 --> from (from_thread stack) - * r1 --> to (to_thread stack) - * r2 --> to_thread - */ -.globl rt_hw_context_switch -rt_hw_context_switch: - stmfd sp!, {lr} @ push pc (lr should be pushed in place of PC) - stmfd sp!, {r0-r12, lr} @ push lr & register file - - mrs r4, cpsr - tst lr, #0x01 - orrne r4, r4, #0x20 @ it's thumb code - - stmfd sp!, {r4} @ push cpsr - -#ifdef RT_USING_LWP - stmfd sp, {r13, r14}^ @ push usr_sp usr_lr - sub sp, #8 -#endif -#ifdef RT_USING_FPU - /* fpu context */ - vmrs r6, fpexc - tst r6, #(1<<30) - beq 1f - vstmdb sp!, {d0-d15} - vstmdb sp!, {d16-d31} - vmrs r5, fpscr - stmfd sp!, {r5} -1: - stmfd sp!, {r6} -#endif - - str sp, [r0] @ store sp in preempted tasks TCB - ldr sp, [r1] @ get new task stack pointer - -#ifdef RT_USING_SMP - mov r0, r2 - bl rt_cpus_lock_status_restore -#endif /*RT_USING_SMP*/ - b rt_hw_context_switch_exit - -/* - * void rt_hw_context_switch_interrupt(rt_uint32 from, rt_uint32 to); - */ -.equ Mode_USR, 0x10 -.equ Mode_FIQ, 0x11 -.equ Mode_IRQ, 0x12 -.equ Mode_SVC, 0x13 -.equ Mode_ABT, 0x17 -.equ Mode_UND, 0x1B -.equ Mode_SYS, 0x1F - -.equ I_Bit, 0x80 @ when I bit is set, IRQ is disabled -.equ F_Bit, 0x40 @ when F bit is set, FIQ is disabled - -.globl rt_thread_switch_interrupt_flag -.globl rt_interrupt_from_thread -.globl rt_interrupt_to_thread -.globl rt_hw_context_switch_interrupt -rt_hw_context_switch_interrupt: -#ifdef RT_USING_SMP - /* r0 :svc_mod context - * r1 :addr of from_thread's sp - * r2 :addr of to_thread's sp - * r3 :to_thread's tcb - */ - - str r0, [r1] - - ldr sp, [r2] - mov r0, r3 - bl rt_cpus_lock_status_restore - - b rt_hw_context_switch_exit - -#else /*RT_USING_SMP*/ - ldr r2, =rt_thread_switch_interrupt_flag - ldr r3, [r2] - cmp r3, #1 - beq _reswitch - ldr ip, =rt_interrupt_from_thread @ set rt_interrupt_from_thread - mov r3, #1 @ set rt_thread_switch_interrupt_flag to 1 - str r0, [ip] - str r3, [r2] -_reswitch: - ldr r2, =rt_interrupt_to_thread @ set rt_interrupt_to_thread - str r1, [r2] - bx lr -#endif /*RT_USING_SMP*/ - -.global rt_hw_context_switch_exit -rt_hw_context_switch_exit: - -#ifdef RT_USING_SMP -#ifdef RT_USING_SIGNALS - mov r0, sp - cps #Mode_IRQ - bl rt_signal_check - cps #Mode_SVC - mov sp, r0 -#endif -#endif -#ifdef RT_USING_FPU -/* fpu context */ - ldmfd sp!, {r6} - vmsr fpexc, r6 - tst r6, #(1<<30) - beq 1f - ldmfd sp!, {r5} - vmsr fpscr, r5 - vldmia sp!, {d16-d31} - vldmia sp!, {d0-d15} -1: -#endif - -#ifdef RT_USING_LWP - ldmfd sp, {r13, r14}^ /* usr_sp, usr_lr */ - add sp, #8 -#endif - ldmfd sp!, {r1} - msr spsr_cxsf, r1 /* original mode */ - ldmfd sp!, {r0-r12,lr,pc}^ /* irq return */ - diff --git a/bsp/ft2004/cpu/cp15.h b/bsp/ft2004/cpu/cp15.h deleted file mode 100644 index e0909eea36..0000000000 --- a/bsp/ft2004/cpu/cp15.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2018-03-25 quanzhao the first version - */ -#ifndef __CP15_H__ -#define __CP15_H__ - -#define __get_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MRC p" #cp ", " #op1 ", %0, c" #CRn ", c" #CRm ", " #op2 \ - : "=r"(Rt) \ - : \ - : "memory") -#define __set_cp(cp, op1, Rt, CRn, CRm, op2) __asm__ volatile("MCR p" #cp ", " #op1 ", %0, c" #CRn ", c" #CRm ", " #op2 \ - : \ - : "r"(Rt) \ - : "memory") -#define __get_cp64(cp, op1, Rt, CRm) __asm__ volatile("MRRC p" #cp ", " #op1 ", %Q0, %R0, c" #CRm \ - : "=r"(Rt) \ - : \ - : "memory") -#define __set_cp64(cp, op1, Rt, CRm) __asm__ volatile("MCRR p" #cp ", " #op1 ", %Q0, %R0, c" #CRm \ - : \ - : "r"(Rt) \ - : "memory") - -unsigned long rt_cpu_get_smp_id(void); - -void rt_cpu_mmu_disable(void); -void rt_cpu_mmu_enable(void); -void rt_cpu_tlb_set(volatile unsigned long *); - -void rt_cpu_dcache_clean_flush(void); -void rt_cpu_icache_flush(void); - -void rt_cpu_vector_set_base(unsigned int addr); - -#endif diff --git a/bsp/ft2004/cpu/cp15_gcc.S b/bsp/ft2004/cpu/cp15_gcc.S deleted file mode 100644 index dd2436ffee..0000000000 --- a/bsp/ft2004/cpu/cp15_gcc.S +++ /dev/null @@ -1,138 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-05 Bernard the first version - */ - -.globl rt_cpu_get_smp_id -rt_cpu_get_smp_id: - mrc p15, #0, r0, c0, c0, #5 - bx lr - -.globl rt_cpu_vector_set_base -rt_cpu_vector_set_base: - /* clear SCTRL.V to customize the vector address */ - mrc p15, #0, r1, c1, c0, #0 - bic r1, #(1 << 13) - mcr p15, #0, r1, c1, c0, #0 - /* set up the vector address */ - mcr p15, #0, r0, c12, c0, #0 - dsb - bx lr - -.globl rt_hw_cpu_dcache_enable -rt_hw_cpu_dcache_enable: - mrc p15, #0, r0, c1, c0, #0 - orr r0, r0, #0x00000004 - mcr p15, #0, r0, c1, c0, #0 - bx lr - -.globl rt_hw_cpu_icache_enable -rt_hw_cpu_icache_enable: - mrc p15, #0, r0, c1, c0, #0 - orr r0, r0, #0x00001000 - mcr p15, #0, r0, c1, c0, #0 - bx lr - -_FLD_MAX_WAY: - .word 0x3ff -_FLD_MAX_IDX: - .word 0x7fff - -.globl rt_cpu_dcache_clean_flush -rt_cpu_dcache_clean_flush: - push {r4-r11} - dmb - mrc p15, #1, r0, c0, c0, #1 @ read clid register - ands r3, r0, #0x7000000 @ get level of coherency - mov r3, r3, lsr #23 - beq finished - mov r10, #0 -loop1: - add r2, r10, r10, lsr #1 - mov r1, r0, lsr r2 - and r1, r1, #7 - cmp r1, #2 - blt skip - mcr p15, #2, r10, c0, c0, #0 - isb - mrc p15, #1, r1, c0, c0, #0 - and r2, r1, #7 - add r2, r2, #4 - ldr r4, _FLD_MAX_WAY - ands r4, r4, r1, lsr #3 - clz r5, r4 - ldr r7, _FLD_MAX_IDX - ands r7, r7, r1, lsr #13 -loop2: - mov r9, r4 -loop3: - orr r11, r10, r9, lsl r5 - orr r11, r11, r7, lsl r2 - mcr p15, #0, r11, c7, c14, #2 - subs r9, r9, #1 - bge loop3 - subs r7, r7, #1 - bge loop2 -skip: - add r10, r10, #2 - cmp r3, r10 - bgt loop1 - -finished: - dsb - isb - pop {r4-r11} - bx lr - -.globl rt_cpu_icache_flush -rt_cpu_icache_flush: - mov r0, #0 - mcr p15, 0, r0, c7, c5, 0 @ I+BTB cache invalidate - dsb - isb - bx lr - -.globl rt_hw_cpu_dcache_disable -rt_hw_cpu_dcache_disable: - push {r4-r11, lr} - bl rt_cpu_dcache_clean_flush - mrc p15, #0, r0, c1, c0, #0 - bic r0, r0, #0x00000004 - mcr p15, #0, r0, c1, c0, #0 - pop {r4-r11, lr} - bx lr - -.globl rt_hw_cpu_icache_disable -rt_hw_cpu_icache_disable: - mrc p15, #0, r0, c1, c0, #0 - bic r0, r0, #0x00001000 - mcr p15, #0, r0, c1, c0, #0 - bx lr - -.globl rt_cpu_mmu_disable -rt_cpu_mmu_disable: - mcr p15, #0, r0, c8, c7, #0 @ invalidate tlb - mrc p15, #0, r0, c1, c0, #0 - bic r0, r0, #1 - mcr p15, #0, r0, c1, c0, #0 @ clear mmu bit - dsb - bx lr - -.globl rt_cpu_mmu_enable -rt_cpu_mmu_enable: - mrc p15, #0, r0, c1, c0, #0 - orr r0, r0, #0x001 - mcr p15, #0, r0, c1, c0, #0 @ set mmu enable bit - dsb - bx lr - -.globl rt_cpu_tlb_set -rt_cpu_tlb_set: - mcr p15, #0, r0, c2, c0, #0 - dmb - bx lr diff --git a/bsp/ft2004/cpu/cpu.c b/bsp/ft2004/cpu/cpu.c deleted file mode 100644 index cb3c9aa7b8..0000000000 --- a/bsp/ft2004/cpu/cpu.c +++ /dev/null @@ -1,89 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2011-09-15 Bernard first version - * 2018-11-22 Jesven add rt_hw_cpu_id() - */ - -#include -#include -#include - -#ifdef RT_USING_SMP - -RT_WEAK int rt_hw_cpu_id(void) -{ - int cpu_id; - __asm__ volatile ( - "mrc p15, 0, %0, c0, c0, 5" - :"=r"(cpu_id) - ); - cpu_id &= 0xf; - return cpu_id; -}; - -void rt_hw_spin_lock_init(rt_hw_spinlock_t *lock) -{ - lock->slock = 0; -} - -void rt_hw_spin_lock(rt_hw_spinlock_t *lock) -{ - unsigned long tmp; - unsigned long newval; - rt_hw_spinlock_t lockval; - - __asm__ __volatile__( - "pld [%0]" - ::"r"(&lock->slock) - ); - - __asm__ __volatile__( - "1: ldrex %0, [%3]\n" - " add %1, %0, %4\n" - " strex %2, %1, [%3]\n" - " teq %2, #0\n" - " bne 1b" - : "=&r" (lockval), "=&r" (newval), "=&r" (tmp) - : "r" (&lock->slock), "I" (1 << 16) - : "cc"); - - while (lockval.tickets.next != lockval.tickets.owner) { - __asm__ __volatile__("wfe":::"memory"); - lockval.tickets.owner = *(volatile unsigned short *)(&lock->tickets.owner); - } - - __asm__ volatile ("dmb":::"memory"); -} - -void rt_hw_spin_unlock(rt_hw_spinlock_t *lock) -{ - __asm__ volatile ("dmb":::"memory"); - lock->tickets.owner++; - __asm__ volatile ("dsb ishst\nsev":::"memory"); -} -#endif /*RT_USING_SMP*/ - -/** - * @addtogroup ARM CPU - */ -/*@{*/ - -/** shutdown CPU */ -RT_WEAK void rt_hw_cpu_shutdown() -{ - rt_uint32_t level; - rt_kprintf("shutdown...\n"); - - level = rt_hw_interrupt_disable(); - while (level) - { - RT_ASSERT(0); - } -} - -/*@}*/ diff --git a/bsp/ft2004/cpu/gic.c b/bsp/ft2004/cpu/gic.c deleted file mode 100644 index 60e924d405..0000000000 --- a/bsp/ft2004/cpu/gic.c +++ /dev/null @@ -1,495 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-20 Bernard first version - * 2014-04-03 Grissiom many enhancements - * 2018-11-22 Jesven add rt_hw_ipi_send() - * add rt_hw_ipi_handler_install() - */ - -#include -#include - -#include "gic.h" -#include "cp15.h" - -struct arm_gic -{ - rt_uint32_t offset; /* the first interrupt index in the vector table */ - - rt_uint32_t dist_hw_base; /* the base address of the gic distributor */ - rt_uint32_t cpu_hw_base; /* the base addrees of the gic cpu interface */ -}; - -/* 'ARM_GIC_MAX_NR' is the number of cores */ -static struct arm_gic _gic_table[ARM_GIC_MAX_NR]; - -/** Macro to access the Generic Interrupt Controller Interface (GICC) -*/ -#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U) -#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U) -#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U) -#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU) -#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U) -#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U) -#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U) -#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU) - -/** Macro to access the Generic Interrupt Controller Distributor (GICD) -*/ -#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) -#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U) -#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n)/32U) * 4U) -#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n)/32U) * 4U) -#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n)/32U) * 4U) -#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n)/32U) * 4U) -#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n)/32U) * 4U) -#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n)/32U) * 4U) -#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n)/32U) * 4U) -#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n)/4U) * 4U) -#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n)/4U) * 4U) -#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n)/16U) * 4U) -#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U) -#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n)/4U) * 4U) -#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n)/4U) * 4U) -#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U) - -static unsigned int _gic_max_irq; - -int arm_gic_get_active_irq(rt_uint32_t index) -{ - int irq; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = GIC_CPU_INTACK(_gic_table[index].cpu_hw_base); - irq += _gic_table[index].offset; - return irq; -} - -void arm_gic_ack(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; - GIC_CPU_EOI(_gic_table[index].cpu_hw_base) = irq; -} - -void arm_gic_mask(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; -} - -void arm_gic_umask(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask; -} - -rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq) -{ - rt_uint32_t pend; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - } - else - { - /* INTID 0-15 Software Generated Interrupt */ - pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; - /* No CPU identification offered */ - if (pend != 0U) - { - pend = 1U; - } - else - { - pend = 0U; - } - } - - return (pend); -} - -void arm_gic_set_pending_irq(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U); - } - else - { - /* INTID 0-15 Software Generated Interrupt */ - /* Forward the interrupt to the CPU interface that requested it */ - GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U); - } -} - -void arm_gic_clear_pending_irq(rt_uint32_t index, int irq) -{ - rt_uint32_t mask; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - mask = 1U << (irq % 32U); - GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; - } - else - { - mask = 1U << ((irq % 4U) * 8U); - GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask; - } -} - -void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config) -{ - rt_uint32_t icfgr; - rt_uint32_t shift; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq); - shift = (irq % 16U) << 1U; - - icfgr &= (~(3U << shift)); - icfgr |= (config << shift); - - GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr; -} - -rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U)); -} - -void arm_gic_clear_active(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; -} - -/* Set up the cpu mask for the specific interrupt */ -void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask) -{ - rt_uint32_t old_tgt; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq); - - old_tgt &= ~(0x0FFUL << ((irq % 4U)*8U)); - old_tgt |= cpumask << ((irq % 4U)*8U); - - GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt; -} - -rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; -} - -void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority) -{ - rt_uint32_t mask; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq); - mask &= ~(0xFFUL << ((irq % 4U) * 8U)); - mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); - GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask; -} - -rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; -} - -void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - /* set priority mask */ - GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base) = priority & 0xFFUL; -} - -rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - return GIC_CPU_PRIMASK(_gic_table[index].cpu_hw_base); -} - -void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point) -{ - GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base) = binary_point & 0x7U; -} - -rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index) -{ - return GIC_CPU_BINPOINT(_gic_table[index].cpu_hw_base); -} - -rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq) -{ - rt_uint32_t pending; - rt_uint32_t active; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - - return ((active << 1U) | pending); -} - -void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = ((filter_list & 0x3U) << 24U) | ((target_list & 0xFFUL) << 16U) | (irq & 0x0FUL); -} - -rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - return GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base); -} - -rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base); -} - -void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group) -{ - uint32_t igroupr; - uint32_t shift; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT(group <= 1U); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq); - shift = (irq % 32U); - igroupr &= (~(1U << shift)); - igroupr |= ( (group & 0x1U) << shift); - - GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr; -} - -rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; -} - -int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start) -{ - unsigned int gic_type, i; - rt_uint32_t cpumask = 1U << 0U; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - _gic_table[index].dist_hw_base = dist_base; - _gic_table[index].offset = irq_start; - - /* Find out how many interrupts are supported. */ - gic_type = GIC_DIST_TYPE(dist_base); - _gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U; - - /* - * The GIC only supports up to 1020 interrupt sources. - * Limit this to either the architected maximum, or the - * platform maximum. - */ - if (_gic_max_irq > 1020U) - _gic_max_irq = 1020U; - if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */ - _gic_max_irq = ARM_GIC_NR_IRQS; - - cpumask |= cpumask << 8U; - cpumask |= cpumask << 16U; - cpumask |= cpumask << 24U; - - GIC_DIST_CTRL(dist_base) = 0x0U; - - /* Set all global interrupts to be level triggered, active low. */ - for (i = 32U; i < _gic_max_irq; i += 16U) - GIC_DIST_CONFIG(dist_base, i) = 0x0U; - - /* Set all global interrupts to this CPU only. */ - for (i = 32U; i < _gic_max_irq; i += 4U) - GIC_DIST_TARGET(dist_base, i) = cpumask; - - /* Set priority on all interrupts. */ - for (i = 0U; i < _gic_max_irq; i += 4U) - GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U; - - /* Disable all interrupts. */ - for (i = 0U; i < _gic_max_irq; i += 32U) - GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU; - -#if 0 - /* All interrupts defaults to IGROUP1(IRQ). */ - for (i = 0; i < _gic_max_irq; i += 32) - GIC_DIST_IGROUP(dist_base, i) = 0xffffffff; -#endif - for (i = 0U; i < _gic_max_irq; i += 32U) - GIC_DIST_IGROUP(dist_base, i) = 0U; - - /* Enable group0 and group1 interrupt forwarding. */ - GIC_DIST_CTRL(dist_base) = 0x01U; - - return 0; -} - -int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - _gic_table[index].cpu_hw_base = cpu_base; - - GIC_CPU_PRIMASK(cpu_base) = 0xf0U; - GIC_CPU_BINPOINT(cpu_base) = 0x7U; - /* Enable CPU interrupt */ - GIC_CPU_CTRL(cpu_base) = 0x01U; - - return 0; -} - -void arm_gic_dump_type(rt_uint32_t index) -{ - unsigned int gic_type; - - gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base); - rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n", - (GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL, - _gic_table[index].dist_hw_base, - _gic_max_irq, - gic_type & (1U << 10U) ? "has" : "no", - gic_type); -} - -void arm_gic_dump(rt_uint32_t index) -{ - unsigned int i, k; - - k = GIC_CPU_HIGHPRI(_gic_table[index].cpu_hw_base); - rt_kprintf("--- high pending priority: %d(%08x)\n", k, k); - rt_kprintf("--- hw mask ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n--- hw pending ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n--- hw active ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n"); -} - -long gic_dump(void) -{ - arm_gic_dump_type(0); - arm_gic_dump(0); - - return 0; -} -MSH_CMD_EXPORT(gic_dump, show gic status); - diff --git a/bsp/ft2004/cpu/gic.h b/bsp/ft2004/cpu/gic.h deleted file mode 100644 index e74b760f45..0000000000 --- a/bsp/ft2004/cpu/gic.h +++ /dev/null @@ -1,62 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-20 Bernard first version - */ - -#ifndef __GIC_H__ -#define __GIC_H__ - -#include -#include - -int arm_gic_get_active_irq(rt_uint32_t index); -void arm_gic_ack(rt_uint32_t index, int irq); - -void arm_gic_mask(rt_uint32_t index, int irq); -void arm_gic_umask(rt_uint32_t index, int irq); - -rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq); -void arm_gic_set_pending_irq(rt_uint32_t index, int irq); -void arm_gic_clear_pending_irq(rt_uint32_t index, int irq); - -void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config); -rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq); - -void arm_gic_clear_active(rt_uint32_t index, int irq); - -void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask); -rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq); - -void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority); -rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq); - -void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority); -rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index); - -void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point); -rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index); - -rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq); - -void arm_gic_send_sgi(rt_uint32_t index, int irq, rt_uint32_t target_list, rt_uint32_t filter_list); - -rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index); - -rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index); - -void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group); -rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq); - -int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start); -int arm_gic_cpu_init(rt_uint32_t index, rt_uint32_t cpu_base); - -void arm_gic_dump_type(rt_uint32_t index); -void arm_gic_dump(rt_uint32_t index); - -#endif - diff --git a/bsp/ft2004/cpu/gicv3.c b/bsp/ft2004/cpu/gicv3.c deleted file mode 100644 index 27663f18df..0000000000 --- a/bsp/ft2004/cpu/gicv3.c +++ /dev/null @@ -1,718 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-20 Bernard first version - * 2014-04-03 Grissiom many enhancements - * 2018-11-22 Jesven add rt_hw_ipi_send() - * add rt_hw_ipi_handler_install() - */ - -#include -#include - -#include "gicv3.h" -#include "cp15.h" - -#ifndef RT_CPUS_NR -#define RT_CPUS_NR 1 -#endif - -struct arm_gic_v3 -{ - rt_uint32_t offset; /* the first interrupt index in the vector table */ - rt_uint32_t redist_hw_base[RT_CPUS_NR]; /* the pointer of the gic redistributor */ - rt_uint32_t dist_hw_base; /* the base address of the gic distributor */ - rt_uint32_t cpu_hw_base[RT_CPUS_NR]; /* the base addrees of the gic cpu interface */ -}; - -/* 'ARM_GIC_MAX_NR' is the number of cores */ -static struct arm_gic_v3 _gic_table[ARM_GIC_MAX_NR]; -static unsigned int _gic_max_irq; - -/** - * @name: arm_gic_cpumask_to_affval - * @msg: - * @in param cpu_mask: - * @out param cluster_id: aff1 [0:7],aff2 [8:15],aff3 [16:23] - * @out param target_list: Target List. The set of PEs for which SGI interrupts will be generated. Each bit corresponds to the - * PE within a cluster with an Affinity 0 value equal to the bit number. - * @return {rt_uint32_t} 0 is finish , 1 is data valid - */ -RT_WEAK rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list) -{ - return 0; -} - -RT_WEAK rt_uint64_t get_main_cpu_affval(void) -{ - return 0; -} - -int arm_gic_get_active_irq(rt_uint32_t index) -{ - int irq; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - __get_gicv3_reg(ICC_IAR1, irq); - - irq = (irq & 0x1FFFFFF) + _gic_table[index].offset; - return irq; -} - -void arm_gic_ack(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT(irq >= 0U); - - __asm__ volatile("dsb 0xF" :: - : "memory"); - __set_gicv3_reg(ICC_EOIR1, irq); -} - -void arm_gic_mask(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq < 32U) - { -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - - RT_ASSERT((cpu_id) < RT_CPUS_NR); - GIC_RDISTSGI_ICENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask; - } - else - { - GIC_DIST_ENABLE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; - } -} - -void arm_gic_umask(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq < 32U) - { -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - RT_ASSERT((cpu_id) < RT_CPUS_NR); - GIC_RDISTSGI_ISENABLER0(_gic_table[index].redist_hw_base[cpu_id]) = mask; - } - else - { - GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, irq) = mask; - } -} - -rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq) -{ - rt_uint32_t pend; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - pend = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - } - else - { - /* INTID 0-15 Software Generated Interrupt */ - pend = (GIC_DIST_SPENDSGI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; - /* No CPU identification offered */ - if (pend != 0U) - { - pend = 1U; - } - else - { - pend = 0U; - } - } - - return (pend); -} - -void arm_gic_set_pending_irq(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) = 1U << (irq % 32U); - } - else - { - /* INTID 0-15 Software Generated Interrupt */ - /* Forward the interrupt to the CPU interface that requested it */ - GIC_DIST_SOFTINT(_gic_table[index].dist_hw_base) = (irq | 0x02000000U); - } -} - -void arm_gic_clear_pending_irq(rt_uint32_t index, int irq) -{ - rt_uint32_t mask; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq >= 16U) - { - mask = 1U << (irq % 32U); - GIC_DIST_PENDING_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; - } - else - { - mask = 1U << ((irq % 4U) * 8U); - GIC_DIST_CPENDSGI(_gic_table[index].dist_hw_base, irq) = mask; - } -} - -void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config) -{ - rt_uint32_t icfgr; - rt_uint32_t shift; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - icfgr = GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq); - shift = (irq % 16U) << 1U; - - icfgr &= (~(3U << shift)); - icfgr |= (config << shift); - - GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) = icfgr; -} - -rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_CONFIG(_gic_table[index].dist_hw_base, irq) >> ((irq % 16U) >> 1U)); -} - -void arm_gic_clear_active(rt_uint32_t index, int irq) -{ - rt_uint32_t mask = 1U << (irq % 32U); - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - GIC_DIST_ACTIVE_CLEAR(_gic_table[index].dist_hw_base, irq) = mask; -} - -/* Set up the cpu mask for the specific interrupt */ -void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask) -{ - rt_uint32_t old_tgt; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - old_tgt = GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq); - - old_tgt &= ~(0x0FFUL << ((irq % 4U) * 8U)); - old_tgt |= cpumask << ((irq % 4U) * 8U); - - GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) = old_tgt; -} - -rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_TARGET(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; -} - -void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority) -{ - rt_uint32_t mask; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq < 32U) - { -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - RT_ASSERT((cpu_id) < RT_CPUS_NR); - mask = GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq); - mask &= ~(0xFFUL << ((irq % 4U) * 8U)); - mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); - GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) = mask; - } - else - { - mask = GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq); - mask &= ~(0xFFUL << ((irq % 4U) * 8U)); - mask |= ((priority & 0xFFUL) << ((irq % 4U) * 8U)); - GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) = mask; - } -} - -rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - if (irq < 32U) - { -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - RT_ASSERT((cpu_id) < RT_CPUS_NR); - return (GIC_RDISTSGI_IPRIORITYR(_gic_table[index].redist_hw_base[cpu_id], irq) >> ((irq % 4U) * 8U)) & 0xFFUL; - } - else - { - return (GIC_DIST_PRI(_gic_table[index].dist_hw_base, irq) >> ((irq % 4U) * 8U)) & 0xFFUL; - } -} - -void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - value &= 0xFFUL; - /* set priority mask */ - __set_gicv3_reg(ICC_SRE, value); -} - -rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - rt_uint32_t value; - - __get_gicv3_reg(ICC_SRE, value); - return value; -} - -void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - priority &= 0xFFUL; - /* set priority mask */ - __set_gicv3_reg(ICC_PMR, priority); -} - -rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - rt_uint32_t priority; - - __get_gicv3_reg(ICC_PMR, priority); - return priority; -} - -void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point) -{ - index = index; - binary_point &= 0x7U; - - __set_gicv3_reg(ICC_BPR1, binary_point); -} - -rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index) -{ - rt_uint32_t binary_point; - - index = index; - __get_gicv3_reg(ICC_BPR1, binary_point); - return binary_point; -} - -rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq) -{ - rt_uint32_t pending; - rt_uint32_t active; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - active = (GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - pending = (GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; - - return ((active << 1U) | pending); -} - -void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode) -{ - rt_uint64_t sgi_val; - if (routing_mode) - { - sgi_val = (1 << 40) | ((irq & 0x0FUL) << 24); //Interrupts routed to all PEs in the system, excluding "self". - /* Write the ICC_SGI1R registers */ - __asm__ volatile("dsb 0xF" :: - : "memory"); - __set_cp64(15, 0, sgi_val, 12); - __asm__ volatile("isb 0xF" :: - : "memory"); - } - else - { - rt_uint32_t cluster_id, target_list; - while (arm_gic_cpumask_to_affval(&cpu_mask, &cluster_id, &target_list)) - { - sgi_val = ((irq & 0x0FUL) << 24 | - target_list | - ((cluster_id >> 8) & 0xff) << GIC_RSGI_AFF1_OFFSET | - ((cluster_id >> 16) & 0xff) << GIC_RSGI_AFF2_OFFSET | - ((cluster_id >> 24) & 0xff) << GIC_RSGI_AFF3_OFFSET); - - __asm__ volatile("dsb 0xF" :: - : "memory"); - __set_cp64(15, 0, sgi_val, 12); - __asm__ volatile("isb 0xF" :: - : "memory"); - } - } -} - -rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index) -{ - rt_uint32_t irq; - RT_ASSERT(index < ARM_GIC_MAX_NR); - - index = index; - __get_gicv3_reg(ICC_HPPIR1, irq); - return irq; -} - -rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - return GIC_CPU_IIDR(_gic_table[index].cpu_hw_base); -} - -void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group) -{ - uint32_t igroupr; - uint32_t shift; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT(group <= 1U); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - igroupr = GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq); - shift = (irq % 32U); - igroupr &= (~(1U << shift)); - igroupr |= ((group & 0x1U) << shift); - - GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) = igroupr; -} - -rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - - irq = irq - _gic_table[index].offset; - RT_ASSERT(irq >= 0U); - - return (GIC_DIST_IGROUP(_gic_table[index].dist_hw_base, irq) >> (irq % 32U)) & 0x1UL; -} - -static int arm_gicv3_wait_rwp(rt_uint32_t index, rt_uint32_t irq) -{ - rt_uint32_t rwp_bit; - rt_uint32_t base; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - if (irq < 32u) - { -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - RT_ASSERT((cpu_id) < RT_CPUS_NR); - base = _gic_table[index].redist_hw_base[cpu_id]; - rwp_bit = GICR_CTLR_RWP; - } - else - { - base = _gic_table[index].dist_hw_base; - rwp_bit = GICD_CTLR_RWP; - } - - while (__REG32(base) & rwp_bit) - { - ; - } - - return 0; -} - -int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start) -{ - rt_uint64_t cpu0_affval; - unsigned int gic_type, i; - - RT_ASSERT(index < ARM_GIC_MAX_NR); - - _gic_table[index].dist_hw_base = dist_base; - _gic_table[index].offset = irq_start; - - /* Find out how many interrupts are supported. */ - gic_type = GIC_DIST_TYPE(dist_base); - _gic_max_irq = ((gic_type & 0x1fU) + 1U) * 32U; - - /* - * The GIC only supports up to 1020 interrupt sources. - * Limit this to either the architected maximum, or the - * platform maximum. - */ - if (_gic_max_irq > 1020U) - _gic_max_irq = 1020U; - if (_gic_max_irq > ARM_GIC_NR_IRQS) /* the platform maximum interrupts */ - _gic_max_irq = ARM_GIC_NR_IRQS; - - GIC_DIST_CTRL(dist_base) = 0x0U; - /* Wait for register write pending */ - arm_gicv3_wait_rwp(0, 32); - - /* Set all global interrupts to be level triggered, active low. */ - for (i = 32U; i < _gic_max_irq; i += 16U) - GIC_DIST_CONFIG(dist_base, i) = 0x0U; - - arm_gicv3_wait_rwp(0, 32); - - cpu0_affval = get_main_cpu_affval(); - /* Set all global interrupts to this CPU only. */ - for (i = 32U; i < _gic_max_irq; i++) - { - GIC_DIST_IROUTER_LOW(dist_base, i) = cpu0_affval; - GIC_DIST_IROUTER_HIGH(dist_base, i) = cpu0_affval >> 32; - } - - arm_gicv3_wait_rwp(0, 32); - - /* Set priority on spi interrupts. */ - for (i = 32U; i < _gic_max_irq; i += 4U) - GIC_DIST_PRI(dist_base, i) = 0xa0a0a0a0U; - - arm_gicv3_wait_rwp(0, 32); - /* Disable all interrupts. */ - for (i = 0U; i < _gic_max_irq; i += 32U) - { - GIC_DIST_PENDING_CLEAR(dist_base, i) = 0xffffffffU; - GIC_DIST_ENABLE_CLEAR(dist_base, i) = 0xffffffffU; - } - - arm_gicv3_wait_rwp(0, 32); - /* All interrupts defaults to IGROUP1(IRQ). */ - for (i = 0U; i < _gic_max_irq; i += 32U) - GIC_DIST_IGROUP(dist_base, i) = 0xffffffffU; - - arm_gicv3_wait_rwp(0, 32); - - /* - The Distributor control register (GICD_CTLR) must be configured to enable the interrupt groups and to set the routing mode. - Enable Affinity routing (ARE bits) The ARE bits in GICD_CTLR control whether affinity routing is enabled. - If affinity routing is not enabled, GICv3 can be configured for legacy operation . - Whether affinity routing is enabled or not can be controlled separately for Secure and Non-secure state. - Enables GICD_CTLR contains separate enable bits for Group 0, Secure Group 1 and Non-secure Group 1: - GICD_CTLR.EnableGrp1S enables distribution of Secure Group 1 interrupts. - GICD_CTLR.EnableGrp1NS enables distribution of Non-secure Group 1 interrupts. - GICD_CTLR.EnableGrp0 enables distribution of Group 0 interrupts. - */ - GIC_DIST_CTRL(dist_base) = GICD_CTLR_ARE_NS | GICD_CTLR_ENGRP1NS; - - return 0; -} - -int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT((cpu_id) < RT_CPUS_NR); - _gic_table[index].redist_hw_base[cpu_id] = redist_addr; - - return 0; -} - -int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id) -{ - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT((cpu_id) < RT_CPUS_NR); - _gic_table[index].cpu_hw_base[cpu_id] = interface_addr; - - return 0; -} - -int arm_gic_redist_init(rt_uint32_t index) -{ - unsigned int i; - rt_uint32_t base; - -#ifdef RT_USING_SMP - rt_int32_t cpu_id = rt_hw_cpu_id(); -#else - rt_int32_t cpu_id = 0; -#endif - RT_ASSERT(index < ARM_GIC_MAX_NR); - RT_ASSERT((cpu_id) < RT_CPUS_NR); - - base = _gic_table[index].redist_hw_base[cpu_id]; - /* redistributor enable */ - GIC_RDIST_WAKER(base) &= ~(1U << 1); - while (GIC_RDIST_WAKER(base) & (1 << 2)) - { - ; - } - - /* Disable all sgi and ppi interrupt */ - GIC_RDISTSGI_ICENABLER0(base) = 0xFFFFFFFF; - arm_gicv3_wait_rwp(0, 0); - - /* Clear all inetrrupt pending */ - GIC_RDISTSGI_ICPENDR0(base) = 0xFFFFFFFF; - - /* the corresponding interrupt is Group 1 or Non-secure Group 1. */ - GIC_RDISTSGI_IGROUPR0(base, 0) = 0xFFFFFFFF; - GIC_RDISTSGI_IGRPMODR0(base, 0) = 0xFFFFFFFF; - - /* Configure default priorities for SGI 0:15 and PPI 16:31. */ - for (i = 0; i < 32; i += 4) - { - GIC_RDISTSGI_IPRIORITYR(base, i) = 0xa0a0a0a0U; - } - - /* Trigger level for PPI interrupts*/ - GIC_RDISTSGI_ICFGR1(base) = 0x0U; // PPI is level-sensitive. - return 0; -} - -int arm_gic_cpu_init(rt_uint32_t index) -{ - rt_uint32_t value; - RT_ASSERT(index < ARM_GIC_MAX_NR); - - value = arm_gic_get_system_register_enable_mask(index); - value |= (1U << 0); - arm_gic_set_system_register_enable_mask(index, value); - __set_gicv3_reg(ICC_CTLR, 0); - - arm_gic_set_interface_prior_mask(index, 0xFFU); - - /* Enable group1 interrupt */ - value = 0x1U; - __set_gicv3_reg(ICC_IGRPEN1, value); - - arm_gic_set_binary_point(0, 0); - - /* ICC_BPR0_EL1 determines the preemption group for both - Group 0 and Group 1 interrupts. - */ - value = 0x1U; - __set_gicv3_reg(ICC_CTLR, value); - - return 0; -} - -void arm_gic_dump_type(rt_uint32_t index) -{ - unsigned int gic_type; - - gic_type = GIC_DIST_TYPE(_gic_table[index].dist_hw_base); - rt_kprintf("GICv%d on %p, max IRQs: %d, %s security extension(%08x)\n", - (GIC_DIST_ICPIDR2(_gic_table[index].dist_hw_base) >> 4U) & 0xfUL, - _gic_table[index].dist_hw_base, - _gic_max_irq, - gic_type & (1U << 10U) ? "has" : "no", - gic_type); -} - -void arm_gic_dump(rt_uint32_t index) -{ - unsigned int i, k; - - k = arm_gic_get_high_pending_irq(0); - rt_kprintf("--- high pending priority: %d(%08x)\n", k, k); - rt_kprintf("--- hw mask ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_ENABLE_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n--- hw pending ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_PENDING_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n--- hw active ---\n"); - for (i = 0U; i < _gic_max_irq / 32U; i++) - { - rt_kprintf("0x%08x, ", - GIC_DIST_ACTIVE_SET(_gic_table[index].dist_hw_base, - i * 32U)); - } - rt_kprintf("\n"); -} - -long gic_dump(void) -{ - arm_gic_dump_type(0); - arm_gic_dump(0); - - return 0; -} -MSH_CMD_EXPORT(gic_dump, show gic status); diff --git a/bsp/ft2004/cpu/gicv3.h b/bsp/ft2004/cpu/gicv3.h deleted file mode 100644 index 0661a82a39..0000000000 --- a/bsp/ft2004/cpu/gicv3.h +++ /dev/null @@ -1,188 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-20 Bernard first version - */ - -#ifndef __GIC_V3_H__ -#define __GIC_V3_H__ - -#include -#include - -#define __get_gicv3_reg(CR, Rt) __asm__ volatile("MRC " CR \ - : "=r"(Rt) \ - : \ - : "memory") -#define __set_gicv3_reg(CR, Rt) __asm__ volatile("MCR " CR \ - : \ - : "r"(Rt) \ - : "memory") - -/* AArch32 System register interface to GICv3 */ -#define ICC_IAR0 "p15, 0, %0, c12, c8, 0" -#define ICC_IAR1 "p15, 0, %0, c12, c12, 0" -#define ICC_EOIR0 "p15, 0, %0, c12, c8, 1" -#define ICC_EOIR1 "p15, 0, %0, c12, c12, 1" -#define ICC_HPPIR0 "p15, 0, %0, c12, c8, 2" -#define ICC_HPPIR1 "p15, 0, %0, c12, c12, 2" -#define ICC_BPR0 "p15, 0, %0, c12, c8, 3" -#define ICC_BPR1 "p15, 0, %0, c12, c12, 3" -#define ICC_DIR "p15, 0, %0, c12, c11, 1" -#define ICC_PMR "p15, 0, %0, c4, c6, 0" -#define ICC_RPR "p15, 0, %0, c12, c11, 3" -#define ICC_CTLR "p15, 0, %0, c12, c12, 4" -#define ICC_MCTLR "p15, 6, %0, c12, c12, 4" -#define ICC_SRE "p15, 0, %0, c12, c12, 5" -#define ICC_HSRE "p15, 4, %0, c12, c9, 5" -#define ICC_MSRE "p15, 6, %0, c12, c12, 5" -#define ICC_IGRPEN0 "p15, 0, %0, c12, c12, 6" -#define ICC_IGRPEN1 "p15, 0, %0, c12, c12, 7" -#define ICC_MGRPEN1 "p15, 6, %0, c12, c12, 7" - -#define __REG32(x) (*((volatile unsigned int *)((rt_uint32_t)x))) - -#define ROUTED_TO_ALL (1) -#define ROUTED_TO_SPEC (0) - -/** Macro to access the Distributor Control Register (GICD_CTLR) -*/ -#define GICD_CTLR_RWP (1 << 31) -#define GICD_CTLR_E1NWF (1 << 7) -#define GICD_CTLR_DS (1 << 6) -#define GICD_CTLR_ARE_NS (1 << 5) -#define GICD_CTLR_ARE_S (1 << 4) -#define GICD_CTLR_ENGRP1S (1 << 2) -#define GICD_CTLR_ENGRP1NS (1 << 1) -#define GICD_CTLR_ENGRP0 (1 << 0) - -/** Macro to access the Redistributor Control Register (GICR_CTLR) -*/ -#define GICR_CTLR_UWP (1 << 31) -#define GICR_CTLR_DPG1S (1 << 26) -#define GICR_CTLR_DPG1NS (1 << 25) -#define GICR_CTLR_DPG0 (1 << 24) -#define GICR_CTLR_RWP (1 << 3) -#define GICR_CTLR_IR (1 << 2) -#define GICR_CTLR_CES (1 << 1) -#define GICR_CTLR_EnableLPI (1 << 0) - -/** Macro to access the Generic Interrupt Controller Interface (GICC) -*/ -#define GIC_CPU_CTRL(hw_base) __REG32((hw_base) + 0x00U) -#define GIC_CPU_PRIMASK(hw_base) __REG32((hw_base) + 0x04U) -#define GIC_CPU_BINPOINT(hw_base) __REG32((hw_base) + 0x08U) -#define GIC_CPU_INTACK(hw_base) __REG32((hw_base) + 0x0cU) -#define GIC_CPU_EOI(hw_base) __REG32((hw_base) + 0x10U) -#define GIC_CPU_RUNNINGPRI(hw_base) __REG32((hw_base) + 0x14U) -#define GIC_CPU_HIGHPRI(hw_base) __REG32((hw_base) + 0x18U) -#define GIC_CPU_IIDR(hw_base) __REG32((hw_base) + 0xFCU) - -/** Macro to access the Generic Interrupt Controller Distributor (GICD) -*/ -#define GIC_DIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) -#define GIC_DIST_TYPE(hw_base) __REG32((hw_base) + 0x004U) -#define GIC_DIST_IGROUP(hw_base, n) __REG32((hw_base) + 0x080U + ((n) / 32U) * 4U) -#define GIC_DIST_ENABLE_SET(hw_base, n) __REG32((hw_base) + 0x100U + ((n) / 32U) * 4U) -#define GIC_DIST_ENABLE_CLEAR(hw_base, n) __REG32((hw_base) + 0x180U + ((n) / 32U) * 4U) -#define GIC_DIST_PENDING_SET(hw_base, n) __REG32((hw_base) + 0x200U + ((n) / 32U) * 4U) -#define GIC_DIST_PENDING_CLEAR(hw_base, n) __REG32((hw_base) + 0x280U + ((n) / 32U) * 4U) -#define GIC_DIST_ACTIVE_SET(hw_base, n) __REG32((hw_base) + 0x300U + ((n) / 32U) * 4U) -#define GIC_DIST_ACTIVE_CLEAR(hw_base, n) __REG32((hw_base) + 0x380U + ((n) / 32U) * 4U) -#define GIC_DIST_PRI(hw_base, n) __REG32((hw_base) + 0x400U + ((n) / 4U) * 4U) -#define GIC_DIST_TARGET(hw_base, n) __REG32((hw_base) + 0x800U + ((n) / 4U) * 4U) -#define GIC_DIST_CONFIG(hw_base, n) __REG32((hw_base) + 0xc00U + ((n) / 16U) * 4U) -#define GIC_DIST_SOFTINT(hw_base) __REG32((hw_base) + 0xf00U) -#define GIC_DIST_CPENDSGI(hw_base, n) __REG32((hw_base) + 0xf10U + ((n) / 4U) * 4U) -#define GIC_DIST_SPENDSGI(hw_base, n) __REG32((hw_base) + 0xf20U + ((n) / 4U) * 4U) -#define GIC_DIST_ICPIDR2(hw_base) __REG32((hw_base) + 0xfe8U) -#define GIC_DIST_IROUTER_LOW(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U) -#define GIC_DIST_IROUTER_HIGH(hw_base, n) __REG32((hw_base) + 0x6000U + (n)*8U + 4) - -/* SGI base address is at 64K offset from Redistributor base address */ -#define GIC_RSGI_OFFSET 0x10000 - -/** Macro to access the Generic Interrupt Controller Redistributor (GICD) -*/ -#define GIC_RDIST_CTRL(hw_base) __REG32((hw_base) + 0x000U) -#define GIC_RDIST_IIDR(hw_base) __REG32((hw_base) + 0x004U) -#define GIC_RDIST_TYPER(hw_base) __REG32((hw_base) + 0x008U) -#define GIC_RDIST_TSTATUSR(hw_base) __REG32((hw_base) + 0x010U) -#define GIC_RDIST_WAKER(hw_base) __REG32((hw_base) + 0x014U) -#define GIC_RDIST_SETLPIR(hw_base) __REG32((hw_base) + 0x040U) -#define GIC_RDIST_CLRLPIR(hw_base) __REG32((hw_base) + 0x048U) -#define GIC_RDIST_PROPBASER(hw_base) __REG32((hw_base) + 0x070U) -#define GIC_RDIST_PENDBASER(hw_base) __REG32((hw_base) + 0x078U) -#define GIC_RDIST_INVLPIR(hw_base) __REG32((hw_base) + 0x0A0U) -#define GIC_RDIST_INVALLR(hw_base) __REG32((hw_base) + 0x0B0U) -#define GIC_RDIST_SYNCR(hw_base) __REG32((hw_base) + 0x0C0U) - -#define GIC_RDISTSGI_IGROUPR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x080U + (n)*4U) -#define GIC_RDISTSGI_ISENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x100U) -#define GIC_RDISTSGI_ICENABLER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x180U) -#define GIC_RDISTSGI_ISPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x200U) -#define GIC_RDISTSGI_ICPENDR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x280U) -#define GIC_RDISTSGI_ISACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x300U) -#define GIC_RDISTSGI_ICACTIVER0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x380U) -#define GIC_RDISTSGI_IPRIORITYR(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0x400U + ((n) / 4U) * 4U) -#define GIC_RDISTSGI_ICFGR0(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC00U) -#define GIC_RDISTSGI_ICFGR1(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xC04U) -#define GIC_RDISTSGI_IGRPMODR0(hw_base, n) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xD00U + (n)*4) -#define GIC_RDISTSGI_NSACR(hw_base) __REG32((hw_base) + GIC_RSGI_OFFSET + 0xE00U) - -rt_uint32_t arm_gic_cpumask_to_affval(rt_uint32_t *cpu_mask, rt_uint32_t *cluster_id, rt_uint32_t *target_list); -rt_uint64_t get_main_cpu_affval(void); -int arm_gic_get_active_irq(rt_uint32_t index); -void arm_gic_ack(rt_uint32_t index, int irq); - -void arm_gic_mask(rt_uint32_t index, int irq); -void arm_gic_umask(rt_uint32_t index, int irq); - -rt_uint32_t arm_gic_get_pending_irq(rt_uint32_t index, int irq); -void arm_gic_set_pending_irq(rt_uint32_t index, int irq); -void arm_gic_clear_pending_irq(rt_uint32_t index, int irq); - -void arm_gic_set_configuration(rt_uint32_t index, int irq, uint32_t config); -rt_uint32_t arm_gic_get_configuration(rt_uint32_t index, int irq); - -void arm_gic_clear_active(rt_uint32_t index, int irq); - -void arm_gic_set_cpu(rt_uint32_t index, int irq, unsigned int cpumask); -rt_uint32_t arm_gic_get_target_cpu(rt_uint32_t index, int irq); - -void arm_gic_set_priority(rt_uint32_t index, int irq, rt_uint32_t priority); -rt_uint32_t arm_gic_get_priority(rt_uint32_t index, int irq); - -void arm_gic_set_interface_prior_mask(rt_uint32_t index, rt_uint32_t priority); -rt_uint32_t arm_gic_get_interface_prior_mask(rt_uint32_t index); - -void arm_gic_set_binary_point(rt_uint32_t index, rt_uint32_t binary_point); -rt_uint32_t arm_gic_get_binary_point(rt_uint32_t index); - -rt_uint32_t arm_gic_get_irq_status(rt_uint32_t index, int irq); - -void arm_gic_send_affinity_sgi(rt_uint32_t index, int irq, rt_uint32_t cpu_mask, rt_uint32_t routing_mode); -rt_uint32_t arm_gic_get_high_pending_irq(rt_uint32_t index); - -rt_uint32_t arm_gic_get_interface_id(rt_uint32_t index); - -void arm_gic_set_group(rt_uint32_t index, int irq, rt_uint32_t group); -rt_uint32_t arm_gic_get_group(rt_uint32_t index, int irq); - -int arm_gic_redist_address_set(rt_uint32_t index, rt_uint32_t redist_addr, rt_uint32_t cpu_id); -int arm_gic_cpu_interface_address_set(rt_uint32_t index, rt_uint32_t interface_addr, rt_uint32_t cpu_id); -int arm_gic_dist_init(rt_uint32_t index, rt_uint32_t dist_base, int irq_start); -int arm_gic_cpu_init(rt_uint32_t index); -int arm_gic_redist_init(rt_uint32_t index); - -void arm_gic_dump_type(rt_uint32_t index); -void arm_gic_dump(rt_uint32_t index); - -void arm_gic_set_system_register_enable_mask(rt_uint32_t index, rt_uint32_t value); -rt_uint32_t arm_gic_get_system_register_enable_mask(rt_uint32_t index); - -#endif diff --git a/bsp/ft2004/cpu/gtimer.c b/bsp/ft2004/cpu/gtimer.c deleted file mode 100644 index b193a1593d..0000000000 --- a/bsp/ft2004/cpu/gtimer.c +++ /dev/null @@ -1,171 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2021-03-30 huijie.feng first version - */ - -#include "cp15.h" -#include - -/** Set CNTFRQ - * This function assigns the given value to PL1 Physical Timer Counter Frequency Register (CNTFRQ). - * @param value: CNTFRQ Register value to set - */ -static inline void __set_cntfrq(rt_uint32_t value) -{ - __set_cp(15, 0, value, 14, 0, 0); -} - -/** Get CNTFRQ - * This function returns the value of the PL1 Physical Timer Counter Frequency Register (CNTFRQ). - * return CNTFRQ Register value - */ -static inline rt_uint32_t __get_cntfrq(void) -{ - rt_uint32_t result; - __get_cp(15, 0, result, 14, 0 , 0); - return result; -} - -/** Set CNTP_TVAL - * This function assigns the given value to PL1 Physical Timer Value Register (CNTP_TVAL). - * param value: CNTP_TVAL Register value to set - */ -static inline void __set_cntp_tval(rt_uint32_t value) -{ - __set_cp(15, 0, value, 14, 2, 0); -} - -/** Get CNTP_TVAL - * This function returns the value of the PL1 Physical Timer Value Register (CNTP_TVAL). - * return CNTP_TVAL Register value - */ -static inline rt_uint32_t __get_cntp_tval(void) -{ - rt_uint32_t result; - __get_cp(15, 0, result, 14, 2, 0); - return result; -} - -/** Get CNTPCT - * This function returns the value of the 64 bits PL1 Physical Count Register (CNTPCT). - * return CNTPCT Register value - */ -static inline rt_uint64_t __get_cntpct(void) -{ - rt_uint64_t result; - __get_cp64(15, 0, result, 14); - return result; -} - -/** Set CNTP_CVAL - * This function assigns the given value to 64bits PL1 Physical Timer CompareValue Register (CNTP_CVAL). - * param value: CNTP_CVAL Register value to set -*/ -static inline void __set_cntp_cval(rt_uint64_t value) -{ - __set_cp64(15, 2, value, 14); -} - -/** Get CNTP_CVAL - * This function returns the value of the 64 bits PL1 Physical Timer CompareValue Register (CNTP_CVAL). - * return CNTP_CVAL Register value - */ -static inline rt_uint64_t __get_cntp_cval(void) -{ - rt_uint64_t result; - __get_cp64(15, 2, result, 14); - return result; -} - -/** Set CNTP_CTL - * This function assigns the given value to PL1 Physical Timer Control Register (CNTP_CTL). - * param value: CNTP_CTL Register value to set - */ -static inline void __set_cntp_ctl(uint32_t value) -{ - __set_cp(15, 0, value, 14, 2, 1); -} - -/** Get CNTP_CTL register - * return CNTP_CTL Register value - */ -static inline rt_uint32_t __get_cntp_ctl(void) -{ - rt_uint32_t result; - __get_cp(15, 0, result, 14, 2, 1); - return result; -} - -/** Configures the frequency the timer shall run at. - * param value The timer frequency in Hz. - */ -void gtimer_set_counter_frequency(rt_uint32_t value) -{ - __set_cntfrq(value); - __asm__ volatile ("isb 0xF":::"memory"); -} - -/** Sets the reset value of the timer. - * param value: The value the timer is loaded with. - */ -void gtimer_set_load_value(rt_uint32_t value) -{ - __set_cntp_tval(value); - __asm__ volatile ("isb 0xF":::"memory"); -} - -/** Get the current counter value. - * return Current counter value. - */ -rt_uint32_t gtimer_get_current_value(void) -{ - return(__get_cntp_tval()); -} - -/** Get the current physical counter value. - * return Current physical counter value. - */ -rt_uint64_t gtimer_get_current_physical_value(void) -{ - return(__get_cntpct()); -} - -/** Set the physical compare value. - * param value: New physical timer compare value. - */ -void gtimer_set_physical_compare_value(rt_uint64_t value) -{ - __set_cntp_cval(value); - __asm__ volatile ("isb 0xF":::"memory"); -} - -/** Get the physical compare value. - * return Physical compare value. - */ -rt_uint64_t gtimer_get_physical_compare_value(void) -{ - return(__get_cntp_cval()); -} - -/** Configure the timer by setting the control value. - * param value: New timer control value. - */ -void gtimer_set_control(rt_uint32_t value) -{ - __set_cntp_ctl(value); - __asm__ volatile ("isb 0xF":::"memory"); -} - -/** Get the control value. - * return Control value. - */ -rt_uint32_t gtimer_get_control(void) -{ - return(__get_cntp_ctl()); -} - diff --git a/bsp/ft2004/cpu/gtimer.h b/bsp/ft2004/cpu/gtimer.h deleted file mode 100644 index 160d0cefde..0000000000 --- a/bsp/ft2004/cpu/gtimer.h +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2021-03-30 huijie.feng first version - */ - -#ifndef __GTIMER_H__ -#define __GTIMER_H__ - -#include - -void gtimer_set_counter_frequency(rt_uint32_t value); -void gtimer_set_load_value(rt_uint32_t value); -rt_uint32_t gtimer_get_current_value(void); -rt_uint64_t gtimer_get_current_physical_value(void); -void gtimer_set_physical_compare_value(rt_uint64_t value); -rt_uint64_t gtimer_get_physical_compare_value(void); -void gtimer_set_control(rt_uint32_t value); -rt_uint32_t gtimer_get_control(void); - -#endif - diff --git a/bsp/ft2004/cpu/interrupt.c b/bsp/ft2004/cpu/interrupt.c deleted file mode 100644 index 8dc9bd7c4b..0000000000 --- a/bsp/ft2004/cpu/interrupt.c +++ /dev/null @@ -1,316 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-06 Bernard first version - * 2018-11-22 Jesven add smp support - */ - -#include -#include -#include "interrupt.h" - -#ifdef RT_USING_GIC_V2 -#include "gic.h" -#else -#include "gicv3.h" -#endif - -/* exception and interrupt handler table */ -struct rt_irq_desc isr_table[MAX_HANDLERS]; - -#ifndef RT_USING_SMP -/* Those varibles will be accessed in ISR, so we need to share them. */ -rt_uint32_t rt_interrupt_from_thread = 0; -rt_uint32_t rt_interrupt_to_thread = 0; -rt_uint32_t rt_thread_switch_interrupt_flag = 0; -#endif - -const unsigned int VECTOR_BASE = 0x00; -extern void rt_cpu_vector_set_base(unsigned int addr); -extern int system_vectors; - -void rt_hw_vector_init(void) -{ - rt_cpu_vector_set_base((unsigned int)&system_vectors); -} - -#ifdef RT_USING_GIC_V2 -/** - * This function will initialize hardware interrupt - */ -void rt_hw_interrupt_init(void) -{ - rt_uint32_t gic_cpu_base; - rt_uint32_t gic_dist_base; - rt_uint32_t gic_irq_start; - - /* initialize vector table */ - rt_hw_vector_init(); - - /* initialize exceptions table */ - rt_memset(isr_table, 0x00, sizeof(isr_table)); - - /* initialize ARM GIC */ - gic_dist_base = platform_get_gic_dist_base(); - gic_cpu_base = platform_get_gic_cpu_base(); - - gic_irq_start = GIC_IRQ_START; - - arm_gic_dist_init(0, gic_dist_base, gic_irq_start); - arm_gic_cpu_init(0, gic_cpu_base); -} -#else -/** - * This function will initialize hardware interrupt - * Called by the primary cpu(cpu0) - */ -void rt_hw_interrupt_init(void) -{ - rt_uint32_t gic_dist_base; - rt_uint32_t gic_irq_start; - - /* initialize vector table */ - rt_hw_vector_init(); - - /* initialize exceptions table */ - rt_memset(isr_table, 0x00, sizeof(isr_table)); - - /* initialize ARM GIC */ - gic_dist_base = platform_get_gic_dist_base(); - gic_irq_start = GIC_IRQ_START; - - arm_gic_dist_init(0, gic_dist_base, gic_irq_start); - - arm_gic_cpu_init(0); - arm_gic_redist_init(0); -} - -#endif - -/** - * This function will mask a interrupt. - * @param vector the interrupt number - */ -void rt_hw_interrupt_mask(int vector) -{ - arm_gic_mask(0, vector); -} - -/** - * This function will un-mask a interrupt. - * @param vector the interrupt number - */ -void rt_hw_interrupt_umask(int vector) -{ - arm_gic_umask(0, vector); -} - -/** - * This function returns the active interrupt number. - * @param none - */ -int rt_hw_interrupt_get_irq(void) -{ - return arm_gic_get_active_irq(0); -} - -/** - * This function acknowledges the interrupt. - * @param vector the interrupt number - */ -void rt_hw_interrupt_ack(int vector) -{ - arm_gic_ack(0, vector); -} - -/** - * This function set interrupt CPU targets. - * @param vector: the interrupt number - * cpu_mask: target cpus mask, one bit for one core - */ -void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask) -{ - arm_gic_set_cpu(0, vector, cpu_mask); -} - -/** - * This function get interrupt CPU targets. - * @param vector: the interrupt number - * @return target cpus mask, one bit for one core - */ -unsigned int rt_hw_interrupt_get_target_cpus(int vector) -{ - return arm_gic_get_target_cpu(0, vector); -} - -/** - * This function set interrupt triger mode. - * @param vector: the interrupt number - * mode: interrupt triger mode; 0: level triger, 1: edge triger - */ -void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode) -{ - arm_gic_set_configuration(0, vector, mode); -} - -/** - * This function get interrupt triger mode. - * @param vector: the interrupt number - * @return interrupt triger mode; 0: level triger, 1: edge triger - */ -unsigned int rt_hw_interrupt_get_triger_mode(int vector) -{ - return arm_gic_get_configuration(0, vector); -} - -/** - * This function set interrupt pending flag. - * @param vector: the interrupt number - */ -void rt_hw_interrupt_set_pending(int vector) -{ - arm_gic_set_pending_irq(0, vector); -} - -/** - * This function get interrupt pending flag. - * @param vector: the interrupt number - * @return interrupt pending flag, 0: not pending; 1: pending - */ -unsigned int rt_hw_interrupt_get_pending(int vector) -{ - return arm_gic_get_pending_irq(0, vector); -} - -/** - * This function clear interrupt pending flag. - * @param vector: the interrupt number - */ -void rt_hw_interrupt_clear_pending(int vector) -{ - arm_gic_clear_pending_irq(0, vector); -} - -/** - * This function set interrupt priority value. - * @param vector: the interrupt number - * priority: the priority of interrupt to set - */ -void rt_hw_interrupt_set_priority(int vector, unsigned int priority) -{ - arm_gic_set_priority(0, vector, priority); -} - -/** - * This function get interrupt priority. - * @param vector: the interrupt number - * @return interrupt priority value - */ -unsigned int rt_hw_interrupt_get_priority(int vector) -{ - return arm_gic_get_priority(0, vector); -} - -/** - * This function set priority masking threshold. - * @param priority: priority masking threshold - */ -void rt_hw_interrupt_set_priority_mask(unsigned int priority) -{ - arm_gic_set_interface_prior_mask(0, priority); -} - -/** - * This function get priority masking threshold. - * @param none - * @return priority masking threshold - */ -unsigned int rt_hw_interrupt_get_priority_mask(void) -{ - return arm_gic_get_interface_prior_mask(0); -} - -/** - * This function set priority grouping field split point. - * @param bits: priority grouping field split point - * @return 0: success; -1: failed - */ -int rt_hw_interrupt_set_prior_group_bits(unsigned int bits) -{ - int status; - - if (bits < 8) - { - arm_gic_set_binary_point(0, (7 - bits)); - status = 0; - } - else - { - status = -1; - } - - return (status); -} - -/** - * This function get priority grouping field split point. - * @param none - * @return priority grouping field split point - */ -unsigned int rt_hw_interrupt_get_prior_group_bits(void) -{ - unsigned int bp; - - bp = arm_gic_get_binary_point(0) & 0x07; - - return (7 - bp); -} - -/** - * This function will install a interrupt service routine to a interrupt. - * @param vector the interrupt number - * @param new_handler the interrupt service routine to be installed - * @param old_handler the old interrupt service routine - */ -rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, - void *param, const char *name) -{ - rt_isr_handler_t old_handler = RT_NULL; - - if (vector < MAX_HANDLERS) - { - old_handler = isr_table[vector].handler; - - if (handler != RT_NULL) - { -#ifdef RT_USING_INTERRUPT_INFO - rt_strncpy(isr_table[vector].name, name, RT_NAME_MAX); -#endif /* RT_USING_INTERRUPT_INFO */ - isr_table[vector].handler = handler; - isr_table[vector].param = param; - } - } - - return old_handler; -} - -#ifdef RT_USING_SMP -void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask) -{ -#ifdef RT_USING_GIC_V2 - arm_gic_send_sgi(0, ipi_vector, cpu_mask, 0); -#else - arm_gic_send_affinity_sgi(0, ipi_vector, cpu_mask, ROUTED_TO_SPEC); -#endif -} - -void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler) -{ - /* note: ipi_vector maybe different with irq_vector */ - rt_hw_interrupt_install(ipi_vector, ipi_isr_handler, 0, "IPI_HANDLER"); -} -#endif diff --git a/bsp/ft2004/cpu/interrupt.h b/bsp/ft2004/cpu/interrupt.h deleted file mode 100644 index dda6c5534a..0000000000 --- a/bsp/ft2004/cpu/interrupt.h +++ /dev/null @@ -1,60 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-06 Bernard first version - */ - -#ifndef __INTERRUPT_H__ -#define __INTERRUPT_H__ - -#include -#include - -#define INT_IRQ 0x00 -#define INT_FIQ 0x01 - -#define IRQ_MODE_TRIG_LEVEL (0x00) /* Trigger: level triggered interrupt */ -#define IRQ_MODE_TRIG_EDGE (0x01) /* Trigger: edge triggered interrupt */ - -void rt_hw_vector_init(void); - -void rt_hw_interrupt_init(void); -void rt_hw_interrupt_mask(int vector); -void rt_hw_interrupt_umask(int vector); - -int rt_hw_interrupt_get_irq(void); -void rt_hw_interrupt_ack(int vector); - -void rt_hw_interrupt_set_target_cpus(int vector, unsigned int cpu_mask); -unsigned int rt_hw_interrupt_get_target_cpus(int vector); - -void rt_hw_interrupt_set_triger_mode(int vector, unsigned int mode); -unsigned int rt_hw_interrupt_get_triger_mode(int vector); - -void rt_hw_interrupt_set_pending(int vector); -unsigned int rt_hw_interrupt_get_pending(int vector); -void rt_hw_interrupt_clear_pending(int vector); - -void rt_hw_interrupt_set_priority(int vector, unsigned int priority); -unsigned int rt_hw_interrupt_get_priority(int vector); - -void rt_hw_interrupt_set_priority_mask(unsigned int priority); -unsigned int rt_hw_interrupt_get_priority_mask(void); - -int rt_hw_interrupt_set_prior_group_bits(unsigned int bits); -unsigned int rt_hw_interrupt_get_prior_group_bits(void); - -rt_isr_handler_t rt_hw_interrupt_install(int vector, rt_isr_handler_t handler, - void *param, const char *name); - -#ifdef RT_USING_SMP -void rt_hw_ipi_send(int ipi_vector, unsigned int cpu_mask); -void rt_hw_ipi_handler_install(int ipi_vector, rt_isr_handler_t ipi_isr_handler); -#endif - -#endif - diff --git a/bsp/ft2004/cpu/mmu.c b/bsp/ft2004/cpu/mmu.c deleted file mode 100644 index edea7e6b8b..0000000000 --- a/bsp/ft2004/cpu/mmu.c +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2012-01-10 bernard porting to AM1808 - */ - -#include -#include -#include - -#include "cp15.h" -#include "mmu.h" - -/* dump 2nd level page table */ -void rt_hw_cpu_dump_page_table_2nd(rt_uint32_t *ptb) -{ - int i; - int fcnt = 0; - - for (i = 0; i < 256; i++) - { - rt_uint32_t pte2 = ptb[i]; - if ((pte2 & 0x3) == 0) - { - if (fcnt == 0) - rt_kprintf(" "); - rt_kprintf("%04x: ", i); - fcnt++; - if (fcnt == 16) - { - rt_kprintf("fault\n"); - fcnt = 0; - } - continue; - } - if (fcnt != 0) - { - rt_kprintf("fault\n"); - fcnt = 0; - } - - rt_kprintf(" %04x: %x: ", i, pte2); - if ((pte2 & 0x3) == 0x1) - { - rt_kprintf("L,ap:%x,xn:%d,texcb:%02x\n", - ((pte2 >> 7) | (pte2 >> 4)) & 0xf, - (pte2 >> 15) & 0x1, - ((pte2 >> 10) | (pte2 >> 2)) & 0x1f); - } - else - { - rt_kprintf("S,ap:%x,xn:%d,texcb:%02x\n", - ((pte2 >> 7) | (pte2 >> 4)) & 0xf, pte2 & 0x1, - ((pte2 >> 4) | (pte2 >> 2)) & 0x1f); - } - } -} - -void rt_hw_cpu_dump_page_table(rt_uint32_t *ptb) -{ - int i; - int fcnt = 0; - - rt_kprintf("page table@%p\n", ptb); - for (i = 0; i < 1024 * 4; i++) - { - rt_uint32_t pte1 = ptb[i]; - if ((pte1 & 0x3) == 0) - { - rt_kprintf("%03x: ", i); - fcnt++; - if (fcnt == 16) - { - rt_kprintf("fault\n"); - fcnt = 0; - } - continue; - } - if (fcnt != 0) - { - rt_kprintf("fault\n"); - fcnt = 0; - } - - rt_kprintf("%03x: %08x: ", i, pte1); - if ((pte1 & 0x3) == 0x3) - { - rt_kprintf("LPAE\n"); - } - else if ((pte1 & 0x3) == 0x1) - { - rt_kprintf("pte,ns:%d,domain:%d\n", - (pte1 >> 3) & 0x1, (pte1 >> 5) & 0xf); - /* - *rt_hw_cpu_dump_page_table_2nd((void*)((pte1 & 0xfffffc000) - * - 0x80000000 + 0xC0000000)); - */ - } - else if (pte1 & (1 << 18)) - { - rt_kprintf("super section,ns:%d,ap:%x,xn:%d,texcb:%02x\n", - (pte1 >> 19) & 0x1, - ((pte1 >> 13) | (pte1 >> 10)) & 0xf, - (pte1 >> 4) & 0x1, - ((pte1 >> 10) | (pte1 >> 2)) & 0x1f); - } - else - { - rt_kprintf("section,ns:%d,ap:%x," - "xn:%d,texcb:%02x,domain:%d\n", - (pte1 >> 19) & 0x1, - ((pte1 >> 13) | (pte1 >> 10)) & 0xf, - (pte1 >> 4) & 0x1, - (((pte1 & (0x7 << 12)) >> 10) | - ((pte1 & 0x0c) >> 2)) & 0x1f, - (pte1 >> 5) & 0xf); - } - } -} - -/* level1 page table, each entry for 1MB memory. */ -volatile static unsigned long MMUTable[4 * 1024] __attribute__((aligned(16 * 1024))); -void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart, - rt_uint32_t vaddrEnd, - rt_uint32_t paddrStart, - rt_uint32_t attr) -{ - volatile rt_uint32_t *pTT; - volatile int i, nSec; - pTT = (rt_uint32_t *)MMUTable + (vaddrStart >> 20); - nSec = (vaddrEnd >> 20) - (vaddrStart >> 20); - for (i = 0; i <= nSec; i++) - { - *pTT = attr | (((paddrStart >> 20) + i) << 20); - pTT++; - } -} - -unsigned long rt_hw_set_domain_register(unsigned long domain_val) -{ - unsigned long old_domain; - - asm volatile("mrc p15, 0, %0, c3, c0\n" - : "=r"(old_domain)); - asm volatile("mcr p15, 0, %0, c3, c0\n" - : - : "r"(domain_val) - : "memory"); - - return old_domain; -} - -void rt_hw_init_mmu_table(struct mem_desc *mdesc, rt_uint32_t size) -{ - /* set page table */ - for (; size > 0; size--) - { - rt_hw_mmu_setmtt(mdesc->vaddr_start, mdesc->vaddr_end, - mdesc->paddr_start, mdesc->attr); - mdesc++; - } -} - -void rt_hw_mmu_init(void) -{ - rt_cpu_dcache_clean_flush(); - rt_cpu_icache_flush(); - - rt_hw_cpu_dcache_disable(); - rt_hw_cpu_icache_disable(); - rt_cpu_mmu_disable(); - - // rt_hw_cpu_dump_page_table(MMUTable); - rt_hw_set_domain_register(0x55555555); - rt_cpu_tlb_set(MMUTable); - rt_cpu_mmu_enable(); - rt_hw_cpu_icache_enable(); - rt_hw_cpu_dcache_enable(); -} diff --git a/bsp/ft2004/cpu/mmu.h b/bsp/ft2004/cpu/mmu.h deleted file mode 100644 index b2875fa21b..0000000000 --- a/bsp/ft2004/cpu/mmu.h +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2019-03-25 quanzhao the first version - */ -#ifndef __MMU_H_ -#define __MMU_H_ - -#include - -#define DESC_SEC (0x2) -#define MEMWBWA ((1 << 12) | (3 << 2)) /* write back, write allocate */ -#define MEMWB (3 << 2) /* write back, no write allocate */ -#define MEMWT (2 << 2) /* write through, no write allocate */ -#define SHAREDEVICE (1 << 2) /* shared device */ -#define STRONGORDER (0 << 2) /* strong ordered */ -#define XN (1 << 4) /* eXecute Never */ -#define AP_RW (3 << 10) /* supervisor=RW, user=RW */ -#define AP_RO (2 << 10) /* supervisor=RW, user=RO */ -#define SHARED (1 << 16) /* shareable */ - -#define DOMAIN_FAULT (0x0) -#define DOMAIN_CHK (0x1) -#define DOMAIN_NOTCHK (0x3) -#define DOMAIN0 (0x0 << 5) -#define DOMAIN1 (0x1 << 5) - -#define DOMAIN0_ATTR (DOMAIN_CHK << 0) -#define DOMAIN1_ATTR (DOMAIN_FAULT << 2) - -/* device mapping type */ -#define DEVICE_MEM (SHARED | AP_RW | DOMAIN0 | SHAREDEVICE | DESC_SEC | XN) -/* normal memory mapping type */ -#define NORMAL_MEM (SHARED | AP_RW | DOMAIN0 | MEMWBWA | DESC_SEC) - -struct mem_desc -{ - rt_uint32_t vaddr_start; - rt_uint32_t vaddr_end; - rt_uint32_t paddr_start; - rt_uint32_t attr; -}; - -void rt_hw_mmu_setmtt(rt_uint32_t vaddrStart, - rt_uint32_t vaddrEnd, - rt_uint32_t paddrStart, - rt_uint32_t attr); - -#endif diff --git a/bsp/ft2004/cpu/pmu.c b/bsp/ft2004/cpu/pmu.c deleted file mode 100644 index 8ffc1dede6..0000000000 --- a/bsp/ft2004/cpu/pmu.c +++ /dev/null @@ -1,20 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - */ -#include -#include "pmu.h" - -void rt_hw_pmu_dump_feature(void) -{ - unsigned long reg; - - reg = rt_hw_pmu_get_control(); - rt_kprintf("ARM PMU Implementor: %c, ID code: %02x, %d counters\n", - reg >> 24, (reg >> 16) & 0xff, (reg >> 11) & 0x1f); - RT_ASSERT(ARM_PMU_CNTER_NR == ((reg >> 11) & 0x1f)); -} diff --git a/bsp/ft2004/cpu/pmu.h b/bsp/ft2004/cpu/pmu.h deleted file mode 100644 index b5aa032cd5..0000000000 --- a/bsp/ft2004/cpu/pmu.h +++ /dev/null @@ -1,174 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - */ -#ifndef __PMU_H__ -#define __PMU_H__ - -#include "board.h" - -/* Number of counters */ -#define ARM_PMU_CNTER_NR 4 - -enum rt_hw_pmu_event_type -{ - ARM_PMU_EVENT_PMNC_SW_INCR = 0x00, - ARM_PMU_EVENT_L1_ICACHE_REFILL = 0x01, - ARM_PMU_EVENT_ITLB_REFILL = 0x02, - ARM_PMU_EVENT_L1_DCACHE_REFILL = 0x03, - ARM_PMU_EVENT_L1_DCACHE_ACCESS = 0x04, - ARM_PMU_EVENT_DTLB_REFILL = 0x05, - ARM_PMU_EVENT_MEM_READ = 0x06, - ARM_PMU_EVENT_MEM_WRITE = 0x07, - ARM_PMU_EVENT_INSTR_EXECUTED = 0x08, - ARM_PMU_EVENT_EXC_TAKEN = 0x09, - ARM_PMU_EVENT_EXC_EXECUTED = 0x0A, - ARM_PMU_EVENT_CID_WRITE = 0x0B, -}; - -/* Enable bit */ -#define ARM_PMU_PMCR_E (0x01 << 0) -/* Event counter reset */ -#define ARM_PMU_PMCR_P (0x01 << 1) -/* Cycle counter reset */ -#define ARM_PMU_PMCR_C (0x01 << 2) -/* Cycle counter divider */ -#define ARM_PMU_PMCR_D (0x01 << 3) - -#ifdef __GNUC__ -rt_inline void rt_hw_pmu_enable_cnt(int divide64) -{ - unsigned long pmcr; - unsigned long pmcntenset; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" - : "=r"(pmcr)); - pmcr |= ARM_PMU_PMCR_E | ARM_PMU_PMCR_P | ARM_PMU_PMCR_C; - if (divide64) - pmcr |= ARM_PMU_PMCR_D; - else - pmcr &= ~ARM_PMU_PMCR_D; - asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); - - /* enable all the counters */ - pmcntenset = ~0; - asm volatile("mcr p15, 0, %0, c9, c12, 1" ::"r"(pmcntenset)); - /* clear overflows(just in case) */ - asm volatile("mcr p15, 0, %0, c9, c12, 3" ::"r"(pmcntenset)); -} - -rt_inline unsigned long rt_hw_pmu_get_control(void) -{ - unsigned long pmcr; - asm("mrc p15, 0, %0, c9, c12, 0" - : "=r"(pmcr)); - return pmcr; -} - -rt_inline unsigned long rt_hw_pmu_get_ceid(void) -{ - unsigned long reg; - /* only PMCEID0 is supported, PMCEID1 is RAZ. */ - asm("mrc p15, 0, %0, c9, c12, 6" - : "=r"(reg)); - return reg; -} - -rt_inline unsigned long rt_hw_pmu_get_cnten(void) -{ - unsigned long pmcnt; - asm("mrc p15, 0, %0, c9, c12, 1" - : "=r"(pmcnt)); - return pmcnt; -} - -rt_inline void rt_hw_pmu_reset_cycle(void) -{ - unsigned long pmcr; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" - : "=r"(pmcr)); - pmcr |= ARM_PMU_PMCR_C; - asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); - asm volatile("isb"); -} - -rt_inline void rt_hw_pmu_reset_event(void) -{ - unsigned long pmcr; - - asm volatile("mrc p15, 0, %0, c9, c12, 0" - : "=r"(pmcr)); - pmcr |= ARM_PMU_PMCR_P; - asm volatile("mcr p15, 0, %0, c9, c12, 0" ::"r"(pmcr)); - asm volatile("isb"); -} - -rt_inline unsigned long rt_hw_pmu_get_cycle(void) -{ - unsigned long cyc; - asm volatile("isb"); - asm volatile("mrc p15, 0, %0, c9, c13, 0" - : "=r"(cyc)); - return cyc; -} - -rt_inline void rt_hw_pmu_select_counter(int idx) -{ - RT_ASSERT(idx < ARM_PMU_CNTER_NR); - - asm volatile("mcr p15, 0, %0, c9, c12, 5" - : - : "r"(idx)); - /* Linux add an isb here, don't know why here. */ - asm volatile("isb"); -} - -rt_inline void rt_hw_pmu_select_event(int idx, - enum rt_hw_pmu_event_type eve) -{ - RT_ASSERT(idx < ARM_PMU_CNTER_NR); - - rt_hw_pmu_select_counter(idx); - asm volatile("mcr p15, 0, %0, c9, c13, 1" - : - : "r"(eve)); -} - -rt_inline unsigned long rt_hw_pmu_read_counter(int idx) -{ - unsigned long reg; - - rt_hw_pmu_select_counter(idx); - asm volatile("isb"); - asm volatile("mrc p15, 0, %0, c9, c13, 2" - : "=r"(reg)); - return reg; -} - -rt_inline unsigned long rt_hw_pmu_get_ovsr(void) -{ - unsigned long reg; - asm volatile("isb"); - asm("mrc p15, 0, %0, c9, c12, 3" - : "=r"(reg)); - return reg; -} - -rt_inline void rt_hw_pmu_clear_ovsr(unsigned long reg) -{ - asm("mcr p15, 0, %0, c9, c12, 3" - : - : "r"(reg)); - asm volatile("isb"); -} - -#endif - -void rt_hw_pmu_dump_feature(void); - -#endif /* end of include guard: __PMU_H__ */ diff --git a/bsp/ft2004/cpu/stack.c b/bsp/ft2004/cpu/stack.c deleted file mode 100644 index 07d795ab5b..0000000000 --- a/bsp/ft2004/cpu/stack.c +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2011-09-23 Bernard the first version - * 2011-10-05 Bernard add thumb mode - */ -#include -#include -#include - -/** - * @addtogroup AM33xx - */ -/*@{*/ - -/** - * This function will initialize thread stack - * - * @param tentry the entry of thread - * @param parameter the parameter of entry - * @param stack_addr the beginning stack address - * @param texit the function will be called when thread exit - * - * @return stack address - */ -rt_uint8_t *rt_hw_stack_init(void *tentry, void *parameter, - rt_uint8_t *stack_addr, void *texit) -{ - rt_uint32_t *stk; - - stack_addr += sizeof(rt_uint32_t); - stack_addr = (rt_uint8_t *)RT_ALIGN_DOWN((rt_uint32_t)stack_addr, 8); - stk = (rt_uint32_t *)stack_addr; - *(--stk) = (rt_uint32_t)tentry; /* entry point */ - *(--stk) = (rt_uint32_t)texit; /* lr */ - *(--stk) = 0xdeadbeef; /* r12 */ - *(--stk) = 0xdeadbeef; /* r11 */ - *(--stk) = 0xdeadbeef; /* r10 */ - *(--stk) = 0xdeadbeef; /* r9 */ - *(--stk) = 0xdeadbeef; /* r8 */ - *(--stk) = 0xdeadbeef; /* r7 */ - *(--stk) = 0xdeadbeef; /* r6 */ - *(--stk) = 0xdeadbeef; /* r5 */ - *(--stk) = 0xdeadbeef; /* r4 */ - *(--stk) = 0xdeadbeef; /* r3 */ - *(--stk) = 0xdeadbeef; /* r2 */ - *(--stk) = 0xdeadbeef; /* r1 */ - *(--stk) = (rt_uint32_t)parameter; /* r0 : argument */ - /* cpsr */ - if ((rt_uint32_t)tentry & 0x01) - *(--stk) = SVCMODE | 0x20; /* thumb mode */ - else - *(--stk) = SVCMODE; /* arm mode */ - -#ifdef RT_USING_LWP - *(--stk) = 0; /* user lr */ - *(--stk) = 0; /* user sp*/ -#endif -#ifdef RT_USING_FPU - *(--stk) = 0; /* not use fpu*/ -#endif - - /* return task's current stack address */ - return (rt_uint8_t *)stk; -} - -/*@}*/ diff --git a/bsp/ft2004/cpu/start_gcc.S b/bsp/ft2004/cpu/start_gcc.S deleted file mode 100644 index 57240226d5..0000000000 --- a/bsp/ft2004/cpu/start_gcc.S +++ /dev/null @@ -1,586 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-05 Bernard the first version - * 2018-11-22 Jesven in the interrupt context, use rt_scheduler_do_irq_switch checks - * and switches to a new thread - */ - -#include "rtconfig.h" -.equ Mode_USR, 0x10 -.equ Mode_FIQ, 0x11 -.equ Mode_IRQ, 0x12 -.equ Mode_SVC, 0x13 -.equ Mode_ABT, 0x17 -.equ Mode_UND, 0x1B -.equ Mode_SYS, 0x1F - -.equ I_Bit, 0x80 @ when I bit is set, IRQ is disabled -.equ F_Bit, 0x40 @ when F bit is set, FIQ is disabled - -#ifdef RT_USING_FPU -.equ UND_Stack_Size, 0x00000400 -#else -.equ UND_Stack_Size, 0x00000000 -#endif -.equ SVC_Stack_Size, 0x00000400 -.equ ABT_Stack_Size, 0x00000000 -.equ RT_FIQ_STACK_PGSZ, 0x00000000 -.equ RT_IRQ_STACK_PGSZ, 0x00000800 -.equ USR_Stack_Size, 0x00000400 - -#define ISR_Stack_Size (UND_Stack_Size + SVC_Stack_Size + ABT_Stack_Size + \ - RT_FIQ_STACK_PGSZ + RT_IRQ_STACK_PGSZ) - - -.macro push_svc_reg - sub sp, sp, #17 * 4 @/* Sizeof(struct rt_hw_exp_stack) */ - stmia sp, {r0 - r12} @/* Calling r0-r12 */ - mov r0, sp - mrs r6, spsr @/* Save CPSR */ - str lr, [r0, #15*4] @/* Push PC */ - str r6, [r0, #16*4] @/* Push CPSR */ - cps #Mode_SVC - str sp, [r0, #13*4] @/* Save calling SP */ - str lr, [r0, #14*4] @/* Save calling PC */ -.endm - - -.section .data.share.isr -/* stack */ -.globl stack_start -.globl stack_top - -.align 3 -stack_start: -.rept ISR_Stack_Size -.byte 0 -.endr -stack_top: - -.text -/* reset entry */ -.globl _reset -_reset: -#ifdef ARCH_ARMV8 - /* Check for HYP mode */ - mrs r0, cpsr_all - and r0, r0, #0x1F - mov r8, #0x1A - cmp r0, r8 - beq overHyped - b continue - -overHyped: /* Get out of HYP mode */ - adr r1, continue - msr ELR_hyp, r1 - mrs r1, cpsr_all - and r1, r1, #0x1f ;@ CPSR_MODE_MASK - orr r1, r1, #0x13 ;@ CPSR_MODE_SUPERVISOR - msr SPSR_hyp, r1 - eret - -continue: -#endif - /* set the cpu to SVC32 mode and disable interrupt */ - cps #Mode_SVC - -#ifdef RT_USING_FPU - mov r4, #0xfffffff - mcr p15, 0, r4, c1, c0, 2 -#endif - - /* disable the data alignment check */ - mrc p15, 0, r1, c1, c0, 0 - bic r1, #(1<<0) /* Disable MMU */ - bic r1, #(1<<1) /* Disable Alignment fault checking */ - bic r1, #(1<<2) /* Disable data cache */ - bic r1, #(1<<11) /* Disable program flow prediction */ - bic r1, #(1<<12) /* Disable instruction cache */ - mcr p15, 0, r1, c1, c0, 0 - - @ get cpu id, and subtract the offset from the stacks base address - mrc p15, 0, r5, c0, c0, 5 @ read multiprocessor affinity register - and r5, r5, #3 @ mask off, leaving CPU ID field - - cmp r5, #0 @ cpu id == 0 - beq normal_setup - - @ cpu id > 0, stop or wait -#ifdef RT_SMP_AUTO_BOOT - ldr r0, =secondary_cpu_entry - mov r1, #0 - str r1, [r0] /* clean secondary_cpu_entry */ -#endif /* RT_SMP_AUTO_BOOT */ - -secondary_loop: - @ cpu core 1 goes into sleep until core 0 wakeup it - wfe -#ifdef RT_SMP_AUTO_BOOT - ldr r1, =secondary_cpu_entry - ldr r0, [r1] - cmp r0, #0 - blxne r0 /* if(secondary_cpu_entry) secondary_cpu_entry(); */ -#endif /* RT_SMP_AUTO_BOOT */ - b secondary_loop - -normal_setup: - /* setup stack */ - bl stack_setup - - /* clear .bss */ - mov r0,#0 /* get a zero */ - ldr r1,=__bss_start /* bss start */ - ldr r2,=__bss_end /* bss end */ - -bss_loop: - cmp r1,r2 /* check if data to clear */ - strlo r0,[r1],#4 /* clear 4 bytes */ - blo bss_loop /* loop until done */ - -#ifdef RT_USING_SMP - mrc p15, 0, r1, c1, c0, 1 - mov r0, #(1<<6) - orr r1, r0 - mcr p15, 0, r1, c1, c0, 1 //enable smp -#endif - - - - /* enable branch prediction */ - mrc p15, 0, r0, c1, c0, 0 - orr r0, r0, #(1<<11) - mcr p15, 0, r1, c1, c0, 0 - - push_svc_reg - bl test_check_debug - - - - /* initialize the mmu table and enable mmu */ - ldr r0, =platform_mem_desc - ldr r1, =platform_mem_desc_size - ldr r1, [r1] - bl rt_hw_init_mmu_table - bl rt_hw_mmu_init - - /* call C++ constructors of global objects */ - ldr r0, =__ctors_start__ - ldr r1, =__ctors_end__ - -ctor_loop: - cmp r0, r1 - beq ctor_end - ldr r2, [r0], #4 - stmfd sp!, {r0-r1} - mov lr, pc - bx r2 - ldmfd sp!, {r0-r1} - b ctor_loop -ctor_end: - - /* start RT-Thread Kernel */ - ldr pc, _rtthread_startup -_rtthread_startup: - .word rtthread_startup - -stack_setup: - ldr r0, =stack_top - - @ Set the startup stack for svc - mov sp, r0 - - @ Enter Undefined Instruction Mode and set its Stack Pointer - msr cpsr_c, #Mode_UND|I_Bit|F_Bit - mov sp, r0 - sub r0, r0, #UND_Stack_Size - - @ Enter Abort Mode and set its Stack Pointer - msr cpsr_c, #Mode_ABT|I_Bit|F_Bit - mov sp, r0 - sub r0, r0, #ABT_Stack_Size - - @ Enter FIQ Mode and set its Stack Pointer - msr cpsr_c, #Mode_FIQ|I_Bit|F_Bit - mov sp, r0 - sub r0, r0, #RT_FIQ_STACK_PGSZ - - @ Enter IRQ Mode and set its Stack Pointer - msr cpsr_c, #Mode_IRQ|I_Bit|F_Bit - mov sp, r0 - sub r0, r0, #RT_IRQ_STACK_PGSZ - - /* come back to SVC mode */ - msr cpsr_c, #Mode_SVC|I_Bit|F_Bit - bx lr - -/* exception handlers: undef, swi, padt, dabt, resv, irq, fiq */ -.section .text.isr, "ax" - .align 5 -.globl vector_fiq -vector_fiq: - stmfd sp!,{r0-r7,lr} - bl rt_hw_trap_fiq - ldmfd sp!,{r0-r7,lr} - subs pc, lr, #4 - -.globl rt_interrupt_enter -.globl rt_interrupt_leave -.globl rt_thread_switch_interrupt_flag -.globl rt_interrupt_from_thread -.globl rt_interrupt_to_thread - -.globl rt_current_thread -.globl vmm_thread -.globl vmm_virq_check - - .align 5 -.globl vector_irq -vector_irq: -#ifdef RT_USING_SMP - clrex - - stmfd sp!, {r0, r1} - cps #Mode_SVC - mov r0, sp /* svc_sp */ - mov r1, lr /* svc_lr */ - - cps #Mode_IRQ - sub lr, #4 - stmfd r0!, {r1, lr} /* svc_lr, svc_pc */ - stmfd r0!, {r2 - r12} - ldmfd sp!, {r1, r2} /* original r0, r1 */ - stmfd r0!, {r1 - r2} - mrs r1, spsr /* original mode */ - stmfd r0!, {r1} - -#ifdef RT_USING_LWP - stmfd r0, {r13, r14}^ /* usr_sp, usr_lr */ - sub r0, #8 -#endif -#ifdef RT_USING_FPU - /* fpu context */ - vmrs r6, fpexc - tst r6, #(1<<30) - beq 1f - vstmdb r0!, {d0-d15} - vstmdb r0!, {d16-d31} - vmrs r5, fpscr - stmfd r0!, {r5} -1: - stmfd r0!, {r6} -#endif - - /* now irq stack is clean */ - /* r0 is task svc_sp */ - /* backup r0 -> r8 */ - mov r8, r0 - - bl rt_interrupt_enter - bl rt_hw_trap_irq - bl rt_interrupt_leave - - cps #Mode_SVC - mov sp, r8 - mov r0, r8 - bl rt_scheduler_do_irq_switch - - b rt_hw_context_switch_exit - -#else - stmfd sp!, {r0-r12,lr} - - bl rt_interrupt_enter - bl rt_hw_trap_irq - bl rt_interrupt_leave - - @ if rt_thread_switch_interrupt_flag set, jump to - @ rt_hw_context_switch_interrupt_do and don't return - ldr r0, =rt_thread_switch_interrupt_flag - ldr r1, [r0] - cmp r1, #1 - beq rt_hw_context_switch_interrupt_do - - ldmfd sp!, {r0-r12,lr} - subs pc, lr, #4 - -rt_hw_context_switch_interrupt_do: - mov r1, #0 @ clear flag - str r1, [r0] - - mov r1, sp @ r1 point to {r0-r3} in stack - add sp, sp, #4*4 - ldmfd sp!, {r4-r12,lr}@ reload saved registers - mrs r0, spsr @ get cpsr of interrupt thread - sub r2, lr, #4 @ save old task's pc to r2 - - @ Switch to SVC mode with no interrupt. If the usr mode guest is - @ interrupted, this will just switch to the stack of kernel space. - @ save the registers in kernel space won't trigger data abort. - msr cpsr_c, #I_Bit|F_Bit|Mode_SVC - - stmfd sp!, {r2} @ push old task's pc - stmfd sp!, {r4-r12,lr}@ push old task's lr,r12-r4 - ldmfd r1, {r1-r4} @ restore r0-r3 of the interrupt thread - stmfd sp!, {r1-r4} @ push old task's r0-r3 - stmfd sp!, {r0} @ push old task's cpsr - -#ifdef RT_USING_LWP - stmfd sp, {r13, r14}^ @push usr_sp, usr_lr - sub sp, #8 -#endif -#ifdef RT_USING_FPU - /* fpu context */ - vmrs r6, fpexc - tst r6, #(1<<30) - beq 1f - vstmdb sp!, {d0-d15} - vstmdb sp!, {d16-d31} - vmrs r5, fpscr - stmfd sp!, {r5} -1: - stmfd sp!, {r6} -#endif - - ldr r4, =rt_interrupt_from_thread - ldr r5, [r4] - str sp, [r5] @ store sp in preempted tasks's TCB - - ldr r6, =rt_interrupt_to_thread - ldr r6, [r6] - ldr sp, [r6] @ get new task's stack pointer - -#ifdef RT_USING_FPU -/* fpu context */ - ldmfd sp!, {r6} - vmsr fpexc, r6 - tst r6, #(1<<30) - beq 1f - ldmfd sp!, {r5} - vmsr fpscr, r5 - vldmia sp!, {d16-d31} - vldmia sp!, {d0-d15} -1: -#endif - -#ifdef RT_USING_LWP - ldmfd sp, {r13, r14}^ @pop usr_sp, usr_lr - add sp, #8 -#endif - - ldmfd sp!, {r4} @ pop new task's cpsr to spsr - msr spsr_cxsf, r4 - - ldmfd sp!, {r0-r12,lr,pc}^ @ pop new task's r0-r12,lr & pc, copy spsr to cpsr - -#endif - - - .align 5 -.weak vector_swi -vector_swi: - push_svc_reg - bl rt_hw_trap_swi - b . - - .align 5 - .globl vector_undef -vector_undef: - push_svc_reg - cps #Mode_UND - bl rt_hw_trap_undef -#ifdef RT_USING_FPU - ldr lr, [sp, #15*4] - ldmia sp, {r0 - r12} - add sp, sp, #17 * 4 - movs pc, lr -#endif - b . - - .align 5 - .globl vector_pabt -vector_pabt: - push_svc_reg - bl rt_hw_trap_pabt - b . - - .align 5 - .globl vector_dabt -vector_dabt: - push_svc_reg - bl rt_hw_trap_dabt - b . - - .align 5 - .globl vector_resv -vector_resv: - push_svc_reg - bl rt_hw_trap_resv - b . - -#ifdef RT_USING_SMP -.global set_secondary_cpu_boot_address -.global secondary_cpu_start -secondary_cpu_start: - -#ifdef RT_USING_FPU - mov r4, #0xfffffff - mcr p15, 0, r4, c1, c0, 2 -#endif - - mrc p15, 0, r1, c1, c0, 1 - mov r0, #(1<<6) - orr r1, r0 - mcr p15, 0, r1, c1, c0, 1 //enable smp - - mrc p15, 0, r0, c1, c0, 0 - bic r0, #(1<<13) - mcr p15, 0, r0, c1, c0, 0 - -#ifdef RT_USING_FPU - cps #Mode_UND - ldr sp, =und_stack_2_limit -#endif - - cps #Mode_IRQ - ldr sp, =irq_stack_2_limit - - cps #Mode_FIQ - ldr sp, =irq_stack_2_limit - - cps #Mode_SVC - ldr sp, =svc_stack_2_limit - - /* initialize the mmu table and enable mmu */ - bl rt_hw_mmu_init - - b secondary_cpu_c_start -#endif - -#ifdef RT_USING_SMP - -.global secondary2_cpu_start -secondary2_cpu_start: - -#ifdef RT_USING_FPU - mov r4, #0xfffffff - mcr p15, 0, r4, c1, c0, 2 -#endif - - mrc p15, 0, r1, c1, c0, 1 - mov r0, #(1<<6) - orr r1, r0 - mcr p15, 0, r1, c1, c0, 1 //enable smp - - mrc p15, 0, r0, c1, c0, 0 - bic r0, #(1<<13) - mcr p15, 0, r0, c1, c0, 0 - -#ifdef RT_USING_FPU - cps #Mode_UND - ldr sp, =und_stack_3_limit -#endif - - cps #Mode_IRQ - ldr sp, =irq_stack_3_limit - - cps #Mode_FIQ - ldr sp, =irq_stack_3_limit - - cps #Mode_SVC - ldr sp, =svc_stack_3_limit - - /* initialize the mmu table and enable mmu */ - bl rt_hw_mmu_init - - b secondary_cpu_c_start -#endif - - -#ifdef RT_USING_SMP -.global secondary3_cpu_start -secondary3_cpu_start: - -#ifdef RT_USING_FPU - mov r4, #0xfffffff - mcr p15, 0, r4, c1, c0, 2 -#endif - - mrc p15, 0, r1, c1, c0, 1 - mov r0, #(1<<6) - orr r1, r0 - mcr p15, 0, r1, c1, c0, 1 //enable smp - - mrc p15, 0, r0, c1, c0, 0 - bic r0, #(1<<13) - mcr p15, 0, r0, c1, c0, 0 - -#ifdef RT_USING_FPU - cps #Mode_UND - ldr sp, =und_stack_4_limit -#endif - - cps #Mode_IRQ - ldr sp, =irq_stack_4_limit - - cps #Mode_FIQ - ldr sp, =irq_stack_4_limit - - cps #Mode_SVC - ldr sp, =svc_stack_4_limit - - /* initialize the mmu table and enable mmu */ - bl rt_hw_mmu_init - - b secondary_cpu_c_start -#endif - - -.bss -.align 2 //align to 2~2=4 -svc_stack_2: - .space (1 << 10) -svc_stack_2_limit: - -irq_stack_2: - .space (1 << 10) -irq_stack_2_limit: - -#ifdef RT_USING_FPU -und_stack_2: - .space (1 << 10) -und_stack_2_limit: -#endif - -svc_stack_3: - .space (1 << 10) -svc_stack_3_limit: - -irq_stack_3: - .space (1 << 10) -irq_stack_3_limit: - -#ifdef RT_USING_FPU -und_stack_3: - .space (1 << 10) -und_stack_3_limit: -#endif - - -svc_stack_4: - .space (1 << 10) -svc_stack_4_limit: - -irq_stack_4: - .space (1 << 10) -irq_stack_4_limit: - -#ifdef RT_USING_FPU -und_stack_4: - .space (1 << 10) -und_stack_4_limit: -#endif \ No newline at end of file diff --git a/bsp/ft2004/cpu/trap.c b/bsp/ft2004/cpu/trap.c deleted file mode 100644 index 7b31c7f6c3..0000000000 --- a/bsp/ft2004/cpu/trap.c +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright (c) 2006-2021, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-20 Bernard first version - */ - -#include -#include -#include - -#include "armv7.h" -#include "interrupt.h" - -#ifdef RT_USING_FINSH -extern long list_thread(void); -#endif - -/** - * this function will show registers of CPU - * - * @param regs the registers point - */ -void rt_hw_show_register(struct rt_hw_exp_stack *regs) -{ - rt_kprintf("Execption:\n"); - rt_kprintf("r00:0x%08x r01:0x%08x r02:0x%08x r03:0x%08x\n", regs->r0, regs->r1, regs->r2, regs->r3); - rt_kprintf("r04:0x%08x r05:0x%08x r06:0x%08x r07:0x%08x\n", regs->r4, regs->r5, regs->r6, regs->r7); - rt_kprintf("r08:0x%08x r09:0x%08x r10:0x%08x\n", regs->r8, regs->r9, regs->r10); - rt_kprintf("fp :0x%08x ip :0x%08x\n", regs->fp, regs->ip); - rt_kprintf("sp :0x%08x lr :0x%08x pc :0x%08x\n", regs->sp, regs->lr, regs->pc); - rt_kprintf("cpsr:0x%08x\n", regs->cpsr); -} - -/** - * When comes across an instruction which it cannot handle, - * it takes the undefined instruction trap. - * - * @param regs system registers - * - * @note never invoke this function in application - */ -void rt_hw_trap_undef(struct rt_hw_exp_stack *regs) -{ -#ifdef RT_USING_FPU - { - uint32_t val; - uint32_t addr; - - if (regs->cpsr & (1 << 5)) - { - /* thumb mode */ - addr = regs->pc - 2; - } - else - { - addr = regs->pc - 4; - } - asm volatile("vmrs %0, fpexc" - : "=r"(val)::"memory"); - - if (!(val & 0x40000000)) - { - /* float ins */ - val = (1U << 30); - - asm volatile("vmsr fpexc, %0" ::"r"(val) - : "memory"); - regs->pc = addr; - return; - } - } -#endif - rt_kprintf("undefined instruction:\n"); - rt_hw_show_register(regs); -#ifdef RT_USING_FINSH - list_thread(); -#endif - rt_hw_cpu_shutdown(); -} - -/** - * The software interrupt instruction (SWI) is used for entering - * Supervisor mode, usually to request a particular supervisor - * function. - * - * @param regs system registers - * - * @note never invoke this function in application - */ -void rt_hw_trap_swi(struct rt_hw_exp_stack *regs) -{ - rt_kprintf("software interrupt:\n"); - rt_hw_show_register(regs); -#ifdef RT_USING_FINSH - list_thread(); -#endif - rt_hw_cpu_shutdown(); -} - -/** - * An abort indicates that the current memory access cannot be completed, - * which occurs during an instruction prefetch. - * - * @param regs system registers - * - * @note never invoke this function in application - */ -void rt_hw_trap_pabt(struct rt_hw_exp_stack *regs) -{ - rt_kprintf("prefetch abort:\n"); - rt_hw_show_register(regs); -#ifdef RT_USING_FINSH - list_thread(); -#endif - rt_hw_cpu_shutdown(); -} - -void test_check_debug(struct rt_hw_exp_stack *regs) -{ - Ft_printf("error is here \r\n"); - rt_hw_cpu_shutdown(); -} - -/** - * An abort indicates that the current memory access cannot be completed, - * which occurs during a data access. - * - * @param regs system registers - * - * @note never invoke this function in application - */ -void rt_hw_trap_dabt(struct rt_hw_exp_stack *regs) -{ - rt_kprintf("data abort:"); - rt_hw_show_register(regs); -#ifdef RT_USING_FINSH - list_thread(); -#endif - rt_hw_cpu_shutdown(); -} - -/** - * Normally, system will never reach here - * - * @param regs system registers - * - * @note never invoke this function in application - */ -void rt_hw_trap_resv(struct rt_hw_exp_stack *regs) -{ - rt_kprintf("reserved trap:\n"); - rt_hw_show_register(regs); -#ifdef RT_USING_FINSH - list_thread(); -#endif - rt_hw_cpu_shutdown(); -} - -void rt_hw_trap_irq(void) -{ - void *param; - int ir; - rt_isr_handler_t isr_func; - extern struct rt_irq_desc isr_table[]; - - ir = rt_hw_interrupt_get_irq(); - - if (ir == 1023) - { - /* Spurious interrupt */ - return; - } - - /* get interrupt service routine */ - isr_func = isr_table[ir].handler; -#ifdef RT_USING_INTERRUPT_INFO - isr_table[ir].counter++; -#endif - if (isr_func) - { - /* Interrupt for myself. */ - param = isr_table[ir].param; - /* turn to interrupt service routine */ - isr_func(ir, param); - } - - /* end of interrupt */ - rt_hw_interrupt_ack(ir); -} - -void rt_hw_trap_fiq(void) -{ - void *param; - int ir; - rt_isr_handler_t isr_func; - extern struct rt_irq_desc isr_table[]; - - ir = rt_hw_interrupt_get_irq(); - - /* get interrupt service routine */ - isr_func = isr_table[ir].handler; - param = isr_table[ir].param; - - /* turn to interrupt service routine */ - isr_func(ir, param); - - /* end of interrupt */ - rt_hw_interrupt_ack(ir); -} diff --git a/bsp/ft2004/cpu/vector_gcc.S b/bsp/ft2004/cpu/vector_gcc.S deleted file mode 100644 index 60d3c6cf1d..0000000000 --- a/bsp/ft2004/cpu/vector_gcc.S +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (c) 2006-2018, RT-Thread Development Team - * - * SPDX-License-Identifier: Apache-2.0 - * - * Change Logs: - * Date Author Notes - * 2013-07-05 Bernard the first version - */ - -.section .vectors, "ax" -.code 32 - -.globl system_vectors -system_vectors: - ldr pc, _vector_reset - ldr pc, _vector_undef - ldr pc, _vector_swi - ldr pc, _vector_pabt - ldr pc, _vector_dabt - ldr pc, _vector_resv - ldr pc, _vector_irq - ldr pc, _vector_fiq - -.globl _reset -.globl vector_undef -.globl vector_swi -.globl vector_pabt -.globl vector_dabt -.globl vector_resv -.globl vector_irq -.globl vector_fiq - -_vector_reset: - .word _reset -_vector_undef: - .word vector_undef -_vector_swi: - .word vector_swi -_vector_pabt: - .word vector_pabt -_vector_dabt: - .word vector_dabt -_vector_resv: - .word vector_resv -_vector_irq: - .word vector_irq -_vector_fiq: - .word vector_fiq - -.balignl 16,0xdeadbeef diff --git a/bsp/ft2004/rtconfig.h b/bsp/ft2004/rtconfig.h index 7f6381ad0b..a717e5b082 100644 --- a/bsp/ft2004/rtconfig.h +++ b/bsp/ft2004/rtconfig.h @@ -7,6 +7,8 @@ /* RT-Thread Kernel */ #define RT_NAME_MAX 32 +#define RT_USING_SMP +#define RT_CPUS_NR 4 #define RT_ALIGN_SIZE 128 #define RT_THREAD_PRIORITY_32 #define RT_THREAD_PRIORITY_MAX 32 -- Gitee From b05345ddf97f4545f623021a8fe8d9fdf87ccb20 Mon Sep 17 00:00:00 2001 From: huanghe Date: Tue, 18 May 2021 18:59:21 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E4=BF=AE=E6=94=B9=E7=BC=96=E8=AF=91?= =?UTF-8?q?=E9=93=BE=E5=9C=B0=E5=9D=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- bsp/ft2004/rtconfig.py | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/bsp/ft2004/rtconfig.py b/bsp/ft2004/rtconfig.py index c6b54f85f1..b8f8897716 100644 --- a/bsp/ft2004/rtconfig.py +++ b/bsp/ft2004/rtconfig.py @@ -1,7 +1,7 @@ ''' Author: your name Date: 2021-03-01 18:57:53 -LastEditTime: 2021-05-06 13:49:19 +LastEditTime: 2021-05-18 18:51:53 LastEditors: Please set LastEditors Description: In User Settings Edit FilePath: \rtthread\bsp\ft2004\rtconfig.py @@ -34,15 +34,13 @@ if os.getenv('RTT_CC'): # only support GNU GCC compiler. PLATFORM = 'gcc' -EXEC_PATH = '/home/carl/ft/gcc-arm-none-eabi-10-2020-q4-major/bin' - +EXEC_PATH = '/usr/lib/arm-none-eabi/bin' if os.getenv('RTT_EXEC_PATH'): EXEC_PATH = os.getenv('RTT_EXEC_PATH') BUILD = 'debug' -LIBPATH = "/home/carl/ft/gcc-arm-none-eabi-10-2020-q4-major/lib/gcc/arm-none-eabi/10.2.1" - +LIBPATH = '/usr/lib/arm-none-eabi/lib' if PLATFORM == 'gcc': -- Gitee